こんにちは。インフラエンジニアの永井(shnagai)です。
コネヒトでは、開発環境に続き、続々と本番サービスにもDockerを導入しています。 今回は、中々運用が大変なcronでスケジュール管理するような定期的なバッチ処理を、Amazon ECSのScheduledTaskを使ってDocker駆動な環境で構築した話です。 他の方法との比較やどのように実現しているのかについて紹介したいと思います。
今回対象とするバッチの種類
今回対象とするバッチ処理は、俗に言うスケジュール系のバッチ処理で、毎日00時00分や10分毎にサイクル起動等、事前に定義した時間に正確に動くことが期待されているものです。 ※ジョブキュー形式のバッチだと、AWS BatchやEBのWorkerもしくは、SQS + Cron on EC2で処理するほうがスマートかと思います。
実行方式の選定
上記要件のバッチを実現する基盤として、下記を検討しました。 基本的には、開発環境はDockerで運用しているのでその資産をうまくいかせるシンプルなアーキテクチャを求めていて、バッチの為だけに新規のインフラ環境を用意するみたいな事はしたくないという前提がありました。 ざっくりですが、pros&consをまとめていたので書いておこうと思います。 ※完全に個人の主観が入っていますので、参考程度に。
Cron on EC2
pros:
- 運用方法が枯れており、安定感はあるので他の方法が取れなかったらこれ
cons:
- EC2インスタンスの運用が必要
- crontabの登録が面倒でジョブスケジューラ系のツール入れるとしても運用が。。
- 実行環境を作らなければならない (開発環境はdockerなのでそれと同じ実行環境をプロビジョニングツールなどで作らないといけない)
Lambda
pros:
- スケジュール実行は出来る
- 手軽に実装出来る
cons:
- 最大の実行時間が、300秒なので不安がある
- 利用できる実行環境(言語)が制限される。今回はPHPで書かれたプログラムを使いたかったので大変
AWS Batch
pros:
- バックエンドがECSなので、ECSの資産を流用出来る
- コンピューティングリソースは全く管理する必要はない (vCpuとメモリをジョブ定義に指定するだけなのでECSでいうクラスタ管理も不要)
cons:
- 肝心のスケジュール起動は出来ない(あくまでもジョブキュー系の処理をするためのもの)
- AWS SAにお聞きしたら、大規模データのバッチ処理みたいな使い方を想定されたサービスで、cron代わりといった用途には向かないとの事。
Amazon ECSのサービス起動しているコンテナ内にcronを定義して実行
pros:
- ECSの資産をそのまま流用出来る
- CMD句に指定する起動シェルでcrontabの登録をすればバッチ毎にコンテナを作って実行するアーキテクチャで出来そう
cons:
- デプロイ処理時に、コンテナを破棄して作り直すのでバッチ実行中にデプロイが走るとバッチが強制終了される
- バッチ全体のスケジュールを一望出来ないので、バッチ管理台帳みたいなものを用意することになりそう
Amazon ECSのScheduledTask
pros:
- cron記法でスケジュールを定義する事が出来るのでわかりやすい(裏では、cloud watch eventのルールが作られてい る)
- コンテナ起動時に、指定したDockerイメージをpullするアーキテクチャなので、デプロイ時に実行中のバッチに影響を与えることがない
- ECSクラスタとしてホストを固定出来るので、docker pull時にキャッシュが効き起動が早い(LambdaやAWS batchは毎回インスタンスが変わるのでpullにそこそこ時間がかかる)
- 既存のDockerイメージの資産を使える
- 標準出力をcloud watch logsに送ることで起動処理の失敗等を、AWSコンソールから気軽に確認出来る(デバッグに役立ちます)
cons:
特に減点になるような要素なし。
- 「枯れてない」「使ったことない」というところは少し気になりましたが、ECSの知見が溜まっておりイケるという感覚があったので採用に至りました。
しいていうなら、ScheduledTaskのAPIがなく、手動で登録しなければいけないところくらい(もしかしたら自分が探せてないだけかもしれませんが)CloudWatchEventsのAPIを使うことで登録出来ました。
バッチの組み方
Amazon ECSのScheduledTaskは減点ポイントが特になく、webで運用しているECSの資産と知見も活かせるといった理由で採用が決まりました。 ここでは、Amazon ECSのScheduledTaskで1本のバッチを動かすのに必要なものについて書こうと思います。 このバッチ環境を作るには下記の4つの要素を準備する必要があります。 ECSの用語に慣れていないと複雑に感じるかもしれませんが、一つ一つ見ていけばそんなに難しいものではありません。 ※下記のスライドで、ECSの用語について説明しているので参考にしてもらえるといいかもしれません。
Dockerを本番導入するにあたり得た知見 - Speaker Deck
クラスタ
- Dockerコンテナを動かすEC2インスタンスの集合体。冗長構成を取るためにmultiAZで2台用意しておくのが無難でしょう。
タスク定義
- バッチ実行コマンドや環境変数を定義します。(dockerコンテナの起動に必要な情報を定義するイメージ)
- バッチ一つにつき一つのタスク定義を用意します。
- CMD句にバッチの実行コマンドを記述して、一つの処理だけを実行するコンテナとして定義します。(終了時にコンテナは破棄される)
- 弊社では、バッチのタスク定義はterraformで管理していて、開発者がPRを投げるような体制を取っています。
Dockerイメージ(ECR)
- タスク定義にセットする、バッチが実際に動く実行環境のDockerイメージを用意します。(フレームワークやミドルウェアなどの実行に必要な環境とソースコードがパッケージングされたDockerイメージ)
- dockerhubでも良く、クラスタ上のホストからpull出来ればどのリポジトリでも構いません。
クラスタ内のScheduledTask
- タスク定義毎に、1つのスケジュールを用意します。
実際の設定方法は、下記の公式ドキュメントがわかりやすいのでご参照下さい。 http://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/scheduled_tasks.htmldocs.aws.amazon.com
アーキテクチャ
デプロイを絡めた全体のアーキテクチャは下記のようなイメージになります。
開発フロー
ステージング環境の例ですが、上の図の青い線の部分を説明するとざっくり下記のような流れになります。
開発環境(Docker)でバッチの開発 ↓ GithubでPR後に、masterマージで自動的にDockerビルド&ECRへデプロイ ↓ 次回のScheduledTask起動時から新規のDockerイメージ(最新のソースコード)でバッチが動く
ScheduledTaskの画面
個人的には、このScheduledTaskの画面は気に入っていてジョブスケジューラ入れずともこの画面が使えるのは中々のメリットだなと思っています。
- スケジュールがGUIで一望出来る(コメントも記入可能)
- Enable,Disableも可能
エラー通知
スケジュールバッチは基本的に、必ずその時間だったり間隔だったりで動くことが期待されているので、エラーがあったら受動的に気づける必要があります。(クリティカルなものであればあるほどリアルタイムの検知は必須になります) アプリケーションのエラーは、弊社だとSentryで取得していますが、Dockerコンテナ起動時のエラー等アプリケーション起動以前のエラーが起きた場合にも、Cloud Watch Logs + SNS + Lambdaを使ってslackにリアルタイムに通知が飛ぶようにしています。 こうすることで、バッチが起動しなかった際は確実に気づける体制を作っています。
Lambdaは初めて触ったのですが、BluePrintという便利なテンプレートが用意されていたので比較的苦労なく実現できました。 コンテナで意図しない標準出力/標準エラー出力があると、下記のようにslackに通知が来ます。
※cloud watch logsにログを送るためには、タスク定義に下記のような設定が必要です。(dockerのログドライバを使う) ※事前にロググループを作成しておかないとコンテナ起動に失敗するので注意が必要です。
これは出来ない
- ジョブスケジューラにあるようなジョブチェーンの実行やリトライは出来ないです。(出来るようになるとうれしい)
- リトライに関しては、エラー通知トリガでRunTaskを実行させてあげるのが今のところ一番よさそう。(そこまではやってなくて今のところはリランしたい場合は、手動でのタスク実行を行います)
http://docs.aws.amazon.com/AmazonECS/latest/APIReference//API_RunTask.htmldocs.aws.amazon.com
まとめ
今回は、運用が辛くなりがちなスケジュールバッチ(cron)をDocker環境で動かす事例を紹介しました。 Dockerベースにすることで、各環境差異でバッチがこけるリスクを回避しつつ、ジョブスケジューラを構築せずとも、AWS ECS Task Schedulerの見やすいビューワーでバッチを一覧して管理出来るというのは中々おすすめな構成です。エラーに関しても、cloud watch logsで拾えるので、ほとんど開発や構築なしにAWSマネージドサービスにどっぷり使ってバッチの実行環境を作ることが出来ます。 今後もっといいものが出てくるかもしれませんが、Dockerでバッチジョブ実行環境としてはかなり有力な選択肢になるのではないでしょうか。 スケジュールジョブの環境をどうしようか悩んでいる方がいたら一度気軽にお試ししてもらえるといいかもしれません。
コネヒトではコンテナ周りの基盤を整備したりサービス改善を行うエンジニアを募集しています。 少しでも興味もたれた方は、是非気軽にオフィスに遊びにきていただけるとうれしいです。