跳轉到

EKS QA 專屬 Node Group 建立除錯全記錄(ps-eks-ng-qaenv 最終成功版)

文檔資訊

  • 分類: tools
  • 難度: advanced
  • 預估閱讀時間: 25 分鐘
  • 標籤: eks, nodegroup, eksctl, cluster-autoscaler, cni, iam, cloudformation, launch-template, qa-isolation, security-group, drain, asg

摘要

QA-test Pod 全部 Pending,原因是無 site=qa-test label 的 node。過程包含:救急 ASG 擴容 → 建立腳本 create_qa_test_dedicated_node.sh 自動貼 label → 嘗試建立專屬 NG 共 5 次失敗(SSH Key 缺失、LT 殘留衝突、capacityType 欄位非法、SG 少帶一個)→ 改名 ps-eks-ng-qaenv 帶齊兩個 SG 成功建立 → 請管理員移除 CA tag → verify 6/6 通過 → 確認 drain 救急 node 的可行性與正式遷移路徑。

關鍵學習

  • eksctl 建立 NG 失敗後會留下殘留 Launch Template,同名重建時報 InvalidLaunchTemplateName.AlreadyExistsException,必須先清除(或改名)才能重建

  • eksctl YAML 不支援 capacityType 欄位,會報 json: unknown field "capacityType",需直接移除;AWS 預設已是 ON_DEMAND

  • propagateASGTags: true 會將 k8s.io/cluster-autoscaler/enabled 和 k8s.io/cluster-autoscaler/ tag 傳播到 ASG,導致 CA 管理此 NG 並可能縮容,需事後請管理員刪除

  • 新 NG 使用 AmazonEKSAutoNodeRole 而非現有 NG 的 IAM Role 時,Node CNI 無法初始化(cni plugin not initialized),必須明確指定 instanceRoleARN

  • eksctl 讀取現有 NG 的 diskSize 回傳 null,須從 Launch Template 的 EBS 設定取得真實值(本例為 100GB gp3)

  • Security Group 有兩個:EKS cluster SG(eks-cluster-sg-)和 remoteAccess SG(eksctl--remoteAccess),兩個都要在 securityGroups.attachIDs 帶入,少一個導致 NodeCreationFailure

  • eksctl 建立失敗後 Stack 狀態為 ROLLBACK_FAILED 時,需用 --deletion-mode FORCE_DELETE_STACK 才能強制刪除

  • CA 只要看到 ASG 同時有 k8s.io/cluster-autoscaler/enabled 和 k8s.io/cluster-autoscaler/ 兩個 tag,就會管理該 NG

  • 救急 node 上有其他業務 pod 時,drain 前必須確認這些 pod 有其他 node 可接手,否則 drain 會造成服務中斷

  • annotation cluster-autoscaler.kubernetes.io/scale-down-disabled=true 要用 kubectl get node -o jsonpath 確認,不能直接看 kubectl annotate 的輸出訊息

  • CA/ASG/NG 三層架構:CA 是控制器,偵測 ASG tag 決定是否管理;ASG 是縮放執行層,定義 min/max/desired;NG 是 EKS 抽象層,管理 EC2 lifecycle 和 Kubernetes node registration

  • pod 有設定 nodeSelector site: qa-test 時,drain 後會自動調度到有對應 label 的 NG,無需手動 redeploy

技術細節

環境資訊

  • Cluster: ps-eks(ap-southeast-1)
  • 現有 NG: ng-private-managed-1,機型 m5.2xlarge,diskSize 100GB gp3(需從 LT 查,eksctl describe-nodegroup 回傳 null)
  • 最終成功 NG: ps-eks-ng-qaenv
  • IAM Role: eksctl-ps-eks-nodegroup-ng-private-NodeInstanceRole-34Qw1NlObe9l
  • Security Groups: sg-0771c3c6f9d3bf548(cluster SG)+ sg-0145ff29d2ad1da6d(remoteAccess SG)

失敗時間軸(5 次)

第一次(ps-eks-ng-qa-test):SSH Key ps-eks-cluster-ssh-key 不存在,現有 NG remoteAccess 查詢回傳 null,腳本不應傳 sshAccess 參數。

