在上一篇文章中,我们介绍了 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 作为一种代理模式,它同样实现了 OnServiceUpdate
、OnEndpointsUpdate
等方法,这两个方法会分别调用相应的变更追踪对象。
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 的作用和实现。