第6章 通过Service访问Pod
我们不应该期望Kubernetes Pod是健壮的,而是要假设Pod中的容器很可能因为各种原因发生故障而死掉。Deployment等Controller会通过动态创建和销毁Pod来保证应用整体的健壮性。换句话说,Pod是脆弱的,但应用是健壮的。
每个Pod都有自己的IP地址。当Controller用新Pod替代发生故障的Pod时,新Pod会分配到新的IP地址。这样就产生了一个问题:
如果一组Pod对外提供服务(比如HTTP),它们的IP很有可能发生变化,那么客户端如何找到并访问这个服务呢?
Kubernetes给出的解决方案是Service。
6.1 创建Service
Kubernetes Service从逻辑上代表了一组Pod,具体是哪些Pod则是由label来挑选的。Service有自己的IP,而且这个IP是不变的。客户端只需要访问Service的IP,Kubernetes则负责建立和维护Service与Pod的映射关系。无论后端Pod如何变化,对客户端不会有任何影响,因为Service没有变。
来看个例子,创建下面的这个Deployment,如图6-1所示。

图6-1
我们启动了三个Pod,运行httpd镜像,label是run: httpd,Service将会用这个label来挑选Pod,如图6-2所示。

图6-2
Pod分配了各自的IP,这些IP只能被Kubernetes Cluster中的容器和节点访问,如图6-3所示。

图6-3
接下来创建Service,其配置文件如图6-4所示。

图6-4
① v1是Service的apiVersion。
② 指明当前资源的类型为Service。
③ Service的名字为httpd-svc。
④ selector指明挑选那些label为run: httpd的Pod作为Service的后端。
⑤ 将Service的8080端口映射到Pod的80端口,使用TCP协议。
执行kubectl apply创建Service httpd-svc,如图6-5所示。

图6-5
httpd-svc分配到一个CLUSTER-IP 10.99.229.179。可以通过该IP访问后端的httpd Pod,如图6-6所示。

图6-6
根据前面的端口映射,这里要使用8080端口。另外,除了我们创建的httpd-svc,还有一个Service kubernetes,Cluster内部通过这个Service访问Kubernetes API Server。
通过kubectl describe可以查看httpd-svc与Pod的对应关系,如图6-7所示。

图6-7
Endpoints罗列了三个Pod的IP和端口。我们知道Pod的IP是在容器中配置的,那么Service的Cluster IP又是配置在哪里的呢?CLUSTER-IP又是如何映射到Pod IP的呢?
答案是iptables。
6.2 Cluster IP底层实现
Cluster IP是一个虚拟IP,是由Kubernetes节点上的iptables规则管理的。
可以通过iptables-save命令打印出当前节点的iptables规则,因为输出较多,这里只截取与httpd-svc Cluster IP 10.99.229.179相关的信息,如图6-8所示。

图6-8
这两条规则的含义是:
(1)如果Cluster内的Pod(源地址来自10.244.0.0/16)要访问httpd-svc,则允许。
(2)其他源地址访问httpd-svc,跳转到规则KUBE-SVC-RL3JAE4GN7VOG DGP。KUBE-SVC-RL3JAE4GN7VOGDGP规则如图6-9所示。
-A KUBE-SVC-RL3JAE4GN7VOGDGP –m comment –comment "default/httpd-svc: " –m statistic –mode random –probability 0.33332999982 –j KUBE-SEP-C5KB52P4BBJQ35PH

图6-9
(1)1/3的概率跳转到规则KUBE-SEP-C5KB52P4BBJQ35PH。
(2)1/3的概率(剩下2/3的一半)跳转到规则KUBE-SEP-HGVKQQZZCF7RV4IT。
(3)1/3的概率跳转到规则KUBE-SEP-XE25WGVXLHEIRVO5。
上面三个跳转的规则如图6-10所示。

图6-10
即将请求分别转发到后端的三个Pod。通过上面的分析,我们得到结论:iptables将访问Service的流量转发到后端Pod,而且使用类似轮询的负载均衡策略。
另外,需要补充一点:Cluster的每一个节点都配置了相同的iptables规则,这样就确保了整个Cluster都能够通过Service的Cluster IP访问Service,如图6-11所示。

