Techfirm Cloud Architect Blog

テックファーム株式会社クラウドインフラグループのブログ

バッチサーバからはじめるサーバレスアーキテクチャ

時代はオンプレ環境から仮想サーバ環境へ移り、そして今はクラウドネイティブという言葉も生まれています。
旧来からあるシステムを運用保守されている方の中には、サーバレス化に興味はあるものの手を付けられていない方もいるかと思います。
そこで、バッチサーバがあるなら、そこのサーバレス化から検討される事をオススメいたします。

バッチサーバをサーバレス化する理由

バッチサーバをサーバレス化するのには、たとえば以下のような理由が考えられます。

  • 小規模から試しやすい
  • コスト削減
  • 耐障害性
  • セキュリティ対策等の影響範囲最小化

小規模から試しやすい

Webアプリケーションは1機能ずつ切り出すことは難しいですが、バッチは1機能ごとに独立して動作する場合が多いと思います。
対象機能が限定されるのでテスト範囲も限られ、小規模なチームでサーバレス化作業を実施しやすくなります。 簡単なバッチのサーバレス化を行うことでDockerコンテナのノウハウ等を蓄積できれば、その後のサーバレス化の工程を進めやすくなるのではないでしょうか。

コスト削減

サーバレス化するには少なからず人が動いてコストがかかります。コストをかける以上、何かしらの効果が見込まれないといけません。
効果としてもっとも説明しやすいものの1つはランニングコストの削減です。

たとえばWebサーバをEC2からFargate構成にした場合、24時間365日動いているEC2サーバがFargateに置き換わるだけです。
WebサーバをFargateにすることには他にもメリットはありますが、コスト的なインパクトはバッチより少ないと言えます。
WebアプリケーションをLambdaにしてリクエストが来た時だけ動く形にもできますが、今度は開発コストがかかりすぎます。

対してバッチサーバは、完全に停止すことが可能です。
たとえば、以下のシナリオを考えます。

バッチ タイミング 平均実行時間 利用メモリ 実行時間/日
Batch1 15分間隔 5分 1GB (96回×5分)480分
Batch2 毎日0:00 30分 2GB 30分
Batch3 毎日1:00 60分 4GB 60分

この場合、Batch1とBatch3が並行稼働してメモリを最大5GB使えるサーバが必要です。
Batch1が他のBatchと並行稼働される6回分を引いた90回×5分=450分、Batch1が単独実行されます。
Batch2とBatch3の実行時間(計90分)を合わせて540分(9時間)がサーバ上で何かしらバッチが動いている時間になります。
Webサーバのようにリクエストを待ち構えているわけではないため、上記の実行時間以外の時間(15時間)はcrond等がバッチ起動タイミングを管理しているだけの時間です。

このバッチサーバを完全にFargate環境に移行した場合、バッチ実行時間480分+30分+60分の570分が起動時間になります。
しかも、それぞれのバッチに必要なスペックのものを起動すればいいだけです。
以下のようなスペックになります。『×時間』とある列はリソースに1日あたりの利用時間をかけたものです。

バッチ vCPU Mem vCPU×時間 Mem×時間
Batch1 0.5 1GB 4 8
Batch2 1 2GB 0.5 1
Batch3 1 4GB 1 4

1日あたり5.5vCPU時間とメモリ13GB時間が必要で、年間で2,007.5vCPU時間とメモリ4,745GB時間になります。
SavingsPlanで1年全額前払いにすると、CPUが約79USD、メモリが約20USD、合計で100USD弱です。 オンデマンドでも、CPUが年間約101USDでメモリが年間約26USDで130USD弱です。

対してEC2サーバでバッチサーバを用意する場合、m6i.large(2vCPU、Mem8GB)のEC2サーバ1台を1年全額前払いした場合でも672USDなので、金額としては2割以下になります。
バッチサーバを冗長化した場合や同じ環境を複数置く場合等はさらにランニングコストの差が開くことになります。

耐障害性

上記の例だと削減額が作業コストに見合わないという事も多いかと思います。
次のメリットとして耐障害性を考えます。

2台のバッチサーバを用意したところでどうやって切り替えるか? ジョブ管理システムを導入するか?
ロードバランサで振り分けて終わりではないバッチサーバの冗長構成は面倒な問題です。

対して、クラウドのマネージドサービスはクラウド内ですでに冗長構成が取られています。
冗長化されたサービスがバッチの起動をトリガーし、その時に起動可能な環境で起動されます。失敗時の再試行も設定できます。
つまり、何も考えずとも最低限の冗長構成となり、さらに再試行の設定等により耐障害性を高めることもできます。

