クローラー（求人取り込みシステム）のリアーキ対応について

この記事は[スタンバイ Advent Calendar 2022](https://qiita.com/advent-calendar/2022/stanby)の16日目の記事です。

# 背景
[求人検索エンジン スタンバイ](https://jp.stanby.com)には、求人情報を収集するためのシステム、通称クローラーが存在します。

システムの心臓部と言える部分で、2015年のサービス開始当初から存在する機能という事もあり、機能追加、速度改善などを繰り返す中で問題点も顕在化してきました。
それらの問題点の改善のため、2022年の上半期でリアーキをおこなったので、やった事、結果的にどういう形になったのか、簡単に共有したいと思います。

# クローラーシステムの概要と顕在化してきた問題点

まずイメージを掴むために、旧構成の簡単な構成図を掲載します。

![クローラー_旧構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/942040/3322c388-d679-d796-9847-daff9fb1bc1e.png)

中心に存在しているEC2上でクローラーが動作します。クローラーのメインの機能は勿論「求人情報の収集処理」ですが、アプリケーション上にはその他の機能も存在します。求人情報の収集処理を含めて一覧にすると、下記となります。

- 求人情報の収集処理
- 求人情報の集計などをおこなうためのバッチ処理（５種類）
- 求人情報の収集処理を自動で起動するためのスケジューリング処理
- 求人情報の検索や削除、求人情報の収集処理を手動で起動するためのAPI処理

ざっくり上記の機能が１つのアプリケーションに集中する形になっていました。

背景でも書いたように、機能追加、性能改善を実施していく中でインスタンスのスケールアップを繰り返すうち、EC2インスタンス・EBSボリューム共に、かなり大きなサイズに至っておりました。

こうした環境下では下記のような問題が想定されます。

- １つのアプリケーションで複数の機能を持つため、１つの機能が原因でアプリケーションがダウンすると全機能がダウンする
- 機能を増強するためにはスケールアップが第１の選択肢になり、いずれ頭打ちになる可能性がある
  - スケールアウトも可能ではあるが、後述するECSと比較すると作り込みが必要で、運用負荷も高いと想定される
- 機能単位での柔軟な設定や拡張が難しい

加えて、プロジェクト全体としてECS on Fargateを利用したコンテナ化を進めたい、という方針からも外れている、という事情もありました。
コンテナ化に向けた作業が中々出来なかった理由として、クローラーシステムは現状の作りとして、それなりに大きなストレーズサイズとファイルの永続化が必要となっています。
Fargateのストレージ拡張は2021年、EFSと連携した永続化は2020年に可能となるなど、比較的最近でした。

:::note info
「永続化の選択肢はEFSだけではないのでは？」という意見もあるかと思いますが、既存の作りとしてローカルディスクにファイルを永続化しており、一番簡単に永続化を実現するにはEFSが適切だと判断した、という背景があります。
ディスクに対するファイル書き込み以外の手段を取ろうとすると、比較的大き目の作り替えが必要になりそうで、それは別対応とする、という形に落ち着きました。
また、よく候補として挙がるS3は書き込み・読み込み速度を考慮して、選択肢から外しました。
:::

という事でコンテナ化に踏み切れる状況がようやく整ってきたので、2022年に着手を開始致しました。

ざっと背景はこのような形になります。次節から具体的にどう対応したのか、を記載していきます。

# 機能分割とコンテナ化

今回の対応におけるテーマとしては、下記の２点となります。

- 機能分割
- コンテナ化

１つ目の機能分割の目的として
- 機能単位でアプリケーションを分割する事で、全機能ダウンのような事故を防ぐ
- 機能単位でアプリケーションを管理する事で、設定や拡張を機能単位で柔軟に実施する

２つ目のコンテナ化については
- プロジェクトの方針
- スケールアウトを比較的簡単に実現する
- 運用負荷を削減する

などが趣旨となります。

例の如く、イメージを掴むためにリアーキ後の構成図を下記に記載します。

![クローラー_新構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/942040/2c2e9927-ce52-5562-17b5-6df3891af60a.png)

何やら登場人物が随分と増えました。
再掲ですが、クローラーの機能はざっくり下記のものが存在します。

- 求人情報の収集処理
- 求人情報の集計などをおこなうためのバッチ処理（５種類）
- 求人情報の収集処理を自動で起動するためのスケジューリング処理
- 求人情報の検索や削除、求人情報の収集処理を手動で起動するためのAPI処理

これらの機能単位でアプリケーションを分割したため、構成要素が増えている訳です。

全体を通して大きく変更したポイントとして、旧構成では１つのアプリケーション上述の機能をカバーしていたので、処理本体だけでなく、処理の進捗状況に応じたステータス管理も１つのアプリケーションでおこなっていました。

それに対して新構成ではAPIやスケジューリング、処理本体を担う機能単位でアプリケーションが分割されています。
処理本体側とは別でステータスを更新する側が存在し、処理本体側はそのステータスを検知する必要があります。

そこで、処理本体は構成図でいうところの中心に存在するRDSを定期的にポーリングして、ステータスが「実行待ち」のものを拾って処理を起動し、処理完了後にステータスを終了までコントロールします。

# スケールアウト

もう１つ、ここはコンテナ化による恩恵なのですが、コンテナは容易に増減が可能という性質を利用して、master - worker構成を採用する事により、処理スペックを柔軟に増減出来るようになっています。

:::note info
勿論、EC2でも「やろうと思えばできる」でしょうが、EC2の場合はインスタンスを立てた上でAnsible等でサーバ環境を構築し、デプロイして・・という作業が必要になります。一方、コンテナであればタスク数を増やすだけです。コンテナのメリットとして挙げられる「可搬性の高さ」が効いてくるわけです。
:::

現在、構成図でいうところの左下の「求人取り込み本体」においてmaster - worker構成を採用しており、それぞれの役割としては下記となります。

- master：ステータスをポーリングし、workerに対して求人情報収集処理を指示
- worker：求人情報収集処理を実施

クローラーのメインの機能という事もあって、収集処理はかなりパワーが必要となります。

プロジェクト全体が成長するにつれて求人数の増加、収集処理の速度アップが要求され、EC2時代はスケールアップで凌いでいました。
今後は、workerの数を増やす事によるスケールが可能になります。

求人の収集処理は１件１件独立して可能であるため、理論上、workerを増やす事によって、求人の数だけスケールする事が可能となります。

# 今回やらなかった事、今後の改善点

今後やるかどうかは未定ですが、改善ポイントとしては下記が想定されます。

- 負荷に応じて自動でworkerをスケールさせる
  - 現時点では、workerをスケールさせるためには手動の操作が必要（とは言っても、コマンド１発）
  - ただし、時間帯によって極端に取り込み量がスパイクする事はないので、必要か？と言われると微妙・・・

- EC2のスケールアップに頼った実装が残っている部分のリファクタによって、処理自体を最適化する
  - ここを改善できれば、コンテナの性能・台数を下げてコストを削減できるかもしれない（飽くまで見込みで要調査・検討）
  - 今回の対応に関しては、機能分離、コンテナ化を２大テーマとして取り組んだので、処理本体のリファクタは最小限に留めている

# まとめ

クローラーシステムのリアーキ対応について、簡単に纏めてみました。

１つのアプリケーションで複数の機能を持つ事、EC2によるスケールアップでは頭打ちになるであろう事、運用面での負荷が高い事、といった問題点に対して、下記の方針で解決を図りました。

- 機能単位でアプリケーションを分割する事で、全機能ダウンのような事故を防ぐ
- 機能単位でアプリケーションを管理する事で、設定や拡張を機能単位で柔軟に実施する
- コンテナ技術を利用する事で、コンテナを容易に増減させて柔軟にスケールアウト・インを可能にする

対応期間としては、設計から実装前の下準備も含めると約半年間の長丁場となりました。

:::note info
合間合間で、優先度高の別件対応をおこなったり、対応を進めていく中で発覚した問題点の対応など、イレギュラーな要素もありました
:::

勿論、今後も機能追加や速度改善要望は継続して出てくると思うので、そこは対処しつつもより最適な形にしていくための努力を継続しておこない、安定した求人取り込みを実現し続ける事が大切だと思います。