1. 每次部署都讓我的伺服器停擺,問題出在哪裡?



在經營個人專案時,常會碰到資源有限的環境(例如 GCP 超小型實例、Raspberry Pi 等)。我同時管理多台伺服器,從高規格的 AI 推理與訓練機器,到僅能跑名稱伺服器的低配 VM,種類相當多元。尤其喜歡 Raspberry Pi 5,因為即使 24 小時運行,電費負擔不大,且性能相當穩定。若把其他伺服器比作需要照料的牲畜,Raspberry Pi 則更像是讓人有感情的寵物。

然而,讓這隻「寵物」負擔過多工作時,部署時 CPU 會瞬間飆到 100%。罪魁禍首是 Celery Worker。為了實現無停機部署,我同時啟動 Blue 與 Green 兩個環境,結果 Worker 數量瞬間翻倍,系統無法負荷。若減少 Worker 數量,處理速度會受影響;若保持原樣,伺服器又會當機,陷入兩難。

象徵在低規格 PC 上執行自動化腳本的圖

為了解決這個問題,我製作了一套 自訂 Blue‑Green 部署腳本,在最小化資源消耗的同時,確保系統穩定性。


2. 解決策略:省資源、提升穩定性

傳統的 Blue‑Green 部署會同時跑兩套環境,我則採取以下方式降低 CPU 壓力:

  1. 先行停止背景服務(Celery):在新版本的 Web 伺服器上線前,先停止舊版的重型背景工作(Worker、Beat),釋放 CPU 資源。
  2. 分段啟動:不一次性啟動所有服務,而是先上線 Web + Redis,完成健康檢查後再繼續。
  3. Human‑in‑the‑loop:所有自動化流程結束後,由管理者親自確認,最後才刪除舊版,確保「最終批准」步驟。

回顧前面的說明,主要問題在於 Celery Worker 在初始化後立刻接收大量任務,導致 CPU 瞬間飆高。雖然降低 Worker 的 concurrency 設定可以緩解,但會讓非同步任務處理變慢,這並非我們想要的。於是,我想到在重新部署時暫時停掉 Celery,先釋放 CPU,再部署新程式碼,從而保持無停機。


3. 核心程式碼一覽



完整程式碼可在我的 GitHub 儲存庫 中取得,以下挑選幾段關鍵邏輯說明。

① 利用 Docker Compose 進行專案隔離

使用 docker compose -p 參數,即使使用相同的 compose 檔,也能建立 bluegreen 兩個獨立的專案(命名空間)。

dc() {
  # 動態指定專案名稱(-p)以實現環境隔離
  docker compose -f "$COMPOSE_FILE" "$@"
}

② 徹底的健康檢查(Health Check)

在新版本完全就緒之前,絕不切換流量。

health_check() {
  # 嘗試 10 次檢查指定端口是否回傳 200 OK
  if curl -fsSIL --max-time "$HEALTH_TIMEOUT" "$url"; then
    ok "Health check passed"
    return 0
  fi
}

③ 失敗時的快速恢復

若新版本出現問題,立即恢復先前停止的服務,避免讓使用者感受到中斷。為此,我沒有使用 rm -f 直接殺掉舊版的 Celery Worker 與 Beat,而是採用 stop 指令快速釋放 CPU,待需要時可即時重新啟動。


4. 運維心得:「確認後刪除」的美學

腳本的最後一步不會自動刪除舊版,而是顯示提示訊息讓管理者自行決定。

「部署已成功,但請自行連線確認。若無異常,請複製下方指令以清理舊版。」

這道安全檢查能防止自動化系統漏掉的 1% 錯誤,提升整體可靠性。


5. 結語

在資源受限的環境中尋求最佳效率的思考,都凝聚於這支腳本。雖然程式碼不算華麗,但我實際使用後感到相當滿意,也相信在這個星球上有許多開發者面臨相似困境。希望我的腳本能對同樣在小規模環境奮鬥的開發者提供幫助。

相關連結: