マイクロサービス共通ライブラリ "servicex" の紹介
- 共通ライブラリを導入することで
- 「すべてのマイクロサービスが備えるべき機能を間違いなく提供できる」だけでなく
- 「アーキテクチャ全体を継続的に・小さく改善していく足がかり」にもなった
- 各言語に実装した共通ライブラリのコア機能は「すべてのサービスが」「すべての入出力にフックし」「入力のコンテキストを出力まで伝搬する」であった
Wantedly ではマイクロサービスアーキテクチャを採用しています。Wantedly における Go 導入にまつわる技術背景 などでも言及のあるとおり、Ruby・Go・Python・Node.js(・Rust)と複数の言語が採用されています。 そして、"servicex" という共通ライブラリがこれらの言語それぞれに用意されています。 servicex の各言語実装はすべて同じ目的を達成するためのもので、その Why / What は以下のように定義されています。
Why新しくサービスを作る際に考えることを減らし,本来実現したいドメインに集中できるようにしたい.
WhatWantedly のすべてのマイクロサービスが備えるべき機能を扱いやすい・統一的な形で提供する.
(wantedly/servicex の README より抜粋)
本章ではこのマイクロサービス共通ライブラリ "servicex" について、なぜ必要だったのか、そしてそれがあることでどんな良いことがあったかなどを紹介します。
機能ベースでは、大きく2種類に分けられます。
まず、ログやメトリクスなど Observability 関連。 マイクロサービスアーキテクチャを採用するとリクエストの流れも複雑になりますし、障害発生時の原因特定などの難易度も上昇します。 よって、それぞれのコンポーネントで Metrics, Logs, Traces などを収集しておくことは重要なのです。 Wantedly のマイクロサービスでは、開発者が意識せずとも servicex さえ導入しておけば、これらの機能が有効になります。
また、だいたいのマイクロサービスで必要になる便利機能群というのも実装されています。 いくつか挙げてみましょう。
- マイクロサービス間通信のデフォルトタイムアウト・認証などの設定
- マイクロサービス間通信で、環境(ローカル, 開発, QA, 本番, ..)を見て URL をいい感じにしてくれる
- BigQuery にイベントを記録できる機能
- RDB に依存しない A/B テスト機構
- ...
ほかにもいくつか機能がありますが、ここでは省略します。
「(プロダクト開発者としては)設定を忘れがちだが、忘れたらかなり困る」ものを忘れないようにするためです。特にログやメトリクスはわかりやすいでしょう。 普段からメトリクスが取れてないと、問題発生時に気づくのが遅れるかもしれません。 また、アクセスログなどは問題に調査・対応するときの最後の砦とな ることもあります。 こういうものはだいたい問題が起きたタイミングで初めて需要が生まれます。しかし、その時点でログが取れてないことに気づいても後の祭りです。
それに、ログやメトリクスの送り先にもちゃんとルールが必要です。 マイクロサービスによってログテーブルの命名規則やデータフォーマットが違っていたらちょっとしんどいですよね。
また、一般的なソフトウェア実装プラクティスの話でもあります。同じようなコードを複数のリポジトリで何度も書くのは不毛ですし、間違いも起きるかもしれません。 「読み出す環境変数の名前を間違えて正しくメトリクスが取れなかった」みたいなことが起きる可能性もあります。自分は3回くらいやりました。
...と、このように、プロダクト自体の開発以外にも「忘れたら困る」「何回も書きたくない」「間違えたら困る」みたいなコードは意外とたくさん存在します。 このような**「新しくマイクロサービスを作る際に考えないといけないこと」を減らし、本来実現したいドメインに集中できるようにする**ために、 Wantedly のすべてのマイクロサービスが備えるべき機能を扱いやすい・統一的な形で提供するのが "servicex" という共通ライブラリの責務です。
この Why と What を踏まえて、servicex リポジトリの description には "Microservices governance through code." と書いてあります。 この文言は書籍 Building Microservices(日本語版: マイクロサービスアーキテクチャ)の2.6章のタイトル "Governance Through Code"(コードを介したガバナンス)から取られています。
ここまでで「マイクロサービス共通ライブラリ "servicex" とは何のために・何を提供するのか」を紹介してきました。 このライブラリを実際に2018年ごろに作り始めてからいままでのあいだに、起きたことを踏まえて、「共通ライブラリがあることで何が起きるか」というのを掘り下げていきます。
さて、前の節で servicex が提供するものに "Observability 関連" を挙げました。 具体的には「Metrics, Logs, Traces を収集するための機能」です。これを達成するためには何が必要になるでしょうか。自分は以下の3項目が重要なのではないかと考えています。
- Observability を網羅的に高めるためには、すべての入出力にフックできる必要がある
- 入力と出力を正しく紐付けるためには、入力のコンテキストを出力部まで伝搬している必要がある
- マイクロサービスをまたいでログ・メトリクス・トレーシングを関連付けるには、すべてのマイクロサービスが上記の性質を満たしている必要がある
入出力 という表現をしましたが、これはマイクロサービスが「どんなリクエストを受けて」「どうやってリクエストを飛ばすか」ということです。 Wantedly ではマイクロサービス間通信に JSON over HTTP だけでなく gRPC を推進しているほか、非同期なやりとりには Cloud Pub/Sub を利用しています。 また、当然 RDB などのデータストアを持つマイクロサービスも多数存在します。ほかにも Redis や Elasticsearch など、いくつかのミドルウェアを利用しています。 これらのマイクロサービス間通信の入出力やミドル ウェアへの出力にフックし、観察することでリクエスト全体が追跡可能になったり、障害発生時にどこで異常が起きているのかを発見しやすくなるのです。

hexagonal-architecture-like-input-and-output
この**「すべてのマイクロサービスが」「すべての入出力にフックし」「入力のコンテキストを出力まで伝搬できている」**という特性があることで、 Wantedly ではシステムの Observability の向上以外にもいくつかの恩恵を受けることができました。
- 事例: Network Observability 向上のための PoC