ECSのdocker composeのハマりポイントまとめ

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-cliecs-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のプロパティの内、restartdepends_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.ymltask_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.ymlhostnamelinksを追加した。
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を簡単に流用できるという感じではないと思った。
慣れれば色々便利なシーンはあると思う。慣れれば。

コメントする