施工管理サービスの定期実行ジョブ処理基盤をEC2からコンテナへ移行しました

施工管理サービスの定期実行ジョブ処理基盤をEC2からコンテナへ移行しました|ANDPAD Advent Calendar 2022

この記事はANDPAD Advent Calendar 2022の 23 日目の記事です。

こんにちは、アンドパッドSWEの あかりです。ニックネームではなく本名です。このひらがな3文字にはネット上では優しくされたいという願望が込められていますw

1. 概要

最近、アンドパッドで最も古くから稼働している施工管理サービス内で定期実行されていた非同期処理(以後、定期実行ジョブとする)の実行基盤を、EC2からコンテナに移行しました。 この記事では、その移行方法について説明します。

2. 前提の説明

説明の前提として、施工管理サービスのインフラ概要図を下図に示します。左がコンテナ移行前の概要図で、右がコンテナ移行後の概要図になります。

インフラ概要

元々、施工管理サービスは3種類のサーバで構成されており、それぞれ以下のような役割を担っていました。

サーバ 説明
frontサーバ ユーザーからのリクエストを同期的に処理するサーバ
backサーバ frontサーバからエンキューされた非同期ジョブを実行するサーバ
batchサーバ 定期実行ジョブをエンキューし、それを処理するサーバ1

どうしてbatchサーバなるEC2インスタンスを含む構成になっていたかと言うと、元々EC2上で稼働していた施工管理サービスをEKSに完全移行する道半ばだったからです2。batchサーバもコンテナへ移行するつもりだったのですが、後述するコンテナへの移植を妨げるロジックが存在しており、取り残されている状態でした。

3. コンテナ移行後の目標構成

定期実行ジョブ処理基盤のコンテナ移行によって、以下のような構成に変更することを目標としました。(上図の右)

説明
変更前 batchサーバから定期実行ジョブをエンキューし、batchサーバ上でジョブを実行する
変更後 schedulerサーバから定期実行ジョブをエンキューし、backサーバ上でジョブを処理する

目標とする構成のポイントは、定期実行ジョブのスケジューリングをbackサーバではなく、新規に作成したschedulerサーバに担わせたことです。施工管理サービスでは、非同期処理の実装にSidekiq、定期実行ジョブの実装にsidekiq-schedulerを利用しているので、backサーバから定期実行ジョブをエンキューして、それをbackサーバが処理する構成を取ることも可能でした。ですが、今回はあえてそうせずに、定期実行ジョブのスケジューリングを行う専用のschedulerサーバを立ち上げています。と言うのも、backサーバで定期実行ジョブをスケジューリングすると、スケジューリング競合が発生し、定期実行ジョブが重複してエンキューされてしまう懸念があったからです。

sidekiq-schedulerは、通常の場合、複数のホスト起動時にもスケジュールされる定期実行ジョブの唯一性をサポートしています。ですが、スケジュールするジョブの唯一性が保証できなくなる場合があり、例えば、100個ほどの多数のジョブが同時にエンキューされる状況が、それに該当します。backサーバは、現時点でも、ピーク時に100台以上にまでオートスケールしており、将来的にも増加傾向にあります。それを鑑みるに、backサーバで定期実行ジョブをスケジュールさせた場合、将来に渡って、スケジュールされるジョブの唯一性を保証することは困難であると予想されました。したがって、定期実行ジョブをスケジュールする機能を別のサーバ(= schedulerサーバ)に切り出し、このschedulerサーバをEKSのワーカーノードが稼働するAZごとに1台ずつ常駐させる3ことで、高い可用性を担保しつつ、スケジュールされるジョブの唯一性も保証することとしました。

4. コンテナ移行の手順

目標とする構成が定まったので、次に移行手順について検討しました。検討の結果、定期実行ジョブを停止することなく、処理基盤をコンテナへ移行するために、下図に示すような段階を踏んでリリースを行うこととしました。

コンテナ移行の手順

