三个节点 — join、drain 与 kubelet 恢复
目标
通过 ssh 登录三个节点来组装 kubeadm 集群,清空一个工作节点做维护,并让重启后迟迟不恢复的另一个工作节点恢复。 在三台真实的 VM 上,练就 CKA 实操考试所要求的“在多个节点之间来回切换操作的手感”。
为什么重要
在只有一台机器的集群上,看不出节点是什么。一旦节点变成多个,有三件事会变得不同。
第一,节点的地址成了问题。本实验三台 VM 的第一块 NIC 地址全都是 10.0.2.2,真正的集群网络是第二块 NIC(eth1)。
kubelet 的 --node-ip、API 服务器的 advertise 地址、CNI 的隧道 NIC 都必须指向 eth1——在多 NIC 服务器上搭建 Kubernetes 时,这里是实际上最容易出错的地方。
第二,出现了维护。关闭节点之前先清空 Pod 的 drain,以及恢复用的 uncordon,是运维的日常。
第三,kubelet 不是 Pod,而是 systemd 服务。节点处于 NotReady 时,用 kubectl 找不到原因,必须登录该节点查看服务。
步骤
- 在 controlplane 上用
ssh node01、ssh node02登录各节点,确认hostname和 eth1 的 IPv4 地址。把三个节点按每行一个이름 주소(占位符依次为节点名与地址)的格式(controlplane、node01、node02)写入/root/cluster/nodes.txt。 - 在 controlplane 上用
--kubernetes-version v1.36.4 --apiserver-advertise-address <controlplane 의 eth1 주소> --pod-network-cidr 172.20.0.0/16 --service-cidr 172.21.0.0/16(占位符为 controlplane 的 eth1 地址)运行kubeadm init,并把/etc/kubernetes/admin.conf复制到/root/.kube/config。controlplane 节点的 InternalIP 必须是 eth1 的地址。 - 在预先下载好的
/root/cluster/kube-flannel.yml(Flannel v0.28.9)中,把net-conf.json的Network改为172.20.0.0/16,并在kube-flannel容器的 args 中加上--iface=eth1后应用。controlplane 必须变为 Ready,且 CoreDNS 必须是 Available。 - 在 controlplane 上生成 join 命令(
kubeadm token create --print-join-command),并在 node01 和 node02 上执行。三个节点必须全部是 Ready,且 node01、node02 的 InternalIP 必须是各自的 eth1 地址。 - 在 default 命名空间中创建 Deployment
web,镜像为registry.k8s.io/e2e-test-images/agnhost:2.53,命令为/agnhost netexec --http-port=8080,副本数为 2。两个 Pod 必须在工作节点上处于 Ready,并且在 controlplane 上访问每个 Pod IP 的:8080/hostname时,必须返回该 Pod 的名称。 - 用
kubectl drain清空 node01(保留 DaemonSet Pod),并把其完整输出保存到/root/cluster/drain.log。node01 上必须一个非 DaemonSet 的 Pod 都不剩,并且web的两个 Pod 必须都是 Ready。 - 重启 node02(
ssh node02 reboot)。恢复之后,在 node02 上找出 node02 仍为 NotReady 的原因并修复,并且让它在下次重启时也能自行恢复。 - 让 node01 重新可调度。三个节点必须全部是 Ready 且可调度,Flannel DaemonSet 在三个节点上都必须就绪,并且
web的两个 Pod 必须是 Ready。
参考
- 这个终端是 controlplane。其他节点用
ssh node01、ssh node02登录,用exit返回。三个节点都是 root。 - 三个节点上都安装了 containerd 2.3.5 和 kubeadm、kubelet、kubectl 1.36.4,运行时设置(SystemdCgroup、ip_forward、br_netfilter)和镜像下载已经完成。集群尚不存在。
- 网段为 Pod
172.20.0.0/16、Service172.21.0.0/16。因为运行这些 VM 的外层集群使用的是 10.244/16 和 10.96/12。 - 常见错误:不带 advertise 地址就执行 init。工作节点会去 10.0.2.2:6443 找 API 服务器,结果连到自己身上。要回退,必须先执行
kubeadm reset -f。 - 常见错误:在第 7 步只做
systemctl start kubelet。现在能恢复,但下次重启还会出同样的问题。 - 会话结束后,三台 VM 会一起消失。超过 60 分钟时,请用屏幕上的+时间延长。
登录三个节点,确认第二块 NIC
在 controlplane 上用 ssh node01、ssh node02 登录各节点,确认 hostname 和 eth1 的 IPv4 地址。把三个节点按每行一个 이름 주소(占位符依次为节点名与地址)的格式(controlplane、node01、node02)写入 /root/cluster/nodes.txt。
用 ip -4 addr 查看会发现有两块 NIC。请确认第一块 NIC(enp1s0)的地址在三个节点上都相同——用这个地址无法区分节点。也可以像 ssh node01 'ip -4 -o addr show eth1' 这样只发送命令。
用 eth1 地址搭建控制平面
在 controlplane 上用 --kubernetes-version v1.36.4 --apiserver-advertise-address <controlplane 의 eth1 주소> --pod-network-cidr 172.20.0.0/16 --service-cidr 172.21.0.0/16(占位符为 controlplane 的 eth1 地址)运行 kubeadm init,并把 /etc/kubernetes/admin.conf 复制到 /root/.kube/config。controlplane 节点的 InternalIP 必须是 eth1 的地址。
如果不指定 advertise 地址,kubeadm 会选择默认路由的 NIC(10.0.2.2)。这样工作节点就会用这个地址去找 API 服务器,结果连到自己身上。kubelet 一侧的地址由 /etc/default/kubelet 中的 --node-ip 决定——里面已经写好了,请读一下。
告诉 Flannel 用哪块 NIC 建立隧道
在预先下载好的 /root/cluster/kube-flannel.yml(Flannel v0.28.9)中,把 net-conf.json 的 Network 改为 172.20.0.0/16,并在 kube-flannel 容器的 args 中加上 --iface=eth1 后应用。controlplane 必须变为 Ready,且 CoreDNS 必须是 Available。
Flannel 默认用默认路由的 NIC 建立 VXLAN。如果这块 NIC 的地址在三个节点上都相同,隧道的目的地就全都是自己——症状是节点是 Ready,却访问不到其他节点的 Pod。args 在 --kube-subnet-mgr 那一行下面,用相同的缩进再加一行。
让两个工作节点加入
在 controlplane 上生成 join 命令(kubeadm token create --print-join-command),并在 node01 和 node02 上执行。三个节点必须全部是 Ready,且 node01、node02 的 InternalIP 必须是各自的 eth1 地址。
join 命令要在工作节点上以 root 身份执行——ssh node01 '<조인 명령>'(占位符为 join 命令)。即使加入完成,在 Flannel Pod 在该节点上启动之前,Ready 也会稍有延迟。也请读一下 join 输出中的 WARNING 行。以后会有用。
确认能否真正访问其他节点的 Pod
在 default 命名空间中创建 Deployment web,镜像为 registry.k8s.io/e2e-test-images/agnhost:2.53,命令为 /agnhost netexec --http-port=8080,副本数为 2。两个 Pod 必须在工作节点上处于 Ready,并且在 controlplane 上访问每个 Pod IP 的 :8080/hostname 时,必须返回该 Pod 的名称。
一行 kubectl create deployment web --image=… --replicas=2 -- /agnhost netexec --http-port=8080 就够了。controlplane 上有 control-plane 污点,所以 Pod 只会去工作节点。如果 curl 卡住,请先怀疑 Flannel 的 NIC。
为了维护而清空 node01
用 kubectl drain 清空 node01(保留 DaemonSet Pod),并把其完整输出保存到 /root/cluster/drain.log。node01 上必须一个非 DaemonSet 的 Pod 都不剩,并且 web 的两个 Pod 必须都是 Ready。
drain 是 cordon(禁止调度)加驱逐。像 Flannel、kube-proxy 这样的 DaemonSet Pod,即使被驱逐也会立刻在同一个节点上重新生成,所以用 --ignore-daemonsets 跳过。被驱逐的 web Pod 会迁移到 node02。输出用 2>&1 | tee 同时保存到屏幕和文件即可。
重启后的 node02 迟迟不恢复
重启 node02(ssh node02 reboot)。恢复之后,在 node02 上找出 node02 仍为 NotReady 的原因并修复,并且让它在下次重启时也能自行恢复。
节点是 NotReady 时,先看该节点的 kubelet——systemctl status kubelet。现在是否在运行(active)和开机时是否会启动(enabled)是不同的问题。还记得 join 时出现的 WARNING 吗?
结束维护并恢复
让 node01 重新可调度。三个节点必须全部是 Ready 且可调度,Flannel DaemonSet 在三个节点上都必须就绪,并且 web 的两个 Pod 必须是 Ready。
恢复 drain 用的是 uncordon。即使恢复,已经迁到 node02 的 Pod 也不会自己回来——调度器只在放置新 Pod 时才做判断。