K8s升级踩坑总结

Jan 于 2026-04-26 发布 浏览量

🚀 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 查看日志,交替出现以下报错:

  1. connect: no such file or directory /var/run/docker.sock
  2. Failed to start ContainerManager failed to get init PID... cgroupfs vs systemd

🔍深度排查与本质

  1. Socket 路径残留:

    Kubelet 在未明确指定 CRI Endpoint 时,旧版本会默认去寻找 docker.sock。即使系统里装了 containerd,kubelet 也无法“智能感应”。

  2. 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 依然拒绝启动,抛出:

🔍深度排查与本质

--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 全部卡在 PendingCrashLoopBackOff

🔍深度排查与本质

  1. API 废弃导致 CNI 插件瘫痪:

    Calico 旧版本(如 3.1x)使用了 K8s 早期的 API 资源(如 extensions/v1beta1 或旧版 CRD)。K8s 1.22+ 移除了大量 v1beta1 API,导致旧版 Calico Controller 直接崩溃。

  2. 多 CNI 配置冲突(幽灵文件):

    测试或切换 CNI 时,/etc/cni/net.d/ 目录下如果存在多个配置文件(如 10-calico.conflist10-flannel.conflist),kubelet 会按字典序加载,可能加载错误的 CNI 导致网络失联。

  3. 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

🔍深度排查与本质

  1. Systemd 解析的怪异行为:

    高版本 Kubelet 将绝大部分核心配置移入了 KubeletConfiguration API(即 config.yaml)。当某个 flag 在新版二进制中被彻底移除时,systemd 在执行 ExecStart=/usr/bin/kubelet $KUBELET_KUBEADM_ARGS 时,会因为遇到无法识别的 flag(如 --container-runtime)而产生位移,把后面的 socket 路径当成了 Linux 命令去执行!

  2. 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 中所有包含 dockernetwork-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% 的历史配置残留风险。