GitHub Actions × ecspresso × CDK で ECS Fargate デプロイを高速化 - デプロイ時間 5分→3分の改善

GitHub Actions × ecspresso × CDK で ECS Fargate デプロイを高速化 - デプロイ時間 5分→3分の改善

2026.02.11

製造ビジネステクノロジー部の小林です。

以前、GitHub Actions と ECS Fargate を組み合わせた CI/CD パイプラインを構築しましたが、今回は GitHub Actions から ecspresso を利用して ECS Fargate をデプロイする方法をご紹介します。

ecspresso とは?

ecspresso(読み方:エスプレッソ)は、Amazon ECS 専用のデプロイツールです。株式会社カヤックの fujiwara 氏が開発・公開している OSS で、GitHub スター数は 1,000 超。クラスメソッドの 2024 年 OSS 支援対象にも選ばれています。

ECS の「タスク定義の登録」「サービスの更新」「ロールバック」といったデプロイ操作を JSON/YAML の設定ファイルとシンプルな CLI コマンドだけで完結させるツールです。

詳細については以下をご覧ください。
https://speakerdeck.com/fujiwara3/yapc-fukuoka-2025

主な特徴

  • タスク定義とサービス定義を JSON ファイルで管理
  • デプロイ前に差分を確認できる(ecspresso diff
  • ロールバックが簡単(ecspresso rollback
  • 強力なテンプレート機能(環境変数・SSM パラメータストア参照)
  • GitHub Actions との統合が簡単

ecspresso の公式リポジトリはこちら。
https://github.com/kayac/ecspresso

ライフサイクルに合わせた最適なツール選択

AWS CDK はインフラをコードで定義・管理できるツールです。ただ、ECS のデプロイにおいては、ecspresso と組み合わせることでさらに開発体験が向上します。

ライフサイクルの違い

インフラとアプリケーションでは、変更頻度が異なります。この特性に合わせてツールを使い分けることで、それぞれの強みを活用できます。

※ 開発初期はインフラも頻繁に変更されますが、構成が固まると変更頻度は下がります。

CDK と ecspresso を比べてみる

観点 CDK (CloudFormation 経由) ecspresso (ECS API 直接)
管理範囲 スタック内の全リソース タスク定義 + サービス
ロールバック CFn スタックロールバック(インフラ全体を戻す) ecspresso rollback(アプリのみ戻す)
差分プレビュー cdk diff(全リソースの状態を確認) ecspresso diff(タスク定義の変更に集中)
得意領域 インフラ全体の構築・管理 ECS アプリの高速デプロイ

CDK + ecspresso の役割分担

CDK と ecspresso はそれぞれの強みを活かし合う補完関係です。「インフラの構築は CDK、アプリのデプロイは ecspresso」と役割を分けることで、両方のメリットを享受できます。
スクリーンショット 2026-02-11 17.18.26

結局 ecspresso を使うと何が嬉しいの?

一言で言えば、『特定の構成下では ECS デプロイが高速化する可能性がある』です。本記事の検証環境では、デプロイ時間が 5 分から 3 分に短縮されました。

  1. デプロイが速い
    ecspresso は ECS API を直接叩いてデプロイを行うため、CloudFormation を経由する CDK と比較して、オーバーヘッドが少なく高速です。

  2. 「インフラ」と「アプリ」の責務を分離できる
    インフラは CDK で、頻繁に更新するアプリの設定は ecspresso で、といった使い分けが可能です。デプロイのたびに重たい IaC ツールを実行するストレスから解放されます。

  3. 実数値としての効果(検証結果)
    本記事の構成で検証したところ、デプロイ時間が 5 分から 3 分に短縮されました。

実際に GitHub Actions から ecspresso を使用して ECS をデプロイしてみる

ここからは、実際に ecspresso を使って ECS をデプロイする方法を見ていきます。

前提条件

  • インフラ構築は AWS CDK を使用(VPC、ALB、ECS クラスター等)
  • ECS サービス・タスク定義 のみ ecspresso で管理
  • GitHub Actions から ecspresso でデプロイする
  • ECS、Docker イメージでは ARM64 を使用

本記事では ecspresso の使い方に焦点を当てるため、CDK によるインフラ構築の詳細は割愛します。

使用する技術スタック

技術 用途
AWS CDK (TypeScript) インフラのコード化
ecspresso ECS デプロイ管理
ECS Fargate コンテナ実行環境
ECR コンテナイメージの保管
ALB ロードバランサー
GitHub Actions CI/CD
OIDC AWS 認証

プロジェクト構成

今回のポイントは ecspresso/ ディレクトリ と .github/workflows/deploy.yml です。

.
├── .github/
│   └── workflows/
│       └── deploy.yml         # GitHub Actions ワークフロー
├── backend/
│   ├── src/
│   │   └── index.ts           # Express アプリケーション
│   ├── Dockerfile
│   └── package.json
├── ecspresso/
│   ├── ecspresso.yml          # ecspresso 設定
│   ├── ecs-service-def.json   # ECS サービス定義
│   └── ecs-task-def.json      # ECS タスク定義
├── lib/
│   ├── constructs/            # CDK Constructs
│   └── stack.ts               # CDK スタック
└── bin/
    └── app.ts                 # CDK エントリーポイント

CDK で準備するリソース

ecspresso を使う前に、CDK で以下のリソースを準備しておきます。

  • ECS クラスター - コンテナを実行する基盤
  • IAM ロール - タスク実行ロール、タスクロール
  • CloudWatch Logs グループ - コンテナログの保存先
  • VPC、ALB、ECR など - ネットワークとイメージ保管

ポイント

タスク定義とサービスは CDK では作成しません。これらは ecspresso で管理します。
CDK が作成したリソース情報(IAM ロール ARN、サブネット ID など)は SSM パラメータストアに保存し、ecspresso から参照します。

ECS クラスターと関連リソースの作成

import * as ecs from "aws-cdk-lib/aws-ecs";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as iam from "aws-cdk-lib/aws-iam";
import * as logs from "aws-cdk-lib/aws-logs";
import { Construct } from "constructs";

export interface EcsConstructProps {
  readonly vpc: ec2.IVpc;
}

/**
 * ECS Construct
 * ECSクラスター、IAMロール、CloudWatch Logsグループを作成する
 * タスク定義とサービスは ecspresso で管理するため、ここでは作成しない
 */
export class EcsConstruct extends Construct {
  public readonly cluster: ecs.Cluster;
  public readonly taskExecutionRole: iam.Role;
  public readonly taskRole: iam.Role;
  public readonly logGroup: logs.LogGroup;

  constructor(scope: Construct, id: string, props: EcsConstructProps) {
    super(scope, id);

    /**
     * ECSクラスターの作成
     * コンテナを実行する論理的なグループ
     */
    const cluster = new ecs.Cluster(this, "Cluster", {
      vpc: props.vpc,
      clusterName: "ecs-cluster",
      containerInsightsV2: ecs.ContainerInsights.ENABLED,
    });
    this.cluster = cluster;

    /**
     * タスク実行ロール
     * ECSがタスクを起動する際に使用するロール
     * - ECRからコンテナイメージをpull
     * - CloudWatch Logsへログを書き込み
     */
    const taskExecutionRole = new iam.Role(this, "TaskExecutionRole", {
      roleName: "ecsTaskExecutionRole",
      assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName(
          "service-role/AmazonECSTaskExecutionRolePolicy"
        ),
      ],
    });
    this.taskExecutionRole = taskExecutionRole;

    /**
     * タスクロール
     * コンテナ内のアプリケーションが使用するロール
     */
    const taskRole = new iam.Role(this, "TaskRole", {
      roleName: "ecsTaskRole",
      assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
    });
    this.taskRole = taskRole;

    /**
     * CloudWatch Logsグループ: コンテナのログを保存(7日間保持)
     */
    const logGroup = new logs.LogGroup(this, "LogGroup", {
      logGroupName: "/ecs/backend",
      retention: logs.RetentionDays.ONE_WEEK,
    });
    this.logGroup = logGroup;
  }
}

SSM パラメータストアへの保存

CDK が作成したリソース情報を SSM パラメータストアに保存します。これにより、ecspresso から動的に参照できるようになります。

パラメータ名 内容
/ecs-app/task-execution-role-arn タスク実行ロール ARN
/ecs-app/task-role-arn タスクロール ARN
/ecs-app/subnet-id-0 プライベートサブネット ID 0
/ecs-app/subnet-id-1 プライベートサブネット ID 1
/ecs-app/security-group-id ECS セキュリティグループ ID
/ecs-app/target-group-arn ALB ターゲットグループ ARN

なぜ SSM パラメータストアを使うのか?
CDK と ecspresso を組み合わせる際、以下の課題があります。

  • CDK はリソース名を自動生成することが多い。(例: EcsStack-TaskRole-ABC123DEF456)
  • IAM ロール ARN、サブネット ID などは CDK デプロイ後に確定する。
  • これらの値を ecspresso の設定ファイルにハードコードすると、インフラ変更のたびに手動更新が必要。

解決策 → SSM パラメータストアを「橋渡し」として使う。

CDK(インフラ構築)
  ↓ リソース作成
  ↓ ARN・IDを取得
  ↓ SSMに保存
SSM パラメータストア(データの橋渡し)
  ↓ デプロイ時に参照
ecspresso(アプリデプロイ)

この仕組みにより、CDK と ecspresso を疎結合に保ち、必要な情報を自動的に連携できます。

/**
 * SSMパラメータストアにリソース情報を保存(ecspressoから参照)
 **/
new ssm.StringParameter(this, "TaskExecutionRoleArnParam", {
  parameterName: "/ecs-app/task-execution-role-arn",
  stringValue: ecsCluster.taskExecutionRole.roleArn,
  description: "ECS Task Execution Role ARN",
});

new ssm.StringParameter(this, "TaskRoleArnParam", {
  parameterName: "/ecs-app/task-role-arn",
  stringValue: ecsCluster.taskRole.roleArn,
  description: "ECS Task Role ARN",
});

new ssm.StringParameter(this, "SubnetIdsParam", {
  parameterName: "/ecs-app/subnet-ids",
  stringValue: network.vpc.privateSubnets.map((s) => s.subnetId).join(","),
  description: "Private Subnet IDs",
});

new ssm.StringParameter(this, "SecurityGroupIdParam", {
  parameterName: "/ecs-app/security-group-id",
  stringValue: network.ecsSecurityGroup.securityGroupId,
  description: "ECS Security Group ID",
});

new ssm.StringParameter(this, "TargetGroupArnParam", {
  parameterName: "/ecs-app/target-group-arn",
  stringValue: alb.backendTargetGroup.targetGroupArn,
  description: "ALB Target Group ARN",
});

CDK のデプロイ

CDK で定義したインフラをデプロイします。このステップで、ECS クラスター、IAM ロール、ネットワークリソースなどが作成されます。

# インフラをデプロイ
npx cdk deploy

デプロイ後、ECS クラスターと関連リソースが作成され、SSM パラメータストアにリソース情報が保存されます。
スクリーンショット 2026-02-11 12.13.28
スクリーンショット 2026-02-11 16.56.04

この時点で、ECS クラスターは作成されていますが、タスク定義とサービスはまだ作成されていません。これらは次のステップで ecspresso を使って作成します。

ecspresso のセットアップ

ここからは、ecspresso を使って ECS のタスク定義とサービスを管理する設定を行います。
CDK で作成したインフラの上に、ecspresso でアプリケーションのデプロイ設定を構築していきます。

ecspresso 設定ファイルの作成

ecspresso の基本設定ファイル ecspresso/ecspresso.yml を作成します。このファイルは、ecspresso がどの ECS クラスターとサービスを管理するかを定義します。

ecspresso/ecspresso.yml
region: ap-northeast-1
cluster: ecs-cluster
service: backend-service
task_definition: ecs-task-def.json
service_definition: ecs-service-def.json
timeout: 10m

各項目の説明:

項目 説明
region AWS リージョン
cluster ECS クラスター名(CDK で作成したクラスター名と一致させる)
service ECS サービス名
task_definition タスク定義ファイルのパス
service_definition サービス定義ファイルのパス
timeout デプロイのタイムアウト時間

参考: ecspresso Configuration file

タスク定義ファイルの作成

ECS タスク定義を JSON ファイルで定義します。タスク定義は、コンテナをどのように実行するかを定義するもので、以下の情報を含みます。

  • 使用するコンテナイメージ
  • CPU・メモリの割り当て
  • IAM ロール
  • ログ設定
  • 環境変数
{
  "family": "backend-task",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "runtimePlatform": {
    "cpuArchitecture": "ARM64",
    "operatingSystemFamily": "LINUX"
  },
  "executionRoleArn": "{{ ssm `/ecs-app/task-execution-role-arn` }}",
  "taskRoleArn": "{{ ssm `/ecs-app/task-role-arn` }}",
  "containerDefinitions": [
    {
      "name": "backend",
      "image": "{{ must_env `ECR_REGISTRY` }}/{{ must_env `ECR_REPOSITORY` }}:{{ must_env `IMAGE_TAG` }}",
      "portMappings": [
        {
          "containerPort": 4000,
          "protocol": "tcp"
        }
      ],
      "essential": true,
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/backend",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "backend"
        }
      },
      "environment": [
        {
          "name": "NODE_ENV",
          "value": "production"
        },
        {
          "name": "PORT",
          "value": "4000"
        }
      ]
    }
  ]
}

