摘要:你是否遇到过这种绝望场景:服务器在内网,没有交换机,只有一根网线直连两台物理机;Worker 节点完全断网,无法拉取镜像;K8s 初始化后 Flannel 插件一直 Init:0/2
本文记录了一次真实的 Kubernetes v1.28 + Containerd 离线集群搭建过程,涵盖了手工迁移 Pause 镜像CNI 插件降级适配双机直连网络持久化等高阶排坑经验。


一、 为什么要写这篇博客?

在 K8s v1.24 版本之后,Docker 被弃用,Containerd 成为主流。对于习惯了 Docker 的老玩家来说,ctrcrictl 的命令本身就生疏,再加上物理环境的限制(网线直连、无外网),搭建难度指数级上升。

本文将解决以下核心痛点:

  1. 物理层:如何通过一根网线实现 Master 与 Worker 的稳定互联?
  2. 运行时:Worker 没网,怎么搞定 K8s 启动必须的 Sandbox (pause) 镜像?
  3. 网络插件: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,但它没网,会一直报错 ImagePullBackOffi/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 插件的版本对齐是打通网络的最后一把钥匙。

希望这篇踩坑实录能帮你节省大量排错时间。如果你在搭建过程中遇到问题,欢迎在评论区留言交流!

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