メルカリ、マイクロサービス化の歴史

中島大一氏:「なぜMicroservicesか?」というタイトルで発表させていただきます、@deeeetといいます。よろしくお願いします。

簡単に経歴を紹介します。

2017年の1月に、メルカリのSREとして入社しました。その後すぐ、組織をスケールさせていくためにマイクロサービス化のプロジェクトが始まり、そのための基盤チームを立ち上げて、今日まで2年間ずっとマイクロサービス(化)のプロジェクトに関わっています。

今日はメルカリのマイクロサービス化の歴史を振り返りつつ、自分がどういうことをチャレンジしてきたのかを紹介して、その経験から得られた学びを共有できればと思います。

最初に、マイクロサービスの歴史についてです。自分が関わったマイクロサービスのプロジェクトは大きく分けて5つあります。

まず、メルカリのマイクロサービス化はUSから始まっています。JPでも始まって、そのための基盤チームを立ち上げて、API Gatewayを実装するなどして、それに合わせて多くのマイクロサービスのリリースにこれまで立ち会ってきました。これらを1個1個見ていきたいと思います。

まず、入社してすぐに関わったのがUSのマイクロサービスです。出張ベースでUSに行って、すでに名村さんを中心に動き始めていたマイクロサービス化の、初期のサポートを行いました。

具体的にやったことは、最初のマイクロサービスとしてサーチ機能の抜き出しをしたり、マイクロサービスがどんどん増えていってもデプロイなどが簡単にできるようにSpinnakerを導入したりしました。

USに関わっていたのは2ヶ月ぐらいで、帰国してすぐ、2017年の7月ぐらいからJPのマイクロサービス化を始めました。最初にやったのはマイクロサービスを動かす基盤を作るための基盤チームをつくるところです。その基盤チームをつくってこの上で最初のサービスを動かし始めるのが、JPのマイクロサービスのフェーズ1かなと思います。

AI出品機能のリリースと課題

詳しく見ていきましょう。まず、最初に作ったMicroservices Platform Teamは何かというと、Backend teamがマイクロサービスの開発とか運用を行うための基盤を提供するチームです。

具体的には、今はKubernetesを使っていますが、Kubernetes用のベースのインフラストラクチャを提供することと、その上でCI/CDやobservabilityといったDevOps関連のツールチェインを提供したり、共通のフレームワークを提供したり、マイクロサービスのベストプラクティスをまとめてドキュメントとして提供したりする役目が、このMicroservices Platform Teamです。

詳しくは「開発者向けの基盤をつくる」というタイトルで発表しているので、興味がある人は見てみてください。

JP Mercariはこれまでさくらインターネットさんをメインのインフラとして採用してきたんですが、マイクロサービスプラットフォームはGCPをメインのクラウドにして、Kubernetesベースのインフラを採用して基盤の構築を始めました。

最初のほうは、USと同じようにデリバリーのためのSpinnakerを導入したり、observabilityのためのDatadogを導入したりしていました。

最初にこの基盤にぶっこんだのが「AI出品」というサービスです。「AI出品」はメルカリの出品機能の1つで、写真を撮るだけで出品時に必要な情報、例えばタイトルやカテゴリを自動で補完してくれるマシンラーニングのサービスです。

このサービスは、モノリスのサブシステムとして呼び出される形式のアーキテクチャとして実装されました。リリースは無事できたんですが、大きな課題が2つありました。

1つ目はセルフサービス化の問題です。基盤としてはぜんぜんできていなかったこともあって、AI出品をリリースするときには、サービスチームには「Dockerfileだけとりあえず書いてくれ」と言っていて、それ以外のインフラのプロビジョニングや、デプロイの設定やモニタリングは全部Microservicesがやる必要がありました。今後サービスが作られるたびにMicroservices Platform Teamがそこに入っていったらぜんぜんスケールしないことが目に見えていました。

もう1つはアーキテクチャ的な問題です。このままのアーキテクチャだと結局モノリスのサブシステムとしてしかマイクロサービスを実装できなくて、モノリスから独立してマイクロサービスを開発したり、モノリスからマイクロサービスに段階的に抜き出していくのは難しい状態でした。

