【AWSハンズオン】第12回 CI/CDパイプライン構築

【AWSハンズオン】第12回 CI/CDパイプライン構築

GitHub Actionsを使用して、コード変更からデプロイまでを自動化するCI/CDパイプラインを構築します。

AWS #AWS#ハンズオン#Github Actions

【AWSハンズオン】第12回 CI/CDパイプライン構築

サムネイル

GitHub Actionsを使用して、コード変更からデプロイまでを自動化するCI/CDパイプラインを構築します。

更新日: 8/13/2025

今回作業対象のブランチ

前提条件

  • 第1回から第11回までの構築が完了していること
  • CI/CD対象のGitHubリポジトリが作成されていること
  • こちらからDockerコンテナでの実行環境が準備できていること

Terraform実行環境の起動

前回と同様に、Dockerコンテナ内で作業を進めます。

# コンテナを起動してbashに入る
docker-compose run --rm terraform

これ以降のコマンドは、すべてこのコンテナ内で実行します。

基礎知識

CI/CD(Continuous Integration/Continuous Deployment)は、コード変更を自動的にビルド・テスト・デプロイする仕組みです。

WEBアプリケーション開発者の視点から見ると

ローカル開発では、コードを変更したら手動でビルドし、テストを実行し、サーバーにデプロイします。しかし、チーム開発や本番環境では以下の課題があります。

手動デプロイの課題 CI/CDによる解決 メリット
デプロイ手順を忘れる 自動化されたワークフロー 人的ミスの削減
テストの実行漏れ プルリクエスト時に自動テスト 品質の担保
環境による差異 同一のビルドプロセス 再現性の確保
デプロイに時間がかかる パイプライン並列処理 リリース頻度の向上

GitHub Actionsの仕組み

GitHub Actionsは、GitHubが提供するCI/CDサービスです。リポジトリ内にワークフローファイルを配置するだけで動作します。

構成要素 説明 用途
Workflow 自動化プロセス全体 ビルドからデプロイまでの一連の流れ
Job ワークフロー内の独立した処理単位 ビルド、テスト、デプロイなど
Step ジョブ内の個別のタスク コマンド実行、アクション実行
Runner ワークフローを実行する仮想マシン Ubuntu、Windows、macOS

デプロイ戦略

戦略 説明 適用場面
ブランチデプロイ 特定ブランチへのプッシュでデプロイ 開発環境への自動デプロイ
タグデプロイ Gitタグ作成でデプロイ 本番環境への計画的リリース
プルリクエストデプロイ PR作成でプレビュー環境構築 レビュー前の動作確認
手動承認デプロイ 承認後にデプロイ実行 本番環境への慎重なリリース

セキュリティの考慮事項

項目 リスク 対策
認証情報の管理 ハードコーディングによる漏洩 GitHub Secretsで暗号化管理
最小権限の原則 過剰な権限による被害拡大 デプロイ専用IAMロール
監査ログ 不正アクセスの見逃し CloudTrailで操作記録
ブランチ保護 意図しない変更のマージ 保護ルールとレビュー必須化

今回作成するリソース

リソース 説明 数量
GitHub Actions Workflow CI/CDパイプライン定義 3個
IAM Role (GitHub Actions用) OIDCによる認証 1個
IAM Policy 必要最小限の権限 複数
S3 Bucket (アーティファクト保存) ビルド結果の保存 1個
Parameter Store 環境変数の管理 複数

Terraformコードの実装

変数の追加

CI/CD関連の変数を追加します。

# terraform/variables.tf に追加

# CI/CD関連
variable "enable_github_oidc" {
  description = "GitHub Actions OIDCを有効にするか"
  type        = bool
  default     = true
}

variable "artifact_retention_days" {
  description = "build artifact の保持期間"
  type        = number
  default     = 30
}

variable "github_repository" {
  description = "GitHubリポジトリ(例: username/repository-name)"
  type        = string
  default     = ""
}

GitHub Actions用IAMロール(OIDC)

アクセスキーを使わず、OIDCで安全に認証します。

# terraform/cicd.tf

