一、基于 iptables 的 Service 实现
Pod的ip地址不是固定了。Service通过selector属性和后端Pod关联,被selector选中的Pod被称为Service的Endpoint。通过该Service的VIP地址就可以访问到它所代理的Pod。VIP地址是kubernetes自动为Service分配的。
Service 是由 kube-proxy 组件,加上iptables 来共同实现的。
一旦创建了Service,那么kube-proxy就可以通过Service的Informer感知到Service的添加,而作为这个事件的响应,他就会在宿主机上创建一条iptables规则,为这个Service设置一个固定的入口地址。由于它只是一条iptables规则上的配置,并没有真正的网络设备,所以ping这个地址,是没有任何反应的。
上面会路由到一组规则的集合,它实际上是一组随机模式的iptables链。转发的最终目的地就是Service代理的Pod,所以这组规则就是Service实现负载均衡的位置。
这组规则实际上是DNAT规则。DNAT规则的作用是在路由之前将流入的ip地址的目标地址和端口转为-to0destination所指定的新的目的地址和端口,这个目的地址和端口正是被代理的pod的ip地址和端口。
所以,访问Service VIP的ip包经过上述的iptables处理之后,就已经变成了访问具体某一个后端pod的ip包了。这些endpoint对应的iptables规则,正是kube-proxy通过监听pod的变化事件在宿主机上生成并维护的。
缺点:会产生大量的iptables规则,而且这些iptables规则需要被不断的刷新
二、IPVS 模式的 Service
IPVS 模式的工作原理,其实跟 iptables 模式类似。当我们创建了前面的 Service 之后,kube-proxy 首先会在宿主机上创建一个虚拟网卡(叫作:kube-ipvs0),并为它分配 Service VIP 作为 IP 地址,如下所示:
而接下来,kube-proxy 就会通过 Linux 的 IPVS 模块,为这个 IP 地址设置三个 IPVS 虚拟主机(实际上就是被代理的pod,这里的例子是三个),并设置这三个虚拟主机之间使用轮询模式 (rr) 来作为负载均衡策略。我们可以通过 ipvsadm 查看到这个设置,如下所示:
可以看到,这三个 IPVS 虚拟主机的 IP 地址和端口,对应的正是三个被代理的 Pod。
这时候,任何发往 10.102.128.4:80 的请求,就都会被 IPVS 模块转发到某一个后端 Pod 上了。
而相比于 iptables,IPVS 在内核中的实现其实也是基于 Netfilter 的 NAT 模式,所以在转发这一层上,理论上 IPVS 并没有显著的性能提升。但是,IPVS 并不需要在宿主机上为每个 Pod 设置 iptables 规则,而是把对这些“规则”的处理放到了内核态,从而极大地降低了维护这些规则的代价。“将重要操作放入内核态”是提高性能的重要手段。
不过需要注意的是,IPVS 模块只负责上述的负载均衡和代理功能。而一个完整的 Service 流程正常工作所需要的包过滤、SNAT 等操作,还是要靠 iptables 来实现。只不过,这些辅助性的 iptables 规则数量有限,也不会随着 Pod 数量的增加而增加。
三、Service 与 DNS 的关系。
在 Kubernetes 中,Service 和 Pod 都会被分配对应的 DNS A 记录(从域名解析 IP 的记录)。
对于 ClusterIP 模式的 Service 来说(比如我们上面的例子),它的 A 记录的格式是:..svc.cluster.local。当你访问这条 A 记录的时候,它解析到的就是该 Service 的 VIP 地址。
而对于指定了 clusterIP=None 的 Headless Service 来说,它的 A 记录的格式也是:..svc.cluster.local。但是,当你访问这条 A 记录的时候,它返回的是所有被代理的 Pod 的 IP 地址的集合。当然,如果你的客户端没办法解析这个集合的话,它可能会只会拿到第一个 Pod 的 IP 地址。
此外,对于 ClusterIP 模式的 Service 来说,它代理的 Pod 被自动分配的 A 记录的格式是:..pod.cluster.local。这条记录指向 Pod的ip地址。
而对 Headless Service 来说,它代理的 Pod 被自动分配的 A 记录的格式是:...svc.cluster.local。这条记录也指向 Pod的ip地址。