iT邦幫忙

2026 iThome 鐵人賽

DAY 14
0
AI Engineering

Agent 開發, 從本地到雲端: AI 工程師的 K8s 筆記系列 第 14 篇

Day 14|它為什麼一直 CrashLoopBackOff:AI 工程師最常撞的 5 種錯與看 log 的方法

  • 分享至 

  • xImage
  •  

cover

昨天 kubectl apply 之後,如果沒看到 Running,最常見的就是 CrashLoopBackOff。

這個狀態的意思很直白:容器起來、掛掉、K8s 重啟它、又掛掉,於是 K8s 開始拉長重啟間隔(back off)。它不是一個錯誤,它是「有個東西一直讓容器退出」的訊號。今天講怎麼把訊號翻成原因。

一樣標 verified: false:這五種錯我在 compose 或 k3s 上都遇過類似的,但指令輸出的文字是依官方文件整理,沒有逐一重現截圖。

先學三個看的動作

kubectl get pods
kubectl describe pod <pod-name>
kubectl logs <pod-name> --previous

get 看狀態和重啟次數(RESTARTS 那欄)。describe 看 K8s 的觀點:拉 image 有沒有成功、被 kill 的原因、Events 區塊的時間軸。

logs --previous 是關鍵。因為容器已經重啟了,logs 預設給你的是「現在這個」的輸出(可能是空的),--previous 才是「剛剛掛掉那個」臨終前說的話(容器沒起來過就沒有 previous,這時看 describe 的 Events)。

錯 1:image 拉不到(ImagePullBackOff)

嚴格說這不是 CrashLoop,但它是第一個會撞到的。describe 的 Events 會寫 Failed to pull image。常見原因:image 名字打錯、tag 不存在、私有 registry 沒給認證。

AI 工程師特別容易撞後者,因為你的 bot image 多半放在自己的 GHCR 或 private registry。解法是建一個 registry 用的 Secret,在 Pod 的 imagePullSecrets 引用(步驟見官方 Pull an Image from a Private Registry),這是 Day 13 那份 YAML 刻意省略的。

錯 2:環境變數沒給到

容器起來、程式一開始讀 os.environ["BOT_TOKEN"]、KeyError、退出。logs --previous 會直接看到 Python traceback。

先分清楚兩種情況:Secret 名稱對不上(secretRef.name 打錯)或根本沒建,Pod 多半卡在 CreateContainerConfigError,容器根本沒起來;KeyError 則是 Secret 掛上了但 key 名不對。kubectl get secret 確認它存在,kubectl describe pod 看 Environment 區塊確認有掛上。

錯 3:OOMKilled

describe 裡 Last State: Terminated, Reason: OOMKilled。最常見是 limit 給太小;沒設 limit 時節點記憶體不夠也會發生,describe 的 Events 一起看。Day 09 講過這件事在 K8s 世界的意思:超過 limit 就是被殺,沒有商量。

我在 Day 13 給 bot 256Mi 的 limit,是基於它實測只吃數十 MiB。但如果你的 agent 會載入模型,或是 RAG 引擎那種(我的 devstack rag-engine 實測約 2.19 GiB),limit 要完全不同量級。先把 limit 調大觀察真實用量,再回頭收,比猜來得快。

錯 4:程式一跑完就退出

bot 程式如果不是 long-running(例如少了 event loop,或 main 函式跑完就 return),容器會以 exit code 0 正常結束。然後 kubelet 依 restartPolicy: Always 在同一個 Pod 裡把容器再拉起來,於是又結束,重複幾次就進 CrashLoopBackOff。describe 會看到 Reason: Completed,exit code 0。

這在 compose 上不明顯(restart: unless-stopped 也會一直重啟,但你比較少盯著看),到 K8s 才被放大。修法在程式端,不在 YAML。

錯 5:健康檢查把自己殺了

如果你有設 livenessProbe,而 bot 啟動比較慢(載入模型、連平台握手),probe 在它準備好之前就判定失敗,K8s 就殺掉重來,永遠起不來。describe 的 Events 會看到 Liveness probe failed。

修法是加 initialDelaySeconds,或改用 startupProbe。Day 13 的最小集刻意沒放 probe,就是為了先避開這一類。

看 log 的習慣

我目前養成的順序是固定的:get(是什麼狀態、重啟幾次)→ describe(K8s 為什麼這樣對它)→ logs --previous(它自己怎麼說)。三步之內,大部分的 CrashLoop 都能定位。

剩下的,通常是網路(連不到平台或資料庫),那要 kubectl exec 進去用 curl 或 nc 試(一直重啟時 exec 不進去,得另開一個 debug Pod),超出今天範圍。

今日一句話

CrashLoopBackOff 不是錯誤,是「有個錯一直發生」;describe 看 K8s 的說法,logs --previous 看容器的遺言,兩邊對起來就找到了。

延伸閱讀


上一篇
Day 13|把一隻 bot 從 compose 搬到 k3s:Deployment + Secret + ConfigMap 最小集
下一篇
Day 15|觀測最小可行組合:kubectl top、metrics、log 聚合,跟 docker stats 差在哪
系列文
Agent 開發, 從本地到雲端: AI 工程師的 K8s 筆記 共 18 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言