# GitHub OIDC Provider
resource "aws_iam_openid_connect_provider" "github" {
  url = "https://token.actions.githubusercontent.com"

  client_id_list = [
    "sts.amazonaws.com"
  ]

  # GitHub OIDCプロバイダーのサムプリント
  thumbprint_list = [
    "6938fd4d98bab03faadb97b34396831e3780aea1",
    "1c58a3a8518e8759bf075b76b750d4f2df264fcd"
  ]

  tags = {
    Name = "${var.project_name}-${var.environment}-github-oidc"
  }
}

# GitHub Actions用IAMロール
resource "aws_iam_role" "github_actions" {
  count = var.enable_github_oidc ? 1 : 0

  name = "${var.project_name}-${var.environment}-github-actions"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Federated = aws_iam_openid_connect_provider.github.arn
        }
        Action = "sts:AssumeRoleWithWebIdentity"
        Condition = {
          StringEquals = {
            "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
          }
          StringLike = {
            # GitHubリポジトリを指定
            "token.actions.githubusercontent.com:sub" = "repo:${var.github_repository}:*"
          }
        }
      }
    ]
  })

  tags = {
    Name = "${var.project_name}-${var.environment}-github-actions"
  }
}

OIDC設定パラメータの説明

パラメータ 設定値 設定理由
url token.actions.githubusercontent.com GitHub ActionsのOIDCプロバイダーURL
client_id_list sts.amazonaws.com AWSのSTSサービスをクライアントとして指定
thumbprint_list GitHub公式の値 SSL証明書の検証用フィンガープリント
Condition.StringLike repo:org/repo:* 特定リポジトリからのアクセスのみ許可

デプロイ用IAMポリシー

GitHub Actionsに必要な権限を付与します。

# terraform/cicd.tf

# GitHub Actions用のIAMポリシー
resource "aws_iam_role_policy" "github_actions" {
  count = var.enable_github_oidc ? 1 : 0

  name = "${var.project_name}-${var.environment}-github-actions-policy"
  role = aws_iam_role.github_actions[0].id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      # ECR関連の権限
      {
        Effect = "Allow"
        Action = [
          "ecr:GetAuthorizationToken",
          "ecr:BatchCheckLayerAvailability",
          "ecr:GetDownloadUrlForLayer",
          "ecr:BatchGetImage",
          "ecr:PutImage",
          "ecr:InitiateLayerUpload",
          "ecr:UploadLayerPart",
          "ecr:CompleteLayerUpload"
        ]
        Resource = [
          aws_ecr_repository.app.arn,
          "${aws_ecr_repository.app.arn}/*"
        ]
      },
      {
        Effect = "Allow"
        Action = [
          "ecr:GetAuthorizationToken"
        ]
        Resource = "*"
      },
      # ECS関連の権限
      {
        Effect = "Allow"
        Action = [
          "ecs:UpdateService",
          "ecs:DescribeServices",
          "ecs:DescribeTasks",
          "ecs:ListTasks",
          "ecs:RegisterTaskDefinition",
          "ecs:DescribeTaskDefinition"
        ]
        Resource = "*"
      },
      # S3関連の権限(フロントエンド用)
      {
        Effect = "Allow"
        Action = [
          "s3:PutObject",
          "s3:PutObjectAcl",
          "s3:GetObject",
          "s3:DeleteObject",
          "s3:ListBucket"
        ]
        Resource = [
          aws_s3_bucket.frontend.arn,
          "${aws_s3_bucket.frontend.arn}/*"
        ]
      },
      # CloudFront関連の権限
      {
        Effect = "Allow"
        Action = [
          "cloudfront:CreateInvalidation"
        ]
        Resource = aws_cloudfront_distribution.frontend.arn
      },
      # IAM PassRole権限(ECSタスク実行用)
      {
        Effect = "Allow"
        Action = [
          "iam:PassRole"
        ]
        Resource = [
          aws_iam_role.ecs_execution_role.arn,
          aws_iam_role.ecs_task_role.arn
        ]
      }
    ]
  })
}