图6-11
6.3 DNS访问Service
在Cluster中,除了可以通过Cluster IP访问Service,Kubernetes还提供了更为方便的DNS访问。
kubeadm部署时会默认安装kube-dns组件,如图6-12所示。

图6-12
kube-dns是一个DNS服务器。每当有新的Service被创建,kube-dns会添加该Service的DNS记录。Cluster中的Pod可以通过<SERVICE_NAME>.<NAMESPACE_NAME>访问Service。
比如可以用httpd-svc.default访问Service httpd-svc,如图6-13所示。

图6-13
如上所示,我们在一个临时的busybox Pod中验证了DNS的有效性。另外,由于这个Pod与httpd-svc同属于default namespace,因此可以省略default直接用httpd-svc访问Service,如图6-14所示。

图6-14
用nslookup查看httpd-svc的DNS信息,如图6-15所示。

图6-15
DNS服务器是kube-dns.kube-system.svc.cluster.local,这实际上就是kube-dns组件,它本身是部署在kube-system namespace中的一个Service。
httpd-svc.default.svc.cluster.local是httpd-svc的完整域名。
如果要访问其他namespace中的Service,就必须带上namesapce了。kubectl get namespace查看已有的namespace,如图6-16所示。

图6-16
在kube-public中部署Service httpd2-svc,配置如图6-17所示。

图6-17
通过namespace: kube-public指定资源所属的namespace。多个资源可以在一个YAML文件中定义,用“---”分割。执行kubectl apply创建资源,如图6-18所示。

图6-18
查看kube-public的Service,如图6-19所示。

图6-19
在busybox Pod中访问httpd2-svc,如图6-20所示。

图6-20
因为不属于同一个namespace,所以必须使用httpd2-svc.kube-public才能访问到。
6.4 外网如何访问Service
除了Cluster内部可以访问Service,很多情况下我们也希望应用的Service能够暴露给Cluster外部。Kubernetes提供了多种类型的Service,默认是ClusterIP。
(1)ClusterIP
Service通过Cluster内部的IP对外提供服务,只有Cluster内的节点和Pod可访问,这是默认的Service类型,前面实验中的Service都是ClusterIP。
(2)NodePort
Service通过Cluster节点的静态端口对外提供服务。Cluster外部可以通过<NodeIP>:<NodePort>访问Service。
(3)LoadBalancer
Service利用cloud provider特有的load balancer对外提供服务,cloud provider负责将load balancer的流量导向Service。目前支持的cloud provider有GCP、AWS、Azur等。
下面我们来实践NodePort,Service httpd-svc的配置文件修改如图6-21所示。

图6-21
添加type: NodePort,重新创建httpd-svc,如图6-22所示。

图6-22
Kubernetes依然会为httpd-svc分配一个ClusterIP,不同的是:
(1)EXTERNAL-IP为nodes,表示可通过Cluster每个节点自身的IP访问Service。
(2)PORT(S)为8080:32312。8080是ClusterIP监听的端口,32312则是节点上监听的端口。Kubernetes会从30000~32767中分配一个可用的端口,每个节点都会监听此端口并将请求转发给Service,如图6-23所示。

图6-23
下面测试NodePort是否正常工作,如图6-24所示。

图6-24
通过三个节点IP+32312端口都能够访问httpd-svc。
接下来我们深入探讨一个问题:Kubernetes是如何将<NodeIP>:<NodePort>映射到Pod的呢?
与ClusterIP一样,也是借助了iptables。与ClusterIP相比,每个节点的iptables中都增加了下面两条规则,如图6-25所示。

图6-25
规则的含义是:访问当前节点32312端口的请求会应用规则KUBE-SVC-RL3JAE4GN7VOGDGP,内容如图6-26所示。

图6-26
其作用就是负载均衡到每一个Pod。
NodePort默认的是随机选择,不过我们可以用nodePort指定某个特定端口,如图6-27所示。

图6-27
现在配置文件中就有三个Port了:
最终,Node和ClusterIP在各自端口上接收到的请求都会通过iptables转发到Pod的targetPort。
应用新的nodePort并验证,如图6-28所示。

图6-28
nodePort: 30000已经生效了。
6.5 小结
本章我们讨论访问应用的机制Service,学习了如何创建Service,Service的三种类型ClusterIP、NodePort和LoadBalancer,以及它们各自的适用场景。