直接通过Pod的IP地址和端口号可以访问到容器应用内的服务,但是Pod的IP地址是不可靠的,例如当Pod所在的Node发生故障时,Pod将被Kubernetes重新调度到另一个Node,Pod的IP地址将发生变化。
如果容器应用本身是分布式的部署方式,通过多个实例共同提供服务,就需要在这些实例的前端设置一个负载均衡器来实现请求的分发。Kubernetes中的Service就是用于解决这些问题的核心组件。
Service示例:以如上webapp应用为例,为了让客户端应用访问到两个Tomcat Pod实例,可以创建一个Service来提供服务。Kubernetes提供了一种快速的方法,即通过kubectl expose命令来创建Service。
[root@k8smaster01 study]# kubectl expose rc webapp
[root@k8smaster01 study]# kubectl get svc | grep -E 'NAME|webapp'
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webapp ClusterIP 10.10.10.51 <none> 8080/TCP 45s
[root@k8smaster01 study]# curl 10.10.10.51:8080 #测试访问
提示:如上Service地址10.10.10.51:8080的访问被自动负载分发到后端两个Pod。
Service示例2:通过Service配置文件暴露服务。
[root@k8smaster01 study]# vi webappsvc.yaml
[HTML] 纯文本查看 复制代码
apiVersion: v1
kind: Service
metadata:
name: webappservice
spec:
ports:
- port: 8081
targetPort: 8080
selector:
app: webapp
[root@k8smaster01 study]# kubectl create -f webappsvc.yaml
[root@k8smaster01 study]# kubectl get svc | grep -E 'NAME|webappser'
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webappservice ClusterIP 10.10.10.83 <none> 8081/TCP 22s
提示:如上Service定义中的关键字段是ports和selector。本例中ports定义部分指定了Service所需的虚拟端口号为8081,由于与Pod容器端口号8080不一样,所以需要再通过targetPort来指定后端Pod的端口号。selector定义部分设置的是后端Pod所拥有的label:app=webapp。
[root@k8smaster01 study]# curl 10.10.10.83:8081 #访问测试
- Service负载分发策略:RoundRobin和SessionAffinity
- RoundRobin:轮询模式,即轮询将请求转发到后端的各个Pod上。
- SessionAffinity:基于客户端IP地址进行会话保持的模式,即第1次将某个客户端发起的请求转发到后端的某个Pod上,之后从相同的客户端发起的请求都将被转发到后端相同的Pod上。
在默认情况下,Kubernetes采用RoundRobin模式对客户端请求进行负载分发,同时可以通过设置service.spec.sessionAffinity=ClientIP来启用SessionAffinity策略。这样,同一个客户端IP发来的请求就会被转发到后端固定的某个Pod上了。
通过Service的定义,Kubernetes实现了一种分布式应用统一入口的定义和负载均衡机制。Service还可以进行其他类型的设置,例如设置多个端口号、直接设置为集群外部服务,或实现为Headless Service(无头服务)模式。
2.2 多端口Service有时一个容器应用也可能提供多个端口的服务,那么在Service的定义中也可以相应地设置为将多个端口对应到多个应用服务。
示例1:如下,Service设置了两个端口号,并且为每个端口号都进行了命名。
[root@k8smaster01 study]# vi twoportservice.yaml
[HTML] 纯文本查看 复制代码
apiVersion: v1
kind: Service
metadata:
name: webapp[/align][align=left]spec:
ports:
- port: 8080
targetPort: 8080
name: web
- port: 8005
targetPort: 8005
name: management
selector:
app: webapp
[root@k8smaster01 study]# vi kubednsservice.yaml
[HTML] 纯文本查看 复制代码
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "KubeDNS"
spec:
selector:
k8s-app: kube-dns
clusterIP: 169.169.0.100
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
2.3 外部服务Service在某些环境中,应用系统需要将一个外部数据库、另一个集群或Namespace中的服务作为服务的后端,则可通过创建一个无Label Selector的Service来实现。
[root@k8smaster01 study]# vi noselectorservice.yaml
[HTML] 纯文本查看 复制代码
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
[root@k8smaster01 study]# kubectl create -f noselectorservice.yaml
如上定义创建的是一个不带标签选择器的Service,即无法选择后端的Pod,系统不会自动创建Endpoint,因此需要手动创建一个和该Service对应的Endpoint,用于指向实际的后端访问地址。
如下所示的Endpoint的定义文件:
[root@k8smaster01 study]# vi noselectorendpoint.yaml
[HTML] 纯文本查看 复制代码
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
subsets:
- addresses:
- IP: 47.96.145.131
ports:
- port: 80
[root@k8smaster01 study]# kubectl create -f noselectorendpoint.yaml
[root@k8smaster01 study]# kubectl get svc | grep -E 'NAME|my-service'
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service ClusterIP 10.10.10.211 <none> 80/TCP 3s
[root@k8smaster01 study]# curl 10.10.10.211
提示:如上所示,访问没有标签选择器的Service和带有标签选择器的Service一样,请求将会被路由到由用户手动定义的后端Endpoint上。
三 Headless Service3.1 无头服务简介在某些应用场景中,若需要人为指定负载均衡器,不使用Service提供的默认负载均衡的功能,或者应用程序希望知道属于同组服务的其他实例。Kubernetes提供了Headless Service来实现这种功能,即不为Service设置ClusterIP(入口IP地址),仅通过Label Selector将后端的Pod列表返回给调用的客户端。
此场景中,Service就不再具有一个特定的ClusterIP地址,对其进行访问将获得包含Label“app=nginx”的全部Pod列表,然后客户端程序自行决定如何处理这个Pod列表。
例如,StatefulSet就是使用Headless Service为客户端返回多个服务地址的。
对于“去中心化”类的应用集群,Headless Service非常适合。
3.2 Nginx场景实验通过对Headless Service搭建Nginx集群,从而自动实现应用集群的创建。
[root@k8smaster01 study]# vi nginx-service.yaml #创建Service
[HTML] 纯文本查看 复制代码
apiVersion: v1
kind: Service
metadata:
labels:
name: nginx-svc
name: nginx-svc
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
name: nginx-demo #定义selector
clusterIP: None
[root@k8smaster01 study]# kubectl create -f nginx-service.yaml
[root@k8smaster01 study]# vi nginx-deployment.yaml
[HTML] 纯文本查看 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
name: nginx-demo
name: nginx-demo
spec:
replicas: 2
selector:
matchLabels:
name: nginx-demo
template:
metadata:
labels:
name: nginx-demo
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
name: web
[root@k8smaster01 study]# kubectl create -f nginx-deployment.yaml
[root@k8smaster01 study]# kubectl create -f nginx-service.yaml
[root@k8smaster01 study]# kubectl get svc -o wide
[root@k8smaster01 study]# kubectl get pods -o wide
[root@k8smaster01 study]# nslookup nginx-svc.default.svc.cluster.local 10.10.190.170
提示:由上可知,通过解析SVC的地址,直接解析出来的为Pod的IP。