
運用負荷の解消に向け、マイクロサービスへの移行を決意
今回移行の対象となったのは、ドリーム・アーツが運用する「Shopらん」というアプリケーション。これは「チェーンストア・多店舗運営コミュニケーションツール」と呼ばれる、流通・小売業の本部・店舗間のコミュニケーションに特化したクラウドサービスだ。例えば、本部から各店舗に対する指示や新商品情報を送ったり、店舗の側から本部に運営状況を報告したりと、ユーザー企業の日常業務に欠かせない数々の機能を提供している。
2008年にサービスを開始し、今年で11年目を迎えた。現在の登録ユーザー数は60万件にのぼり、コードベースは約200万行。預かっているデータの総容量は23TBに達する巨大アプリケーションだ。
同社がこのアプリのマイクロサービス移行に着手したきっかけは、巨大さゆえの運用負荷の増大だった。またStruts1で開発され、JDBC経由でデータベースにMySQLを置く構成も、現在ではもはや「古臭くてモノリシックなアーキテクチャにしか見えなかった」と石田氏は振り返る。
「ビルドした200万行(165MB)をコンパイルすると30分。さらにこれを本番環境にデプロイすると、ブートだけで30分を越えてしまうので、1日に何回もデプロイしたくても、そう簡単にできません。構成管理だけはChefを使って何とか回してきたのを、ようやく今年からDockerのランタイム上で動かすようになったところでした」
とはいうものの、デプロイのたびに「30分×サーバーの台数分」の時間がかかってしまっていた。ビジネスの要求に応えるために、アクロバチックな構造も次第に増えてきた。その結果、内部の処理の流れが複雑化して、もはや手に負えないところまで来ていたという。サービスを継続するために、全面的なリファクタリングが必要なのは、誰の目にも明らかだった。
「どうせ改修するなら、今のアーキテクチャを引き継ぐのではなく、根本的に刷新したかった。そこでコンテナオーケストレーション上にマイクロサービスを乗せる仕組みにしようと考えたのです」
石田氏自身、コンテナには以前から関心を持っていたが、自分たちには手に余ると思っていた。そこに昨年あたりからKubernetes as a serviceが盛り上がってきて、これなら自分たちでもできると確信を持ったという。

「Spring Boot」と「Swagger」を組み合わせ、マイクロサービスの作成効率をアップ
新しいマイクロサービスのプラットフォームに採用したのは、「Azure Kubernetes Service(AKS)」だった。マイクロソフトが提供する、このフルマネージドのKubernetesコンテナオーケストレーションサービスを選んだ理由は、自社の既存環境との親和性だ。すでに「Shopらん」のデータストアでAzure SQL Databaseのマネージドサービスやデータベースを導入しており、処理の連携などを考慮すれば最適かつ必然的な選択だったといえよう。
マイクロサービス化で最初に着手したのは、アプリケーション対応だった。現在、稼働中のアプリのコードをいきなりマイクロサービス化してコンテナに乗せかえるのはリスクが大きいため、従来の実装を維持して万が一の場合はすぐに切り替えられるように備えた。
マイクロサービスの作成には「Spring Boot」と「Swagger」を組み合わせた。セキュリティインシデントの状況などから「さすがにもうStrutsは避けたい」と考えた石田氏は、数多くのフレームワークを検討した結果、この組み合わせに決めたと語る。
「世の中のトレンド的にもSpring Bootが来ており、スキルを持ったエンジニアが多いことが大きかった。また機能面も、他の候補と比較してなかなか優れていました。そこでSpring Bootに、REST APIを記述するための仕様を組み合わせるといいのではと考えてSwaggerを選びました」
この2つを組み合わせて標準基本構成(テンプレート)を作成。これを継承して、さまざまなマイクロサービスを組み立てていく方針を決めた。石田氏は、「テンプレート化した背景には、開発スキルのポータビリティやメンテナンス性を向上させるねらいがありました」と語る。

またSpring Bootを利用すると、作成したマイクロサービスをKubernetes上にデプロイしていく際、ビルドプロセスにDockerビルドを組み込んで、小さなマイクロサービスを簡単に作成できるといった独自のメリットが得られるという。
デプロイ時の面倒な定義作成は「Kustomize」を使って簡単&効率化
作成したマイクロサービスのデプロイメントは、すでにMicrosoft Azureの仮想ネットワーク(VNet)上に仮想サーバーを構築していたため、それを利用した。さらにAzure Cosmos DBやAzure SQL Databaseのマネージドサービスを使うと、このVLANの中にエンドポイント接続できる。その結果、システム全体を仮想ネットワークの中で完結できた。
「あとはノードサイズやノード数を設定して作成を進めていきます。ここで注意するのは、マイクロサービスでは大量のIPを消費するので、あらかじめ十分なサイズのサブネットを仮想ネットワークに作成しておくことです」
次に必要となるのが、Dockerで作ったそれぞれのサービスの設定ファイルを、Spring Bootに渡していく仕組みだ。KubernetesではConfigMapを使って定義しておくと、コンテナの側で起動時にファイルとしてマップされるようになっている。
またマイクロサービスのデプロイ作業は、YAMLを大量に書く必要がある。この膨大なYAMLをどう管理していくかが問題だ。この対策には「Kustomize」というツールを採用した。Kustomizeを使うと、まず共通する定義を作成し、そこから差分だけを別に定義していけるため、時間や労力がかなり節約できる。
「Kustomizeのもうひとつの良いところは、ConfigMap名にハッシュ値を入れてimmutable化できる点です。通常Kubernetesでは、ConfigMapを変更してもPodを自動では更新してくれません。この場合、名前が同じままだとKubernetesは検知してくれないので読み直す必要が生じます。しかしKustomizeを使うと、ConfigMapを変更した場合に必ず名前も変更してくれるようになります」

日進月歩の技術を積極的に取り入れ、さらなる機能向上を目指す
今回のマイクロサービス化で、もうひとつ石田氏が工夫を凝らしたのが、パフォーマンス改善の仕組みだ。移行前はモノリシックなアプリケーションだったため、関数呼び出しがマイクロ秒単位の速さを実現できていた。また近隣のサーバーとも、JDBCクエリを使って同様の速度による処理が可能だったという。
「これをマイクロサービス化してKubernetesのアーキテクチャに乗せると、格段にスピードが落ちてしまうのがわかりました。実際に計測してみたところ、従来だと7.05マイクロ秒だったのが26.07マイクロ秒となり大幅なダウンです。これではユーザーエクスペリエンスにかなり影響が出てしまいます」
これまでは「ビュー」「コントローラー」「キャッシュ」「DAO」まで、すべての階層が1つのJVMインスタンス内に収められていた。それがマイクロサービス化すると、階層が分離されて「https REST API」や「JDBC Query」の2段階を経由して処理が流れることになる。これが遅延の原因だった。
「この教訓としては、ある程度の大きな単位でロジックを切り出していくこと。マイクロサービスならとにかく細かい単位で作ればいいといった声もありますが、計測結果を見る通り、関数呼び出しを多用する場合は注意しなければいけません」
このパフォーマンス問題の解決のため、アプリ側にキャッシュ層を残すことや、非同期・並列プログラミングといった工夫を凝らしている。
「まだまだ改善していきたい部分は数多く残っており、現時点ではクリアしきれない難しい課題もあります。しかしマイクロサービス関連の技術は急速に進歩しており、その成果をいち早く取り入れながら、さらに機能や使い勝手の向上を目指していきたい」と展望を語り、石田氏はセッションを終えた。

お問い合わせ
株式会社ドリーム・アーツ