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
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
spec:
#clusterIP:指定该Service使用的IP地址,如果不填写表示自动分配,该地址为安装集群的时候指定的clusterIP地址端10.99.0.0/24,而不是写入etcd里面写入的flannel地址
clusterIP: 10.99.97.97
#selector:定义标签选择器,键值为app:myapp的Pod都会被关联至该Service中
selector:
app: myapp
#ports:定义需要暴漏的服务端口,protocol表示协议,prot表示Service的端口,targetPort表示Pod的端口
ports:
- protocol: TCP
name: http
port: 80
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资源进行定义
[root@k8s-master01 test]# kubectl apply -f myapp-svc.yaml
service/myapp-svc created
[root@k8s-master01 test]#
[root@k8s-master01 test]# kubectl get svc myapp-svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myapp-svc ClusterIP 10.99.97.97 <none> 80/TCP 29s
[root@k8s-master01 test]#
[root@k8s-master01 test]# kubectl describe svc myapp-svc
Name: myapp-svc
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp-svc","namespace":"default"},"spec":{"clusterIP":"10.99.97.9...
Selector: app=myapp
Type: ClusterIP
IP: 10.99.97.97
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: <none>
Session Affinity: None
Events: <none>
[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
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: my-nginx
image: docker.io/ikubernetes/myapp:v2
ports:
- containerPort: 80
查看endpoints资源的端点列表
[root@k8s-master01 test]# kubectl apply -f myapp.yaml
deployment.apps/myapp created
[root@k8s-master01 test]#
[root@k8s-master01 test]# kubectl apply -f myapp.yaml --record
deployment.apps/myapp created
[root@k8s-master01 test]#
[root@k8s-master01 test]# kubectl get endpoints myapp-svc
NAME ENDPOINTS AGE
myapp-svc 10.244.60.2:80,10.244.60.3:80,10.244.91.2:80 11m
[root@k8s-master01 test]#
[root@k8s-master01 test]# kubectl get pods -l app=myapp -o wide -L app
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES APP
myapp-8858448d-j4r5t 1/1 Running 0 45s 10.244.60.3 192.168.1.9 <none> <none> myapp
myapp-8858448d-pmt57 1/1 Running 0 45s 10.244.91.2 192.168.1.10 <none> <none> myapp
myapp-8858448d-x6sg4 1/1 Running 0 45s 10.244.60.2 192.168.1.9 <none> <none> myapp
[root@k8s-master01 test]#
Service资源对象创建完成后可作为服务被客户端访问,但要真正响应这些请求,还是需要依赖于各后端的资源对象
向Service对象发送请求
Service资源的默认类型为ClusterIP,它仅能接收来自于集群各工作节点当中的Pod对象中的客户端程序的访问请。下面,创建一个专用的Pod对象,利用其交互式接口访问测试,我们直接使用cirros当客户端进行访问测试,cirros是设计用来进行云计算测试环境的Linux微型发行版,它拥有HTTP客户端工具和curl等
[root@k8s-master01 test]# kubectl run cirros-$RANDOM --rm -it --image=cirros -- sh
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.
If you don't see a command prompt, try pressing enter.
/ #
/ # curl http://10.99.97.97
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
/ # / # for loop in 1 2 3 4;do curl http://10.99.97.97/hostname.html;done
myapp-8858448d-j4r5t
myapp-8858448d-pmt57
myapp-8858448d-x6sg4
myapp-8858448d-j4r5t
/ #
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
用于配置会话保持的时长,它是一个嵌套字段,使用格式如下
spec
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: <integer>
多端口的Service资源
对于某些服务来讲,可能需要暴漏多个端口,Service资源支持暴漏多个端口。为服务使用多端口时,必须提供端口的名称,并且需要确保它们的名称不会重复
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
spec:
#clusterIP:指定该Service使用的IP地址,如果不填写表示自动分配,该地址为安装集群的时候指定的clusterIP地址端10.99.0.0/24,而不是写入etcd里面写入的flannel地址
clusterIP: 10.99.97.97
#selector:定义标签选择器,键值为app:myapp的Pod都会被关联至该Service中
selector:
app: myapp
#ports:定义需要暴漏的服务端口,protocol表示协议,prot表示Service的端口,targetPort表示Pod的端口
ports:
#需要暴露的端口1
- protocol: TCP
name: http
port: 80
targetPort: 80
#需要暴露的端口2
- protocol: TCP
name: https
port: 443
targetPort: 443
查看443端口是否暴漏
[root@k8s-master01 nginx]# kubectl apply -f myapp-svc.yaml
service/myapp-svc configured
[root@k8s-master01 nginx]#
[root@k8s-master01 nginx]# kubectl get svc myapp-svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
myapp-svc ClusterIP 10.99.97.97 <none> 80/TCP,443/TCP 5h21m app=myapp
[root@k8s-master01 nginx]#
通过上述命令结果可以看到,service资源一共暴漏了两个端口,一个80一个443端口,这时候如果后端的Pod监听了443端口,我们通过其他的Pod客户端发起请求,也是能够正常访问的