サーバで動かしているバッチをFargateへ移行しています

こんにちは!インフラユニットの小林です。

今回はバッチサーバで動いているバッチをECS(Fargate)に移行する話を書きます。

抱えていた課題

SEO分析ツール【TACT SEO】 はAWS上にシステムを構築しており、APサーバとバッチサーバはEC2インスタンスを使用しています。

今後のサービス拡大を見据えた場合に可用性と費用面において、バッチに以下の課題を抱えていました。

バッチシステムの可用性

サーバ2台で構成されていて、バッチを実行するサーバとJOB管理をするサーバに分かれていますがどちらも1台構成で、SPOFが2つもある構成になっていました。

EC2インスタンスの利用効率

40程度のバッチが登録されていて、1番メモリを消費するバッチは10GB以上のメモリを消費し、そのほかのバッチは多くても2GB程度のメモリ消費量でした。

1番大きいバッチは1日に1回しか動かないのですが、そのために大きめにインスタンスを用意する必要があり、インスタンス費用を考えると費用対効果が低い状態になっていました

検討した方法

以下の方法を検討しました。

サーバ多重化

単純にバッチサーバを多重化する方法です。JOB管理サーバに障害が起きた場合は手動でキックする方法を前提としています。

ただし、バッチサーバで障害が起きた場合のバッチのリラン方法が煩雑になる、コストが増額になる、といった理由で見送りになりました。

コンテナ化

コンテナ化、今回はFargateを選択しました。 aws.amazon.com

Fargateは、EC2インスタンス不要のマネージドなコンテナサービスです。 コンテナの実行基盤をAWSが管理してくれ、オンデマンドでコンテナを実行できます。

ECSでのコンテナ実行形式には『タスク』と『サービス』があり、『タスク』はコンテナをシンプル実行する仕組みで、コンテナに割り当てるCPUやメモリを設定し、 docker run コマンドを実行するようなイメージです。手動でもスケジュール設定でも起動できます。『サービス』はWebサービスのような、常時起動が必要なコンテナに適してた実行形式です。内部的にはELBやAuto Scaling グループと紐づいています。例えば『Apacheコンテナを常時2個起動』のようにサービスを設定すると、2つコンテナが起動した状態を保つように動作してくれます。

タスクをスケジュール実行することで、定時バッチを動かせそうです。

バッチ(コンテナ)の実行基盤と、バッチのタスクをコントロールする機能をマネージドにできそうです。

選ばれたのはコンテナ化でした

以下のメリットを享受できます。

メリット

  • ECS(Fargate)のタスクは、cron形式またはn時間おきといった方法でバッチを実行できるので、JOB管理サーバも不要になる。
  • ECSのコンテナ実行基盤はEC2とFargateの2種類があるが、Fargateを使うことでEC2インスタンスの管理が不要になり、管理工数を削減できる。
  • Fargateの課金体系は、vCPU およびメモリリソースに基づいて計算され、最も近い秒数に切り上げられます。1バッチ=1コンテナにし、メモリを適切に設定することでEC2インスタンスを常時起動しておくよりも費用を削減できる。

心配の種は我々の経験不足です。開発環境はコンテナを使って構築していましたが、コンテナの本番運用に関する知見が足りていませんでした。テスト/ステージング環境での十分な検証が必要となります。

Fargate移行後の構成

サーバとは異なることが多く、特にデプロイと監視は以下の方法で行うことにしました。

デプロイ

今まではデプロイサーバからCapistranoでデプロイしていましたが、Fargate移行後は、普段からリポジトリとして使用しているBitbucket、BitbucketPipelineと組み合わせて、以下のようにしました。

f:id:kobayashi-ryotaro:20200204173723p:plain
アプリケーションのデプロイイメージ

コンテナの実行は「Laravel artisanコマンド」で実行しています。サーバの時は以下のコマンドをバッチサーバで実行するように、JOB管理サーバに登録していました。

artisan <バッチ名>

artisan <バッチ名> をコンテナの起動時に実行するとバッチを起動できますが、コンテナイメージ内にバッチ名をコーディングしてしまうと、1バッチ=1コンテナになり40~50のコンテナイメージを作成管理しなければなりません。

そこで、今回はバッチ名を環境変数に設定し、それぞれ別のFargateタスクとすることで、1つのイメージで複数種類のバッチを実行できるようにしました。

監視

バッチシステムはサーバのログをZABBIXで管理していましたが、FargateのログドライバーはCloudWatchLogsとSplunkの2種類です。Splunkは使っておらず、ZABBIXはKinesisやLambdaを組み合わせてログを転送すれば監視可能ですが、シンプルな構成が良かったので見送りました。

