Service资源的基础应用

Service资源本身并不提供任何服务,真正处理并相应客户端请求的是后端的Pod资源,这些Pod资源通常由前面介绍的各类控制器对象所创建和管理,因此Service资源通常需要与控制器资源(最为常用的控制器之一是Deployment)协同使用以完成应用的创建和对外发布

创建Service资源

创建Service对象的常用方法有两种:一种是直接使用kubectl expose命令,这个在前面有讲到过。另一个是使用资源配置文件,它与此前使用资源清单文件配置其他资源的方法类似。定义Service资源对象时,spec字段有两个较为常用的内嵌字段,分别是selector和ports,分别用于定义使用的标签选择器和要暴漏的端口。.spec.clusterIP用于指定Service资源的固定IP地址,用户选择的IP地址必须合法,并且这个IP地址在service-cluster-rangeCIDR范围内,这对 API Server 来说是通过一个标识来指定的。 如果 IP 地址不合法,API Server 会返回 HTTP 状态码 422,表示值不合法,该字段为不是必选字段,如果不填写,则会自动分配一个IP地址。下面的配置清单是一个Service资源示例,我们将其保存为myapp-svc.yaml

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: myapp-svc
  5. spec:
  6. #clusterIP:指定该Service使用的IP地址,如果不填写表示自动分配,该地址为安装集群的时候指定的clusterIP地址端10.99.0.0/24,而不是写入etcd里面写入的flannel地址
  7. clusterIP: 10.99.97.97
  8. #selector:定义标签选择器,键值为app:myapp的Pod都会被关联至该Service中
  9. selector:
  10. app: myapp
  11. #ports:定义需要暴漏的服务端口,protocol表示协议,prot表示Service的端口,targetPort表示Pod的端口
  12. ports:
  13. - protocol: TCP
  14. name: http
  15. port: 80
  16. targetPort: 80