API Gatewayの開発

これらの課題を解決するために動き始めたのが、API Gatewayの開発とMicroservices Starter-kitの開発です。次のフェーズでは、これらを使って新規サービスである「Offer service」をリリースしました。

API Gatewayはどういうことをやっていたかというと、これまでのリクエストの流れとしては、クライアントが直接モノリスのAPIを叩いている状態でした。

この間に基盤上で動くAPI Gatewayを挿し込みました。

こうすると何がうれしいかというと、ストラングラーパターンという手法で、このモノリスのほうに流れているリクエストを段階的に新基盤のほうに移していくことが、これによって可能になります。

これでモノリスと独立して新規サービスをプラットフォーム上にリリースすることができるし、モノリスの機能をマイクロサービスとしてどんどん切り出していくこともできるようになりました。

このAPI Gatewayは、拡張性を考えて、Goでフルスクラッチで実装しました。

当時メルカリのUKもあって、UKでもマイクロサービス化を進めていきたい構想だったので、Mercari JPだけじゃなくてほかのリージョンでも使えるものを作りました。結局UKでは使えなかったんですが、メルペイを作るときにGatewayが必要になって、このとき作ったGatewayが使えるようになったので、結果としてそういう実装にしてよかったと思っています。

もう1つは、のちのメルペイとなる「決済プラットフォーム」というプロジェクトがすでに動いていて、そのプロジェクトのために@kazegusuriさんたちが認証基盤を実装していました。Gatewayはそれと連携することに決めて、これをやることで、将来的にGatewayを中心としたマイクロサービス間の認証認可をどうしていくかという構想がここでできました。

Gatewayはデザインから実装してリリースまで半年ぐらいかかりました。実装そのものはそんなに難しくないんですけど、メルカリの全リクエストを受けないといけない。つまり、ピーク時でいうとだいたい6万RPSぐらい来るようなところで、そこが死ぬとメルカリ全体が死ぬようなコンポーネントなので、段階的に出していかないといけないし、テストもすごくしっかりやらないといけなくて、そういう部分でかなり大きなプロジェクトでした。

最低限必要なものを一発で作成するMicroservices Starter-kitを開発

次にやったのが、さっきも言ったようなマイクロサービスをつくるためにPlatform Teamが毎回巻き込まれるという課題があったので、それを解決するためにMicroservices Starter-kitをチームで開発し始めました。

Microservices Starter-kitは、マイクロサービスを作るために必要となる共通インフラをブートストラップするTerraformのモジュールです。

例えばGCPのプロジェクトだったり、Kubernetesのnamespaceを作ったり、PagerdutyのTeamだったりGitHub Teamみたいな、最低限必要なものを一発で作れるようにするものです。

これも具体的には下のスライドが詳しいので、興味がある人は見てください。

これを作ることによって、各チームがプラットフォームチームのサポートなしに、自立的にインフラのセットアップをして、サービスをデプロイできるようになりました。

このAPI GatewayとStarter-kitによって最初に実現したものが「Offer service」です。Offer serviceは、購入者が出品者に対して「この値段だったら買いたいです」というオファーを出して、出品者が許可すると取引が成立するというサービスです。このサービスをこのアーキテクチャでリリースすることができるようになりました。

メルペイをメルカリの基板上へ

同時に1個あった大きな意思決定の1つとして、このときすでにメルペイのプロジェクトが動いていて、メルペイのマイクロサービスをこのMicroservices Platformに載せるという意思決定がされました。

理由は大きく3つありました。先ほどお話ししたようなMicroservices Starter-kitが揃いつつあったこと。それから、今後メルカリのマイクロサービスがどんどんこの基盤上に出されていったときに、サービス間の連携がやりやすくなること。

それから「メルペイを載せると、メルカリの基盤のほうのセキュリティレベルを上げないといけないんじゃないか?」というセキュリティ的な懸念があったんですが、メルカリも将来的にはメルペイと同じぐらいセキュリティレベルを上げる必要があったことも理由になりました。

