404 Not Found

404 Not Found


nginx

CI/CD — GitHub Actions 自動デリバリーパイプライン

CI/CDは自動車工場の自動組み立てラインのようなもの - 部品 (コード)が入力され, 溶接 (lint), 品質検査 (テスト), 塗装 (セキュリティスキャン)を経て, 検査に合格した完成品 (Dockerイメージ)が自動的に販売店 (本番環境)に配送されます。

1. 学ぶ内容


2. Aliceの実話

(1) 悩み:手動デプロイでのミスが頻発

Bobがコードを提出した後, Aliceはサーバーから手動でコードを取得し, テストを実行し, イメージをビルドし, コンテナを再起動していました。このプロセスには毎回30分かかり, テストステップをスキップすることもよくありました - ある時, Aliceはマイグレーションの実行を忘れ, 新エンドポイントが本番で500エラーを報告しました。Charlieは3つの環境 (dev, staging, prod)でこれらのタスクを手動で実行できると言いましたが, リスクが高すぎました。

(2) GitHub Actionsによる解決策

GitHub Actionsは, プッシュやプルリクエストのたびにフルパイプライン - lint → テスト → ビルド → デプロイ - を自動実行します。すべてのステップはコードで定義されるため, 見落としはなく, 失敗すれば自動的にマージがブロックされます。

(3) 成果

デプロイ時間は30分 (手動)から5分 (自動化)に短縮され, テストの見落としは完全に排除され (テストに失敗したPRはマージできません), 3つの環境すべてでデプロイが完全に一貫性を保つようになりました。


3. GitHub Actionsの基本

(1) コア概念

100%
flowchart LR
    Trigger[Trigger: push/PR] --> Workflow[Workflow]
    Workflow --> Job1[Job 1: Lint+Test]
    Workflow --> Job2[Job 2: Build]
    Job1 --> Step1[Step: ruff check]
    Job1 --> Step2[Step: pytest]
    Job2 --> Step3[Step: docker build]
    Job2 --> Step4[Step: docker push]
概念 説明
Workflow 自動プロセス定義 .github/workflows/ci.yml
Trigger トリガー条件 push, pull_request
Job ステップの集合からなる実行単位 test, build, deploy
Step 単一操作 run: pytest
Action 再利用可能なアクション actions/checkout@v4
Runner 実行環境 ubuntu-latest

(2) ブランチと環境のマッピング

