話題の記事

【アップデート】ECSタスク定義を利用したローカル環境でのテスト実行が可能に!

完全独自仕様だったECSのタスク定義はローカル環境で使うのが難しかったのですが、それがめっさ簡単になるという超絶アップデートです。
2019.07.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

「Docker ComposeとECSタスク定義の2重管理めんどくさい(TдT)」

ECSはフルマネージドのコンテナコントロールプレーンとして、AWS上のコンテナワークロードのデファクトスタンダードと言えます。ただ、EKSとは違ってECSは完全にAWS独自仕様のため、ローカル環境やAWS以外の環境で、その設定情報をそのまま流用できないのが難点でした。

今回、ECS CLIのローカルテスト機能が、思いっきりアップデートされました。

今回のアップデートで、ECSのタスク定義をシームレスにDocker Compose仕様に変換したり、ローカル環境差分を別ファイルで管理することができるようになっているため、ローカル環境でのタスク定義を利用したテストが、非常に簡単になっています。

日常的にECSを使われている方は、是非一度、この機能試していただければと思います。

タスク定義とローカル環境がワッショイワッショイ!!??

  ( ゚д゚) ガタッ
  /   ヾ
__L| / ̄ ̄ ̄/_
  \/   /

ECSのローカルテストの概要

今回、新しくアナウンスされたECSのローカルテスト機能のアップデートですが、正確には、従来あったECS コマンドラインインターフェイスの機能拡張となります。

Amazon ECS コマンドラインインターフェイスの使用 - Amazon Elastic Container Service

Amazon Elastic Container Service (Amazon ECS) コマンドラインインターフェイス (CLI) には、ローカル開発環境からのクラスターおよびタスクの作成、更新、モニタリングを簡素化する高レベルコマンドが用意されています。Amazon ECS CLI では、Docker Compose ファイル (バージョン 1、バージョン 2、バージョン 3) サポートされています。Docker Compose は、マルチコンテナアプリケーションを定義して実行するためによく使用されるオープンソースツールです。
引用:Amazon ECS コマンドラインインターフェイスの使用 - Amazon Elastic Container Service

ECS CLIでは、以前(2019年5月27日)に、ローカルテスト機能の一部(AWS認証情報の確認や、タスクメタデータサービスのエンドポイント利用)が利用可能になっていました。今回のアップデートで、そのローカルテスト機能が大幅に拡張されたことになります。

ECS CLIローカルの関連ドキュメント

ECS CLIのGitHubはこちら。

GitHubのうち、ECSタスクをローカルで動かすREADMEはこちら。

また、AWS側の網羅的なコマンドリファレンスはこちらになります。

AWSの公式ドキュメントも一式更新されているため、下記で記載するコマンドの詳細は、公式ドキュメントを参照ください。

事前準備:ECS CLIのインストール(アップデート)

最初にECS CLIのインストールを行います。ドキュメントは以下に記載があります。macOS、Linux、Windowsに対応しています。ECS CLIを元々使っていた人も、今回のローカルテスト実行には最新版が必要と思われるので、同じく実施しておきましょう。

自分のローカル環境はMacなので、mac用のバイナリをダウンロードし、バイナリフォルダに格納。

$ sudo curl -o /usr/local/bin/ecs-cli https://amazon-ecs-cli.s3.amazonaws.com/ecs-cli-darwin-amd64-latest

バイナリへの実行権限付与。

$ sudo chmod +x /usr/local/bin/ecs-cli

インストールを確認します。

$ ecs-cli --version
ecs-cli version 1.15.1 (628b3e6)

基礎編:ECSタスクをローカルで動かしてみる

GitHubのREADMEに基本的な解説があります。

ローカルテスト用のタスク定義(JSON)の用意

最初に、ローカルテストに利用するECSのタスク定義(JSON)を用意します。今回は、WordPressとMySQLをそれぞれコンテナで起動する以下のタスク定義を用意します。

ファイル名wp-mysql.jsonで、以下のファイルを作成します。

wp-mysql.json

