Microservices Patterns を読んで(2)

Chris Richardson 氏の Microservices Patterns を読んだ。前回からの続きです。

Microservices Patterns: With examples in Java

Microservices Patterns: With examples in Java

前回はこちら。

kencharos.hatenablog.com

全13章の後半、8章からです。

8章 外部API

マイクロサービス群を外部公開する場合に、API Gateway を適用することが書かれている。 API Gateway に求めらる機能は、ルーティングのほか、認証、プロトコル変換(REST->gRCPとか)、前述の API Composition など。

認証の例として、外部からの Basic 認証に対して、API Gateway が内部の OAtuh サーバーと接続して認証を肩代わりしたり、 JWT トークンを Cookie に付け替えたり、リフレッシュトークンのリフレッシュなどを行い、外部クライアントの負荷を減らす案などが出ていた。

API Gateway を実現する手段として、 AWSAPI Gateway, Spring Cloud Gateway, Apollo GraphQL (node.js), NetFlix Falcor などがある。 API Gateway のカスタマイズ性を重視するかどうかでどれを選ぶかが変わってくる。 また、アクセスが集中する部品なので性能面での注意も必要となる。例えば Spring Cloud Gateway は、Reactor と WebFlux を使って大量アクセスが可能になっている。

API Gateway に各サービスを連携する際の主管をどうするかが興味深かった。 API Gateway を管理するチームと、各サービスを担当するチームが異なっていると調整コストが発生する。 Netflix では、API Gateway を階層化し、API Gateway チームが基本的な Gateway 機能を提供し、そこに各チームが作成した API Gateway をぶら下げる。 各チームが外部公開に特化した API を作るこのパターンは、 BFF (Backend For Frontend) と呼ばれる。

9章 マイクロサービスのテスト 1

テストに関する章その1 。

筆者の経験から、マイクロサービスの開発を滞りなく行うには 自動化されたテストとビルドパイプラインが必須。 一方で、今まで見てきたプロジェクトでは自動化テストをちゃんとやっている現場が割となかったため、 まずはテストの基本について書くことにしたとのこと。

以下に挙げるテストの4分類に関する記述が主な内容となる。

  • End-to-End テスト : システム全体の受入テスト。全ての要素を本番同様の構成にする。
  • Component テスト : サービス単体の受入テスト。テスト対象以外のサービスはスタブを用いる。
  • Integration テスト : API呼び出しやインフラに依存する部分のテスト。スタブ、CDC を用いる。
  • Unit テスト : クラス単体のテスト。依存性はモックを使う。

上記の4つはピラミッドの構成を取り、上に行くほどテストの構成が複雑で実行時間が長く、脆くなる。 各テストの性質に合わせて使用する技術やテストの量を調整して行く。

Unit テストでは、テストダブル (モックやフィクスチャなどで依存性を置き換えること)の手法が紹介されている。

Integration テストでは、コンシューマー駆動契約テスト, CDC (Consumer Driven Contract) が紹介されている。 CDCはサービスのAPIを呼ぶ側のコンシューマーと、APIを提供する側のプロデューサーとで、 両者のサービスを実際にデプロイしてテストを行う代わりに、 コンシューマーがAPIの入出力に対する期待値を定義し、その期待値を満たしているかをお互いにチェックする。

期待値を DSL として定義すると、コンシューマー側では DSL からモックサーバーを立てるのでモックサーバーに向けてテストを行う。 プロデューサー側では DSL からAPIにアクセスを行うテストを生成するので、プロデューサーが期待した通りの入出力を行うかテストする。 こうすると API 呼び出しのテストにおいて、他のサービスを実際にデプロイすることがないのでテストの効率化が期待できる。

また、CDC はビルドパイプラインに載せることが重要。他のサービスが CDC の DSL を更新したらそれを自動的にテストできるようにしておく。 そうするとことで、早期に API の整合性が合わなくなったことを検知できる。

CDC をサポートするライブラリは、Spring Cloud Contract や Pact がある。

10章 マイクロサービスのテスト 2

主に Integration テストの記述方法の紹介。

Spring Cloud Contract や、 Evantuate Tram のテストライブラリを使うと、 RESTやメッセージングなどの連携部分を CDC でテストできる。 DB の Integration テストについては、 JPA のプロパティの変更の検証で代替するか、 Docker を使うかといった感じ。

Component テストについては、サービスに依存する DB や kafka, 他のサービスについては、インメモリ型のライブラリや Docker, サービスに見立てた HTTPサーバを立てて、テストを行う。 Component テストの記述は受入テストであることを踏まえて、シナリオ形式で書くことが望ましく、 Cucumber や Gherkin といったライブラリが紹介されている。

End-to-End テストも同様に Cucumber を使う。

(ただし、個人的な意見でいえば、 BDD はどうなんだろうというスタンス)

11 章 本番稼働に向けた開発

マイクロサービスにおける非機能要件をまとめた章。

マイクロサービスを問題なく運用して行くためには、セキュリティ、設定、監視が重要なファクターと言える。

セキュリティについては、API Gateway で認証や認可を一元的を行う、監査ログの発行、TLSによる暗号化などが要素となる。

設定については設定ファイルなどをアプリケーションに埋め込むのではなく外部から設定可能なようにすることが重要となる。

環境変数などを使って外部から設定を上書きできるようにする push-based 型や、 コンフィグレーションサーバーを立てて各サービスの設定を一元管理し、各サービスが設定を起動時に取りに行く pull-base 型 などがある。 また、サービスを再起動せずとも設定のリロードが可能なような仕組みもあると望ましい。

