「CLOVA」から「出前館」コンシューマサイドの開発へ

東山昌彦氏:東山から、出前館のコンシューマサイドのLINE KYOTOサーバーサイドエンジニアの取り組みについて説明したいと思います。

まず自己紹介です。私は、京都開発室のK2チームでサーバーエンジニアをしています。Java、Kotlin、Spring Boot、Pythonを使っており、LINEではもともとは「LINE CLOVA」を開発していて、途中から出前館に来ました。

好きなアルゴリズムは、形態素解析なんかで使われているアルゴリズムのダブル配列が好きでRubyとかJavaとか、何種類かの言語で実装しています。最近行けていませんが、趣味は旅行です。

(スライドを示して)LINE Engineeringにインタビュー記事が掲載されたので、ぜひ見ていただければと思います。「LINE Engineering」で検索すると、LINE Engineeringのサイトが出てくるので、インタビューからたどってもらえれば私の記事が読めます。

あと最近で言うと、アプリのチームになりますが、同じ出前館で、しかも京都開発室で開発をしている黒澤さん(黒澤慎治氏)のインタビュー記事が載っているので、もしよければ見てください。

使っている言語・フレームワーク・ライブラリ・インフラの説明

今日は、約2年前に出前館にジョインしてからやってきたことと、そのアーキテクチャについて説明できればなと思っています。

「やってきたこと」。SMS認証の機能の導入、開発環境の整備、レコメンドAPIの開発、レビューサービスの開発、バナー配信サービスの開発などをやってきました。

コンシューマサイドとはそもそもどういうものなのか。先ほどの古田さん(古田大志氏)の説明でだいたいわかると思いますが、出前館のサイトやアプリなど、出前館を利用する人向けのシステムをコンシューマサイドといいます。

(スライドを示して)具体的に使っているTech Stackです。これはサーバーサイドがメインです。コンシューマサイドのBFFは、先ほどの加盟店のBFF(Backend For Frontend)と違って、フロントチームがNode.jsで作っていたりするので、そちらが利用しているものは入っていません。サーバーサイドではJava、Kotlin、Terraformが言語としてよく使われています。

フレームワーク、ライブラリはSpring Boot、Retrofit2などを使っています。インフラとしては、出前館のサーバサイドすべてで導入しているNewRelicを利用していたり、DBはOracle Databaseをメインで使っています。最近はOracle Exadataとかに移行したりとか、そういうこともやっています。

あとは、AWSをよく使っています。RedisのElastiCacheやMySQLのAuroraを使っています。Terraformを使って、AWSのインフラをInfrastructures as a Codeというかたちで管理してます。

やってきたことその1 SMS認証機能の導入

やってきたことを説明します。まず、SMS認証機能を導入しました。これはどういうものかというと、会員登録時にSMSによって電話番号が正しいものであることを確認して、同じユーザーが重複して登録するのを防止する機能です。

これはそんなに複雑なことはしておらず、LINE内部にSMS認証を代わりに実施してくれるサービスがあったので、そのAPIを利用するかたちで実装しました。

SMSを送れるサービスはたくさんありますが、きちんとユーザーに送信され、到達するかを考えると、けっこう大変です。「実際LINEで使われている機能があるのだから、それを使えば信頼性は高いよね」というところでそうなったところもあります。

(SMS認証機能の)何が大変だったか。内製化への移行期だったのが一番つらかったです。最初、LINEのメンバーはレポジトリにコミットもできず、出前館のプロパーの人に「コミットしてください」とお願いをすることしかできませんでした。

また、利用可能な開発環境がないコンポーネントが存在していました。請負契約している委託先には開発環境がきちんと構築されているけれど、僕らのほうはなかったり。

他にも、開発環境のあるコンポーネントも環境数が少なく、他のプロジェクトで利用されていたことにより、クオリティアシュアランスという品質保証のテストの直前まで動作確認できなかったりなど、そういったことがありました。

「実装してよかったこと」。出前館にSMS認証機能が導入されたことで、新規利用者向けクーポンを新しいユーザーがきちんと利用してくれるようになったのが本当によかったと思っています。

やってきたことその2 開発環境整備

次にやってきたことは、開発環境整備です。先ほどお話した当初の環境がつらかったので、何がなんでも必要だということで開発環境を整備してきました。(スライドを示して)ちょっと図が見にくいのですが、QAのために3面まで用意しています。

このへんのはステージング環境で3面あって、こちらがベータ環境で5面フロントからつながっている環境を用意してます。それにプロダクションが1面ですね。

