CI/CD 與部署¶
概覽¶
4pass 使用三個 GitHub Actions 工作流程自動部署至 AWS ECS,搭配多階段 Docker 建構、OIDC 驗證(無需儲存 AWS 憑證)、以及具備健康檢查驗證的滾動部署。
部署安全性
所有部署均為手動觸發 — 不在推送時自動部署。交易平台需要審慎發布。每次部署使用 ECS 滾動式更新搭配 10 分鐘穩定性檢查、100% 最低健康(零停機),以及新 Task 未通過健康檢查時的斷路器自動回滾。GitHub 中不存在長期 AWS 憑證 — 認證使用 OIDC 臨時憑證,1 小時後過期。
graph LR
Dev["開發者"] -->|"git push / dispatch"| GHA["GitHub Actions"]
GHA -->|"OIDC 驗證"| AWS["AWS STS"]
GHA -->|"建置 + 推送"| ECR["Amazon ECR"]
GHA -->|"更新任務定義"| ECS["Amazon ECS"]
ECS -->|"滾動部署"| Service["ECS Service"]
Service -->|"健康檢查"| ALB["ALB /health"]
部署流程¶
流程概覽¶
| 工作流程 | 觸發方式 | 目標 | 策略 |
|---|---|---|---|
deploy-api.yml |
手動觸發 | ECS API 服務 | 滾動更新,10 分鐘穩定等待 |
deploy-worker.yml |
手動觸發 | ECR Worker 映像 | 僅推送映像(Worker 為短暫性質) |
deploy-ec2.yml |
手動觸發 | EC2 實例 | 容量提供者更新 |
所有工作流程使用 workflow_dispatch(手動觸發)進行受控部署。推送時不自動部署——交易平台需要謹慎的發版流程。
API 部署流程¶
sequenceDiagram
participant Dev as 開發者
participant GHA as GitHub Actions
participant ECR as Amazon ECR
participant ECS as Amazon ECS
participant ALB as ALB 健康檢查
Dev->>GHA: 觸發 deploy-api 工作流程
GHA->>GHA: 檢出程式碼
GHA->>GHA: 建置 Vue.js 前端(npm build)
GHA->>GHA: 建置多階段 Docker 映像
GHA->>ECR: 推送映像(SHA 標籤 + latest)
GHA->>ECS: 註冊新任務定義
GHA->>ECS: 更新服務(滾動部署)
loop 每 15 秒,持續 10 分鐘
ECS->>ALB: 健康檢查 /health
ALB-->>ECS: 200 OK
end
GHA->>GHA: 驗證執行中任務使用新定義
GHA->>GHA: 驗證 /health 回傳 200
主要特色:
- 多階段建構:Node.js 建構 Vue 前端 → Python 執行環境搭配 FastAPI
- OIDC 驗證:GitHub 中不儲存 AWS 存取金鑰——使用
aws-actions/configure-aws-credentials搭配 OIDC - 滾動部署:最低 100% 健康,最高 200%——零停機時間
- 斷路器 (Circuit Breaker):部署失敗時自動回滾
- 驗證:確認新任務定義正在執行 + 健康端點回應正常
Docker 建構¶
API 映像 (Dockerfile)¶
graph TB
subgraph stage1["階段 1:前端建置"]
Node["Node.js 20"] --> NPM["npm install + build"]
NPM --> Dist["dist/ (Vue SPA)"]
end
subgraph stage2["階段 2:Python 執行環境"]
Python["Python 3.11-slim"] --> Deps["pip install requirements.txt"]
Deps --> App["複製 app/ + 前端 dist"]
App --> Health["健康檢查 /health"]
end
Dist --> App
| 屬性 | 值 |
|---|---|
| 基礎映像 | python:3.11-slim |
| 前端 | 以 Vite 建構的 Vue 3 SPA |
| 伺服器 | Gunicorn + Uvicorn (16 workers, preload) |
| 執行使用者 | 非 root(透過 gosu 使用 appuser) |
| 密鑰 | 啟動時透過 entrypoint.sh 從 AWS Secrets Manager 載入 |
| 健康檢查 | curl /health |
Worker 映像 (Dockerfile.worker)¶
Worker 映像經過極致優化,以達到快速啟動和最小體積:
| 指標 | 優化前 | 優化後 | 改善幅度 |
|---|---|---|---|
| 映像大小 | 541 MB | 254 MB | 縮小 53% |
| 啟動時間 | 0.75s | 0.43s | 加快 43% |
| AWS SDK | 完整 boto3 | 精簡至僅 ECS/STS/Logs/SQS | 縮小約 60% |
graph TB
subgraph stage1["階段 1:建置"]
B1["安裝所有依賴"]
B1 --> B2["編譯 .pyc 位元組碼"]
end
subgraph stage2["階段 2:優化"]
O1["移除 pip、setuptools、pygments"]
O1 --> O2["精簡 botocore 至 5 個服務"]
O2 --> O3["移除 __pycache__、tests、docs"]
end
subgraph stage3["階段 3:執行環境"]
R1["python:3.11-slim 基礎映像"]
R1 --> R2["複製優化後的 site-packages"]
R2 --> R3["預編譯位元組碼"]
R3 --> R4["254 MB 最終映像檔"]
end
stage1 --> stage2 --> stage3
已套用的優化:
- 3 階段建構:Builder → Optimizer → Runtime
- 預編譯位元組碼:建構時執行
compileall,非執行時 - 精簡 boto3/botocore:僅保留 ECS、Secrets Manager、STS、Logs、SQS 服務
- 移除 pip/setuptools:執行時不需要
- 移除 AWS CLI:直接使用 boto3
驗證與安全¶
OIDC(無儲存憑證)¶
sequenceDiagram
participant GHA as GitHub Actions
participant STS as AWS STS
participant IAM as IAM Role
GHA->>STS: AssumeRoleWithWebIdentity(OIDC token)
STS->>IAM: 驗證信任政策(repo: fullpass-4pass/4pass)
IAM-->>STS: 臨時憑證(1 小時)
STS-->>GHA: Access Key + Secret + Session Token
GHA->>GHA: 使用憑證進行 ECR、ECS 操作
- GitHub Secrets 中不存放長期 AWS 憑證
- IAM 角色信任政策限制為特定儲存庫
- 臨時憑證在 1 小時後過期
- 遵循 AWS 安全最佳實踐
映像標籤¶
| 標籤 | 格式 | 用途 |
|---|---|---|
| SHA 標籤 | sha-abc1234 |
不可變,可追溯至 Commit |
latest |
始終更新 | 方便手動執行 |
ECR 設定¶
| 儲存庫 | 用途 | 生命週期 |
|---|---|---|
shioaji-api |
API + 前端映像 | 保留最近 10 個映像 |
shioaji-worker |
交易 Worker 映像 | 保留最近 10 個映像 |
部署檢查清單¶
部署前
- 所有測試在本機通過
- 資料庫遷移已套用(
alembic upgrade head) - 環境變數已在 Secrets Manager 中更新
- Worker 映像與新 API 相容(若有破壞性變更)
部署後
- 驗證
/health端點回傳 200 - 檢查 CloudWatch 是否有錯誤激增
- 監控 SQS DLQ 是否有失敗訊息
- 驗證 Worker Pool 健康狀態(maintenance Lambda 日誌)