事例:KDDI様 KDDIビジネスオンラインサポートのAWS移行&マイクロサービス化

AWSマイクロサービス

前置き

こんにちは、デベロッパーの宮武です

私は現在、KDDI様の法人向けクラウドサービスであるKDDIビジネスオンラインサポート(以下KBOSと表記)およびその関連サービスの開発に携わっています

私はKBOSの開発に2015年から参画し、特にこの1年間はKBOSのAWS移行とマイクロサービス化を担当するチームに属しています

昨年10月に、その第一弾としてKBOSのサブサービスの一つである「KDDIマネージドポータル」のマイクロサービス分割、AWS移行を行いましたので、その紹介をいたします

なお、この記事は、KDDI様およびiret様(本案件の運用を担当)との記事同時公開企画の一環でもあります

ぜひ KDDI様のこちらの記事と、iret様のこちらの記事もご覧ください

背景

KBOSは2014年にリリースしたサービスですが、機能や関連サービスを追加してきた結果、アプリケーション規模が肥大化し続けています

また、チームとしても大きくなり、複数のサブチームに分かれました

それが一つのアプリケーションを開発することより、リリースタイミングの合意形成に対するコスト増、機能改修での意図しない機能への影響、といった問題が発生するようになっていました

そのため、システムを独立性の高い単位で分割し、個々のサービスを担当チームの責任範囲で開発、リリース、運用できるよう、マイクロサービスアーキテクチャへの転換を行うこととなりました

同時に、インフラ面でもOS、ミドルウェアのEOLが課題となっており、アップデートかリプレースかの選択を迫られていました

マイクロサービス化にあたり、コンテナ基盤、メッセージ基盤などが必要となってくることも鑑みて、各種基盤がマネージドサービスとして提供されるクラウドにリプレースすることを決め、今回はAWSを選定しました

そして、第一弾として「KDDIマネージドポータル」を分割し、AWSに移行することとしました

構成の話

まずは移行前後の構成について説明します

移行前

次の図が移行前の構成になっています

before.png

もともとのKBOSはKDDI様のIaaSサービスであるKDDIクラウドプラットフォームサービス(以下KCPSと表記)の仮想サーバー上に構築されています

フロントエンドwebアプリケーションはwebサーバーにデプロイされており、アプリケーションサーバー上で、ゲートウェイアプリケーション、APIアプリケーションが起動しています

KBOSへのリクエストは、ロードバランサーにより、パスベースでゲートウェイとwebサーバーに振り分けられています

ゲートウェイは認可等のチェックを行なった後、APIサーバーにリクエストをフォワードします

さらに背後には、PostgreSQLのデータベース、外部システムへのデータ連携を行う非同期処理用のサービス、スケジューラーで管理されたバッチサービスがあります

今回移行対象のマネージドポータルの機能は、オレンジに色付けした通り各アプリケーションの一部として動作していました

移行後

続いて、移行後の構成を説明します

after.png

リクエストは全てKCPSのロードバランサーで受けつけており、外から見た構成は変わらないようになっています

webアプリにはS3の静的webホスティングを使用し、バックエンドのアプリケーションは、FargateのECSクラスター上で起動しています

KCPSの既存環境とAWSの間はDirect Connectで接続し、コンテナへは内部通信としてアクセスする形をとっています

マネージドポータルのwebアプリ、APIに対するリクエストは、KCPS側のwebサーバーとゲートウェイで、URLのパス第一階層を見てAWSに転送するようにしています

今後も第一階層ごとに少しずつマイクロサービス化していくことを見越し、どのパスをAWSのどのサービスに転送するかは、設定だけで切り替えられるように設計しています

また、非同期処理の呼び出しは、今まではデータベースを経由した独自フレームワークのような形で実現していましたが、これをSQSに置き換えています

バッチ処理はCloudWatch Eventsでスケジューリングし、ECSタスクとして起動しています

コードの話

次に、コードレベルで、どのようにマネージドポータルを切り離していったのかを説明します

前提として、KBOSのバックエンドは次のような技術スタックで作られています

言語:Java

フレームワーク:Spring Framework 4

切り離しにあたって、考慮点として以下のような点がありました

  1. 移行開発の間に新機能開発を止めないこと
  2. できる限り既存実装を活かすこと(作り直しはしない)
  3. フレームワークのバージョンをアップデートする(Spring Boot 2系、コンテナ化)

これらの条件を満たすために、同一のコードベースから「旧フレームワークバージョンのモノリスアプリ」と「新フレームワークバージョンのマネージドポータルマイクロサービスアプリ」の両方をビルドできるようにする必要がありました

