HomeLab Kubernetes 升级计划

背景

HomeLab 运行着一个 3 节点的 kubeadm 集群(全部 control-plane),承载了 Harbor、GitLab、WordPress、Longhorn 存储、Calico 网络等约 20 个服务。集群从 2022 年初部署后一直运行在 K8s v1.22.0,containerd v1.4.3,至今已超过 4 年未升级。

考虑到新版本的kubesphere最低支持到1.23,且后续升级需要逐步升级。

K8s 1.23 的关键变更

变更 影响
TTLAfterFinished feature gate 升为 GA 不允许再通过 --feature-gates 设置
IPv6DualStack feature gate 升为 GA 同上
rbac.authorization.k8s.io/v1alpha1 移除 我们已用 v1,无影响
--experimental-cluster-signing-duration 废弃 需改为 --cluster-signing-duration

kubeadm 升级过程

手动二进制升级流程

由于集群是手动安装(非 apt),升级步骤:

# 1. 下载二进制
curl -L https://dl.k8s.io/release/v1.23.17/bin/linux/amd64/kubeadm -o /root/kubeadm-1.23.17
curl -L https://dl.k8s.io/release/v1.23.17/bin/linux/amd64/kubelet -o /root/kubelet-1.23.17
curl -L https://dl.k8s.io/release/v1.23.17/bin/linux/amd64/kubectl -o /root/kubectl-1.23.17
chmod +x /root/kubeadm-1.23.17 /root/kubelet-1.23.17 /root/kubectl-1.23.17

# 2. 替换 kubeadm
cp /root/kubeadm-1.23.17 /usr/bin/kubeadm

# 3. 执行升级
kubeadm upgrade apply v1.23.17 --ignore-preflight-errors=ImagePull

# 4. 替换 kubelet(需先停止)
systemctl stop kubelet
cp /root/kubelet-1.23.17 /usr/bin/kubelet
cp /root/kubectl-1.23.17 /usr/bin/kubectl
systemctl daemon-reload
systemctl start kubelet

kubeadm 的镜像仓库变更

现象: kubeadm upgrade apply 在 preflight 阶段失败,尝试从 registry.k8s.io 拉取镜像超时。

原因: kubeadm v1.23.17 的发布时间晚于 K8s 镜像仓库从 k8s.gcr.io 迁移到 registry.k8s.io 的时间点,所以 kubeadm 二进制中硬编码了 registry.k8s.io 作为默认仓库。

解决: 预先从阿里云拉取并 tag 为两个地址:

# 从阿里云拉取
crictl pull registry.aliyuncs.com/google_containers/kube-apiserver:v1.23.17

# tag 为 registry.k8s.io(kubeadm 实际拉取的地址)
ctr -n k8s.io images tag \
  registry.aliyuncs.com/google_containers/kube-apiserver:v1.23.17 \
  registry.k8s.io/kube-apiserver:v1.23.17

# 加 --ignore-preflight-errors=ImagePull 跳过在线拉取检查
kubeadm upgrade apply v1.23.17 --yes --ignore-preflight-errors=ImagePull

问题:kubelet 二进制替换时 “Text file busy”

cp: cannot create regular file '/usr/bin/kubelet': Text file busy

原因: kubelet 进程正在运行,无法覆盖其二进制文件。

解决: 必须先停止 kubelet:

systemctl stop kubelet
cp /root/kubelet-1.23.17 /usr/bin/kubelet
systemctl start kubelet

升级后kube-controller-manager卡CrashLoopBackOff

升级完成后,发现 kube-controller-manager 在所有节点上 CrashLoopBackOff

问题1:已 GA 的 Feature Gate 不可再设置

错误: controller-manager 启动后立即 Fatal 退出(exit code 255)。

根因: manifest 中仍有 --feature-gates=TTLAfterFinished=true。在 K8s 1.23 中,TTLAfterFinished 已升为 GA 并锁定为 true,不允许通过 feature gate 再次显式设置

