我用 kubeadm 在虚拟机中搭好了一个可用的 Kubernetes 集群,它现在撑起了我家庭云的“大脑”。
这篇文章记录整个过程,踩坑不少,也收获很多。
🧱 我的集群结构
- 系统环境:Ubuntu Server 22.04(运行在 KVM 虚拟机中)
- 控制节点:
k8s-master(2 核 4G,初步实验用) - 工作节点:后续通过
kubeadm join 加入 - 网络插件:Flannel
- 容器运行时:Containerd
- 组件:Helm、Kubernetes Dashboard
- 宿主机通过 FRP 实现端口转发(如 Dashboard)
🚧 环境准备:内核 & 模块配置
Kubernetes 需要开启 IP 转发,并加载 overlay 和 br_netfilter 模块:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 启用 IP 转发
cat << EOF | tee /etc/sysctl.d/k8s.conf
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
# 加载必要内核模块
cat << EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
|
🐳 安装 Containerd
我使用的是 containerd v1.7.27:
1
2
3
4
5
6
7
8
9
10
| wget https://github.com/containerd/containerd/releases/download/v1.7.27/containerd-1.7.27-linux-amd64.tar.gz
sudo tar Cxzvf /usr/local containerd-1.7.27-linux-amd64.tar.gz
# 获取并注册 containerd 的 systemd 服务
sudo mkdir -p /usr/local/lib/systemd/system
sudo curl -o /usr/local/lib/systemd/system/containerd.service https://raw.githubusercontent.com/containerd/containerd/main/containerd.service
# 启动 containerd
sudo systemctl daemon-reload
sudo systemctl enable --now containerd
|
安装成功后用 sudo systemctl status containerd 验证运行状态。
🔐 安装 runc & CNI 插件
1
2
3
4
5
6
7
8
9
| # 安装 runc
wget https://github.com/opencontainers/runc/releases/download/v1.2.6/runc.amd64
sudo install -m 755 runc.amd64 /usr/local/sbin/runc
# 安装 CNI 插件
wget https://github.com/containernetworking/plugins/releases/download/v1.7.1/cni-plugins-linux-amd64-v1.7.1.tgz
sudo mkdir -p /opt/cni/bin
sudo tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.7.1.tgz
|
⚙️ 配置 Containerd
1
2
| sudo mkdir -p /etc/containerd/
sudo bash -c 'containerd config default > /etc/containerd/config.toml'
|
编辑 /etc/containerd/config.toml:
- 设置
SystemdCgroup = true - 设置 sandbox 镜像为kubernetes 版本对应的
pause 版本
1
2
3
4
5
| [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.k8s.io/pause:3.10"
|
保存后重启:
1
| sudo systemctl restart containerd
|
🔧 安装 kubeadm / kubelet / kubectl
1
2
3
4
5
6
7
8
9
10
| sudo swapoff -a
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33/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.33/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
|
配置 kubelet 使用 systemd cgroup:
1
2
| echo 'KUBELET_EXTRA_ARGS=--cgroup-driver=systemd' | sudo tee /etc/default/kubelet
sudo systemctl enable --now kubelet
|
kubelet 现在每隔几秒就会重启,因为它陷入了一个等待 kubeadm 指令的死循环。
🏷️ 设置主机名与 hosts 文件
在部署 Kubernetes 前,为了方便集群通信,统一为每台虚拟机设置了主机名,并配置了 /etc/hosts,避免因 DNS 失败导致通信异常。
✅ 修改主机名
以 k8s-master 为例:
1
| sudo hostnamectl set-hostname k8s-master
|
其他节点按需设置为 k8s-worker1、k8s-worker2、k8s-worker3。
✅ 编辑 /etc/hosts
在每台节点的 /etc/hosts 文件中添加以下内容:
1
2
3
4
| 192.168.122.169 k8s-master
192.168.122.170 k8s-worker1
192.168.122.171 k8s-worker2
192.168.122.172 k8s-worker3
|
确保所有节点都能通过主机名互相通信。
🚀 初始化集群(主节点)
查看本机 IP:
1
2
| ip route show | grep default
# 举例:default via 192.168.122.1 dev enp1s0 proto dhcp src 192.168.122.169 metric 100
|
执行初始化命令:
1
2
3
4
5
6
| sudo kubeadm init \
--control-plane-endpoint=k8s-master \
--apiserver-advertise-address=192.168.122.169 \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.96.0.0/12
|
这里的k8s-master是虚拟机的主机名,--apiserver-advertise-address 是虚拟机 IP 地址。
完成后执行以下操作:
1
2
3
| mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
|
🌐 安装 Flannel 网络插件
1
| kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
|
安装 Pod 网络后,你可以通过在 kubectl get pods --all-namespaces 输出中检查 CoreDNS Pod 是否 Running 来确认其是否正常运行。 一旦 CoreDNS Pod 启用并运行,你就可以继续加入节点。
如果你的网络无法正常工作或 CoreDNS 不在 Running 状态,请查看 kubeadm 的故障排除指南。
通过以下命令查看 Flannel Pod 的日志:
1
| kubectl logs -n kube-flannel kube-flannel-ds-2s5dr
|
如果出现 Failed to check br_netfilter 报错,请检查你的虚拟机是否加载了对应模块(见文首)。
🤝 Worker 节点加入集群
在完成主节点初始化后,kubeadm 会生成一条 join 命令,用于将工作节点加入集群。
在每台 Worker 节点上执行:
1
2
| sudo kubeadm join k8s-master:6443 --token x3n5o4.innk28m148gig4rf \
--discovery-token-ca-cert-hash sha256:1b5fe8185016ecf8d75f8d9d7753637a6cb1406ac16b41c17e3b10c8dcbc0e98
|
加入成功后,在 Master 节点查看节点状态:
输出示例:
1
2
3
4
5
| NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 10m v1.33.0
k8s-worker1 Ready <none> 2m v1.33.0
k8s-worker2 Ready <none> 2m v1.33.0
k8s-worker3 Ready <none> 2m v1.33.0
|

🧰 安装 Helm、Dashboard (主节点)
安装 Helm
1
| curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
安装 Kubernetes Dashboard
1
2
3
4
| # Add kubernetes-dashboard repository
helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboard/
# Deploy a Helm Release named "kubernetes-dashboard" using the kubernetes-dashboard chart
helm upgrade --install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard --create-namespace --namespace kubernetes-dashboard
|
端口转发:
1
| kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard-kong-proxy 8443:443 --address=0.0.0.0
|
📡 宿主机转发端口到虚拟机
我使用 frps + frpc 实现了从公网访问虚拟机服务。
例如,将宿主机的 8443 端口转发到虚拟机的 Dashboard:
1
2
3
4
5
6
| [[proxies]]
name = "kubernetes-dashboard"
type = "tcp"
localIP = "192.168.122.169"
localPort = 8443
remotePort = 8443
|

🔍 常用命令收集
1
2
3
4
5
6
7
8
9
10
11
| # 管理容器(类似 docker ps)
sudo crictl ps
# 查看节点
kubectl get nodes
# 查看 Pod
kubectl get pods -A
# 查看日志
kubectl logs -n kube-system kube-controller-manager-xxx
|
📌 小结
虽然是从头部署,但整个流程清晰明确,只要细节不马虎,完全可以在家用虚拟机环境中跑一个 Kubernetes 练手集群。
下一步,我会尝试用这个集群跑起我的个人博客,并通过 Drone 实现 GitOps 风格的持续部署。