まず、第一ステップとして、マネージドポータルに関するコードをMavenのサブモジュールとして分割しました

いわゆるモジュラーモノリスの状態を一度経由しています

これにより、モノリスとマネージドポータルモジュールの結合点を可視化でき、切り離す際の影響範囲を最小化できました

第二ステップとしてさらにMavenモジュールを分割し、新バージョンに移行するコードだけをそちらに移動していきました

非同期処理の独自フレームワークに依存した処理や、ライブラリのバージョンアップに伴う非互換な部分(Spring Data JPAのRepositoryメソッド名など)を残し、業務コードはこちらのモジュールに全て移動しました

その際に、フレームワークと密結合した業務コードについては、インターフェースを間に挟むなどのリファクタリングを行っています

最後に第三ステップとして、AWS側で動作させるSpring Bootアプリのモジュールを作成し、2のモジュールに依存することで、新機能開発と並行して移行を行うことができました

modules.png

プラットフォームとしての話

ファーストリリースのターゲットはマネージドポータルだけでしたが、今後を見据えてマイクロサービスプラットフォームとしてどう設計するか、も考える必要がありました

KBOSのマイクロサービスプラットフォームとして、標準・ルールを策定したのは次のような部分です

  1. トレーシング・ログ
  2. 認証・認可

トレーシング・ログ

まずはトレーシングとログについて説明します

マイクロサービスアーキテクチャでは、ユーザーからの単一のリクエストが複数のサービスに伝播して処理されることになります

そこで、単一リクエストに起因するログを、サービスを追いかけられるようにする必要があります

そのためには、ログの検索性をリクエストごとに一意のトレースIDがあること、複数のサービスをまたいだログを一箇所で検索できるログ基盤、の二つが求められます

一意のトレースIDについては、リクエストごとにゲートウェイで発行し、それをサービス間で持ち回り、ログに出力するルールとしました

また、サービスをまたぐたびにランダムのID(一般にこれをスパンIDと呼ぶそうです)をさらに追記し、トレースIDを伸長する設計としました

サービスをまたぐタイミングでトレースIDに親子関係を持たせ、障害調査の際に直系の親子IDを辿れるようにしています

今回はここを独自実装としていますが、将来的にサービスが増え細かい分析が必要になってきた場合に備え、X-Rayなどの利用も検討していきたいと考えています

また、それぞれのサービスでこの機構を実装する必要がないよう、これら共通基盤のサイドカー化も進めていきたいと考えています

ログ基盤については、現在はシンプルにCloudWatch Logsを用いています

まだサービス数が少なく、ロググループを跨いで検索するユースケースもないため、トレースIDを使ってロググループごとに検索すればよいと考えています

ただ、ログをjson形式で構造化すること、トレースIDのタグ名、タイムスタンプの形式などは統一ルールを設けています

こちらも将来的にサービス分割が進んでいくと、Elasticsearch Serviceの導入などが必要になってくると考えています

認証・認可

次に認証・認可です

KBOSの関連サービスであるKDDI Business IDがOpenID Providerとなっており、KBOSはもともとユーザーの認証にOpenID Connectを使用しています

その資源を活かし、マイクロサービス間の通信の認可も、OAuth2のアクセストークンを利用することとしました

呼び出し側のマイクロサービスは、OAuth2のClient Credentialsフローを使用してアクセストークンを発行し、他のサービスにアクセスします

この場合、こちらの記事にあるようなAPIの玉突き問題が発生し、ユーザーによる認可を呼び出し先のサービスが確認できないことになります

そこで、ユーザーからのリクエスト時に、ゲートウェイがユーザーの属性情報をリクエストに付与し、それをマイクロサービス間の通信やメッセージングを通じて持ち回ることで、属性ベースでの認可を行うような設計にしました

また、OAuth2のスコープを整理し、各フロントエンドアプリやマイクロサービスのアクセス範囲をスコープ単位で宣言できるようにしました

最後に

この他にも、AWSリソースのTerraformによるコード化、監視のDatadog化(ここはiret様の記事に譲ります)、CI・CDの構築、サービス分割のための既存機能・業務の分析など、行なったことは多岐に渡ります

このように幅広い範囲のことを検討する必要があり、ひたすら学び、考えて続けていたプロジェクトとなりました

そのため、話があちこちに散らばってしまい、わかりにくい記事になってしまいました(言い訳です)

引き続きKBOSでは残りの機能もAWS移行、マイクロサービス分割を進めています

マイクロサービスプラットフォームとしても、さらに進化していくことになると思います

さらに進化した姿をまたブログで紹介できるよう突き進んでいきたいと思います