目次
AWSのECSにはdocker-compose.ymlを実行できる仕組みが用意されています。
今回はそれを触ってみた所、細かい仕様の違いが多く、数々のエラーに遭遇したので記録として残しておきます。
前提
docker-compose.ymlは個人プロダクトの本番環境で運用していた物を用意した。
一部を紹介するとこんな感じ。
特徴としてはLightsail(VPS)で運用していたので、コンテナのビルドは本番環境上で行っていました。(なので各サービスにimageプロパティではなくbuildプロパティがある)
version: '3'
services:
app:
container_name: ${COMPOSE_PROJECT_NAME}-app
build: &app-build
context: app
dockerfile: ${ENVIRONMENT}.Dockerfile
args:
- CODE_REGISTRY=${CODE_REGISTRY}
- APP_UID=${APP_UID}
- APP_GID=${APP_GID}
restart: always
volumes: &app-volumes
- ./app/.env:/var/www/app/.env:ro
- app-storage:/var/www/app/storage:rw
これをECSで実行してみます。
クラスタを作る
マネジメントコンソールからあらかじめクラスタを作成しておく。
ここではEC2タイプのクラスタを作成した

aws-cliとecs-cliをインストールする
ドキュメントに従ってaws-cliとecs-cliをインストールした。(細かい手順はリンク先のドキュメント参照)

ecs-cliの初期設定を行う
アクセスキーとシークレットキーを登録する
ちなみに入力した内容は~/.ecs/配下に保存されている。
ecs-cli configure profile --profile-name XXXX --access-key XXX --secret-key XXXX
操作対象のクラスタを設定する--clusterはクラスタ名を指定する--regionはリージョンを指定する--config-nameはこの設定に名前を付けられる。
ecs-cli configure --cluster XXXX --region ap-northeast-1 --config-name XXXX
ecs composeを実行する
ecs-cli compose -f docker-compose.yml service up
ここから長い戦いが始まるのであった・・・
コンテナは予めビルドしておく必要がある
以下のエラーが発生した。
imageの指定がないサービスは駄目らしい。
つまり、ビルドしたイメージを予めECRなどにプッシュしておく必要がある模様。
(docker composeならビルドも出来たが、ECS composeでは不可)
ERRO[0000] Error registering task definition error="ClientException: Container.image should not be null or empty." family=XXXX
ERRO[0000] Create task definition failed error="ClientException: Container.image should not be null or empty."
FATA[0000] ClientException: Container.image should not be null or empty.
コンテナをビルドしてECRに登録した後、docker-compose.ymlのbuildプロパティをimageプロパティに置き換えた。
version: '3'
services:
app:
image: XXXX.dkr.ecr.us-east-1.amazonaws.com/XXXX:latest
メモリ上限の指定が必要
以下のエラーが発生した。
コンテナ毎にメモリ使用量の上限を設定する必要があるらしい。
INFO[0000] Using ECS task definition TaskDefinition="XXX:1"
INFO[0000] Couldn't run containers reason="RESOURCE:MEMORY"
docker-compose.ymlと同じ階層にecs-params.ymlを作成し、以下の用にmem_limitを記載した。
ecs-params.ymlの仕様はこちらを参照。
version: 1
task_definition:
services:
app:
mem_limit: 400000000
ちなみにdocker-compose.ymlにもmem_limitというプロパティがあるが、こちらを使おうとすると「そのプロパティはサポートしていない」旨のエラーが発生する。
使えないプロパティがある
以下のエラーが発生した。
docker-compose.ymlのプロパティの内、restartやdepends_onはサポートされていない模様(2020年10月現在)
使えるプロパティはこちらを参照
FATA[0000] Unable to create and read ECS Compose Project error="Configuration contains forbidden properties"
コマンドは通ったが・・・
完全には起動しておらず、再起動を繰り返している模様。
INFO[0305] Created an ECS service service=XXXX taskDefinition="XXXX:4"
FATA[0305] Deployment has not completed: Running count has not changed for 5.00 minutes
どういう状態になっているのか確認していく。
composeで起動した場合もタスク定義が作られてサービスが起動するのは同じ模様。
以下の様に一つのタスク定義に複数のコンテナが定義されていた。

クラスターのサービスタブを確認するとサービスが追加されている。

サービスをクリックして、イベントタブを開くと、コンテナの起動に失敗している旨が表示されていた。

