はじめに
バッチの実行環境を作る場合、いろんな選択肢があります。
- ec2を立ててその上でcronを動かす。
- lambdaとcloudWatchEventの組み合わせ。一つのバッチに対して一つのlambdaがひもづくみたいな環境を作る。
- AWS batchを使う。
- ECSのサービス起動しているコンテナ内にcronを定義して実行
- ECSのScheduleTaskを使う。
それぞれのメリデメは以下の記事にまとまっており、大変参考になりました。
この記事でも書いてある通り、ECSのScheduleTaskを使うのがバッチ処理にはすごく向いていると思い、試してみました。
http://tech.connehito.com/entry/2017/09/13/171914
サンプルとして使うAPIサーバ
以下のapiを叩くと、slackに通知が行くような簡単なAPIを作りました。
コードはこちら => github
このサーバーのデプロイとスケジュール実行にECSを使っていきます。
curl ***.***.***.***/api/hoge
curl ***.***.***.***/api/sample
事前準備
ecs-cliのインストール
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ECS_CLI_installation.html
ecs-cliで利用するアカウントの設定
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/cmd-ecs-cli-configure-profile.html
あとで使うため、awsコンソール上でキーペアを作成
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/get-set-up-for-amazon-ecs.html#create-a-key-pair
クラスターの作成
$ ecs-cli configure --region ap-northeast-1 --cluster batch
INFO[0000] Saved ECS CLI cluster configuration default.
$
クラスタの起動
--keypair
には事前準備で作成しておいたキーペアのキーペア名を入れます
$ ecs-cli up --keypair ecs1 --capability-iam --instance-type t2.micro
INFO[0000] Using recommended Amazon Linux 2 AMI with ECS Agent 1.29.0 and Docker version 18.06.1-ce
INFO[0000] Created cluster cluster=batch region=ap-northeast-1
INFO[0001] Waiting for your cluster resources to be created...
INFO[0001] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS
INFO[0061] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS
INFO[0122] Cloudformation stack status stackStatus=CREATE_IN_PROGRESS
VPC created: vpc-06a96d66a28a309d5
Security Group created: sg-06963801d57d9b397
Subnet created: subnet-002a9338da5410dcf
Subnet created: subnet-0cf2a1265a7b2dae4
Cluster creation succeeded.
$
docker-compose.ymlを使ってタスク定義を作成、起動する
※docker-compose.ymlがあるdirに移動してから実行
$ ecs-cli compose up
INFO[0000] Using ECS task definition TaskDefinition="ecs-batch:4"
INFO[0001] Starting container... container=2cd79d8c-9374-4a9f-a956-453246a0f079/app
INFO[0001] Describe ECS container status container=2cd79d8c-9374-4a9f-a956-453246a0f079/app desiredStatus=RUNNING lastStatus=PENDING taskDefinition="ecs-batch:4"
INFO[0013] Started container... container=2cd79d8c-9374-4a9f-a956-453246a0f079/app desiredStatus=RUNNING lastStatus=RUNNING taskDefinition="ecs-batch:4"
$
APIサーバ自体のデプロイは完了
ここまでの手順で
curl ***.***.***.***/api/hoge
curl ***.***.***.***/api/sample
を実行するとslackに通知が飛ぶサーバーを構築することはできました。
これをECSのScheduleTaskから実行できるようにしていきます。
curlするだけのdockerを作る。
このcurlするだけのdockerイメージは docker hub
か ECR
にpushしておきます。
FROM alpine:3.7
RUN apk update && apk add curl
CMD curl --help
このイメージを何に使うかというと、このcurlをただ実行するだけのコンテナをECSのタスク定義として登録します。そしてこのイメージの CMD
を上書きする形でECSのScheduleTaskを登録していくイメージです。
ScheduleTaskを定義する際、コンテナのコマンドの上書きができます。
curl ***.***.***.***/api/hoge
, curl ***.***.***.***/api/sample
にコンテナのコマンドを上書き、それらをスケジュール登録していきます。
curl --helpを実行するタスク定義を作る
新しいタスク定義の作成 をクリック
EC2を選んで 次のステップ をクリック。(ただcurlを実行するだけで常に起動している必要がないのでFARGATEの方がいい気がする。)
コンテナの追加 を押す
コンテナ名: curl
イメージ: docker hubかECRのイメージを入力
メモリ制限: 128(埋めないと追加ボタン押せなかった)
追加をクリック
ただ curl --help
を実行するだけのタスク定義が作成される
ECSのScheduleTaskを登録していく
スケジュールルールの名前: sample
スケジュールルールの説明: sample API
スケジュール間隔は1分おき。※等間隔実行だけでなく、cron形式でも書くことができます。
ターゲットID: sample
タスク定義: 事前に作成しておいた curl
を選択
コンテナの上書き。コマンドをsampleAPIを叩くcurlに変更して作成をクリック
注意するところして、コマンドはカンマ区切りの文字列である必要があります。
コマンドのオーバーライドの場合: [Command override] に、送信するコマンドオーバーライドを入力します。コンテナ定義で ENTRYPOINT が指定されていない場合は、引用符のない文字列のカンマ区切りリストである必要があります。例:
バッチの実行確認
上と同じ感じで curl ***.***.***.***/api/hoge
の方のScheduleTaskも登録します。
※hogeの方は5分間隔で作成しました。
slackを確認するとこの二つのバッチが指定した間隔通りに定期実行されていることがわかります。
最後に
ECSのタスクのスケジューリングの機能を使えば、awsの画面上からバッチの一覧を確認できますし、特定のバッチの有効/無効も画面上でぽちぽちするだけで完結します。
また、スケジュール系のバッチサーバはドメインロジックに深く関わってくる、落ちたらやばいサーバーとなることがほとんどだと思います。
node.jsで作ったサーバー(expressとか)などの場合、pm2を用いて常時サーバーが起動していることの担保をしたりしますが、たまにpm2自体が音もなく死んでいることがありました。
ECSを使う場合、サービスを定義すれば常時何個のタスク(コンテナの集合体)を起動しておくかを定義できるため、pm2からも解放される気がしてます。