US版Mercariのリニューアルと今後 (サーバサイド)

この記事はMercari Advent Calendar10日目の記事です。昨日は@syu_creamメルカリのデータ分析基盤の紹介〜BigQuery周辺の話〜でした。
本日はUSサンフランシスコのサーバサイドエンジニア@deme0607が、今年2017年にリニューアルしたUS版Mercariのサーバサイドについてご紹介します。

現在のアーキテクチャ

Brand new US Mercari (Android 編)でもご紹介したように、US版Mercariは今年、より一層USマーケットにフィットしたアプリを提供しUSでの成長を加速すべく、大きなリニューアルが実施されました。前述の記事はAndroidアプリのリニューアルに関するものですが、iOSアプリやAPIサーバも同様にリニューアルしています。

以下の図は、リニューアル後のUS版Mercariのサーバサイドアーキテクチャです。

f:id:deme0607:20171208043026p:plain

ここでは、リニューアルにおける主な変更点についてご紹介します。

API gateway + microservices + monolith API

従来、MercariではPHPで実装したmonolith APIを採用してきました。一方で事業の拡大やそれに伴う開発チームの増員、東京・サンフランシスコの2拠点開発が進むにつれて、microservicesアーキテクチャへの移行の必要性も大きくなってきました。しかしこのAPIは既に数十万行のコードからなる大型のアプリで、限られた開発リソースでそれら全ての機能をゼロから書き直すのは現実的ではありませんでした。そこで私たちは、以下のようなアーキテクチャを採用しました。

  • API gateway
    • iOS/Androidクライアントからのリクエストを全て受けるAPI
    • 必要に応じてmonolith APIやmicroservicesの各serviceにリクエストをproxyする
    • Goで実装
  • microservices
    • リニューアルに伴う新機能はmicroservicesで実装
  • monolith API: 従来から開発・利用してきたAPI
    • 新アーキテクチャではAPI gatewayを通して利用

上記のようにAPI gatewayのバックエンドとして従来のmonolith APIと新規のmicroservicesを配置することで、リニューアル後も引き続き必要なコア機能はmonolith APIを利用しつつ、新機能や大幅な変更が伴う機能はmicroservicesとして新規に実装できるようになりました。またAPI gatewayの導入により、後述のクライアント・APIサーバ間のリクエストフォーマットの変更も実現できました。

Protocol Buffersの導入

従来JSONを利用してきたクライアント・APIサーバ間のリクエストフォーマットをProtocol Buffersに変更しました。Protocol Buffersとは、Googleが公開したシリアライゼーションフォーマットで、JSONやXMLと比較すると高パフォーマンスであることや、モデルの自動生成が可能といった特徴があります。
また、API gatewayと各種microserviceの間の通信にはProtocol Buffersを利用したRPCフレームワークであるgRPCを導入しました。

Protocol Buffersを導入することで、クライアント・APIサーバ間やサーバサイドのサービス間通信のフォーマットを定義しやすくなり、仕様を決める際のエンジニア間のコミュニケーションが改善しました。APIのインタフェースの変更・追加が発生するような新機能を開発する場合、まずはProtocol Buffersの定義ファイルをAPI・クライアントエンジニア間でレビューします。その後定義ファイルからAPI gateway向けのGo、Androidクライアント向けのJava、iOSクライアント向けのSwiftモデルファイルを自動生成します。生成したモデルファイルを各アプリにインポートしてからビジネスロジックやユーザインタフェースを実装するので、仕様の齟齬による手戻りやバグが削減され、開発効率が向上しました。

kubernetes上で動作、Spinnakerによるデプロイ

API gatewayや各microserviceは全てコンテナ化し、Google Kubernetes Engine(以降、GKE)上のkubernetes(以降、k8s)上で動作しています。k8sとはコンテナのオーケストレーションツールで、GKEはGoogleがGoogle Cloud Platform上で提供するk8sのマネージドサービスです。