この3つの理由で、メルペイもMicroservices Platform上に載せることを決めて、プラットフォーム上でメルペイのマイクロサービスの開発もこの時期に始まりました。

モノリスのコードフリーズを決定

API Gatewayをリリースして、その配下に新しいマイクロサービスをリリースすることができたし、Starter-kitや基盤も整いつつありました。しかし、マイクロサービスに真剣に取り組んでいるのはチームのバックエンドエンジニアとPlatform Teamだけで、モノリスから機能を抜き出していくことに取り組む人があまりいないという課題がありました。

この課題を解決するため、去年の春ぐらいに、モノリスをコードフリーズするという意思決定がされました。これによって、出品機能の抜き出しが行われました。これが3つ目のフェーズです。

これは、一部の例外を除いてモノリスには基本的に新機能を追加しないという意思決定で、マイクロサービス化をさらに加速させるためにCTOレベルで決められました。

急にフリーズすることはできないので、半年ぐらい猶予期間を設けて、その間に今後必要となるコア機能をマイクロサービスとして切り出すというプロジェクトが始まりました。

今後メルカリで重要になっていきそうで、かついろんなコア機能を触って難易度が一番高かった出品機能を、マイクロサービスとして切り出すことになりました。

これは実際に出品機能の切り出しをリードしたエンジニアのスライドから拝借しています。切り出しは、まずメルカリのメインのUXの機能を列挙するところから始まりました。

例えば出品や、検索や、購入という部分がメインのUXとしてあります。そのUXに対して、それらの基礎となるコアコンポーネントを分析しました。

この分析によって、例えば出品だったら「User」や「Item」や「Photo」がコアコンポーネントになりそうだとわかりました。この分析を基に、必要なマイクロサービスをどんどん切り出していきました。

このプロジェクトによって出品機能がモノリスから切り出されて、多くのマイクロサービスがこのプラットフォーム上で動くようになりました。

データの移行まではスコープ外だったので、よりデータに近い部分で、さくらのデータセンターにもマイクロサービスがデプロイされるというのもこの頃に起こり始めました。

出品が切り出されたあとにメルペイのリリースもありました。

詳細は省くんですけど、今は40以上のマイクロサービスがこの基盤上で動いていることになります。

マイクロサービス化は進んでいるようですが、まだまだ課題はあります。一番大きいのはやっぱりまださくら(インターネット)のほうにデータが残っている部分です。

これを切り出さないかぎり、各サービスが独立してリリースを行うというマイクロサービスの利点を得ることができません。

この課題を解決しているのが今まさに動いているフェーズで、データのマイグレーションを含めたマイクロサービス化や、さらなる出品以外のUXの部分も切り出していこうと、まさに今やっています。かなりすごいプロジェクトが動いている状態です。

重要なのは“なぜ”やるか

ここまで、メルカリのマイクロサービスの歴史をざっと紹介しました。ここからはこの経験から得た学びを、今後マイクロサービスをやろうと思っている人に対して簡単に紹介したいと思います。

1個目は「Why」「なぜやるか?」が一番重要であるということです。

ここで、この発表のタイトルである「なぜMicroservicesか?」を改めて整理したいと思います。

「マイクロサービスは組織論」とはよく言われますが、マイクロサービス化の究極的な成果物は新しい組織図です。新しいアーキテクチャに基づくチームの編成と、組織の再編を狙うことが一番の大きなゴールです。

この図は、マイクロサービス化が可能にしてくれることを端的に1つの図で表していて、けっこうお気に入りです。

まず、右下のMicroserivice architectureよって、自立して独立したcross functionalなチームをその周りにつくれるようになります。この組織とマイクロサービス化という2つが揃って初めてできるようになるのが、Continuous Deliveryというプロセスそのものです。

要は、こんな感じでモノリスとBackend Teamがあるところを、こんな感じでマイクロサービス化して抜き出して、その周りにチームを配置することで、このチームで独立してContinuous Deliveryを回せるようになる。つまり、アウトプットを加速させることができる。これが、マイクロサービス化で一番やりたいことです。

ベストな「How」は「Why」でしか規定できない

