这篇记录把三台新的 VMware Ubuntu 虚拟机加入现有 Kubernetes 集群,作为普通 worker 节点使用。
目标节点:
192.168.3.218 k8s-worker1
192.168.3.219 k8s-worker2
192.168.3.220 k8s-worker3
现有控制面:
192.168.3.214 k8s-master1
192.168.3.215 k8s-master2
192.168.3.216 k8s-master3
192.168.3.217 k8s-vip
Kubernetes API 入口继续使用:
192.168.3.217:8443
这里有一个关键前提:这三台是 worker,不是 control-plane,所以不要在它们上面配置 Keepalived、HAProxy、etcd,也不要执行带 --control-plane 的 join 命令。
一、先切换 VMware 网卡为 VMXNET3
之前集群里出现过 VMware e1000 网卡在高流量下卡死的问题,所以新 worker 节点一开始就切到 vmxnet3。
先关闭虚拟机,然后在 VMware 界面里把网卡类型改为 VMXNET3。如果界面里没有这个选项,也可以直接编辑虚拟机的 .vmx 文件。
找到类似配置:
ethernet0.virtualDev = "e1000"
改成:
ethernet0.virtualDev = "vmxnet3"
保存后启动虚拟机。
启动后检查网卡驱动:
sudo apt update
sudo apt install -y ethtool
ip -br addr
ethtool -i ens160
期望看到:
driver: vmxnet3
切换到 VMXNET3 后,Ubuntu 里的网卡名很可能从 ens33 变成 ens160。后面所有配置都按 ens160 写,如果实际网卡名不同,就替换成自己的。
二、配置固定 IP
三台 worker 都建议使用固定 IP。下面以 192.168.3.218 为例,另外两台把 IP 和 hostname 改掉即可。
先备份 netplan 配置:
sudo cp /etc/netplan/*.yaml /tmp/
ls /etc/netplan/
编辑 netplan:
sudo vim /etc/netplan/00-installer-config.yaml
示例配置:
network:
version: 2
ethernets:
ens160:
dhcp4: false
addresses:
- 192.168.3.218/24
routes:
- to: default
via: 192.168.3.1
nameservers:
addresses:
- 192.168.3.1
- 223.5.5.5
- 8.8.8.8
应用配置:
sudo netplan try
sudo netplan apply
检查网络:
ip addr show ens160
ip route
ping -c 4 192.168.3.217
ping -c 4 www.baidu.com
三台节点分别使用:
k8s-worker1 192.168.3.218/24
k8s-worker2 192.168.3.219/24
k8s-worker3 192.168.3.220/24
gateway 192.168.3.1
interface ens160
如果虚拟机是从旧模板克隆出来的,可以检查是否还有旧网卡名:
grep -R "ens33" /etc/netplan /etc/systemd /etc/NetworkManager 2>/dev/null
如果搜到 ens33,改成当前实际网卡名,比如 ens160。
三、设置 hostname 和 hosts
在 192.168.3.218 上执行:
sudo hostnamectl set-hostname k8s-worker1
在 192.168.3.219 上执行:
sudo hostnamectl set-hostname k8s-worker2
在 192.168.3.220 上执行:
sudo hostnamectl set-hostname k8s-worker3
三台 worker 都写入相同的 /etc/hosts:
sudo tee -a /etc/hosts >/dev/null <<'EOF'
# k8s hosts begin
192.168.3.214 k8s-master1
192.168.3.215 k8s-master2
192.168.3.216 k8s-master3
192.168.3.217 k8s-vip
192.168.3.218 k8s-worker1
192.168.3.219 k8s-worker2
192.168.3.220 k8s-worker3
# k8s hosts end
EOF
是的,三台 master 节点也建议补上 worker 的 hosts 映射。这样在 master 上执行排查命令时,可以直接使用 k8s-worker1、k8s-worker2、k8s-worker3 这些 hostname。
在 k8s-master1、k8s-master2、k8s-master3 上分别执行:
sudo tee -a /etc/hosts >/dev/null <<'EOF'
# k8s workers begin
192.168.3.218 k8s-worker1
192.168.3.219 k8s-worker2
192.168.3.220 k8s-worker3
# k8s workers end
EOF
验证:
hostname
ping -c 2 k8s-vip
ping -c 2 k8s-worker1
ping -c 2 k8s-worker2
ping -c 2 k8s-worker3
四、关闭 swap 并配置内核参数
三台 worker 都执行。
关闭 swap:
sudo swapoff -a
sudo cp /etc/fstab /etc/fstab.bak.$(date +%Y%m%d%H%M%S)
sudo sed -ri '/[[:space:]]swap[[:space:]]/ s/^([^#])/#\1/' /etc/fstab
加载 Kubernetes 需要的内核模块:
sudo tee /etc/modules-load.d/k8s.conf >/dev/null <<'EOF'
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
配置 sysctl:
sudo tee /etc/sysctl.d/k8s.conf >/dev/null <<'EOF'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
检查:
free -h
lsmod | egrep 'overlay|br_netfilter'
sysctl net.ipv4.ip_forward
五、安装 containerd
三台 worker 都执行。
sudo apt update
sudo apt install -y containerd ca-certificates curl gpg apt-transport-https
生成默认配置,并启用 systemd cgroup:
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd
sudo systemctl enable containerd
sudo systemctl status containerd --no-pager
确认:
systemctl is-active containerd
sudo ctr version
六、安装 kubeadm、kubelet、kubectl
版本要和现有集群保持一致。先在任意 master 上查看:
kubectl get nodes
kubectl version
我这套文档里当前按 v1.36 写:
K8S_VERSION=v1.36
三台 worker 都执行:
sudo mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL "https://pkgs.k8s.io/core:/stable:/${K8S_VERSION}/deb/Release.key" \
| sudo gpg --batch --yes --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/${K8S_VERSION}/deb/ /" \
| sudo tee /etc/apt/sources.list.d/kubernetes.list >/dev/null
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
sudo systemctl enable --now kubelet
# 如果集群实际不是 `v1.36`,把 `K8S_VERSION` 改成当前集群对应小版本仓库,例如 `v1.35`
sudo apt install -y --allow-change-held-packages kubelet kubeadm kubectl
如果集群实际不是 v1.36,把 K8S_VERSION 改成当前集群对应的小版本仓库,例如 v1.35。
安装完成后,给 crictl 写入默认 runtime endpoint:
# 安装工具
sudo apt install -y cri-tools
sudo tee /etc/crictl.yaml >/dev/null <<'EOF'
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
EOF
确认 containerd 可以被 CRI 工具访问:
sudo crictl info | grep runtimeType
七、固定 kubelet 的 node-ip
如果机器只有一张网卡,这一步通常不是必须的。但为了避免 kubelet 选错 IP,建议显式指定。
192.168.3.218 执行:
NODE_IP=192.168.3.218
sudo mkdir -p /etc/systemd/system/kubelet.service.d
sudo tee /etc/systemd/system/kubelet.service.d/20-node-ip.conf >/dev/null <<EOF
[Service]
Environment="KUBELET_EXTRA_ARGS=--node-ip=${NODE_IP}"
EOF
sudo systemctl daemon-reload
sudo systemctl restart kubelet
192.168.3.219 和 192.168.3.220 分别把 NODE_IP 改成自己的 IP。
八、生成 worker join 命令
在任意 master 上执行:
kubeadm token create --print-join-command
输出类似:
kubeadm join 192.168.3.217:8443 \
--token xxxxxx.xxxxxxxxxxxxxxxx \
--discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
如果输出里不是 192.168.3.217:8443,先确认当前集群的 controlPlaneEndpoint:
kubectl -n kube-system get configmap kubeadm-config -o yaml | grep controlPlaneEndpoint
普通 worker 的 join 命令不要带这些参数:
--control-plane
--certificate-key
建议补上 containerd 的 CRI socket:
sudo kubeadm join 192.168.3.217:8443 \
--token xxxxxx.xxxxxxxxxxxxxxxx \
--discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx \
--cri-socket=unix:///run/containerd/containerd.sock
在 192.168.3.218、192.168.3.219、192.168.3.220 三台 worker 上分别执行这条命令。
九、检查节点状态
回到任意 master,检查节点:
kubectl get nodes -o wide
期望看到:
k8s-master1 Ready control-plane ... 192.168.3.214
k8s-master2 Ready control-plane ... 192.168.3.215
k8s-master3 Ready control-plane ... 192.168.3.216
k8s-worker1 Ready <none> ... 192.168.3.218
k8s-worker2 Ready <none> ... 192.168.3.219
k8s-worker3 Ready <none> ... 192.168.3.220
给 worker 补一个角色标签,方便显示:
kubectl label node k8s-worker1 node-role.kubernetes.io/worker=
kubectl label node k8s-worker2 node-role.kubernetes.io/worker=
kubectl label node k8s-worker3 node-role.kubernetes.io/worker=
再看:
kubectl get nodes -o wide
如果集群已经切到 Calico,可以检查 Calico 相关 Pod 是否正常:
kubectl get pods -A -o wide | egrep 'calico|tigera|k8s-worker'
也可以直接跑一个测试 Pod:
kubectl run test-nginx --image=nginx:alpine --restart=Never
kubectl get pod test-nginx -o wide
kubectl delete pod test-nginx
十、Longhorn 节点准备
如果 worker 后面要承载 Longhorn 卷,三台 worker 还要安装 Longhorn 常用依赖。
sudo apt update
sudo apt install -y open-iscsi nfs-common
sudo systemctl enable --now iscsid
检查:
systemctl is-active iscsid
然后在 Longhorn UI 或命令行里确认新节点已经出现,并根据实际磁盘规划决定是否允许调度副本。
十一、常见问题
1. 节点一直 NotReady
先看 kubelet:
sudo systemctl status kubelet --no-pager
sudo journalctl -u kubelet -n 100 --no-pager
再看 CNI:
kubectl get pods -A -o wide | egrep 'calico|flannel|cni'
如果 CNI Pod 没有在新 worker 上起来,节点通常会保持 NotReady。
2. kubeadm join 连接不上 API
在 worker 上确认能访问 VIP:
ping -c 4 192.168.3.217
nc -vz 192.168.3.217 8443
如果 8443 不通,回 master 检查 HAProxy 和 Keepalived:
systemctl is-active haproxy keepalived
ip addr | grep 192.168.3.217
3. VMXNET3 后 IP 不见了
大概率是网卡名变化导致 netplan 还写着旧的 ens33。
ip -br link
grep -R "ens33" /etc/netplan /etc/NetworkManager 2>/dev/null
把配置里的旧网卡名改成当前实际网卡名,比如 ens160,再执行:
sudo netplan apply
4. 节点曾经加入过其他集群
如果这台 worker 不是全新机器,先清理旧 kubeadm 状态:
sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d /var/lib/cni /var/lib/kubelet/pki
sudo systemctl restart containerd kubelet
然后重新执行 join。
最后检查清单
三台 worker 加入完成后,至少检查这些:
kubectl get nodes -o wide
kubectl get pods -A -o wide | grep k8s-worker
kubectl top nodes
VMXNET3 检查:
for iface in ens160; do
ethtool -i "$iface" | grep driver
done
预期结果:
三台 worker 都是 Ready
三台 worker InternalIP 分别是 192.168.3.218、192.168.3.219、192.168.3.220
网卡驱动是 vmxnet3
Pod 能调度到新 worker
kubectl top nodes 能看到新节点指标
到这里,三台新虚拟机就已经以普通 worker 身份加入集群。后续可以根据用途再给它们打标签,比如通用业务、GitLab、数据库、存储或监控节点。