Service(SVC)
K8S SVC定义了一种抽象:一组Pod的逻辑分组,一种可以访问他们的策略——通常成为微服务。这一组Pod能够被Service访问到,通常是通过Label Selector
SVC能提供负载均衡的能力,但是有限制:
只提供4层负载能力,没有7层功能(Ingress有7层功能)
SVC的类型
ClusterIP:默认类型,自动分配一个仅Cluster内部可以访问的虚拟IP
NodePort:在ClusterIP基础上为Service在每台及其上绑定一个端口,这样就可以通过NodeIP:NodePort
来访问该服务
LoadBalancer:在NodePort的基础上,基础cloud provider创建一个外部父子均衡器,并将请求转发到NodeIP:NodePort
ExternalName:将集群外补的服务引入到集群内来,在即群内部直接诶使用。没有任何代理被创建。
ClusterIP
ClusterIP主要在每个node节点使用iptables/IPVS,将发向ClusterIP对应端口的数据,转发到kube-proxy中。然后kube-proxy自身内部实现由负载均衡的方法,并可以查询到这个service下对应Pod的地址和端口,进而把数据转发给对应的Pod的地址和端口
为了实现图上的功能,主要需要以下组件的协同工作
- apiserver:用户通过kubectl命令向apiserver发送创建SVC的命令,apiserver接收到请求后将数据存储到etcd中。
- kube-proxy: kubernetes的每个节点都有kebu-proxy进程,这个进程负责感知SVC,pod的变化,并肩变化信息写入本地的iptables/IPVS中
- iptables/IPVS:使用NAT技术将virtualIP的流量转至endpoint中
示例:
先创建一个deployment
pyapp-deploy.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pyapp-deploy
spec:
replicas: 2
selector:
matchLabels:
app: pyapp
template:
metadata:
labels:
app: pyapp
spec:
imagePullSecrets:
- name: harbor-admin
containers:
- name: pyapp-pod
image: harbor.tangotz.com/pyapp/pyapp:v2
ports:
- containerPort: 5000
启动这个deployment
访问对应ip可以返回预期结果
在创建一个使用clusterIP模式的svc,并通过标签app: pyapp绑定deploy管理的pod
svc-clusterip.yml
apiVersion: v1
kind: Service
metadata:
name: pyapp-svc-clusterip
spec:
type: ClusterIP
selector:
app: pyapp
ports:
- port: 8080
targetPort: 5000
注意对比两个yaml文件:在 Kubernetes 中,Service 和 Deployment 都使用 selector
字段来标识与它们关联的 Pod。但是,这两个资源在如何表示这个 selector
上有所不同。
- 在 Deployment 中:
selector
字段通常包含一个嵌套的matchLabels
字段,该字段定义了用于匹配 Pod 的标签。这是因为 Deployment 需要确保它所控制的 ReplicaSet(进而控制 Pod)具有特定的标签。 - 在 Service 中:
selector
字段直接是一个标签选择器,它可以是基于标签的键值对列表。Service 使用这个选择器来确定哪些 Pod 的流量应该被路由到。它不需要像 Deployment 那样嵌套在matchLabels
下。
这种设计上的区别反映了 Service 和 Deployment 在 Kubernetes 中的不同角色和职责。Service 负责将流量路由到匹配的 Pod,而 Deployment 则负责确保有正确数量和配置的 Pod 在运行。
查看一下SVC的信息
kubectl get svc pyapp-svc-clusterip
再来看一下pyapp的pod 和ipvs的信息就可以看到,对应的ip10.107.218.161被转发到pyapp所在pod的IP上
实验:
如果此时修改delpoyment的replicas数量,SVC会做和操作呢?
将replicas数量修改为4 ,再看一下ipvs。同一个记录下多了两个新pod的ip
NodePort
nodeport的原理在于在node上开了一个端口,将向改端口的流量导入到kube-proxy,然后由kuebe-proxy进一步给到对应pod
资源文件写法与CluseterIP基本一致,只不过将type改为NodePort
svc-nodeport.yml
apiVersion: v1
kind: Service
metadata:
name: pyapp-svc-nodeport
spec:
type: NodePort
selector:
app: pyapp
ports:
- port: 8080
targetPort: 5000
注意:在这个NodePort示例中,匹配标签的Pod依旧是app: pyapp,与上文ClusterIP匹配标签相同。这样的操作是可以的,并不冲突。即Pod可以通过标签绑定多个SVC。
查看创建好的SVC
可以看到NodePort同样拥有一个ClusterIP,但是在PORTS字段,ClusterIP与NodePort是不相同的,多出了一个映射端口。
使用ipvs可以看到,访问这个31771端口会被转发到对应的pod上
那么访问pod的方法有两个
- 在k8s集群内使用ClusterIP:port 此例中为 10.103.45.255:8080
- 在网络可到达的范围内使用NodeIP:nodePort 此例中为
master 192.168.0.221:31771
node01 192.168.0.231:31771
node03 192.168.0.233:31771
node04 192.168.0.234:31771
nodePort
在Kubernetes中,使用NodePort模式时,生成的端口默认是随机的。这个随机端口号通常在30000-32767的范围内。然而这个端口是可以固定的。
如果想要指定一个固定的NodePort端口,可以通过在Kubernetes的YAML配置文件中进行配置来实现。在Service的YAML配置文件中,可以在type: NodePort
下指定nodePort
字段,并设置为你想要的端口号。例如:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30080
selector:
app: my-app
在上面的示例中,我们指定了NodePort的端口为30080,对应的容器端口为8080。这样,当外部流量通过节点的30080端口访问时,Kubernetes会将其转发到运行在容器中的8080端口上。
注意,指定的NodePort端口必须在Kubernetes集群中可用,并且不能与集群中其他服务的NodePort端口冲突。如果指定的端口已被其他服务使用,Kubernetes将无法启动服务。
修改后的效果
ClusterIP没有变化但你是PORTS中的nodeport变化了,相应的使用你哦的IP:nodeport方式访问就需要使用30080端口
总结来说,NodePort和ClusterIP的主要区别在于NodePort允许从集群外部访问Service,而ClusterIP仅允许在集群内部访问。因此,可以将NodePort视为ClusterIP的一个扩展或超集。