目次
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まで。