Event Bridge Schedulerを使ってECRからpullしたイメージをECSで定期実行する設定方法をご紹介します
ECS周りの設定は既に済んでるものと仮定しての説明となります点ご了承ください
ECSで実行するアプリケーションはSpring BatchでApplicationRunnerを起動するものとなっております(後述しますが、実行するバッチ種類を指定するためにコンテナに対してパラメータを付与します)
早速設定してみる
スケジュール名と説明
このあたりは機能に直接影響があるものではないので、わかりやすい識別子を設定して次へ行きます
スケジュールのパターン
毎日0時に起動するよう設定していきたいと思います
cron式はUNIX系OSお馴染みのものです!
具体的に設定できる値についてはawsの方でもわかりやすくドキュメント化してくださってるのでこちらを参照
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/events/ScheduledEvents.html
時間枠
ターゲットの詳細
次画面に遷移すると色々なAWSサービスの色々な操作を選択できる画面が表示されます
今回は既に存在するECSクラスタ、タスク定義を使ってECSタスクを実行する目的なので「RunTask」を選択します
RunTask
RunTaskに必要なさまざまな設定をしていきます
ECSクラスターとECSタスク定義をここで選択します
プルダウン展開で選択候補値をだしてくれます
コンピューティングオプション選択は注意が必要です
- プラットフォームのバージョン
- サブネット
- セキュリティグループ
はそれぞれ手入力が必要です(*2023/04/04現在)
プラットフォームのバージョンは「LATEST」で良いと思います
他は各サービスメニューからサブネットID、セキュリティグループIDをコピーして貼り付けましょう(他のサービスはこの辺の選択がプルダウンになっているのでそのうち対応されると筆者は思ってます)
また、自動割り当てパブリックIPは「有効」にしてください
しないとこちらのエラーが発生します・・! ECRからpullできない?
デフォルトからの変更点は「実行コマンドを有効化」している点です
今回は起動するバッチの種類をアプリケーションに対して引数で渡したいため、コマンドの上書きを行います
はまったポイント
{
"containerOverrides": [
{
"name": "batch",
"command": [
"--batch.execute=sample"
]
}
]
}
スケジュールの状態 / 再試行ポリシーとデッドレターキュー
本タスクは24時間に1度起動なので、イベント最大経過時間はデフォルトのままにしました
再試行回数はデフォルト185回ですが、失敗するパターンとすればプログラムやデータに何らかの考慮不足があるときなので3回程度としました(もちろん機能が違えば失敗するパターンも違ってくるので応じて判断していただければと思います)
暗号化 / アクセス許可
これらはデフォルトのままとしました
実行ロールについては TODO
設定完了
まず、起動成功した or エラーってどう見るの
起動できているかどうかの確認は、CloudTailから確認できそうです
イベント時刻をみると15分おきにRunTaskが実行されているのが確認できます
エラーの場合
エラーコードの箇所にエラーの種類が表示されます(今回サンプルで示しているケースだと「InvalidParameterException)
詳細を確認するには、画面下部のイベントレコードを確認します
InvalidParameterExceptionで検索してみてください
エラーを発生させるために、RunTask > コンピューティングオプション > 起動タイプ を未指定にしてみました
成功の場合
設定中はまったこと
コマンドの上書き指示でエラー
{
"containerOverrides": [
{
"command": [
"--batch.execute=sample"
]
}
]
}
構築初期、調べに調べて捻り出したコマンド上書きの設定値がこちらで、正しいものと比べるとnameが欠落してました
コマンドの上書き指示になにを渡したら良いかが当時参考文献少なく、結構ハマりました
こちらのドキュメントを参照することで何を設定したら良いか、なにが必須なのかがわかりました!
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/APIReference/API_ContainerOverride.html
~~ 略 ~~
"errorCode": "InvalidParameterException",
"errorMessage": "name cannot be blank.",
"requestParameters": {
"platformVersion": "LATEST",
"overrides": {
"containerOverrides": [
{
"command": [
"--batch.execute=sample"
]
}
]
},
~~ 略 ~~
because no identity-based policy allows the ecs:RunTask action
"errorMessage": "User: arn:aws:sts::xxx:assumed-role/Amazon_EventBridge_Scheduler_ECS_yyy/zzz is not authorized to perform: ecs:RunTask on resource: arn:aws:ecs:ap-northeast-1:xxx:task-definition/sample-def because no identity-based policy allows the ecs:RunTask action"
※ 2023/04/04現在 自動生成の実行ロールを指定している場合はこのエラーは発生しません
本記事では触れてませんが、実運用ではアクセス許可に設定する実行ロールを「既存のロールを使用」して独自で定義したポリシーをアタッチしてました。
また、その実行ロールを複数のスケジュールで共有しておりました。
上記の条件で以下のようなポリシーを定義している場合はエラーが発生しますので注意です
悪い例
Resourceに指定するECSタスク定義に「:*」の指定がある
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:RunTask"
],
"Resource": [
"arn:aws:ecs:ap-northeast-1:xxxx:task-definition/development-batch-def:*"
],
"Condition": {
"ArnLike": {
"ecs:cluster": "arn:aws:ecs:ap-northeast-1:xxxx:cluster/development"
}
}
},
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": [
"*"
],
"Condition": {
"StringLike": {
"iam:PassedToService": "ecs-tasks.amazonaws.com"
}
}
}
]
}
良い例
Resourceに指定するECSタスク定義ARNに「:*」の指定がない
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:RunTask"
],
"Resource": [
"arn:aws:ecs:ap-northeast-1:xxxx:task-definition/development-batch-def"
],
"Condition": {
"ArnLike": {
"ecs:cluster": "arn:aws:ecs:ap-northeast-1:xxxx:cluster/development"
}
}
},
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": [
"*"
],
"Condition": {
"StringLike": {
"iam:PassedToService": "ecs-tasks.amazonaws.com"
}
}
}
]
}
~~ 略 ~~
"errorCode": "AccessDenied",
"errorMessage": "User: arn:aws:sts::894229353696:assumed-role/Amazon_EventBridge_Scheduler_ECS_8840c322a4/xxxx is not authorized to perform: ecs:RunTask on resource: arn:aws:ecs:ap-northeast-1:894229353696:task-definition/development-batch-def because no identity-based policy allows the ecs:RunTask action",
"requestParameters": null,
"responseElements": null,
~~ 略 ~~