Kamal 在 2024 年底釋出 2.0,加入自家開發的 kamal-proxy 取代原本的 Traefik,正式跟 Ruby/Rails 解耦——任何用 Docker 打包的 web 服務都能用它部署。Basecamp 母公司 37signals 把整個產品線從 AWS 搬回自有伺服器、年省七位數美金的故事流傳之後,Kamal 從 Rails 圈擴散到更廣的開發者社群。
但網路上多數繁體中文資料只停在「安裝 + hello world」階段,對於 kamal-proxy 怎麼做零停機、accessories 怎麼處理資料庫、多伺服器部署的 roles 機制這些實際生產環境會用到的東西幾乎沒有提。這篇把這些細節一次講清楚。
不是又一個 Coolify
Coolify、Dokploy 這類自架 PaaS 走的是「在 VPS 上裝一個 dashboard,所有部署透過介面完成」的路線。Kamal 走的是相反的方向:VPS 上不裝任何 agent、不跑 dashboard、不需要管理用資料庫——所有操作都從開發機透過 SSH 完成,伺服器上唯一多出來的程式是一個叫 kamal-proxy 的輕量反向代理。
這個差別不是實作細節,是設計哲學。對團隊大、需要 PR preview 環境、非工程角色也要按按鈕的場景,dashboard 是必要的;對單一開發者或 2–3 人團隊、習慣 Git push 後一行 kamal deploy 結束的工作流,dashboard 反而是負擔。Kamal 把賭注押在後者。
實際資源占用差距也很明顯。一臺 2 vCPU / 2 GB 的 VPS 跑 Coolify dashboard 本身就要吃掉 600 MB 以上記憶體;同一臺 VPS 跑 Kamal,kamal-proxy 大約 20 MB,省下來的記憶體都留給應用程式。
一個 deploy.yml 看完整個架構
整個 Kamal 專案的設定都集中在 config/deploy.yml,沒有散落各處的設定檔:
1 | service: my-app |
servers 區段定義 roles——web 跟 job 是兩個獨立角色,可以分到不同 VPS。proxy.ssl: true 一打開,kamal-proxy 會去 Let’s Encrypt 自動申請憑證,前提是 DNS A record 已經指好。env.secret 列出的變數會從本機 .kamal/secrets 讀取,不會明文寫進設定檔。
完成這個檔案後,kamal setup 跑第一次部署、之後改 code 推 git,本機執行 kamal deploy 就完成 build → push registry → pull → 零停機切換的整個流程。
零停機是怎麼做到的
kamal-proxy 處理流量切換的邏輯比想像中單純,但有兩個常被忽略的細節。
當新版本 container 啟動後,kamal-proxy 不會馬上切流量。它會持續打 health check endpoint(預設 /up),收到連續成功回應才開始路由新流量。這代表應用必須提供這個 endpoint,且要能反映真實的健康狀態——只看 process 還活著不夠,至少要確認資料庫連線、cache 連線都正常。否則新 container 啟動但連不到 DB,流量切過去之後變成 500 風暴。
舊 container 不會立刻被殺掉。kamal-proxy 會等現有連線處理完才停掉舊 container,這個 drain timeout 預設 30 秒。如果應用有長連線(WebSocket、SSE)或長時間 request,要把這個值調大,否則部署時會看到連線被中斷的客訴。
accessories:資料庫不該跟著 app 一起重啟
Kamal 把資料庫、Redis、搜尋引擎這類附屬服務歸類為 accessories,跟主應用分開管理。每次 kamal deploy 只會更新主 service,accessories 維持不動——這是設計上很關鍵的決定,因為資料庫的生命週期本來就不該跟應用程式同步。
實務上有幾個建議值得遵守:
PostgreSQL 之類有狀態的 accessory,盡量單獨放一臺 VPS,跟 web role 分開。一來故障爆炸半徑小,二來 web 那臺壞掉重建時不會碰到資料磁碟。
directories 對應的是 host 上的路徑,預設會放在 /var/lib/<accessory> 之類的位置。要做備份,restic 或 borg 直接打這個目錄即可——但記得 PostgreSQL 不能熱備檔案,要用 pg_dump 或啟用 WAL archiving 才能在 container 跑著的狀態下安全備份。
如果同一個 accessory 服務多個應用程式,把它從 deploy.yml 抽出來、改用獨立的 Docker Compose 管理會更乾淨。Kamal 沒有原生的「accessory 跨專案共用」概念。
多伺服器 roles 怎麼用
servers 底下每個 key 都是一個 role,例如 web、job、worker。同一個 image 可以用不同 cmd 啟動,跑成不同角色的 container:
1 | servers: |
執行 kamal deploy --roles=web 只會更新 web role,背景 worker 不動。這在「只改前端要快速 rollout、不想動到正在跑長任務的 worker」這種場景特別有用。
要注意一個容易誤解的點:kamal-proxy 是部署在每一臺 web 主機上的,本身並不負責跨主機的流量分配。多臺 web 同時對外服務,前面還是要靠 DNS round-robin 或外部 load balancer 來把使用者導到不同主機。應用程式不能假設使用者連續兩個 request 會打到同一臺 container,session 一律走 Redis 或資料庫,不要存在 process 記憶體裡。
kamal-proxy 的 SSL 有幾個邊界
Let’s Encrypt 自動憑證很方便,但有幾個情境會卡住。
它走 HTTP-01 挑戰,代表 80 port 必須對外開啟,且 DNS A record 要在 kamal setup 之前就指對。如果在 Cloudflare proxy 模式(橘色雲朵)後面,Cloudflare 本身會把 ACME challenge 轉發到 origin,所以單純開橘色雲朵不見得會卡住憑證申請。實際讓 kamal-proxy 取得憑證失敗的,多半是這幾種設定組合:SSL/TLS 模式設成 Full 或 Full (strict) 而 origin 還沒有任何有效憑證、「Always Use HTTPS」把 HTTP challenge 直接導去 HTTPS、或是 WAF 規則擋掉 /.well-known/acme-challenge/ 路徑。實務上最乾淨的做法是第一次 setup 前暫時關閉 proxy(灰色雲朵)取得憑證後再開回橘色,或直接用 Cloudflare 的 origin 憑證跳過 kamal-proxy 的 SSL 流程。
如果 SSL 由前面一層 reverse proxy(例如另一臺 nginx)處理,把 proxy.ssl 關掉、proxy.host 留 hostname,kamal-proxy 就只做純 HTTP 反向代理。生產環境如果要做 IP allowlist 或 WAF,這種前置 nginx 的架構反而比較有彈性。
多網域指向同一個服務也支援,把 host 寫成 list 即可,每個域名都會自動取得對應的憑證。
什麼情況不該選 Kamal
工具沒有絕對好壞,以下情境 Kamal 不是最佳解:
- 需要 PR preview 環境:每個 branch 自動部署一份的需求 Kamal 沒有原生支援,Coolify 跟 Dokploy 在這塊更成熟。
- 非工程師需要操作部署:Kamal 只有 CLI,沒辦法給產品經理或客服按按鈕重啟服務。
- 微服務上百個:跨服務的相依管理、auto-scaling、service mesh 這些 Kamal 不碰,那是 Kubernetes 的地盤。
- 單一服務、單一伺服器、不在意零停機:Docker Compose 加一個 GitHub Actions workflow 就夠了,沒必要多裝 Kamal。
Kamal 的甜蜜點是「2–5 臺 VPS、3–10 個服務、團隊規模小、想要可預測且可版本控制的部署流程」這個區間。剛好對應一大批從雲端 PaaS 出走、想找便宜可控替代方案的中小型團隊。
在臺灣 VPS 上跑 Kamal 的幾個實務點
Kamal 對伺服器地點沒有偏好,但實務上有幾個體感差異。
deploy 過程裡 docker push 跟 docker pull 都會走 registry,從臺灣到亞太區 registry(GHCR 亞太、Docker Hub 亞太節點)走得快很多;如果 registry 在歐美,build 完整段流程可能要等好幾分鐘。本機跟伺服器之間的 SSH 互動如果延遲高,kamal app exec、kamal logs 這類指令會明顯卡頓,調試體驗下降。
伺服器放在臺灣機房,這些操作的體感跟跑在區網內差不多——本機推一行指令就立刻看到伺服器端反應。對於日常頻繁部署的開發節奏,這個差距比想像中重要。
NCSE Network 的 VPS 服務位於臺灣是方電訊機房,採用 Intel Gold CPU 與 NVMe SSD,對 Kamal 這種需要頻繁 build、push、pull 大型 Docker image 的部署流程相當合適。需要把生產服務從雲端搬回自有環境、又不想處理 Kubernetes 複雜度的團隊,可以到 NCSE Network 看看適合的方案。