マイクロサービスアーキテクチャ 第2版 を読んだメモ
第1版は数年前に読んだ。第2版は結構中身が変わってそう。
全体を通してのまとめと感想
変化する状況へ素早く適応をしたい、という欲求がある。
そのためには、小さなチームがが独立・自立してプロダクトを運営できるようにするのが良い。
そこで、プロダクトを大きな一枚岩のソフトウェアで構成するのではなく、チームで管理できる単位にマイクロサービスとして切り出すことで、チームが独立してプロダクト(の一部)を開発運用できるようになり、チームの独立性や自律性を高めることができる。
本書では、マイクロサービスを開発するためのベストプラクティスや、効率よく運用するためのノウハウが詰まっている。
ただし忘れてはいけないのは、マイクロサービスは手段であるということだ。目的を忘れないようにしたい。
1章 マイクロサービスとは
モノリス悪くない
近年のマイクロサービスの広がりを受をうけて、無条件でマイクロサービスを採用する風潮へ注意喚起
モノリスは悪ではないし、デフォルトの選択肢の一つ
モノリスでは解決できない課題があれば、解決の選択肢としてマイクロサービスを考えるべき
独立デプロイ可能性
マクロサービスの特徴を一つ上げるとしたら「“独立デプロイ可能性”」
他のサービスを変更せずに、デプロイできるようにするべき
そのために疎結合でなければならい
ビジネスドメインに基づくモデル化
マイクロサービスは、技術的機能の高凝集度よりもビジネス機能の高凝集度を上げたもの、とも言える
独立したチームが、自分の責任範囲のマイクロサービスを管理できる
→ これは、チームのエンパワーメントにも通じるものがありそう
コンテナと Kubernetes
Kubenetes やコンテナの採用を急ぐ必要はない。
必要になったら使おう
できるだけマネージドサービスを使って運用を避けよう
感想
どれもよく分かるし、今苦労しているところ。
目的が手段にならないように気をつけたいんだが、どうしても作ることに集中してしまって、振り返りができてないことが多い。
2章 マイクロサービスのモデル化
結合の種類
4つ紹介されている。下に行くほど密結合。
ドメイン結合 あるマイクロサービスが他のドメインが提供する機能を利用する
パススルー結合 あるマイクロサービスが別のマイクロサービスのさらに下流のマイクロサービスにデータを(明示的に)渡す状況
共通結合 2つ以上のマイクロサービスが共通データを扱うために、同じDBテーブルを更新している、などの状況
内容 上流サービスが下流サービスの内部に到達し、その内部状態を変更する状態(下流サービスのDBテーブルを直接変更するなど)
過不足のないドメイン駆動設計
ドメイン駆動設計が強力である主な要因は、境界づけられたコンテキストが、明示的に情報隠蔽に関するものであること
ドメイン駆動設計が唯一の手段ではない
感想
ドメイン駆動設計によって高凝集で疎結合な境界を引ける、という話だと受け取った。
同じ目的で変更される箇所を一つにまとめることで、サービス開発の独立性や速度を上げることができそう。
必要な情報だけ公開する(情報隠蔽する)ことで、サービス内部の変更が用意になるので、これもサービス開発の独立性や速度を上げることにつながりそう。
ドメイン駆動設計の良い振り返りになった。
3章 モノリスの分割
ほとんどの場合、モノリスは敵ではない
改めて強調
ドメインに関する知識が曖昧なまま、時期尚早なマイクロサービス化は危険
マイクロサービスによって達成したいことを明確してから、すすめるべき
レポートデータベース
内部データストレージへのアクセスも隠蔽すると、複数のマイクロサービスから、データにアクセスする正当なユースケースがある場合、問題になる。
外部アクセス専用にレポートデータベースを作る方法がある。
レポートデータベースを他のマイクロサービスエンドポイントと同様に扱うべき。
つまりレポートデータベースの作成や互換性の維持はマイクロサービスの責務となる。
感想
レポートデータベースをエンドポイント(インターフェース) として捉えるのはいい考えな気がする。
ただレポートデータベースを作り維持するコストを考えて、スキップしちゃうケースが多そう。
4章 マイクロサービスの通信パターン
パターン
同期ブロッキング
- リクエスト/レスポンス → REST orver HTTP など
非同期非ブロッキング
リクエスト/レスポンス → キューベースのブローカーを介してリクエストレスポンスをやり取りするなど
イベント駆動
共有データ → ファイルを通した連携など
イベント駆動
この方式の場合、マイクロサービスはイベントを発行するまでが責務
そのイベントががどう使われるかは関知しない
何をすべきかの判断は受信者に委ねる
リクエスト/レスポンスでは受信者がやるべきことを知っていないといけないので、結合が強まる
イベント発行者は下流につて知らなくていいので、結合が大幅に弱まる
ただし複雑さが問題になることもあるので、導入は慎重に検討するべき
感想
ファイル連携もそいうえば通信だよなってのは気づきだった。
イベント駆動の章は良い振り返りになった。
5章 マイクロサービスの通信の実装
スキーマ
契約の破壊は「構造的」破壊、「意味的」破壊の2種類ある。
構造的破壊は、エンドポイントの構造が変わるもの(プロパティがなくなるとか)
意味的破壊は、エンドポイントの構造は変わらないが、エンドポイントの振る舞いが変わるもの
変更の管理
ロックステップデプロイ マイクロサービスとコンシューマが同時に変更する チーム内だったらいいじゃないか
互換性のないマイクロサービスバージョンの共存 長期間併存すると苦しい
旧インターフェースのエミュレーション これが一番管理しやすい
感想
マイクロサービスが公開するインターフェースをスキーマによって明示することで、以下のような利点がある
構造的破壊に気づく仕組みを作りやすくなる
コンシューマ(クライアント)はマイクロサービスに何を期待できるかはっきりする
6章 ワークフロー
分散トランザクション
分散トランザクションの実装の概要
投票フェーズ 対象の操作が可能かどうか投票する
コミットフェーズ 対象の操作を実行する
投票フェーズとコミットフェーズの間がどのくらい開くか想定できない。
投票でOKを出したが、コミットするまでに状態が変わってしまうと対象の操作ができないので、 投票フェーズからコミットフェーズまで長期間ロックしておかないといけない。
サーガ
長時間リソースをロックする必要がないように設計されたアルゴリズム
長期トランザクション =(LLT:Long Lived Transaction)
LLT を一連のトランザクションに分解
障害時の復旧方法
後方復旧 障害時にロールバックするために補正(補償)アクションを実行する
前方復旧 障害時にリトライして処理をすすめる
サーガの実装には2つある
- オーケストレーションベースのサーガ
- コレオグラフィベースのサーガ
2つの組み合わせもあり得る
感想
オーケストレーションベースのサーガは、複数のマイクロサービスにまたがる処理をうまくモデル化してあるし、ロールバック方法も「補正アクション」としてモデル化されていて扱いやすそうだと感じた。
サーガは試しに実装してみてコツを掴んでおきたい。
7章 ビルド
本当にCIを行っているか?
CIツールを使っていてもCIをしているとは言えない。
CIを本当に理解しているかを確認する質問
- 1日1回メインブランチにマージしているか?
- 変更を検証するためのテストスイートがあるか?
- ビルドが壊れたら、その修正が最優先事項か?
ソースコードとビルドのマイクロサービスへのマッピング
- マルチリポ 1マイクロサービス1リポジトリ
- モノリポ 複数のマイクロサービスを1つのリポジトリで管理 複数のチームで同じリポジトリを管理することになる
本来のモノリポとは違うが、チームごとに1つのモノリポをつかう折衷案もある
Google や Microsoft がモノリポだからといって、あなたもモノリポにする必要があるかはわからない
感想
リポジトリが多すぎると切り替えや管理が煩雑になるので、モノリポにするものいい選択肢だと感じた。
8章 デプロイ
GitOps Weaveworks が先駆者
Wasm 様々なプログラミング言語で書かれたプログラムをクライアントブラウザで実行する技術 WASI を使えば、Wasm をブラウザ以外で実行できるようになる
Kubernetes を使わないことを恥ずかしいと思わなくてOK もっと簡単な方法がある FaaS PaaS という選択肢もある 最初の目的を忘れないように
感想
マイクロサービスのデプロイに関してのプラクティがまとまっており、振り返りになった。
デプロイとリリースの分離については曖昧な理解だったのではっきりしてよかった。
9章 テスト
手動の探索的テスト
テスト自動化をしてできたテスター時間を、手動の探索的テストの時間に当てよう
この本では手動の探索的テストについては深堀りしないが、決して意味がないと言っているのではない
エンドツーエンドを避けるべきか
エンドツーエンドテストは必要だが信頼性が低くなる運命なので、できるだけ数を減らしたい
マイクロサービスのインターフェースの構造的破壊や意味的破壊を検知できるようにしておけば、複雑なエンドツーエンドの必要性が低くなる
スキーマを定義しておけば構造的破壊の検知が可能になる
契約テストとコンシューマ駆動契約によって意味的破壊の検知が可能になる
契約テストとコンシューマ駆動契約(CDC)
契約テストは、コンシューマ(上流)マイクロサービスが、プロデューサ(下流)マイクロサービスにどのような振る舞いを期待するかを、明示的にプログラムで表現したもの。
CDCでは、コンシューマチームは、プロデューサチームと契約テストを共有するようにする
プロデューサチームは、ビルドのたびに、各コンシューマの契約テストを実行する
これによって、自分のマイクロサービスが、その期待をを満たせているか確認できる。
感想
契約テストとCDCはかなり役に立ちそう
本番でテストすること、パフォーマンステストを自動化することも良いプラクティスだと思う。
10章 監視から可観測性へ
- 監視 - 我々が行う活動
- 可観測性 - システムの特性
最初からログ集約を行うべき。またログに相関IDをつけておくべき。
ログの形式は共通化しておこう
多すぎるアラートはアラート疲れを引き起こすし、緊急時の対応の邪魔になる。
セマンティック監視 「システムは期待度どおり動作しているか?」を監視する
本番環境でのテスト 偽のユーザ動作を入れて、それを監視する。
感想
アラート疲れでかなり苦労したことがあるので、どれも納得感のある内容だった。
アラートを改善することでアラート疲れはかなり改善できると思う。
まだまだセマンティック監視(SLOの監視)はできていないので、やることは多い。
11章 セキュリティ
基本原則
最小権限の原則 必要最低限の権限を、必要な期間だけ与える。 -> 期間の概念もあるのに気づけた
多層防御 保護メカニズムが一つだけだと脆弱
セキュリティコントロールの種類
予防的 攻撃の発生を阻止
検知的 攻撃を検知して警告する
対応的 攻撃中や攻撃後の対応を支援 再構築の自動化メカニズム。バックアップ。コミュニケーション計画など。
資格情報 = クレデンシャル
暗黙の信頼とゼロトラスト
暗黙の信頼 自分のネットワーク境界内からのリクエストを信頼する
ゼロトラスト すでに侵略された環境で運用していると仮定する ゼロトラストは考え方(製品やツールではなく、ゼロトラストかどうかのゼロイチではない)
感想
JWT によるリクエストの検証は、侵入してきた攻撃者への防御としてなかなか有効なきがする。
ただし侵入してきた攻撃者が、DBを直接閲覧できたりしたら、マイクロサービスへの呼び出しを保護してもしょうがない。
包括的に考えて、一番弱いところから改善していく必要がある。
12章 レジリエンス (回復性)
レジリエンスを構成する4つの概念
- 堅牢性 (ロバストネス, rbustness)
- 回復姓 (rebound)
- グレースフルな拡張性 (graceful extensibility)
- 持続的な適応性 (sustained adaptability)
実際に起きた障害に対する対応
- タイムアウトの設定
- リトライ
- バルクヘッド(船の隔壁)
- サーキットブレーカーの追加
感想
バルクヘッドと同じプラクティスを実行していたが、バルクヘッドと呼ぶのは知らなかった。
システムに自動回復姓をもたせて、夜はゆっくり寝たいし、休日は家族と過ごしたい。
13章 スケーリング
スケーリングの4つの軸
- 垂直スケーリング CPUのコア数を増やすとか、メモリを増やすとか
- 水平スケーリング インタンスを増やすとか、リードレプリカを増やすとか
- データのパーティション分割 顧客A-M はインスタンス1、顧客N-Z はインスタンス2 二分割するとか
- 機能分割 モノリシックアプリケーションの注文機能を注文マイクロサービスに切り出すとか
機能分割は複雑さを持ち込むことになるので、他のスケーリングを試してから取り掛かるべき。
キャッシュ
目的
- パフォーマンスのためのキャッシュ
- スケールのためのキャッシュ 競合を避けるためのキャッシュ。リードレプリカなど。
- 堅牢性のためのキャッシュ キャッシュすることでオリジンが落ちていても稼働できるようになる
どこでキャッシュするか
- クライアント側 キャッシュ対象の情報を利用する側
- サーバ側 キャッシュ対象の情報を生成する側
- リクエストをキャッシュ (クライアント側の一種) キャッシュ対象を含む情報へのリクエスト自体をキャッシュする
無効化
- TTL
- 条件付きGET ETag を使うと、リソースが変更されていなければ 304 Not Modified が返却される
- 通知ベース
通知ベースが最も洗練されたやり方だが複雑。
感想
スケーリングの目的はパフォーマンスの改善だと考えがちだったが、スケールのためや堅牢性のためのキャッシュもあることに気づけた。実際は堅牢性のために使っている部分もあったが、スケーリングの目的を言語化して分類してあることで納得できた。
14章 UI
ストリームアラインドチーム
エンドツーエンド機能の完全な所有権を持つチームのほうが、より迅速に動くことができる。
完全な所有権があるとエンドユーザと直接会話できる。
バックエンドチームはエンドユーザが誰か見失いがち。
enabling team: 専門家のチームで、他のチームが特定の分野で自立できるように支援するチーム
ソフトウェアはチームの境界に沿っているときに上手く機能することが多い (コンウェイの法則)
様々な分解パターン
- モノリシックフロントエンド SPAを構成するときに人気
- マイクロフロントエンド ウィジットベースの分解とページベースの分解がある
- ページベースの分解 UIを複数のWebページ分解
- ウィジットベースの分解
- 中央集約ゲートウェイ 所有権が問題になりやすい
- BFF(Frontend For Backend) BFFをいくつにするか?という選択肢がある
感想
アジャイル開発やチームエンパワーメントのために、ストリームアラインドチーム + イネーブリングチーム の構成への動きが今後も続きそう。
現場としては、覚えることが多くなるけど、こっちのほうが楽しいじゃないかな。
15章 組織構造
- 疎結合の組織 ストリームアラインドチーム
- コンウェイの法則 システムを設計する組織は、その構造が組織のコミュニケーション構造の複製となる設計を生み出す
- イネイブリングチーム ストリームアラインドチームをサポートするためのチーム
- 実践コミュニティ (CoP: Community of Practice) 相互の共有や学習を促進する横断的グループ
- プラットフォーム ストリームアラインドチームが利用するセルフサービス(プラットフォーム)を管理するためのチーム
- 小さなチーム大きな組織 チームの人数は多くても8〜10人 小さなチームほど動きやすい
感想
少人数のストリームアラインドチームが、責任を持ってマイクロサービスを所有し、開発、リリース、運用する。狙いは開発の高速化、モチベーションの向上など。イネイブリングチームがサポートすることで、少人数のストリームアラインドチームを実現できる。
ソフトウェアだけを考えていてもだめで、どうやって動きやすい組織を作るか?望ましいアーキテクチャを実現するための組織を作るか?もセットで考えないとだめだと感じた。
16章 進化的アーキテクト
進化的アーキテクトの主な責務
構想 システムに統合された技術的構成があるようにする責任がある
共感 顧客、同僚に対する自分の判断の影響を理解する
協調 高層の定義、改良、実行を支援するために、できるだけ多くの仲間、同僚と関わる。
適応性 顧客、組織からの要求により、技術的構想を変更するようにする
自律性 チームに対して、標準化、自律性の実現の間の適切なバランスを見出す
ガバナンス 実装しているシステムを技術的高層に合わせ、人々が正しいことをするのを簡単にする
感想
このトピックは高次元のこと過ぎてピンとこなかった。
技術的構想を描き、同僚に共感してもらい、それにそってシステムを構築してもらう必要がある。
大きな会社だと組織をまたぐ、技術的な構想になるのだろうか。