Kubernetes Pod
是平凡的,由Deployment
等控制器管理的Pod
对象都是有生命周期的,它们会被创建,也会意外挂掉。虽然它们可以由控制器自动重建或者滚动更新,但是重建或更新之后的Pod
对象的IP地址等都会发生新的变化。这样就会导致一个问题,如果一组Pod
(称为backend
)为其它Pod
(称为frontend
)提供服务,那么那些frontend
该如何发现,并连接到这组Pod
中的哪些backend
呢? 这时候就用到了:Service
示例说明为什么要使用Service
如下图所示,当Nginx Pod
作为客户端访问Tomcat Pod
中的应用时,IP
的变动或应用规模的缩减会导致客户端访问错误。而Pod
规模的扩容又会使得客户端无法有效的使用新增的Pod
对象,从而影响达成规模扩展之目的。为此,Kubernetes
特地设计了Service
资源来解决此类问题。
一个
Service
对象就是工作节点上的一些iptables
或ipvs
规则,用于将到达Service
对象IP
地址的流量调度转发至相应的Endpoints
对象指定的IP
地址和端口之上。kube-proxy
组件通过API Server
持续监控着各Service
及其关联的Pod
对象,并将其创建或变动实时反映到当前工作节点上的iptables
规则或ipvs
规则上。
ipvs
是借助于Netfilter
实现的网络请求报文调度框架,支持rr
、wrr
、lc
、wlc
、sh
、sed
和nq
等十余种调度算法,用户空间的命令行工具是ipvsadm
,用于管理工作与ipvs
之上的调度规则。
Service IP
事实上是用于生成iptables
或ipvs
规则时使用的IP
地址,仅用于实现Kubernetes
集群网络的内部通信,并且能够将规则中定义的转发服务的请求作为目标地址予以相应,这也是将其称为虚拟IP
的原因之一。
相对userspace
模式来说,iptables
模式无须将流量在用户空间和内核空间来回切换,因而更加高效和可靠。其缺点是iptables
代理模型不会在被挑中的Pod
资源无响应时自动进行重定向;而userspace
模式则可以。
[root@k8s-master ~]# kubectl run nginx --image=nginx:1.12 --replicas=3 #创建pod资源指定副本数量为3个 [root@k8s-master ~]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-67685f79b5-688s7 1/1 Running 0 5s 10.244.2.61 k8s-node2nginx-67685f79b5-gpc2f 1/1 Running 0 5s 10.244.1.63 k8s-node1 nginx-67685f79b5-grlrz 1/1 Running 0 5s 10.244.2.60 k8s-node2 [root@k8s-master ~]# kubectl get deployment #查看deployment控制器资源 NAME READY UP-TO-DATE AVAILABLE AGE nginx 3/3 3 3 35s
#下面这条命令表示为deployment控制器资源nginx创建一个service对象,并取名为nginx、端口为80、pod内暴露端口80、协议为TCP协议。 [root@k8s-master ~]# kubectl expose deployment nginx --name=nginx --port=80 --target-port=80 --protocol=TCP service/nginx exposed [root@k8s-master ~]# kubectl get service #查看创建的service资源 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1443/TCP 27d nginx ClusterIP 10.104.116.156 80/TCP 9s
[root@k8s-master ~]# kubectl get endpoints NAME ENDPOINTS AGE kubernetes 192.168.1.31:6443 27d nginx 10.244.1.63:80,10.244.2.60:80,10.244.2.61:80 29s
二、资源清单定义
[root@k8s-master ~]# vim manfests/service-demo.yaml apiVersion: apps/v1 kind: Deployment metadata: name: service-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: service-deploy-demo template: metadata: name: svc-deploy labels: app: service-deploy-demo spec: containers: - name: svc-pod image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 --- #定义service apiVersion: v1 kind: Service metadata: name: service-demo #service名称 spec: selector: #用于匹配后端的Pod资源对象,需和上面定义pod的标签一致 app: service-deploy-demo ports: - port: 80 #service端口号 targetPort: 80 #后端Pod端口号 protocol: TCP #使用的协议
2)创建并查看
[root@k8s-master ~]# kubectl apply -f manfests/service-demo.yaml #创建资源对象 deployment.apps/service-deploy created service/service-demo created [root@k8s-master ~]# kubectl get svc #查看service资源对象,"kubectl get svc"等于"kubectl get service" NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1443/TCP 27d nginx ClusterIP 10.104.116.156 80/TCP 80m service-demo ClusterIP 10.98.31.157 80/TCP 7s [root@k8s-master ~]# kubectl get pods -o wide -l app=service-deploy-demo #查看pod资源对象 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES service-deploy-66548cc57f-982cd 1/1 Running 0 15s 10.244.2.63 k8s-node2 service-deploy-66548cc57f-blnvg 1/1 Running 0 15s 10.244.1.67 k8s-node1 service-deploy-66548cc57f-vcmxb 1/1 Running 0 15s 10.244.2.62 k8s-node2 [root@k8s-master ~]# kubectl get endpoints service-demo 查看生成的endpoints对应关系 NAME ENDPOINTS AGE service-demo 10.244.1.67:80,10.244.2.62:80,10.244.2.63:80 43s
3)节点访问测试(这里使用创建一个新的pod资源模拟客户端进行访问)
[root@k8s-master ~]# kubectl run busybox --image=busybox --rm -it -- /bin/sh #使用busybox创建一个临时pod客户端 / # wget -O - -q http://10.98.31.157/ #访问上面创建的service对象的Cluster IP Hello MyApp | Version: v1 | "hostname.html">Pod Name / # for i in 1 2 3 4; do wget -O - -q http://10.98.31.157/hostname.html; done #循环访问测试站点下的hostname.html页面,可以看出是轮循的分配给后端的pod资源。 service-deploy-66548cc57f-982cd service-deploy-66548cc57f-blnvg service-deploy-66548cc57f-982cd service-deploy-66548cc57f-982cd #说明:myapp容器中的“/hostname.html"页面能够输出当前容器的主机名。
Service
资源支持Session affinity
(粘性会话或会话粘性)机制,能够将来自同一个客户端的请求始终转发至同一个后端的Pod
对象。这意味着会影响调度算法的流量分发功能,进而降低其负载均衡的效果。所以,当客户端访问pod
中的应用程序时,如果有基于客户端身份保存某些私有信息,并基于这些私有信息追踪用户的活动等一类的需求时,就可以启用session affinity
机制。
Session affinity
的效果仅在一段时间期限内生效,默认值为10800
秒,超出此时长之后,客户端的再次访问会被调度算法重新调度。Service
资源的Session affinity
机制仅能基于客户端的IP
地址识别客户端身份,把经由同一个NAT
服务器进行源地址转换的所有客户端识别为同一个客户端,便导致调度效果不佳,所以,这种方法并不常用。
Service
资源通过service.spec.sessionAffinity
和service.spec.sessionAffinityConfig
两个字段配置粘性会话。sessionAffinity
字段用于定义要使用的粘性会话的类型,仅支持使用“None”
和“ClientIp”
两种属性值。
None:不使用
sessionAffinity
,默认值。ClientIP:基于客户端
IP
地址识别客户端身份,把来自同一个源IP
地址的请求始终调度至同一个Pod
对象。
示例
[root@k8s-master ~]# vim manfests/service-demo.yaml ...... spec: selector: app: service-deploy-demo ports: - port: 80 targetPort: 80 protocol: TCP sessionAffinity: ClientIP #指定使用ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 10 #配置session超时时间,这里为了看效果设置的比较短 [root@k8s-master ~]# kubectl apply -f manfests/service-demo.yaml deployment.apps/service-deploy unchanged #同样使用pod客户端访问测试 / # for i in 1 2 3 4; do wget -O - -q http://10.98.31.157/hostname.html; done service-deploy-66548cc57f-blnvg service-deploy-66548cc57f-blnvg service-deploy-66548cc57f-blnvg service-deploy-66548cc57f-blnvg #等待10秒过后再次访问 / # for i in 1 2 3 4; do wget -O - -q http://10.98.31.157/hostname.html; done service-deploy-66548cc57f-vcmxb service-deploy-66548cc57f-vcmxb service-deploy-66548cc57f-vcmxb service-deploy-66548cc57f-vcmxb
ClusterIP:通过集群内部
IP
地址暴露服务,此地址仅在集群内部可进行通行,无法被集群外部的客户端访问。NodePort:通过每个
Node
上的IP
和静态端口(NodePort
)暴露服务,会自动为Service
分配集群IP
地址,并将此作为NodePort
的路有目标。通过请求访问到一个
: --> : --> : NodePort
服务。LoadBalancer:构建在
NodePort
之上,并创建一个外部负载均衡器,路由到ClusterIP
。因此LoadBalancer
一样具有NodePort
和ClusterIP
。EXternalName:通过返回
CNAME
和它的值,可以将服务映射到externalName
字段的内容。换言之,此种类型并非定义由Kubernetes
集群提供的服务,而是把集群外部的某服务以DNS CNAME
记录的方式映射到集群内,从而让集群内的Pod
资源能够访问外部的Service
的一种实现方式。这种类型的Service
没有ClusterIP
和NodePort
,也没有标签选择器用于选择Pod
资源,因此也不会有Endpoints
存在。
[root@k8s-master ~]# vim manfests/redis-svc.yaml #编写yaml格式的清单文件 apiVersion: apps/v1 kind: Deployment metadata: name: redis-deploy namespace: default spec: replicas: 2 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis-pod image: redis ports: - name: redis containerPort: 6379 --- apiVersion: v1 kind: Service metadata: name: redis-svc #service对象名 spec: type: ClusterIP #这里指定使用ClusterIP,默认也是ClusterIP,这里可有可无 selector: app: redis #匹配上面定义的pod资源 ports: - port: 6379 #service端口 targetPort: 6379 #后端pod端口 protocol: TCP #协议 [root@k8s-master ~]# kubectl apply -f manfests/redis-svc.yaml #创建资源对象 deployment.apps/redis-deploy created service/redis-svc created
2)查看创建的资源对象
[root@k8s-master ~]# kubectl get svc #查看service资源 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1443/TCP 27d nginx ClusterIP 10.104.116.156 80/TCP 17h redis-svc ClusterIP 10.102.44.127 6379/TCP 8s service-demo ClusterIP 10.98.31.157 80/TCP 16h [root@k8s-master ~]# kubectl get pods -l app=redis -o wide #查看标签app=redis的pod资源 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES redis-deploy-6559cc4c4c-5v7kx 1/1 Running 0 33s 10.244.2.65 k8s-node2 redis-deploy-6559cc4c4c-npdtf 1/1 Running 0 33s 10.244.1.69 k8s-node1 [root@k8s-master ~]# kubectl describe svc redis-svc #查看redis-svc资源对象详细信息 Name: redis-svc Namespace: default Labels: Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"redis-svc","namespace":"default"},"spec":{"ports":[{"port":6379,"... Selector: app=redis Type: ClusterIP IP: 10.102.44.127 Port: 6379/TCP TargetPort: 6379/TCP Endpoints: 10.244.1.69:6379,10.244.2.65:6379 #可以看出这里已经和上面的pod资源绑定 Session Affinity: None Events:
3)集群内部进行测试
#(1)集群内部的节点上面测试 [root@k8s-master ~]# redis-cli -h 10.102.44.127 10.102.44.127:6379> ping PON #(2)在后端pod上面进行测试 [root@k8s-master ~]# kubectl exec redis-deploy-6559cc4c4c-5v7kx -it -- /bin/sh # redis-cli -h 10.102.44.127 10.102.44.127:6379> ping PONG
[root@k8s-master ~]# vim manfests/nginx-svc.yaml #编写yaml格式的清单文件 apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy namespace: default spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx-pod image: nginx:1.12 ports: - name: nginx containerPort: 6379 --- apiVersion: v1 kind: Service metadata: name: nginx-svc #service对象名 spec: type: NodePort #这里指定使用ClusterIP,默认也是ClusterIP,这里可有可无 selector: app: nginx #匹配上面定义的pod资源 ports: - port: 80 #service端口 targetPort: 80 #后端pod端口 nodePort: 30080 #节点端口 protocol: TCP #协议 [root@k8s-master ~]# kubectl apply -f manfests/nginx-svc.yaml #创建资源对象 deployment.apps/nginx-deploy created service/nginx-svc created
2)查看创建的资源对象
[root@k8s-master ~]# kubectl get svc #查看service资源 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1443/TCP 27d nginx-svc NodePort 10.105.21.137 80:30080/TCP 4s redis-svc ClusterIP 10.102.44.127 6379/TCP 55m service-demo ClusterIP 10.98.31.157 80/TCP 16h [root@k8s-master ~]# kubectl get pods -l app=nginx -o wide #查看标签app=nginx的pod资源 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-deploy-b6f876447-nlv6h 1/1 Running 0 33s 10.244.1.71 k8s-node1 nginx-deploy-b6f876447-xmn2t 1/1 Running 0 33s 10.244.2.66 k8s-node2 [root@k8s-master ~]# kubectl describe svc nginx-svc #查看nginx-svc资源对象详细信息 Name: nginx-svc Namespace: default Labels: Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"nginx-svc","namespace":"default"},"spec":{"ports":[{"nodePort":30... Selector: app=nginx Type: NodePort IP: 10.105.21.137 Port: 80/TCP TargetPort: 80/TCP NodePort: 30080/TCP #这里可以看到多了NodePort且端口为30080 Endpoints: 10.244.1.71:80,10.244.2.66:80 Session Affinity: None External Traffic Policy: Cluster Events:
3)集群外部进行测试
[root@courtoap ~]# curl 192.168.1.31:30080 #访问集群master节点的30080端口Welcome to nginx! [root@courtoap ~]# curl 192.168.1.32:30080 #访问集群node节点的30080端口Welcome to nginx!
Service
对象隐藏了各Pod
资源,并负责将客户端请求流量调度至该组Pod
对象之上,但也可能存在客户端直接访问Service
资源后端的所有Pod
资源,这时就应该向客户端暴露每个Pod
资源的IP
地址,而不是中间层Service
对象的ClusterIP
,这种类型的Service
资源便称为Headless Service
(无头服务)。
Headless Service
对象没有ClusterIP
,因此便没有相关负载均衡或代理问题,其如何为此类Service
配置IP
地址,其取决于标签选择器的定义。
具有标签选择器:端点控制器(
Endpoints Controller
)会在API
中为其创建Endpoints
记录,并将ClusterDNS
服务中的A
记录直接解析到此Service
后端的各Pod
对象的IP
地址上。没有标签选择器:端点控制器(
Endpoints Controller
)不会再API
中为其创建Endpoints
记录,ClusterDNS
的配置分为两种情形,对ExternalName
类型的服务创建CNAME
记录,对其他三种类型来说,为那些与当前Service
共享名称的所有Endpoints
对象创建一条记录。
[root@k8s-master ~]# vim manfests/httpd-svc-headless.yaml apiVersion: apps/v1 kind: Deployment metadata: name: httpd-deploy namespace: default spec: replicas: 3 selector: matchLabels: app: httpd template: metadata: labels: app: httpd spec: containers: - name: httpd-pod image: httpd ports: - name: httpd containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: httpd-svc #service对象名 spec: clusterIP: None #将ClusterIP字段设置为None即表示为headless类型的service资源对象 selector: app: httpd #匹配上面定义的pod资源 ports: - port: 80 #service端口 targetPort: 80 #后端pod端口 protocol: TCP #协议 [root@k8s-master ~]# kubectl apply -f manfests/httpd-svc-headless.yaml deployment.apps/httpd-deploy created service/httpd-svc created
2)查看创建的资源对象
[root@k8s-master ~]# kubectl get svc #查看service资源 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE httpd-svc ClusterIP None80/TCP 4s kubernetes ClusterIP 10.96.0.1 443/TCP 27d nginx-svc NodePort 10.105.21.137 80:30080/TCP 112m redis-svc ClusterIP 10.102.44.127 6379/TCP 168m service-demo ClusterIP 10.98.31.157 80/TCP 18h [root@k8s-master ~]# kubectl get pods -l app=httpd -o wide #查看标签app=httpd的pod资源 NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES httpd-deploy-5494485b74-4vx64 1/1 Running 0 27s 10.244.2.72 k8s-node2 httpd-deploy-5494485b74-j6hwm 1/1 Running 0 27s 10.244.2.71 k8s-node2 httpd-deploy-5494485b74-jn48q 1/1 Running 0 27s 10.244.1.74 k8s-node1 [root@k8s-master ~]# kubectl describe svc/httpd-svc #查看httpd-svc资源对象详细信息 Name: httpd-svc Namespace: default Labels: Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"httpd-svc","namespace":"default"},"spec":{"clusterIP":"None","por... Selector: app=httpd Type: ClusterIP IP: None Port: 80/TCP TargetPort: 80/TCP Endpoints: 10.244.1.74:80,10.244.2.71:80,10.244.2.72:80 Session Affinity: None Events:
3)测试资源发现
#(1)通过创建一个专用的测试Pod资源对象,而后通过其交互式接口进行测试 [root@k8s-master ~]# kubectl run cirror-$RANDOM --rm -it --image=cirros -- /bin/sh / # nslookup httpd-svc Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: httpd-svc Address 1: 10.244.2.71 10-244-2-71.httpd-svc.default.svc.cluster.local Address 2: 10.244.1.74 10-244-1-74.httpd-svc.default.svc.cluster.local Address 3: 10.244.2.72 10-244-2-72.httpd-svc.default.svc.cluster.local #(2)直接在kubernetes集群上解析 [root@k8s-master ~]# dig -t A httpd-svc.default.svc.cluster.local. @10.96.0.10 ...... ;; ANSWER SECTION: httpd-svc.default.svc.cluster.local. 26 IN A 10.244.2.72 httpd-svc.default.svc.cluster.local. 26 IN A 10.244.2.71 httpd-svc.default.svc.cluster.local. 26 IN A 10.244.1.74 ......