はじめに

コンテナアプリケーションを本番環境で運用する際、デプロイの自動化は開発効率と品質の両面で不可欠な要素です。手動でDockerイメージをビルドし、ECRへプッシュし、ECSサービスを更新するワークフローは、ヒューマンエラーのリスクが高く、リリースサイクルのボトルネックになります。

AWSでは、CodePipelineとCodeBuildを組み合わせることで、Gitリポジトリへのコードプッシュから本番デプロイまでを完全に自動化できます。本記事では、GitHub連携からECSへの自動デプロイまで、実運用に必要なCI/CDパイプラインの構築手順を体系的に解説します。

この記事を読むことで、以下のことが理解できるようになります。

  • コンテナCI/CDパイプラインの全体アーキテクチャと各サービスの役割
  • CodePipelineによるパイプラインの設計と構築手順
  • CodeBuildでのDockerイメージビルドとECRプッシュの設定
  • ECSサービスへの自動ローリングデプロイの実装
  • パイプラインのセキュリティと運用のベストプラクティス

コンテナCI/CDパイプラインの全体像

パイプラインアーキテクチャ

AWSでコンテナCI/CDパイプラインを構築する場合、以下のサービスが連携して動作します。

flowchart LR
    subgraph Source["ソース管理"]
        GitHub["GitHub"]
        CodeCommit["CodeCommit"]
    end
    
    subgraph Build["ビルドステージ"]
        CodeBuild["AWS CodeBuild"]
    end
    
    subgraph Registry["レジストリ"]
        ECR["Amazon ECR"]
    end
    
    subgraph Deploy["デプロイステージ"]
        ECS["Amazon ECS"]
    end
    
    subgraph Orchestration["オーケストレーション"]
        CodePipeline["AWS CodePipeline"]
    end
    
    GitHub --> CodePipeline
    CodeCommit --> CodePipeline
    CodePipeline --> CodeBuild
    CodeBuild --> ECR
    CodeBuild --> CodePipeline
    CodePipeline --> ECS

各サービスの役割

サービス 役割 主な機能
CodePipeline パイプライン管理 ステージ間のワークフロー制御、トリガー管理
CodeBuild ビルド実行 Dockerイメージのビルド、テスト実行、ECRプッシュ
ECR イメージ保存 コンテナイメージのバージョン管理、脆弱性スキャン
ECS アプリケーション実行 コンテナのデプロイ、ローリングアップデート

CI/CDパイプラインのフロー

具体的なパイプラインの処理フローは以下のようになります。

sequenceDiagram
    participant Dev as 開発者
    participant Git as GitHub
    participant CP as CodePipeline
    participant CB as CodeBuild
    participant ECR as Amazon ECR
    participant ECS as Amazon ECS
    
    Dev->>Git: git push
    Git->>CP: Webhook通知
    CP->>CP: Sourceステージ開始
    CP->>CB: Buildステージ開始
    CB->>CB: ソースコード取得
    CB->>CB: docker build
    CB->>CB: docker tag
    CB->>ECR: docker push
    CB->>CP: ビルド成功通知
    CP->>ECS: Deployステージ開始
    ECS->>ECS: 新タスク起動
    ECS->>ECS: ヘルスチェック
    ECS->>ECS: 旧タスク停止
    ECS->>CP: デプロイ完了通知

前提条件と準備

必要なリソース

CI/CDパイプラインを構築する前に、以下のリソースが準備されている必要があります。

リソース 用途 事前準備
ECRリポジトリ Dockerイメージの保存先 リポジトリ作成済み
ECSクラスター コンテナ実行環境 クラスター作成済み
ECSサービス アプリケーションの実行管理 サービス作成済み
GitHubリポジトリ ソースコード管理 リポジトリ作成済み
IAMロール 各サービスの権限管理 本記事で作成

プロジェクト構成

サンプルプロジェクトの構成は以下のとおりです。

