【Mercari Tech Conf 2018 レポート】メルカリの出品機能のモノリスからマイクロサービスへの移行についてのセッション『Listing Service: モノリスからマイクロサービスへ』#mtc18

2018/10/04 に行われた『Mercari Tech Conf 2018』で発表されたメルカリの出品機能のモノリスからマイクロサービスへの移行についてのセッション『Listing Service: モノリスからマイクロサービスへ』のレポートです。
2018.10.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Listing Service: モノリスからマイクロサービスへ

メルカリで現在進められているマイクロサービス化の1つとして出品機能のマイクロサービス化があります。PHP & モノリスのコードを可能な限り互換性を保ちながらGo & マイクロサービスに移植するにあたり、どのように開発を進めているのかを紹介します。

スライド

Listing Service

  • 出品機能を担当するマイクロサービス
  • モノリスからマイクロサービスに移行する最初のプロジェクト
  • 最初は1つの HTTP エンドポイントを対象として移行
  • サービス間通信は gPRC

戦略

  • API の互換性を保ちつつサービスを分割
    • API の互換性を保つことでカナリアリリースを可能にし、リスクを低下させる
    • 既存の API に対するアプリの自動テスト
    • QA チームによる手動確認
    • AQA チームによるインテグレーションテストツール(開発中)
    • 出品に関連があるロジックのみを移植
    • 一部 PHP に残したまま Webhook で連携
  • マイクロサービス化の目的はコードの分割ではなくスケール可能な組織にすること

課題

GCP(マイクロサービス) から Sakura(モノリス) の MySQL へ接続

課題

  • マイクロサービス化が完了するまではモノリスの PHP と DB を共有
  • セキュリティ的に MySQL のポートを外部公開したくない

対策

  • SQL on gPRC のサービスと Go のクライアントライブラリを開発
  • 基本的な CRUD のみ対応し Join やトランザクションは非対応

  • 普通の SQL を触るのに近い感覚で扱うことができる

トランザクションの分割

課題

  • 商品, 配送情報, 分析用データなどの更新が1つのトランザクションに含められていた

対策

  • 商品データは Item Service(Sakura) がトランザクション処理
  • お客様の目に触れるデータはできるだけリクエスト内で処理
  • 内部データは Cloud PubSub を使用し, 非同期の結果整合性にする

$db->begin();
$item->save();     // トランザクション: 商品の出品なので見えないと不自然
$photo->save();    // 同期: 商品の画像が見えないと不自然
$shipping->save(); // 非同期: 商品が売れるまで配送されない
$analysis->save(); // 非同期: 分析用データはお客様の目に触れない
$db->commit();

機能追加, 修正への追従

課題

  • 出品はメルカリのコア機能の一つなので PHP から Go への移行中にも機能追加や修正が行われる

対策

  • PHP のリポジトリに変更追跡用ブランチをつくり Go に移行したコードを削除
  • 変更が行われたら merge するときにコンフリクトする
  • コンフリクトしないケースでも, 変更追跡用ブランチに PHP のコードが残るので気づくことができる
  • 全てのコードが消えたら完了

  • 左が PHP の変更追跡用ブランチ, 右が Go のコードです

  • Go にコードを移植していきます

  • PHP のコードに変更があった場合, 変更追跡用ブランチに master を merge するタイミングでコンフリクトが発生します

  • コードの追加などでコンフリクトが発生しないケースでも変更追跡用ブランチに PHP のコードが残ったままの状態になるので, 移行が行われていないことに気づくことができます

PHP 例外の互換性

課題

  • API のレスポンスにエラーコードとして PHP の例外名が含まれている
    • クライアントは PHP の例外名を使用している
    • Go からも PHP の例外名を返す必要がある
  • 例外の種類毎にレスポンスの JSON が一部異なる
  • マイクロサービス化によって例外も各サービスに分散
    • 各サービスが PHP 例外との互換性を保つ必要がある

対策

  • PHP の例外との互換性を保つための Go の共有ライブラリを開発
  • エラーに対応する PHP の例外情報を gRPC の Status.datails に付与
  • PHP 互換の HTTP エンドポイントでは例外情報を取り出し, レスポンスの JSON を直接操作

まとめ

  • 長期的な課題を解決できるように, 負債を生まないように意識してマイクロサービス化を進めている
  • PHP のモノリスから Go のマイクロサービスに移行する最初のプロジェクトなので資産が整っていなくて大変な部分は多い
  • 正解がない中ひたすら正しさを求める楽しさがある
    • 俺が信じる設計を信じろ

おわりに

メルカリさんの PHP のモノリスから Go のマイクロサービスに移行を進めるお話でした。あれだけの巨大なサービスをマイクロサービスに移行する知見という大変ためになるお話を聞くことができました。モノリスからマイクロサービスに切り離す大変さや, 互換性やモノリスとの共存を図りながら Go への移植を行う際の戦略など様々な努力や苦労などが伝わってきて、とても勉強になりました。

その他の『Mercari Tech Conf 2018 レポート』記事

【Mercari Tech Conf 2018 レポート】メルカリのマイクロサービスプラットフォームチームの取り組みについてのセッション『Microservices Platform at Mercari』#mtc18

【Mercari Tech Conf 2018 レポート】メルペイの進める決済処理のマイクロサービス化についてのセッション『どうして僕らは決済処理をマイクロサービス化しようとしているのか』#mtc18

【Mercari Tech Conf 2018 レポート】メルカリ Web の新アーキテクチャについてのセッション『Web Application as a Microservice』#mtc18