セキュリティ対策等の影響範囲最小化

サーバレス化するとバッチ1つずつ独立して動くようになります。
これにより、ライブラリや実行環境のバージョンアップや設定変更をバラバラに実施可能です。 影響の少ないバッチへの対策をより早く実施する事が可能ですし、1つのバッチの改善のために全体をテストする必要も無くなります。

利用するサービスの選択

バッチサーバのサーバレス化にあたって、AWSのどのサービスを利用するか?
『トリガー』『実行制御』『実行環境』の3観点から主要なサービスを紹介します。(この観点はAWS等が定義しているものではありません)

サーバレスバッチ基本構成

以下のようなイメージです。

オンプレでの例 概説
トリガー cron、SystemdのTimerユニット バッチの起動タイミングを管理する
実行制御 起動スクリプト、SystemdのServiceユニット エラー時の再試行等を管理する
実行環境 -(サーバ自体) 実際にバッチが動作する

バッチの特性や移行要件によって何で実行するか(実装するか)を決め、その後に再試行や実行順序に関するサービスを決めるのが良いかと思います。
そのため、『実行環境』『実行制御』『トリガー』の順に説明します。

実行環境

サーバレスでは、主にコンテナかLambdaとしてアプリケーションを実行します。

ここでのコンテナはDockerコンテナを指します。
Dockerコンテナは他の仮想化技術と利用方法が違い、1コンテナに対して1コマンド(1ミドルウェア)しか実行しないのが基本です。

バッチをサーバレス化する時は、バッチプログラムを含むDockerイメージを作成してAWS上で実行できるようにします。
Linuxで動作しているプログラムはそのままDockerコンテナで動作するため必要な改修は少ない場合が多いです。(注意点は後述します)

Amazon ECSは上記のコンテナの実行を管理するサービスです。
実際に動作するのはEC2サーバ上またはAWS Fargate上になります。(データプレーンと呼ばれます)
Fargateではコンテナの定義に合わせてリソースが確保され、利用者側でサーバを意識する必要はありません。起動するコンテナのスペックだけ意識します。

短時間(15分以内)で完了する軽量なバッチはAWS Lambdaにする方法もあります。
コンテナではミドルウェア等も含めたコンテナイメージから起動するのに対し、Lambdaはコードのみデプロイします。 Lambdaの仕様に従ったハンドラー関数を作成しておき、実行時はその関数が呼び出されるかたちになります。コマンド実行しているバッチをそのまま動かすことはできません。
そのため、既存のバッチをLambdaで動かす場合は少なからずプログラムの改修が必要になります。

実行制御

ここで言う実行制御は以下のようなもの想定しています。

  • 起動失敗時の再実行
  • エラー終了時の再実行や後処理
  • バッチ終了時の後続バッチの実行

ここでは、AWS BatchとStep Functionsを紹介します。

AWS BatchはECSコンテナとしてバッチプログラムを実行するサービスです。
ECSの細かい設定等を実施せずにジョブを定義して実行できます。エラー時の再試行も自動化が可能です。
ログ出力の細かい設定やエラー時に後処理用のバッチを起動するような細かい制御はできません。
AWS Batchのジョブ自体を後述するStep Functionsから実行することも可能です。

AWS Step Functionsでは、複数サービスのタスクをステートマシン(一連のワークフロー)として定義します。
ステートマシンからLambdaやECS/Fargateを直接呼び出すこともできますし、AWS Batchを呼び出すこともできます。
Step Functionsを利用すれば、複数のバッチを一連の流れとして実行可能です。同時に、再試行の定義をするだけでなくエラー時に実行するバッチを定義可能です。
後から前処理を前判定が必要になった場合も、バッチ自体を変更せずに新しいLambdaを既存バッチの前に実行するようにできます。

トリガー

Amazon EventBridge(旧CloudWatch Events)を利用します。
バッチ処理でもっとも多いのは定期実行かと思いますが、EventBridgeのルールでスケジュール実行が可能です。(cron式で定義できます)
これにより、cronで実行していた定期実行も簡単にAWS上で移行する事ができます。

また、AWSサービスで発生するイベントを契機にプログラム実行することもできます。たとえば、S3へのファイルアップロードやファイル削除です。
これまで定期的にファイルがアップロードされたか監視していたものも、AWSの機能によってアップロードのタイミングでプログラムを起動する事ができます。
他にもAWSサービス同士を結合できる部分は多く、独自に作りこんでいた部分をマネージドサービスに委ねることが可能です。

サーバレス化の注意点