説明
コンテナ移行前 batchサーバから定期実行ジョブをエンキューし、それをbatchサーバで処理する。
1ステップ目 エンキューされた定期実行ジョブを、backサーバで処理するように変更する。
2ステップ目 定期実行ジョブをエンキューする専用のschedulerサーバを起動し、batchサーバとschedulerサーバとで定期実行ジョブのスケジューリングを行うように変更する。
コンテナ移行後 batchサーバを削除4し、schedulerサーバのみで定期実行ジョブのスケジューリングを行うように変更する。

5. コンテナ移行作業

コンテナ移行の目標構成とその移行手順が定まったので、あとは移行作業に着手するのみです。 移行作業をするにあたり、大きく分けて2つの課題があり、これを順に解決していきました5

  1. ジョブの唯一性の保証方法を「ジョブの実行状態をローカルのファイルで管理するロジック」から「ジョブの実行状態をデータベースのカラムで管理するロジック」へ変更すること

  2. 複数のschedulerサーバが起動している場合でも、定期実行ジョブのスケジューリングが唯一になるよう、スケジュールタイプを変更すること

一つ目については、そもそも定期実行ジョブのコンテナ化を先送りにしていた一番の課題になります。「ジョブの実行状態をローカルのファイルで管理するロジック」と言うのは、ジョブが単一のホストで処理されることを前提にした実装であり、分散クラスタであるbackサーバとは相性の悪いロジックになります。というのも、backサーバのような分散クラスタの場合には、実際にどのサーバでジョブが実行されるのかがあらかじめ決まっていないからです。この意味で、定期実行ジョブの実装には、アプリケーションとしてのポータブル性が欠落していました。ですので、「ジョブの実行状態をローカルのファイルで管理するロジック」を修正し、ジョブの実行状態をデータベースのカラムに管理させることで、どのサーバでジョブを実行しても同じ処理が行われる実装にしました。

また、二つ目についてですが、複数のスケジューリングホストが稼働するようになったので、その場合にも定期実行ジョブの唯一性が保証されるように、ジョブのスケジュールタイプを変更する必要がありました。というのも、元々の設定ファイルでは、everyintervalなど、時間間隔を指定するスケジュールタイプが利用されていたので、このままでは複数あるschedulerサーバが協調動作できず、定期実行ジョブの唯一性を保証することができませんでした。そこで、スケジュールタイプとしてcronを利用し、ジョブをスケジュールする時刻を指定することで、複数のスケジューリングホストが存在する場合にも定期実行ジョブの唯一性が保証される6ように、修正しました。

6. まとめ

本記事では、アンドパッドで最も古くから稼働している施工管理サービスに関して、定期実行ジョブの処理基盤をコンテナ移行した話を説明しました。電子面からでは伝わりにくいですが、4年以上前に実装された当時のまま残されていた技術的負債と文字通り「格闘」したので、苦労の多いプロジェクトでしたw

今回、記事の都合上説明を省略しましたが、まだコンテナ移行できずに残っているEC2インスタンスが存在していますので、これらにも引き続き対処していき、また達成次第でやったことをテックブログにまとめていこうと思います!

最後に

アンドパッドでは、様々な技術要素を活用して開発に取り組んでいます。 一緒に開発、挑戦していただける方を大募集しておりますので興味のある方は下記をご覧ください!

engineer.andpad.co.jp

hrmos.co

明日は非同期処理の基盤をBlue/Greenデプロイ化した話を公開しようと思います!執筆者は明日も私、あかりです。お楽しみに!


  1. 実際には他の役割も担っているのですが、主題とは無関係なので説明を省略しています。
  2. frontサーバとbackサーバをEC2からEKSへ移植する話は以下の記事をご参照ください。tech.andpad.co.jp
  3. Pod間アンティアフィニティを利用して、これを実現しています。
  4. 本記事の主題とは関係のない事情があり、実際にはbatchサーバのSidekiqプロセスを停止したのみです。
  5. 説明の都合上省略していますが、wheneverというgemを利用してOSのcronジョブとして実行されていた処理をsidekiq-schedulerへ移植することを最初に行いました。
  6. 詳細の説明はsidekiq-schedulerの公式ドキュメントをご参照ください。