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