目录

k8s Service简记


Service是虚拟的!

Service是什么

  • Pod中的容器可以通过pause容器共享网络命名空间实现互访
  • 同一个Node内的不同Pod可以通过Docker0网桥实现互访
  • 不同Node上的Pod可以通过CNI插件,例如Flannel实现互访

通过以上三点,一个覆盖了k8s集群的扁平化Pod互访网络就建立起来了,这个Overlay Network中Pod的IP地址由Flannel进行划分。

但是Pod的生命周期是短暂的,Pod的死亡、Pod重新被拉起、Pod的扩容都会导致IP地址的变化。所以不能在Pod内直接使用IP地址来进行Pod间的访问。我们需要一个持久的静态IP来实现Pod间的访问,同时实现负载均衡和服务发现。Service是一个抽象出来的k8s资源,是由每个worker node上的kube-proxy组件通过修改iptables(iptables mode)来实现的。

可以将Service理解为一个虚拟IP。Service的IP空间又是独立于Pod的IP和Node的IP,可以被人为指定,所以一般来讲k8s的网络空间被分为三层。Pod的IP是由Flannel划分的,一个Node上一个子空间;Node的IP就是真实物理网卡IP;再加上Service的IP,组成三层网络模型。

Service如何实现

Service是由kube-proxy组件通过修改iptables实现的。

wikipedia
iptables是运行在用户空间的应用软件,通过控制Linux内核netfilter模块,来管理网络数据包的处理和转发。
https://chengleqi-blog-image.oss-cn-hangzhou.aliyuncs.com/img/202203252133130.jpeg
iptables Process Flow

kube-proxy主要设置了iptables中的nat表,通过在nat表中设置PREROUTING和OUTPUT链来劫持网络数据,负载均衡地导向至Service选择到的Pod。

一个例子

具体的yaml就不展示了,清单里有一个Service,Service选择一个Pod,Pod有8080、1985、1935三个端口。

此时使用iptables -t nat -nvL查看nat表中的所有链,发现PREROUTING和OUTPUT链均在头部插入了一个KUBE-SERVICES。这个KUBE-SERVICES链就是k8s的Service的门户,Service就是通过这个链劫持流量,进行DNAT和SNAT完成Pod间的互访。

通过kubectl get svc可以查看到Service的Cluster IP是10.43.69.202。接着可以跟踪KUBE-SERVICES。如下

https://chengleqi-blog-image.oss-cn-hangzhou.aliyuncs.com/img/202203252133131.png
KUBE-SERVICES链

可以看出所有目的地为10.43.69.202(也就是Service的Cluster IP)且目的端口为1935的数据包都被导向至KUBE-SVC-FQTBCD7TKAEMWX5M链,可以理解为就是Service。接着跟踪KUBE-SVC-FQTBCD7TKAEMWX5M链。如下

https://chengleqi-blog-image.oss-cn-hangzhou.aliyuncs.com/img/202203252133132.png
KUBE-SVC-FQTBCD7TKAEMWX5M链

发现只有一个KUBE-SEP-CQBFNZNKSCBZ27C2链,SEP意思就是Service End Point。因为我的Service下只有一个Pod所以这个地方只有一个SEP链,如果Service选择了多个Pod的话,这里应该有对应的endPoint数量的SEP链数,并且使用iptables statistic 模块做round-robin的负载均衡。其实看到这里就可以发现使用iptables模式实现Service的一些不足,例如:如果创建了许多的Service,那么iptables的链数就会变得肿胀,性能就会下降。而且iptables只有RR这一种负载均衡策略,但现实是我们需要更多的负载均衡策略,于是在Kubernetes1.14版本以后,就默认使用ipvs模式来替代iptables模式。

继续跟进KUBE-SEP-CQBFNZNKSCBZ27C2链可以发现

https://chengleqi-blog-image.oss-cn-hangzhou.aliyuncs.com/img/202203252133133.png
KUBE-SEP-CQBFNZNKSCBZ27C2链

最后的DNAT,也就是目的地址转换,将目的地址从最初的虚拟的Service Cluster IP转换至Pod的IP和目的端口1935。

NodePort类型

Service常见的有类型ClusterIP(默认)、NodePort和Load Balancer。

在常见的情况下,集群内Pod间的互访是通过设置Service Cluster IP命中OUTPUT链最终被导向Pod。

而集群外的请求进入集群内则需要将Service设置为NodePort(生产环境中不这么做),kube-proxy会在每个worker node上开启端口,外部请求会命中PREROUTING链最后的KUBE-NODEPORTS链,匹配规则是"ADDRTYPE match dst-type LOCAL"。之后会继续根据端口导向至对应的Service链。

总结

k8s中的Service是一个抽象出来的功能概念。

我们可以将Service想象成为一个接口,它定义了几个功能:

  1. 有贯穿Service生命周期的固定的IP和端口
  2. 服务发现
  3. 负载均衡

而iptables模式是对Service的一个具体实现:

  1. kube-proxy通过设置iptables对流量进行转发
  2. kube-proxy通过watch api-server(是否有Pod死亡?重新拉起?扩容?)来改写iptables规则
  3. 在iptables的SVC链中对SEP链进行RR的负载均衡

iptables实现Service可以用一张图总结:

https://chengleqi-blog-image.oss-cn-hangzhou.aliyuncs.com/img/202203252133134.png
通过iptables实现Service

所以我们可以说Service是通过kube-proxy和iptables联合实现的,是为Pod创造出来的假象。

参考:

  1. https://www.jianshu.com/p/67744d680286
  2. https://www.cnblogs.com/charlieroro/p/9588019.html