# ECRへのプッシュ権限
resource "aws_iam_role_policy" "github_actions_ecr" {
  count = var.enable_github_oidc ? 1 : 0

  name = "${var.project_name}-${var.environment}-github-ecr"
  role = aws_iam_role.github_actions[0].id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "ecr:GetAuthorizationToken",
          "ecr:BatchCheckLayerAvailability",
          "ecr:GetDownloadUrlForLayer",
          "ecr:BatchGetImage",
          "ecr:PutImage",
          "ecr:InitiateLayerUpload",
          "ecr:UploadLayerPart",
          "ecr:CompleteLayerUpload"
        ]
        Resource = [
          aws_ecr_repository.app.arn,
          "${aws_ecr_repository.app.arn}/*"
        ]
      },
      {
        Effect = "Allow"
        Action = [
          "ecr:GetAuthorizationToken"
        ]
        Resource = "*"
      }
    ]
  })
}

# ECSデプロイ権限
resource "aws_iam_role_policy" "github_actions_ecs" {
  count = var.enable_github_oidc ? 1 : 0

  name = "${var.project_name}-${var.environment}-github-ecs"
  role = aws_iam_role.github_actions[0].id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "ecs:UpdateService",
          "ecs:DescribeServices",
          "ecs:DescribeTaskDefinition",
          "ecs:RegisterTaskDefinition",
          "ecs:ListTaskDefinitions",
          "ecs:DescribeClusters"
        ]
        Resource = [
          aws_ecs_cluster.main.arn,
          aws_ecs_service.app.id,
          "arn:aws:ecs:${var.aws_region}:*:task-definition/${var.project_name}-${var.environment}-app:*"
        ]
      },
      {
        Effect = "Allow"
        Action = [
          "iam:PassRole"
        ]
        Resource = [
          aws_iam_role.ecs_execution_role.arn,
          aws_iam_role.ecs_task_role.arn
        ]
      }
    ]
  })
}

# S3とCloudFrontデプロイ権限
resource "aws_iam_role_policy" "github_actions_frontend" {
  count = var.enable_github_oidc ? 1 : 0

  name = "${var.project_name}-${var.environment}-github-frontend"
  role = aws_iam_role.github_actions[0].id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:PutObject",
          "s3:PutObjectAcl",
          "s3:GetObject",
          "s3:DeleteObject",
          "s3:ListBucket"
        ]
        Resource = [
          aws_s3_bucket.frontend.arn,
          "${aws_s3_bucket.frontend.arn}/*"
        ]
      },
      {
        Effect = "Allow"
        Action = [
          "cloudfront:CreateInvalidation"
        ]
        Resource = aws_cloudfront_distribution.frontend.arn
      }
    ]
  })
}

IAMポリシー権限の説明

権限グループ アクション 必要な理由
ECR権限 PutImage, InitiateLayerUpload Dockerイメージのプッシュ
GetAuthorizationToken ECRへのログイン
ECS権限 UpdateService 新しいタスク定義でサービス更新
RegisterTaskDefinition 新しいタスク定義の登録
PassRole タスクロールの引き継ぎ
S3権限 PutObject, DeleteObject 静的ファイルのアップロード
CloudFront権限 CreateInvalidation キャッシュの無効化

アーティファクト保存用S3バケット

ビルド結果を保存するバケットを作成します。

# terraform/cicd.tf

# アーティファクト保存用S3バケット
resource "aws_s3_bucket" "artifacts" {
  bucket        = "${var.project_name}-${var.environment}-artifacts"
  force_destroy = true

  tags = {
    Name = "${var.project_name}-${var.environment}-artifacts"
  }
}