第二次(救急路線):擴容現有 NG ASG 到 4 台,腳本 create_qa_test_dedicated_node.sh 自動完成等待 Ready、確認無業務 pod、貼 label,救急 node ip-192-168-157-85 上線。期間嘗試用 AmazonEKSAutoNodeRole 建立 node,CNI 無法初始化(cni plugin not initialized),改回正確 NG 的 ASG 擴容。

第三次(ps-eks-ng-qa-test 重建):LT 名稱衝突,InvalidLaunchTemplateName.AlreadyExistsException,失敗後 LT 殘留,無 ec2:DeleteLaunchTemplate 權限,改名繞過。

第四次(ps-eks-ng-qa):capacityType 欄位不合法,json: unknown field "capacityType",移除即可。

第五次(ps-eks-ng-qa 重建):NodeCreationFailure,compare_ng_launch_templates.sh 揭露差異:新 LT 只有 sg-0771c3c6f9d3bf548,少了 sg-0145ff29d2ad1da6d(remoteAccess SG)。

CA Tag 機制說明

propagateASGTags: true 把以下 tag 帶入 ASG:

  • k8s.io/cluster-autoscaler/enabled = true
  • k8s.io/cluster-autoscaler/ps-eks = owned

CA 偵測到這兩個 tag 後開始管理此 NG,可能在 pod 少時縮容到 minSize。最終解法是管理員刪除這兩個 tag,CA 即看不到此 NG。無法在 eksctl YAML 中阻止這兩個 tag 被 propagate,只能事後移除。

遷移前驗證(pre_migrate_check.sh 輸出)

救急 node ip-192-168-157-85 上有: - qa-test pod:5 個(可 drain,因 nodeSelector site: qa-test 會自動調度到 QA 專屬 NG) - DaemonSet pod:13 個(drain 自動跳過) - 其他業務 pod:20 個(需確認有其他 node 可接手)

What Changed

救急階段:QA pod 全 Pending 超過 2 天,原因是無 site=qa-test label 的 node。透過 create_qa_test_dedicated_node.sh 自動完成 ASG 擴容、等待 Ready、確認無業務 pod、貼 label,救急 node ip-192-168-157-85 成功上線。過程中發現 annotation 要用 kubectl get node -o jsonpath 確認,不能看 kubectl annotate 輸出。

正規建立階段(5 次嘗試):嘗試建立 QA 專屬 NG 失敗 4 次:SSH Key 不存在 → LT 殘留同名衝突(無刪除權限,改名繞過)→ capacityType 非法欄位 → SG 只帶一個導致 NodeCreationFailure。關鍵診斷工具 compare_ng_launch_templates.sh 揭露 SG 差異(少了 remoteAccess SG sg-0145ff29d2ad1da6d)。最終用名稱 ps-eks-ng-qaenv、在 securityGroups.attachIDs 帶齊兩個 SG 成功建立。

保護設定與驗證propagateASGTags: true 把 CA tag 帶入 ASG,CA 開始管理此 NG。請管理員刪除兩個 tag 後,verify_qa_ng_protection.sh 驗證 6/6 通過:CA 無法看到此 NG,minSize=1 保底,qa-test pod 全部 Running(5/5)。共留下 2 個殘留 LT(lt-0033d10b3efe87d57、lt-01dc8005cf3ee7075)和多個殘留 CloudFormation Stack 需管理員清理。

So What

QA 環境一直依賴共享 Node,導致資源競爭且難以隔離故障。本次建立獨立 NG 實現 pod 與資源完全隔離,CA 無法縮容此 NG(tag 已移除,minSize=1 保底),保障 QA 環境穩定。

整個過程踩遍了 eksctl 建立 Managed NG 的常見陷阱(LT 衝突、IAM Role、capacityType 欄位、SG 遺漏、CA tag),產出的工具腳本(preflight、cleanup、preview、verify、pre_migrate_check)可直接複用於未來的 NG 建立流程。

