はじめに
こんにちは。High Linkのデータエンジニアの芦川 (@hirorororo772) です。
私たちが運営する香水サブスクサービス「カラリア」では、「香水診断」、「レコメンド機能」、「フレグランスプロフィール」など、データを活用したさまざまな機能を提供しています。
こういった機能を提供するためには、ロジックの開発だけでなく、安定的に提供するための基盤や開発を加速させるためのCI/CD基盤やデータパイプラインの構築(MLOps)が重要になってきます。
今回は、カラリアにおけるデータを活用した機能の裏側についてご紹介したいと思います。
スタートアップである私たちは、小さくはじめてスピードは保ちつつ、中長期的に開発スピードや運用コストにレバレッジを効かせられるよう意識してきました。
設計面で考慮したポイントや、実際に運用してみた所感なども併せてご紹介いたしますので、これからミニマムにデータ基盤・機械学習基盤を立ち上げようと考えている方のご参考に少しでもなれば幸いです。
基盤整備の背景
弊社ではデータ基盤の整備や機械学習基盤の整備を2021年から注力し始めました。
それまではデータウェアハウスも導入しておらず、サービスDBに直接metabaseを連携して権限を持った一部の人が分析などに活用している状態でした。機械学習APIについても一部提供しておりましたが、データパイプラインなどもなく、CI/CD基盤も整っていないような状態でした。
当時はサービスのユーザーも急増(前年比11倍)していたこともあり、今後のスケールを考えた上で改めて基盤を整備することにしました。
データ基盤を整備することで事業へのデータ活用を促進すること、機械学習基盤を整備することでユーザー体験の強化を高速に行える状態にすることを目的としてスタートしました。
設計面で考慮したポイント
スタートアップというリソースが少ない中で、安定的にサービスを提供し運用可能な状態を構築すること、長期的に見た時に開発にレバレッジを効かせられるようMLOpsを取り入れていくことを意識してアーキテクチャ設計やパイプラインの設計を行いました。
重視したポイント
- 全体の工数はなるべくミニマムに抑えて要件をクリアすること
- なるべくマネージドサービスを活用し、運用負荷を抑えること
- DockerコンテナをベースにAPIをホスティングすることでベンダーロックインを低減すること
- MLOpsは初めから意識し、モデルのアップデート自動化やAPIのCI/CDを実現することで少人数でも運用できる状態にすること
スコープアウトしたポイント
- 大規模トランザクション・大規模データに耐えうる設計
- スタートアップではすぐに考えないといけないポイントではないと判断しました。
- ディープニューラルネットワークなどGPUを用いてサービングしないといけない機能への対応
- まずはCPUで動くものベースに進め、必要になってからアップデートというタイムスケールで十分だと判断しました。
データ基盤とサービスの全体像
結論から言うと現在の構成は以下のような形になっています。
- カラリアサービスの本体サーバーはAWSのECS Fargateで運用。
- AWSからGCPに展開しているAPIサーバーにアクセスしています。
- サービスDBのデータやGoogle Analyticsで取得したユーザー行動ログはBigQueryに集約されています。
- APIサーバーはFastAPIで実装したコンテナをContainer Registryに格納し、Cloud Runで実行しています。
- 例として図示しているCloud Runサービスはメモリベースのアルゴリズムになっているので、コンテナ起動時に必要なデータはGCSから読み込みます。
- 日々アイテムデータやユーザーインタラクションデータは更新されるので、日次バッチで、以下の一連のワークフローをCloud Workflowsで実行しています。
- BigQueryからデータを読み出し
- Cloud Functionsで前処理
- 最新のデータをGCSに保存
- Cloud Runを起動し直すことで最新のデータを読み込み直す
以降、各コンポーネントについて深掘りしていきたいと思います。
データ基盤
データ基盤の整備として、まずデータウェアハウスサービスの導入を行いました。
この際、GCPのBigQueryの他に、AWSのRedShiftも候補に上がりましたが、Google Analytics4のBigQuery export機能を活用したいためBigQueryを選定しました。
主なデータソースですが、カラリア本体のサービスはAWSで運用しているため、RDSにカラリアサービスのマスターデータ、トランザクションデータが格納されています。これを毎日BigQueryに同期するデータパイプラインをEmbulkを用いて実現しています。
私たちはデータの民主化の観点から、BigQueryに格納されているデータは基本社内なら誰でも分析可能な状態を目指していました。そこで、「BigQueryに転送する時点」で個人情報は取り除くorマスクするようにして「BigQueryにあるデータ=情報セキュリティ的に安全」な状況を作ることでセキュリティ管理を容易にしつつアクセスフリーな状態を作りました。
当初はとにかくミニマムかつスピーディーに基盤を整えることを重視していたので、徐々に課題も見つかり改善を行なっております。
また、上述の通りデータ加工におけるELT(Extract, Load, Transform)のELの部分はEmbulkとなっておりますが、Tの部分はdbtを活用して運用しております。 こちらも別記事にて詳細をご紹介しておりますのでよろしければご覧ください。
機械学習基盤
データ基盤をBigQueryにしたことに伴い、データ連携のしやすさの観点から機械学習基盤全体をGCPに寄せるようにしています。最近ではAWSもGCPもお互い同等の機能を提供していますので、どちらを用いても基本的なことは実現できるかと思います。
APIサーバー
GCPにおいてAPIをホスティングすることができるサーバーレスプラットフォームはCloud Run、App Engine、Cloud Functionsなどが挙げられますが、その中でも今回は以下の点を考慮し、Cloud Runを利用することにしました。
- App Engineと比較して、Cloud Runはコンテナの実装に柔軟性がある点。
- Cloud Functionsと比較して、Cloud Runは一つのインスタンスで複数のリクエストを捌くことができる点。
仮説検証等実験はGoogle Colaboratory(Jupyter Notebook)を利用し、Cloud Runで実行するアプリケーションはFastAPIを用いて実装しています。
Cloud Runはゼロスケーリングに対応しており、リクエストがない間はインスタンス数を0にして料金がかからないという利点があります。
一方で、リクエストが入ってからコンテナを起動するため、コンテナ起動時にデータを読み込むような場合は一時的にレスポンスタイムが大きく増大してしまいます。これをcold start問題と呼びます。
当時はPREVIEW版だったのですが、現在は最小インスタンス数を1にしてCPUを常に割り当てる設定がGA版になっているので、これを用いて対処しています。
最近ではcold start問題を低減する Startup CPU boost という機能もPREVIEW版になっているので気になる方は併せてチェックしてみてください。
前処理
機械学習APIにおいて、事前にベクトルを計算するなど、多くの場合前処理が必要になってきます。 このような処理はパイプライン上でトリガされることがメインで、日々たくさんのリクエストを捌く必要がありません。 そこでCloud Run同様ゼロスケーリングに対応しており、なおかつ関数の実装のみで簡単にデプロイすることができるCloud Functionsを選定しました。
Cloud Functionsはpython環境ではFlaskと同等の機能を提供するので、リクエストに対するレスポンスの部分をmain.pyに実装するだけでOKです。また、必要なパッケージはrequirements.txtに記述しておけばpipを使用して反映することが可能です。
ネットワーク
APIのルーティングにはCloud Load Balancerを用いています。アクセスされたエンドポイントに応じて特定のAPI(Cloud Runコンテナ)にルーティングすることを可能にしています。
また、Cloud Armorと組み合わせることでアクセスのIP制限を行い、AWSの特定のリソースからのみアクセスできるようにしています。
MLOpsの取り組み
提供するサービスのデータソースは日々アップデートされますし、アルゴリズムを改善していくとアプリケーションの実装も日々アップデートされていきます。
一方で、データソースが新しくなるたびにモデリングをして手動でデプロイするのは大変ですし、コンテナをアップデートするたびにデプロイプロセスを手動で行うとヒューマンエラーの温床となります。
こういったアップデートの自動化を意識することで少人数でも運用負荷を減らし、中長期的な開発スピードの向上を見込むことができます。
MLパイプライン
日々アイテムデータやインタラクションデータは更新されるので、日次バッチで、以下ようなの一連の処理をCloud Workflowsを用いて定義・実行しています。
- BigQueryからデータを読み出し
- Cloud Functionsで前処理
- 最新のデータをGCSに保存
- Cloud Runを起動し直すことで最新のデータを読み込み直す
ワークフロー実行のトリガとしてはCloud Schedulerを利用しています。
新しくAPIを実装する際には必ずワークフローとセットで実装することで自動アップデートを実現するだけでなく、前処理とアプリケーションの依存関係を明示することができ管理を容易にします。
また、ワークフローに正常完了・失敗のslack通知を仕込むことで毎日簡単にモニタリングができるように工夫しています。
CI/CD基盤
CI/CDにはGitHub Actionsを利用し、特定のブランチにマージされると同時に最新のコンテナをContainer RegistoryにプッシュしてCloud Runサービスを起動しなおすようにしています。
Cloud Functions や Cloud Workflows についても同様に、プッシュ → デプロイを自動化することが可能です。
おわりに
今回はカラリアのデータ基盤と機械学習基盤についてご紹介しました。世の中にたくさんあるアプローチのうちのひとつとして、ご参考になりましたら幸いです。
最小限でも、パイプラインの整備とCI/CDを整備したことで後々の運用負荷は大幅に減ったことを実感しています。
カラリアではこれから新しいサービスや機能がどんどん増えてきてアルゴリズムだけでなく、基盤周りの面白いフェーズになってきていると日々感じております。
宣伝になってしまいますが、私たちと一緒にデータ・機械学習を通して香りとの出会いを最適化していくエンジニアを募集しております!
https://herp.careers/v1/highlink/RIGo8XhyyCPQ?utm_source=hatena&utm_medium=highlinktechblog&utm_campaign=coloria-data-platform-mlopsherp.careers
https://herp.careers/v1/highlink/-0mpTggpwsjk?utm_source=hatena&utm_medium=highlinktechblog&utm_campaign=coloria-data-platform-mlopsherp.careers