# バケットの暗号化
resource "aws_s3_bucket_server_side_encryption_configuration" "artifacts" {
  bucket = aws_s3_bucket.artifacts.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

# ライフサイクルポリシー
resource "aws_s3_bucket_lifecycle_configuration" "artifacts" {
  bucket = aws_s3_bucket.artifacts.id

  rule {
    id     = "delete-old-artifacts"
    status = "Enabled"

    filter {}

    expiration {
      days = var.artifact_retention_days
    }
  }
}

Parameter Store

環境変数を安全に管理します。

# terraform/cicd.tf

# デプロイ設定をParameter Storeに保存
resource "aws_ssm_parameter" "ecr_repository_url" {
  name  = "/${var.project_name}/${var.environment}/ecr-repository-url"
  type  = "String"
  value = aws_ecr_repository.app.repository_url

  tags = {
    Name = "${var.project_name}-${var.environment}-ecr-url"
  }
}

resource "aws_ssm_parameter" "ecs_cluster_name" {
  name  = "/${var.project_name}/${var.environment}/ecs-cluster-name"
  type  = "String"
  value = aws_ecs_cluster.main.name

  tags = {
    Name = "${var.project_name}-${var.environment}-ecs-cluster"
  }
}

resource "aws_ssm_parameter" "ecs_service_name" {
  name  = "/${var.project_name}/${var.environment}/ecs-service-name"
  type  = "String"
  value = aws_ecs_service.app.name

  tags = {
    Name = "${var.project_name}-${var.environment}-ecs-service"
  }
}

出力の追加

# terraform/outputs.tf に追加

# CI/CD関連の出力
output "github_actions_role_arn" {
  description = "GitHub Actions用IAMロールARN"
  value       = var.enable_github_oidc ? aws_iam_role.github_actions[0].arn : ""
}

output "artifacts_bucket_name" {
  description = "アーティファクト保存用S3バケット名"
  value       = aws_s3_bucket.artifacts.id
}

GitHub Actionsワークフローの作成

今回はdevelopブランチに、dev環境としてデプロイするように整備していきます。
自身もAIに手伝ってもらいつつCI/CDを書いたので、各々の環境に合わせて試行錯誤してみてください。

サンプルコード

バックエンドAPI用ワークフロー

テストは書いていないので一旦コメントアウトです。

https://github.com/Yutahhhhh/aws-hands-on/blob/tf-dev/.github/workflows/backend-deploy.yml

# .github/workflows/backend-deploy.yml

name: Backend Deploy

on:
  push:
    branches:
      - develop
    paths:
      - 'backend/**'
      - '.github/workflows/backend-deploy.yml'
  workflow_dispatch:

env:
  AWS_REGION: ap-northeast-1
  PROJECT_NAME: ${{ vars.PROJECT_NAME }}
  ENVIRONMENT: dev

permissions:
  id-token: write
  contents: read

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

  #     - name: Setup pnpm
  #       uses: pnpm/action-setup@v4
  #       with:
  #         version: 9

  #     - name: Setup Node.js
  #       uses: actions/setup-node@v4
  #       with:
  #         node-version: '20'
  #         cache: 'pnpm'
  #         cache-dependency-path: backend/pnpm-lock.yaml

  #     - name: Install dependencies
  #       working-directory: backend
  #       run: pnpm install --frozen-lockfile

  #     - name: Run tests
  #       working-directory: backend
  #       run: pnpm test

  #     - name: Run linter
  #       working-directory: backend
  #       run: pnpm run lint

  build-and-push:
    # needs: test
    runs-on: ubuntu-latest
    outputs:
      image-uri: ${{ steps.image.outputs.uri }}
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          role-session-name: github-actions
          aws-region: ${{ env.AWS_REGION }}

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

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

      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ steps.login-ecr.outputs.registry }}/${{ env.PROJECT_NAME }}-${{ env.ENVIRONMENT }}-app
          tags: |
            type=ref,event=branch
            type=sha,prefix={{date 'YYYYMMDD'}}-

      - name: Extract single image URI
        id: image
        run: |
          # 最初のタグを取得(通常はsha形式のタグ)
          IMAGE_URI=$(echo "${{ steps.meta.outputs.tags }}" | head -n1)
          echo "uri=${IMAGE_URI}" >> $GITHUB_OUTPUT

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: backend
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          platforms: linux/amd64

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          role-session-name: github-actions
          aws-region: ${{ env.AWS_REGION }}

      - name: Get current task definition
        run: |
          aws ecs describe-task-definition \
            --task-definition ${{ env.PROJECT_NAME }}-${{ env.ENVIRONMENT }}-app \
            --query taskDefinition > task-definition.json

      - name: Update task definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: task-definition.json
          container-name: ${{ env.PROJECT_NAME }}-${{ env.ENVIRONMENT }}-app
          image: ${{ needs.build-and-push.outputs.image-uri }}

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

