green snake
Photo by Pixabay on Pexels.com
目次

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つを最初に決めておくと迷いにくくなります。

  1. どのタイミングでテストを走らせるか(push、PR、mainマージなど)。
  2. どこまでを自動化し、どこから人間の承認をはさむか。
  3. アーティファクト(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からkubectlkustomizeを使ってK8sへデプロイする例が紹介されています。

ここでは「push型デプロイ」として、Actionsから直接kubectl applyを実行する最小構成を示します。

5.1 クラスタへの接続情報をGitHub側に登録

Kubernetesクラスタの認証はクラウドや構成によって差がありますが、代表的には次のような方法があります。

  • kubeconfigの内容をGitHub Secretsとして登録し、ワークフロー内でファイルとして展開。
  • クラウドプロバイダ提供のsetup-*系アクション(例:GKEのgcloud連携)を使って認証。

ここでは、簡易的に「kubeconfig全体をSecretにいれて使う」パターンにします。

  1. 手元の ~/.kube/config から対象クラスタの設定部分を抽出して、KUBECONFIG_CONTENT というGitHub Secretsに保存。
  2. ワークフロー内でファイルとして出力し、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

ポイント:

  • environmentstaging と書くことで、GitHub側の「環境機能」を使い、保護ルールやレビューを設定できます。
  • sed でYAML内のimage:を上書きするのは簡易的な方法です。kustomize や Helm を使うと、より柔軟にイメージ差し替えが可能になります。
  • 本番環境用には deploy-production ジョブを用意し、environment.name: production と手動トリガー、レビュー必須などを設定するのがおすすめです。

6. 環境ごとの切り替えと保護ルール

本番とステージングは、次の2点で差をつけるのが定番です。

  1. 使用するSecrets/ConfigMapの内容(接続先DB・外部APIキーなど)。
  2. デプロイフローの「自動・手動」の違い(ステージングは自動、本番は手動+承認)。

6.1 GitHub Environmentsの活用

GitHub Actionsには「Environments」という仕組みがあり、環境ごとに専用のSecretsや保護ルールを持たせることができます。

  • staging 環境:

    • Secrets: KUBECONFIG_STAGING, DATABASE_URL_STAGING など
    • 自動デプロイOK(保護ルールは緩め)
  • production 環境:

    • Secrets: KUBECONFIG_PROD, DATABASE_URL_PROD など
    • 手動承認が必要、特定ユーザーのみ承認できる、など保護ルールを設定

ワークフローでは、ジョブに environment を指定することで、その環境に紐づいたSecretsが利用できるようになります。

environment:
  name: production
  url: https://app.example.com

6.2 FastAPI設定との連携

以前の記事で扱った pydantic-settings のenvなどを使い、ENV=stgENV=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テスト:TestClienthttpx.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. 導入ロードマップ(少しずつ段階的に)

  1. テスト専用のCIワークフローを作る(push/PR)。
  2. Dockerイメージをビルドし、GHCRなどのコンテナレジストリへプッシュするステージを追加。
  3. ステージングクラスタへのデプロイを自動化し、ENV=stgのFastAPI設定と連携。
  4. GitHub EnvironmentsとSecretsを使い、本番用のdeploy-productionジョブを作る(手動+承認付き)。
  5. ロールバック用ワークフローやHelm/kustomize統合、カスタムメトリクスによるHPA連携など、必要に応じて発展させる。

13. 参考リンク

公式ドキュメントや信頼性の高い解説記事を中心に、深掘りしたい方向けのリンクをまとめます。


14. まとめ

  • GitHub Actionsを使えば、FastAPIアプリのテスト・Dockerビルド・レジストリへのプッシュ・Kubernetesデプロイまでを、1つのワークフローにまとめて自動化できます。
  • EnvironmentsやSecretsを活用することで、ステージングと本番の差分管理や権限管理がしやすくなり、ヒューマンエラーを減らせます。
  • Dockerイメージタグ、ロールバック用ワークフロー、テスト戦略の工夫により、トラブル時にも落ち着いて対応できる体制が整います。
  • 最初から完璧を目指さず、「テスト → ビルド → ステージングデプロイ」の3ステップから始め、少しずつ本番や高度な仕組みへ広げていくのがおすすめです。

今日のところは、まず最初の一歩として「テスト専用ワークフロー」を作り、その上にDockerビルドとデプロイを乗せていきましょう。
あなたのFastAPIアプリが、押すだけでリリースできる状態に近づいていくのは、とてもワクワクする体験になるはずです。


投稿者 greeden

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)