体はドクペで出来ている

インフラ、Goの割合が多い技術ブログ

書評 O'Reilly マイクロサービスアーキテクチャ

はじめに

この本の原書は2015年に発売された Building Microservices で、その翌年に 日本語版 が発売されました。原著者であるSam Newman氏は米ThoughtWorks社に務めるエンジニア *1 で、同社に在籍するMartin Fowler、Jim Highsmith両氏 *2 が提唱したアーキテクチャーをまとめあげたものです。

内容の目玉となるのはやはりタイトルの通り「マイクロサービスはどのようなものであるか」「どうモデル化するか」「各サービスをどう統合し一つのアプリケーションとして動作させるか」等というところでしょう。しかしそれだけに留まらず、分散システム一般の課題や、テスト・運用をどう行うべきかという範囲にも言及されています。大きくわけて前半が「マイクロサービスとは何か、どう考えて設計するか」が書かれ後半で「開発、運用に関する知見」が書かれているという構成になっています。

以下私が参考になった点を箇条書きで書き出して行き最後に感想を書きますので、興味のあるワードがあれば実際の書籍を手にとって見るとよいでしょう。

読書メモ

1章 マイクロサービス

  • ソフトウェアの凝集性と同様にマイクロサービスも「変更する理由が同じものを集め、異なるものは分ける」
    • 単一責任原則
  • サービスの境界をビジネスの境界に合わせる
  • 1つのマイクロサービスの規模感は2週間で書き直せるくらい
    • 十分に小さく丁度良い大きさ
  • サービスが小さいほどマイクロサービスアーキテクチャーの利点と欠点が大きくなる
  • 他に何も変更せず単独でサービスの変更・デプロイが行えるか?
    • 行えない場合マイクロサービスの利点が享受できない
  • 共有ライブラリーやモジュール化とは役割が異なる
  • 銀の弾丸ではない
    • 分散システムに由来する複雑さがあり適切に管理することは難題

2章 進化的アーキテクト

  • マイクロサービス特有の意思決定機会がある
    • 技術スタックを統一するか、複数採用するか
    • サービスを分割or統合すべきか
  • アーキテクトも開発に参加し開発者がどのように感じているか知り判断材料とするべき
  • アーキテクトは建築士ではなく都市計画家であるべき
    • 「家をどこに建てるか」「どのような家を建てるか」ではなく区画を整理し全住民が将来に渡って住みやすい環境を整備する
  • 標準として定めておくべき事項
    • 各サービスだけでなくシステム全体の状態を俯瞰して把握できる監視手法・運用ルール
    • サービスがAPIを公開するためのインターフェース技術
    • 単一のトラブルが全体に波及しないための仕組み
  • 標準から逸脱しないためにサービスを立ち上げるためのテンプレートを作る
    • 但しDRYに固執した結果密結合を生まないように注意

3章 サービスのモデル化方法

  • 疎結合と高凝集を維持する
    • 他のサービスに影響せずデプロイできるようにする
    • 境界付けられたコンテキストにより関連する振る舞いをまとめる
  • ドメイン知識が薄いとコンテキストの境界を見極めるのは困難
    • まずはモノリシックで構築し、ドメイン知識が増えたところで分割していく方が無難
  • ビジネスの観点でサービス間通信を検討する
    • ドメイン言語をインターフェースに反映するべき
    • 全社で書類のフォーマットを統一することと似ている

4章 統合

  • 破壊的変更は可能な限り回避する
    • やむを得ない場合は並行期間を設け呼び出し側が移行できるようにする
  • 特定の技術に依存したAPIは避ける
    • 呼び出し元の技術選定の自由度を確保する
    • 将来に渡って新しい技術を導入しやすくする
  • データベース共有は疎結合と高凝集が失われるので避ける
  • サービス間の通信方法はいくつか選択肢があるので要件に応じて適切に選択する
    • リクエスト・レスポンス
      • (同期)サービスを呼びその結果が応答されるまで待つ
      • (非同期)サービスを呼びコールバックを登録、結果は処理の終了時に別途通知する
      • HTTPS, REST, JSONの組み合わせが一般的
    • イベントベース
      • ある事象が起きたことを周囲に通知し、それを他サービスが受信することで動作が開始される
        • 通知する側は誰にどう伝わったかは関知しない
  • イベント駆動・非同期処理は本質的に複雑
    • ユニークなIDを発行し処理の中で伝播させることで連動する振る舞いを紐付け監視できるようにする
  • 標準として定められた監視やインターフェースに関すること以外のコードはサービスをまたいで共有しない
  • UIとの統合にはいくつかのパターンがある
    • API合成
      • フロントエンドが各種APIを叩き必要な情報を揃え画面を構成する
    • UI部品合成
      • 各サービスがUIに必要な部品を直接返却しフロントエンドがそれを組み立てる
    • Backend For Frontend (BFF)
      • フロントエンド向けに複数サービスを呼び出し結果をまとめて返却するサービスを用意する
    • UIも一つの合成レイヤーと見做せる

