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

Kubernetes 网络 — 在真集群上

切分 Pod 网段并设计插件链

在 TT Lab 中继续学习

目标

同时从节点对象和插件配置两方面确认 Pod 地址从哪里来,并亲手计算如何切分出互不重叠的网段。

为什么重要

新建集群时,最先要定下、也最难回头的就是地址网段。Pod、Service、节点这三种地址分别由不同的分配者负责,它们彼此并不商量,因此确保互不重叠是设计者的责任。此外,hostPort 或带宽限制这类功能,清单里写了,但没有插件就会被悄悄忽略;如果不先分清由谁负责什么,就会长时间困在“配置明明是对的,却不生效”的问题里。

步骤

  1. 把集群 Pod 网段 10.244.0.0/16 切分成 /24,从前往后依次固定给 lab-node-0、lab-node-1、lab-node-2(spec.podCIDR 和 spec.podCIDRs 两个字段都要设置)。然后在 /root/k8nd-cni/01-podcidr.txt 中按节点名称顺序写入三行 노드이름 대역(占位符依次为节点名称与网段)。
  2. 在 /root/k8nd-cni/02-capacity.txt 中写入五行——subnets=(把 10.244.0.0/16 切成 /24 时得到的分片数)、addresses_per_subnet=(每个分片的地址总数)、usable_per_subnet=(去掉网络地址和网关后的数量)、node_pod_limit=(节点对象中 status.allocatable.pods 的值)、binding_limit=(两者中先达到上限的一方,填写 subnet 或 node)。
  3. 创建 /root/k8nd-cni/10-k8nd.conflist——cniVersion 为 1.0.0,name 为 k8nd-pod-network,plugins 恰好三个,依次为 bridge、portmap、bandwidth。bridge 的 ipam 为 host-local,subnet 为 lab-node-0 的 Pod 网段,portmap 的 capabilities.portMappings 必须为 true,bandwidth 的 capabilities.bandwidth 必须为 true。
  4. 在 /root/k8nd-cni/shaped.yaml 中编写 Pod 清单并应用——命名空间为 k8nd-cni,名称为 shaped,注解为 kubernetes.io/ingress-bandwidth: 1M 和 kubernetes.io/egress-bandwidth: 1M,容器为 web(nginx:1.27-alpine),containerPort 和 hostPort 均为 8080。然后在 /root/k8nd-cni/04-chain.txt 中写入 hostport_plugin=、bandwidth_plugin=、ingress_annotation=、egress_annotation= 四行(插件填写第 3 步 conflist 中负责该功能的插件的 type,注解填写实际添加的值)。
  5. 在 /root/k8nd-cni/edge-node.yaml 中编写节点 k8nd-cni-edge 并应用——把 spec.unschedulable 设为 true,并在 spec.taints 中添加一个自定义的键 k8nd.example.com/cni-missing(effect 为 NoSchedule)。应用之后,在 /root/k8nd-cni/05-notready.txt 中写入 node=、custom_taint=(你添加的键)、controller_taint=(控制器额外添加的内置污点键)、unschedulable= 四行。
  6. 创建 /root/k8nd-cni/06-node1.conflist 和 /root/k8nd-cni/06-node2.conflist。它们的 ipam.subnet 分别使用固定给 lab-node-1 和 lab-node-2 的网段,ipam.gateway 为该网段的第一个可用地址,ipam.routes 中必须同时包含 0.0.0.0/0 和集群 Pod 网段 10.244.0.0/16。ipam.type 为 host-local。
  7. 判断四种网段规划——p1 的 Pod 为 10.244.0.0/16、Service 为 10.96.0.0/16、节点为 192.168.10.0/24;p2 的 Pod 为 10.96.0.0/12、Service 为 10.96.0.0/16、节点为 192.168.10.0/24;p3 的 Pod 为 10.244.0.0/16、Service 为 10.245.0.0/16、节点为 10.244.3.0/24;p4 的 Pod 为 172.16.0.0/16、Service 为 10.96.0.0/16、节点为 192.168.10.0/24。在 /root/k8nd-cni/07-overlap.txt 中写入从 p1= 到 p4= 的四行(值为 ok 或 overlap),再加上 rule=pod,service,node 一行,共五行。
  8. 在 /root/k8nd-cni/08-report.md 中写入 cluster_pod_cidr=、per_node_prefix=、nodes_addressable=(该前缀能容纳的节点数)、notready_node=(第 5 步创建的节点名称)、chained_plugins=(第 3 步 conflist 中接在 bridge 之后的插件,用逗号分隔)五行,并在下面写至少四行以 - 开头的行。

