こんにちは、Zeals CTOの島田です。
本日は、進化するZeals(旧fanp)サービスのアーキテクチャを、 リリース当初と現在で比較して紹介 します。
以下のようなアジェンダで解説していければと思います。
Go(gRPC, Protocol Buffers)を用いたmicrosevicesやkubernetes(GKE)を用いた新しいインフラ環境など、これからのZealsを支えるコア技術について解説できればと思います。
はじめに
まずは、前提を合わせるために、簡単にZealsの機能紹介からはじめます。
Zealsは、LINEおよびFacebook Messenger上で動作するチャットボットサービスで、WebのForm体験をチャット上で完結させる ことができます。
私達は、これを”チャットコマース”と呼びサービスを展開しております。
サービスは大きく分けると、
- チャットボットの管理用Webアプリケーション
- チャットボットへのメッセージ送受信サービス
- 1と2から共通処理を切り出したマイクロサービス群
の3つに分かれています。
1.のWebアプリケーションでは、 ユーザー管理やチャットボットの発話内容(以下、シナリオ)のCRUD, クライアントとのDB連携の設定 等が可能です。
アプリケーションは、Ruby on RailsとReact + Redux(& TypeScript)で実装されています。
2.メッセージ送受信機能では、チャットボットに流入したエンドユーザーに対して、設定されたシナリオの応答や配信処理 を行います。
10万人を超えるユーザーに対する配信もここで行っており、フレームワークを採用しないピュアなPythonで実装されています。
3.のマイクロサービス群は、1,2で共通して発生する処理をAPIとして切り出したロジック達 です。
導入に至った背景も合わせてこちらの記事で説明したいと思います。
リリース当初のアーキテクチャ
技術選定
以下の図は、リリース当初(2016年)のアーキテクチャです。
リリース当初に存在したのは、Ruby on Rails+ReactのWebアプリケーションとPythonのメッセージ送受信サービスのみでした。
リリース時はZealsのエンジニアチームがまだ少数だったため、開発のオーバーヘッドが少ないと予測されるRuby on Railsを採用 しました。
また、当時はWebのアプリケーションのスマホ画面対応も念頭にあったため、フロントエンドのJavaScriptのフレームワークにはReactを選定しました。
Pythonのメッセージ送受信サービスをWebアプリケーションと別コンポーネントにしている理由は、システムをトータルで考えた際のSPOFを無くすためです。
クライアントのアカウントでチャットボットを運用するビジネスの特性上、ユーザーの流入およびチャットボットの発話停止リスクは可能な限り避ける必要があります。
Webアプリケーションの管理画面側でバグや障害が発生した際に、道連れ式にチャットボット機能も停止することを避けるためにこのような構成になっております。
メッセージ送受信サービスにPythonを採用した理由は、前身となるチャットボットサービスが既にPythonで実装されていたからです。
何でも捨ててしまうのではなく、使える資源は使うべきと判断しました。
少し特殊な部分としては、Azure Function + ServiceBus(=GCPでいうCloudFunction, PubSub)の箇所が挙げられます。
上記を簡単なUMLにまとめ図示しました。
Serverless は Azure Function/Cloud Function、Broker は ServiceBus/PubSubとお考えください。
説明すると、プラットフォームのAPIの仕様上、 ユーザーの流入や発話はJSONデータとしてエンドポイントに送信 されます。
ServerlessがBrokerに会話のJSONデータをスタックし、すぐにHTTP 200番を返します。
万が一Pythonのメッセージ送受信サービスが停止した場合も、トピックにメッセージが蓄積されるため、多少レイテンシが増加しても返答可能な構成となりました。
ちなみに当時のインフラはMicrosoft Azureで、Webは一般的な LB + VM構成でした。
リリース当初は、Dockerなどのコンテナ技術の本番運用はまだ少し時期尚早だったと記憶しています。
新しくプロダクトの課題
リリース以降、新しい機能追加がされていきましたが、それに伴い以下課題が生じました。
- SDKをはじめ、PythonとRuby on Railsのどちらの責務にも該当しない機能が必要 になった
- PythonとRuby on Railsで同様のロジック実装が多く必要になり開発コストが増加 した
- RDBに格納する ログテーブルのレコード数が爆発的に増加 した
- サービスのトラフィックが増加し、あらゆる負荷対策が必要 となった
2については、Python/Railsの技術選定がやや裏目に出たと感じています。
同じ言語で書かれていればライブラリ化し各アプリケーションでインポートすれば解決可能ですが、その対応ができないからです。
現在
いろいろと改修を重ねた結果の、現在(2019年9月時点)のアーキテクチャがこちらです。
主な差分としては、以下の部分が挙げられます。
- Azure -> GCPへの完全移行(GKEによるk8s化 )
- 静的ファイル配信のため、Fastlyを導入
- ユーザーへのシナリオ配信のqueueとして、Redis を導入
- Google BigQueryによる 分析基盤の構築
- 複数アプリケーションの共通処理を切り出すために gRPC を導入
本記事を作成するために振り返って気づいたのですが、上記のほぼすべての変更点が2019年に入ってから実施されたものです。
これらの開発プロジェクトを高いスピード感で進められるところに、Zealsはプロダクトだけでなく開発チームや各エンジニアも大きく成長していることを実感しました。
さて、上記開発を高速で進めた背景として、2019年に入り「サービスを導入いただくクライアントが増え、それと同時にトラフィックが爆発的に増加した」ということが挙げられます。
複数アプリケーションの共通処理における責務の問題は、gRPCの導入で解決しようとしています。
現在は、主に「配信対象となるユーザーの絞り込み」の機能がPythonとRails間で共通のロジックで、かつ使用頻度が高いため、そちらから処理を切り出していくプロジェクトを実施しています。
トラフィック増加への問題の対応方針は以下の通りです。
- 動的ページ -> k8sでスケールアウトしやすいアーキテクチャへ
- 静的ページ/ファイル -> Fastlyを導入し対処
- 大量のログデータ -> アプリケーションで参照しないものはBigQueryへ流し込み管理をする
- 大量送信など、急激に負荷のかかるものはRedisでqueueの機構を構築してスケールアウト可能に変更
BigQueryを用いたログ分析環境の構築については、以前書いたこちらの記事で詳しく解説しております。
よろしければ、ぜひご覧ください。
今後の展望
大まかな今後の展望もとい、やりたいと思っていることとしては、以下のようなものが挙げられます。
- Webアプリケーション/メッセージ送受信サービスの両方で提供すべきでない処理をgRPCで切り出し
- 依然として存在するRDBのSPOF化の解決
- 一斉配信の処理をPythonからGo等のコンパイル言語に置き換え高速化
- アプリケーション内の責務の明確化とサービスの分離
- 機械学習によりBigQueryに流した行動ログを分析し、サービスへフィードバックする
さいごに
Zealsでは、発見した課題をワクワクしながら技術で解決するエンジニアを求めています!
いわゆる「1->10」で確実に伸びているフェーズだからこそ経験できることも多いはずです!
スパイシーで楽しい開発がたくさん待っています。
前のめりになれたら必ず楽しい環境なので、もし興味があったら下記募集からをご応募ください!