5章 モノリス(一枚岩)の分割

  • 分離して扱え他のコードに影響を与えず変更できる「接合部」を探す
  • 境界付けられたコンテキストを接合部とする
  • プログラミング言語名前空間を使って整理する
  • 分割はミスの影響を軽減するため少しずつ進める方がいい
  • コードを分離したら次はデータベースのスキーマを分離する
    • 同時に進めるのではなく段階的に
    • この段階ではまだ分離が適切か判断できないのでサービスは分割しない
    • 分割が理に適っていると判断できたらサービス分割に進む
  • データベースを分割するとトランザクションによる厳格な整合性は担保できなくなる
    • 結果整合性
    • 一時的な不整合を前提として処理中の表現や不整合を補正する仕組みが必要
  • レポート系の機能がある場合、分割された複数のサービスからデータを集約して作成しなければならない
    • イベント駆動でデータを集約できるレポート用のデータベースを用意する
    • 小さい規模であればレポート集約用のAPI
  • 分割したくなるまでサービスが肥大化すること自体は問題無い
    • 分割コストが高くなりすぎる前に分割を検討できるようにする

6章 デプロイ

  • サービスごとに独立したCI/CDパイプラインを持つべき
  • 同様にコードを置くリポジトリーも独立させる
  • 環境に依存する設定は環境変数として切り出し同じコードでも異なる環境で動作させられるようにする
  • 1つの環境に複数サービスを同居させるべきではない
    • デプロイや監視に支障をきたす
    • 仮想化・コンテナで隔離環境を作りサービス同士が影響を及ぼさないようにする
      • リソースオーバーヘッドの観点からコンテナが望ましい

7章 テスト

  • 自動テストに複数のスコープを設けフィードバックを高速に得られるようにする
    • 単体テスト
      • 外部ファイル、ネットワーク越しの連携は行わない小さいスコープのテスト
      • ビジネス要件ではなく技術的側面を担保する
    • さービステスト
      • 一つのサービスを対象としたテスト
      • ネットワーク越しの外部連携はスタブ・モック化する
    • E2Eテスト
      • UIやネットワーク越しの連携も含めたシステム全体としてのテスト
    • コンシューマー駆動テスト
      • ユーザーからみた振る舞いを観点としたテスト
  • テストコードはコードオーナーが書くべき
  • デプロイとリリースを分離し間で本番環境でテストできるようにする
    • スモークテスト
    • Blue/Greenデプロイメント
    • カナリアリリース
  • 非機能要件もテストする
    • 性能テスト
    • 実行するための準備負荷が高く頻繁にはできないので部分的に日時、全体を週次等で行うのが一般的

8章 監視

  • 複数のサービスに分割されると監視項目が比例して増えるので人手を煩わせない監視の仕組みにする
    • データの自動収集
    • 全貌
  • 自動スケールに追随できる必要がある
  • サービス単体だけでなくシステム全体としての状態も監視する必要がある
    • 合成監視
    • セマンティック監視
  • サービス間の連携を追跡できるよう「相関ID」を発行してサービス間で伝播させながらログを出力する
    • 分散トレーシング
  • 監視においては各サービスが追随する標準化が非常に重要
  • 連鎖的トラブルを検知・防止できるようにする