参考

为每个节点切分 Pod 网段

把集群 Pod 网段 10.244.0.0/16 切分成 /24,从前往后依次固定给 lab-node-0、lab-node-1、lab-node-2(spec.podCIDR 和 spec.podCIDRs 两个字段都要设置)。然后在 /root/k8nd-cni/01-podcidr.txt 中按节点名称顺序写入三行 노드이름 대역(占位符依次为节点名称与网段)。

Pod 地址不是由 API 服务器分配的。每个节点领取属于自己的网段,网络插件的 IPAM 再从中为 Pod 逐个分出地址。因此即使节点增加,地址管理也只在节点内部完成。用 kubectl patch node <이름> --type=merge -p '{"spec":{"podCIDR":"...","podCIDRs":["..."]}}'(占位符为节点名称)固定网段,再用 kubectl get node <이름> -o jsonpath='{.spec.podCIDR}'(占位符为节点名称)读回确认。值一旦写入,就不能改成别的值。

这个网段能容纳多少个 Pod

在 /root/k8nd-cni/02-capacity.txt 中写入五行——subnets=(把 10.244.0.0/16 切成 /24 时得到的分片数)、addresses_per_subnet=(每个分片的地址总数)、usable_per_subnet=(去掉网络地址和网关后的数量)、node_pod_limit=(节点对象中 status.allocatable.pods 的值)、binding_limit=(两者中先达到上限的一方,填写 subnet 或 node)。

统计地址和统计 Pod 是两回事。一个 /24 分片含有 256 个地址,但网络地址和网关各占掉一个。而节点接收的 Pod 数量由 kubelet 一侧的上限单独决定,通常先达到上限的是它,而不是地址。节点上限用 kubectl get node lab-node-0 -o jsonpath='{.status.allocatable.pods}' 读取。

串联插件配置

创建 /root/k8nd-cni/10-k8nd.conflist——cniVersion 为 1.0.0,name 为 k8nd-pod-network,plugins 恰好三个,依次为 bridge、portmap、bandwidth。bridge 的 ipam 为 host-local,subnet 为 lab-node-0 的 Pod 网段,portmap 的 capabilities.portMappings 必须为 true,bandwidth 的 capabilities.bandwidth 必须为 true。

在一个配置文件中写入多个插件的格式叫 conflist。插件从前往后依次被调用,后面的插件接收并调整前面插件产生的结果。因此,分配地址、开放 hostPort、限制带宽这三件事,由不同的插件分别负责。默认配置目录是 /etc/cni/net.d,二进制目录是 /opt/cni/bin。写完之后,用 jq . <파일>(占位符为文件名)检查语法。

hostPort 和带宽限制由谁负责

在 /root/k8nd-cni/shaped.yaml 中编写 Pod 清单并应用——命名空间为 k8nd-cni,名称为 shaped,注解为 kubernetes.io/ingress-bandwidth: 1M 和 kubernetes.io/egress-bandwidth: 1M,容器为 web(nginx:1.27-alpine),containerPort 和 hostPort 均为 8080。然后在 /root/k8nd-cni/04-chain.txt 中写入 hostport_plugin=、bandwidth_plugin=、ingress_annotation=、egress_annotation= 四行(插件填写第 3 步 conflist 中负责该功能的插件的 type,注解填写实际添加的值)。

Pod 规范中的 hostPort 和带宽注解都不是 API 服务器负责的事情,二者都由接在插件链上的插件读取并执行。所以,如果节点上没有安装该插件,清单虽然能通过,却什么都不会发生——这是一类被悄悄忽略的故障。用 kubectl apply -f 创建 Pod,再用 kubectl -n <ns> get pod shaped -o jsonpath='{.metadata.annotations}'(占位符为命名空间)读回注解。

