跳轉到

Jenkins Pipeline + MSK IAM Auth 三層除錯:Pod 名稱錯誤、缺少 aws-msk-iam-auth JAR、分支 Image 造成 CrashLoopBackOff(融合演化版 v11)

文檔資訊

  • 分類: debugging
  • 難度: intermediate
  • 預估閱讀時間: 15 分鐘
  • 標籤: kafka, aws-msk, kubernetes, jenkins, iam-auth, kubectl-exec, deployment, imagepullbackoff, verify-script, configmap-diff, crashloopbackoff, image-version, redis-index, branch-deploy

摘要

Jenkins ResetKafkaOffset Pipeline 連續出現三層錯誤:(1) kubectl exec 使用裸 Pod 名稱導致 NotFound;(2) kafka-client container 缺少 aws-msk-iam-auth JAR;(3) kafka-client 修復後,syuejun 的 game/sink pods 仍 CrashLoopBackOff。根本原因是 syuejun 使用分支 image(pocketstore:26.03.syuejun,基於 feat/2026/refactor-mission_Tutorial),而非穩定版 0.14.0。RedisDBIndex 差異(15 vs 8)是正常站點隔離設計,checkRedisIndex.py 實際輸出確認 index=15 在 redis-dev-1 與 valkey-dev-1 均在空閒池中,不是問題根因。v11 為 v10 第三次確認版,所有結論一致。

關鍵學習

  • kubectl exec 應使用 deploy/<name> 而非裸 Pod 名稱,避免 Pod 重建後名稱改變失效

  • MSK IAM 認證需要 aws-msk-iam-auth JAR 在 container classpath,缺少時報 IAMClientCallbackHandler could not be found

  • 同一 Deployment 不同時間點執行可能不一致:舊 Pod 仍帶有 JAR 時成功,新 Pod rollout 後失敗(3/26 snapshot 成功的原因)

  • ImagePullBackOff 卡住時,刪除非 Running Pod 讓 Deployment controller 重新 reconcile

  • 每個站點可能使用不同 image tag(分支 build vs 穩定版),compare-image-versions.sh 是快速診斷手段

  • AutoBuildSiteDeploy 每次從指定 branch 重 build image,CrashLoopBackOff 若發生在特定站點,優先懷疑分支程式碼問題

  • checkRedisIndex.py 顯示的「空閒 index」是可分配的 index,不代表配置錯誤

  • compare-site-config.sh 用替換站點名稱後 diff 的方式,能快速識別兩站點 ConfigMap 的實質差異

技術細節

問題一:kubectl exec 使用裸 Pod 名稱

原始 Pipeline 指令直接使用 Deployment 名稱 kafka-client,實際 Pod 名稱為 kafka-client-d948649db-ml6gn,導致 Error from server (NotFound): pods "kafka-client" not found

正確寫法:

kubectl exec -i -n ps --context gamania-ps-dev deploy/kafka-client -- kafka-topics.sh ...

問題二:MSK IAM Auth JAR 遺失

修正 Pod 名稱後出現 IAMClientCallbackHandler could not be found。根因是 kafka-client Deployment image 被更換,新 image 未包含 aws-msk-iam-auth-*.jar。套用 upgrade-scripts/kafka-client-pod.yaml 後修復,新 image 含 aws-msk-iam-auth-2.3.2-all.jar(14M),位於 /opt/bitnami/kafka/libs/

問題三:syuejun CrashLoopBackOff 根因

compare-image-versions.sh 揭示關鍵差異:syuejun 使用 pocketstore:26.03.syuejun,而 snapshot/dev/beta 使用 pocketstore:0.14.0

AutoBuildSiteDeploy #584 完整 Pipeline 日誌確認:buildBranch=feat/2026/refactor-mission_TutorialSkipBuildImage=false,BuildServer #1188 從該分支成功 build image。真正根因為分支應用程式碼問題,而非 ConfigMap 差異。