先ほど発表であったように、メルカリの組織はかなり大きくなっています。組織が大きくなってもパフォーマンスを落とさず、むしろアウトプットを加速させることこそが、マイクロサービス化をする一番の「Why」です。

ではなぜ「Why」が重要かというと、どうやるかなんていうのがいくらでもやり方はあって、一番ベストな「How」は「Why」でしか規定できないからです。なので、「Why」がおかしいと「How」もおかしくなってしまいます。

マイクロサービス化というでかいプロジェクトでは何度も意思決定をしなければいけないので、「なぜマイクロサービスか?」という「Why」を常に意識していないと、けっこう失敗します。

じゃあ振り返ってみて、自分はどうだったか。今でこそ組織論だ、と偉そうに言っていますが、最初はやっぱり「どう抜き出すか?」という「How」に盲目的だったことがありました。そうなるとどう失敗するかを簡単に紹介します。

1つの失敗として、Gatewayの実装があります。

さっき、Gatewayの配下にサービスを配置することで、リクエストのルーティングができるようになったと話しましたが、(逆にいうと)今、ルーティングを設定するためには、Gatewayのコードベースにプルリクエストを送って、Gatwayを管理しているチームがそれをマージしてデプロイする、という手順が必要な構成になっています。

多くのサービスがGatewayの配下で動き始めた現在、Gatewayがボトルネックになりつつある。もし自分がGatewayの実装時にマイクロサービスの「Why」、自立したチームをつくっていくことを強く意識していたら、おそらくもっと設定のデリゲーションができる実装をしたと思います。

こういう状況を避けるために、「Why」を中心とした意思決定を常にやることがとても大事です。これが1つ目の学びです。

ボトムアップだけでは解決できない問題も多い

もう1つは、トップダウン的なディシジョンが必要ということです。

トップダウン的に物事を決めるのは悪いというイメージが、自分の中にもありました。しかし、ボトムアップだけでは解決できない問題も実はめっちゃあるんです。マイクロサービスが組織の課題だからこそかもしれませんが、マイクロサービス化に関わることで、両方のバランスが取れていることが大事なんだと、強く感じるようになりました。

先ほどお話ししたコードフリーズはまさにトップダウン的な決定でしたが、実際どうだったかを振り返ると、最初はやっぱり内部で反発があって、ブチ切れる人もいました(笑)。今になって振り返ると、コードフリーズをしていなかったら、今ほどマイクロサービス化は進んでいなかったと思います。こういう意思決定ができることこそがメルカリの強さじゃないかと思っています。

話が少し逸れますが、マイクロサービス以外にも、例えば最近の技術プラクティスで、SLI/SLOとかDesign Docみたいなものも、ある種トップダウン的に決められないと効果を発揮しないと思うんです。トップダウンとボトムアップのバランスがすごく大事なのかなと、この経験を基に感じるようになりました。

完全な自由はtrueではない

3つ目の学びは、完全な自由はtrueではないという部分です。

マイクロサービス化の記事や文章を読むと、よく「マイクロサービスは最も適した言語やフレームワークを自由に選択できる利点がある」と書かれていますが、それはtrueかどうかと聞かれたら、僕は短期的にはNoだと答えます。

マイクロサービス化の初期は、かなり多くのマイクロサービスをリリースすることになります。その開発のフリクションをなるべく減らすことはめちゃくちゃ大切で、そのために技術スタックをある程度統一化したり、基盤を整備することは必須だと思っています。

あとは、新しい技術を増やしていくときは、その組織の大きさ(基盤にどれだけリソースを割けるか)や、マイクロサービスの成熟度によって増やすのがいい戦略じゃないかなと思っています。

こういうと「自立したチーム作りと拮抗しないか?」と思うかもしれませんが、「独立した意思決定をして機能を開発できるようにすること」と「技術的な選択をできること」は次元が違う話だと思います。前者ができて初めて、後者をやる意義がある。まずは独立したチームで機能開発ができるよう目指すことが一番大事かなと、今は思います。

駆け足になりましたが、メルカリのマイクロサービス化の歴史と、そこから得られた個人的な学びについて共有しました。以上で発表を終わります。ありがとうございます。

(会場拍手)