各項目の説明:

項目 説明
family タスク定義ファミリー名(リビジョン管理の単位)
networkMode awsvpcモード(各タスクに ENI を割り当て)
requiresCompatibilities Fargate で実行
cpu タスクに割り当てる CPU(256)
memory タスクに割り当てるメモリ(512MB)
runtimePlatform.cpuArchitecture ARM64 アーキテクチャ
executionRoleArn タスク実行ロール(ECR から pull、ログ書き込み)
taskRoleArn タスクロール(アプリケーションが使用)
containerDefinitions コンテナの設定

CDK との連携

CDK が作成するリソース(IAM ロール ARN、サブネット ID 等)を SSM パラメータストアに保存し、ecspresso の設定ファイルから{{ ssm "/path" }}で動的に参照します。

これにより、インフラ変更時に設定ファイルを更新する手間が不要になります。

サービス定義ファイルの作成

ECS サービス定義を JSON ファイルで定義します。サービス定義は、タスクをどのように運用するかを定義するもので、以下の情報を含みます。

  • 起動するタスクの数
  • ネットワーク設定(VPC、サブネット、セキュリティグループ)
  • ロードバランサーとの連携
  • デプロイ戦略
  • ヘルスチェック設定
ecspresso/ecs-service-def.json
{
  "serviceName": "backend-service",
  "desiredCount": 2,
  "launchType": "FARGATE",
  "networkConfiguration": {
    "awsvpcConfiguration": {
      "subnets": {{ ssm `/ecs-app/subnet-ids` | fromJSON }},
      "securityGroups": ["{{ ssm `/ecs-app/security-group-id` }}"],
      "assignPublicIp": "DISABLED"
    }
  },
  "loadBalancers": [
    {
      "targetGroupArn": "{{ ssm `/ecs-app/target-group-arn` }}",
      "containerName": "backend",
      "containerPort": 4000
    }
  ],
  "healthCheckGracePeriodSeconds": 60,
  "deploymentConfiguration": {
    "maximumPercent": 200,
    "minimumHealthyPercent": 100,
    "deploymentCircuitBreaker": {
      "enable": true,
      "rollback": true
    }
  },
  "enableExecuteCommand": true
}