ConfigMap diff 唯一實質差異為 RedisDBIndex(syuejun=15,snapshot=8),但 checkRedisIndex.py 確認: - redis-dev-1 空閒池含 [15, 14, 13, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - valkey-dev-1 空閒池含 [15, 4, 3]

index=15 已正確釋放,不是問題根因

What Changed

第一階段:Pipeline 腳本修正

將 Jenkins Pipeline 中 kubectl exec 的目標從裸 Pod 名稱 kafka-client 改為 deploy/kafka-client,使其不依賴特定 Pod 名稱,能在 Pod 重建後持續有效。

第二階段:kafka-client image 修復

套用 upgrade-scripts/kafka-client-pod.yaml(含正確 image),新 image 包含 aws-msk-iam-auth-2.3.2-all.jar(14M)。清除 ImagePullBackOff 失敗 Pod 後 rollout 成功,verify-kafka.sh 輸出 ALL CHECKS PASSED

第三階段:版本演進確認(v11)

v2 文檔誤以為 RedisDBIndex 差異(15 vs 8)是 syuejun CrashLoopBackOff 的根因。v3 修正此結論,v4-v7 以 checkRedisIndex.py 實際輸出作為佐證。v8/v9 補充 AutoBuildSiteDeploy #584 完整 Pipeline 日誌(含 BuildServer #1188 從 feat/2026/refactor-mission_Tutorial 分支 build 的確認),強化「分支程式碼問題」的結論。v10/v11 為連續融合確認版,與 v9 結論完全一致,無新增推翻性結論。

So What

這個除錯過程揭示了多環境 K8s 部署的三個典型陷阱:

陷阱一:硬編碼 Pod 名稱在自動化腳本中是定時炸彈。Pod 因任何原因重建後立即失效。改用 deploy/<name> 是低成本的永久修復。

陷阱二:image 版本升級容易遺漏依賴 JAR,且因舊 Pod 不立即替換,會出現「某些執行成功、某些失敗」的間歇性問題,容易誤導排查方向。

陷阱三:站點隔離環境中,每個站點可能基於不同分支 build image。當某站點 CrashLoopBackOff 而其他站點正常時,優先比對 image 版本,不要先看 ConfigMap 差異。

Trade-offs

  • 使用 deploy/<name> exec:穩定不依賴 Pod 名稱;但 kubectl 會隨機選擇一個 ready 的 Pod,多 replica 時行為可能不一致
  • JAR 打包進 image vs InitContainer 下載:打包進 image 部署簡單但 image 較大;InitContainer 方式靈活但需要 S3/網路存取,增加啟動時間
  • 直接 rollout vs 先測試新 image:直接 apply 較快,但若 image 有問題會造成短暫中斷
  • 刪除失敗 Pod vs 等待自動重試:刪除 Pod 可立即讓 controller 重試;但若根本原因未解決會持續循環
  • 站點用分支 image vs 穩定版 image:分支 image 讓開發者隔離測試新功能;但若分支有問題會造成該站點 CrashLoopBackOff

Try It Fast

# 1. 確認 kafka-client JAR 是否存在
kubectl exec -i -n ps --context <ctx> deploy/kafka-client -c kafka \
  -- find /opt/bitnami/kafka/libs /usr/share/java -name 'aws-msk-iam-auth*' 2>/dev/null

# 2. 清除 ImagePullBackOff 的 Pod 並重新 apply
kubectl delete pod -n ps --context <ctx> -l app=kafka-client \
  --field-selector=status.phase!=Running
kubectl apply -f ./upgrade-scripts/kafka-client-pod.yaml --context <ctx>
kubectl rollout status deploy/kafka-client -n ps --context <ctx> --timeout=120s

# 3. 比較所有站點的 image 版本(優先診斷 CrashLoopBackOff 差異)
./compare-image-versions.sh

# 4. 比對兩站點 ConfigMap 差異
./compare-site-config.sh <broken-site> <working-site>

# 5. 確認 Redis index 空閒狀態
python3 checkRedisIndex.py --kubeContext <ctx> --site <site> --redisName valkey-dev-1

Recommendation

  1. 所有 CI/CD Pipeline 中的 kubectl exec 一律使用 deploy/<name>statefulset/<name> 格式,禁止硬編碼裸 Pod 名稱
  2. kafka-client image 的 Dockerfile 必須包含 COPY aws-msk-iam-auth-*.jar /opt/bitnami/kafka/libs/ 步驟,並在 CI build 加入驗證確認 JAR 存在
  3. 建立並維護 verify-kafka.sh,每次 image 更新後必須執行
  4. 當某站點 CrashLoopBackOff 而其他站點正常時,第一步比對 image 版本./compare-image-versions.sh),而非直接看 ConfigMap
  5. 確認分支 image 是否基於問題分支,若是則需 cherry-pick 修復或 revert 再重 build
  6. RedisDBIndex 不同站點值不同是正常的隔離設計,用 checkRedisIndex.py 確認 index 是否正確釋放即可
  7. 建立 compare-site-config.shcompare-image-versions.sh 作為標準診斷工具

本文檔由 Semi-Brain 自動生成

Session ID: e9680a7b-7572-4a2f-949b-d4d7c75a4e96

分析信心度: 97%