エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

Javaバッチのクラウド移行プロジェクトの泥臭い挑戦

この記事はエムスリー Advent Calendar 2024 の18日目の記事です。

こんにちは、基盤チームエンジニアの桑原です。最近はKeychron Q11を購入してキーボードライフを楽しんでいます。昨日はキースイッチの交換に失敗し、10年ぶりにはんだ付けをして何とか事なきを得ました。

はんだ付けしたら巨大な鉛の塊ができてしまったの図

本日は私が半期取り組んでいたJavaバッチのオンプレからクラウドリフトプロジェクトについて紹介します。なかなか泥臭い作業が多かったのですが、ありのままの仕事内容をお伝えします。

概要

本ブログの目的は、実際の業務内容をオープンにして移行プロジェクトを詳細に紹介することです。

今回のプロジェクトでは、OSの保守期限が近い古いオンプレサーバーで動いていたJavaバッチをそのままEC2にリフトしました。また、PHPの部分はAPI GatewayとLambdaを利用して若干の変更を加えました。インフラの管理にはTerraformとAnsibleを使用しています。

フルクラウド化を目指しており、今後の進捗についても報告していく予定です。

システムの概要

会員向けメールマガジンに差し込む広告エリア(カスタマイズエリア、CA)に、会員毎に最適な広告を決定し差し込むためのシステムです。

プロジェクト初期概要
メール配信システムから広告エリアの情報と対象の会員のリストが渡され、ファイルに永続化します。そのファイルをcronでJavaバッチが起動し広告内容を決定し、結果をファイルに吐き出ししています。

プロジェクトの概要

プロジェクトの背景

オンプレサーバーは帳票環境と呼ばれており、維持費や保守期限の観点から全社的にリプレイスが進められています。本システムも例に漏れず、保守性や安全面の観点から脱オンプレが必要になりました。

www.m3tech.blog

脱オンプレを進めるにあたってのハードル

レガシー

歴史的経緯が積み重なり、Javaバッチのソースコードにはカスタマイズエリア出力のため以外のソースコードも大量に含まれています。また、本システムのソースコードは全く別のサービスもビルドしていたため迂闊に手を入れてしまうとそちらに影響を与えてしまう可能性があるリスクもありました。さらに、歴史あるシステムであり、コンテナイメージ化しようとしても一筋縄ではいかない環境でした。

全体像の見えなさ

大規模なJavaバッチのソースコード、シェルスクリプト、ビルドファイルなど、範囲が広い上に他のシステムと同居しているインフラだったため、調査範囲や脱オンプレの作業範囲、完了条件など全体像を把握するのが難しい状態でした。

実際の移行作業

方針決定

JavaバッチとそのInput/Outputには手を一切入れずにクラウドへリフトするという方針を立て、必要最小限の工数で脱オンプレを実現することを最優先にしました。そのため当初構想にはあったもののJavaのコンテナイメージ化は優先度を落としスコープ外に、また、本来はファイルIFの廃止やバッチの進捗通知の改善も行いたかったのですが、肝心の脱オンプレの実現が遅れてしまうため、これもスコープ外としました。

Javaバッチのコンテナイメージ化ができないので、自然とクラウドはEC2をベースとして他の必要なサービスを追加していくことにしました。

EC2へのリフト

Java自体はさすが Write once, run anywhere であって、心配はしていませんでしたし、実際特に問題なくリフト自体はできました。ただ、周辺のスクリプトの移行はなかなか骨が折れました。Javaのビルドやデプロイはオンプレサーバー上で独自のビルドスクリプトが使われていたので呼び出されている別のスクリプト、必要なライブラリのインストール、ディレクトリ構造などを調査して一歩づつ解決していきました。この作業が泥臭さのピークだったと思います。全部読んだつもりでも動かしていると呼び出しているスクリプトが漏れていたり、暗黙的に必要としているライブラリに気づくことができずに密かにエラーも吐かずに落ちていくのを繰り返しているときは心に来るものがありました。

また今回はIaCにAnsibleを選択しました。始めてのAnsibleだったのですが、書き方のレビューはSREチームも協力してくれたため品質担保を取りながら進めることができました。チームの垣根が低いので必要ならチームまたがってレビューを気軽にできる・してもらえる文化なのはとても好きです。

全体像

クラウド移行後

Java以外のPHPなどはEC2に入れたくなかったので API Gateway + Lambda(Python) にするなど、クラウドリフトだけでなくクラウドシフトも多少同時に取り込んでいます。

技術的な課題と解決策

パフォーマンスの悪化

本番切り替えギリギリのタイミングでパフォーマンスが悪化していることに気づきました。オンプレよりも性能を上げたはずのEC2でもなぜか処理時間が4,5倍に伸びるケースがありました(30分だったケースが2時間になるなど)。オンプレ時代と異なるのはサーバーがクラウドに置かれていることくらいであり、ソースコードやDBはオンプレ時代と同じ環境なのにです。

調査を進めていくとどうやらネットワーク遅延が原因だったことがわかりました。オンプレ時代はアプリケーションとデータベースが物理的に近い位置にいたため、アプリケーションをクラウドリフトしたことで(データベースはオンプレのまま)、ネットワーク遅延の影響をもろに受けました。1件1件のネットワーク遅延は数ミリ秒程度であっても、それが数十万の会員数 × 数個の広告の掛け合わせで遅延が無視できない状況になりました。

解決

NW起因の性能悪化だったのでサーバーのスペックアップなどでも解決は難しく、DBのチューニングも効果的ではない状態で頭を抱えました。バッチ処理が遅れてしまうと適切な広告を差し込むことができなくなり機会損失にもつながってしまいます。

今回はInputファイルをあらかじめn分割して、Javaのプロセスもn等分して起動するという結構な力技で解決しました。これであればJavaバッチに手をいれることなくスレッド競合を気にすることもなくなり、比較的安全に並行処理を行うことができたため今思うとスピード感と品質のバランスをとったアイデアであるなと、提案してくれたメンバーに感謝です。

Javaバッチはもともと複数のプロセスが同時に起動しうるため適切なメモリ設定も悩みました。数十万件の処理に必要なメモリサイズと同時並行稼働数を考慮してOSのスペックも決めて行く必要がありました。 今回はjstatでメモリダンプを定期的に取得しながらメモリ使用量の推移を見て、色々条件を変えながらテストを繰り返し、必要最小限のJavaメモリ量を決定していきました。

50万件実行時のjstatグラフ

移行後の効果

今まではオンプレという特性上、他のサービスが同居していたため権限管理として問題があってもSREチームへ作業依頼や調査依頼も必要になっていましたが、EC2を自分たちでも管理するようになったので、調査に必要な権限等は自分たちで取得もできるためシステムにおけるチームの自立性も高まったと思います。

成果と今後の展望

来年末には本システムが強く依存している商用DBからの脱却も、コストダウンのため必須になります。そのためここから大規模なシステムの改修が必要になっていきます。今回スコープ外にしたJavaバッチ本体の改修も不可避なので、思い切ったリアーキテクティングも含めてもっとクラウドネイティブな作りに持っていくことができたらいいなと思います。

まとめ

泥臭い調査や作業を積み重ねて脱オンプレ、クラウドリフトが完遂しました! サーバーの場所がオンプレからクラウドに移動しただけでもNW遅延が無視できない状態になって困るいい経験ができました。

We are Hiring!

エムスリーはまだまだJavaやKotlinなどのJVM系言語によるシステム開発も行っております! 興味を持たれたらぜひこちらから!

jobs.m3.com