my-container-app/
├── Dockerfile
├── buildspec.yml          # CodeBuild設定
├── taskdef.json           # ECSタスク定義テンプレート
├── appspec.yaml           # CodeDeploy設定(オプション)
├── src/
│   └── index.js
└── package.json

サンプルDockerfile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY src/ ./src/

EXPOSE 3000

USER node

CMD ["node", "src/index.js"]

CodeBuildプロジェクトの設定

buildspec.ymlの作成

CodeBuildの動作を定義するbuildspec.ymlを作成します。このファイルでは、Dockerイメージのビルド、タグ付け、ECRへのプッシュを行います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
version: 0.2

env:
  variables:
    AWS_DEFAULT_REGION: ap-northeast-1
    IMAGE_REPO_NAME: my-container-app
  parameter-store:
    DOCKERHUB_USER: /codebuild/dockerhub-user
    DOCKERHUB_TOKEN: /codebuild/dockerhub-token

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
      - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
      # Docker Hub認証(レートリミット対策)
      - echo $DOCKERHUB_TOKEN | docker login --username $DOCKERHUB_USER --password-stdin
  
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t $REPOSITORY_URI:latest .
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
  
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo Writing image definitions file...
      - printf '[{"name":"app","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json

artifacts:
  files:
    - imagedefinitions.json

buildspec.ymlの各セクション解説

セクション 役割 主な処理
env.variables 環境変数定義 リージョン、リポジトリ名の設定
env.parameter-store Secrets取得 Docker Hub認証情報の取得
pre_build ビルド前処理 ECR/Docker Hub認証、変数設定
build ビルド処理 Dockerイメージのビルドとタグ付け
post_build ビルド後処理 ECRプッシュ、成果物ファイル生成
artifacts 成果物定義 次ステージへ渡すファイルの指定

imagedefinitions.jsonの重要性

imagedefinitions.jsonは、CodePipelineのECSデプロイアクションが参照するファイルです。このファイルにより、デプロイ時に使用するイメージURIが決定されます。

1
2
3
4
5
6
[
  {
    "name": "app",
    "imageUri": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-container-app:abc1234"
  }
]

nameフィールドは、ECSタスク定義内のコンテナ名と一致させる必要があります。

IAMロールの設定

CodeBuild用IAMロール

CodeBuildがECRへイメージをプッシュするために必要なIAMロールを作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage",
        "ecr:PutImage",
        "ecr:InitiateLayerUpload",
        "ecr:UploadLayerPart",
        "ecr:CompleteLayerUpload"
      ],
      "Resource": "arn:aws:ecr:ap-northeast-1:123456789012:repository/my-container-app"
    },
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/codebuild/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameters"
      ],
      "Resource": "arn:aws:ssm:ap-northeast-1:123456789012:parameter/codebuild/*"
    }
  ]
}

信頼ポリシーは以下のように設定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "codebuild.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

CodePipeline用IAMロール

CodePipelineには、各ステージのサービスを操作する権限が必要です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "codebuild:BatchGetBuilds",
        "codebuild:StartBuild"
      ],
      "Resource": "arn:aws:codebuild:ap-northeast-1:123456789012:project/my-container-build"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecs:DescribeServices",
        "ecs:DescribeTaskDefinition",
        "ecs:DescribeTasks",
        "ecs:ListTasks",
        "ecs:RegisterTaskDefinition",
        "ecs:UpdateService"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "iam:PassRole"
      ],
      "Resource": [
        "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
        "arn:aws:iam::123456789012:role/ecsTaskRole"
      ],
      "Condition": {
        "StringEquals": {
          "iam:PassedToService": "ecs-tasks.amazonaws.com"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::codepipeline-ap-northeast-1-*/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "codestar-connections:UseConnection"
      ],
      "Resource": "arn:aws:codestar-connections:ap-northeast-1:123456789012:connection/*"
    }
  ]
}

CodePipelineの構築

パイプラインの全体構成

CodePipelineは3つのステージで構成します。

