GitHub Actions × ecspresso × CDK で ECS Fargate デプロイを高速化 - デプロイ時間 5分→3分の改善
製造ビジネステクノロジー部の小林です。
以前、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 コマンドだけで完結させるツールです。
詳細については以下をご覧ください。
主な特徴
- タスク定義とサービス定義を JSON ファイルで管理
- デプロイ前に差分を確認できる(
ecspresso diff) - ロールバックが簡単(
ecspresso rollback) - 強力なテンプレート機能(環境変数・SSM パラメータストア参照)
- GitHub Actions との統合が簡単
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」と役割を分けることで、両方のメリットを享受できます。

結局 ecspresso を使うと何が嬉しいの?
一言で言えば、『特定の構成下では ECS デプロイが高速化する可能性がある』です。本記事の検証環境では、デプロイ時間が 5 分から 3 分に短縮されました。
-
デプロイが速い
ecspresso は ECS API を直接叩いてデプロイを行うため、CloudFormation を経由する CDK と比較して、オーバーヘッドが少なく高速です。 -
「インフラ」と「アプリ」の責務を分離できる
インフラは CDK で、頻繁に更新するアプリの設定は ecspresso で、といった使い分けが可能です。デプロイのたびに重たい IaC ツールを実行するストレスから解放されます。 -
実数値としての効果(検証結果)
本記事の構成で検証したところ、デプロイ時間が 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 パラメータストアにリソース情報が保存されます。


この時点で、ECS クラスターは作成されていますが、タスク定義とサービスはまだ作成されていません。これらは次のステップで ecspresso を使って作成します。
ecspresso のセットアップ
ここからは、ecspresso を使って ECS のタスク定義とサービスを管理する設定を行います。
CDK で作成したインフラの上に、ecspresso でアプリケーションのデプロイ設定を構築していきます。
ecspresso 設定ファイルの作成
ecspresso の基本設定ファイル ecspresso/ecspresso.yml を作成します。このファイルは、ecspresso がどの ECS クラスターとサービスを管理するかを定義します。
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、サブネット、セキュリティグループ)
- ロードバランサーとの連携
- デプロイ戦略
- ヘルスチェック設定
{
"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 を実行します。
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 を使ってデプロイを実行してみます。
- GitHub リポジトリの Actions タブを開く
- Deploy Infrastructure and Application ワークフローを選択
- Run workflow をクリック

デプロイの動作パターン
ワークフローは、変更されたファイルに応じて実行内容を自動判定します。
lib/やbin/を変更 → CDK でインフラをデプロイbackend/やecspresso/を変更 → ecspresso でアプリをデプロイ- 両方変更 → 両方実行
- それ以外のファイル → スキップ
この仕組みにより、必要な処理だけを実行し、デプロイ時間を最適化しています。
初回デプロイの実行
現在の main ブランチでデプロイを実行したところ、変更がないため両方のステップがスキップされました。

これは正常な動作です。変更がない場合、無駄なデプロイを実行しません。
実際にデプロイしてみる
直近のコミットで 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 コンソールで、タスク定義が正常に作成されていることも確認できました。

デプロイ時間の比較
実際のデプロイ時間を比較してみました。
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 分 でした。

CDK + ecspresso でのデプロイ
デプロイの合計時間は 約 3 分 でした。

ecspresso はなぜ速いのか?
従来の CDK + AWS CLI(aws ecs wait services-stable) でのデプロイと比較すると、その差がよくわかります。
CDK 経由の場合
CDK を使うと、ECS のデプロイだけでも以下のステップが発生します。
- TypeScript → CloudFormation テンプレートへの変換
- CloudFormation スタックの差分チェック
- スタック更新の実行
- aws ecs wait services-stable で安定を待機(15秒間隔のポーリング)
アプリのコードを1行変えただけでも、スタック全体の処理が走ります。
ecspresso の場合
ecspresso は ECS API を直接叩くため、CloudFormation を経由しません。
- タスク定義を登録
- サービスを更新
- タスクの状態をリアルタイム監視(数秒間隔)
CloudFormation のオーバーヘッドがないため、純粋な「ECS デプロイ時間」だけで済みます。
補足 ポーリング間隔の違い
待機処理にも差があります。aws ecs wait services-stable は15秒間隔ですが、ecspresso は数秒間隔でタスクの状態を監視し、安定した瞬間に完了を検知します。
わずかな差に見えるかもしれませんが、1日に何度もデプロイする現場では、この『数秒の積み重ね』と『リアルタイムな進捗表示』が開発体験の向上につながる可能性があります。
まとめ
今回は GitHub Actions と ecspresso を使った ECS Fargate のデプロイ方法をご紹介しました。
CDK だけで運用していて「デプロイの待ち時間がちょっと長いな…」と感じている方は、ぜひ ecspresso を試してみてください。インフラ基盤は CDK で守りつつ、アプリは ecspresso で素早くデプロイする。この使い分けが日々の開発体験を快適にするかもしれません。
参考リンク






