04、Kubernetes学习-Kubernetes Service 的实现原理

在上一篇文章中,我们介绍了 Kubernetes中pod的实现原理,Pod 是 Kubernetes 中非常轻量的对象。

集群中的每一个 Pod 都可以通过 podIP 被直接访问的,但是正如我们所看到的,Kubernetes 中的 Pod 是有生命周期的对象,尤其是被 ReplicaSet、Deployment 等对象管理的 Pod,随时都有可能由于集群的状态变化被销毁和创建。 

这也就造成了一个非常有意思的问题,当 Kubernetes 集群中的一些 Pod 需要为另外的一些 Pod 提供服务时,我们如何为提供同一功能服务的一组 Pod 建立一个抽象并追踪这组服务中节点的健康状态。

这一个抽象在 Kubernetes 中其实就是 Service,每一个 Kubernetes 的 Service 都是一组 Pod 的逻辑集合和访问方式的抽象,我也可以把 Service 加上的一组 Pod 称作是一个微服务。

在这篇文章中,我们将分两个部分介绍 Kubernetes 中 Service 的实现原理,在第一部分我们将介绍 Kubernetes 如何处理服务的创建,第二部分会介绍它是如何转发来自节点内部和外部的流量。

创建服务

在Kubernetes 中创建一个新的 Service 对象需要两大模块同时协作,其中一个模块是控制器,它需要在每次客户端创建新的 Service 对象时,生成其他用于暴露一组 Pod 的 Kubernetes 对象,也就是 Endpoint 对象;另一个模块是 kube-proxy,它运行在 Kubernetes 集群中的每一个节点上,会根据 Service 和 Endpoint 的变动改变节点上 iptables 或者 ipvs 中保存的规则。

代理模式

在Kubernetes 集群中的每一个节点都运行着一个 kube-proxy 进程,这个进程会负责监听 Kubernetes 主节点中 Service 的增加和删除事件并修改运行代理的配置,为节点内的客户端提供流量的转发和负载均衡等功能,但是当前 kube-proxy 的代理模式目前来看有三种: 

这三种代理模式中的第一种 userspace 其实就是运行在用户空间代理,所有的流量最终都会通过 kube-proxy 本身转发给其他的服务,后两种 iptable 和 ipvs 都运行在内核空间能够为 Kubernetes 集群提供更加强大的性能支持。

userspace

作为运行在用户空间的代理,对于每一个 Service 都会在当前的节点上开启一个端口,所有连接到当前代理端口的请求都会被转发到 Service 背后的一组 Pod 上,它其实会在节点上添加 iptables 规则,通过 iptables 将流量转发给 kube-proxy 处理。

如果当前节点上的 kube-proxy 在启动时选择了 userspace 模式,那么每当有新的 Service 被创建时,kube-proxy 就会增加一条 iptables 记录并启动一个 Goroutine,前者用于将节点中服务对外发出的流量转发给 kube-proxy,再由后者持有的一系列 Goroutine 将流量转发到目标的 Pod 上。 

这一系列的工作大都是在 OnServiceAdd 被触发时中完成的,正如上面所说的,该方法会调用 mergeService 将传入服务 Service 的端口变成一条 iptables 的配置命令为当前节点增加一条规则,同时在 addServiceOnPort 方法中启动一个 TCP 或 UDP 的 Socket:

iptables

另一种常见的代理模式就是直接使用 iptables 转发当前节点上的全部流量,这种脱离了用户空间在内核空间中实现转发的方式能够极大地提高 proxy 的效率,增加 kube-proxy 的吞吐量。 

iptables 作为一种代理模式,它同样实现了 OnServiceUpdateOnEndpointsUpdate 等方法,这两个方法会分别调用相应的变更追踪对象。

ipvs

ipvs 就是用于解决在大量 Service 时,iptables 规则同步变得不可用的性能问题。与 iptables 比较像的是,ipvs 的实现虽然也基于 netfilter 的钩子函数,但是它却使用哈希表作为底层的数据结构并且工作在内核态,这也就是说 ipvs 在重定向流量和同步代理规则有着更好的性能。 

在处理Service 的变化时,ipvs 包和 iptables 其实就有非常相似了,它们都同样使用 ServiceChangeTracker 对象来追踪变更,只是两者对于同步变更的方法 syncProxyRules 实现上有一些不同。

小结

三种不同的代理模式其实是一个逐渐演化的过程,从最开始运行在用户空间需要『手动』监听端口并对数据包进行转发的用户空间模式,到之后使用运行在内核空间的 iptables 模式,再到 Kubernetes 1.9 版本中出现的 ipvs 模式,几种不同的模式在大量 Service 存在时有数量级别效率差异。

总结

Kubernetes 中的 Service 将一组 Pod 以统一的形式对外暴露成一个服务,它利用运行在内核空间的 iptables 或者 ipvs 高效地转发来自节点内部和外部的流量。除此之外,作为非常重要的 Kubernetes 对象,Service 不仅在逻辑上提供了微服务的概念,还引入 LoadBalancer 类型的 Service 无缝对接云服务商提供的复杂资源。

理解Kubernetes 的 Service 对象能够帮助我们梳理集群内部的网络拓扑关系,也能让我们更清楚它是如何在集群内部实现服务发现、负载均衡等功能的,在后面的文章中我们会展开介绍 kube-proxy 的作用和实现。