AWS Batchを本番導入するにあたって考えたことアレコレ

この記事は

Speeeアドベントカレンダー2017の1日目です!よければ購読してやってください。

qiita.com

Speee DEVELOPER BLOGもよろしくね!

TL; DR

  • AWS Batchを本番導入したよ
  • 結構クセはあるが、それを補って余りあるメリット(特にスケーラビリティ)
  • 現状はAWS Batchの活かし方の知見を貯めている段階だが、今後適切な用途があれば積極的に使いたい

AWS Batchとは

一言で言うと、「ECSクラスタの薄いラッパー + ちょっと賢いJOB Queue + 賢いAutoScaling(オプション)」て感じ。

f:id:nisshiee:20171130202850p:plain

  1. ベースにあるのはECSクラスタなので、DockerImageを用意して、ジョブをDockerContainerの実行として定義して、ジョブを発行するという流れはECSと同じ。
  2. ECSはクラスタの各インスタンスが持っている計算リソースと、事前にジョブに定義された必要リソースから、実行可能なインスタンスを選んで実行してくれたが、これもほぼ同じ。ただしECSのそれを利用するのではなく、Batch独自のリソース管理になっている。
  3. 待望のジョブキュー。ECS単体だと、リソースに空きがなければジョブ発行がエラーになるので自前でリトライを組む必要があったが、Batchだとジョブキューがあるよ!
  4. 1つのクラスタに複数のジョブキューを紐付けることが可能。このとき、ジョブキューに「優先度」を設定することができるので、クラスタは優先度の高いキューに入っているジョブから処理する。
  5. 逆に、1つのジョブキューに複数のクラスタを紐付けることも可能。このとき、紐付けるクラスタは優先順に並べるように設定するので、優先順が上のクラスタに空きリソースがあればそちらを、空きがなければ次のクラスタを・・・という動きになる。
  6. ジョブ間の依存関係を定義することが可能。依存ジョブがセットされたジョブは、依存先ジョブが全て完了するまで「pending」というステータスで待たされる。
  7. キューにジョブが溜まっていたら勝手にクラスタにインスタンスが追加され、キューが空になったらインスタンスを止めてくれる(Managedクラスタと呼ぶ。これを使わずに自前でクラスタ管理することも可能)。
  8. Managedクラスタを構成するインスタンスのサイズは固定することも、複数指定することもできる。複数指定した場合は、ジョブが要求するリソースサイズにあったインスタンスサイズを選択してくれる。
  9. ManagedクラスタをSpotインスタンスで組むことも可能。この場合、入札基準を決めておくと勝手に入札してくれる。

これらの特徴だけ聞くと、ワクワクしてくるよね!
でもやっぱり僕もいろいろ試してみた結果、「向き不向きがあるなぁ」という印象なので、落ち着いてメリット/デメリットを整理してみよう。

メリット

  • ジョブの量が不定だったり変動するような環境において、ジョブ量に合わせて計算リソースを変動させるのがめっちゃ楽

    • 単純にManagedクラスタを使うだけで、必要な分だけインスタンスを立ち上げたり落としたりしてくれる
    • 以下のような構成にしておくと、Spotインスタンスによるコスト削減とOnDemandインスタンスの安定性の両方を取れる

      f:id:nisshiee:20171130203640p:plain

      • 優先度がSpotインスタンスクラスタの方が高いので、通常はSpotインスタンスで処理される
      • もしSpotインスタンスが値上がりしたら、Spotインスタンスクラスタにリソース空きが無い状態になるので、ジョブがOnDemandインスタンスクラスタに流れる
    • この構成を全てポチポチだけで作れる(一切のコード不要)

    • 後からこの構成に変更するのもポチポチだけ(無停止)でできる
      • 実際に本番環境で急遽ジョブをさばく速度を上げげたくなったので、Spotインスタンスクラスタを追加する操作をした
  • Managedクラスタが適切なサイズのインスタンスを立ててくれる機能が最高

    • 今回本番導入を決めた理由で一番大きいのがコレ
    • toBサービスだとクライアントごとに規模(売上からデータ量まで)がバラバラなの、あるある
      • 大きなクライアントの処理はメモリがたくさん要る

デメリット

※ ここに挙げたのは2017年10月時点で調査した内容です

  • 空きリソース再利用オーバーヘッドが大きい
    • ジョブが完了したら、確保していた分の計算リソースが開放されて次のジョブを受け入れられるようになる
    • このとき、ジョブの完了から次のジョブ受け入れまで、1分程度待ちが発生する30秒程度待ちが発生する*1
  • ジョブの失敗を検知する仕組みがない
    • 失敗するとジョブのステータスが「FAILED」になる
    • しかし、ジョブがFAILEDになったというイベントをトリガーにして何かをすることができない
      • CloudWatch Eventを発行して欲しい・・・・
      • (2017-12-01追記) 今はjob statusの遷移に対してCloudWatch Eventを発火してくれるみたいです(コメントthx)
    • 「ジョブが実行されずにずっと待ちになってる」という状況は検知してくれない
  • 標準出力に吐いたログが全て /aws/batch/job というロググループにまとめられる
    • stagingもproductionも全部・・・(これかなりきつい)
  • ジョブキューとは言っても、FIFOを保証されているわけではない
    • 「稀に」とかではなく、結構カジュアルに順序が入れ替わる
    • FIFOをどうしても担保したかったら、SQSのFIFOキュー(東京リージョンはよ)との組み合わせかな?
  • デフォルトで、ジョブキューが10、クラスタが5個までしか作れない
    • 制限緩和申請しても、クラスタは10までしか増やせないらしい
  • ジョブスケジューラ機能はない
    • 今、AWSのサービスでやろうと思ったら、CloudWatch Event → LambdaからBatchジョブ発行、かな・・・

導入するにあたって注意したいこと

  • ジョブの粒度に気をつける
    • キュー自体は数百ジョブ投げても正常に稼働するが、計算リソース再利用オーバーヘッドが大きすぎる
      • 例えば「数秒で終わるジョブを数百個投げる」みたいな使い方は基本無理(オーバーヘッドだけで数時間)
    • 少なくとも1ジョブあたり5分以上ぐらいの粒度になるようにジョブをまとめたい
      • 特にtoBサービスで処理量にバラツキがある場合は注意
      • ウチは、クライアント毎に先に処理量を見積もって適度にジョブをまとめる形でジョブを作っている
    • 1日1回だけやるジョブとかだったら、粒度小さくても悪くないかも
      • 勝手にクラスタ縮退してくれることによるコスト削減だけ狙える
  • ジョブ実行のリアルタイム性は担保できない
    • 数分〜30分程度は実行が遅れても構わないジョブにしか現状Fitしなそう
  • 基本は、様々な種類のジョブでクラスタを共有する形で設計するしかない
    • 10クラスタしか作れないからね・・・

というわけで

AWS Batch、まだまだ荒削りっぽいところもあって可愛いと思えるならアリ。

というか、ジョブキュー+柔軟なスケールアップ&スケールアウトを勝手にやってくれるというのは、明らかに悩ましかった(もしくは自前でなんとかするのがダルかった)問題を解決してくれる。この課題解決の価値が高い状況で、「注意したいこと」をクリアできそうなジョブがあったら、ぜひAWS Batchの利用を検討してみてほしい。

(Fargateも気になるけど・・・w)

明日は

kawakuboxより、『🐎の予想モデルを設計してみる』です。
お楽しみに!

*1:10月時点では1分かかってたのですが、re:Inventを見てもう一度試してみたら短くなってました