こんにちは。エンジニアの渡邉です。普段はLIFULL HOME'Sの売買領域のエンジニアチームにて開発を担当しています。好きなApache SolrのAPIはJSON Facet APIです。
今回はLIFULL HOME'Sの画像変換サーバの構成を見直し、アプリケーションの再構築並びに基盤刷新を行い、コスト面、性能面、運用面での改善を行った話をさせていただきます。
画像変換サーバの特性
弊社ではLIFULL HOME'Sを始め、さまざまなアプリケーションから画像変換サーバを利用しています。
今回移行した画像変換サーバは、nginx用の動的画像モジュールのngx_small_lightを採用した自作のアプリケーションとなっています。
主な役割について簡単に説明すると、呼び出し元のアプリケーションが画像を取得する際に画像変換サーバは画像のサイズと画質を動的に変換し、アプリケーションに返却する役割を担っています。 常に数千RPSを受け取り、繁忙期にはさらに多くのリクエストが増えます。
開発における課題
もともと画像変換サーバを移行するきっかけになったのは私がLIFULL HOME'Sの画像の最適化を行いたいと思ったのがきっかけでした。 ユーザーに対して品質のよい画像を届けたり優れた体験のために読み込み速度を改善したいというモチベーションで改善を行いたいと思っていました。
LIFULL HOME'Sでは物件の内観や外観の画像を始め、ほとんどのページで画像を画像変換サーバから取得しています。 LIFULL HOME'Sのような不動産情報ポータルサイトにおける画像の重要性は非常に高く、画像配信を行うアプリケーションに機能改修を行うことでサイト全体の大幅なパワーアップを行うことができます。
しかしながらそれを実現するにあたりさまざまな課題を感じ、今のまま改修をするのは効率が良くないと思い、開発体験の向上を行う手段を講じることにしました。
開発体験の向上を行うにあたり、システム刷新をするかアプリケーションの新基盤刷新を行うかを天秤に取ったところ、画像変換サーバを利用するアプリケーションが多いことから影響範囲の広さと実装コストなどさまざまな要因から検討を重ねた結果、今回はアプリケーションの新基盤移行を行うことにしました。
課題1: 開発しづらいアプリケーション
既存の画像変換サーバは、アプリケーション開発者にとって改修しづらいアプリケーションになっていました。 その要因は大きく分けて以下の2点です。
- 運用をしているチームがインフラ開発チームであること
- アプリケーションの検証環境を気軽に用意できないこと
画像変換サーバは大量のインスタンスにより動作するアプリケーションであり、インフラチームの管轄として運用されていました。
そのためアプリケーション開発者には制限が多く、 検証サーバの作成やデプロイなどにも手間がかかり画像変換サーバを改修することが容易ではありませんでした。
また、開発にはインフラチームに検証環境を用意してもらう必要があり、すぐに検証を行うことができないことも開発速度を遅くしていました。 したがって、開発者が手元のマシンで気軽にアプリケーションを立ち上げるしくみを用意する必要がありました。
課題2: インフラリソースの最適化の余地の向上
画像変換サーバに対して圧縮率の変更やフォーマットのWebP化などの機能改修を行いたいモチベーションがあるのはアプリケーション開発者にあることが多いです。 また、インフラリソースなどの変更に対する権限もアプリケーションの特性を把握しやすいアプリケーション開発者が持つことによるメリットは多いと思います。
しかしながら、現状の組織運用体制ではアプリケーション開発者に検証と調整を気軽に行うことができませんでした。
したがって、アプリケーション開発者に自由にインフラリソースを変更する権限を与えられるようにすることが必要だと考えていました。
また、もともとの画像変換サーバはEC2インスタンスを非常に多く起動させるアプリケーションであることからコスト面での課題を抱えていました。
弊社のアプリケーションのほとんどが画像変換サーバを利用していることから、サーバがダウンしてしまうことに対するリスクは非常に大きいです。 そのため唐突なスパイクリクエストが来ても問題なくアプリケーションが動作できるだけの運用台数を用意する必要がありました。
大きめのEC2インスタンスを数十台で運用し、高コストでの運用が続いていました。
実際にやったこと
上記に挙げた課題を解決するためにやったことをセクションベースで記載していきます。
実際の画像変換サーバを読み解く
画像変換サーバを刷新することを決めたものの、その状況は芳しくないものでした。
というのも古いアプリケーションであり機能改修が少なかったこともあり、実際の運用やアプリケーションについての特性を正しく把握している人が社内に存在していませんでした。
したがって実際のデプロイフローやnginxの内容から何をしているアプリケーションなのかやデプロイに必要な構成要素を洗い出す必要がありました。
まずデプロイ内容やインフラ構成については、既存の画像変換サーバはリポジトリ管理等もされておらず、リリースに必要なものが各所に点在している状況でした。 ですので、当時デプロイ運用で利用されていたAnsible playbookとCloudFormationの内容を読み解き、デプロイに必要な要素を一つ一つ洗い出しつつまとめていきました。
またアプリケーションを理解している人もいない状況でした。 そこで、インフラチームに既存と同一のインスタンスを準備してもらった上で、nginxの内容を読み解きつつ必要なモジュールの確認や挙動の確認を進めました。
コンテナベースに置き換える
大まかな構成要素が洗い出されましたので、アプリケーションを構成し直しました。
もともと課題にあげた通り、開発者が気軽に検証環境を手元に用意できないという課題を解決するために、コンテナ化をすることにしました。
基本的なアプリケーションの構成要素やデプロイに必要なものはAnsible playbook等の内容に記載されていたので、それにしたがってコンテナ化を進めていきました。
そのタイミングで利用しているソフトウェアやBase Imageなども極力アップデートしています。
実行基盤をEC2からKubernetesに変更する
課題2に挙げた通り、アプリケーション開発者がインフラリソースを変更できるようにしたいという希望がありました。
それを実現する具体的なアプローチとして、実行基盤をKubernetesに移行することとしました。
弊社では全社的にインフラ環境をKEELという独自のKubernetesベースのアプリケーション実行基盤に移行していくことになっています。
KEELについてはこちらのエントリに記載されています。
https://www.lifull.blog/entry/2020/12/02/000000
もともとEC2で運用をされていたアプリケーションをKubernetes環境であるKEELに移行することは今回の課題であるアプリケーション開発者がインフラリソースを容易に調整をできることや検証環境を容易に作成できる点に対する明確かつ有効的なアプローチであることからKEELの実行基盤への移行を決定し、既存アプリケーションのコンテナ化を実施したうえで、KEEL環境へデプロイする形にしました。
負荷テストの実施
コンテナ化とKubernetes移行が完成したタイミングで負荷検証を実施しています。 既存の画像変換サーバが大量にリクエストされるアプリケーションであり、さまざまなアプリケーションから使われている都合上、アプリケーションの切り替えには無停止での入れ替えをする必要がありました。
したがって、既存のリクエストがさばけるように長期間にかけて、多種多様のリクエストを想定したシナリオを用意し安定してさばくことができるリソースの調整を行いました。
具体的には以下の手順にで負荷検証を実施しました。
- 1台のpodがさばける限界のリクエスト量を測定
- 一台あたりのスループットを確認
- 1.で求めた最大のリクエスト量の2倍を2台のpodでさばくことができるかを検証
- podが増えたことで線形にスループットが増えることを検証
- スケールアウトする閾値の検証
- 今回はcpuに負荷のかかるアプリケーションであったため、2.で実行したリクエスト量を安定してさばける台数とスケールアウトするタイミングを決定
- 実際に必要なpodの数を検証
- 3.でもとめたリクエスト量を安定してさばくことができるpodの台数から実際のリクエストを受け取る場合に必要な台数を求めて必要なpodの台数を決定
- 実際にプロダクトインしたケースを踏まえてsoakテスト(長時間のリクエストテスト)の実施
- 長時間リクエストを受け続けてもアプリケーションに問題がないことを確認
スケール戦略の変更
今回の移行時にスケールアアップ+スケールアウトの両軸で運用していた画像変換サーバをスケールアウトに寄せる形で運用しなおしました。
Kubernetes環境になったことで大きく変わったことがいくつかありますが、その一つがスケールアウトの時間が大幅に削減したことでした。
具体的な数字を挙げると、既存の画像変換サーバがスケールアウトするのには1分から2分必要だったところKubernetes環境化にすることで、15秒ほどにまで削減することが可能になりました。
それにより、スケールアウトに対する比重を非常に高く持つことができるようになりました。 したがって、サーバ1台に対してリソースの余裕を持たせる割合が少なくなり、サーバ1台あたりのリソースを削減できることができました。
また、既存の画像変換サーバは大きめのインスタンスをある程度準備している状況を常態化させる形にしていました。 したがって、朝方などのリクエストの少ない時間にリソースが余る状況になってしまっていたことも多々ありました。 そこで、一台あたりのリソースを極限に少なくし大量のサーバでさばく戦略に変えることで、リクエストの数に応じたリソース調整を行える形にすることでリソースに対する無駄を排除することにしました。
カナリアリリースの実施
今回の画像変換サーバは数多くのアプリケーションから利用されているアプリケーションであるため、無停止での移行をする必要があったので、それを行う手法としてカナリアリリースを実施しています。
約二週間の期間をかけて徐々に割合を上げていく形で切り替えていきました。
成果
上記に挙げたやったことを実施したことでいくつか成果につながることができましたので紹介します。
開発速度の向上
コンテナ化やKubernetes環境に移行したことで、自前で開発用のサーバを気軽に用意したり検証できるようになったので、今までに比べて格段に開発しやすくなりました。
そのわかりやすい例として、もともと私が基盤移行をする前に画像変換サーバに対して行いたかった改修の一つとして、画像の画質変更をするというものがありました。
内容としてはそこまで工数がかかるものではなかったのですが、既存の画像変換サーバに機能改修をすると他グループの作業にも依存するため検証環境の構築待ちなども発生しおおよそ2週間ほど時間を要してしまうことになるところ、検証まで含めて一日でリリースできるようになりました。
非常に検証が行いやすくなったことやアプリケーション開発者側にデプロイの権限が移ったことにより開発速度の圧倒的な向上を行うことができたのは機能開発に対するコストの兼ね合いからかなり大きな成果でした。
運用コストの軽減
nginxのチューニングやスケールアウト戦略に変更したことで非常にリソースを抑えることに成功しました。
リソースを縮小させた主な要因は以下のようなことが挙げられます。
- pod一台あたりのリソースの縮小化
- スケールアップ戦略からスケールアウト戦略への変更
- 一台あたりがさばけるリクエストの数を増加させる
その結果、もともと大きめのEC2インスタンスを数十台で運用していたため高コストで運用していた画像変換サーバを約1/3のコストで運用することに成功しました。
レスポンス速度の向上
アプリケーションの構成を入れ替えの実施や、nginx-exporterを導入したうえでworker数の最適化を行うなどの改善をしたことにより、レスポンス速度にも大きな改善が見られました。
具体的な数値としては、平均レスポンスタイムは230msec → 110msecほどまでに短縮でき、アプリケーション全体のパフォーマンスを向上させることにつながっています。
既存の構成・知識の集約
GitHubリポジトリを今回新規に用意し、リリースに必要なものをすべて集約しました。
画像サーバはもともとリポジトリ管理されているアプリケーションではありませんでした。 したがって、アプリケーションの構成に必要なファイルやスクリプトが一ヵ所に集約されておらず、リリースに必要なものがすぐにはわからないという状態でした。
それを今回のタイミングでリポジトリ管理したことにより見るべき場所を単一化できたほか、機能改修のバージョニングを行うことができたので開発者目線で運用しやすいプロダクトになりました。
まとめ
今回はさまざまな要因から開発しづらいアプリケーションだった画像変換サーバを再構築並びに新規実行基盤に移し替えたお話をさせていただきました。
レガシーなシステムであったり、運用体制であったりと開発工数や調査工数が嵩んでしまうことというのはいたってめづらしい話しではないかと思います。
そのような場合にはたいへんだなーで終わらせず現状の課題を洗い出し、エンジニア的アプローチで改善をしてみることで、未来へつながるアプリケーションにできるかもしれません。
今回はその手法として基盤刷新をするというものでしたが、基盤刷新に限った話ではないと思いますので、さまざまなアプローチを視野に入れてアプリケーションの改善してはいかがでしょうか。
最後に、LIFULL ではともに成長していける仲間を募集しています。よろしければこちらのページもご覧ください。