環境変数を指定する
これまで.envファイルに機密情報や環境依存の設定を記載していたが、ECSだとその手段が使えない。
ecs-params.ymlで環境変数を指定でき、更に値をパラメータストアから引っ張ってくるという事ができるのでこれを活用する。
まずはSystemManagerパラメータストアに値を登録していく。
以下のコマンドで登録できる
aws ssm put-parameter --region ap-northeast-1 --name "パラメータ名" --type String --value "値"
マネジメントコンソールで確認すると、こんな感じで値が登録されている。

ecs-params.ymlで環境変数を定義する。nameが環境変数の名前になる。value_fromでSystemManagerパラメータストアの値を引っ張ってこれる。
version: 1
task_definition:
services:
app:
mem_limit: 400000000
secrets: &app-secrets
- value_from: xxxx_app-name
name: APP_NAME
- value_from: xxxx_db-database
name: DB_DATABASE
- value_from: xxxx_db-host
name: DB_HOST
- value_from: xxxx_db-password
改めてecs-cli composeを実行すると以下のエラーが発生した。secretsプロパティを使用する場合はexecutionRoleArnの指定が必須らしい。
FATA[0000] ClientException: When you are specifying container secrets, you must also specify a value for 'executionRoleArn'.
試しにAmazonECSTaskExecutionRolePolicyを持ったロールを作成してecs-params.ymlのtask_execution_roleにロールのARNを指定した。
version: 1
task_definition:
task_execution_role: arn:aws:iam::XXXXX:role/XXXXX
改めてecs-cli composeを実行すると別のエラーが発生した
service XXXXX failed to launch a task with (error ECS was unable to assume the role 'arn:aws:iam::XXXXX:role/XXXXXX' that was provided for this task. Please verify that the role being passed has the proper trust relationship and permissions and that your IAM user has permissions to pass this role.
信頼関係を以下の様に変更した

{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
改めてecs-cli composeを実行すると更に別のエラーが発生した
作成したロールからパラメータストアにアクセスする権限がない模様。
Fetching secret data from SSM Parameter Store in ap-northeast-1: AccessDeniedException: User: XXX is not authorized to perform: ssm:GetParameters on resource: arn:aws:ssm:ap-northeast-1:XXX:parameter/XXXX status code: 400, request id: XXXX
以下のポリシーを作成して、ロールに付与した。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ssm:GetParameters",
"ssm:GetParameter"
],
"Resource": "arn:aws:ssm:ap-northeast-1:XXXX:parameter/XXXXX"
}
]
}
コンテナ間通信にhostnameとlinksが必要
以下のエラーが発生した。
コンテナ間通信で失敗している。
docker composeの場合、サービス名がそのままホスト名になるが、ECS composeではそうではない模様。
2020/10/10 15:45:45 [emerg] 1#1: host not found in upstream "app" in /etc/nginx/conf.d/site.conf:32
nginx: [emerg] host not found in upstream "app" in /etc/nginx/conf.d/site.conf:32
docker-compose.ymlにhostnameとlinksを追加した。hostnameでコンテナのホスト名を設定して、linksでコンテナ間通信する相手ホストを設定する。
version: '3'
services:
app:
hostname: app
image: XXXX.dkr.ecr.us-east-1.amazonaws.com/XXXX:latest
links: &app-links
- kvs
kvs:
hostname: kvs
image: XXXX.dkr.ecr.us-east-1.amazonaws.com/XXXX:latest
コンテナが一つでも停止すると全て停止される
docker composeの場合、コンテナが1つ停止しても他のコンテナに影響しないが、ECS composeの場合、コンテナが1つでも停止すると全てのコンテナが停止される。(その後自動的に再起動を試みる)
なので、ECS composeでは、compose対象のコンテナの中に、常時起動のコンテナと、起動終了を繰り返す様なコンテナが混在すると常時起動のコンテナも巻き添えで停止してしまう。
所感
結構クセがあるので既存のdocker-compose.ymlを簡単に流用できるという感じではないと思った。
慣れれば色々便利なシーンはあると思う。慣れれば。
Webエンジニアをやっています
UX/UIデザインからプログラミング、DB設計、SEO、インフラ構築など幅広く対応してます
PHP/PHPUnit/Laravel/Vue/Nuxt/Docker/Terraform
ご連絡はTwitterのDMまで。