監視については、ヘルスチェックAPIの提供、 kibanaなどを使ったログサーバーによるログの一元管理、分散トレーシング、 メトリクスの定期的な収集、監査ログなどがある。

メトリクスは CPU使用率などのマシン状態だけでなく、業務的なメトリクスの記録(一定以上の金額の取引があった回数とか)も行うと良い。 micromater はカスタムメトリクスを記録し、 Prometheus などのメトリクス収集ツールと連携する。

監査ログの収集は収集漏れを防ぐために、AOPドメインイベントと連携すると良い。

上記のように、マイクロサービスに求められる非機能要件は膨大であるため、こういった機能を基盤として提供できるようにしたライブラリを microservice chassis と呼ぶ。 Spring Boot, Spring Boot Actuator, Spring Cloud などが該当する。

ただし、 microservice chassis は言語やバージョンがある程度固定化されてしまう欠点がある。 そこで最近注目されているのが、マイクロサービスの実行基盤側で上記の非機能要件を提供する service mesh である。 service mesh は sidecar として自分たちの作ったサービスの横にプロキシとして存在し、サービスへの in/out 両方の通信を仲介することで、 上記の非機能要件を付与する。 また、 sidecar の一元管理を行うための control plane という機能も一緒に登場する。 ( sidecar は control plane の対比として、 data plane とも呼ばれる)

service mesh を提供するライブラリとして、 istio, envoy, linkerd, consul connect, NGINX Controller などがある。

12章 マイクロサービスのデプロイ

アプリケーションのデプロイに関する章。

アプリケーションのデプロイは、 TomcatWebLogic などアプリケーションサーバーに同居させる形式から、 クラウドの登場により マシンイメージの展開や SaaSなどが生まれ、 最近は Docker によるコンテナ仮想化、k8sによるコンテナクラスタAWS Lambda などのサーバーレスといった様々な変遷があった。

コンテナを使う方がより早く柔軟である。 Docker や k8s を使ったコンテナイメージの作成や、k8s での負荷分散、ローリングデプロイなどについて一通り触れられている。 ただし、これらの内容はそれだけで一冊の本になる内容なので、入門的な内容を軽く触れる感じとなる。

最後に少しだけ、サービスの一部を AWS Lambda に切り出してデプロイする例が載っている。

13章 マイクロサービスのリファクタリング

既存のモノリシックなシステムをどうやってマイクロサービスに変えて行くかという章。

Big Bang Rewrite (既存システムを無視して0から作り直すこと) を絶対に避ける。基本的にうまくいかないし、コストがかかりすぎる。 また、デプロイメントパイプラインも自動化テストも無いのに作り直すのは生存できないというような強い言葉も書かれている。

Strangler Application パターンという徐々にモノリスを分解してマイクロサービスに変えていき、最終的にモノリスを無くすような戦略の方がうまくいく。 初めは設定ファイルを外出しするような小さな修正だけでも十分。

クラウドベンダーや SaaS ベンダーがサービスをクラウドに載せ替えてコスト削減のようなことを言ってきても、検討しないで飛び乗るような真似をしてはいけない。徐々に移行しながら適切な移行先の基盤を検討する方が良い。

ではどうやってリファクタリングをしていくかだが、3つの方法がある。

  1. 新規機能をマイクロサービスにする
  2. フロントエンドとバックエンドの分離
  3. モノリスの一部をマイクロサービスとして切り出す

いずれの方法であってもまずは前段に API Gateway を導入し、モノリスと新規開発部分を振り分け可能にしておくことが必須となる。

場合によってはモノリスとマイクロサービス間でデータの取得や同期が必要となる。 その際にモノリス側の修正が最低限で済むような工夫が必要となる。

例えば、サービスを分割する際は分割後不要となる項目であっても読み取り専用で残し、 マイクロサービス側で反映を行なったりする。 その反映もイベント連携としたり、モノリスの修正が一切できないような状況なら、DBのトランザクションログからデータ反映するようなツールを作る。

認証についても、既存の認証機能を API Gateway で上手く吸収し、マイクロサービス側へ連携させるようにするなどと言った工夫がある。

また、マイクロサービス側がモノリス側のデータ構造に引っ張られすぎないように、 腐敗防止層(ACL: Anti corruption Layer) といった防御のためのサービスを作ることも検討する。

まとめ

9章のテストちゃんとやってる現場が少ないという発言は胸に来ますね。 13章にも名言がたくさんあり、著者の経験を物語っているなと感じました。

英語で400ページを超える分量の本ですが、 DDDやマイクロサービスについては以前から調べていたのでどうにか読むことができました。

マイクロサービスに関する知見を幅広く集めて整理した本書は、サーバーサイドに関わる人であれば読んでみて損はないかなと思います。 サンプルコードの多くは Java や Spring なので、その辺の知見がないと少々難しいかもしれないですが。

マイクロサービスという言葉が出てきたのは2014年頃だったと思います。 その頃はマイクロサービスは今後はやっていくのかは怪しいなという思いでした。

とはいえ、増大して行くモバイルデバイスとインターネットに対し、Web を戦場とするサービス提供者にとっては、 いかに早くサービスを改善・拡大できるかが重要だったと思います。 そこで先人が色々な試行錯誤を重ねた結果として、サービスを分散させても上手く行くアイデアがようやくまとまってきた段階に来たんだなと感じます。 また、それを支えるのはクラウドサービスの普及と発展なのは間違いのないことです。

今なら、マイクロサービスに挑戦しても無謀ではないと本書を読んで思いました。