這篇是自己的備忘錄。記錄為什麼不直接 git pull 就好,以及 Config Repo 模式的設計邏輯和它的不完整之處。


# 傳統部署(推式 / Push-based)

最直覺的做法:

# 在 server 上
git clone https://github.com/org/teleagent-backend
cd teleagent-backend
git pull
docker-compose up --build

# 兩種型態

型態一:純手動

ssh user@server
cd /app/teleagent-backend
git pull
docker-compose up --build -d

完全靠人,每次部署都要 SSH 進去手動執行。

型態二:CI 自動 SSH

# GitHub Actions
- name: Deploy
  uses: appleboy/ssh-action@v1
  with:
    host: $<!--swig0-->
    script: |
      cd /app/teleagent-backend
      git pull
      docker-compose up --build -d

CI 幫你自動 SSH 進去跑指令,看起來像「自動化」,但本質還是一樣——CI 主動把指令推進 server 執行(Push-based)。

# 流程

開發者 push code


GitHub Actions(CI)
  └─ 跑測試
  └─ SSH 進 server(自動或手動)


Server
  └─ git pull(拿 source code)
  └─ docker-compose up --build -d(在 server 上 build + 跑)

兩種型態的本質都一樣:CI 是核心,主動 push 變更進 server。

# 缺點

1. 程式碼、設定、密鑰混在一起

docker-compose.yml.env、SSL 憑證都跟 source code 放在同一個 repo,甚至同一個目錄下。萬一 repo 外洩,密鑰也跟著洩漏。

2. Server 需要完整開發環境

因為要 --build,server 上得有 Go / Node / Python 等語言環境、build tool,純粹跑服務的機器卻要安裝一堆不必要的東西。

3. 多台 server 很難管

三台 server 就要 SSH 三次,手動依序更新,容易漏掉或搞錯版本。

4. 沒有部署記錄

「誰、什麼時候、把哪個版本部署到哪台 server」這件事完全沒有記錄,debug 出問題很難追溯。


# GitOps / Config Repo 模式(拉式 / Pull-based)

# 核心概念

把「程式碼」跟「部署設定」分成兩個 repo 管理。

  • Application Repoteleagent-backend):只管程式碼和 CI
  • Config Repotbd-teleagent-120):只管這台 server 的部署設定

Git 本身就是「什麼版本應該跑在哪裡」的唯一事實來源(Single Source of Truth)。

# Config Repo 結構

tbd-teleagent-120/          ← 這台 120 server 專屬的 repo
├── teleagent-backend/
│   ├── docker-compose.yml  ← 寫明要跑哪個 image tag
│   ├── credentials/        ← 這台專屬的密鑰(不在 app repo 裡)
│   └── .envrc              ← 這台專屬的環境變數
├── ssos/
│   └── docker-compose.yml
└── aiphone-backend/
    └── docker-compose.yml

# 流程

開發者 push code


GitHub Actions(CI)
  └─ 跑測試
  └─ build Docker image
  └─ push image 到 ECR(帶版本 tag,例如 v1.2.3)


手動(或自動工具)更新 config repo:
  docker-compose.yml 裡的 image tag 改成 v1.2.3


Server 上的部署工具偵測到 config repo 有變動
  └─ docker pull image(從 ECR)
  └─ docker-compose up -d(不 build,直接用 image)

關鍵差異:server 不需要 source code,只需要 Docker。Image 在 CI 環境 build 好,server 只負責「拉 image、跑起來」。

# 優點

1. 密鑰與程式碼徹底分離

credentials/ 只在 config repo,而 config repo 通常是 private + 存取受限。即使 app repo 外洩,密鑰不受影響。

2. Server 乾淨,只需要 Docker

不需要 Go、Node、Python、build tool。一台純 production server 只跑 container,環境單純、攻擊面小。

3. 每台 server 設定獨立

tbd-teleagent-120 是這台機器的設定,tbd-teleagent-121 是另一台的設定,不同機器的 credentials、env var 完全隔離,改一台不影響另一台。

4. 部署歷史完整可追溯