リニューアル版Mercariのリリース当初はSlackのChat Botからkubectlコマンドを実行することでアプリケーションのデプロイを実施していましたが、現在はGKEに対応したContinuous DeliveryツールであるSpinnakerを導入しています。Spinnakerを導入することで、

  • microservice毎のアプリケーション・デプロイの設定を各サービスの担当者が柔軟に行えるようになった
  • SpinnakerのBlue/Green DeploymentによるImmutable Infrastructureの実現
    • Immutable Infrastructure: デプロイ後のアプリケーションやコンテナの状態が変更されない環境

といった改善が実現しました。Spinnakerについて詳しくは、@deeeetの記事もご覧ください。

tech.mercari.com

リニューアルがチームにもたらしたもの

各チーム/エンジニアへの権限委譲

microservicesアーキテクチャの導入により、サービス単位の設計や開発に利用する技術スタックの選択の自由度が高まりました。当然、Mercariのプロダクト全体として担保すべきパフォーマンスや信頼性の基準はクリアする必要がありますが、サービス毎の要件やアサインされたメンバーの得意分野によって柔軟な意思決定が開発チーム・エンジニアの単位で行えるようになりました。

具体例としては、大量のデータを保存する必要がある新機能向けのmicroserviceを開発する際に、データストアとして高い整合性と水平スケーリングの機能を備えたCloud Spannerを採用しました。また、これまで内製システムで実現してきた商品検索・Push通知・A/Bテストといった機能の一部を外部のSaaSに試験的に置き換え、その効果を検証するといったことも行っています。

多様なエンジニアが活躍しやすい環境になった

まず第一に、プログラミング言語を始めとする技術スタックの選択肢が広がりました。メルカリでは事業の拡大に伴い、様々な技術的バックグラウンドをもったエンジニアがチームにジョインするようになりました。
例えば従来のmonolith APIはPHPで実装されており、サーバサイドのプロダクト開発にはPHPのスキルが必須でしたが、microservices化に伴いPHP以外の言語でもプロダクト開発が可能になりました。
前述のAPI gatewayや現在稼働中のmicroservicesは全てGoで実装していますが、今後は状況に応じてPHPはもちろん、Node.jsやJava、Pythonによる開発も検討しています。

次に、開発に関するコミュニケーションの言語が英語になったことで、より多くのエンジニアが活躍できるようになりました。
メルカリではプロダクトだけでなく組織のグローバル化も強く推進しており、US版Mercariの開発に関わるサンフランシスコ・東京オフィスでも日本語を話さないエンジニアの数は日々増加しています。
2017年初頭より、ソースコード上のコメントやコードレビュー等は全て英語化していますが、monolith APIは2013年の日本でのメルカリのリリース当初から開発されており、日本語のコメント・ドキュメントの数も少なくありません。
今回開発・リリースしたAPI gatewayや各種microservicesはコメント・コードレビュー・Slack上でのやり取りなどを全て英語化しており、日本語がプロダクト開発での障壁とならないように配慮しています。
もちろんmonolith APIには未だ日本語の情報が存在し、そういった情報の英語化も進めています。しかし新アーキテクチャでは新機能の開発の大部分がAPI gatewayやmicroservices上で行われており、プロダクト開発における言語環境は大きく改善しました。

課題点

新アーキテクチャに移行して数ヶ月ほど開発・運用を行っていくのに伴い、新たな課題点も見えてきました。それらの課題点とこれまでに取り組んだ解決策、今後の展望をご紹介します。

microservices開発のオーバヘッド

microservicesで新機能を開発する場合、monolithicなアプリケーションに機能を追加する場合と比較して多少のオーバヘッドがあります。
しかし多くの場合、中長期的には組織や開発した機能・プロダクトの成長に伴いmicroservicesアーキテクチャの方が開発効率が高いことが予想されます。
一方でメルカリではプロダクトの性質上、1日でも早く新機能をリリースしお客さまへ新たな価値を届けたり、新機能の検証を行うことも重要です。
ここではmicroservicesアーキテクチャの利点と、新機能リリースまでの開発期間短縮を両立するための戦略をご紹介します。