Service资源myapp-svc通过标签选择器关联至标签为”app=myapp”的各Pod对象,它会自动创建名为myapp-svc的endpoints资源对象,如果ClusterIP字段没有填写,则会自动配置一个clusterIP,暴漏的端口由Ports字段进行指定,后端各Pod对象的端口则由targetPort指定,也可以使用同port字段的默认值 如果不指定.spec.cluster字段,则会自动创建一个clusterIP 也可以不为sevice资源指定.spec.selector属性值,其关联的Pod资源可由用户手动创建endpoints资源进行定义

  1. [root@k8s-master01 test]# kubectl apply -f myapp-svc.yaml
  2. service/myapp-svc created
  3. [root@k8s-master01 test]#
  4. [root@k8s-master01 test]# kubectl get svc myapp-svc
  5. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  6. myapp-svc ClusterIP 10.99.97.97 <none> 80/TCP 29s
  7. [root@k8s-master01 test]#
  8. [root@k8s-master01 test]# kubectl describe svc myapp-svc
  9. Name: myapp-svc
  10. Namespace: default
  11. Labels: <none>
  12. Annotations: kubectl.kubernetes.io/last-applied-configuration:
  13. {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp-svc","namespace":"default"},"spec":{"clusterIP":"10.99.97.9...
  14. Selector: app=myapp
  15. Type: ClusterIP
  16. IP: 10.99.97.97
  17. Port: <unset> 80/TCP
  18. TargetPort: 80/TCP
  19. Endpoints: <none>
  20. Session Affinity: None
  21. Events: <none>
  22. [root@k8s-master01 test]#

上面命令中的结果显示,myapp-svc的类型为ClusterIP,其使用的IP地址是我们手动指定的10.99.97.97。此类型的service对象仅能通过此IP地址接受来自于集群内工作节点当中的Pod资源对象的请求。若集群上存在标签为”app=myapp”的Pod资源,则他们会被关联和创建,作为此service对象的后端endpoints对象,并负责接受相应的请求流量,类似下面的命令,我们创建一个myapp的deployment控制器,并且Pod标签为app=myapp,然后获取相关的endpoint对象 新建一个Pod资源的清单,并且标签选择器为app=myapp

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: myapp
  5. spec:
  6. replicas: 3
  7. selector:
  8. matchLabels:
  9. app: myapp
  10. template:
  11. metadata:
  12. labels:
  13. app: myapp
  14. spec:
  15. containers:
  16. - name: my-nginx
  17. image: docker.io/ikubernetes/myapp:v2
  18. ports:
  19. - containerPort: 80

查看endpoints资源的端点列表

  1. [root@k8s-master01 test]# kubectl apply -f myapp.yaml
  2. deployment.apps/myapp created
  3. [root@k8s-master01 test]#
  4. [root@k8s-master01 test]# kubectl apply -f myapp.yaml --record
  5. deployment.apps/myapp created
  6. [root@k8s-master01 test]#
  7. [root@k8s-master01 test]# kubectl get endpoints myapp-svc
  8. NAME ENDPOINTS AGE
  9. myapp-svc 10.244.60.2:80,10.244.60.3:80,10.244.91.2:80 11m
  10. [root@k8s-master01 test]#
  11. [root@k8s-master01 test]# kubectl get pods -l app=myapp -o wide -L app
  12. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES APP
  13. myapp-8858448d-j4r5t 1/1 Running 0 45s 10.244.60.3 192.168.1.9 <none> <none> myapp
  14. myapp-8858448d-pmt57 1/1 Running 0 45s 10.244.91.2 192.168.1.10 <none> <none> myapp
  15. myapp-8858448d-x6sg4 1/1 Running 0 45s 10.244.60.2 192.168.1.9 <none> <none> myapp
  16. [root@k8s-master01 test]#

Service资源对象创建完成后可作为服务被客户端访问,但要真正响应这些请求,还是需要依赖于各后端的资源对象

向Service对象发送请求

Service资源的默认类型为ClusterIP,它仅能接收来自于集群各工作节点当中的Pod对象中的客户端程序的访问请。下面,创建一个专用的Pod对象,利用其交互式接口访问测试,我们直接使用cirros当客户端进行访问测试,cirros是设计用来进行云计算测试环境的Linux微型发行版,它拥有HTTP客户端工具和curl等

  1. [root@k8s-master01 test]# kubectl run cirros-$RANDOM --rm -it --image=cirros -- sh
  2. kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
  3. If you don't see a command prompt, try pressing enter.
  4. / #
  5. / # curl http://10.99.97.97
  6. Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
  7. / # / # for loop in 1 2 3 4;do curl http://10.99.97.97/hostname.html;done
  8. myapp-8858448d-j4r5t
  9. myapp-8858448d-pmt57
  10. myapp-8858448d-x6sg4
  11. myapp-8858448d-j4r5t
  12. / #

Service会话粘性

Service资源还吃Session Affinity(会话粘性)机制,它能够将来自同一个客户端的请求始终转发至同一个后端的Pod对象,这意味着它会影响调度算法的流量分发功用,进而降低其负载均衡的效果。因此,当客户端访问Pod中的应用程序时,如果有基于客户端身份保存某些私有信息,并基于这些私有信息追踪用户的活动等一类的需求时,那么应该启用session affinity机制 Session Affinity的效果仅会在一定时间期限内生效,默认值是10800秒,超出此时长之后,客户端的再次访问呢会被调度算法重新调度。另外Service资源的Session Affinity机制仅能基于客户端IP地址识别客户端身份,它会把经由同一个NAT服务器进行源地址转换的所有客户端识别为同一个客户端,调度粒度粗糙且效果不佳,因此,实践中并不推荐使用此种方式实现粘性会话 spec.sessionAffinity字段用于定义要使用的粘性会话的类型,它仅支持使用”None”和”ClientIP”两种属性

  • None:表示不使用sessionAffinity,默认值
  • ClientIP:表示基于客户端IP地址识别客户端身份,把来自同一个源IP地址的请求始终调度至同一个Pod
    在启用会话粘性机制时,.spec.sessionAffinityConfig用于配置会话保持的时长,它是一个嵌套字段,使用格式如下
  1. spec
  2. sessionAffinity: ClientIP
  3. sessionAffinityConfig:
  4. clientIP:
  5. timeoutSeconds: <integer>

多端口的Service资源

对于某些服务来讲,可能需要暴漏多个端口,Service资源支持暴漏多个端口。为服务使用多端口时,必须提供端口的名称,并且需要确保它们的名称不会重复

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: myapp-svc
  5. spec:
  6. #clusterIP:指定该Service使用的IP地址,如果不填写表示自动分配,该地址为安装集群的时候指定的clusterIP地址端10.99.0.0/24,而不是写入etcd里面写入的flannel地址
  7. clusterIP: 10.99.97.97
  8. #selector:定义标签选择器,键值为app:myapp的Pod都会被关联至该Service中
  9. selector:
  10. app: myapp
  11. #ports:定义需要暴漏的服务端口,protocol表示协议,prot表示Service的端口,targetPort表示Pod的端口
  12. ports:
  13. #需要暴露的端口1
  14. - protocol: TCP
  15. name: http
  16. port: 80
  17. targetPort: 80
  18. #需要暴露的端口2
  19. - protocol: TCP
  20. name: https
  21. port: 443
  22. targetPort: 443

查看443端口是否暴漏

  1. [root@k8s-master01 nginx]# kubectl apply -f myapp-svc.yaml
  2. service/myapp-svc configured
  3. [root@k8s-master01 nginx]#
  4. [root@k8s-master01 nginx]# kubectl get svc myapp-svc -o wide
  5. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
  6. myapp-svc ClusterIP 10.99.97.97 <none> 80/TCP,443/TCP 5h21m app=myapp
  7. [root@k8s-master01 nginx]#

通过上述命令结果可以看到,service资源一共暴漏了两个端口,一个80一个443端口,这时候如果后端的Pod监听了443端口,我们通过其他的Pod客户端发起请求,也是能够正常访问的