自己紹介とアジェンダ

可児友裕氏:本日は「ZOZOTOWNの検索機能のマイクロサービス化への取り組み」を紹介します。はじめに自己紹介をします。株式会社ZOZOテクノロジーズEC基盤本部検索基盤部検索基盤チームの可児です。

2017年4月にスタートトゥデイ工務店に中途入社しZOZOTOWNのバックエンド開発を経て、リプレイスプロジェクトに参加していました。現在も検索機能のバックエンド開発に従事し、主に検索機能のマイクロサービス化を担当しています。

本日のアジェンダは、「はじめに」「なぜ検索機能をマイクロサービス化したか」「どのように構築したか」「得られた効果」です。

まず「はじめに」。本日話す内容はTech Blogにも上がっているので、よければそちらもご覧ください。

本日は、ZOZOTOWNで利用しているマイクロサービスから検索APIを切り出して、検索機能に特化したマイクロサービスを構築した話です。(スライドを指して)検索機能のページはこちらです。よろしければZOZOTOWNをご利用ください。

なぜ検索機能をマイクロサービス化したか

では、「なぜ検索機能をマイクロサービス化したか」に入ります。(スライドを指して)こちらはZOZOTOWNの、検索機能をマイクロサービス化する前のシステム概要図です。中央でIIS(Internet Information Services)がクライアントからリクエストを受けると、IISからAPIを叩き、その先でSQL ServerとElasticsearchがバックエンドとして検索しています。非常に簡単な図で恐縮ですが、今回はこちらを概要図として紹介します。

「検索機能をマイクロサービス化する前の課題」について。既存のマイクロサービスについての課題を、開発と保守・運用の2つに分けて書いています。

まずは、開発での課題です。1つのマイクロサービスに、非常に多くのAPIが存在していました。リプレイスを進めるにつれマイクロなサービスではなくなってしまい、検索機能もそこに含まれていました。

リプレイスプロジェクト自体は2017年から開始され、非常に長い歴史がありますが、(それに伴って)APIも多くなっているのかなと思いました。把握していないAPIや共通処理も多く、当然それに伴って開発コストがかさんでいました。さらに、SQL ServerとElasticsearchを参照していたことで、マイクロサービスとしての役割が多い印象を受けたので、課題として挙げています。

次に保守・運用です。(保守・運用は)検索基盤チームとは別のチームが主管していました。機能追加や改修の際、レビューやリリースを別のチームに依頼して作業を行ってもらっていたため、自チームのみで完結できる作業でも連携が必要でした。

また、改修のマージやリリースタイミングも自由にハンドリングできず、リリースのフローに則ってリリースしてもらうように運用が動いていました。最後の障害発生時は、原因の調査が広域に及ぶため切り分けが難しく、複数のチームが合同で調査することが多々ありました。

課題は以上です。これらの課題をどうにかしたくて、検索機能に特化したマイクロサービスを構築したいという思いが強くなりました。

検索機能に特化したマイクロサービスを構築する目的

「検索機能に特化したマイクロサービスを構築する目的」です。目的は大きく2つあります。1つ目は、開発速度を向上させる。こちらは、検索基盤チームが主管である検索APIのみに対するマイクロサービスです。

精通したAPIにのみ集中して開発できるので向上できるのではないか。また、チャレンジングな実装に関しても、スムーズな意思決定ができるようになることで、かなり速度が向上するのではないかと思っています。

続いて、検索機能に特化したマイクロサービスを開発する。検索機能のバックエンドは主にElasticsearchのみなので、思い切ってElasticsearchのみのマイクロサービスを構築して、役割を明確かつシンプルにしてしまおうという狙いです。

具体的な構築方法

「どのように構築したか」です。(スライドを指して)先ほども紹介した検索機能をマイクロサービス化する前の概要図です。そして、実際にマイクロサービス化した概要図がこちらです。

中央の青で塗りつぶした「検索API」と書いてある部分を、マイクロサービスとして構築しました。バックエンドはElasticsearchのみになっています。

次に、構築する方法として2つの案を検討しました。1つ目の案は、リポジトリを丸々コピーしてマイクロサービスを構築する方法です。メリットとデメリットはスライドに記載したとおりです。

簡単に紹介すると、メリットは、リポジトリをコピーするため少ない作業量で本番稼働が可能な点かと思います。デメリットは、不要なAPIやコードがまるっと残ってしまうことです。ほかに、SQL Serverの参照や古くなったライブラリもそのまま残ってしまうので、そのあたりのメンテナンスがかかるかもしれません。

2つ目の案は、検索APIのみを切り出してマイクロサービスを構築する方式です。こちらのメリットは、Elasticsearchを参照する検索APIのみが実装されたマイクロサービスが構築できることで、非常にシンプルなマイクロサービスが構築できるのではないか。

また、コードのリファクタリングやコードをコピーするタイミングでリファクタリングやバージョンアップができるので、それもメリットだと思っています。デメリットは、1つ目のパターンよりも作業量が多くなること。

結果として、検索APIのみを切り出してマイクロサービスを構築する方式をとりました。

「不要なAPIを取り除いたマイクロサービスを構築」です。技術的負債を抱えたくないという思いから、不要なAPIを取り除いて検索APIのみを実装することにしました。

検索機能のマイクロサービス構築時に行った5つのこと

