本文 AI 產出,尚未審核
FastAPI 部署與架構:CI/CD 自動部署(GitHub Actions)
簡介
在現代的 Web 開發流程中,持續整合 / 持續部署(CI/CD) 已成為不可或缺的最佳實踐。對於使用 FastAPI 建置的微服務,透過 GitHub Actions 讓程式碼在每次 push、pull request 或是發佈標籤(tag)時自動完成測試、建置 Docker 映像、並部署到雲端環境,能大幅提升開發效率、降低人為錯誤,並確保服務始終以最新且通過測試的版本上線。
本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,帶領讀者一步步建立 FastAPI + GitHub Actions 的全自動部署工作流程,適合剛接觸 CI/CD 的初學者,也能為已有部署經驗的開發者提供參考。
核心概念
1️⃣ 為什麼選擇 GitHub Actions?
- 原生整合:GitHub 儲存庫即內建 Actions,免除額外服務的設定與費用。
- 彈性工作流程:支援多種觸發條件(push、pull_request、schedule 等),且可以同時跑測試、建置、部署等多個 job。
- 社群豐富:Marketplace 上有大量現成的 Action(如
docker/build-push-action、appleboy/ssh-action),可直接引用。
2️⃣ CI/CD 流程概觀
code commit → GitHub → GitHub Actions
│ │
└─> run tests ──► build Docker image
│
└─> push to registry (Docker Hub / GHCR)
│
└─> deploy to server / cloud
3️⃣ 必備檔案與目錄結構
my-fastapi-app/
│
├─ app/ # FastAPI 程式碼
│ └─ main.py
├─ tests/ # pytest 測試
│ └─ test_main.py
├─ Dockerfile # 建置映像
├─ requirements.txt
└─ .github/
└─ workflows/
└─ ci-cd.yml # GitHub Actions 工作流程
4️⃣ 主要技術
- pytest:執行單元測試。
- Docker:將 FastAPI 包裝成可移植的容器。
- GitHub Container Registry (GHCR):存放私有 Docker 映像。
- SSH / SCP:將映像部署至自建伺服器(亦可改用 AWS ECS、Google Cloud Run 等)。
程式碼範例
4.1 Dockerfile(建置 FastAPI 映像)
# Dockerfile
FROM python:3.11-slim
# 建立非 root 使用者以提升安全性
RUN useradd -m appuser
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY ./app ./app
# 使用非 root 使用者執行
USER appuser
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
註解:
python:3.11-slim體積小、適合生產環境。USER appuser可避免容器內程式以 root 身份執行,降低安全風險。
4.2 pytest 範例(測試 FastAPI 路由)
# tests/test_main.py
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_read_root():
"""檢查根路由是否回傳 200 與預期訊息"""
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello, FastAPI!"}
說明:
TestClient讓我們在不啟動實體伺服器的情況下直接呼叫 API。- 測試失敗會直接讓 GitHub Actions 中的 test job 失敗,阻止後續部署。
4.3 GitHub Actions 工作流程(ci-cd.yml)
# .github/workflows/ci-cd.yml
name: CI/CD for FastAPI
on:
push:
branches: [ main ]
tags: [ 'v*' ] # 只在標籤為 v* 時執行部署
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest
- name: Run tests
run: pytest -q
build-and-push:
needs: test # 必須在 test 成功後才執行
runs-on: ubuntu-latest
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') # 只在打 tag 時推送映像
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository_owner }}/fastapi-app:${{ github.ref_name }}
deploy:
needs: build-and-push
runs-on: ubuntu-latest
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
steps:
- name: Deploy to remote server via SSH
uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
docker pull ghcr.io/${{ github.repository_owner }}/fastapi-app:${{ github.ref_name }}
docker stop fastapi-app || true
docker rm fastapi-app || true
docker run -d --name fastapi-app -p 80:8000 ghcr.io/${{ github.repository_owner }}/fastapi-app:${{ github.ref_name }}
重點說明:
- 測試階段 (
test) 必須全部通過,否則後續建置與部署不會執行。- 建置與推送 只在打上
v*標籤(如v1.0.0)時觸發,避免每次 push 都上傳映像。- 部署階段 透過
appleboy/ssh-action在遠端伺服器上拉取最新映像、停止舊容器、重新啟動。- 所有機密資訊(SSH 金鑰、伺服器 IP)均放在 GitHub Secrets,絕不硬編碼在程式碼內。
4.4 使用 GitHub Secrets 儲存機密
| Secret 名稱 | 用途說明 |
|---|---|
GITHUB_TOKEN |
內建 token,可登入 GHCR(無需額外設定) |
SERVER_HOST |
部署目標伺服器 IP 或域名 |
SERVER_USER |
SSH 登入帳號 |
SERVER_SSH_KEY |
私密金鑰(Base64 編碼或直接貼上) |
在 GitHub Repo → Settings → Secrets and variables → Actions 中新增上述項目。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
| 測試未覆蓋 | 只跑 pytest,但測試覆蓋率低,可能讓錯誤程式碼通過 CI。 |
加入 pytest-cov,在 test job 中使用 --cov=app --cov-fail-under=80 讓覆蓋率低於 80% 時失敗。 |
| Docker cache 失效 | 每次建置都重新安裝依賴,耗時長。 | 在 docker/build-push-action 中使用 cache-from 與 cache-to,或在 Dockerfile 中把 requirements.txt 先 copy 再 install,利用層級快取。 |
| 機密資訊寫死 | 把 SSH 金鑰寫在 workflow 檔案裡,會被公開。 | 務必使用 GitHub Secrets,且在 workflow 中僅以 ${{ secrets.xxx }} 讀取。 |
| 標籤格式不一致 | 打錯標籤(如 v1)導致部署腳本找不到正確 tag。 |
統一使用 vMAJOR.MINOR.PATCH,可在 CI 中加入正則驗證。 |
| 容器未健康檢查 | 部署後容器啟動失敗,但 Actions 仍顯示成功。 | 在 Dockerfile 加入 HEALTHCHECK,或在部署腳本使用 docker ps 檢查容器狀態。 |
最佳實踐
- 分支策略:
main為穩定分支,所有 PR 必須通過測試才能 merge。 - 標籤發布:使用
git tag -a v1.0.0 -m "Release 1.0.0",再git push origin v1.0.0觸發部署。 - 日誌集中:將容器日誌輸出到
stdout,配合雲端日誌服務(如 CloudWatch、Grafana Loki)統一監控。 - 藍綠部署:若需求較高,可在
deployjob 中先啟動新容器(fastapi-app-new),測試成功後再切流量,最後移除舊容器。
實際應用場景
小型 SaaS 服務
開發團隊每天多次提交功能,透過 GitHub Actions 自動跑測試、建置映像,並在每次正式版(v*)發布時自動部署至 AWS EC2。開發者只需要在本地git push即可完成全流程。內部微服務平台
企業內部有多個 FastAPI 微服務,每個服務都有自己的 repo。使用相同的 CI/CD 模板,統一管理 Docker 映像與部署腳本,降低維運成本。教育或開源專案
想要讓使用者快速體驗專案,只要在 GitHub 上打上一個 Release 標籤,GitHub Actions 會自動把最新映像推到 GHCR,使用者即可透過docker run直接執行。
總結
- CI/CD 是 FastAPI 專案走向自動化、可持續交付的關鍵。*
- GitHub Actions 提供了從 測試 → Docker 建置 → 映像推送 → 部署 的完整流水線,只要妥善設定 workflow、使用 Secrets 保存機密,即可在每次標籤發布時自動將最新的 FastAPI 服務上線。*
- 透過本文的範例與最佳實踐,你可以快速在自己的專案中建立穩定且安全的自動部署機制,從而把更多時間投入在 業務邏輯 與 API 設計 上,而不是繁雜的手動部署工作。
祝你在 FastAPI 的部署之路上一路順風,持續交付高品質的 API! 🚀