ECSをCI/CDにのせるとインフラとアプリ側で差分が生まれやすくなります。この差分を埋めてくれるのがecspressoです。
仮にCI/CDを導入していなくてもインフラとアプリではデプロイのサイクルが違うので、アプリのサイクルに対してインフラに問題があると、アプリのサイクルにインフラの問題が影響してしまいます。
この記事ではこれらの問題点をより明確にして、ecspressoの利点を正しく理解した上で実際に導入してみます![]()
なぜecspressoが必要なのか?
CI/CDを利用している場合と、そうでないパターンの2つを書きます。
パターン1:CI/CDを利用している場合
まずはCI/CDを導入している場合から解説します。
ecspressoを導入していない場合
インフラ(Terraform)とアプリ(CI/CD)の両方からタスク定義を触れることになります。こうなるとアプリ側から更新することが基本になりますが、アプリ側で変更を行うとインフラ側で何かしら更新をかけるときに差分を検知しています。
じゃあタスク定義を管理外から外せばいいやんとなるかもしれませんが、結局サービス側でタスク定義を管理しているので、バージョンの差分を検知してしまうことになります。
ecspressoを導入した場合
ECSのサービスレベルからアプリ側で管理することになるため、インフラ側で差分を気にする必要がなくなります。インフラ側はクラスターまで、アプリ側はサービスまでと明確な境界線を引くことができるようになりました。
パターン2:CI/CDを利用していない場合
次にCI/CDを利用していない場合です。この場合においても、ecspressoは力を発揮してくれます。
ecspressoを導入していない場合
アプリはデプロイやサービスの更新などで、そのサイクルはインフラに比べて早くなりがちです。そうなると、インフラから切り離して管理したいという思いが出てきます。
Terraformならstateを分ける発想もあるかと思いますが、設計する手間もありますし、サービスに増える予定があるなら、なおさら手間が大きくなります。
ecspressoを導入した場合
ECSサービス以下がecspressoで管理できるようになります。こうすることでインフラを意識せず、アプリのサイクルを回すことができます。
実際に導入してみる
ecspressoはCI/CDを利用していることでより恩恵を受けることができるのため、今回の導入はCI/CD前提で導入してみます。
GitHub
下記のリポジトリを使えば、お試しの環境をそのまま導入することができます。
インフラ
アプリ
インフラ環境の構築
ECSサービスの側になるVPC、ALB、ECSのSGやロール、S3などを作ります。このあたりは本筋ではないので、構築はインフラのGitHubリポジトリを参照ください。
Terraformのstateはecspressoに読んでもらう必要があるため、ここで作成したS3へ保存するようになっています。TerraformでS3を作ったらbackendを切り替えてください。もちろんS3は手動で作って、そこをbackendとして読ませてもOKです。
ECSの作成はECSクラスターまでにとどめてください。
アプリ環境の構築
IAMロールの登録
インフラで作成したIAMロールをGitHub側に登録します。やり方は下記を参考にしてください。
イメージの準備
デプロイ用のイメージを作成しておきます。前述したリポジトリでは、nginxのイメージを用意しました。
ecspresso
ここでようやく本題である、ecspressoの準備をします。
ecspresso.ymlを設定のエントリーポイントとして記述し、ECSサービスをecs-service-def.jsonに、タスク定義を ecs-task-def.json に記述します。
region: ap-northeast-1
cluster: ecspresso-cluster
service: ecspresso-service
task_definition: ecs-task-def.json
service_definition: ecs-service-def.json
plugins:
- name: tfstate
config:
url: s3://ecspresso-terraform-state-202512292024/ecspresso-infra/terraform.tfstate
{
"launchType": "FARGATE",
"desiredCount": 1,
"loadBalancers": [
{
"containerName": "app",
"containerPort": 8080,
"targetGroupArn": "{{ tfstate `aws_lb_target_group.main.arn` }}"
}
],
"networkConfiguration": {
"awsvpcConfiguration": {
"assignPublicIp": "ENABLED",
"securityGroups": [
"{{ tfstate `aws_security_group.ecs.id` }}"
],
"subnets": [
"{{ tfstate `aws_subnet.public_1.id` }}",
"{{ tfstate `aws_subnet.public_2.id` }}"
]
}
}
}
{
"family": "ecspresso-td",
"requiresCompatibilities": [
"FARGATE"
],
"networkMode": "awsvpc",
"taskRoleArn": "{{ tfstate `aws_iam_role.ecs_task.arn` }}",
"executionRoleArn": "{{ tfstate `aws_iam_role.ecs_task.arn` }}",
"cpu": "256",
"memory": "512",
"containerDefinitions": [
{
"name": "app",
"image": "{{ must_env `IMAGE_URI` }}",
"cpu": 0,
"essential": true,
"portMappings": [
{
"containerPort": 8080,
"protocol": "tcp"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "{{ tfstate `aws_cloudwatch_log_group.main.name` }}",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
検証環境のため、IAMロールは同一のものを使用しています。実際に運用する場合などは適宜個別のIAMロールを用意してください。
GitHub Actions
最後はworkflow.yamlです。フローの最後でecspressoを動作させます。
# ...
jobs:
deploy:
# ...
steps:
# ...
- name: Setup ecspresso
uses: kayac/ecspresso@v2
with:
version: v2.7.0
- name: Deploy with ecspresso
env:
IMAGE_URI: ${{ steps.ecr.outputs.registry }}/ecspresso-ecr:${{ github.sha }}
run: |
ecspresso deploy --config deploy/ecspresso.yml
動かしてみる
アプリをgit pushしてCI/CDを動作してみます。
アプリ自体も正常にデプロイできました。
以上です。
さいごに(作者について)
作者は日本のfujiwaraさんという方でした。ECSの運用において必須級とも言えるこのツールの作成に感謝です。
PipeCDとかもそうですが、このあたりに日本人の作成者がいるのは、ほんとにすごいことだなと思いました![]()




