K8s的Service资源是一种为一组功能相同的Pod提供统一不变的流量入口的资源。这里介绍两种典型的Service类型:ClusterIP Service、NodePort Service
ClusterIP Service
ClusterIP Service通过集群的内部IP暴露服务,使得服务只能够在集群内部被访问。这也是默认Service类型。下面我们通过RS创建一组Pod,然后通过ClusterIP Service统一流量入口。使得无论RS的Pod无论怎么变化,都可以被访问到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| apiVersion: apps/v1
kind: ReplicaSet metadata: name: my-kubia-rs-1 spec: replicas: 3 selector: matchLabels: app: kubia template: metadata: labels: app: kubia spec: containers: - name: my-kubia-1 image: luksa/kubia ports: - containerPort: 8080 protocol: TCP
---
apiVersion: v1
kind: Service metadata: name: kubia-service spec: type: ClusterIP selector: app: kubia ports: - port: 9696 targetPort: 8080
|
效果如下所示
由于集群内各Pod之间的网络是互通的,故我们可以先通过Pod的IP、Port来访问验证下Pod是否可以正常工作。效果如下所示
现在我们尝试通过服务的IP、Port进行访问,效果如下所示。不难看出服务同时具有负载均衡的功能了
此外,还可以通过sessionAffinity配置服务的会话亲和性。当配置为ClientIP时,其可以保证将来自相同客户端IP的请求总是转发至同一个Pod中
1 2 3 4 5
| apiVersion: v1 kind: Service spec: sessionAffinity: ClientIP
|
需要注意的是,当Service暴露多个端口时,需要给每个端口指定名字。如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13
| apiVersion: v1 kind: Service spec: ports: - name: http-port port: 80 targetPort: 3080 - name: https-port port: 443 targetPort: 3443 - name: test-port port: 69 targetPort: 3069
|
NodePort Service
该类型的服务类似于ClusterIP Service,集群内部的Pod可以直接通过该服务的IP、Port来访问服务。不同之处在于该NodePort Service还会在K8s集群中每个节点打开一个端口,同时将节点上该端口的流量进行转发重定向到该服务当中。这也是该服务类型名称的由来。此时一方面,Pod就可以通过任意节点的IP、NodePort Service在集群该节点打开的端口来访问访问服务;另一方面,如果K8s集群外的客户端可以访问K8s集群节点的话,就可以实现将K8s中的服务暴露给外部客户端了。而不仅仅是集群内的Pod才能访问了。示例配置文件如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| apiVersion: apps/v1
kind: ReplicaSet metadata: name: my-kubia-rs-1 spec: replicas: 3 selector: matchLabels: app: kubia template: metadata: labels: app: kubia spec: containers: - name: my-kubia-1 image: luksa/kubia ports: - containerPort: 8080 protocol: TCP
---
apiVersion: v1
kind: Service metadata: name: my-nodeport-service-1 spec: type: NodePort selector: app: kubia ports: - port: 80 targetPort: 8080 nodePort: 30123
|
效果如下所示,我们可以直接通过服务的集群IP、端口访问服务。这点与ClusterIP Service并无二异
现在我们尝试通过集群节点IP、NodePort Service在节点上打开的端口来访问服务。效果如下所示
服务发现
环境变量
当服务先创建、Pod后创建时,在创建Pod过程中会把活跃Service的IP、Port信息写入该Pod的环境变量当中。具体地,服务IP、Port的环境变量名分别为{ServiceName}_SERVICE_HOST、{ServiceName}_SERVICE_PORT。其中,ServiceName是服务名称的大写,服务名称中的横线被转换成下划线
1 2
| kubectl exec <Pod的名称> env
|
效果如下所示。这样集群内的Pod就可以通过环境变量获取服务的IP、Port。但如果先创建Pod、再创建服务的话,此时Pod当中就不会存在后续所创建服务的环境变量了
DNS
集群中的Pod还可以通过服务FQDN全限定域名作为服务的IP进行访问。FQDN的名称规则为..svc.cluster.local。其中FQDN名称中的service-name表示服务名称,namespace表示命名空间的名称,svc.cluster.local表示所有集群本地服务名称中使用的可配置集群域后缀。例如,default命名空间下的kubia-service服务,其FQDN名称为kubia-service.default.svc.cluster.local。甚至我们可以直接用FQDN的前半部分——.来作为服务的IP。效果如下所示
Note
Service的集群IP是一个虚拟IP,故无法ping通是正常的
参考文献
- Kubernetes in Action中文版 Marko Luksa著
- 深入剖析Kubernetes 张磊著