今回採用したのはCloudWachLogsにフィルターを設置し。『ERROR』などの文字列を検知するとLambdaとAmazon SNS(Simple Notification Service)を使ってSlackに送信する仕組みです。

ファイルに出力していたログを標準出力に変更する必要があり、アプリケーションの改修が必要となります。

f:id:kobayashi-ryotaro:20200204175848p:plain
実際の送信画面

メモリやCPU使用量など、バッチ全体を俯瞰で見るために、GrafanaからCloudWachのメトリクスを可視化しています。

f:id:kobayashi-ryotaro:20200204191410p:plain
GrafanaでCloudWachメトリクスを可視化

実行順序が重要なバッチ

「バッチAはバッチBが正常終了したのちに実行」のような、実行順序の制御が必要なバッチがいくつかあったので、StepFunctionsを使いました。

aws.amazon.com

jsonでのタスク定義に慣れが必要ですが、以下のように複数のタスク実行フローを定義できます。

f:id:kobayashi-ryotaro:20200204184217p:plain
定義されたタスクのレイアウト画面

ハマりどころ

開発、検証中に困ったことを紹介します。

debugしづらい

Fargateはコンテナの実行が終わると消えてしまいます。debugのためにコンテナにログインしたい場合は、一旦EC2で実行するようにしました。

スケジュール化したタスクの止め方にクセがある

スケジュール化したタスクを消すときは、おもむろに登録解除(削除)してはなりません。

以下は Amazon Elastic Container Service 開発者ガイド からの引用です。

Amazon ECS でタスク定義が不要になった場合は、タスク定義を登録解除すると、タスクの実行やサービスの更新時、そのタスク定義は ListTaskDefinition API 呼び出しやコンソールに表示されなくなります。

タスク定義は登録解除されると、すぐに INACTIVE とマークされます。INACTIVE のタスク定義を参照する既存のタスクとサービスは、中断することなく実行され続けます。

簡単に言うと、消したはずのタスクが実行され続けてしまうので、以下のようにスジュールルールを無効にしてから削除するようにしましょう。

f:id:kobayashi-ryotaro:20200204183557p:plain

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/deregister-task-definition.html

スケジュール実行した CloudWatch Event が複数起動する

cron形式でスケジュール設定したCloudWatch Event でいくつかのバッチを実行しているのですが、重複して実行される事象を確認しました。12時に1回だけ実行するようにスケジュールしても、複数回実行されしまいます。

毎回ではなくレアな事象です。設定どおり1回だけ実行する時もあれば2,3回実行されてしまう時もあります。

以下は CloudWatch イベント のトラブルシューティング からの引用です。

まれに、同じルールが単一のイベントまたはスケジュールされた時間に対して複数回トリガーされるか、特定のトリガーされたルールに対して同じターゲットが複数回呼び出されることがあります。

これは現在対応方法検討中の案件で、別途JOB管理コンテナをECSのサービスとして実行すること等を検討しています。

今後

今現在、我々はクラウドネイティブへと舵を切った状態です。

クラウドネイティブとは

クラウドネイティブとは何でしょう。クラウド、コンテナ、Kubernates、サーバレスアーキテクチャ、SRE、DevOpsなど様々な用語が飛び交っていますが、何をすればクラウドネイティブなのでしょうか。

我々はクラウドネイティブを『クラウドの力(機能)を使って無駄のなく生産性が高い、競合優位性の高い組織を作る事』と考えています。

こちらのスライドが非常にわかりやすいので、是非参考にしてください。

speakerdeck.com

今年の9月にクラウドネイティブに関する大規模カンファレンス「CloudNative Days Tokyo 2020」が予定されており、クラウドネイティブ界隈は今年も目が離せません。

私も実行委員の一人として準備を始めています。

medium.com

現在、今年開催のCloudNative Days Tokyo 2020にてより良いコンテンツをお届けするため、事前アンケートを実施しています。

皆様のご意見やご要望を反映したコンテンツ作りの実現のため、忌憚のないご意見をお待ちしております。 【締切:2/29】

t.co

今回紹介したFargateへの移行はまだ途中ですが、すでに月額数百ドルの経費削減、バッチの安定稼働、運用負荷の軽減など、様々な効果が出てきています。

今後も我々のクラウドネイティブジャーニーに関する取り組みを紹介していきたいと思います。

最後までお読みいただき有難うございました。