flowchart TB
    subgraph Pipeline["CodePipeline"]
        subgraph Source["Sourceステージ"]
            S1["GitHub Connection"]
            S2["ソースコード取得"]
        end
        
        subgraph Build["Buildステージ"]
            B1["CodeBuild起動"]
            B2["Docker Build"]
            B3["ECR Push"]
            B4["artifacts生成"]
        end
        
        subgraph Deploy["Deployステージ"]
            D1["imagedefinitions.json読込"]
            D2["タスク定義更新"]
            D3["ECSサービス更新"]
        end
    end
    
    Source --> Build
    Build --> Deploy

AWS CLIによるパイプライン作成

以下のJSONファイルを使用してパイプラインを作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
{
  "pipeline": {
    "name": "my-container-pipeline",
    "roleArn": "arn:aws:iam::123456789012:role/CodePipelineRole",
    "artifactStore": {
      "type": "S3",
      "location": "codepipeline-ap-northeast-1-123456789012"
    },
    "stages": [
      {
        "name": "Source",
        "actions": [
          {
            "name": "Source",
            "actionTypeId": {
              "category": "Source",
              "owner": "AWS",
              "provider": "CodeStarSourceConnection",
              "version": "1"
            },
            "configuration": {
              "ConnectionArn": "arn:aws:codestar-connections:ap-northeast-1:123456789012:connection/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
              "FullRepositoryId": "your-org/my-container-app",
              "BranchName": "main",
              "OutputArtifactFormat": "CODE_ZIP"
            },
            "outputArtifacts": [
              {
                "name": "SourceOutput"
              }
            ],
            "runOrder": 1
          }
        ]
      },
      {
        "name": "Build",
        "actions": [
          {
            "name": "Build",
            "actionTypeId": {
              "category": "Build",
              "owner": "AWS",
              "provider": "CodeBuild",
              "version": "1"
            },
            "configuration": {
              "ProjectName": "my-container-build",
              "EnvironmentVariables": "[{\"name\":\"AWS_ACCOUNT_ID\",\"value\":\"123456789012\",\"type\":\"PLAINTEXT\"}]"
            },
            "inputArtifacts": [
              {
                "name": "SourceOutput"
              }
            ],
            "outputArtifacts": [
              {
                "name": "BuildOutput"
              }
            ],
            "runOrder": 1
          }
        ]
      },
      {
        "name": "Deploy",
        "actions": [
          {
            "name": "Deploy",
            "actionTypeId": {
              "category": "Deploy",
              "owner": "AWS",
              "provider": "ECS",
              "version": "1"
            },
            "configuration": {
              "ClusterName": "my-cluster",
              "ServiceName": "my-service",
              "FileName": "imagedefinitions.json"
            },
            "inputArtifacts": [
              {
                "name": "BuildOutput"
              }
            ],
            "runOrder": 1
          }
        ]
      }
    ]
  }
}

パイプラインを作成するコマンドは以下のとおりです。

1
aws codepipeline create-pipeline --cli-input-json file://pipeline.json

GitHub接続の設定

CodePipelineとGitHubを連携するには、AWS CodeStar Connectionsを使用します。

1
2
3
4
5
6
7
# 接続の作成
aws codestar-connections create-connection \
  --provider-type GitHub \
  --connection-name my-github-connection

# 作成された接続はPENDING状態
# AWSコンソールで接続を承認する必要がある

接続の承認手順は以下のとおりです。

  1. AWS マネジメントコンソールで「Developer Tools」から「Connections」を選択
  2. 作成した接続を選択し、「Update pending connection」をクリック
  3. GitHubの認証画面でアクセスを許可
  4. 接続ステータスが「Available」になることを確認

CodeBuildプロジェクトの作成

プロジェクト設定

CodeBuildプロジェクトをAWS CLIで作成します。

