404 Not Found

404 Not Found


nginx

CI/CD

Bobは手動デプロイのたびにミスをしています - マイグレーションの実行忘れ, 環境変数の設定ミス, テスト未通過のコードの本番へのプッシュ。CharlieはCI/CDの自動化を必要としています:コードプッシュ時の自動テスト, プルリクエストの自動プレビュー, mainブランチからの本番への自動デプロイ, すべて手動介入なしで実現します。

1. 学ぶ内容


2. 管理者のリアルストーリー

(1) ペインポイント:手動デプロイでの頻繁なミス

BobはMegaShopを手動でデプロイしています。手順は:1) リポジトリをクローン 2) npm installを実行 3) テストを実行 (忘れた) 4) ビルド 5) アップロード 6) マイグレーションを実行 (忘れた) 7) サービスを再起動。1つのステップでも抜かすと問題が発生し, 月に平均2回のデプロイインシデントが起きています。

(2) GitHub Actions CI/CDソリューション

プッシュのたびにフルプロセスが自動実行されます:

YAML
# .github/workflows/deploy.yml
on: push
jobs:
  test:    → lint + typecheck + vitest
  build:   → npm run build
  deploy:  → docker compose up -d

(3) 利点:手作業ゼロ + インシデントゼロ

コードプッシュ後の完全自動テスト, ビルド, デプロイ;プルリクエストは自動的にプレビュー環境を生成;コードのmainブランチへのマージは自動デプロイ;デプロイインシデントがゼロに。


3. GitHub Actionsワークフロー

(1) CI/CDパイプラインの段階

100%
flowchart LR
    A[Push / PR] --> B[Lint + TypeCheck]
    B --> C[ユニットテスト]
    C --> D[ビルド]
    D --> E{ブランチ?}
    E -->|PR| F[プレビューデプロイ]
    E -->|main| G[ステージングデプロイ]
    G --> H[スモークテスト]
    H --> I[本番デプロイ]
    I --> J[ヘルスチェック]
    J --> K[完了 ✅]

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

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

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

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npx nuxi typecheck

  test:
    runs-on: ubuntu-latest
    needs: lint
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
          POSTGRES_DB: megashop_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    env:
      DATABASE_URL: postgresql://test:test@localhost:5432/megashop_test
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npx prisma migrate deploy
      - run: npm run test:coverage
      - name: カバレッジのアップロード
        uses: codecov/codecov-action@v4

  build:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npx prisma generate
      - run: npm run build
      - name: ビルド成果物のアップロード
        uses: actions/upload-artifact@v4
        with:
          name: nuxt-build
          path: .output/

出力:

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

4. コード品質管理

(1) 品質ゲート設定

(1) ▶サンプル:ESLint + Prettier設定

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/eslint'],

  eslint: {
    config: {
      stylistic: {
        indent: 2,
        quotes: 'single',
        semi: false
      }
    }
  }
})

出力:

TEXT
// 実行成功

(2) ▶サンプル:package.jsonスクリプト

JSON
{
  "scripts": {
    "dev": "nuxi dev",
    "build": "nuxi build",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "typecheck": "nuxi typecheck",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage",
    "test:e2e": "playwright test",
    "validate": "npm run lint && npm run typecheck && npm run test"
  }
}

出力:

JSON
{
  "scripts": {
    "dev": "nuxi dev",
    "build": "nuxi build",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "typecheck": "nuxi typecheck",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage",
    "test:e2e": "playwright test",
    "validate": "npm run lint && npm run typecheck && npm run test"
  }
}

(2) 品質ゲート指標

指標 ツール ゲート閾値 説明
コードスタイル ESLint 0エラー 一貫したコードスタイル
フォーマット Prettier 0警告 自動フォーマット
型チェック vue-tsc 0エラー TypeScript型安全性
ユニットテスト Vitest ≥ 80%カバレッジ コアロジックのカバー
E2Eテスト Playwright 全て合格 主要フローの検証
バンドルサイズ rollup-plugin < 200 KB gzip サイズ膨張の防止

5. マルチ環境管理

(1) ▶サンプル:環境設定ファイル

TEXT
# .env.development
DATABASE_URL=postgresql://dev:dev@localhost:5432/megashop_dev
REDIS_URL=redis://localhost:6379
JWT_ACCESS_SECRET=dev-access-secret

# .env.staging
DATABASE_URL=postgresql://staging:xxx@staging-db.internal:5432/megashop_staging
REDIS_URL=redis://staging-redis.internal:6379
JWT_ACCESS_SECRET=${{ secrets.STAGING_JWT_SECRET }}

# .env.production
DATABASE_URL=postgresql://prod:xxx@prod-db.internal:5432/megashop
REDIS_URL=redis://prod-redis.internal:6379
JWT_ACCESS_SECRET=${{ secrets.PROD_JWT_SECRET }}

(2) ▶サンプル:ステージングデプロイワークフロー

YAML
# .github/workflows/deploy-staging.yml
name: Deploy Staging

on:
  push:
    branches: [main]

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    needs: [lint, test]
    environment: staging
    steps:
      - uses: actions/checkout@v4
      - name: ステージングにデプロイ
        run: |
          ssh staging-server << 'EOF'
          cd /opt/megashop
          git pull origin main
          docker compose up -d --build
          docker compose exec web npx prisma migrate deploy
          EOF

      - name: スモークテスト
        run: |
          sleep 10
          curl -f https://staging.megashop.com/api/products?limit=1 || exit 1

      - name: チームに通知
        if: failure()
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {"text": "ステージングデプロイが失敗しました!"}

出力:

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

(1) 環境の比較

