TT Lab
开始
学习 学习路径 课程

Kubernetes 网络 — 在真集群上

API 服务器并不知道 Pod 的地址

在 TT Lab 中继续学习

一句话总结

Kubernetes 不会直接给 Pod 分配地址。API 服务器只管理 Service 地址,Pod 地址由节点上的网络插件分配。因此,“Pod 没有启动”和“Pod 之间不通”是不同层面的故障。

为什么需要它

把多个容器放到同一台机器上,最先撞上的就是端口问题。三个团队都想用 8080,就必须有人让步;让步的一方要把端口做成配置变量,而服务发现也就必须知道这些端口。官方文档把这条路总结为“规模变大后协调极其困难,并会产生用户无法控制的集群级问题”。

Kubernetes 没有走这条路,而是给每个 Pod 分配一个地址。在 Pod 内部,容器之间通过 localhost 互相访问;在 Pod 外部,则通过地址访问。端口协调这个问题本身也就不存在了。

问题在于,这个地址由谁、怎样分配。不同云平台的做法不同,本地机房(on-premises)又是另一套,有的用路由实现,有的用隧道实现。Kubernetes 对此留出空位,只规定了规范,这个规范就是 CNI(Container Network Interface)。

工作原理

官方文档的表述很明确——“要实现 Kubernetes 网络模型,需要 CNI 插件。”并且插件必须兼容 CNI 规范 v0.4.0 及以上版本,项目建议兼容 v1.0.0。

由谁调用。1.24 之前,kubelet 通过 cni-bin-dir 和 network-plugin 选项管理插件。这两个选项在 1.24 中已被移除。如今调用 CNI 的不是 kubelet,而是容器运行时(containerd、CRI-O)。所以,一半的“改了 CNI 配置却不生效”的报告,都是因为重启了 kubelet。

从哪里读取。配置的默认目录是 /etc/cni/net.d,二进制文件的默认目录是 /opt/cni/bin。在一个配置文件中写入多个插件的格式叫 conflist,插件会从前往后依次被调用。

为什么需要多个。因为一个插件不能包办所有事情。官方文档自己举了两个例子。

功能 负责的插件 启用方式
hostPort portmap 在 capabilities 中设置 portMappings: true
带宽限制 bandwidth 在 capabilities 中设置 bandwidth: true,并给 Pod 添加 kubernetes.io/ingress-bandwidth 注解
回环接口 lo loopback 必须由运行时为每个沙箱提供

由此得出一个运维上的重要事实:写了 hostPort,却可能什么都没有发生。清单能通过 API 校验,Pod 会变成 Running,端口却没有打开。只要 portmap 不在插件链中,就是这种情况。这是一类被悄悄忽略的故障。

地址由 IPAM 分配。bridge 这类主插件中的 ipam 项负责这项工作。最常见的 host-local 正如其名,只在该节点内部管理地址,不会询问各节点互相分出去了什么。因此必须预先为每个节点划分互不重叠的网段,这个网段就是节点对象的 spec.podCIDR。官方示例写成 "subnet": "usePodCidr",指的就是这个意思。

三种地址。官方文档明确规定,集群必须互不重叠地分配 Pod、Service、节点这三种地址,并且分别指明了各自的分配者——Pod 由网络插件分配,Service 由 kube-apiserver 分配,节点由 kubelet 或云控制器分配。这三个分配者彼此并不商量,所以防止重叠是设计者的责任。

在现场相遇的样子

“只有一个节点不接收 Pod。” Kubernetes 用污点来表达这种情况。官方文档的内置污点列表中,有一项 node.kubernetes.io/network-unavailable,含义是“节点的网络不可用”。因此,问题会以“Pod 处于 Pending”的形式报上来,但要修复的地方不是调度器,而是该节点的插件配置:可能只有这个节点缺少插件配置文件,或者二进制目录不同,或者运行时读取的是另一条路径。

“两个节点上的 Pod IP 相同。” host-local 不了解节点之外的情况,所以把同一个网段分给两个节点时,它会若无其事地分出相同的地址。症状表现为随机的连接失败,需要很长时间才能找到原因。

“网段划错了,我要改。”节点的 spec.podCIDR 一旦写入值,就不能改成别的值。在集群设计中,最难回头的决定就是地址网段。

官方文档:Network Plugins · Cluster Networking

下一项实验要做什么

为三个节点各固定一个 /24 的 Pod 网段,并计算该网段能容纳的地址数与节点接收的 Pod 数,看哪一个先达到上限。然后编写一个把 bridge、portmap、bandwidth 三个插件串联起来的 conflist,创建带有 hostPort 和带宽注解的 Pod,记录由哪个插件负责这些功能。最后模拟一个没有插件的节点,制造出 NotReady 状况,并通过计算判断四种网段规划中哪些存在重叠。