{
  "containerDefinitions": [
    {
      "name": "wordpress",
      "links": [
        "mysql"
      ],
      "image": "wordpress",
      "essential": true,
      "portMappings": [
        {
          "containerPort": 80,
          "hostPort": 8080
        }
      ],
      "memory": 500,
      "cpu": 10
    },
    {
      "environment": [
        {
          "name": "MYSQL_ROOT_PASSWORD",
          "value": "password"
        }
      ],
      "name": "mysql",
      "image": "mysql",
      "cpu": 10,
      "memory": 500,
      "essential": true
    }
  ],
  "family": "hello_world"
}

タスク定義JSONから、Docker Composeファイルの生成

以下のコマンドで、タスク定義JSONのwp-mysql.jsonから、docker composeのYAMLのdocker-compose.wp-mysql.ymlを作成します。

$ ecs-cli local create --task-def-file wp-mysql.json --output docker-compose.wp-mysql.yml
INFO[0000] Successfully wrote docker-compose.wp-mysql.yml 
INFO[0000] Successfully wrote docker-compose.wp-mysql.override.yml

そうすると、以下のdocker composeファイルdocker-compose.wp-mysql.ymlが生成されます!すげぇな!

docker-compose.wp-mysql.yml

version: "3.4"
services:
  mysql:
    environment:
      AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: /creds
      ECS_CONTAINER_METADATA_URI: http://169.254.170.2/v3
      MYSQL_ROOT_PASSWORD: password
    image: mysql
    labels:
      ecs-local.task-definition-input.type: local
      ecs-local.task-definition-input.value: /Users/hamada.koji/prj/hamada/ecs-local/wp-mysql.json
    networks:
      ecs-local-network: null
  wordpress:
    environment:
      AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: /creds
      ECS_CONTAINER_METADATA_URI: http://169.254.170.2/v3
    image: wordpress
    labels:
      ecs-local.task-definition-input.type: local
      ecs-local.task-definition-input.value: /Users/hamada.koji/prj/hamada/ecs-local/wp-mysql.json
    links:
    - mysql
    networks:
      ecs-local-network: null
    ports:
    - target: 80
      published: 8080
networks:
  ecs-local-network:
    external: true

ローカル環境での起動

生成されたdocker-compose.wp-mysql.ymlを元に、ローカル環境でコンテナを起動します。初回起動時は、関連イメージのダウンロードも同時に実行されるはずです。

$ ecs-cli local up --task-def-compose docker-compose.wp-mysql.yml 
INFO[0000] The network ecs-local-network already exists 
INFO[0000] Created the amazon-ecs-local-container-endpoints container with ID a6ae1a5cfeaa2523137d3ff38a4e25ca732f1f2f2ee267740d5b87188728d209 
INFO[0000] Started container with ID a6ae1a5cfeaa2523137d3ff38a4e25ca732f1f2f2ee267740d5b87188728d209 
INFO[0000] Using docker-compose.wp-mysql.yml, docker-compose.wp-mysql.override.yml files to start containers 
Creating ecs-local_mysql_1 ... done
Creating ecs-local_wordpress_1 ... done

起動確認は、ecs-cli local psコマンドで実施します。このとき、元ネタとなっているwp-mysql.jsonを指定しないと、プロセスが見えないので注意が必要です。

wordpressコンテナは、ローカルホストの8080を受けて要ることなどもわかります。

$ ecs-cli local ps -f wp-mysql.json 
CONTAINER ID        IMAGE               STATUS              PORTS                         NAMES                    TASKDEFINITION
a98576f9210a        wordpress           Up 4 minutes        0.0.0.0:8080->80/tcp          /ecs-local_wordpress_1   /Users/hamada.koji/prj/hamada/ecs-local/wp-mysql.json
c7509cda504c        mysql               Up 4 minutes        :0->3306/tcp, :0->33060/tcp   /ecs-local_mysql_1       /Users/hamada.koji/prj/hamada/ecs-local/wp-mysql.json

この時、docker psコマンドを実行すると、WordpressとMySQLコンテナ以外に、ecs localのエンドポイントを提供するコンテナが起動しているのが特徴です。

