FastAPI×GitHub Actionsで作るCI/CDパイプライン入門――テスト自動化・Dockerビルド・コンテナレジストリ・Kubernetesデプロイ
要約(最初に全体像)
- FastAPIアプリの変更を、プッシュから本番リリースまで自動で流すために、GitHub ActionsでCI/CDパイプラインを構築します。
- パイプラインは「テスト(CI) → Dockerビルド&プッシュ → Kubernetesデプロイ」の3段階を基本とし、開発・ステージング・本番の3環境を安全に切り替えます。
- コンテナレジストリには GitHub Container Registry(GHCR)を例にしつつ、任意のレジストリにも応用できる構成を紹介します。
- Kubernetesへのデプロイは「push型」(GitHub Actionsから直接
kubectlなどを叩く)で実現し、将来的なArgo CDなどのpull型への発展も見据えます。 - Secrets、環境ごとの保護ルール、テストの粒度、ロールバック戦略まで含めて、運用しやすい設計ポイントを整理します。
誰が読んで得をするか(かなり具体的に)
-
個人開発・学生の方
GitHubにFastAPIアプリを置いているが、毎回手動でテスト・Dockerビルド・デプロイをしている。
→ プッシュすると自動でテストが走り、ステージングまでデプロイされる「半自動」運用を目指します。 -
小規模チーム(3〜5名)のWeb/バックエンドエンジニア
Pull Requestごとのレビュー、テスト、ステージングデプロイが属人化しがち。
→ GitHub Actionsのワークフローを共通ルールにし、テストとデプロイをチームの「標準プロセス」にします。 -
Kubernetes上でFastAPIを運用したいスタートアップチーム
すでにKubernetesで動いているが、手動kubectl applyや雑多なスクリプトが溜まり、どれが正なのか分からない。
→ ワークフローに「ビルド → レジストリ → K8sデプロイ」を集約し、本番リリースの流れを一本化します。
アクセシビリティ評価
- 情報構造
冒頭で全体像→CI設計→CD設計→Kubernetes連携→環境・Secrets管理→運用Tips→まとめ、の順に段階を追っています。 - 言葉の使い方
用語は初出時に短く説明し、その後は用語を統一して迷いを減らしています。英語用語も必要以上に増やさないようにしました。 - コードとYAML
固定幅ブロックで表示し、コメントは短く。目で追いやすいように空行も多めに取っています。 - 想定読者の幅
CI/CDが初めての方にも届くように、一つひとつのステップに背景理由を添えつつ、経験者にとっても「型」として再利用できる深さを意識しました。
総合的に、技術記事としてはAA相当の読みやすさを目指しています。
1. まず決めるべきこと:FastAPIのCI/CD設計方針
FastAPIに限らず、WebアプリのCI/CDでは次の3つを最初に決めておくと迷いにくくなります。
- どのタイミングでテストを走らせるか(push、PR、mainマージなど)。
- どこまでを自動化し、どこから人間の承認をはさむか。
- アーティファクト(Dockerイメージなど)をどこに保存し、どの環境にどのタイミングで配布するか。
今回は、よくある構成として次のような方針で進めます。
- mainブランチへのプッシュで
- テスト(CI)
- Dockerイメージのビルド&レジストリへのプッシュ
- ステージング環境への自動デプロイ
- 本番デプロイは、GitHub Actionsの「Environment(環境)」の保護ルールや手動トリガー(
workflow_dispatch)を使って、人間の承認をはさむ。
この形が、個人開発〜小規模チームまで扱いやすく、運用のクセもつきにくいバランスです。
2. FastAPIリポジトリの前提を揃える
2.1 ディレクトリ構成の一例
fastapi-app/
├─ app/
│ ├─ main.py
│ ├─ routers/
│ └─ core/
├─ tests/
│ └─ test_*.py
├─ Dockerfile
├─ requirements.txt / pyproject.toml
└─ .github/
└─ workflows/
└─ ci-cd.yaml # これから作る
2.2 テストコマンドの統一
CIで走らせるテストコマンドは、ローカルでも同じコマンドで動くように揃えておきます。
例(pyproject.toml の [tool.pytest.ini_options] など):
pytest -q
あるいはフォーマッタや型チェックを含めるなら:
ruff check .
pytest -q
mypy app
この「ローカルとCIで同じコマンドを叩く」という習慣が、トラブル時の切り分けをとても簡単にしてくれます。
3. GitHub Actionsの基本:ワークフローの骨組み
GitHub Actionsは、.github/workflows/*.yaml に書いたワークフロー定義を、pushやPRなどのイベントに応じて自動実行してくれます。
まずはテストだけを実行する最小構成から始めましょう。
3.1 テスト専用ワークフロー
# .github/workflows/ci-cd.yaml
name: CI/CD for FastAPI
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
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 -r requirements-dev.txt
- name: Run tests
run: pytest -q
これで、mainブランチへのプッシュとPRで、テストが自動的に実行されるようになります。
判断ポイント:
- テストが遅くなる場合、lint・単体テスト・統合テストを別ジョブに分けて並列化すると効率的です。
- main以外のブランチでは「テストだけ」、mainマージで「ビルド+デプロイ」など、イベントごとにワークフローを分割してもよいです。
4. Dockerイメージのビルドとレジストリへのプッシュ
FastAPIアプリを本番で使うなら、多くのケースでDockerイメージとして配布することが推奨されています。
ここでは GitHub Container Registry(GHCR) を例に、イメージビルド&プッシュを行うジョブを追加します。
4.1 GHCR向けの準備
- リポジトリの Settings → Packages からGHCRの利用を確認。
- イメージ名の慣例:
ghcr.io/<OWNER>/<REPO>:<TAG>
<OWNER> は通常 GitHubのユーザー名またはOrganization名です。
4.2 ビルド&プッシュジョブの追加
公式の docker/build-push-action を使うと、ビルドとプッシュが簡潔に書けます。
jobs:
test:
# ...(さきほどのtestジョブ)
build-and-push:
needs: test # テストが通ったら実行
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' # mainブランチだけビルド
permissions:
contents: read
packages: write
id-token: write # OIDCを使う場合など
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }} # owner/repo
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
ポイント:
needs: testによって、テスト成功後のみビルドが走ります。GITHUB_TOKENを使えば、別途トークンを用意しなくてもGHCRへのプッシュが可能です(公開設定や組織の設定には注意)。- タグには
latestに加え、git commitのSHAを付けておくと、特定バージョンへのロールバックがしやすくなります。
5. Kubernetesへのデプロイを自動化する
次は、ビルドされたDockerイメージをKubernetesクラスタへデプロイするCDステージを追加します。GitHub公式ドキュメントや各種記事でも、GitHub Actionsからkubectlやkustomizeを使ってK8sへデプロイする例が紹介されています。
ここでは「push型デプロイ」として、Actionsから直接kubectl applyを実行する最小構成を示します。
5.1 クラスタへの接続情報をGitHub側に登録
Kubernetesクラスタの認証はクラウドや構成によって差がありますが、代表的には次のような方法があります。
- kubeconfigの内容をGitHub Secretsとして登録し、ワークフロー内でファイルとして展開。
- クラウドプロバイダ提供の
setup-*系アクション(例:GKEのgcloud連携)を使って認証。
ここでは、簡易的に「kubeconfig全体をSecretにいれて使う」パターンにします。
- 手元の
~/.kube/configから対象クラスタの設定部分を抽出して、KUBECONFIG_CONTENTというGitHub Secretsに保存。 - ワークフロー内でファイルとして出力し、
KUBECONFIGとして参照。
5.2 デプロイジョブの例
deploy-staging:
needs: build-and-push
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment:
name: staging
url: https://stg.example.com # ステージングURL(任意)
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up kubectl
uses: azure/setup-kubectl@v4
with:
version: "v1.30.0"
- name: Configure kubeconfig
run: |
echo "${KUBECONFIG_CONTENT}" > kubeconfig
chmod 600 kubeconfig
env:
KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG_STAGING }}
- name: Set KUBECONFIG env
run: echo "KUBECONFIG=$PWD/kubeconfig" >> $GITHUB_ENV
- name: Update image in manifest
run: |
IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
sed -i "s|image: .*|image: ${IMAGE}|g" k8s/deployment.yaml
- name: Apply manifests
run: |
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
ポイント:
environmentにstagingと書くことで、GitHub側の「環境機能」を使い、保護ルールやレビューを設定できます。sedでYAML内のimage:を上書きするのは簡易的な方法です。kustomizeや Helm を使うと、より柔軟にイメージ差し替えが可能になります。- 本番環境用には
deploy-productionジョブを用意し、environment.name: productionと手動トリガー、レビュー必須などを設定するのがおすすめです。
6. 環境ごとの切り替えと保護ルール
本番とステージングは、次の2点で差をつけるのが定番です。
- 使用するSecrets/ConfigMapの内容(接続先DB・外部APIキーなど)。
- デプロイフローの「自動・手動」の違い(ステージングは自動、本番は手動+承認)。
6.1 GitHub Environmentsの活用
GitHub Actionsには「Environments」という仕組みがあり、環境ごとに専用のSecretsや保護ルールを持たせることができます。
-
staging環境:- Secrets:
KUBECONFIG_STAGING,DATABASE_URL_STAGINGなど - 自動デプロイOK(保護ルールは緩め)
- Secrets:
-
production環境:- Secrets:
KUBECONFIG_PROD,DATABASE_URL_PRODなど - 手動承認が必要、特定ユーザーのみ承認できる、など保護ルールを設定
- Secrets:
ワークフローでは、ジョブに environment を指定することで、その環境に紐づいたSecretsが利用できるようになります。
environment:
name: production
url: https://app.example.com
6.2 FastAPI設定との連携
以前の記事で扱った pydantic-settings のenvなどを使い、ENV=stg/ENV=prodといった環境変数をKubernetes側から注入することで、アプリコードを変えずに設定だけ切り替えられます。
7. Secretsとセキュリティの注意点
GitHub Actionsで扱うSecretsは、GitHub側で暗号化され、ログに直接表示されることはありませんが、ワークフロー内の扱い方には注意が必要です。
7.1 やってはいけないこと
- Secretを
echoでそのままログに出力する。 - デバッグのために
set -xを常時有効にして、コマンドの引数にSecretを含めたまま実行する。 - kubeconfigをマニフェストに埋め込んだり、リポジトリにコミットする。
7.2 安全に扱うためのコツ
- 必要最低限のSecretsだけをワークフローに渡す(「何でもかんでも」詰め込みすぎない)。
- 本番とステージングでSecretsを分け、誤ってステージング用の認証情報で本番更新をしてしまう事故を防ぐ。
- 用が済んだ一時ファイル(kubeconfigなど)はジョブ内で削除しておくと安心です。
8. テスト戦略をCIとどう結びつけるか
CI/CDの品質は、結局のところ「どんなテストを、どこまで自動で回しているか」で決まります。
8.1 層ごとのテスト例
- Lint(スタイル/静的解析):
ruff,flake8,mypyなど - 単体テスト:サービス層の関数・クラス
- APIテスト:
TestClientやhttpx.AsyncClientでFastAPIのエンドポイントを検証 - 統合テスト:実際のDBやミドルウェアを使ったエンドツーエンドテスト
GitHub Actionsでは、これらをジョブに分けて並列実行し、結果をバッジ(status badge)としてREADMEに貼ることで、プロジェクトの健康状態をひと目で確認できるようにできます。
8.2 実務的なラインの決め方
- PR時:Lint+単体テスト+重要なAPIテスト
- mainマージ時:上記+一部統合テスト
- 夜間や指定時間:長時間かかる統合テストや負荷テストをスケジュール実行
といったように、「いつなら少し重いテストを走らせても問題ないか」をチーム内で合意しておくと、CI時間と品質のバランスが取りやすくなります。
9. ロールバックとトラブル時の運用
CI/CDがあると、「壊れたバージョン」を素早く元に戻せるかどうかがとても重要になってきます。
9.1 Dockerイメージタグを活かす
前述の通り、Dockerイメージには latest だけでなく git SHA のタグもつけておくと、特定のコミットに簡単に戻せます。
- 例:
ghcr.io/owner/repo:3f2a9c1
Kubernetesマニフェストのimage:をこのタグに差し替えて再適用するだけで、以前の状態へ戻せます。
9.2 GitHub Actionsでのロールバックワークフロー
慣れてきたら、「ロールバック専用」のworkflow_dispatch(手動起動ワークフロー)を作るのもおすすめです。
- 入力として「ロールバック先のタグ」や「コミットSHA」を受け取る。
- デプロイジョブと同じ手順で、Kubernetesへマニフェストをapplyする。
こうしておくと、深夜のトラブル時でも「決まった手順」で安全に戻すことができます。
10. 読者別にみるCI/CD導入のインパクト
10.1 個人開発・学習者の場合
- pushするだけでテストが走るようになり、「テストを忘れて壊れたままデプロイ」という事故が激減します。
- Dockerビルドも自動化されるため、「昨日どうやって本番イメージを作ったんだっけ?」という記憶頼みから解放されます。
- Kubernetesまでは使わないとしても、「CIの型」を覚えておけば、将来どんな環境でも応用が効きます。
10.2 小規模チーム・受託開発の場合
- PR→テスト→レビュー→ステージングデプロイまでの流れが自動化され、レビューの粒度に集中できます。
- リリース手順がワークフローとしてコード化されることで、「人によってやり方が違う」問題が解消されます。
- 新しいメンバーにも、「このYAMLを見ればリリースの全体像が分かる」という安心感を提供できます。
10.3 スタートアップSaaSチームの場合
- 本番環境へのアクセスをGitHub Actionsに集約することで、個々人のローカルから直接本番へ触る必要がなくなります。
- 環境ごとのSecretsや保護ルールにより、本番リリースの権限を細かく設計しやすくなります。
- 将来的にHelmやArgo CDを導入して「GitOps」スタイルに移行する際も、今のワークフローを土台として整えていけます。
11. よくある落とし穴と回避策
| 症状 | 原因 | 対策 |
|---|---|---|
| CIが遅く、誰も待たなくなる | 全テストを毎回フル実行 | PR時とmainマージ時で実行テストを分ける、ジョブの並列化 |
| 本番環境だけ失敗する | Secretsや環境変数の差分 | 環境ごとにEnvironmentsで設定を管理、.env.exampleとドキュメント整備 |
| kubeconfigを紛失/漏洩 | ワークフローやログに生の情報を書いた | kubeconfigはSecretsに保存、ログに出さない、一時ファイルは削除 |
| デプロイに失敗しても気づかない | 通知設定不足 | GitHub通知、Slackやメール連携を追加して失敗を見逃さない |
| YAMLが複雑になりすぎる | 1つのワークフローで何でもやろうとする | CI/CDを分割、ステージング/本番を別ワークフローにする |
12. 導入ロードマップ(少しずつ段階的に)
- テスト専用のCIワークフローを作る(push/PR)。
- Dockerイメージをビルドし、GHCRなどのコンテナレジストリへプッシュするステージを追加。
- ステージングクラスタへのデプロイを自動化し、
ENV=stgのFastAPI設定と連携。 - GitHub EnvironmentsとSecretsを使い、本番用の
deploy-productionジョブを作る(手動+承認付き)。 - ロールバック用ワークフローやHelm/kustomize統合、カスタムメトリクスによるHPA連携など、必要に応じて発展させる。
13. 参考リンク
公式ドキュメントや信頼性の高い解説記事を中心に、深掘りしたい方向けのリンクをまとめます。
-
GitHub Actions
-
FastAPI デプロイ関連
-
Docker / コンテナレジストリ
-
Kubernetes × GitHub Actions
-
CI/CDとKubernetes全般
14. まとめ
- GitHub Actionsを使えば、FastAPIアプリのテスト・Dockerビルド・レジストリへのプッシュ・Kubernetesデプロイまでを、1つのワークフローにまとめて自動化できます。
- EnvironmentsやSecretsを活用することで、ステージングと本番の差分管理や権限管理がしやすくなり、ヒューマンエラーを減らせます。
- Dockerイメージタグ、ロールバック用ワークフロー、テスト戦略の工夫により、トラブル時にも落ち着いて対応できる体制が整います。
- 最初から完璧を目指さず、「テスト → ビルド → ステージングデプロイ」の3ステップから始め、少しずつ本番や高度な仕組みへ広げていくのがおすすめです。
今日のところは、まず最初の一歩として「テスト専用ワークフロー」を作り、その上にDockerビルドとデプロイを乗せていきましょう。
あなたのFastAPIアプリが、押すだけでリリースできる状態に近づいていくのは、とてもワクワクする体験になるはずです。