Trade-offs

  • 獨立 NG vs 貼 label 的單一 node:獨立 NG 有完整 ASG 縮放配置和清楚的成本追蹤,代價是略高(主要是 EC2 費用;EKS NG 管理費幾乎為零)
  • CA tag 移除 vs 保留:移除 tag 後 CA 看不到此 NG,縮容完全由 minSize 控制;若保留,CA 可依需求縮到 minSize,但有意外縮容風險
  • 改名重建 vs 等管理員刪 LT:改名可立即解決、不阻塞工作;代價是留下舊名稱的殘留資源需後續清理(本次共留 2 個殘留 LT 和 2 個殘留 Stack)
  • drain 遷移 vs 重新 deploy:drain 讓 pod 自然調度到正確 NG(因有 nodeSelector site: qa-test),無需手動重新 deploy;但需先確認救急 node 上的非 qa-test pod 有其他 node 可接手
  • Istio 跨 NG 通訊:QA NG 與 Istio 所在 NG 不同,但同一 VPC 子網路,通訊不受影響

Try It Fast

# 建立前完整檢查(17 項驗證)
./qa_ng_tools.sh preflight

# 預覽 YAML 並與現有 NG 比對 SG/Disk/IAM(不實際建立)
./preview_qa_ng_yaml.sh

# 實際建立 QA 專屬 NG
./1_1_1_createQaTestNodeGroup.sh

# 驗證 CA 保護設定(確認 CA tag 已移除,6/6 項目通過)
./verify_qa_ng_protection.sh

# 查詢殘留資源(供管理員清理)
./qa_ng_tools.sh cleanup

# 比對新舊 NG 的 Launch Template 差異(SG/Disk 是關鍵)
./compare_ng_launch_templates.sh

# 遷移前檢查(救急 node 是否可 drain、pod 是否可安全遷移)
./pre_migrate_check.sh

# 確認 annotation 是否正確設定(必須用 jsonpath,不能直接看輸出)
kubectl get node <node-name> -o jsonpath='{.metadata.annotations.cluster-autoscaler\.kubernetes\.io/scale-down-disabled}'
# 期望輸出: true

# 確認 CA tag 是否已移除
aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names eks-ps-eks-ng-qaenv-62cea1d8-6fc4-2251-376c-6ac1ecf7eb9c \
  --region ap-southeast-1 \
  --query 'AutoScalingGroups[0].Tags[?starts_with(Key, `k8s.io/cluster-autoscaler/enabled`) || starts_with(Key, `k8s.io/cluster-autoscaler/ps-eks`)].{Key:Key,Value:Value}' \
  --output json
# 期望輸出: []

# QA pod 遷移(drain 救急 node,pod 自動調度到 QA 專屬 NG)
kubectl drain ip-192-168-157-85.ap-southeast-1.compute.internal \
  --ignore-daemonsets --delete-emptydir-data

Recommendation

  1. 每次建立 NG 前必跑 ./qa_ng_tools.sh preflight,確保無 LT/Stack/NG 殘留,避免同名衝突
  2. NG 建立失敗後,若無刪除 EC2 LT 的權限,直接改名重建;同時請管理員用 ./admin_cleanup_commands.sh 輸出的指令清理殘留
  3. eksctl YAML 不要加 capacityType 欄位,eksctl schema 不接受此欄位(AWS 預設 ON_DEMAND)
  4. diskSize 需從 Launch Template 的 EBS 設定取得,不要從 eksctl describe-nodegroup 讀(會得到 null)
  5. Security Group 要帶兩個:cluster SG(eks-cluster-sg-*)和 remoteAccess SG(eksctl-*-remoteAccess),在 eksctl YAML 的 securityGroups.attachIDs 明確列出;用 ./compare_ng_launch_templates.sh 對比驗證
  6. 建立後立即執行 ./verify_qa_ng_protection.sh,若 CA tag 存在,馬上請管理員刪除兩個 tag(k8s.io/cluster-autoscaler/enabledk8s.io/cluster-autoscaler/ps-eks
  7. 遷移前跑 ./pre_migrate_check.sh,確認救急 node 上的非 qa-test pod 有其他 node 可接手,再執行 drain
  8. drain 遷移優於重新 deploykubectl drain <救急-node> --ignore-daemonsets --delete-emptydir-data,pod 會因 nodeSelector site: qa-test 自動調度到 QA 專屬 NG
  9. annotation 確認要用 jsonpathkubectl get node <n> -o jsonpath='{.metadata.annotations.cluster-autoscaler\.kubernetes\.io/scale-down-disabled}',直接看 kubectl annotate 輸出不可靠

本文檔由 Semi-Brain 自動生成

Session ID: 9678fdc0-346d-4959-94b9-7caae9731d2f

分析信心度: 98%