🚀 Kubernetes 1.17 → 1.28 跨代升级踩坑深度复盘
本次升级跨度极大,涵盖了 K8s 历史上几个极其重要的架构变革节点(特别是 1.20 宣布废弃 docker、1.24 正式移除 dockershim)。
整个过程的核心教训是: K8s 的升级不仅是二进制文件的替换,更是底层运行时(CRI)、网络接口(CNI)和组件配置规范(ComponentConfig API)的全面洗牌。
一、底层运行时切换:Docker → Containerd
❗现象
在将节点的 Container Runtime 从 Docker 切换到 containerd 后,kubelet 启动失败
通过 journalctl -u kubelet -e 查看日志,交替出现以下报错:
connect: no such file or directory /var/run/docker.sockFailed to start ContainerManager failed to get init PID... cgroupfs vs systemd
🔍深度排查与本质
-
Socket 路径残留:
Kubelet 在未明确指定 CRI Endpoint 时,旧版本会默认去寻找
docker.sock。即使系统里装了 containerd,kubelet 也无法“智能感应”。 -
Cgroup 驱动冲突(致命坑):
Docker 时代,很多集群默认的 cgroup driver 是
cgroupfs。但新版本的 containerd 和 kubelet 强烈推荐使用systemd。如果 containerd 配置了systemd,而 kubelet 配置残留了cgroupfs,kubelet 将无法创建 Pod 沙箱。
✅彻底解决方案
1. 修正 Kubelet 启动参数(针对旧版留存节点):
编辑 /etc/systemd/system/kubelet.service.d/10-kubeadm.conf 或 /var/lib/kubelet/kubeadm-flags.env,确保包含:
KUBELET_KUBEADM_ARGS="--container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --cgroup-driver=systemd"
2. 对齐 Containerd 配置 (/etc/containerd/config.toml):
必须生成并修改默认配置,否则必定踩坑:
# 开启 systemd cgroup 驱动
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
# 替换正确的国内源或指定版本的 pause 镜像
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.k8s.io/pause:3.9"
二、1.24 史诗级清理:Dockershim 残留与参数废弃
❗现象
在升级到 1.24+ 时,即使底层已经换成了 containerd,kubelet 依然拒绝启动,抛出:
Error: unknown flag: --network-plugindockershim not found相关的上下文初始化失败。
🔍深度排查与本质
--network-plugin=cni 成为历史:
在 1.24 之前,K8s 支持多种网络插件模式(如 kubenet)。1.24 之后,CNI 成为唯一标准,因此 --network-plugin=cni 这个参数被硬编码废弃并移除。带这个参数启动 kubelet 会直接 panic。
✅彻底解决方案
在升级 1.24 前置节点操作中,加入强制清理步骤:
# 1. 删除废弃的网络参数
sed -i 's/--network-plugin=cni//g' /var/lib/kubelet/kubeadm-flags.env
# 2. 如果还有旧的 docker 标识,一并清理
sed -i 's/--container-runtime=remote//g' /var/lib/kubelet/kubeadm-flags.env
# 3. 重新加载并重启
systemctl daemon-reload
systemctl restart kubelet
三、网络大崩溃:Calico 版本断代与 CNI 幽灵配置
❗现象
升级控制面后,发现所有节点变为 NotReady,新建的 Pod 全部卡在 Pending 或 CrashLoopBackOff。
kubectl describe pod coredns-xxx显示:networkPlugin cni failed to set up pod...- CoreDNS 日志报错:
dial tcp 10.96.0.1:443: i/o timeout(无法访问 apiserver)。
🔍深度排查与本质
-
API 废弃导致 CNI 插件瘫痪:
Calico 旧版本(如 3.1x)使用了 K8s 早期的 API 资源(如
extensions/v1beta1或旧版 CRD)。K8s 1.22+ 移除了大量 v1beta1 API,导致旧版 Calico Controller 直接崩溃。 -
多 CNI 配置冲突(幽灵文件):
测试或切换 CNI 时,
/etc/cni/net.d/目录下如果存在多个配置文件(如10-calico.conflist和10-flannel.conflist),kubelet 会按字典序加载,可能加载错误的 CNI 导致网络失联。 -
iptables 规则残留:
K8s 组件升级重启期间,旧的
kube-proxy规则未能及时刷新,导致 Overlay 网络流量被黑洞丢弃。
✅彻底解决方案
1. 严格遵守兼容性矩阵升级 CNI:
必须在升级 K8s 之前(或紧接着升级控制面之后),将 Calico 升级到匹配 1.28 的版本(建议 v3.26+)。
Bash
# 备份旧版 CRD 及配置
kubectl get ipamblocks,bgppeers,ippools -o yaml > calico-backup.yaml
# 应用新版 Calico
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml
2. 节点网络净空操作:
如果节点网络彻底混乱,在排空 Pod 后,执行彻底的清理:
rm -rf /etc/cni/net.d/*
rm -rf /var/lib/cni/*
ip link delete cni0
ip link delete flannel.1 # 如果曾经用过
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X
随后重启 kubelet 和 calico-node Pod,让其重建干净的网络拓扑。
四、1.28 高隐蔽深坑:Kubelet 配置驱动的全面接管
❗现象
在 1.28 节点上,kubeadm-flags.env 似乎完全“失效”了。即使把 --container-runtime-endpoint 写在里面,kubelet 也会报:
unknown command unix:///run/containerd/containerd.sock
🔍深度排查与本质
-
Systemd 解析的怪异行为:
高版本 Kubelet 将绝大部分核心配置移入了
KubeletConfigurationAPI(即config.yaml)。当某个 flag 在新版二进制中被彻底移除时,systemd 在执行ExecStart=/usr/bin/kubelet $KUBELET_KUBEADM_ARGS时,会因为遇到无法识别的 flag(如--container-runtime)而产生位移,把后面的 socket 路径当成了 Linux 命令去执行! -
Kubeadm 行为变更:
新版 kubeadm 初始化的集群,
/var/lib/kubelet/kubeadm-flags.env几乎是空的,所有运行时配置都写在了/var/lib/kubelet/config.yaml中。
✅彻底解决方案
全面放弃命令行参数(Flags),拥抱配置文件。
1. 清空冗余 flags:
修改 /var/lib/kubelet/kubeadm-flags.env,仅保留极少数必须通过命令行传递的参数(如 pod-infra 容器镜像,但建议也放进 containerd 配置里)。最好将其清空或仅保留 --v=2。
2. 将配置写入 config.yaml:
打开 /var/lib/kubelet/config.yaml,在末尾确保存在:
# 明确指定 CRI 接口
containerRuntimeEndpoint: "unix:///run/containerd/containerd.sock"
# 确保 cgroup 驱动一致
cgroupDriver: "systemd"
📊 K8s 1.17 → 1.28 升级核心避坑指南
| 涉及版本/组件 | ❗ 故障现象 / 报错特征 | 🎯 核心本质 (Root Cause) | ✅ 终极解决对策 (Action) |
|---|---|---|---|
| CRI 切换 (1.18+) | no such file /var/run/docker.sock |
Kubelet 不会自动识别 containerd | 启动参数强制指定 containerd.sock |
| 1.24 大更新 | Kubelet 启动失败: unknown flag: --network-plugin dockershim not found |
1.24 彻底移除了底层 dockershim 代码; 强制废弃了旧版网络插件传参模式。 | 清理 /var/lib/kubelet/kubeadm-flags.env 中所有包含 docker 和 network-plugin 的历史参数。 |
| Calico | 节点 NotReady CoreDNS 频繁超时/重启 |
旧版 Calico 调用的 v1beta1 API 被高版本 K8s 废弃; 节点存在多个 CNI 配置残影。 | 1. 严格按兼容矩阵提前升级 Calico。 2. 极端情况清理 /etc/cni/net.d/ 并 iptables -F。 |
| 1.28 终极演进 | systemd 报错: unknown command unix:///... |
高版本移除了大部分命令行 Flag,导致 systemd 把参数值当成了系统命令执行。 | 全面转向配置文件驱动: 清空命令行 flag,将配置全部写入 /var/lib/kubelet/config.yaml。 |
💡 核心工程建议: 跨度极大的升级,优先选择 “新增高版本节点 → 驱逐旧节点 Pod (Drain) → 下线旧节点” 的轮替模式,而非在原节点上原地升级,可规避 90% 的历史配置残留风险。