フロントエンド用ワークフロー

テストは書いていないので一旦コメントアウトです。

https://github.com/Yutahhhhh/aws-hands-on/blob/tf-dev/.github/workflows/frontend-deploy.yml

# .github/workflows/frontend-deploy.yml

name: Frontend Deploy

on:
  push:
    branches:
      - develop
    paths:
      - 'frontend/**'
      - '.github/workflows/frontend-deploy.yml'
  workflow_dispatch:

env:
  AWS_REGION: ap-northeast-1
  PROJECT_NAME: ${{ vars.PROJECT_NAME }}
  ENVIRONMENT: dev

permissions:
  id-token: write
  contents: read

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

  #     - name: Setup Node.js
  #       uses: actions/setup-node@v4
  #       with:
  #         node-version: '20'
  #         cache: 'npm'
  #         cache-dependency-path: frontend/package-lock.json

  #     - name: Install dependencies
  #       working-directory: frontend
  #       run: npm ci

  #     - name: Run tests
  #       working-directory: frontend
  #       run: npm test

  build-and-deploy:
    # needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8
          run_install: false

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'pnpm'
          cache-dependency-path: frontend/pnpm-lock.yaml

      - name: Get pnpm store directory
        shell: bash
        run: |
          echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

      - name: Setup pnpm cache
        uses: actions/cache@v3
        with:
          path: ${{ env.STORE_PATH }}
          key: ${{ runner.os }}-pnpm-store-${{ hashFiles('frontend/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pnpm-store-

      - name: Install dependencies
        working-directory: frontend
        run: pnpm install --frozen-lockfile

      - name: Build application
        working-directory: frontend
        run: pnpm run build

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          role-session-name: github-actions
          aws-region: ${{ env.AWS_REGION }}

      - name: Deploy to S3
        run: |
          aws s3 sync frontend/dist s3://${{ env.PROJECT_NAME }}-${{ env.ENVIRONMENT }}-frontend \
            --delete \
            --cache-control "public, max-age=31536000" \
            --exclude "index.html" \
            --exclude "*.json"
          
          aws s3 cp frontend/dist/index.html s3://${{ env.PROJECT_NAME }}-${{ env.ENVIRONMENT }}-frontend/ \
            --cache-control "public, max-age=300"

      - name: Invalidate CloudFront cache
        run: |
          DISTRIBUTION_ID=$(aws cloudfront list-distributions \
            --query "DistributionList.Items[?Comment=='${{ env.PROJECT_NAME }} frontend distribution'].Id" \
            --output text)
          
          aws cloudfront create-invalidation \
            --distribution-id $DISTRIBUTION_ID \
            --paths "/*"

Terraform適用用ワークフロー

今回はコードとしてサンプルを用意していませんが、以下のようなコードが想定されると思います。
検証外で、terraformによるリソースの変更が頻繁に変わる場合はCI/CD化しておくと便利です。

# .github/workflows/terraform.yml

name: Terraform

on:
  pull_request:
    paths:
      - 'terraform/**'
      - '.github/workflows/terraform.yml'
  push:
    branches:
      - main
    paths:
      - 'terraform/**'

env:
  AWS_REGION: ap-northeast-1
  TF_VERSION: 1.5.7

permissions:
  id-token: write
  contents: read
  pull-requests: write

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

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          role-session-name: github-actions
          aws-region: ${{ env.AWS_REGION }}

      - name: Terraform Init
        working-directory: terraform
        run: terraform init -backend-config=backend.hcl

      - name: Terraform Format Check
        working-directory: terraform
        run: terraform fmt -check

      - name: Terraform Validate
        working-directory: terraform
        run: terraform validate

      - name: Terraform Plan
        id: plan
        working-directory: terraform
        run: terraform plan -no-color -out=tfplan
        continue-on-error: true

      - name: Comment PR
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const output = `#### Terraform Plan 📖
            \`\`\`
            ${{ steps.plan.outputs.stdout }}
            \`\`\`
            `;
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: output
            });

  apply:
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    needs: plan
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          role-session-name: github-actions
          aws-region: ${{ env.AWS_REGION }}

      - name: Terraform Init
        working-directory: terraform
        run: terraform init -backend-config=backend.hcl

      - name: Terraform Apply
        working-directory: terraform
        run: terraform apply -auto-approve

環境変数の設定

# .env 追記

# GitHubのユーザー名またはOrganization名/GitHubリポジトリ名
TF_VAR_github_repository="ORG/repo"

今回環境変数を設定するため、以下のコマンドで再起動。

# exitで抜けた後に
docker-compose run --rm terraform

リソースを作成

Terraformコンテナ内で以下のコマンドを実行します。

# terraformディレクトリへ移動
cd terraform

# 実行計画の確認
terraform plan

# リソースの作成
terraform apply

GitHubリポジトリの設定

Secretsの設定

GitHubリポジトリの Settings > Secrets and variables > Actions で以下を設定します。

# IAMロールのARNを取得
terraform output github_actions_role_arn
Secret名 説明
AWS_ROLE_ARN arn:aws:iam::… GitHub Actions用IAMロールのARN

Variablesの設定

GitHubリポジトリの Settings > Secrets and variables > Actions > Variables で設定します。
これはterraformの環境変数(TF_VAR_project_name)で設定しているやつです。

Variable名 説明
PROJECT_NAME your-project プロジェクト名

ブランチ保護ルール(推奨)

Settings > Branches で main ブランチの保護ルールを設定します。

ルール 設定 理由
Require pull request reviews 有効 直接プッシュを防止
Require status checks 有効 テスト成功を必須化
Include administrators 有効 管理者も例外なし
Allow force pushes 無効 履歴の改変を防止

動作確認

CI/CDのコードからデプロイされたことを確認したら、ディストリビューションドメイン名(https://d2vxxxxxxxx.cloudfront.net/)からアクセスして確認してみましょう。
基本的にはここでCRUDが機能していればOKです。

デプロイの確認

# ECSタスクの更新確認
aws ecs describe-services \
  --cluster $(terraform output -raw ecs_cluster_id) \
  --services $(terraform output -raw ecs_service_name) \
  --query 'services[0].deployments[*].[status,taskDefinition,desiredCount,runningCount]' \
  --output table

# CloudFront無効化の確認
aws cloudfront list-invalidations \
  --distribution-id $(terraform output -raw cloudfront_distribution_id) \
  --query 'InvalidationList.Items[0:3].[Id,Status,CreateTime]' \
  --output table

ビルドアーティファクトの確認

# S3バケット内のアーティファクト確認
aws s3 ls s3://$(terraform output -raw artifacts_bucket_name)/ --recursive | head -10

トラブルシューティング

OIDC認証エラー

エラー 原因 対処方法
Error assuming role IAMロールの信頼関係設定ミス リポジトリ名の確認、sub条件の修正
Invalid OIDC token OIDCプロバイダー未作成 terraform applyでプロバイダー作成
AccessDenied 権限不足 IAMポリシーの見直し

デプロイエラー

エラー 原因 対処方法
Task definition not found タスク定義の名前相違 環境変数PROJECT_NAMEの確認
Service update failed ヘルスチェック失敗 アプリケーションログ確認
ECR push denied ECRリポジトリ権限なし IAMポリシーにECR権限追加

コスト管理

サービス 項目 料金
GitHub Actions 実行時間(プライベートリポジトリ) 2,000分/月まで無料
S3 (アーティファクト) ストレージ $0.025/GB・月
Parameter Store 標準パラメータ 無料
CloudWatch Logs ログ保存 $0.50/GB・月

開発環境では、以下の方法でコストを削減できます。

# terraform.tfvars
artifact_retention_days = 7  # アーティファクトを7日で削除

次のステップ

次回は、本番運用とスケーリングの設定を行います。

  • Terraformコードのモジュール化
  • 環境別設定
  • Auto Scalingの設定
  • RDS Performance Insightsの活用
  • 復旧計画の策定

検索

検索条件に一致する記事が見つかりませんでした