AWS Fargate/Batchでスケールするなるはや動画エンコード
伏見です。
今回は弊社の一部ビデオ系プロジェクトでのFargate & Batchの使用例について書いてみたいと思います。
まず、サービスとして次のような処理の流れがあるとします。
(1) ユーザーさんが好きな動画ファイル(mp4/prores)をブラウザから複数同時アップロードする
(2) アップロードされた動画ファイルに対して、フレームレートやコーデックなど、軽い検品を行う
(3) サイズ圧縮版のmp4/サムネイル用gifを作る
(4) 生成したファイルをs3に格納しつつ、ユーザーさんにプレビュー用動画や検品結果を表示する
基本的にはこれだけの流れですが、単純にスケーリングでまず行き詰まりました。
動画のエンコードは「重い」
プロジェクト当初はEC2上で直接ffmpegを操作し、各種プレビューやサムネイルを生成していましたが、なにぶんエンコードはCPUリソース食いなので、アクセス流用が読めない環境では問題が大きすぎ、インスタンスのスケールにもそれなりに気を使うことになります。
特にProresファイルはサイズが巨大。迂闊に並列でダウンロードもできません。
加えて、重いのは基本エンコード処理だけなため、常駐インスタンスを増やしたくありません。
エンコード用SaaSであるAWS MediaConvertを使う..という選択肢もありますが、ラフに使うにはなかなかコスト面でこれも辛いところがあります。
弊社では、解決策としてAWS BatchとFargateというコンテナ実行環境を採用しました。
これらを併用して「安く」「管理コストも低く」「なるべく速く結果を返す」プレビュー生成環境を構築しています。
全体の流れ
- (1) ユーザーさんのオリジナル動画のアップロード
まず、ブラウザ上でfile api経由で動画情報を読み込み、Web Worker側に投げて分割された動画のファイルをs3にマルチパートアップロードします。 -
(2)タスクルーティングのLambda
s3側では、動画のアップロードが完了すると、Lambdaが実行され、そのファイルサイズを読み取ります。
ファイルサイズが一定以上なら、「AWS Batch側」の検品/プレビュー作成タスクを実行します。
動画が~3GB程度以下なら、「Fargate側」で検品/プレビュー作成タスクを実行します。
Fargateタスクの現在の実行数が一定以上の時も、余裕を持ってエンコード処理はBatch側に投げるようにしています。 -
(3) コンテナでのタスク実行
同リージョン上のECRにあるエンコード実行用イメージを使い、タスクが実行されます。
Fargate/Batchで処理が実行し、プレビュー動画をs3に保存しつつ、同VPC内のEC2に結果を通知します。 -
(4) エンコード結果の表示
(1)の段階でブラウザ側は自身のビデオの状態を確認するためポーリングを開始しています。
(3)が終了していればユーザー画面に結果が表示されます。
Fargate/Batch
以上の流れの通り、ffmpegによる検品やエンコードの稼働部分では、BatchやFargateなどのコンテナ環境を両方とも使用しています。
弊社では始めBatchのみを使用していましたが、後にFargateとの並行運用、という形になりました。
これはなぜなのか。
Batchの利点/欠点
Pros
- 実行環境が柔軟
Batchは自身がジョブキューを持っており、タスクを投げつけておけば勝手にEC2が立ち上がり、随時実行していきます。
一度立ち上がってしまえばdocker pullが再度走ることもなく、瞬時に次のジョブを実行していきます。
また、基本的にECS/AutoScaleのラッパーなので、好きに稼働用EC2のAMIに対しボリュームを追加しておくことができます。 -
タイムアウト
ジョブあたりのタイムアウト設定があるので、万が一ハングした時もエラーとなり、CloudWatch Eventなどで捕捉できます。
Cons
- EC2の起動時間とshutdown時間
しかし、同時にいくつものエンコード命令が出された場合、ジョブのスケジューリングと新たにEC2の立ち上がりの実行時間を待つために時間はかかります。
さらに、Batchは一度EC2が立ち上がると、全くジョブがなくても10分ほど立ち上がったままになり、その分の稼働料金がかかります。
Fargeteの利点/欠点
Pros
-
「なるはや」要件への合致
Fargateはタスク実行のたびにdocker pull
(に該当するもの)が走ってしまいますが、コンテナイメージのサイズさえ小さくしてしまえば、最短十数秒程度までコンテナの起動が短縮できます。
またそこまでイメージサイズをシビアにチューニングしなくても、BatchのスケールのようにEC2の立ち上がりを待つよりははるかに起動は速いです。 -
コスト
Fargateはタスクの終了と共にコンテナが破棄され、課金対象もその稼働時間だけとなり、コストパフォーマンスが良いです。
Cons
-
容量問題
大きな問題として、2019年1月現在、Fargateのストレージには容量制限があります。
タスクの始めに、ユーザーさんがs3にアップロードした動画をコンテナにダウンロードする作業が入ります。
この時、10GB超えの動画ファイルのケースであったりすると、この容量制限に引っかかり、タスクが失敗してしまいます。
納品形式がmp4程度の容量であれば大概Fargateで処理できますが、Proresなど非常に大きな動画ファイルの場合、この制限は致命的になります。
この辺りのFargateの制限は問い合わせの結果やフォーラムでの公式回答などでも、検討中..?らしいオーラを出しているので、強く期待したいところです。
(もちろん、フルサイズの動画をコンテナ内に一気にダウンロードせず、s3のpresignUrl越しなどで直接ffmpegに食わせ、都度ストリームを捨てる…という技もあります。
ですが、タスクの中で繰り返し同じ動画ファイルに処理をかけるなど、使い勝手の都合があり、現在はこの方式をとっています。) -
タスク実行数上限
Fargateにはタスクタイプの実行数上限があります(上限緩和申請可能)。
デフォルトは20程度なので、結構シビアです。 -
タイムアウト
設定機能がないため、この辺りはタスク実行時のコマンドで対応しています。
まとめと展望
以上の利点/欠点から、
- オリジナル動画のサイズ
- Fargateタスクの現在の実行数
を主な因子として、Fargate/Batch、どちらの環境でタスクを実行するか判定しています。
ややこしそうですが、コンテナイメージやタスク実行命令、アプリはほとんど同じものが使えるので、Lambdaで判定コードを入れるだけですぐに対応できました。
フルマネージドなコンテナ実行環境を使うことで、動画アップロード数/サイズに煩わされず、「なるはやなエンコード環境」が出来上がりました。
常駐アプリとなるとまだまだ身構えてしまうコンテナ導入ですが、バッチ処理や単一のタスク実行用途では迷わずどんどん使っていきたいな、と考えています。