config repo 的每一筆 commit 都是一次「宣告這台 server 應該跑什麼版本」的記錄,附帶時間、作者。需要 rollback 只要 revert commit 就好。


# 兩種方式的核心差異對比

傳統部署 GitOps / Config Repo
部署觸發 CI 主動 push(推式) Server 偵測 Git 變動拉取(拉式)
Source of Truth CI pipeline Git config repo
Server 需要 完整開發環境 + source code 只需要 Docker
密鑰管理 混在 app repo 獨立 config repo
多台 server 各自 SSH 手動更新 各自有 config repo,獨立管理
部署記錄 Git commit history
Rollback 手動重跑舊版 CI Revert config repo commit

# 這個專案目前的狀態:半手動 GitOps

目前的流程走到這裡就停了:

CI push image 到 ECR(帶 tag)

      ✋ 手動去 config repo 改 docker-compose.yml 裡的 tag


SSH 進 server,手動 docker-compose up -d

這就是為什麼「推了 code 但沒更新」——CI 那段是自動的,但 config repo 更新和 server 重啟這兩步是手動的,很容易忘。


# 完整 GitOps 需要什麼?

要把「半手動」補完,需要在 CI push image 之後,自動完成這兩件事:

# 1. 自動更新 config repo 的 image tag

ArgoCD Image Updater(Kubernetes 環境)

  • 監聽 ECR,偵測到新 image tag
  • 自動更新 Git repo 裡的 manifest
  • ArgoCD 偵測到 Git 有變動,自動 sync 到 cluster

Renovate / Dependabot(非 Kubernetes 也能用)

  • 掃描 docker-compose.yml 裡的 image tag
  • 發 PR 更新 tag,merge 後自動觸發部署

自己寫 CI script(最輕量)

# GitHub Actions,在 CI 最後一步
- name: Update config repo
  run: |
    git clone https://github.com/org/tbd-teleagent-120
    cd tbd-teleagent-120/teleagent-backend
    sed -i "s|image: .*|image: 123456.ecr.aws/teleagent:$<!--swig1-->|" docker-compose.yml
    git commit -am "ci: bump teleagent to $<!--swig2-->"
    git push

# 2. 自動重啟 server 上的服務

Watchtower(最簡單,適合小專案)

  • 一個 Docker container,持續監聽 registry 有沒有新 image
  • 偵測到新版本自動 docker pull + 重啟
  • 缺點:不是從 Git 驅動,是直接監聽 registry,跟 GitOps 精神有點偏差

⚠️ 注意:Watchtower 目前處於 unmaintained 狀態(2025 年社群反映維護停滯),小專案用還好,正式環境要謹慎。

Portainer Webhooks

  • config repo 更新後,透過 webhook 通知 Portainer 重新 pull + 重啟
  • 比 Watchtower 更可控,可以指定更新哪個 stack

SSH + 簡單 script(夠用就好)

#!/bin/bash
# deploy.sh,被 CI 或 webhook 觸發
cd /srv/tbd-teleagent-120/teleagent-backend
git pull
docker-compose pull
docker-compose up -d

# 完整自動化流程(目標狀態)

開發者 git push


GitHub Actions
  ├─ 跑測試
  ├─ build image
  ├─ push image 到 ECR(tag: git SHA 或語意版本)
  └─ 更新 config repo 的 image tag(自動 commit)


Config Repo 有新 commit

      ▼  (Webhook / Portainer / ArgoCD 偵測到)

Server
  ├─ docker pull 新 image
  └─ docker-compose up -d


✅ 部署完成,全程不需要人工介入

# 小結

場景 建議
個人小專案、一台 server 傳統部署就夠,簡單直接
多台 server、需要密鑰隔離 Config Repo 模式
要完全自動化 + 部署歷史 Config Repo + ArgoCD Image Updater 或自訂 CI script
Kubernetes 環境 ArgoCD + Image Updater 是標配

GitOps 不是銀彈,是一種設計哲學:把「系統應該長什麼樣子」的宣告放進 Git,讓工具負責讓現實跟宣告一致。


最後更新:2026-03-11