1
2
3
4
5
6
aws codebuild create-project \
  --name my-container-build \
  --source type=CODEPIPELINE \
  --artifacts type=CODEPIPELINE \
  --environment type=LINUX_CONTAINER,computeType=BUILD_GENERAL1_SMALL,image=aws/codebuild/amazonlinux2-x86_64-standard:5.0,privilegedMode=true \
  --service-role arn:aws:iam::123456789012:role/CodeBuildRole

環境設定のポイント

設定項目 推奨値 理由
privilegedMode true Docker daemonを使用するため必須
computeType BUILD_GENERAL1_SMALL 一般的なビルドには十分
image amazonlinux2-x86_64-standard:5.0 Docker対応の最新イメージ
buildTimeout 30分 イメージサイズに応じて調整

キャッシュの活用

ビルド時間を短縮するために、Dockerレイヤーキャッシュを活用できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
      # キャッシュ用にlatestイメージをプル
      - docker pull $REPOSITORY_URI:latest || true
  
  build:
    commands:
      # キャッシュを利用したビルド
      - docker build --cache-from $REPOSITORY_URI:latest -t $REPOSITORY_URI:latest .

cache:
  paths:
    - '/root/.docker/**/*'

ECSサービスへの自動デプロイ

デプロイの仕組み

CodePipelineのECSデプロイアクションは、以下の処理を自動的に実行します。

flowchart TB
    subgraph DeployProcess["ECSデプロイプロセス"]
        A["imagedefinitions.json読込"]
        B["新タスク定義リビジョン作成"]
        C["ECSサービス更新"]
        D["新タスク起動"]
        E["ヘルスチェック待機"]
        F["旧タスクドレイン"]
        G["旧タスク停止"]
        H["デプロイ完了"]
    end
    
    A --> B
    B --> C
    C --> D
    D --> E
    E -->|成功| F
    E -->|失敗| Rollback["ロールバック"]
    F --> G
    G --> H

デプロイ設定の最適化

ECSサービスのデプロイ設定により、ローリングデプロイの挙動を制御できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "deploymentConfiguration": {
    "deploymentCircuitBreaker": {
      "enable": true,
      "rollback": true
    },
    "maximumPercent": 200,
    "minimumHealthyPercent": 100
  }
}
パラメータ 設定値 説明
maximumPercent 200 デプロイ中のタスク最大数(希望数の200%)
minimumHealthyPercent 100 最低限稼働させるタスク数(希望数の100%)
deploymentCircuitBreaker enable: true デプロイ失敗時の自動ロールバック有効化

ヘルスチェックの設定

ALBのヘルスチェック設定は、デプロイの成否判定に直結します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "healthCheck": {
    "path": "/health",
    "protocol": "HTTP",
    "interval": 30,
    "timeout": 5,
    "healthyThreshold": 2,
    "unhealthyThreshold": 3
  }
}

アプリケーション側でヘルスチェックエンドポイントを実装します。

1
2
3
4
5
// Express.jsの例
app.get('/health', (req, res) => {
  // 依存サービスのチェックを含めることも可能
  res.status(200).json({ status: 'healthy' });
});

Blue/Greenデプロイの実装

Blue/Greenデプロイの概要

より安全なデプロイを実現するために、CodeDeployを使用したBlue/Greenデプロイも選択できます。

flowchart TB
    subgraph BlueGreen["Blue/Greenデプロイ"]
        subgraph Blue["Blue環境(現行)"]
            B1["タスクセット"]
            B2["ターゲットグループ"]
        end
        
        subgraph Green["Green環境(新規)"]
            G1["新タスクセット"]
            G2["新ターゲットグループ"]
        end
        
        ALB["Application Load Balancer"]
        
        ALB -->|本番トラフィック| B2
        ALB -.->|テストトラフィック| G2
    end

appspec.yamlの作成

Blue/Greenデプロイにはappspec.yamlが必要です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: <TASK_DEFINITION>
        LoadBalancerInfo:
          ContainerName: "app"
          ContainerPort: 3000
        PlatformVersion: "LATEST"

