
昨天提過,我的開發機上跑了十幾個 container。
最一開始其實很單純,就只有一隻 Discord bot:Discord WebSocket -> LLM API -> 回傳,Console 跑在背景就可以。
後來利用了 OpenAB(開源在 github.com/openabdev/openab)在同一台主機上跑了十幾個容器,回頭看主要歷經幾個階段:
docker compose 來管理這些 process。隨著開發專案推進,bot 的用途跟平台開始發散,像是:
於是一整排的 bot,清單整理起來大致如下:
openab-lineoa-course
openab-lineoa-family
openab-warroom(Discord)openab-copilot、openab-max-agy、openab-omp-discord,還有一隻筆記 bot其實每個 bot 容器吃的記憶體都不大,很多只有個位數到幾十 MiB。容器本身不是吃資源的怪獸,但管理它們開始變得很痛苦。
剛開始為了快速上線,每個容器在 compose 裡都無腦設 restart: unless-stopped。
有一次開始清查整個環境,發現其中一個專案的 dev 容器閒置了好幾天,白白佔了 1.25 GiB 記憶體。
=> 原因就在 unless-stopped:只要沒明確執行 docker stop,就算重開機它也會自動跑起來。
而大多數的的 bot 確實需要隨時 listening,但本地或測試用的 container 卻沒有跑完就釋放。
=> 在 docker compose 裡這兩者的設定長得一模一樣,根本沒辦法宣告容器各自的生命週期。
這個月月初有過幾天,我那台機器上的 swap 整個被塞滿(後面 Day 04 會細講)。當時發現幾隻 bot 回覆逐漸變很慢,當時我的反應就是重啟 bot,但沒幫助。
=> 因為 bot 程式本身沒 crash,是旁邊的 container 把系統資源搶光了。查看 bot log 只有看到 request timeout,根本看不出主機 busy for swap in/out。
舉例來說: 當 LINE bot 沒回應了,要不要重啟?負責回應 LINE Webhook 的 tunnel 要重開嗎?
=> 這些處理順序,在 compose 裡頂多寫個 depends_on 顧到啟動順序
=> 但運行時誰掛了該怎麼聯動,只能留在我的腦袋裡或自己寫 script 處理。
回頭看這些問題,其實正是 K8s 在處理的基礎概念:
restartPolicy,或是區分常駐的 Deployment 與跑完即退出的 Jobresources.requests 和 resources.limits
當 container 越來越多,docker compose 基本的 restart、mem_limit、depends_on 很快會不夠用。這時候要嘛自己寫各種 shell script 和 crontab 來處理,要嘛就是借用現成的編排工具。這個系列接下來要談的,就是這兩條路的取捨。
明天先從最基本的講起:Container 到底替 AI 工程師解決了哪些痛點?如果連這層都還沒理清,直接跳進 K8s 只會踩更多坑。
當多個類似性質的 process (這次是 Bot) 同時跑,服務的目的也是類似,就可以考慮往分散式系統思考,而且系統的問題通常不會寫在單一隻 Bot 的 log 裡。
restart 說明 — Docker Compose Restart Specification
restartPolicy — Kubernetes Pod Lifecycle & Restart Policy
github.com/openabdev/openab