less is more

心のフルスタックエンジニア👨‍💻バイブスでコードを書いています🤘

ECSのタスク実行をCLIからやる

f:id:bluepixel:20200415190526p:plain

すでに作成済みのタスク定義を指定して、CLIから任意のタスクを実行する環境を整えます。

IAMポリシーのセットアップ

タスク実行に必要な ecs:RunTask の他に、タスク定義を参照するための ecs:DescribeTasksや、 CloudWatchへのログ出力のためのポリシーが必要になります。

最終的な設定はこんな感じになった。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "0",
            "Effect": "Allow",
            "Action": [
                "iam:PassRole",
                "ecs:RunTask",
                "ecs:DescribeTasks"
            ],
            "Resource": [
                "arn:aws:ecs:*:*:task/*",
                "arn:aws:ecs:*:*:task-definition/foo*",
                "arn:aws:iam::*:role/foo*"
            ]
        },
        {
            "Sid": "1",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:DescribeLogGroups",
                "logs:DescribeLogStreams",
                "logs:FilterLogEvents",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}

一つハマったのが、タスク定義のリビジョン指定。

マネジメントコンソールからビジュアルエディタで作る場合、リソースの指定は task definition family nametask definition revision number に分かれていて、できあがるARNはfoo:1とかfoo:2のようになる。

f:id:bluepixel:20200415191546p:plain

大抵のケースではリビジョンまで絞る必要はないので、foo:*ワイルドカードになるのだが、これが落とし穴で、

AWS CLI から Run Task API を呼び出す際に、タスク定義のリビジョンを省略すると、最新のアクティブなリビジョンを使用するようになっている。

The family and revision (family:revision ) or full ARN of the task definition to run. If a revision is not specified, the latest ACTIVE revision is used.

docs.aws.amazon.com

便利なのだが、これだと先ほどのリソース指定のワイルドカードに含まれる:が邪魔になって、パーミッションエラーになる。

なので、直接JSONを編集してfoo*とするとよい。これならリビジョンを指定しても省略しても動くようになる。

入力テンプレートの作成

特にコンテナのコマンドを上書きする場合など、渡すパラメータが長くなりがちなので、ファイルで定義することにする。

--generate-cli-skeleton オプションで雛形を生成する。
yamlの方が好みなので、yaml-inputをつける。

aws ecs run-task --generate-cli-skeleton yaml-input > tmp.yml

最低限このくらい渡せば動く。

cluster: foo
count: 1
launchType: FARGATE
networkConfiguration:
  awsvpcConfiguration:
    subnets:
      - foo
    securityGroups:
      - bar
    assignPublicIp: ENABLED
overrides:
  containerOverrides:
    - name: foo
      command: 
        - echo
        - Hello
taskDefinition: foo

実行

aws ecs run-task --cli-input-yaml file://./tmp.yml

これもハマりどころの一つだが、ローカルにあるファイルを指定する場合は、プレフィックスfile://を付ける必要がある。

tasks を含む長いレスポンスが返ってきたら成功。

"tasks": [
  ....
]

タスクの起動は非同期になるので、この時点ではあくまでタスク実行リクエストを受け取ったという成功に過ぎない。Fargateは起動に1分くらいかかるし、タスクが exit 0で正常終了するかどうかまではさすがに待ってくれない。

ログの確認

Cloud Watch Logs にログを流している場合を想定している。 これもCLIからログを見てみる。

aws logs get-log-events \
  --log-group-name foo \
  --log-stream-name $(aws logs describe-log-streams \
    --log-group-name foo \
    --order-by LastEventTime \
    --descending \
    --max-items 1 \
    | jq -r '.logStreams[] | .logStreamName') \
  --query "events[].[message]" \
  --output text

最新のストリーム1つだけを取得したいので、最新のイベント順に並び替えて1件だけ取得し、ストリーム名をget-log-eventsAPIに渡している。雑な方法だがすぐに確認したい場合であればこれで十分かなー。

以上です。