同 pod 内的容器间通信最简单,这些容器共享网络命名空间,每个命名空间下都有lo回环接口,可以通过localhost来完成通信。
同节点上的 pod 间通信
当我们将curl容器和httpbin分别在两个 pod 中运行,这两个 pod 有可能调度到同一个节点上。curl发出的请求根据容器内的路由表到达了 pod 内的eth0接口。然后通过与eth0相连的隧道veth1到达节点的根网络空间。
veth1通过网桥cni0与其他 pod 相连虚拟以太接口vethX相连,网桥会询问所有相连的接口是否拥有原始请求中的 IP 地址(比如这里的10.42.1.9)。收到响应后,网桥会记录映射信息(10.42.1.9=>veth0),同时将数据转发过去。最终数据经过veth0隧道进入 podhttpbin中。
不同节点的 pod 间通信
跨节点的 pod 间通信会复杂一些,且不同网络插件的处理方式不同,这里选择一种容易理解的方式来简单说明下。
前半部分的流程与同节点 pod 间通信类似,当请求到达网桥,网桥询问哪个 pod 拥有该 IP 但是没有得到回应。流程进入主机的路由寻址过程,到更高的集群层面。
在集群层面有一张路由表,里面存储着每个节点的 Pod IP 网段(节点加入到集群时会分配一个 Pod 网段(Pod CIDR),比如在 k3s 中默认的 Pod CIDR 是10.42.0.0/16,节点获取到的网段是10.42.0.0/24、10.42.1.0/24、10.42.2.0/24,依次类推)。通过节点的 Pod IP 网段可以判断出请求 IP 的节点,然后请求被发送到该节点。
总结
现在应该对 Kubernetes 的网络通信有初步的了解了吧。
整个通信的过程需要各种组件的配合,比如 Pod 网络命名空间、pod 以太网接口eth0、虚拟以太网接口vethX、网桥(network bridge)cni0等。其中有些组件与 pod 一一对应,与 pod 同生命周期。虽然可以通过手动的方式创建、关联和删除,但对于 pod 这种非永久性的资源会被频繁地创建和销毁,太多人工的工作也是不现实的。
实际上这些工作都是由容器委托给网络插件来完成的,而网络插件所遵循的规范 CNI(Container Network Interface)。
网络插件都做了什么?