「検索機能のマイクロサービス構築時にやったこと」です。こちらは、API実装、静的解析、ヘルスチェックの実装、OSSライセンスチェック、リリースの5つを簡単に紹介します。

API実装は、ユニットテストが書いてあると安心して移植できます。最初期にAPIを作ったのは4本ですが、コードの大半は移植して、必要に応じて実装していました。この時、ユニットテストが書いてあったおかげで安心して移植できました。そのため、ユニットテストがとても大事だということを再認識できました。

静的解析は、「SonarCloud」を利用して、コードの品質を保つためにコードの状態を可視化するようにしています。チームとしての取り組みとして、ユニットテストのガイドラインなどを作っています。カバレッジは現状86.1パーセントという数字を維持しています。

ヘルスチェックの実装。Elasticsearchは複数エンドポイントで運用しているので、それに合わせて独自のヘルスチェックを実装しました。

OSSライセンスチェックは、社内のOSSガイドラインに準拠するためにスライドのようなコマンドを使って、OSSを不正に利用していないかを一覧を作ってチェックしました。

リリースです。本番稼働しているAPIを切り替えるのはとても大変です。実際のリリース時はほかの案件も並行して動いていたので、その合間を見ての検索機能のマイクロサービスのリリースはとても大変でした。

リリース時の取り組みをスライドの下部に書いていますが、比較テストを実施しました。さらに、既存のAPIに対する開発案件の差分です。当然、差分が発生するので毎週差分取り込みをチェックしました。

続いて、カナリアリリースです。検索APIは非常に大量のリクエストを受け付けているので、一度に切り替えず段階的に行うことで、安定的なリリースを実施しました。これらの取り組みにより、ミスやプログラムのバグもなくAPIをリリースすることができました。

構築により得られた効果

「得られた効果」の1つ目は開発速度の向上、つまり「自チームでハンドリングできるマイクロサービスは意思決定が早く、開発速度も速い」です。定量的な測定はできていないため定性的な評価になってしまいますが、開発速度が向上した実感はあります。

2つ目は、検索機能に特化したマイクロサービスとして「自チームでAPI開発が完結でき、想定どおりのパフォーマンスで安定稼働」です。パフォーマンス、特にユニットテストについては安定した稼働につながったと思っています。

機能単位の特化は非常に重要

最後にまとめです。Simple is the best。機能単位で特化することは非常に重要です。多くの役割を担うことなく、シンプルに検索に特化することで得られた恩恵は非常に大きかった。また、システム改修に柔軟に対応するかたちに分離するということは今後につながり、さらなる検索速度や改善に取り組む環境が作れました。

繰り返しますが、機能単位で特化することは非常に重要だったと思います。みなさんもぜひ、マイクロサービス化にチャレンジしてください。

質問回答

それでは、「マイクロサービス化したことで感じる一番のメリットは何ですか?」という質問に回答します。メリットはたくさんありますが、一番は自チームがいろいろなことをハンドリングできるようになったことです。

開発の意思決定が早くなったことで開発スピードが向上したのが一番のポイントです。これによってだいぶ開発スピードが上がったので、いろいろな面で効率的になったと思います。

次に、「ユニットテストのカバレッジを維持するためのポイントは何ですか?」。僕が開発する際は必ずユニットテストを書いて、手を抜かないようにしています。ユニットテストが足りていない部分は、追加で書くようにしています。

さらに、コードのレビューの際に足りていないと思った部分は、書いてもらうよう依頼しています。さらに、週次でカバレッジの数値をチームで共有することで、「現状はここだ」と認識することで、維持できていると思います。

次に、「開発期間とメンバー構成はどのような感じですか?」。開発以外に検討・調査フェーズやリリースなども含めると、半年弱かかりました。実際のコーディングはそんなに長くありませんが、調査フェーズのほかリリース期間もあります。実運用、実際の開発がほかで走っている状況でどんどんリリースする上、カナリアリリースや負荷テストも行うので、リリース面ではわりと時間かかりました。

メンバー構成は、管理者が1名、開発者が4名です。ピーク時が4名で、常時開発しているのは1名か2名、スポットのピーク時に開発者が4名いました。そのほか、SREが1名付いていました。

次に、「マイクロサービス化によりElasticsearchへデータを連携する部分で難しさはありましたか?」。Elasticsearchへのデータ連携は、マイクロサービスとは別の部分でバッチが動いていたので、マイクロサービスによる難しさというのは特にありません。裏側のElasticsearchへのデータを連携する仕組みはすでに完成された状態で実施したので、特にありませんでした。

次に、「マイクロサービス化において助けになったユニットテストの範囲(コントローラレベルやサービスレベルからクラスレベルまで色々あると思うので)はどこからどこまでですか?」。僕はサービスレベルだと思います。

なぜかというと、ビジネスロジックのサービス層に集中しているので、ここをきちんとユニットテストしておくことによって、移植なりを正しく実装する点でとても助けになったからです。ユニットテストの範囲はサービスレベルで、ビジネスロジックの部分を書いておくととても安心感が出ると思います。

最後の質問です。「検索のビジネスロジック実装において他チームとのコミュニケーションはスムーズにいきましたか?」。僕が所属しているチームは検索基盤チームで、検索のロジックは基本的に検索基盤チームが管理していたので、他チームとのコミュニケーションはほとんど発生していません。実装自体は僕が所属している検索基盤チームだけでほぼ完結してしまったので、コミュニケーションは特にありませんでした。

以上です。ありがとうございました。