没有插件,节点就会被整体挡住

在 /root/k8nd-cni/edge-node.yaml 中编写节点 k8nd-cni-edge 并应用——把 spec.unschedulable 设为 true,并在 spec.taints 中添加一个自定义的键 k8nd.example.com/cni-missing(effect 为 NoSchedule)。应用之后,在 /root/k8nd-cni/05-notready.txt 中写入 node=、custom_taint=(你添加的键)、controller_taint=(控制器额外添加的内置污点键)、unschedulable= 四行。

没有配置网络插件的节点无法接收任何 Pod。真正造成这种隔离的是污点,官方文档的内置污点列表中并列有 node.kubernetes.io/network-unavailable(网络不可用)和 node.kubernetes.io/unschedulable。这里有一个重要区别——由状况派生的内置污点由控制器管理。即使手动添加,只要状况不是那个状态,它就会立刻被清除;反过来,把 spec.unschedulable 设为 true 后,控制器会自动添加一个污点。所以人可以添加的只有使用自己键名的污点。不要假定列表中的第一项就是你写的那一个,请用 kubectl get node <이름> -o json | jq -r '.spec.taints[].key'(占位符为节点名称)全部确认。

为每个节点创建不同的 IPAM 配置

创建 /root/k8nd-cni/06-node1.conflist 和 /root/k8nd-cni/06-node2.conflist。它们的 ipam.subnet 分别使用固定给 lab-node-1 和 lab-node-2 的网段,ipam.gateway 为该网段的第一个可用地址,ipam.routes 中必须同时包含 0.0.0.0/0 和集群 Pod 网段 10.244.0.0/16。ipam.type 为 host-local。

host-local 正如其名,只在该节点内部管理地址,不会询问各节点互相分出去了什么——所以网段重叠时,两个节点会若无其事地分出相同的地址。切分出互不重叠的网段,是人或控制平面的责任。之所以要单独写出通往集群网段的路由,是因为走默认路由的话,Pod 之间的流量会被送出节点,而这里需要在节点内部处理。

重叠时,去向就无法确定

判断四种网段规划——p1 的 Pod 为 10.244.0.0/16、Service 为 10.96.0.0/16、节点为 192.168.10.0/24;p2 的 Pod 为 10.96.0.0/12、Service 为 10.96.0.0/16、节点为 192.168.10.0/24;p3 的 Pod 为 10.244.0.0/16、Service 为 10.245.0.0/16、节点为 10.244.3.0/24;p4 的 Pod 为 172.16.0.0/16、Service 为 10.96.0.0/16、节点为 192.168.10.0/24。在 /root/k8nd-cni/07-overlap.txt 中写入从 p1= 到 p4= 的四行(值为 ok 或 overlap),再加上 rule=pod,service,node 一行,共五行。

Kubernetes 官方文档明确规定,Pod、Service、节点这三种地址不能互相重叠。一旦重叠,先命中哪条规则取决于具体实现,这种差异会表现为“只有某些节点不行”。不要靠肉眼判断,请计算——一行 python3 -c "import ipaddress; print(ipaddress.ip_network('10.96.0.0/12').overlaps(ipaddress.ip_network('10.96.0.0/16')))" 就够了。前缀越短,网段越大,这一点很容易被忽略。

留下 Pod 网络设计备忘

在 /root/k8nd-cni/08-report.md 中写入 cluster_pod_cidr=、per_node_prefix=、nodes_addressable=(该前缀能容纳的节点数)、notready_node=(第 5 步创建的节点名称)、chained_plugins=(第 3 步 conflist 中接在 bridge 之后的插件,用逗号分隔)五行,并在下面写至少四行以 - 开头的行。

值要取自前面步骤创建的文件,而不是凭记忆填写。说明行中要写下下次设计集群时真正用得上的句子——比如“分配地址的是谁”“什么会被悄悄忽略”。