$ docker ps
CONTAINER ID        IMAGE                                         COMMAND                  CREATED             STATUS              PORTS                  NAMES
a98576f9210a        wordpress                                     "docker-entrypoint.s…"   7 minutes ago       Up 7 minutes        0.0.0.0:8080->80/tcp   ecs-local_wordpress_1
c7509cda504c        mysql                                         "docker-entrypoint.s…"   7 minutes ago       Up 7 minutes        3306/tcp, 33060/tcp    ecs-local_mysql_1
a6ae1a5cfeaa        amazon/amazon-ecs-local-container-endpoints   "/local-container-en…"   7 minutes ago       Up 7 minutes        80/tcp                 amazon-ecs-local-container-endpoints

動作確認

ブラウザでlocalhost:8080にアクセスすると、無事、WordPressの初期設定画面が起動します。よござんす!!

ここまで、元ネタはECSのタスク定義のJSONだけだったのですが、それだけを元にして、ローカルでの起動が確認できました。

実践編:超絶便利!!AWS上ECSタスク定義からの直接ローカル実行

以上、タスク定義JSONから、ECSローカルによる起動を確認してみました。ただ、既存のECS環境から、直接ECSローカルを起動してみたという人も多いと思います。特に、WebコンソールやCloudFormationでタスク定義を登録している場合、別途タスク定義のJSONを用意するのは、存外めんどくさいものです。

実は、上述したecs-cli local upコマンドには、ECSタスク定義のARNを直接利用して、ローカルでタスク(コンテナ)を起動するオプションがあります。これがもう、超絶便利。

事前に、ECSタスク定義をWebコンソールなどで登録しておきます。

以下のコマンドでARNを取得します。事前にAWS CLIと認証情報が設定されていることが条件となります。以下のコマンド例では、タスク名がfargate-php-taskの、最新リビジョンのARNを取得しています。

$ aws ecs describe-task-definition --task-definition fargate-php-task --query "taskDefinition.taskDefinitionArn" --output text
arn:aws:ecs:ap-northeast-1:123456789012:task-definition/fargate-php-task:2

ARNを取得できたら、以下のコマンドでECSローカルを起動します。オプションの--task-def-remoteでARNを指定します。

$ ecs-cli local up --task-def-remote arn:aws:ecs:ap-northeast-1:123456789012:task-definition/fargate-php-task:2
INFO[0000] Reading task definition from fargate-php-task:2
 
INFO[0000] Task Definition network mode is ignored when running containers locally. Tasks will be run in the ecs-local-network.  networkMode=awsvpc
WARN[0000] awslogs log driver is ignored when running locally. Tasks will default to json-file instead. This can be changed in your compose override file. 
INFO[0000] Successfully wrote docker-compose.ecs-local.yml 
INFO[0000] Successfully wrote docker-compose.ecs-local.override.yml 
INFO[0000] The network ecs-local-network already exists 
INFO[0000] The amazon-ecs-local-container-endpoints container already exists with ID a6ae1a5cfeaa2523137d3ff38a4e25ca732f1f2f2ee267740d5b87188728d209 
INFO[0000] Started container with ID a6ae1a5cfeaa2523137d3ff38a4e25ca732f1f2f2ee267740d5b87188728d209 
INFO[0000] Using docker-compose.ecs-local.yml, docker-compose.ecs-local.override.yml files to start containers 
Creating ecs-local_php-container_1 ... done

おっしゃ、これで動作確認するぞ!と思ったんですが、うまく動きません。もともとこのタスク定義はFargate用のタスク定義のため、ホストインスタンス側のポート設定がされていないものでした。

docker psで確認すると、ホストインスタンスとのポートマッピングが設定されていないことがわかります。

CONTAINER ID        IMAGE                                               COMMAND                  CREATED              STATUS              PORTS                  NAMES
47e53adde965        hamako9999/list-environments-php-container:latest   "docker-php-entrypoi…"   About a minute ago   Up About a minute   80/tcp                 ecs-local_php-container_1

こういった場合の回避策として、Docker Composeではローカル環境用の設定をオーバーライド(上書き)するための機能が存在します。

