社内に蓄積された顧客データを整理・統合し、マーケティングに最適なデータに進化させる顧客データ統合サービス「Sansan Data Hub」。そして、あらゆる請求書をオンラインで受け取り、請求書受領から月次決算を加速するインボイス管理サービス「Bill One」。いずれも急成長を遂げており、Sansan株式会社の事業の柱になっているサービスです。
これらのサービスは、データの処理効率やシステムの信頼性などを向上させるために、さまざまなアーキテクチャの工夫が行われています。今回は「Sansan Data Hub」と「Bill One」それぞれの開発の中核を担う千田智己と加藤耕太にインタビューし、前後編の2回に分けて記事化。前編では、両サービスのアーキテクチャ設計の工夫や思想について語ってもらいました。
【Sansan Data Hub】アーキテクチャ解説
――「Sansan Data Hub」と「Bill One」のシステムアーキテクチャの全体像を教えてください。まずは「Sansan Data Hub」からお願いします。
千田:「Sansan Data Hub」はコアの技術としてAzure Functionsを用いており、それぞれの関数の処理をメッセージキューでつなぐ構成になっています。この設計にしている理由は、スケーラビリティの担保にあります。
ローンチ当初から「Sansan Data Hub」は、「膨大な量のデータを処理するシステム」と想定されていました。そこで、基本的にすべての処理を非同期並列分散で行う設計にしました。仮にいずれかの箇所でボトルネックが発生しても、その処理のみ並列度を上げることが可能です。
Azure Functionsのホスティングモデルとしては、専用プランという通常のApp Service プラン上で動かすプランを選択しています。「Sansan Data Hub」は常にデータが流れ続けるシステムなので、サーバーレスの従量課金モデルで使うとかなりコストがかかってしまうんですよ。
また、メッセージングのサービスとしてはAzure Service BusとAzure Event Hubsを使っています。セキュリティを向上させるためにAzure Service BusをPremiumレベルで運用しているんですが、これのレベルは性能の制限がかなり厳しいんですね。
そこで、性能的な制限をオフロードするために、特に大量のデータが流れる箇所についてはAzure Event Hubsを利用しています。一方でAzure Event Hubsはリトライなどの細かい制御が苦手なので、Azure Event Hubsがなんらかの理由で処理できなかったデータは、Azure Service Bus側にフォールバックしています。
また、データストアとしてはもともとKVS(Key-Value Store)のAzure Table Storageを主に使っていましたが、ここ1~2年くらいは新規でAzure Table Storageを用いることはほぼありません。このサービスを使っていたのは、「Sansan Data Hub」で扱うデータの形式が多種多様でありスキーマ設計が難しいから、という理由がありました。
ですが、運用を続ける中でデータモデルについての知見が蓄積されてきたことから、現在は運用省力化や保守性を重視してRDBでデータを管理する方針に切り替えています。
――どこかの段階で、Azure Table Storageを完全にリプレイスする可能性はありますか?
千田:徐々に移行していますね。より正確に言うと、「Sansan Data Hub」はドメインごとに設計を分割しており、データストアやコードベースも分離しています。そのため、一気に全体を置き換えるのではなく、それぞれのシステムを少しずつ刷新していく形をとっています。データを格納するサービスとしては、他にAzure SQL DatabaseやElasticsearch、Azure Cosmos DBなどを使っています。
――今後、アーキテクチャを改善したい点はありますか?
千田:先ほど、「ボトルネックが発生しても、その処理のみ並列度を上げる」という話をしましたが、実はこの設計が完全には機能しておらず、現在はElasticsearchがボトルネックになっているんですよね。そこで、少しずつElasticsearchへの依存を減らそうとしており、Elasticsearchに格納する必要のないデータをAzure SQL Databaseに移行しています。
他には、Azure FunctionsのKubernetes移行をしたいと考えています。Azure Functionsを専用プランにホスティングしているという話をしましたが、この運用だとリソース配置の柔軟性が低いんですね。1台のサーバーに対して高密度にAzure Functionsをのせているんですが、スケールさせようと思うとサーバーをスケールアウトする必要があり、ボトルネックが発生していない関数も並列度が上がってしまいます。リソースの柔軟性や利用効率向上のために、Kubernetesに移行したいです。
さらに、チーム内でgRPCの利用を推進しています。「Sansan Data Hub」はC#と.NETで開発を行っており、Azureも含めて完全にMicrosoftのテクノロジーに振り切っています。そして2020年にリリースされた.NET 5で gRPC-web を標準で提供するようになったため、この機能を活用しています。
【Bill One】アーキテクチャ解説
――では次に「Bill One」についてお願いします。
加藤:「Bill One」はGCPによって構築された、比較的オーソドックスな構成のWebアプリケーションです。ロードバランサーで受け付けたユーザーからのリクエストを、App Engine上で動くBackends For Frontendsが受け付けます。そして、その後段にはCloud Runで動くバックエンドのマイクロサービス群があります。データストアとしてはCloud SQLとCloud Storageを用いています。
――他のクラウドサービスではなく、GCPを選んだのはなぜですか?
加藤:私がチームに参画する前の時代にさかのぼりますが、「Bill One」の最初期のフェーズでは、かつて存在したデータ統括部門DSOC(Data Strategy & Operation Center)により開発が行われていました。DSOCではGCPをよく用いていたため、それを引き継いだ形ですね。それから、App EngineやCloud RunはいわゆるPlatform as a Serviceのサービスですから、サーバーの運用などにかかる工数を大幅に低減できます。新規事業開発では人手が限られるため、運用を楽にできることも利点でした。
――「Bill One」のように新しいサービスをスピーディに立ち上げる場合、モノリシックなアーキテクチャにする選択肢もあります。なぜ、バックエンドをマイクロサービス構成にしたのでしょうか?
加藤:「Bill One」は正式リリースされる前に、サービスの内容を数回ほどピボットしているんですね。その試行錯誤の過程で、ドメインをどのように分割すべきかがある程度見えていたため、マイクロサービス構成にする選択肢が有効だと判断しました。
――「Bill One」がローンチされてから現在までで、アーキテクチャが変わった部分はありますか?
加藤:それほど大きくは変わっていませんが、例を挙げるともともとCloud FunctionsをNode.js・JavaScriptで書いていたのを、Goに書き換えました。Node.jsはプログラムを実行するために多数のライブラリの依存関係を解決する必要がありますし、ライブラリのバージョンアップに伴って非互換変更が入ることもあります。運用工数の削減と、技術的なチャレンジ、ドメイン知識の継承を兼ねて、Goで再実装する選択肢は妥当だと考えました。
これから変更していきたいのは、まずデータベースですね。最近、GCPでAlloyDBというPostgreSQL互換のサービスが出たんですが、これを使うことでスケールアップなどの運用が楽になりそうなのでCloud SQLから乗り換えたいと考えています。それから「Bill One」には請求書検索の機能がありますが、今後データ量がさらに増えてくると処理が重くなる可能性があります。そこで、Elasticsearchなど何かしらの全文検索エンジンを導入したいです。
interview:中薗昴
▼後編はこちら
buildersbox.corp-sansan.com