AWSのECSを使った環境構築、↓この記事からの続きで、
uyamazak.hatenablog.com
Laravelの
php artisan schedule:run
を実行するためにタスクのスケジューリングを使ってみることに。
新規作成はコンソール上でなんなくできたものの、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
これでサービスやタスク定義の更新後に、スケジュールしたタスクの定義も更新することができました。
ただ常に最新になってしまうので、もしかしたら問題が起きるときもあるかも?(ロールバックとか)