GAミント至上主義

Web Monomaniacal Developer.

CircleCIでAWS ECSのScheduled Taskのタスク定義を最新のリビジョンに更新する

AWSのECSを使った環境構築、↓この記事からの続きで、
uyamazak.hatenablog.com
Laravelの

php artisan schedule:run

を実行するためにタスクのスケジューリングを使ってみることに。

f:id:uyamazak:20210416180458p:plain

新規作成はコンソール上でなんなくできたものの、CircleCIからの更新方法が見つかりませんでした。


コンテナのビルドからECRへのプッシュとか、ECSのサービスの更新などはCircleCIの公式Orbがあったけど、スケジュールに関するものは見当たらない。
CircleCI Developer Hub - circleci/aws-ecs


どうやらスケジュールタスクはECSの画面にあるもののAWSの裏側ではいろいろ組み合わせて構成されてるようで、ECSのコマンドでは更新できないもよう。
shirakiya.hatenablog.com
この記事だと手で叩くことはできても、タスク定義のリビジョンは毎回異なるため、そこは別に用意する必要があった。

公式ドキュメントには新規作成だけあった。
AWS CLI を使用したスケジュールされたタスクの作成 - Amazon Elastic Container Service

個人のOrbもあったけど、ソースをみるとPython使ってたり、ちょっと無理やりな感じなので勉強も兼ねてAWS CLIでやることに。
CircleCI Developer Hub - pbrisbin/aws-ecs-scheduled-tasks




実行タイミング的には上記Orbのaws-ecs/deploy-service-updateが終わり、タスク定義が更新された後。

手で更新してみる

まずはCIでいちいちやると大変なので、自分のPCから更新が成功するか試します。AWS CLIのインストールや設定は大前提。
上記の記事のようにaws events put-targetsを使います。
基本的に指定はArnなのでECSの画面とかIAMとかで調べながら埋めていきます。

helpを見ながら叩いて、エラーで出た不足した項目を追加していったところ、最低限はこんな感じ。

aws events put-targets --rule your-rule-id --targets="[{"Id":"ターゲットのID","Arn":"クラスターのArn","RoleArn":"ロールのARN","EcsParameters":{"TaskDefinitionArn":タスク定義ののArn"`}}]"

タスク定義のArnには最新でなくとも、変わっているのが分かるように、今のスケジュールタスクの指定とは違うものを指定すればよさそう。

こんな感じの結果が返ってきて、コンソールで見て変更されてればOK

{
    "FailedEntryCount": 0,
    "FailedEntries": []
}

最新のタスク定義1件のArnを取得する

上記のコマンドがうまくいくのがわかったら、TaskDefinitionArnの部分を動的に最新の1件を取得するようにします。

タスク定義はECSの範疇なのでaws ecs list-task-definitionsで取得し、--sortと--max-itemsを使って最新1件にしぼります

$ aws ecs list-task-definitions --family-prefix タスク定義の名前 --sort DESC --max-items 1                                
{
    "taskDefinitionArns": [
        "arn:aws:ecs:{リージョン}:{アカウントID}:task-definition/タスク定義の名前:8"
    ],
    "NextToken": "tekitooooooooooo=="
}

このままだとJSON的なものなので使いにくい・・・使えなくはないけどCIでjqとかインストールするのもださい。
ということでいろいろ調べているとAWS CLIには--queryという全体で使えるオプションがあることを知りました。

AWS CLI 出力をフィルタリングする - AWS Command Line Interface

ほしいのはtaskDefinitionArnsの1つ目なので--query "taskDefinitionArns[0]"をつけます。

$ aws ecs list-task-definitions --family-prefix タスク定義の名前 --sort DESC --max-items 1   --query "taskDefinitionArns[0]"
"arn:aws:ecs:{リージョン}:{アカウントID}:task-definition/タスク定義の名前:8"

これでタスク定義のArnだけが取得できました。

あとはシェル芸なんですが、私はあまりシェル力は高くないので一旦JSON的な部分を環境変数にして使うことにしました。

最終的にこんな感じの.circleci/config.yml。
とりあえず今回環境で変わりそうなところだけparameter化したけどもっときれいにできそう。

version: 2.1
orbs:
  aws-cli: circleci/aws-cli@2.0.0
commands:
  put_targets:
    parameters:
      target_id:
        type: string
      cluster:
        type: string
      family:
        type: string
      rule:
        type: string
    steps:
      - run:
          command: |
            TARGETS="[{\"Id\":\"<< parameters.target_id >>\",\"Arn\":\"arn:aws:ecs:リージョン:アカウントID:cluster/<< parameters.cluster >>\",\"RoleArn\":\"arn:aws:iam::アカウントID:role/ecsEventsRole\",\"EcsParameters\":{\"TaskDefinitionArn\":`aws ecs list-task-definitions --family-prefix << parameters.family >> --sort DESC --max-items 1 --query "taskDefinitionArns[0]"`}}]"
            aws events put-targets --rule << parameters.rule >> --targets=$TARGETS

jobs:
  update_scheduled_task:
    executor: aws-cli/default
    steps:
      - aws-cli/setup
      - put_targets:
          cluster: hogeeee
          family: hoggeeee
          target_id: hogeeee
          rule: hogeeee


workflows:
  hogeeee-workflow-name:
    jobs:
      - update_scheduled_task:
          requires:
            - deploy-service-update-queue
          filters:
            branches:
              only:
                - stage
                - push_dockerimage_to_ecr

これでサービスやタスク定義の更新後に、スケジュールしたタスクの定義も更新することができました。
ただ常に最新になってしまうので、もしかしたら問題が起きるときもあるかも?(ロールバックとか)