ソフトウェアエンジニアの 鈴木 (@szk3) です。
先日、カミナシにおいて古くから存在する1つの機能をリアーキテクティングしました。
その結果、処理時間は4分の1以下、コストは90%程度削減 と大きな成果を出すことができました👏
本記事では、その機能が抱えていた課題に対しどのような改善のアプローチをして上記の結果に結びついたのか?について共有します。
Excel変換とは
今回、リアーキテクティングの対象となった機能は、カミナシに帳票として記録されたデータをExcel形式に変換して出力する機能です。
これを、”Excel変換” と呼んでいます。
Excel変換は、カミナシのサービスの中でも比較的古くから存在する機能です。
ここ数年での利用ユーザーの増加と共に、設計当初のシステムアーキテクチャが技術的な負債となっている状態でした。
Excel変換の課題
まず最初に、設計当初のアーキテクチャを紹介します。
Excel変換は一般的なバッチ処理として実装されています。
Amazon EventBridgeから定期的にAWS CodeBuild を呼び出し、CodeBuild内でDokcerイメージを実行するようなアーキテクチャになっており、アプリケーションは Golangで実装されておりgoroutineで変換処理を並列化していました。
正直な話をすると、当時の技術選定について知る方がおらず、このアーキテクチャの意図や背景はよくわかっていませんでした。
問題1 タイムアウト問題
このアーキテクチャで最初に起きた問題は、CodeBuildがタイムアウトするという問題でした。
1回のバッチで処理する件数は利用者側の設定に依存するため、利用者のニーズにあわせて変動します。
そのため月末月初の特定時間帯のような変換ニーズが多くなりやすいタイミングにおいて処理時間が長くなり、当時設定していたCodeBuildのビルド処理がタイムアウトすることがありました。
このタイムアウトが起きると、未処理の変換処理の洗い出しやリカバリに対して、エンジニアが丸一日つきっきりになるような状態でした。
具体的には、クラウド側でリトライすることを前提としたコードやシステムアーキテクチャになっていなかったため、エンジニアがローカルのコードを書き換えて直接本番データベースに接続し、CodeBuild 上でタイムアウトによって失敗した処理と同等の処理をローカル環境で実行する必要がありました。
当時の課題感として、発生頻度は少ないもののリカバリに対する工数を重要な課題として捉え、この問題への対応が意思決定されました。
しかし、当時のチームリソースで大きなシステム変更を加える余力はなく、一旦タイムアウトや割り当てリソース(vCPU、メモリ)設定をCodeBuildが許容する限界値にスケールアップすることで、タイムアウト問題を回避している状態でした。
問題2 スケールアップの限界
一旦、限界までスケールアップすることで一時的にはタイムアウト問題は解消しました。
しかし、予測が難しい負荷を考慮し上限までスケールアップした💵札束で殴るアーキテクチャは無駄なコストがかかるのと、それ以上のスケールアップの余地がないため、不確実性が残り続けます。
Excel変換機能の利用ユーザー数は増加する傾向にあり、近い将来にこの状態では解決できなくなるのは時間の問題で、スケールアウトできるシステムアーキテクチャの検討が急務になりました。
また、大きなマシンリソースが必要な変換はほんの一部であり、大半が過剰なリソースでの実行になっていたため、コストの最適化も同時に検討する必要がありました。
問題3 リカバリ負荷が高すぎる
問題1でも軽く触れましたが、以前のExcel変換ではエラーが発生したことをお客様からの問合せで気づくという状態であり、その度にリアクティブに対応していました。
これらの対応は、リカバリ対象の洗い出しや再実行などの手順が一定程度マニュアル化されていたものの、リカバリ担当者による手動オペレーションが必要な状態でした。
しかし、手順があるのは大変ありがたいのですが、実際はオペレーションに深いドメイン知識が要求されるものであり、歴の浅いメンバーではなかなか安全にリカバリさせるのが難しいというのが実情でした。
また、これらの作業工数がかかりすぎることは、重要な課題として認識されており、リカバリの負荷を減らすための仕組みも同時に用意する必要がありました。
ここまでのまとめ
ここまでを整理すると、リアーキテクティング以前のExcel変換は大きく3つの課題を抱えていました。
- スケールアップが限界になっている
- AWSリソースの利用コストが無駄にかかっている
- リカバリに対する工数がかかりすぎる
これらの課題を解決し、将来に対する不確実性を軽減するため、技術的負債の返済の一環としてExcel変換処理のリアーキテクティングを行うことになりました。
新アーキテクチャの全体像
では、リファクタリング後はどうなったでしょうか?
今回のタイミングで、アプリケーションを再作成し、アーキテクチャもそれに合わせて再設計しました。
こちらがリアーキテクティング後のアーキテクチャです。
処理の流れとしては下記のようになります。
- 対象データを取得するECSタスクをAmazon Event Bridgeで定期実行する
- 対象データを変換処理単位に分解し、AWS Batchのジョブとして登録する
- AWS Batchのジョブ(ECSタスク)で、変換処理を並列実行する
大きな変更点としては、一つのアプリケーションを処理の責務によって分割し、コントロールしやすいスケールアウトをAWS Batchを使って実現している点です。
新しいアーキテクチャの特徴を見ていきます。
アプリケーションの分割
アプリケーションは “変換対象データの取得処理”と、”Excel形式へのデータ変換処理”を分離し、それぞれECSタスクとして実行するように作り直しました。
取得と変換の処理を分割することで、全体的なスループットをコントロールしやすくし、変換処理単位を小さく分離することで、処理が未完了または失敗する不確実性を下げ、リカバリしやすい状態を実現しています。
また、処理のトレーサビリティを確保するために、変換処理の状態を永続化することで、デバッグしやすい状態も構築しました。
リカバリの効率化
以前のアーキテクチャでは、CodeBuildに一定以上の時間がかかったことを検知するためにCloudWatchアラームを設定していましたが、実際に変換処理がエラーになったことを検知できるような監視の仕組みがありませんでした。
そのため、今回から変換処理でエラーになった場合はAWS Chatbotを使いSlackに連携するようにしました。
また、対象データの取得とExcel形式への変換処理を分割したことで、処理単位での再実行が格段にやりやすくなりました。
結果、エンジニア1人が1日単位で実行していたリカバリは、AWS Batchのジョブをリトライするだけになり、リカバリ効率を格段に上げることができました。
スケールアウトの実現
goroutine で 並列処理していた変換処理は、AWS Batchのジョブとして並列で実行するように変更しました。
AWS Batchのマネージドなコンピュート環境でFargateが実行される為、AWS Batchの最大vCPUとECSタスクのvCPUを対比することで、簡単に並列数をコントロールすることができるようになりました。
具体的には、AWS Batch の最大vCPUを32とすると、ECSタスクのvCPUが2の場合に最大16並列で処理を実行するようにコントロールできます。
また、今回の対応で一番処理が詰まりやすい時間帯での実行時間が、およそ4分の1以下で終わるようになり大幅な高速化を実現しています。
コストの最適化
今回のリアーキテクティングにより、CodeBuildのおおよそ90%のコストを削減することができました。 また、変換処理の多くが短時間で終了する為、ECSタスク(Fargate)のコストへの影響も少なく、システム全体的に見ても大幅なコスト削減を実現できました。
まとめ
以上、リアーキテクティングによる改善事例をお伝えしました。
結果として、
- スケールアウトできる仕組みを実現
- 処理時間は4分の1以下
- AWSリソースの利用コストをおおよそ90%削減
- 監視やリカバリに対する不安も解消
と、めちゃくちゃ改善することができました💪
結果が出たことも嬉しいですが、今回のプロジェクトはカミナシにおいて既存システムの大きなリアーキテクティングは初めての経験であり、このプロジェクトをチームでやりきることで、メンバー間のお互いへの信頼感や団結感が高まったことが、個人的に一番嬉しいポイントでした🙌
最後に宣伝です!カミナシでは強いエンジニアリングチームを一緒に作っていく仲間を募集しています📣
やりたいことがありすぎて手が足りておりません、是非一緒に現場ドリブンやっていきましょう👍