CodeDeployの設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{
  "applicationName": "my-ecs-app",
  "deploymentGroupName": "my-ecs-dg",
  "deploymentConfigName": "CodeDeployDefault.ECSLinear10PercentEvery1Minutes",
  "ecsServices": [
    {
      "serviceName": "my-service",
      "clusterName": "my-cluster"
    }
  ],
  "loadBalancerInfo": {
    "targetGroupPairInfoList": [
      {
        "targetGroups": [
          {
            "name": "my-tg-blue"
          },
          {
            "name": "my-tg-green"
          }
        ],
        "prodTrafficRoute": {
          "listenerArns": [
            "arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:listener/app/my-alb/xxx/yyy"
          ]
        }
      }
    ]
  },
  "blueGreenDeploymentConfiguration": {
    "terminateBlueInstancesOnDeploymentSuccess": {
      "action": "TERMINATE",
      "terminationWaitTimeInMinutes": 5
    },
    "deploymentReadyOption": {
      "actionOnTimeout": "CONTINUE_DEPLOYMENT",
      "waitTimeInMinutes": 0
    }
  }
}

パイプラインの監視とトラブルシューティング

CloudWatchによる監視

パイプラインの実行状況をCloudWatchで監視します。

flowchart LR
    subgraph Pipeline["CodePipeline"]
        Stage1["Source"]
        Stage2["Build"]
        Stage3["Deploy"]
    end
    
    subgraph Monitoring["監視"]
        CW["CloudWatch Metrics"]
        Alarm["CloudWatch Alarms"]
        SNS["Amazon SNS"]
    end
    
    Pipeline --> CW
    CW --> Alarm
    Alarm --> SNS
    SNS --> Email["Email通知"]
    SNS --> Slack["Slack通知"]

主要なメトリクス

メトリクス 説明 アラーム閾値例
ActionExecution アクション実行状態 Failed状態の検知
StageExecution ステージ実行状態 Failed状態の検知
PipelineExecution パイプライン全体の実行状態 実行時間の異常

EventBridgeによる通知設定

パイプラインの状態変化をEventBridgeでキャプチャし、通知を送信します。

1
2
3
4
5
6
7
{
  "source": ["aws.codepipeline"],
  "detail-type": ["CodePipeline Pipeline Execution State Change"],
  "detail": {
    "state": ["FAILED", "SUCCEEDED"]
  }
}

Lambda関数でSlack通知を実装する例は以下のとおりです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import json
import urllib3
import os

http = urllib3.PoolManager()

def lambda_handler(event, context):
    webhook_url = os.environ['SLACK_WEBHOOK_URL']
    
    pipeline_name = event['detail']['pipeline']
    state = event['detail']['state']
    execution_id = event['detail']['execution-id']
    
    color = '#36a64f' if state == 'SUCCEEDED' else '#ff0000'
    
    message = {
        'attachments': [
            {
                'color': color,
                'title': f'Pipeline: {pipeline_name}',
                'fields': [
                    {'title': 'State', 'value': state, 'short': True},
                    {'title': 'Execution ID', 'value': execution_id, 'short': True}
                ]
            }
        ]
    }
    
    http.request(
        'POST',
        webhook_url,
        body=json.dumps(message),
        headers={'Content-Type': 'application/json'}
    )
    
    return {'statusCode': 200}

よくあるエラーと対処法

エラー 原因 対処法
ECR認証エラー IAMロール権限不足 ecr:GetAuthorizationToken権限を追加
Docker pushエラー リポジトリ権限不足 ECRリポジトリポリシーを確認
ECSデプロイタイムアウト ヘルスチェック失敗 ヘルスチェック設定とアプリケーションを確認
タスク起動失敗 イメージ取得エラー タスク実行ロールのECR権限を確認

CodeBuildログの確認