環境 目的 データベース デプロイ方法 アクセス権限
development ローカル開発 ローカルPostgreSQL npm run dev 開発者
staging 統合テスト 独立したPostgreSQL 自動 (mainプッシュ) チーム
production 本番環境 本番PostgreSQL 自動 (tag/release) 全ユーザー

6. デプロイ戦略

(1) デプロイ戦略の比較

戦略 原理 ダウンタイム ロールバック速度 複雑さ
直接置き換え 古いものを止めて新しいものを起動 🔴 あり (5-30秒) 🟡 再デプロイ 🟢 シンプル
ブルーグリーンデプロイ 2つの環境を切り替え 🟢 なし 🟢 数秒で切り替え 🟡 中
ローリングアップデート インスタンスを1つずつ置き換え 🟢 なし 🟡 1つずつロールバック 🟡 中
カナリアリリース 少数のユーザーで先に検証 🟢 なし 🟢 即座にロールバック 🔴 複雑

(1) ▶サンプル:ブルーグリーンデプロイスクリプト

BASH
#!/bin/bash
# deploy-blue-green.sh

CURRENT=$(docker compose ps --format '{{.Name}}' | grep -o 'blue\|green' | head -1)
if [ "$CURRENT" = "blue" ]; then
  NEXT="green"
else
  NEXT="blue"
fi

echo "$NEXT環境にデプロイ中..."

# 次の環境をビルドして起動
docker compose -f docker-compose.yml -f docker-compose.$NEXT.yml up -d --build

# ヘルスチェックを待機
for i in {1..30}; do
  if curl -sf http://localhost:3001/health; then
    echo "ヘルスチェック合格"
    break
  fi
  sleep 2
done

# Nginxを新しい環境に切り替え
sed "s/$CURRENT/$NEXT/g" nginx.conf > /tmp/nginx.conf
docker compose exec nginx nginx -s reload

echo "$CURRENTから$NEXTに切り替えました"

出力:

TEXT
CONTAINER ID   IMAGE     STATUS    
abc123         latest    Up 2 hours

7. 総合例:MegaShopの完全なCI/CDフロー

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

on:
  release:
    types: [published]

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

      - name: Dockerイメージをビルド
        run: docker build -t megashop:${{ github.sha }} .

      - name: レジストリにプッシュ
        run: |
          docker tag megashop:${{ github.sha }} ghcr.io/megashop/megashop:latest
          echo ${{ secrets.GHCR_TOKEN }} | docker login ghcr.io -u $ --password-stdin
          docker push ghcr.io/megashop/megashop:latest

      - name: 本番にデプロイ
        run: |
          ssh prod-server << EOF
          cd /opt/megashop
          docker pull ghcr.io/megashop/megashop:latest
          docker compose up -d --no-build
          docker compose exec web npx prisma migrate deploy
          EOF

      - name: ヘルスチェック
        run: |
          for i in {1..10}; do
            if curl -sf https://megashop.com/api/health; then exit 0; fi
            sleep 5
          done
          exit 1

      - name: 失敗時にロールバック
        if: failure()
        run: |
          ssh prod-server << EOF
          cd /opt/megashop
          docker compose down
          docker tag megashop:previous megashop:latest
          docker compose up -d
          EOF

❓よくある質問

Q GitHub Actionsの無料枠で十分ですか?
A パブリックリポジトリは無制限の分数があります。プライベートリポジトリは月間2,000分の制限があります。MegaShopの1回のCI実行は約10分なので, 1日10回実行しても約100分であり, 十分です。
Q PRプレビュー環境はどのように設定しますか?
A Vercel Previewで自動生成されます。各PRには独自のURL (pr-123-megashop.vercel.app)があり, プルリクエストのマージ後に自動的に削除されます。
Q CIでデータベースマイグレーションはどのように実行しますか?
A CIではprisma migrate deployを使用してください (既存のマイグレーションのみを適用し, 新しいものは作成しません)。開発中はprisma migrate devでマイグレーションファイルを作成し, Gitにコミットしてください。
Q ゼロダウンタイムデプロイはどのように実現しますか?
A ブルーグリーンデプロイ (2つの環境を切り替え)またはPM2クラスタリロード (ワーカーを1つずつ置き換え)を使用してください。Dockerではローリングアップデートを使用します。
Q ロールバックはどのように行いますか?
A git revertして再デプロイしてください。Dockerでは前のイメージタグで再起動します。PM2ではpm2 stopしてから旧バージョンを起動します。自動ロールバックのためには, デプロイスクリプトにヘルスチェック失敗時の処理ロジックを追加してください。
Q シークレットはどのように安全に管理すべきですか?
A シークレットはGitHub Secretsに環境変数として保存し, Gitにコミットしないでください。runtimeConfigのプライベートフィールドはサーバーサイドのみです。CIでは${{ secrets.XXX }}で参照してください。

📖まとめ


📝練習問題

  1. 基本問題 (難易度:⭐):GitHub Actions CIワークフローを作成し, コミットプッシュ時に自動的にlint, 型チェック, テストを実行してください
  2. 応用問題 (難易度:⭐⭐):ステージングへの自動デプロイを追加してください - mainブランチのプッシュ時に自動的にステージングサーバーにデプロイし, デプロイ後にスモークテストを実行してください
  3. チャレンジ (難易度:⭐⭐⭐):ブルーグリーンデプロイスクリプト + 自動ロールバックを実装してください - ヘルスチェックが失敗した場合に自動的に前のバージョンに切り替える仕組みを構築してください

---|

Web-Tutorial.com

Web-Tutorial 技術チーム

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

100%