雛形となるサービスの用意

新機能を実装する際に全くのゼロからコードを書こうとすると、リポジトリの命名規則やディレクトリ構成などから検討する必要があり、どうしても時間がかかります。
そこで、microserviceでAPIを実装する際の雛形となるリポジトリを1つ作成し、新たなmicroserviceを開発する際はその雛形リポジトリを元に開発に着手できるようにしました。
この雛形リポジトリは受けたリクエストをそのまま返すミニマムなAPIが実装されており、社内では”echo-service”と呼んでいます。

microserviceの数を増やす際に手間が掛かる点として、CIやCDの設定も挙げられます。こちら関してもecho-service上に基本的な設定が実現しており、echo-serviceを雛形としたmicroserviceではCI・CDの設定の手間が削減できます。

f:id:deme0607:20171207130026p:plain
echo-serviceのREADME(一部)

API gatewayの利用

echo-serviceの導入により新たなmicroserviceの開発効率は大きく向上しましたが、それでも新たなmicroserviceを開発する際のオーバヘッドはゼロではありません。
例えば何らかの仮説に基いて新機能を開発する場合、まずは最小限の要件を満たすMinimum Viable Product(以降、MVP)を素早くリリースし、その新機能が仮説通りの価値をお客さまに提供できているかを検証することが重要です。
そういった場合はMVP開発段階でのmicroservice新規開発オーバヘッドも無視できない場合があるため、まずは新規のmicroserviceとしてではなくAPI gatewayにMVPを実装することで、新機能リリースのスピードを犠牲にしないようにしています。

MVPのリリース後、当初の仮説が正しいことが確認できればそのタイミングでその機能をmicroservice化します。新機能が仮説通りの結果を出せなかった場合は、その機能を改善する、もしくはAPI gatewayから機能を取り去るといった選択肢も考えられます。

運用ポリシーの統一

各サービスを運用していく上で、考慮しなければならない項目は多数あります。以下にその代表的な項目を列挙します。

  • デプロイ自動化
  • CIパイプライン
  • サービスの死活・エラーログ・メトリクスの監視とそれに伴うアラート対応
  • Failoverの自動化
  • データのバックアップ

microservicesのサービス数が増加してくると、これらの項目をどの程度満たせているかがサービスによってまちまちになってしまいがちです。また、チーム間での人員の移動が活発になると、何か問題が起こった際に誰が対応できるのかがはっきりしない、最悪の場合は問題に対処できる人が誰もいないことに問題が発生してから気づく、といったこともあり得ます。

現在は上記のいくつかの項目は前述のecho-serviceやSpinnakerの利用、GKEを始めとするフルマネージドサービスの活用により、新たなmicroserviceを開発しても比較的簡単にクリアすることができます。
しかし今後microserviceの数、開発チームの人数共に増加していくと、現行の仕組みではカバーしきれないことが予想されます。そこでSREチームとも協力し、以下のような施策でサービス運用ポリシーの統一を計画しています。

  • オーナーシップの明文化。現時点でのオーナーシップが誰にあるかをサービスのリポジトリで管理
  • 各項目の設定内容の監視
  • 各項目の設定の自動化

まとめ

今回はUS版Mercariがリリースされて以来の大規模リニューアルと、それに伴って起こったこと、今後のチャレンジについてご紹介しました。
明日11日目の執筆担当は@sawa-zenです。引き続きお楽しみください。

メルカリでは技術とチームの力でグローバルマーケットに挑戦するエンジニアを募集中です。

採用情報 | 株式会社メルカリ

  • X
  • Facebook
  • linkedin
  • このエントリーをはてなブックマークに追加