100%
graph TD
    Feature[feature/*] -->|PR| Develop[develop]
    Develop -->|Deploy| DevEnv[Dev環境]
    Develop -->|PR| Main[main]
    Main -->|Deploy| Staging[Staging環境]
    Main -->|Tag v*| Prod[本番環境]
ブランチ 環境 トリガー デプロイ方式
feature/* - PR自動テスト デプロイなし
develop Dev 自動プッシュ Docker Compose
main Staging 自動プッシュ Docker Compose
v* tag 本番 tag手動 Docker Compose / K8s

4. CIパイプライン

(1) ▶ サンプル:完全なCIワークフロー

YAML
# .github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install UV
        run: curl -LsSf https://astral.sh/uv/install.sh | sh

      - name: Install dependencies
        run: uv sync --frozen

      - name: Run ruff lint
        run: uv run ruff check app/ tests/

      - name: Run ruff format check
        run: uv run ruff format --check app/ tests/

  test:
    runs-on: ubuntu-latest
    needs: lint
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: pricetracker
          POSTGRES_PASSWORD: test_password
          POSTGRES_DB: pricetracker_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      redis:
        image: redis:7-alpine
        ports:
          - 6379:6379
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4

      - name: Install UV
        run: curl -LsSf https://astral.sh/uv/install.sh | sh

      - name: Install dependencies
        run: uv sync --frozen

      - name: Run pytest
        env:
          DATABASE_URL: postgresql+asyncpg://pricetracker:test_password@localhost:5432/pricetracker_test
          REDIS_URL: redis://localhost:6379/0
          SECRET_KEY: test-secret-key-for-ci
        run: uv run pytest tests/ -v --cov=app --cov-report=xml

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage.xml

  security:
    runs-on: ubuntu-latest
    needs: lint
    steps:
      - uses: actions/checkout@v4

      - name: Run safety check
        run: |
          pip install safety
          safety check --json || true

      - name: Run bandit
        run: |
          pip install bandit
          bandit -r app/ -f json || true

  build:
    runs-on: ubuntu-latest
    needs: [test, security]
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          file: docker/Dockerfile
          push: false
          tags: pricetracker:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

出力:

TEXT
CI/CDパイプラインを読み込みました
パイプラインステータス:合格
テスト:12合格, 0不合格

5. CDパイプライン

(1) ▶ サンプル:本番デプロイワークフロー

YAML
# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    tags:
      - 'v*'

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push image
        uses: docker/build-push-action@v5
        with:
          context: .
          file: docker/Dockerfile
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:${{ github.ref_name }}
            ghcr.io/${{ github.repository }}:latest
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Deploy to server
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            cd /opt/pricetracker
            docker compose pull
            docker compose up -d --remove-orphans
            docker compose exec api alembic upgrade head
            echo "Deployed version ${{ github.ref_name }}"

出力:

TEXT
CONTAINER ID   IMAGE          STATUS         PORTS
abc123         nginx:latest   Up 2 hours     0.0.0.0:80->80/tcp

(2) 完全なCI/CDパイプラインワークフロー

100%
flowchart LR
    Push[Bobがコードをプッシュ] --> Lint[Lint: ruff]
    Lint --> Test[Test: pytest]
    Lint --> Sec[Security: safety + bandit]
    Test --> Build[Docker Build]
    Sec --> Build
    Build -->|On tag| Push[Push to GHCR]
    Push --> Deploy[Deploy to Production]
    Deploy --> Health[Health Check]
    Health --> Done[✓ Live]

(2) ▶ サンプル:ブランチ保護と環境保護ルール

YAML
# .github/workflows/branch-protection.yml
name: Branch Protection Check

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - name: Verify PR target is main
        run: |
          if [ "${{ github.base_ref }}" != "main" ]; then
            echo "PR must target main branch"
            exit 1
          fi

      - name: Check environment approval
        if: github.event.pull_request.merged == true
        uses: octokit/request-action@v2
        with:
          route: POST /repos/{owner}/{repo}/deployments
          environment: staging
          required_reviewers: 1

出力:

TEXT
CI/CDパイプラインを読み込みました
パイプラインステータス:合格
テスト:12合格, 0不合格

❓ よくある質問

Q GitHub Actionsの無料枠は十分ですか?
A パブリックリポジトリには制限がありません。プライベートリポジトリは月2,000無料分があります。PriceTrackerのCIは1回約5分で実行され, 1日10回で約1,500分なので, 十分です。
Q CIでデータベースマイグレーションはどう処理しますか?
A CIテストではBase.metadata.create_all()で直接テーブルを作成し (Alembicを使わず), CDデプロイスクリプトでalembic upgrade headを実行します。
Q シークレットはどのように安全に保管しますか?
A GitHubプロジェクトのSettings → Secrets and variables → Actionsで, SERVER_SSH_KEY, DB_PASSWORDなどを保存し, YAML内で${{ secrets.XXX }}で参照します。
Q mainブランチのみにデプロイするにはどうしますか?
A on: push: tags: ['v*']を使用し, タグ作成時のみデプロイをトリガーします。開発ブランチではテストのみを実行し, デプロイは行いません。
Q Dockerキャッシュはどう活用しますか?
A cache-from: type=ghaを使用してGitHub Actionsキャッシュを活用します。初回ビルドは5分, 以降の変更は変更レイヤーのみ再ビルドされ, 1〜2分かかります。
Q デプロイ失敗後のロールバックはどうしますか?
A docker compose down + docker compose pull <previous-tag> + docker compose up -d。前バージョンのイメージタグを保持しておき, ロールバックを容易にします。

📖 まとめ


📝 練習問題

  1. 基本問題 (難易度 ⭐):.github/workflows/ci.ymlを作成し, プッシュやPR作成時にruff checkpytestが自動実行されるように設定し, GitHub Actionsページで緑色の「Passed」が表示されることを確認してください。ヒント:on: push: branches: [main]
  2. 応用問題 (難易度 ⭐⭐):テストジョブにPostgreSQLとRedisのサービスコンテナを追加し, 環境変数を設定してpytestがテストデータベースに接続できるようにし, Dockerビルドステップを追加してイメージのビルドが成功することを確認してください。ヒント:services: + options: --health-cmd
  3. チャレンジ (難易度 ⭐⭐⭐):CI/CDを完成させる - CIパイプラインにはlint, テスト, セキュリティチェック, ビルドを含め, CDパイプラインはv*タグ作成時にイメージをGHCRにプッシュしSSHでサーバーにデプロイし, GitHub Secretsでキーを管理してください。ヒント:docker/login-action + appleboy/ssh-action + ${{ secrets.XXX }}

---|

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%