こんにちは、株式会社LegalOn TechnologiesでPlatform Engineerをしている杉田(@toshi0607)です。
先日、CloudNative Days Winter 2024で「50以上のマイクロサービスを支えるアプリケーションプラットフォームの設計・構築の後悔と進化」と題し、LegalOn TechnologiesにおけるPlatform Engineeringの取り組みについてお話してきました。この記事では、Platform Engineering Advent Calendar 2024の23日目の記事として登壇内容の前半部分を書き起こします。
Introduction
杉田寿憲と申します。社会人大学院生としてコンピューターサイエンスを学びながら、LegalOn Technologiesという会社でPlatform Engineerをしています。アプリケーションプラットフォームのテックリードです。
LegalOn Technologiesは、「法とテクノロジーの力で、安心して前進できる社会を創る。」というPurposeを掲げています。法とテクノロジーを掛け合わせて企業の法務業務を支援するプロダクトを開発し、提供しています。
こちらがプロダクトラインナップです。法務・契約業務の支援を中心に、さまざまなプロダクトを提供しています。法務領域だけでなく、経営支援においてもプロダクトを提供しています。
スライド上部の緑枠で囲まれた「LegalOn Cloud」は、2024年4月にリリースされた当社の主力プロダクトです。青枠で囲まれている「LegalForce」、「LegalForceキャビネ」が支援した契約ドメインを踏襲し、法務ドメイン全般をワンストップで提供する法務プラットフォームとして生まれました。元々、「LegalForce」はAWS、「LegalForceキャビネ」はGoogle Cloud上で稼働していたプロダクトです。「LegalOn Cloud」は、Google Cloud上で稼働するプロダクトとして生まれ変わりました。今日はこの「LegalOn Cloud」の開発を支えたアプリケーションプラットフォームを中心にお話しします。
「LegalOn Cloud」をリリースするまでのタイムラインはこちらです。2023年3月にキックオフし、その1年後に正式リリースしています。
ここからは、1年でリリースするための工夫、直面している課題と後悔、マルチプロダクト・マルチリージョンをサポートするプラットフォームへ進化するための取り組みの3つのパートに分けてお話しします。
1年でリリースするための工夫
「LegalOn Cloud」の開発には、つぎのような特徴がありました。
1つ目は一からの構築です。既存プロダクトである「LegalForce」と「LegalForceキャビネ」を移行するのではなく、実現したいコンセプトに基づいた新設計・新技術スタックで開発しました。そのため、必ずしも全てのメンバーが新しい技術スタックになじみがあるわけではありませんでした。
2つめは、開発期間の短さです。3〜4年かけて開発した既存プロダクトの機能に加え、新たな機能を追加して1年でリリースしました。
3つめは、50以上のマイクロサービスで構築している点です。製品機能を開発するチームや、認証や通知などの共通基盤を開発するチームが同時多発的に開発を進めてきました。
それらの特徴に対し、アプリケーションプラットフォームはつぎのようであるべきだと考えました。
まず、開発ライフサイクルをカバーする標準ツールを整備することです。サービス横断的な課題をプラットフォームで解決することで、各チームが探索と実装に集中できるようにしました。
つぎに、一定の構成でサービスを開発することです。開発言語やデータベースなど、サービス毎に自由に技術選定しないことで、開発や運用の速度や品質を高められるようにしました。
最後に各チームが自律的に開発・デプロイできるようにすることです。共通のデプロイ機構や一貫したデプロイ方法をとりながらも、サービス毎にバージョン管理してボトルネックを作らないようにしました。
詳細な工夫の内容に入る前に、アーキテクチャや開発の概要を説明します。マイクロサービスは共通のGKEクラスタ上で稼働しています。各サービスのKubernetes Namespaceと対応するGoogle Cloud Projectを持ちます。そのProjectの中で、Pub/SubやSecret Managerなど、サービス専用のリソースを管理しています。マイクロサービスは、コアドメインを担うFunctional Serviceや支援サブドメインを担うData Serviceなど、レイヤーアーキテクチャになっています。
リポジトリは、アプリケーションとインフラに分かれています。アプリケーションリポジトリでは、全サービスのAPIスキーマとサービスのソースコードを管理しています。インフラリポジトリでは、プラットフォームとサービスそれぞれのKuberentes manifestとTerraformコードを管理しています。
CI/CDには、GitHub ActionsとArgo CDを利用しています。アプリケーションリポジトリでは、コンテナイメージのビルドとプッシュまでを行います。Argo CDは、Argo CD Image Updaterを組み合わせています。dev環境ではコンテナイメージの追加をトリガーに、継続的にデプロイしています。QAやPrd環境では、GitOpsでデプロイします。このように、環境によって使い分けています。デプロイの結果は、Argo CD Notificationsを利用して、Slackに通知しています。
ここからアプリケーションプラットフォーム色が強くなっていきます。アプリケーションプラットフォームは、コードを書き、テストし、デプロイし、運用するという開発ライフサイクル全体で標準的に利用できるコンポーネントを提供します。それにより、開発をより快適なものにします。たとえば、コードを書くときには、テンプレートや共通ライブラリを利用できます。運用時には、非機能要件などリリースに必要な観点を網羅的に確認するProduction readiness checkを提供しています。それらに加え、土台となる堅牢なインフラも開発・運用します。
今回はProto生成コード管理、テンプレート、デプロイ、DBスキーママイグレーションについて紹介します。
Proto定義とコード生成は、各チームがサーバー・クライアント間のAPI合意のため、開発の初期に利用を開始します。そのため、全利用言語の生成コードをライブラリとして利用できる環境を迅速に準備する必要がありました。
これに対し、buf CLIとBuf Schema Registryを利用したProto管理基盤を構築しました。
Buf Schema Registry(BSR)はproto管理のマネージドサービスです。ProtoファイルをBSRにアップロードすると、サポートされた言語とプラグインに応じたSDKが利用できます。TypeScript、Go、Java、Python、Swift、Kotlinなど幅広く対応しており、サポートの拡大も活発です。
BSRには複数バージョンのプラグインから生成されたSDKがホストされており、ダウンロード時にバージョン指定ができます。
ローカルで実験したいときは、buf CLIが活躍します。buf CLIにはビルドツール、フォーマッター、リンター、破壊的変更ツールが含まれており、フィードバックを容易に得られます。また、protocやプラグインがリモートで解決されます。エコシステムが抽象化されることでローカルで考えないといけないことが減り、開発体験がよくなります。
最低限のCIのセットアップも数行で済みます。Bufが公式でメンテナンスしているbufbuild/buf-actionを利用できます。buf CLIに備わっているビルドツール、フォーマッター、リンター、破壊的変更ツールが内包されています。
工夫 ~サービステンプレート~
つぎはサービステンプレートです。アプリケーション用リポジトリで50サービスがアプリケーションを初期化するのは大変です。
そこで、バックエンドとフロントエンドのアプリケーションテンプレート、共通ライブラリ、開発ツール、個別サービス用のGitHub Actions workflowとそれらを初期化するスクリプトを用意しました。
サービスを追加するとき、サービス名と利用言語を指定して冒頭のスクリプトを実行します。 go run tools/service-new/main.go
です。そうすると、ローカル開発に必要なツール一式とKubernetes上で稼働することを意識したgRPCサーバーのアプリケーションが生成されます。さらに、テストやリント、コンテナイメージのビルドとプッシュを行うGitHub Actions workflowも同時に生成されます。workflowが利用するモジュールはcomposite actionとして言語毎に管理しています。
工夫 ~デプロイの段階的移行~
デプロイに関しては、時期によって求められることが異なるのが課題でした。
開発初期は、アプリケーションリポジトリのPRマージでどんどんデプロイしていましたが、QAや本番リリースを迎えるにあたり、厳密にバージョン管理するニーズが高まりました。
これに対し、Argo CD Image Updaterによる、コンテナイメージ追加をトリガーとするデプロイとArgo CDのGitOpsによるデプロイを段階的に移行したり併用したりして対応しました。
初期はアプリケーションリポジトリでPRをマージすると、GitHub Actionsでコンテナイメージのビルドが走ってArtifact Registryにプッシュされ、Image Updaterが検知し、DevとQA環境にデプロイしていました。アプリケーションリポジトリでデプロイが完結し、インフラリポジトリは全く意識することなく開発が進められます。その後、厳密なQAテストが始まり、アプリケーションリポジトリでのGitタグ打ちで指定したバージョンのコンテナイメージのビルドとプッシュを行い、インフラリポジトリのKubernetes manifestでバージョンを指定するように移行しました。本番環境へのデプロイが始まってからも同様にバージョン管理しています。
工夫 ~DBスキーマイグレーション~
最後はDBスキーママイグレーションです。
アプリケーションのフレームワークや言語に依存せず、Argo CDと併用できる必要がありました。これを解決するため、ローカル開発でも利用するsqldefを利用したスクリプトを実行するKubernetes JobをArgo CDのpre-syncで実行するようにしました。
直面している課題と後悔
さまざまな工夫で「LegalOn Cloud」のリリースを支えたアプリケーションプラットフォームでしたが、後悔していることもあります。
単一プロダクトを単一リージョン展開する上では困りません。しかし、マルチプロダクト・マルチリージョンをサポートするプラットフォームを作る上で課題があります。
課題は人的拡張性、地域的拡張性、プロダクト的拡張性の3つの拡張性に集約されます。
まず、人的拡張性です。
リリースまで、GKE各Namespace内のマイクロサービス用インフラ構築は、SREメンバーが担っていました。画面右図のようなシートを利用し、サービス名、利用するコンテナ、ワークロードの種類、利用する外部サービス、接続先サービスなどをヒアリングし、TerraformとKuternetes manifestを記述します。新規採用技術スタックが多い中、開発初期は、コンテナイメージのビルドより後の工程を開発者が極力意識しない状態にしていました。
しかし、今後これまで以上に多くの価値を高品質かつ迅速にお客様に届ける必要があります。現在、徐々に開発者からSREメンバーへのインフラ構築依頼構造はセルフサービスに変わりつつありますが、マイルストーン実現のためのインフラ構築に多くのパワーを割いており、スケールしづらい状況です。また、サービス横断的な課題の解決にも時間を割きづらくもあります。
2つ目は、地域的拡張性です。
サービスやプラットフォームコンポーネントのNamesapce毎に割り当てられるGoogle Cloud Project IDには、30文字の制限があります。その中で他のプロダクトとの区別、Kubernetes Namespaceとの対応関係、環境区分を表現する命名規則を採用していました。 [product]-[service]-[stage]-[env]
のようになっています。一方、Kubernetes Namespaceでは、サービス名を通じてGoogle Cloud Project IDとの対応関係を表現しています。 [service]-[stage]
のようになっています。
LegalOn Technologiesは、海外展開を進めています。2023年2月、米国向けにAIレビューのベータ版を提供開始しました。それ以来、様々な機能をリリースしています。さらに先月、英国向けにサービスの提供を開始しました。
そうした中で、紹介した命名規則では表現できてない部分があります。更新ができないGoogle Cloud Project IDなど、後から変更しづらい部分で国やリージョンの概念を表現しておらず、サポートする地域を追加しづらいプラットフォーム構造になっています。異なる命名規則を混在させて同じ構成で無理に追加すると、CI/CDなどの開発・運用コストが地域拡大毎に線形に増加してしまいます。さらに、サービス提供地域が拡大しても、各地のレギュレーションに準拠した分離要件を維持する必要があります。
最後に、プロダクトの拡張性です。
アプリケーションプラットフォームが提供するProto管理基盤、CIやアプリケーションのテンプレート、共通ライブラリはすべてアプリケーションリポジトリ内にあります。インフラリポジトリも、対象となるプロダクトを示すプレフィックスがない状態でインフラコードを管理しています。
冒頭で紹介したように、LegalOn Technologiesは「LegalOn Cloud」以外にも、経営支援などの複数のプロダクトを提供しています。「LegalOn Cloud」向けに準備したテンプレート、Proto管理基盤、インフラなどは、本来プロダクトに依存しないように設計できます。現状それらは「LegalOn Cloud」に密結合な状態になっており、他のプロダクトでは利用できません。モジュール化しなければコピーもしくは再作成が必要であり、プロダクトマーケットフィットまでの貴重な時間を車輪の再発明に割くことになってしまいます。
後からなら何とでも言えますが、マルチプロダクト・マルチリージョンをサポートするアプリケーションプラットフォームの夢は最初から見ていただけに、とても悔しい状況です。
後編では、課題を克服し、マルチプロダクト・リージョンを支えるプラットフォームへの進化のための取り組みについて紹介します。
仲間募集
私たちのチームでは、一緒に働く仲間を募集しています。ご興味がある方は、以下のサイトからぜひご応募ください。皆様のご応募をお待ちしております。