如果你准备在本机用 VMware 创建一套三节点 Kubernetes 1.29.14 集群运行环境,最该先想清楚的其实不是命令怎么敲,而是虚拟机资源怎么分配、网络走哪种模式、组件版本怎么锁定。这篇文章按完整落地顺序拆一遍:从虚拟机创建到集群初始化,再到测试业务部署和常见故障处理,适合那些不打算租云服务器、希望保留一套可重复使用的本地 Kubernetes 实验环境的人。
这类环境最大的价值在于:它比 minikube 更接近真实集群,又比生产集群轻量得多。三节点能验证多节点调度、Pod 跨节点通信、节点故障隔离等场景。下面我把搭建过程拆成六个部分,每部分都附上判断标准和排查思路。
1. 先确认这套三节点环境解决什么问题
1.1 单节点、双节点和三节点有什么差别
如果只是学 Kubernetes 命令,minikube 或 kind 就够了。但这类单节点环境有两个限制:默认情况下所有 Pod 都落在同一个节点上,很多调度逻辑体现不出来;节点变成 NotReady 之后,整个环境基本无法继续练习排障。
三节点集群是另一个量级。控制平面节点单独承担 kube-apiserver、etcd、controller-manager、scheduler,工作节点负责运行业务 Pod。你可以观察 Deployment 的 Pod 如何被调度到不同节点;可以给某个节点打污点,看调度结果怎么变化;可以手动停掉一台工作节点,看集群如何驱逐 Pod。这些是单节点环境模拟不出来的。
如果只有两台虚拟机,也不是不行,但控制平面和工作负载堆在一起,etcd 和业务 Pod 抢资源,排查问题时日志会混在一起。三台各司其职,反而省心。
1.2 这套集群的适用场景与明显边界
这套运行环境适合四类场景:
- 学习 kubeadm 的 init、join、reset 全流程。
- 练习 Deployment、Service、Ingress、PV/PVC 等基础对象。
- 做中间件集群实验,比如 Redis Cluster、Kafka、Spark 这类依赖多节点通信的组件。
- 验证网络插件行为,比如 Flannel、Calico 的 Pod 网段和路由规则。
但它的边界也要说清楚。这是本地虚拟机环境,不是生产高可用集群。VMware Workstation 的虚拟化性能受宿主机资源限制,磁盘 IO 和网络吞吐都远不如物理机。etcd 只有单实例,控制平面节点挂了集群就不可用。如果你的目标是模拟生产高可用架构,至少还需要再加一层负载均衡,并把 etcd 拆成独立节点。
注意:不要因为套件跑起来了,就把负载测试、性能测试、长时间稳定性验证这类结论直接套到这上面。虚拟机环境更适合验证“能不能跑通”,不适合验证“能承受多大压力”。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 搭建前的资源估算与网络模式选择
2.1 三台虚拟机的资源分配建议
Kubernetes 1.29 集群对单台节点的最低要求不算高,但三台同时运行,对宿主机压力是叠加的。我的建议是先按下面的配置准备,如果宿主机内存紧张,可以把工作节点降到 3GB 内存,但尽量别低于这个数。
| 节点角色 | 主机名示例 | CPU | 内存 | 系统盘 | 说明 |
|---|---|---|---|---|---|
| 控制平面节点 | k8s-master | 2 核 | 4 GB | 40 GB | 运行 etcd、apiserver、controller-manager、scheduler |
| 工作节点 | k8s-node1 | 2 核 | 4 GB | 40 GB | 运行业务 Pod 和 kube-proxy |
| 工作节点 | k8s-node2 | 2 核 | 4 GB | 40 GB | 运行业务 Pod 和 kube-proxy |
宿主机总体建议 6 核以上 CPU、16GB 内存、120GB 以上磁盘空间。如果少于这个数字,三台虚拟机同时开机容易进入内存交换,表现就是操作卡顿、kubelet 响应慢、Pod 反复重启。
如果你只是学习,不想开那么多资源,可以先关掉一台工作节点,把集群临时缩成两节点。但不要一边开三台一边又跑编译任务或大型桌面程序。
2.2 网络模式:学习环境首选 NAT
VMware 提供三种常见网络模式:桥接、NAT、仅主机。对这套环境,我建议直接用 NAT。
NAT 模式下,虚拟机通过宿主机共享 IP 访问外网,安装软件包、拉取容器镜像都比较顺畅。三个节点之间通过虚拟网卡互通,也能满足集群通信需求。
桥接模式适合需要局域网其他机器直接访问集群服务的情况。但桥接依赖局域网有可用 IP,而且换一个网络环境后 IP 会变,Kubernetes 节点地址跟着变,处理起来麻烦。
仅主机模式完全隔离外网,适合离线实验,但不适合初次搭建,因为安装依赖要先解决离线包问题,复杂度会明显上升。
我推荐的网络方案是:NAT 模式 + 静态 IP。不要依赖 DHCP,因为重启后 IP 如果变化,注册到集群里的节点地址也要跟着改,很容易把问题引到“节点 NotReady”上。
2.3 系统镜像与 VMware 平台准备
虚拟机操作系统我建议用 Ubuntu Server 22.04 LTS。它的 apt 源、内核、containerd 兼容性都比较稳,网上能找到的 K8s 部署资料也最多。
VMware 平台用 Workstation Pro、Workstation Player 或 ESXi 都行,操作步骤基本一致。安装系统时只需要一个标准 Ubuntu Server 镜像,不需要装图形界面,少一层桌面环境就少占几百 MB 内存。
创建虚拟机之前先确认宿主机 BIOS 里已经开启虚拟化。如果 VMware 启动虚拟机时报“无法启用虚拟机平台”这类错误,绝大多数情况是宿主机同时开启了 Hyper-V、WSL2、Windows 沙盒或 Credential Guard,和 VMware 的虚拟化层冲突了。解决思路是关闭冲突功能,或者换一个版本再试。
如果你在安装 Ubuntu 时经常出现界面卡死、花屏或 VMware 报非法指令,优先检查镜像是否完整,再确认分配的 CPU 和内存不是虚高但实际不足。别急着改内核参数,先换一个低版本镜像或改引导方式往往更快。
3. 从一台虚拟机模板复制出三个节点
3.1 先创建一台模板虚拟机并安装系统
不建议一次性创建三台虚拟机。正确做法是先创建一台干净的模板机,安装好系统,确认网络、ssh、apt 源都正常,然后克隆成三个节点。这样能减少重复安装时间,也保证三台机器的系统软件完全一致。
创建模板机时注意三点:
- 磁盘类型选“厚置备”还是“精简置备”都可以,精简置备更省磁盘空间,但性能弱一些。学习环境用精简置备问题不大。
- 系统安装时磁盘分区交给 Ubuntu 默认方案即可,不用手工 LVM。
- 安装完系统后先执行
apt update && apt upgrade,再安装openssh-server和基础工具,避免之后每个节点重复装一遍。
模板机的主机名不需要提前想好,因为克隆之后会改。
3.2 用完整克隆复制出另外两个工作节点
在 VMware 里右键模板机,选择“管理”里的“克隆”。克隆方式有两种:链接克隆和完整克隆。学习环境建议选完整克隆,虽然占用磁盘多一些,但它是独立的新虚拟机,和模板机之间没有底层依赖,后续做快照、迁移、恢复都不容易出问题。
克隆完成后,你会得到三台几乎一模一样的系统。这时不能直接启动就建集群,因为三台机器的主机名、machine-id、IP 完全一样,Kubernetes 节点注册和 etcd 通信都会出错。
3.3 修改主机名、machine-id、IP 与 hosts
给三台机器的命名分别是:
- k8s-master:192.168.x.21
- k8s-node1:192.168.x.22
- k8s-node2:192.168.x.23
IP 段以你 NAT 网络的实际网段为准,不要照抄。
进入模板机后,先把主机名改掉,然后删除 machine-id,让系统重新生成唯一标识。这一步不处理,后面 kubelet 注册时会遇到节点信息冲突。
bash复制sudo hostnamectl set-hostname k8s-node1
sudo rm -f /etc/machine-id /var/lib/dbus/machine-id
sudo systemd-machine-id-setup
接着修改网络配置。Ubuntu Server 22.04 使用 netplan。下面是一个示例,对应 ens33 网卡,具体网卡名需要先通过 ip addr 确认。
yaml复制network:
version: 2
ethernets:
ens33:
dhcp4: no
addresses:
- 192.168.x.22/24
routes:
- to: default
via: 192.168.x.2
nameservers:
addresses:
- 192.168.x.2
- 223.5.5.5
应用配置:
bash复制sudo netplan apply
然后在三个节点上统一维护 /etc/hosts,让节点之间通过主机名访问,而不是依赖域名解析服务:
bash复制192.168.x.21 k8s-master
192.168.x.22 k8s-node1
192.168.x.23 k8s-node2
完成之后,先在控制平面节点上执行 ssh k8s-node1、ssh k8s-node2,确认相互能通。再继续后面的依赖安装。
4. 安装 containerd 并以 1.29.14 版本固定 Kubernetes 组件
4.1 配置 containerd 时最容易忽略的一步
Kubernetes 1.29 集群通常使用 containerd 作为容器运行时。Ubuntu Server 默认没有装 containerd,先手动安装并设置自启动。
bash复制sudo apt update
sudo apt install -y containerd
安装好之后,containerd 默认不会生成可直接使用的配置文件。需要手动生成:
bash复制sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
这个配置里有两个关键点。第一个是 SystemdCgroup,必须从 false 改成 true。原因是 kubelet 默认使用 systemd cgroup driver,而 containerd 如果使用 cgroupfs,两者不一致就会导致容器启动后立刻被清理,现象是 Pod 卡在 ContainerCreating,kubelet 日志里反复出现 cgroup 相关报错。
修改命令:
bash复制sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
第二个是 sandbox_image。默认指向 registry.k8s.io/pause,这个地址在部分网络环境里拉取很慢或直接失败。你可以把它改成当前环境能稳定访问的公共镜像仓库地址,或者提前下载好 pause 镜像再导入。具体改成什么,以你实际网络条件为准。
bash复制sudo sed -i 's#registry.k8s.io/pause:3.9#你的镜像地址/pause:3.9#g' /etc/containerd/config.toml
sudo systemctl restart containerd
我一般会先跑一条测试命令确认 runtime 是可用的:
bash复制sudo crictl version
如果输出里有 containerd 版本号而没报错,说明运行时已经通了。
注意:不要只改 cgroup 不重启 containerd,也不要把 etcd 的时间同步问题留到后面。镜像地址、cgroup、时间同步,这三项是本地集群搭建里最容易埋坑的地方。
4.2 安装 kubeadm、kubelet、kubectl 并锁定版本
使用 kubeadm 安装集群,需要三个组件:kubeadm 负责初始化集群,kubelet 是每个节点上始终运行的 agent,kubectl 是用户命令工具。三个组件必须锁定同一版本,否则可能出现 client 与 server 版本不匹配的问题。
添加 Kubernetes 官方 1.29 软件的 apt 源:
bash复制sudo apt-get install -y apt-transport-https ca-certificates curl gpg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --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:/v1.29/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
安装指定版本。kubeadm、kubelet、kubectl 都要用 1.29.14:
bash复制sudo apt-get install -y kubeadm=1.29.14-* kubelet=1.29.14-* kubectl=1.29.14-*
sudo apt-mark hold kubeadm kubelet kubectl
如果你不知道当前源里 1.29.14 对应的精确后缀,可以先执行:
bash复制apt-cache policy kubeadm
查看可用版本,再安装对应版本号。apt-mark hold 的作用是防止 apt upgrade 时误升到其他小版本,否则集群过一个周末回来可能就不在同一个版本线上了。
4.3 初始化控制平面节点
在 k8s-master 上执行初始化命令。执行前先确认两个前提:节点 IP 正确,Pod 网段没有被宿主机实际网段占用。
bash复制sudo kubeadm init \
--kubernetes-version=v1.29.14 \
--apiserver-advertise-address=192.168.x.21 \
--pod-network-cidr=10.244.0.0/16
--apiserver-advertise-address 要填控制平面节点的实际 IP,不是虚拟网卡里随便挑的地址。--pod-network-cidr 后面安装网络插件时要用,如果选 Flannel,通常用 10.244.0.0/16;如果选 Calico 并且不修改配置,也可以用 192.168.0.0/16。这一步决定 Pod 网段,后续不要随意改。
初始化成功后,输出末尾会有一段 kubeadm join 命令,包含 token 和 ca-cert-hash。把它保存下来,如果丢了也不要慌,之后可以通过 kubeadm token create --print-join-command 再生成。
执行以下命令让当前普通用户使用 kubectl:
bash复制mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
4.4 安装网络插件并加入工作节点
控制平面初始化完成后,此时立刻 kubectl get nodes,看到的 master 节点可能还是 NotReady。不要着急排查,原因通常是网络插件还没装。
网络插件负责给每个 Pod 分配虚拟 IP,并打通跨节点通信。先安装 Flannel 或 Calico。以 Flannel 为例:
bash复制kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
如果你的网络环境访问 GitHub 不稳定,就先在工作环境里下载好 YAML 文件,再拷贝到 master 节点执行。安装完成后,可以观察插件 Pod:
bash复制kubectl get pods -n kube-flannel
插件 Pod 状态变成 Running,再回到两个工作节点执行 join:
bash复制sudo kubeadm join 192.168.x.21:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash>
工作节点 join 完成后回到 master 节点检查:
bash复制kubectl get nodes
三个节点都显示 Ready,才说明集群基础环境已经成立。
5. 验证集群功能:从节点状态到 Nginx 实例调度
5.1 先看节点状态,再看组件 Pod
集群刚搭好,不要急着部署业务应用。先确认控制平面组件和网络插件都正常。用下面两条命令做一个快速体检:
bash复制kubectl get nodes -o wide
kubectl get pods -A
get nodes -o wide 可以看到每个节点的 Internal IP、版本号、kubelet 状态。get pods -A 可以看到 kube-system 下的 etcd、apiserver、scheduler、controller-manager,以及网络插件 Pod。
正常的判断标准有三个:
- 三个节点都是 Ready。
- kube-system 和网络插件命名空间下的 Pod 全部 Running 或 Completed。
- 没有出现大量 CrashLoopBackOff。
如果节点状态是 Ready 但部分 Pod 反复重启,先别动集群,按第 6 部分的排查顺序走。
5.2 用 Nginx 验证跨节点调度和网络连通
集群验证最简单的手段是部署一个 Nginx 应用。在 master 节点执行:
bash复制kubectl create deployment nginx-demo --image=nginx --replicas=2
这会创建 2 个 Nginx Pod。继续创建一个 NodePort 类型的 Service,把 80 端口暴露到每台节点上:
bash复制kubectl expose deployment nginx-demo --port=80 --type=NodePort
kubectl get svc nginx-demo
Service 会分配一个 NodePort,例如 3xxxx。验证方法有两个:
一是看 Pod 是否分布到不同工作节点:
bash复制kubectl get pods -o wide
正常结果应该是一个 Pod 在 k8s-node1,另一个在 k8s-node2。
二是在宿主机或任意节点用 curl http://节点IP:NodePort 访问。如果能返回 Nginx 默认页面,说明 kube-proxy、iptables 规则、跨节点访问都是通的。
这个测试很能说明问题。后面的 Redis Cluster、Kafka 集群实验,本质上也是在验证这种跨节点通信能力。
5.3 关机、启动、快照的正确顺序
虚拟机环境里跑 K8s,最怕的不是集群坏了,而是不按顺序关机导致下次启动后状态混乱。我的习惯是:
关机顺序:先关工作节点,再关控制平面节点。因为工作节点关闭时,控制平面会发现节点失联并开始驱逐 Pod,如果控制平面先关,工作节点上的 kubelet 会反复尝试连接 apiserver,日志刷得很快。
启动顺序:先开控制平面节点,等它稳定运行,再开工作节点。不要三台一起开机。控制平面需要先拉起 etcd 和 apiserver,工作节点启动后需要向 apiserver 重新注册,如果 apiserver 还没有就绪,工作节点上的 kubelet 会一直重试,状态看起来像卡住。
快照的话,建议做两个关键快照点:
- 系统基础配置完成后一份快照,命名类似
sys-init。此时没有 Kubernetes,恢复后可以重新走 join 流程。 - 集群初始化成功、Nginx 验证通过后一份快照,命名类似
k8s-ready。以后做实验搞坏了,直接恢复到这个点,比重新建集群快得多。
注意:打快照前要先确认集群没有正在执行的批量任务。在大量创建或删除 Pod 的过程中打快照,恢复后可能遇到 etcd 数据与虚拟机磁盘状态不一致,表现是 Pod 状态异常但日志又看不出直接原因。
6. 常见故障排查与集群重置
6.1 节点 NotReady 时先看 kubelet 日志
三节点环境里最常见的故障就是某个节点变成 NotReady。这时候不要急着 restart kubelet,也不要马上重新 join。先登录到异常节点,看 kubelet 日志:
bash复制sudo journalctl -u kubelet -f
再回到 master 节点,看节点详情:
bash复制kubectl describe node k8s-node1
describe node 末尾会有 Node Conditions 和最近的事件,通常会说明原因。常见原因按频率排序:
- coredns 或网络插件 Pod 没有就绪,节点网络被判定异常。
- kubelet 所在节点时间偏差太大,导致证书校验失败。
- 磁盘空间不足,kubelet 触发了 eviction。
- 节点 IP 或 hostname 与预期不符,通常是克隆后没有处理 machine-id。
如果看到 kubelet 反复报 node not found,那大概率是 hostname 错误或者集群里的 Node 对象被清理了。最直接的验证方式是在异常节点上执行:
bash复制hostname
确认它和你预期的 k8s-node1 一致。
6.2 Pod 一直 ContainerCreating 时的排查链
Pod 卡在 ContainerCreating,在虚拟机环境里大多是运行时或网络插件的问题。按下面顺序排查:
先看 Pod 事件:
bash复制kubectl describe pod <pod-name>
事件里如果有 Failed to pull image,去节点上用 crictl images 看镜像是否存在,或者手动拉取验证网络。
事件里如果有 cgroup 相关报错,回查 containerd 的 SystemdCgroup 配置和宿主机内核参数。很多 1.29 集群的问题都出在 containerd 使用的是默认 cgroupfs,和 kubelet 用 systemd 不一致。
如果事件显示 Sandbox 创建失败,尝试重启 containerd:
bash复制sudo systemctl restart containerd
然后重新删除卡住的 Pod,让控制器重新创建。注意不要同时把所有 Pod 删掉,一次删一个,观察恢复情况。
如果只是某个节点的 Pod 一直 ContainerCreating,而其他节点正常,优先怀疑这台节点的 containerd 配置有问题或者镜像没有提前拉取。把其他节点能用的配置原样复制过来,再重启试一次。
6.3 VMware 平台层问题:网络不通、控制台连不上
有时候问题不在 Kubernetes,而在虚拟机平台本身。常见现象是:节点状态显示 Ready,但用 SSH 连不上;或者 VMware 控制台一直显示“无法连接到虚拟机”。
排查逻辑是先区分层级:
- 如果宿主机 ping 不通节点 IP,先看 VMware 网络编辑器和节点网卡是否还在 NAT 网段上。
- 如果三个节点之间 ping 不通,检查各节点的静态 IP 是否设置重复,或者 hosts 文件是否少了条目。
- 如果 VMware 控制台打不开虚拟机,先在 VMware 里把虚拟机“关闭”再重新“启动”,不要直接点恢复快照。
另外,宿主机如果安装了多个虚拟化软件,比如 VirtualBox 和 VMware 并存,NAT 网络可能互相影响。发现虚拟机网卡 IP 反复变化,优先检查 NAT 网段是不是改过。
bash复制brctl show
可以在 Linux 宿主机上查看虚拟网桥状态,但 Windows 宿主机没有这个命令。Windows 下主要靠 VMware 虚拟网络编辑器和 ipconfig 来核对网段。
6.4 环境搞乱之后的止损方案
如果 Kubernetes 集群状态已经乱到没法继续排查,重置比硬修快。kubeadm 提供了重置命令,可以在每个节点上执行:
bash复制sudo kubeadm reset -f
sudo rm -rf /etc/cni/net.d
sudo rm -rf $HOME/.kube
然后回到 master 节点重新执行 kubeadm init,工作节点重新执行 kubeadm join。注意:kubeadm reset 不会删除你已经部署的容器镜像,所以重新初始化时镜像拉取通常会快很多。
但如果连 containerd 都出问题了,可以直接从第 4 部分开始重来。有快照的话,恢复到 k8s-ready 是更省事的路径。
我自己遇到集群问题时的判断标准是:如果是单个节点异常,尽量修复;如果三个节点都在报错且日志互相矛盾,直接重置。本地虚拟机的意义在于快速试错,不是考验耐心。
最后留几个落地建议
这套 VMware 三节点 Kubernetes 1.29.14 环境,最值得盯住的不是“有没有初始化成功”,而是重启、快照、重置之后还能不能恢复。虚拟机环境天然适合反复实验,但前提是基础网络和版本锁定做得干净。
我个人的操作习惯是:把 sys-init 和 k8s-ready 两个快照当成保险,平时所有实验都在这两个点之上做;如果要验证中间件集群,比如 Redis Cluster 或 Kafka,先确认 Nginx 那个基础测试仍然正常,再往下叠加组件。
这样一套环境跑顺之后,你会明显感觉到,很多在文档里看起来简单的概念,真正落到三台虚拟机之间通信时,需要自己动手确认的细节比想象中多得多。
