【硬核实战】网线直连+离线环境:从零搭建 Kubernetes v1.28 集群避坑指南
摘要:你是否遇到过这种绝望场景:服务器在内网,没有交换机,只有一根网线直连两台物理机;Worker 节点完全断网,无法拉取镜像;K8s 初始化后 Flannel 插件一直
Init:0/2?
本文记录了一次真实的 Kubernetes v1.28 + Containerd 离线集群搭建过程,涵盖了手工迁移 Pause 镜像、CNI 插件降级适配、双机直连网络持久化等高阶排坑经验。
一、 为什么要写这篇博客?
在 K8s v1.24 版本之后,Docker 被弃用,Containerd 成为主流。对于习惯了 Docker 的老玩家来说,ctr 和 crictl 的命令本身就生疏,再加上物理环境的限制(网线直连、无外网),搭建难度指数级上升。
本文将解决以下核心痛点:
- 物理层:如何通过一根网线实现 Master 与 Worker 的稳定互联?
- 运行时:Worker 没网,怎么搞定 K8s 启动必须的 Sandbox (
pause) 镜像? - 网络插件:Flannel YAML 文件里的版本与本地镜像不一致,如何“移花接木”?
二、 架构与环境规划
为了还原最真实的“艰苦”环境,我们的配置如下:
- 架构:单 Master + 单 Worker
- 网络:双机网卡直连(无路由器/交换机)
- 系统:Ubuntu 20.04 / 22.04 LTS
- K8s 版本:v1.28
- CRI 运行时:Containerd
| 节点名称 | 角色 | IP 地址 (静态) | 网络情况 |
|---|---|---|---|
user01-nf5280m5 |
Master | 10.10.10.10 |
可通过 USB 共享临时上网 |
k8s-worker |
Worker | 10.10.10.20 |
纯内网,无互联网权限 |
三、 基础环境初始化(地基不稳,地动山摇)
⚠️ 注意:以下操作需在 Master 和 Worker 上同时执行。
1. 永久关闭 Swap(血的教训)
K8s 调度器强制要求关闭 Swap,否则 kubelet 会无限重启,且日志报错极其隐晦。
# 1. 临时关闭
sudo swapoff -a
# 2. 永久关闭(防止重启后集群挂掉)
# 自动注释掉 /etc/fstab 中包含 swap 的行
sudo sed -i '/swap/s/^\(.*\)$/#\1/g' /etc/fstab
# 3. 验证(必须全是 0B)
free -h
2. 开启内核转发
由于是直连网络,必须开启内核级转发,否则 Pod 之间无法跨节点通信。
# 加载必要模块
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# 开启转发(重启不失效)
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# 立即应用
sudo sysctl --system
3. 网络与主机名持久化
不要依赖 ip addr add 这种临时命令!必须写入 Netplan 配置文件,并配置 /etc/hosts。
配置 Hosts 映射:
sudo tee -a /etc/hosts <<EOF
10.10.10.10 user01-nf5280m5
10.10.10.20 k8s-worker
EOF
四、 Containerd 配置与离线镜像“空投”
1. 修正 crictl 指向
K8s 默认可能还会去找 Docker 的 socket,我们需要强制它看 Containerd。
sudo crictl config --set runtime-endpoint=unix:///run/containerd/containerd.sock --set image-endpoint=unix:///run/containerd/containerd.sock
2. 解决 Worker 没网拉不到 Pause 镜像的问题(重点!)
这是离线安装最容易卡住的地方。Worker 启动 Pod 时需要拉取 registry.k8s.io/pause:3.8,但它没网,会一直报错 ImagePullBackOff 或 i/o timeout。
操作手法:从 Master 搬运镜像到 Worker
- Step 1 (Master): 导出镜像
sudo ctr -n k8s.io images export pause38.tar registry.k8s.io/pause:3.8
- Step 2 (传输): 通过内网 SCP 发送
scp pause38.tar user@10.10.10.20:~/
- Step 3 (Worker): 导入镜像
# 必须导入到 k8s.io 命名空间,否则 K8s 看不见
sudo ctr -n k8s.io images import pause38.tar
五、 集群初始化与网络插件适配(避坑核心)
1. Master 初始化
sudo kubeadm init \
--apiserver-advertise-address=10.10.10.10 \
--image-repository registry.aliyuncs.com/google_containers \
--pod-network-cidr=10.244.0.0/16
初始化完成后,复制生成的 kubeadm join 命令,在 Worker 节点执行加入。
2. 安装 Flannel:手动降级与对齐
此时执行 kubectl get nodes,你会发现节点是 NotReady。因为 CNI 插件还没装。
坑点:官方最新的 kube-flannel.yml 可能引用了 v0.28.0 的镜像,但你本地预加载的可能是 v0.25.1。如果不改 YAML,Worker 节点会因为拉不到新镜像而卡死。
解决步骤:
A. 下载并修改 YAML (在 Master 执行)
wget https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
# 强制将 YAML 中的版本号替换为本地已有的版本
sed -i 's/v0.28.0/v0.25.1/g' kube-flannel.yml
sed -i 's/v1.8.0-flannel1/v1.4.0-flannel1/g' kube-flannel.yml
# 替换仓库地址(如果是从 docker.io 拉取的)
sed -i 's/ghcr.io\/flannel-io/docker.io\/flannel/g' kube-flannel.yml
kubectl apply -f kube-flannel.yml
B. 手动创建 CNI 目录 (在 Worker 执行)
Flannel 的 Init 容器需要将配置写入 /etc/cni/net.d,将二进制写入 /opt/cni/bin。如果系统没有这俩目录,Pod 会一直卡在 Init:0/2。
sudo mkdir -p /opt/cni/bin
sudo mkdir -p /etc/cni/net.d
C. 重启 Containerd 触发重试
sudo systemctl restart containerd
六、 验证与自愈测试
完成上述步骤后,等待 1-2 分钟。
1. 验证集群状态
在 Master 执行:
kubectl get nodes -o wide
期望输出:
NAME STATUS ROLES AGE VERSION
k8s-worker Ready <none> 33m v1.28.0
user01-nf5280m5 Ready control-plane 55m v1.28.15
看到 Ready,恭喜你,通关了!
2. 验证 Pod 状态
kubectl get pods -n kube-flannel
必须显示 Running。如果显示 Init:0/2,请检查上面的“创建 CNI 目录”步骤。
3. 断电重启自愈测试
既然是生产级搭建,必须经得起重启。
分别重启 Master 和 Worker。等待 3 分钟后再次查看 kubectl get nodes。如果依然 Ready,说明你的 Swap、IP、Kernel 配置均已完美持久化。
七、 总结
在离线和受限网络环境下搭建 K8s,最大的挑战不在于命令本身,而在于环境的一致性。
- Swap 和内核参数是地基。
- Pause 镜像的手动搬运是离线环境的生命线。
- CNI 插件的版本对齐是打通网络的最后一把钥匙。
希望这篇踩坑实录能帮你节省大量排错时间。如果你在搭建过程中遇到问题,欢迎在评论区留言交流!
更多推荐



所有评论(0)