
集群升級前的風險核查節(jié)點排空停滯時應先檢查 PodDisruptionBudget、不可驅逐工作負載和副本余量。入口類服務還要確認遷移期間是否保有足夠副本避免把維護窗口變成單點風險。在 Kubernetes 生產集群大版本升級例如從 1.25 跨版本升級至 1.28實踐中常見隱患除了工具或組件報錯外還包括平時未顯現(xiàn)、升級時觸發(fā)的配置死鎖與廢棄 API 盲區(qū)。本文復盤生產集群升級中的排障細節(jié)并提供升級前前的自動化校驗方案。節(jié)點驅逐指令下發(fā)后 Worker 節(jié)點卡在 Drain 狀態(tài)。運維團隊準備升級 Worker 節(jié)點的內核與 Kubelet 版本。執(zhí)行驅逐指令后終端持續(xù)輸出以下信息node/node-worker-03 cordoned evicting pod ingress-nginx/ingress-nginx-controller-7d9f4b59c-x29fz error when evicting pod ingress-nginx-controller-7d9f4b59c-x29fz (retry it later): Cannot evict pod as it would violate the pods disruption budget. evicting pod ingress-nginx/ingress-nginx-controller-7d9f4b59c-x29fz運維人員通過診斷命令查看 PodDisruptionBudget (PDB) 與節(jié)點資源分配kubectl get pdb -A kubectl describe pdb ingress-nginx-pdb -n ingress-nginx kubectl get nodes -o wide --show-labels輸出反映出兩項配置組合的制約首先為了保證入口網關的高可用前人配置了 PDB 資源ingress-nginx-pdb其中設置了minAvailable: 2。然而當前集群因為資源緊張Ingress controller 恰好只拉起了 2 個 Pod 副本且分布在node-worker-03和node-worker-04上。當試圖驅逐node-worker-03上的 Pod 時驅逐控制器Eviction API發(fā)現(xiàn)若殺死該 Pod在新的 Pod 成功調度并通過 ReadinessProbe 就緒之前集群里可用 Pod 數(shù)量就會降為 1違背了 minAvailable: 2 的強約束于是驅逐 API 拒絕執(zhí)行操作。此外新的節(jié)點已經被打上了NoSchedule污點導致驅逐控制器陷入了無終止條件的死循環(huán)。如果使用--force強刪 Pod可能會在流量高峰期引發(fā)入口網關的瞬間丟包。找出廢棄 API 存量資源與 PodDisruptionBudget 盲區(qū)。導致升級卡頓的另一個隱患是跨版本升級時被移出支持Deprecated Removed的 API 版本。在 K8s 升級演進中官方會定期移除老舊的 API Group。例如從 1.25 版本開始autoscaling/v2beta1、poddisruptionbudget/v1beta1以及k8s.gcr.io鏡像倉庫域名均被移出支持。部分團隊在使用 Helm 或 kubectl 部署應用時歷史上殘留了帶有舊apiVersion頭的 YAML 存根。升級前雖然 Pod 依然可以正常運行但一旦 API Server 升級完成帶有舊 API 版本的 Helm Release 在試圖執(zhí)行helm upgrade或回滾時會因為 API 無法識別導致失敗。因此在升級之前必須全面掃清集群內部所有的 PDB 邏輯死鎖與廢棄 API 存根。用 Go 編寫集群升級前廢棄 API 自動化掃描校驗工具。為了避免升級前人工查閱大量 YAML 文件團隊使用 Go 語言配合 K8s Discovery Client 編寫了一個自動化預檢與 PDB 死鎖掃描校驗工具package main import ( context fmt os path/filepath time metav1 k8s.io/apimachinery/pkg/apis/meta/v1 k8s.io/client-go/discovery k8s.io/client-go/kubernetes k8s.io/client-go/tools/clientcmd k8s.io/client-go/util/homedir ) func main() { var kubeconfig string if home : homedir.HomeDir(); home ! { kubeconfig filepath.Join(home, .kube, config) } config, err : clientcmd.BuildConfigFromFlags(, kubeconfig) if err ! nil { fmt.Printf(無法加載 kubeconfig: %v\n, err) os.Exit(1) } clientset, err : kubernetes.NewForConfig(config) if err ! nil { fmt.Printf(創(chuàng)建 ClientSet 失敗: %v\n, err) os.Exit(1) } discoveryClient, err : discovery.NewDiscoveryClientForConfig(config) if err ! nil { fmt.Printf(創(chuàng)建 DiscoveryClient 失敗: %v\n, err) os.Exit(1) } ctx, cancel : context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() fmt.Println( 1. 掃描集群內部 PodDisruptionBudget (PDB) 死鎖隱患 ) pdbs, err : clientset.PolicyV1().PodDisruptionBudgets().List(ctx, metav1.ListOptions{}) if err ! nil { fmt.Printf(獲取 PDB 失敗: %v\n, err) } else { for _, pdb : range pdbs.Items { // 判別邏輯若 minAvailable 設為了 100% 或與期望 Pod 數(shù)相等可能導致 Drain 阻塞 if pdb.Spec.MinAvailable ! nil { fmt.Printf([警告 PDB] 命名空間: %s, 名稱: %s, MinAvailable: %s (請核實是否有足夠副本用于驅逐)\n, pdb.Namespace, pdb.Name, pdb.Spec.MinAvailable.String()) } } } fmt.Println(\n 2. 檢查 API Server 暴露的 API Group 組版本 ) apiGroups, err : discoveryClient.ServerGroups() if err ! nil { fmt.Printf(獲取 API 組失敗: %v\n, err) os.Exit(1) } deprecatedGroups : map[string]string{ extensions/v1beta1: 已在 1.22 移出支持請遷移至 networking.k8s.io/v1, autoscaling/v2beta1: 已在 1.25 移出支持請遷移至 autoscaling/v2, policy/v1beta1: 已在 1.25 移出支持請遷移至 policy/v1, } for _, group : range apiGroups.Groups { for _, version : range group.Versions { gv : version.GroupVersion if replacement, isDeprecated : deprecatedGroups[gv]; isDeprecated { fmt.Printf([嚴重警告 廢棄 API] 發(fā)現(xiàn)存量 API 版本: %s - 解決方案: %s\n, gv, replacement) } } } fmt.Println(\n掃描完成請?zhí)幚硗戤吷鲜鲲L險點后再發(fā)起 K8s 控制平面升級。) }這段工具的核心邏輯在于調用 Discovery Client 獲取當前 K8s 控制平面注冊的 API 組與資源映射比對內嵌的棄用規(guī)則庫輸出集群中的合規(guī)性死角。在 PDB 檢測模塊中工具會列舉出所有可能觸發(fā)Cannot evict pod堵塞的預警配置方便運維在驅逐節(jié)點前提前介入調優(yōu)。演練 Kubernetes 控制平面升級時的檢查與保護動作。在掃清配置死角后團隊將升級流程標準化為帶有檢查點的 Shell 執(zhí)行腳本#!/usr/bin/env bash set -euo pipefail echo 升級前置檢查 1: 掃描棄用 API 與 PDB 死鎖 go run /opt/tools/k8s_upgrade_checker.go echo 升級前置檢查 2: 驗證 etcd 數(shù)據(jù)備份狀態(tài) ETCDCTL_API3 etcdctl --endpointshttps://127.0.0.1:2379 \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ snapshot save /var/backups/etcd-snapshot-pre-upgrade.db echo 執(zhí)行控制平面 upgrade plan 評估 kubeadm upgrade plan echo 依次安全驅逐節(jié)點流量 (設置超時保護) TARGET_NODEnode-worker-03 kubectl drain ${TARGET_NODE} \ --ignore-daemonsets \ --delete-emptydir-data \ --timeout5m || { echo CRITICAL: Drain node ${TARGET_NODE} timed out! Rolling back cordon... kubectl uncordon ${TARGET_NODE} exit 1 } echo 執(zhí)行 Kubelet 升級與重啟 apt-get update apt-get install -y kubelet1.28.2-00 systemctl restart kubelet kubectl uncordon ${TARGET_NODE} echo Node ${TARGET_NODE} upgraded and uncordoned successfully.升級 Kubernetes 需要嚴謹?shù)牟襟E控制。通過在升級前完成廢棄 API 的自動化掃描、解除 PDB 死鎖限制并在驅逐節(jié)點時設置嚴格的--timeout回退保護機制有助于在大版本升級中保障業(yè)務平滑演進。