# 修复:删除所有 GA feature gates
sed -i '/--feature-gates=TTLAfterFinished=true/d' \
  /etc/kubernetes/manifests/kube-controller-manager.yaml \
  /etc/kubernetes/manifests/kube-apiserver.yaml \
  /etc/kubernetes/manifests/kube-scheduler.yaml

为什么 kubeadm 没有自动清理? 因为 kubeadm 在升级 manifest 时会保留用户自定义的 extraArgs。如果你手动添加了 feature-gates 到 kubeadm-config 的 extraArgs 中,它会原样保留。

问题2:双栈 cluster-cidr 被错误修改

清理 feature gate 后,controller-manager 仍然 Fatal:

error starting controllers: node:masterb has an allocated cidr: fc00::/64 
at index:1 that does not exist in cluster cidrs configuration

根因: kubeadm 升级时将 controller-manager 的 --cluster-cidr 从双栈 100.64.0.0/10,fc00::/48 修改为仅 IPv4 的 100.64.0.0/10。但节点已经分配了 IPv6 CIDR,造成不一致。

# 修复:恢复双栈配置
sed -i 's|--cluster-cidr=100.64.0.0/10|--cluster-cidr=100.64.0.0/10,fc00::/48|' \
  /etc/kubernetes/manifests/kube-controller-manager.yaml

根因推测: 这是因为之前 --feature-gates=IPv6DualStack=true 被删除后,kubeadm 认为不再需要双栈配置,自动剥离了 IPv6 CIDR。

问题3:修改 manifest 后 apiserver 没有自动重启

修改 static pod manifest 后,kubelet 应该自动检测变更并重启 Pod。但实际情况是:apiserver 停止后没有重启

原因: kubelet 停掉了旧的 apiserver 容器,但新的 Pod sandbox 因为某种原因没有被创建(containerd 层面的 sandbox 残留)。

解决: 手动清理旧的 sandbox 容器:

# 查看 sandbox
crictl pods | grep kube-apiserver-mastera
# 6d2f74d9e6688  44 hours ago  NotReady  kube-apiserver-mastera

# 强制清理
crictl stopp 6d2f74d9e6688
crictl rmp 6d2f74d9e6688

# kubelet 会在数秒内自动创建新的 sandbox 和容器

升级后遗留问题:Calico CNI 授权失败

升级完成后,新 Pod 无法创建,报错:

Failed to create pod sandbox: error getting ClusterInformation: 
connection is unauthorized: Unauthorized

根因

Calico CNI 插件在每个节点上使用一个 kubeconfig 文件(/etc/cni/net.d/calico-kubeconfig)来访问 K8s API。这个 kubeconfig 中的 token 是 Pod-bound Service Account Token,绑定到特定的 calico-node Pod UID。

K8s 升级过程中 calico-node Pod 被重建,UID 改变,导致旧 token 失效。而 CNI 是在 容器 sandbox 创建时被 kubelet 调用的,此时使用的是磁盘上的静态 kubeconfig,不是 Pod 内的自动挂载 token。

解决

重启 calico-node DaemonSet,让新 Pod 写入新的 CNI kubeconfig:

kubectl rollout restart ds calico-node -n kube-system

# 如果旧 Pod 因为 Calico 问题无法正常终止,强制删除
kubectl delete pod calico-node-xxxx -n kube-system --force --grace-period=0

# 验证新 kubeconfig 已更新
ls -la /etc/cni/net.d/calico-kubeconfig
# 确认时间戳是最新的

关键点: 这个问题不会在升级时立即暴露,因为已有的 Pod 不受影响。只有在创建新 Pod 时才会触发 CNI 调用并暴露 token 失效问题。

最终状态

NAME      STATUS   ROLES                  VERSION
mastera   Ready    control-plane,master   v1.23.17
masterb   Ready    control-plane,master   v1.23.17
masterc   Ready    control-plane,master   v1.23.17

所有服务正常运行,Calico 网络通信正常,Longhorn 存储无影响。下一步将继续升级到 1.24,届时需要面对 containerd CRI v1alpha2 废弃和 dockershim 移除的挑战。

 

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