CI/CD(1/2)

パイプラインと AWS 想定。

CI/CD: AWS想定 (仮)

全体像

flowchart LR Dev[Developer] -->|push| GH[GitHub] GH -->|trigger| GHA[GitHub Actions] GHA -->|OIDC AssumeRole| IAM[AWS IAM Role] GHA -->|build push| ECR[Amazon ECR] GHA -->|update service| ECS[ECS Fargate] ECS -->|read| SM[Secrets Manager] ECS -->|read write| S3[Amazon S3] ECS -->|stdout| CWL[CloudWatch Logs] ECS -->|connect| RDS[Amazon RDS] ECS -->|connect| EC[ElastiCache Redis]

サービス選定 (仮想定)

役割 サービス 備考
ソース管理 GitHub AWS CodeCommitは実務でほぼ使わない
CI (テスト/lint) GitHub Actions 無料枠あり、エコシステム豊富
CD (ビルド/デプロイ) GitHub Actions + AWS CLI CodePipeline/CodeBuildは選定しない
イメージレジストリ Amazon ECR プライベートで安全
実行環境 Amazon ECS (Fargate) サーバレス、運用負荷低
シークレット管理 AWS Secrets Manager / SSM Parameter Store 機密度で使い分け
ログ集約 CloudWatch Logs Datadog連携も可
監視・アラート CloudWatch Alarms (+ Datadog 任意)
ストレージ Amazon S3 (本番) / MinIO (ローカル) (11.4)
RDB Amazon RDS (PostgreSQL/MySQL) / Aurora
キャッシュ ElastiCache (Redis)
ロードバランサ ALB HTTPSはACMで証明書管理
CDN CloudFront 静的アセット高速化
Infra as Code Terraform / AWS CDK Terraformが事実上の標準

.github/workflows/ci.yml (テスト/Lint、PR・push時)

name: CI

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

permissions:
  contents: read
  id-token: write   # OIDC用

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

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

      - name: Build dev image
        run: docker compose build app

      - name: Lint
        run: docker compose run --rm app ruff check .

      - name: Type check
        run: docker compose run --rm app mypy src

      - name: Test
        run: docker compose run --rm app pytest -v --cov=src --cov-report=xml

      - name: Upload coverage
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: coverage
          path: coverage.xml

CI もローカルと同じ Docker環境で実行する。「ローカルで通ったらCIでも通る」を担保する基本戦術。

.github/workflows/deploy.yml (main マージ時)

name: Deploy to ECS

on:
  push:
    branches: [main]

permissions:
  contents: read
  id-token: write

env:
  AWS_REGION: ap-northeast-1
  ECR_REPOSITORY: app
  ECS_CLUSTER: app-cluster
  ECS_SERVICE: app-service
  CONTAINER_NAME: app

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

      - name: Configure AWS credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsDeployRole
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to ECR
        id: ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build and push image
        run: |
          IMAGE="${{ steps.ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }}"
          docker build -f docker/app/Dockerfile -t $IMAGE .
          docker push $IMAGE
          echo "IMAGE=$IMAGE" >> $GITHUB_ENV

      - name: Render new task definition
        id: taskdef
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: .aws/task-definition.json
          container-name: ${{ env.CONTAINER_NAME }}
          image: ${{ env.IMAGE }}

      - name: Deploy to ECS
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          task-definition: ${{ steps.taskdef.outputs.task-definition }}
          service: ${{ env.ECS_SERVICE }}
          cluster: ${{ env.ECS_CLUSTER }}
          wait-for-service-stability: true

OIDC認証 (重要)

長期アクセスキーをGitHub Secretsに保存しない。代わりに OIDC でその場限りの認証情報を発行する。

セットアップ手順:

  1. AWS IAM で OIDC Identity Provider を追加 (token.actions.githubusercontent.com)
  2. GitHubリポジトリ・ブランチ限定の IAM Role を作成
  3. Role の信頼ポリシーで Repository / Branch を絞る (他リポジトリから乗っ取られないため)
  4. GitHub Actions 側で aws-actions/configure-aws-credentials@v4 でRole Assume

信頼ポリシー例 (mainブランチのみ許可):

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
      },
      "StringLike": {
        "token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main"
      }
    }
  }]
}

ECS Task Definition の要点 (.aws/task-definition.json 抜粋)

{
  "family": "app",
  "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::123456789012:role/AppTaskRole",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "containerDefinitions": [
    {
      "name": "app",
      "image": "PLACEHOLDER",
      "essential": true,
      "portMappings": [{"containerPort": 8000, "protocol": "tcp"}],
      "environment": [
        {"name": "AWS_REGION", "value": "ap-northeast-1"},
        {"name": "S3_BUCKET_UPLOADS", "value": "app-prod-uploads"}
      ],
      "secrets": [
        {
          "name": "DATABASE_URL",
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:app/db-url"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/app",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "app",
          "awslogs-create-group": "true"
        }
      },
      "healthCheck": {
        "command": ["CMD-SHELL", "curl -f http://localhost:8000/healthz || exit 1"],
        "interval": 30,
        "timeout": 5,
        "retries": 3,
        "startPeriod": 10
      }
    }
  ]
}
" aria-label="前後の章">← 運用ルール / Skills →" aria-label="前後の章">← 提出運用 →