9章 セキュリティ

  • 複数のサービスで認証・認可するためSSOの仕組みが必要
  • 個々のサービスではなく認証・認可のためのゲートウェイを作る
  • 多層防御
    • 通信内容の暗号化(主にHTTPS
    • クライアント証明
    • ファイアーウォール
    • ロギング
    • IDS/IPS
    • ネットワーク分割
    • OS
  • セキュリティの実装は可能な限り既存の実装を流用する
  • システムを細かい粒度でサービスに分割すると選択肢が増える
    • サービスの責務に応じてセキュリティレベルを変えられる

10章 コンウェイの法則とシステム設計

  • コンウェイの法則: システムを設計するあらゆる組織は必ずその組織のコミュニケーション構造に倣った構造を持つ設計を生み出す
  • 組織構造はシステムの性質・品質に強く影響する
    • 組織が疎結合(リモートワークが前提のチーム等)の場合、システムのモジュール性が高まる傾向がある
      • 逆もしかり
    • 組織の結合度とシステムの結合度に乖離があると変更に困難が生じる
    • 組織の分割単位と境界付けられたコンテキストを一致させる
  • サービスは単一のチームが所有権を持ち全ての責務を持つ(設計、実装、テスト等)
    • 当事者意識が強くなり自律性が増すことでデリバリーが早くなる
  • マイクロサービスではコードの記述だけでなくより広範なスキルが必要
    • 他システムとの連携・配慮
    • ネットワーク越しの呼び出し特有の問題に対する配慮

11章 大規模なマイクロサービス

  • 障害は必ず起きるので防止よりも回復を重視した対策を検討する
  • 達成すべき非機能要件定め監視に組み込む
  • システムのいつどこで障害が起こるかわからないので安全にデグレードさせる仕組みが必要
    • あるサービスが停止した時どのようにデグレードさせるかはビジネス面で判断
  • アンチフラジャイルな組織
    • 実際の障害をわざと発生させることで障害対策の充分さをチェックする
    • NetflixのChaos Monkeyが有名
  • 障害が起きた際になるべく他サービスへ波及させないための仕組みはいくつかパターンがある
    • アーキテクチャー上の安全対策
    • タイムアウト
      • 呼び出し先の遅延の影響を受けにくくするためのタイムアウト設定
    • 隔壁
      • 呼び出し先が複数ある場合、一つの呼び出し先の障害に影響されないようコネクションプールを分離しておく
      • 船舶の防水隔壁になぞらえている
    • サーキットブレーカー
      • 呼び出しに一定数失敗したら復旧するまで呼び出しを停止することで無用なリトライを減らし素早く失敗を応答する
      • 復旧するまではデグレード状態になる
  • APIは可能な限り冪等に作る
    • 障害発生時に状態を考慮する必要がなくなり単にリトライするだけで良くなる
  • 可用性と性能を担保するため水平スケールを前提とする
    • 垂直スケールでは可用性の担保にならず、性能の限界が比較的早く訪れる
  • 複数のサービスでサーバーの相乗りをすることは避ける
  • 最初から大規模向けに設計するのではなく、ビジネスの成長に沿って随時アーキテクチャーを変更していく
    • 必要になるかどうかわからない負荷対策を考えるよりビジネスの成長を考えるべき
  • データベースインスタンスの共有は障害時の影響が大きくなるので避ける
  • サービスの性質によってCAP定理の何を優先するか決める
    • 大抵はAP(結果整合)になるはず
  • サービスが提供するAPIの仕様をドキュメント化する

12章 まとめ

(まとめをまとめたらほぼ書籍の内容のままになってしまったので割愛)

感想

実際に読んでみた感想としては必要な前提知識が比較的多いことと、実装事例ではなく考え方の紹介が中心で抽象的な記述が多いので理解に時間のかかる一冊であるということが先立ちました。特に「サービスをどのように分割するか」ということがメインテーマとなる3章はドメイン駆動設計の知識が必要で、Googleで知識補完しながらという読み方ではピンと来ないのではないかと思います。オライリーらしく中々に高密度な書籍なのでじっくりと思考しつつ書籍外からの学習も加えながら読んでいくのがよいでしょう。

言及範囲がシステム全体と網羅的であるためマイクロサービス以外の面でも勉強になるでしょう。例えば「分散システムの生まれた背景、一般的な課題」や「テストとはどうあるべきか」等といったところの知識の習得としても役立ちます。また「銀の弾丸は無い」「マイクロサービスアーキテクチャーにしない方が良い場合」にも言及されているところは技術書として好印象でした。

また自分が所属している会社はマイクロサービスアーキテクチャーを採用しているのですが、本書の知見が存分に活かされていると感じました。進●ゼミではありませんし、そもそも順番が逆ですが「あ!ここ弊社のあの仕組みだ!」というところが散見され、書籍の内容が現実で上手く機能していることが裏付けられました。

アーキテクトやリード的な面での業務経験(特にモノリスで変更の副作用でハマったり、パッケージの責務がメチャクチャになってしまったり等の苦労)があると尚本書の内容は染み入ると思います。私個人としては小規模ではありますがそういった経験がいくつかあったので、あの時どうすべきだったかを考察しながら読み進められ良い思考材料を得られる良書でした。

*1:ここでいう「エンジニア」は日本的な意味合いの表現であり、同社における肩書ではありません

*2:アジャイルソフトウェア宣言 の著者17名の内の2人