各項目の説明:

項目 説明
serviceName ECS サービス名
desiredCount 起動するタスク数
launchType Fargate を使用
networkConfiguration VPC 設定(プライベートサブネットに配置)
loadBalancers ALB との連携設定
healthCheckGracePeriodSeconds ヘルスチェック開始までの猶予時間
deploymentConfiguration.maximumPercent デプロイ時の最大タスク数(200% = 2 倍まで起動可)
deploymentConfiguration.minimumHealthyPercent デプロイ時の最小ヘルシータスク数(100% = 常に維持)
deploymentCircuitBreaker デプロイ失敗時の自動ロールバック
enableExecuteCommand ECS Exec でコンテナに接続可能にする

GitHub Actions の設定

GitHub Actions から ecspresso を使ってデプロイする設定を行います。

ワークフローファイルの作成

.github/workflows/deploy.yml を作成します。
このワークフローは、変更されたファイルに応じて CDK と ecspresso を条件分岐します。step-security/changed-files を使って、インフラ変更時のみ CDK を実行し、アプリ変更時のみ ecspresso を実行します。
https://github.com/step-security/changed-files

name: Deploy Infrastructure and Application

on: workflow_dispatch

# OIDC認証に必要な権限
permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: dev

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      # 変更されたファイルを検出して、インフラとアプリのどちらをデプロイするか判断
      - name: Detect changed files
        id: changed-files
        uses: step-security/changed-files@v46
        with:
          files_yaml: |
            infrastructure:
              - lib/**
              - bin/**
              - cdk.json
              - package.json
              - pnpm-lock.yaml
              - tsconfig.json
            application:
              - backend/**
              - ecspresso/**

      # OIDC認証でAWSにログイン
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1

      # ===== インフラデプロイ(CDK)=====

      # Node.js のセットアップ
      - name: Setup Node.js
        if: steps.changed-files.outputs.infrastructure_any_changed == 'true'
        uses: actions/setup-node@v4
        with:
          node-version: "22.x"

      # pnpm のセットアップ
      - name: Setup pnpm
        if: steps.changed-files.outputs.infrastructure_any_changed == 'true'
        uses: pnpm/action-setup@v4
        with:
          version: 9

      # 依存関係のインストール
      - name: Install dependencies
        if: steps.changed-files.outputs.infrastructure_any_changed == 'true'
        run: pnpm install --frozen-lockfile

      # CDK でインフラをデプロイ
      - name: CDK Deploy
        if: steps.changed-files.outputs.infrastructure_any_changed == 'true'
        run: npx cdk deploy --require-approval never

      # ===== アプリデプロイ(ecspresso)=====

      # ecspresso のインストール
      - name: Setup ecspresso
        if: steps.changed-files.outputs.application_any_changed == 'true'
        uses: kayac/ecspresso@v2
        with:
          version: v2.7.1

      # ECR へのログイン
      - name: Login to Amazon ECR
        if: steps.changed-files.outputs.application_any_changed == 'true'
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      # Docker Buildx のセットアップ
      - name: Set up Docker Buildx
        if: steps.changed-files.outputs.application_any_changed == 'true'
        uses: docker/setup-buildx-action@v3

      # Docker イメージをビルドして ECR にプッシュ(GitHub Actionsキャッシュで高速化)
      - name: Build and push Backend image
        if: steps.changed-files.outputs.application_any_changed == 'true'
        uses: docker/build-push-action@v6
        with:
          context: ./backend
          platforms: linux/arm64
          push: true
          tags: |
            ${{ steps.login-ecr.outputs.registry }}/${{ vars.ECR_REPOSITORY }}:${{ github.sha }}
            ${{ steps.login-ecr.outputs.registry }}/${{ vars.ECR_REPOSITORY }}:latest
          cache-from: type=gha # 前回のビルドキャッシュを読み込み
          cache-to: type=gha,mode=max # 今回のビルドキャッシュを保存(全レイヤー)

      # ecspresso で ECS にデプロイ実行
      - name: Deploy to ECS with ecspresso
        if: steps.changed-files.outputs.application_any_changed == 'true'
        working-directory: ecspresso
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{ vars.ECR_REPOSITORY }}
          IMAGE_TAG: ${{ github.sha }}
        run: ecspresso deploy

      # デプロイ結果の確認
      - name: Verify deployment
        if: steps.changed-files.outputs.application_any_changed == 'true'
        working-directory: ecspresso
        run: ecspresso status

ワークフローのポイント

変更ファイルの検出
step-security/changed-files を使って、変更されたファイルを検出します。

- name: Detect changed files
  id: changed-files
  uses: step-security/changed-files@v46
  with:
    files_yaml: |
      infrastructure:
        - lib/**
        - bin/**
        - cdk.json
        - package.json
      application:
        - backend/**
        - ecspresso/**

これにより、以下の出力が得られます。

  • infrastructure_any_changed: インフラ関連ファイルが変更されたか
  • application_any_changed: アプリ関連ファイルが変更されたか

条件付き実行

各ステップに if 条件を追加して、必要な処理だけを実行します。

# CDK は infrastructure が変更された時のみ実行
- name: CDK Deploy
  if: steps.changed-files.outputs.infrastructure_any_changed == 'true'
  run: npx cdk deploy --require-approval never

# ecspresso は application が変更された時のみ実行
- name: Deploy to ECS with ecspresso
  if: steps.changed-files.outputs.application_any_changed == 'true'
  run: ecspresso deploy

アプリのコード変更の場合、CDK をスキップすることでデプロイ時間を短縮できます。

GitHub Environment Variables の設定

GitHub リポジトリの Settings → Environments → dev で以下の変数を設定します。

変数名 説明 値の例
ECR_REPOSITORY ECR リポジトリ名 ecs-backend

GitHub Actions からのデプロイ実行

ここからは、実際に GitHub Actions を使ってデプロイを実行してみます。

  1. GitHub リポジトリの Actions タブを開く
  2. Deploy Infrastructure and Application ワークフローを選択
  3. Run workflow をクリック

スクリーンショット 2026-02-11 15.51.12

デプロイの動作パターン
ワークフローは、変更されたファイルに応じて実行内容を自動判定します。

  • lib/bin/を変更 → CDK でインフラをデプロイ
  • backend/ecspresso/を変更 → ecspresso でアプリをデプロイ
  • 両方変更 → 両方実行
  • それ以外のファイル → スキップ

この仕組みにより、必要な処理だけを実行し、デプロイ時間を最適化しています。

初回デプロイの実行

現在の main ブランチでデプロイを実行したところ、変更がないため両方のステップがスキップされました。
スクリーンショット 2026-02-11 15.52.32
これは正常な動作です。変更がない場合、無駄なデプロイを実行しません。

実際にデプロイしてみる

直近のコミットで ecspresso/ecs-task-def.json を修正したので、再度デプロイを実行します。この変更により、ecspresso/ ディレクトリが変更されたと検知され、ecspresso デプロイが実行されます。

デプロイログの確認

GitHub Actions の実行ログを確認してみましょう。

ecspresso deploy の実行

Run ecspresso deploy --config ecspresso.yml
2026-02-11T08:33:08.301Z [INFO] ecspresso version: v2.7.1
2026-02-11T08:33:08.302Z [INFO] [backend-service/ecs-cluster] Starting deploy
2026-02-11T08:33:08.801Z [INFO] [backend-service/ecs-cluster] Service backend-service not found. Creating a new service
2026-02-11T08:33:08.801Z [INFO] [backend-service/ecs-cluster] Starting create service
2026-02-11T08:33:09.950Z [INFO] [backend-service/ecs-cluster] Registering a new task definition...
2026-02-11T08:33:10.114Z [INFO] [backend-service/ecs-cluster] Task definition is registered backend-task:1
2026-02-11T08:33:11.358Z [INFO] [backend-service/ecs-cluster] Service is created
2026-02-11T08:33:14.528Z [INFO] [backend-service/ecs-cluster] Waiting for service stable...(it will take a few minutes)
  • サービスが存在しないことを検知
  • タスク定義を登録(backend-task:1)
  • サービスを作成(backend-service)
  • サービスが安定するまで待機

タスクの起動

2026-02-11T08:33:25.065Z [INFO] [backend-service/ecs-cluster]  PRIMARY backend-task:1 desired:0 pending:0 running:0 IN_PROGRESS(ECS deployment ecs-svc/6372757946953777653 in progress.)
2026-02-11T08:33:37.091Z (service backend-service) has started 1 tasks: (task e752a5cf79b24cee9c2d34de3b9b7b35).
2026-02-11T08:33:45.042Z [INFO] [backend-service/ecs-cluster]  PRIMARY backend-task:1 desired:1 pending:1 running:0 IN_PROGRESS(ECS deployment ecs-svc/6372757946953777653 in progress.)
  • desired:1 - 目標タスク数が 1 に設定
  • pending:1 - タスクが起動準備中
  • タスク ID: e752a5cf79b24cee9c2d34de3b9b7b35

ALB へのターゲット登録

2026-02-11T08:34:07.628Z (service backend-service) registered 1 targets in (target-group arn:aws:elasticloadbalancing:ap-northeast-1:395663595720:targetgroup/EcsEcs-AlbBa-MFGUPGHW4OPN/6272bb5c2405e114)
2026-02-11T08:34:15.049Z [INFO] [backend-service/ecs-cluster]  PRIMARY backend-task:1 desired:1 pending:0 running:1 IN_PROGRESS(ECS deployment ecs-svc/6372757946953777653 in progress.)
  • ALB のターゲットグループに登録完了
  • running:1 - タスクが正常稼働中

2 つ目のタスクの起動

2026-02-11T08:34:18.165Z (service backend-service) has started 1 tasks: (task 47d9d8e19245469daf4b1879f4e6ab42).
2026-02-11T08:34:25.036Z [INFO] [backend-service/ecs-cluster]  PRIMARY backend-task:1 desired:2 pending:1 running:1 IN_PROGRESS(ECS deployment ecs-svc/6372757946953777653 in progress.)
2026-02-11T08:34:49.183Z (service backend-service) registered 1 targets in (target-group arn:aws:elasticloadbalancing:ap-northeast-1:395663595720:targetgroup/EcsEcs-AlbBa-MFGUPGHW4OPN/6272bb5c2405e114)
  • desired:2 - 目標タスク数が 2 に(設定通り)
  • タスク ID: 47d9d8e19245469daf4b1879f4e6ab42
  • ALB のターゲットグループに登録完了

デプロイ完了

2026-02-11T08:35:04.249Z [INFO] [backend-service/ecs-cluster]  PRIMARY backend-task:1 desired:2 pending:0 running:2 COMPLETED(ECS deployment ecs-svc/6372757946953777653 completed.)
2026-02-11T08:34:58.667Z (service backend-service) (deployment ecs-svc/6372757946953777653) deployment completed.
2026-02-11T08:34:58.668Z (service backend-service) has reached a steady state.
2026-02-11T08:35:04.249Z [INFO] [backend-service/ecs-cluster] Service is stable now. Completed!
  • desired:2 pending:0 running:2 - 2 つのタスクが正常稼働
  • サービスが安定状態(steady state)に到達
  • 所要時間:約 2 分

ECS コンソールで、タスク定義が正常に作成されていることも確認できました。
スクリーンショット 2026-02-11 17.42.57

デプロイ時間の比較

実際のデプロイ時間を比較してみました。

CDK + AWS CLI でのデプロイ(ecspresso なし)

使用したワークフローはこちら
# ECS Fargate へのデプロイワークフロー
# CDK でインフラをデプロイし、Docker イメージをビルドして ECS サービスをローリングデプロイする
name: Deploy to ECS

on:
  workflow_dispatch: # 手動実行のみ

# OIDC 認証に必要な権限設定
permissions:
  id-token: write # OIDC トークンの発行を許可
  contents: read # リポジトリの読み取りを許可

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: dev # GitHub Environment(環境変数を管理)

    steps:
      # ソースコードをチェックアウト
      - name: Checkout
        uses: actions/checkout@v6

      # Node.js のセットアップ
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "22.x"

      # pnpm のセットアップ
      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 9

      # 依存関係のインストール
      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      # TypeScript をビルド
      - name: Build CDK
        run: pnpm build

      # OIDC 認証で AWS にログイン
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1

      # CDK でインフラをデプロイ
      - name: CDK Deploy
        run: npx cdk deploy --require-approval never

      # ECR へのログイン(Docker イメージのプッシュに必要)
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      # Docker Buildx のセットアップ
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      # Docker イメージをビルドして ECR にプッシュ
      # ARM64 アーキテクチャでビルド(Graviton 対応)
      - name: Build and push Backend image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker buildx build \
            --platform linux/arm64 \
            -t $ECR_REGISTRY/${{ vars.ECR_REPOSITORY_BACKEND }}:$IMAGE_TAG \
            -t $ECR_REGISTRY/${{ vars.ECR_REPOSITORY_BACKEND }}:latest \
            --push \
            ./backend

      # ECS サービスを更新してローリングデプロイを開始
      # --force-new-deployment: 同じイメージでも新しいタスクを起動
      - name: Deploy to ECS
        run: |
          aws ecs update-service \
            --cluster ${{ vars.ECS_CLUSTER_NAME }} \
            --service ${{ vars.ECS_SERVICE_NAME }} \
            --force-new-deployment

      # デプロイ完了まで待機(新しいタスクが正常に起動するまで)
      - name: Wait for service to stabilize
        run: |
          aws ecs wait services-stable \
            --cluster ${{ vars.ECS_CLUSTER_NAME }} \
            --services ${{ vars.ECS_SERVICE_NAME }}

デプロイの合計時間は 約 5 分 でした。
スクリーンショット 2026-02-11 18.41.52

CDK + ecspresso でのデプロイ

デプロイの合計時間は 約 3 分 でした。
スクリーンショット 2026-02-11 18.47.13

ecspresso はなぜ速いのか?

従来の CDK + AWS CLI(aws ecs wait services-stable) でのデプロイと比較すると、その差がよくわかります。

CDK 経由の場合

CDK を使うと、ECS のデプロイだけでも以下のステップが発生します。

  1. TypeScript → CloudFormation テンプレートへの変換
  2. CloudFormation スタックの差分チェック
  3. スタック更新の実行
  4. aws ecs wait services-stable で安定を待機(15秒間隔のポーリング)

アプリのコードを1行変えただけでも、スタック全体の処理が走ります。

ecspresso の場合

ecspresso は ECS API を直接叩くため、CloudFormation を経由しません。

  1. タスク定義を登録
  2. サービスを更新
  3. タスクの状態をリアルタイム監視(数秒間隔)

CloudFormation のオーバーヘッドがないため、純粋な「ECS デプロイ時間」だけで済みます。

補足 ポーリング間隔の違い

待機処理にも差があります。aws ecs wait services-stable は15秒間隔ですが、ecspresso は数秒間隔でタスクの状態を監視し、安定した瞬間に完了を検知します。
https://docs.aws.amazon.com/cli/latest/reference/ecs/wait/services-stable.html

わずかな差に見えるかもしれませんが、1日に何度もデプロイする現場では、この『数秒の積み重ね』と『リアルタイムな進捗表示』が開発体験の向上につながる可能性があります。

まとめ

今回は GitHub Actions と ecspresso を使った ECS Fargate のデプロイ方法をご紹介しました。

CDK だけで運用していて「デプロイの待ち時間がちょっと長いな…」と感じている方は、ぜひ ecspresso を試してみてください。インフラ基盤は CDK で守りつつ、アプリは ecspresso で素早くデプロイする。この使い分けが日々の開発体験を快適にするかもしれません。

参考リンク

https://dev.classmethod.jp/articles/ideal-and-reality-when-implementing-cicd-for-ecs-on-fargate-with-aws-cdk/
https://dev.classmethod.jp/articles/ecspresso-ecs-deployment-tool/
https://dev.classmethod.jp/articles/shoma-deploy-ecs-fargate-from-github-actions/

この記事をシェアする

FacebookHatena blogX

関連記事