サーバレス化するにあたって以下のような点について注意が必要です。

  • ログ出力
  • ファイルの永続化
  • 商用製品やサービスの利用
  • セキュリティ製品の利用

ログ出力

サーバで実行している時、ログはファイルへ出力しているかと思います。
Dockerコンテナでは、通常は標準出力と標準エラー出力に出力します。これはLambdaでも同様です。

AWS BatchやLambdaでは、標準出力や標準エラーを自動的にCloudWatch Logsへ転送します。
逆に、ローカルのファイルへ出力されたログはバッチの終了とともに破棄されるので参照できません。(次のファイル永続化の問題と同じです)
そのため、バッチのログ出力を標準出力や標準エラー出力へ変更するとともに、監視や集計等でログを参照する場合はCloudWatch Logsを参照するよう修正が必要です。

Step FunctionsからECSのタスクを起動する場合も、バッチのログ出力を標準出力や標準エラー出力に変更します。
ECSでは上と同じようにCloudWach Logsへ出力するawslogsドライバの他に、AWS FireLensという選択もあります。
こちらでは、コンテナの標準出力と標準エラー出力をFluentdやFluentBitを実行したコンテナへ転送することが可能です。
ただし、この場合でも最終的な保存先は一般的にCloudWatch LogsやS3になります。

ファイルの永続化

サーバレス環境では、実行が終了するとその環境は破棄されます。(Lambdaは再利用されるためすぐに破棄されないのですが、最終的には破棄されます)
これは、ローカルのディスクへ出力したファイルは実行終了後に消えてしまうことを意味します。
後で利用したいファイルは別のサービスに保存する必要があります。

Amazon S3は安価に利用できるオブジェクトストレージサービスです。
多くの場合はAWS SDKやAWS CLIからアクセスしますので、そのまま通常のファイルのように扱う事はできません。(HTTPで直接アクセスすることも可能です)
既存のバッチをコンテナ化する場合は、起動スクリプトを作成してバッチ実行前に必要ファイルのダウンロード、バッチ実行後に成果物をアップロードする処理を入れることで比較的簡単に対応できるかと思います。

Amazon EFSはNFSマウントが可能なストレージサービスです。
EFSはECSやLambda等の各サービスからマウントすることが可能ですので、そのままファイルシステムとしてアクセス可能です。
ただし、ローカルストレージへのアクセスと同じパフォーマンスというわけにはいきませんので、巨大ファイルへ頻繁にアクセスするようなバッチは一度ローカルストレージへファイルをコピーしておく方が良いかと思います。

商用製品やサービスの利用

前述のように環境は毎回破棄されるため、商用製品等が対応しているか確認が必要です。理論上は動作可能であってもサポートを受けられない場合もあります。
製品が動作するか、ライセンス上の問題がないか、適切なサポートを受けられるか確認が必要です。

また、MSP(運用サービス)等を利用している場合も注意が必要です。
こちらも、サービスが対応しているか、サービスの対応範囲はどうなるのか、料金設定がどのようになっているか確認が必要です。
マネージドサービスになって責任範囲や影響範囲が変わるため、場合によってはMSP(運用サービス)の利用廃止も検討します。

セキュリティ製品の利用

前述のように商用製品についてはさまざまな考慮が必要になります。その中でもセキュリティ製品は影響を受けやすいと言えます。
たとえば、コンテナやLambdaでは旧来のマルウェア対策ソフトウェアは利用できません。
サーバレスに対応した製品もありますが、これまでよりコストがかかるようになることも多いかと思います。

場合によってはセキュリティに対する考え方を変えることも必要です。
たとえば、保存されたイメージから起動されるため、改ざんされたバッチプログラムが起動する可能性は低くなります。
また、ECSにはルートリードオンリーというファイルシステム全体を読込専用にして改ざん不可能にする機能もあります。
(その上で書き込み可能なストレージを別途マウントすることも可能です)

時には、オンプレとは別にサーバレス用のセキュリティポリシー等を定義する必要が出てきます。

最後に

ここ数年で、サーバレスやクラウドネイティブという言葉は当たり前に聞くようになりました。
サーバの構築や管理の手間を軽くし、新しいシステムの導入コストを下げることもできます。スケールしやすくスモールスタートもしやすいです。
しかし、サーバレス環境にはオンプレ環境とはまったく違うノウハウが必要です。
ノウハウがないうちは新システムの導入見積りも難しいのではないかと思います。

ノウハウの蓄積には実際に使ってみるのが一番です。
そこで、今回はその1歩目としてバッチサーバのサーバレス化をオススメさせて頂きました。
みなさまがクラウドを活用するための1歩となればと思います。