ビルド失敗時は、CloudWatch Logsで詳細を確認します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 最新のビルドログを取得
aws logs get-log-events \
  --log-group-name /aws/codebuild/my-container-build \
  --log-stream-name $(aws logs describe-log-streams \
    --log-group-name /aws/codebuild/my-container-build \
    --order-by LastEventTime \
    --descending \
    --limit 1 \
    --query 'logStreams[0].logStreamName' \
    --output text)

セキュリティのベストプラクティス

最小権限の原則

IAMロールには必要最小限の権限のみを付与します。

flowchart TB
    subgraph Principals["プリンシパル"]
        CodePipeline["CodePipeline"]
        CodeBuild["CodeBuild"]
        ECS["ECS Task"]
    end
    
    subgraph Roles["IAMロール"]
        CPRole["CodePipelineRole"]
        CBRole["CodeBuildRole"]
        TaskExecRole["TaskExecutionRole"]
        TaskRole["TaskRole"]
    end
    
    subgraph Resources["リソース"]
        S3["S3 Artifacts"]
        ECR["ECR"]
        Logs["CloudWatch Logs"]
        SSM["Parameter Store"]
    end
    
    CodePipeline --> CPRole
    CodeBuild --> CBRole
    ECS --> TaskExecRole
    ECS --> TaskRole
    
    CPRole --> S3
    CBRole --> ECR
    CBRole --> Logs
    CBRole --> SSM
    TaskExecRole --> ECR
    TaskExecRole --> Logs

シークレット管理

認証情報はParameter StoreまたはSecrets Managerで管理します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Parameter Storeにシークレットを保存
aws ssm put-parameter \
  --name /codebuild/dockerhub-token \
  --type SecureString \
  --value "your-dockerhub-token"

# buildspec.ymlで参照
# env:
#   parameter-store:
#     DOCKERHUB_TOKEN: /codebuild/dockerhub-token

イメージスキャンの有効化

ECRの脆弱性スキャンをビルドプロセスに組み込みます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# buildspec.ymlに追加
phases:
  post_build:
    commands:
      - echo Initiating vulnerability scan...
      - aws ecr start-image-scan --repository-name $IMAGE_REPO_NAME --image-id imageTag=$IMAGE_TAG
      - echo Waiting for scan results...
      - aws ecr wait image-scan-complete --repository-name $IMAGE_REPO_NAME --image-id imageTag=$IMAGE_TAG
      - |
        SCAN_FINDINGS=$(aws ecr describe-image-scan-findings \
          --repository-name $IMAGE_REPO_NAME \
          --image-id imageTag=$IMAGE_TAG \
          --query 'imageScanFindings.findingSeverityCounts' \
          --output json)
        echo "Scan findings: $SCAN_FINDINGS"
        # CRITICALまたはHIGHがあればビルドを失敗させる
        CRITICAL=$(echo $SCAN_FINDINGS | jq '.CRITICAL // 0')
        HIGH=$(echo $SCAN_FINDINGS | jq '.HIGH // 0')
        if [ "$CRITICAL" -gt 0 ] || [ "$HIGH" -gt 0 ]; then
          echo "Critical or High vulnerabilities found!"
          exit 1
        fi

まとめ

本記事では、AWSでコンテナCI/CDパイプラインを構築する方法を解説しました。主なポイントは以下のとおりです。

項目 要点
パイプライン構成 CodePipeline + CodeBuild + ECR + ECSの連携
ビルド設定 buildspec.ymlでDocker build/push/artifacts生成を定義
デプロイ方式 ローリングデプロイまたはBlue/Greenデプロイを選択
権限管理 最小権限のIAMロールを各サービスに設定
監視 CloudWatch + EventBridgeで状態監視と通知を実装
セキュリティ シークレット管理とイメージスキャンの組み込み

CI/CDパイプラインの構築により、コードの変更から本番デプロイまでを自動化し、開発チームの生産性とリリース品質を向上させることができます。まずはシンプルなローリングデプロイから始め、運用が安定したらBlue/Greenデプロイへの移行を検討してください。

参考リンク