上記コマンド(ecs local up)を実行したとき、サーバーのARNからECSローカルが直接起動しているわけではなく、ローカル側で、Docker Compose用のファイルが2つ自動的に生成されています(特にオプションでファイル名を指定しない場合)。

  • docker-compose.ecs-local.yml

docker-compose.ecs-local.yml

version: "3.4"
services:
  php-container:
    environment:
      AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: /creds
      ECS_CONTAINER_METADATA_URI: http://169.254.170.2/v3
    image: hamako9999/list-environments-php-container:latest
    labels:
      ecs-local.task-definition-input.type: remote
      ecs-local.task-definition-input.value: arn:aws:ecs:ap-northeast-1:123456789012:task-definition/fargate-php-task:2
    logging:
      driver: awslogs
      options:
        awslogs-group: /ecs/fargate-php-task
        awslogs-region: ap-northeast-1
        awslogs-stream-prefix: ecs
    networks:
      ecs-local-network: null
networks:
  ecs-local-network:
    external: true
  • docker-compose.ecs-local.override.yml

docker-compose.ecs-local.override.yml

version: "3.4"
services:
  php-container:
    environment:
      AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: /creds
    logging:
      driver: json-file

今回は、docker composeファイルにポート指定がないため、ローカルホストとのポートマッピングをdocker-compose.ecs-local.override.ymlに、以下のように追記します。

docker-compose.ecs-local.override.yml

version: "3.4"
services:
  php-container:
    environment:
      AWS_CONTAINER_CREDENTIALS_RELATIVE_URI: /creds
    logging:
      driver: json-file
    ports:
    - target: 80
      published: 9000

オーバーライドファイルを作成したら、先程のコマンドに--overrideオプションで、作成したオーバーライドファイルを指定します。

$ ecs-cli local up --task-def-remote arn:aws:ecs:ap-northeast-1:123456789012:task-definition/fargate-php-task:2 --override docker-compose.ecs-local.override.yml

docker psコマンドで、ホストインスタンス側のポート9000がコンテナにマッピングされていることを確認します。

CONTAINER ID        IMAGE                                               COMMAND                  CREATED             STATUS              PORTS                  NAMES
16e12bcfbd9d        hamako9999/list-environments-php-container:latest   "docker-php-entrypoi…"   7 seconds ago       Up 6 seconds        0.0.0.0:9000->80/tcp   ecs-local_php-container_1

このコンテナは、localhost/phpinfo.phpで環境変数の一覧を返します。今回はポート9000をマッピングしているため、localhost:9000/phpinfo.phpにアクセスし、無事コンテナにアクセスできればOK!!

「ECSタスク定義のみによるコンテナ環境管理が可能に!!」

当たり前の話ですが、ECSのタスク定義はAWS上で動作することを前提として設定します。JSONファイルも独自仕様です。その定義ファイルをそのままローカル環境で使うことはできないため、ローカル環境独自のDocker Composeファイルを用意し運用している現場も多いと思います。特に、複数コンテナが1タスクに入る場合(非常におおくある)場合は、Docker Composeファイルの別管理はほぼ必須でした。

今回のECS CLIのローカル機能拡張により、ECSのタスク定義から自動でDocker Composeファイルを生成し、さらにローカル環境との設定差分を管理する機能が組み込まれました。これによりAWS上のECSタスク定義をシームレスにローカル環境で使うことができます。

だいたい、ここらへんのシェルを設定ファイルを用意しておけば大丈夫そうです。

  • タスク定義の最新バージョンのARNを自動的に取得
  • 取得したARNからECSローカルでコンテナ起動
  • クラウド側とローカル側の差分設定のみをオーバーライドファイルとして用意
    • DB接続先などを各ローカル環境設定ファイルとして用意

フルマネージドのECSは設定ファイルも完全独自仕様だったので、こんな形でローカル環境での流用が可能になるとは思いませんでした。開発環境のコンテナ設定管理を非常に簡略化できる可能性があると思うので、一度ECS CLIローカル、試してみていただければと思います。

それでは、今日はこのへんで。濱田(@hamako9999)でした。