インフラ構築側のメンバーと、コンシューマサイドの開発メンバーの2名で開発環境を整備してきました。もう1人の担当者がメインでやってくれて、私はサブでやっていました。

その他にも、例えば「ブランチルールとかどうしていきます?」とか「GitHubにきちんと移行しましょう」とか議論をしました。もともとはSubversionを使っていたんですね。それをGitHubに移行しましょう、ブランチをどうしましょうという話をしていたのがこの頃でした。きちんと開発できる体制が整ってきたなと思えてきたのが、この頃でした。

やってきたことその3 レコメンドAPIの構築

その後にやったのが、レコメンドAPIの構築です。出前館のサイトを見てもらうと、ログイン後のトップページの上のところに「あなたにおすすめ」と店舗が出てきます。そのレコメンド機能を実装しました。

レコメンドデータ自体は、LINEのData Scienceセンター​​のチームが作成してくれて、日次バッチでそのレコメンドデータを取り込んでAPIで配信するという仕組みにしていました。

(スライドを示して)一番右下のところですね。LINE社内のGPUクラスタからオブジェクトストレージのサービスにデータを入れます。

あとはデータ連携をして、Amazon S3にデータを持ってきて、そのデータをバッチサーバーからRedisに書き込んで、APIサーバーはそのRedisの情報を受け取ります。そこまでで、いったんバッチの処理は終わりです。

あとはユーザーからページ閲覧をWebフロント経由を通してAPIにレコメンドデータリクエストを送った時に、Redisからデータを取得します。Redisから取得したデータには、店舗のIDが入っているので、その店舗のIDの情報と店舗のデータを紐づけて、ユーザーに返します。その店舗のデータを持っているのはElasticsearchです。そんなかたちに作りました。

これは何が大変だったのか。トップページで、かつ、キャッシュができないところなので、DB高負荷のため1度ロールバックしました。表示するために必要なテーブルがけっこう多く、いろいろ結合するとけっこう重たかったので、ひたすらつらかったねという状態でした。

先ほどElasticsearchの話を出したと思いますが、もともと最初はDBで実装していました。その後、データの非正規化して、リアルタイムでElasticsearchへ持っていきましょうというプロジェクトが別に動いていたので、そちらの成果を利用する方式に変更しました。

この頃に、やっとライバルに対抗できる機能が揃ってきたかなという状態になりました。

やってきたことその4 レビューサービスの構築

残り時間が少ないので端折って説明していきますが、その次はレビューサービスの構築をやりました。この頃からマイクロサービス化をきちんとやっていくほうに進めていける状態になりました。

(スライドを示して)ここはけっこう複雑な構成に見えますが、基本的にはWebフロントからレビューAPIサーバーにリクエストがあって、それらがレビューのDBを見て、あとキャッシュはRedisを使うぐらいの、実はけっこうシンプルなシステムの構成になってます。

レビューはそもそも、注文した人に対してレビューをしてくださいと依頼するので、注文情報が絶対必要です。

じゃあ注文情報をどうやって受け取るのか。注文情報は、本当はAPIで連携してもらうのがよいのですが、この頃はその部分はまだできてなかったので、(スライドを示して)ここの右下のところにあるOracleに全部のデータが入っているので、そこから間接的に取得します。

AWS上にレプリケーションしたDBからバッチで注文データの情報のレビューに必要な分を抜き出して、レビューに必要なかたちに変換して、DBを入れるというのを定期的に実行するバッチを作成し、がんばって切り出しました。

なので、今後も修正しなきゃいけないところではありますが、そのように1サービスで完結するように持ってきました。

(スライドの)上のほうは、クラウドごとでレビューの写真画像も投稿できるので、その開発対応についてです。

やってきたことその5 バナー配信サービスの実装

最後です。バナー配信サービスについて。例えばユーザーがWebページにアクセスしてきた時に、ユーザーに合わせてバナーを配信したり、ルールに基づいてバナーを配信する仕組みを作りたいねという話をしていて、それを実現したのがこの実装です。

アーキテクチャとしては、管理画面でユーザーのアクションルールと、実際どのバナーを出すかというアクションを2つ定義してあげて、それをMemoryDBに格納します。

MemoryDBは、Redisの少し高耐久のものです。ユーザーに関しては、ALB(Application Load Balancer)からBFF、ALBを経由して、配信サーバーにリクエストが来て、さらにルールだけで判断できないところがあれば奥のAPIサーバーにアクセスして、参照DB経由で必要な情報を取ってきて、ルールにマッチするかどうかを判定して、バナーを返すというかたちで実装しているのがバナー配信サービスです。

このような感じで、いろいろがんばってきましたよというのが、まとめです。以上で僕の発表は終わります。