読者です 読者をやめる 読者になる 読者になる

マーチン・ファウラーによるマイクロサービス解説

Microservices

「マイクロサービス」という単語は根強く生きているが、どうも人によって概念のレベル・射程が大きく異なるように感じる。個人的には「サービス分割」くらいに捉えている。 しかし、マイクロサービスのエコシステムとして様々なサービス・ミドルウェアが開発されていることも事実。中期的なトレンドを見誤らないように、中心となっているコンセプトの理解や課題の解決をどう捉えているのかを考えたい。

www.martinfowler.com

個人的感想

記事中に述べられているように、個々の要素に真新しさは感じない。UNIX思想、オブジェクト指向プログラミング*1、DDD、アジャイル、DevOpsなどによって共有されているベストプラクティスを実践するためには、アプリケーションを小さな単位のコンポネントの集合体として設計することが有益、と言った感じ。

なかでも、クラウドのエコシステムによる運用制約の軽減とアジャイルの普及が最大の原動力であるように感じる。そのため、開発観点からのトップダウンな設計手法として理解するよりも、向上された運用能力を活用したアジャイル開発ベストプラクティス、といった観点から考えるほうが理解が進むのではないか。

そのため、マイクロサービスを導入するならば、開発観点からトップダウンで設計していくのではなく、CI/CD環境でアジャイルに開発していく中でどのようにサービス群が進化していくかをボトムアップで考え、最低限のポリシーをざっくり設計して資産やノウハウを蓄積していくいいのかもしれない。なぜなら理論的な裏付けがあるわけではないから。

記事を読む前に検討事項を列挙してみたが、いくつかは短所として、いくつかは困難としてさらっと書かれている。

  • オーバーヘッド
    • コンポネント数増加による開発工数・運用コスト・性能への影響は?
    • ある機能をライブラリ・サービスのどちらで設計すべき?
    • DB・キャッシュは独立させる?ある程度共有する?
  • 互換性
    • 必然的に生じる変更の影響(互換性維持・依存管理)はどうマネージする?
    • サービスをまたぐ設計変更がきつくならないか?
  • 組織的な問題
    • APIのポリシー、設計方針などがバラバラになってもよい?
    • チーム・組織感の調整が大変なので、結局コンウェイの法則に縛られるのでは?
    • 車輪の再発明・コピペが増えないか?問題ない?

設計上重要なインターフェイスについては「進化的設計」として互換性維持の方法とConsumer-Driven Contractsが挙げられている。後者はどうやら契約をクライアントごとに定義するパターンらしい。あとで読む。

翻訳は見直していないので記録まで。ちなみに、書いたあとに同様のメモマイクロサービスの前提条件を見つけた。

Martin FoulerによるMicroservices解説(2014年)

定義

マイクロサービスは多くの組織で使われ始めている。UNIX思想*2のようなもので、革新的ではない。しかし有益であるにもかかわらず十分普及しているとはいえないので、ぜひ検討すべき。

マイクロサービスに厳密な定義はないが、以下の特徴がよく挙げられる。

  • 独立してデプロイ可能なサービスの集合
  • 各サービスはプロセス内で動作し、軽量なAPI(HTTPなど)で通信
  • サービスはビジネス観点で分割されている
  • 完全自動デプロイ
  • 中央集権的な管理を極力排する。言語、DB、開発チームを統制しない

対照的なのはすべてのドメインロジックが1つに集約されたモノリシックアプリケーション。

  • 変更がある場合はすべてビルド・デプロイしなければならない。
  • モジュール構造が複雑になっていきがち
  • スケールさせる場合に不要なロジックもスケールされる

以下、特徴を列挙。

サービス経由でのコンポネント化

用語定義

  • コンポネント:独立して置換・更新可能な単位
  • サービス:プロセス外コンポネント。サービス間は軽量なHTTPやRPCなどで通信する
  • ライブラリ:プログラムにリンクされ、インメモリのファンクションコールによって利用されるコンポネント

複数プロセス(アプリと専用DBなど)を1つのサービスとしてもよい。

ライブラリよりもサービスを使うべき

  • 独立してデプロイできる。小さいほど全体に占める変更の影響は少ない。依存されるサービスへの影響は、cohesive boundaryとcontractのevolution mechanismsで対応(?)
  • インターフェイスがより明示的になる。Published Interfaceが考慮されないから、クライアントと密結合になってしまう。

欠点

  • リモートコールのコスト
  • コンポネントをまたぐ責任の移行が困難

ビジネス観点での組織化

Client/Server/DBのように技術観点でアプリケーションを分割すると、コンウェイの法則にあるように手の届く範囲で最適化されていく。また、メンバは複数のビジネス領域にまたがって責任を持ちがちで効率悪い。

マイクロサービスでは、ビジネス観点でサービスに分割。つまり、PM含め開発に必要な能力を備えたクロスファンクショナルなチームになる。1チーム6-12人くらい。

プロジェクトでなくプロダクト

プロジェクトベースの開発(終わったのであとよろしく)ではなく、プロダクトに責任をもつ。Amazonでいう"you build, you run it"。開発チームも本番運用に責任を持つ。

マイクロサービスでなくても実現できるが、前述のチーム編成の話も含め、粒度が小さいほうが人間関係を築きやすい。

スマートエンドポイントと軽量パイプ

プロセス間通信に対して、Enterprise Service Bus (ESB)のようなスマートなアプローチがたくさんある。

マイクロサービスでは別のアプローチを取る。UNIXのコマンドとパイプ(入力 - 処理 - 出力)のような疎結合・高凝縮な設計を目指す。つまり、サービス内でスマートなロジックを完結させ、RESTful APIのようなシンプルなプロトコルでつないでいく。

よく使われるプロトコル

  • HTTP:wwwやUNIXの思想に従う。Be of the web, not behind the web。
  • 軽量メッセージバス:RabbitMQやZeroMQのような、非同期処理・メッセージルーティングのための軽量なもの。

モノリスを分割する際にはメソッド呼び出しをこういった通信パターンに移行するのが大変。愚直にやると大量のリクエストでパフォーマンス劣化するので、バッチ化するなどうまく対応すべき。

非一元な組織管理(Decentralized Governance)

組織が一元管理されていると、ひとつの技術プラットフォームを標準化しがち。だが標準化はオーバーヘッドを生じがち。目的に合わせて技術を使えない。

ドキュメントを書いて標準化するよりも、ツールを作ってgit上の社内オープンソースとして共有することを好む。(Netflixオープンソースが好例)

また中央集権的な標準化ではなく、サービスコントラクトをTolerant ReaderConsumer-Driven Contractsといったノウハウでマネージしている。YAGNIを避け、不要な依存をつくらない。

(追記:Tolerant Readerとは、XMLでいうXPathなどを利用して、必要なフィールドのみ部分的に利用することを指している。JSONバイナリでももちろん可能。)

"build it / run it"で24/7サポートしていると、中央集権的な管理から遠のいていくだろう。

非一元的なデータ管理(Decentralized Data Management)

モデル、データストレージの選定も非一元化。

  • システムによって概念のモデルは異なる(e.g. 販売/サポートの「顧客」の扱いの違い)
  • システム内でも概念の扱いがコンテキストによって異なることはある(cf. DDDのBounded Context)
  • データベースも適材適所で使うべき

トランザクションではなく結果整合性を使う。密結合の原因となりがちだから。Starbucks Does Not Use Two-Phase Commit

インフラの自動化

CD/CI

失敗のための設計

サービス障害の影響を可能な限り軽減する。事前に障害を想定した設計をし、NetflixのSimian Armyのように障害を故意に起こしてテストする。障害検知、自動復旧、モニタリング、ビジネス用の数値、circuit breaker。 特に、マイクロサービス化によって分割されたサービス間のコネクション断絶の影響は考慮すべき。

進化的設計(Evolutionary Design)

マイクロサービスの実践者は進化的設計の観点から、コンポネントの分割を素早く、頻繁に、適切に変化をコントロールする手段として考える。 分割の指針となるのは、独立して置換・更新可能であること。つまり他コンポネントに影響を与えずに変更できること。長期的にはサービスは進化していくよりも書き直されることを想定している。 これはモジュラーデザインの一例。同時に変更されるものを一箇所に、変更されないものを頻繁に変更されるものとは別にする。いつも二箇所を変更しているならばひとつにまとめる。

コンポネントをサービスに分割することによってリリースの粒度も正確になる。デプロイも速い。 ただし、変更が利用者に与える影響を考慮する必要がある。伝統的にバージョニングが使われてきたが、これは最後の手段にすべき。変更への耐性を向上することで避けることができる。

(追記:どうやら互換性をできるだけ維持するようなインターフェイス設計のことのようだ。RESTfulのベストプラクティスなど*3

マイクロサービスが未来の設計か?

マイクロサービスは重要なアイデアであり、真剣に考慮すべき。しかし、様々なメリットがあるにせよ、マイクロサービスがアーキテクチャの進む先だとは断言しない。今のところモノリスに比べてメリットは多いが、まだ判断するには早い。Sam Newmanの本*4を次に読むべき。

アーキテクチャに対する判断の真価は、数年後にならないとわからない。多くの人がマイクロサービスでは境界が明白なのでモノリスのような経年劣化は起こらないはずだと考えているが、どのように成熟するかはまだ評価できない。

数年後に劣化する可能性は明らかにある。コンポネントの境界は必ずしも明白ではないからだ。進化的設計ではリファクタのためにうまく境界を設計しようとするが、それが困難であることは知られている。マイクロサービスはリモート通信によってより困難になるだろう。サービスの境界を超えたコードの移動、インターフェイス変更のための利用者との協業、互換性維持のためのレイヤ、より複雑なテストなど。

また、コンポネント境界が明白でない場合、複雑性をコンポネント間の通信に漏出してしまうことになる。より不明瞭で、コントールし難い場所に複雑性を押し出してしまうかもしれない。小さなコンポネント内はうまくいっても、サービス間は無茶苦茶になっているかも。

最後に、スキルの問題。新技術はスキルのあるチームでうまくいっても、そうでないチームはついていけないことが多い。マイクロサービスが、スキルが十分でないチームのシステムを向上させるか、悪化させるかは予想できない。

また「マイクロサービス・アーキテクチャとして設計を開始すべきでない」という合理的な意見も聞いた。モノリスで始めて分割していけばよい、と。(プロセス内とプロセス間のインターフェイスでは要件が違うのでこれは理想的ではないが。)

だからマイクロサービスを楽観的に勧めることはできず、最終的にどうなるか確信はないが、いまある不完全な情報で判断しなきゃならないので難しいよね。

*1:

アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技

アジャイルソフトウェア開発の奥義 第2版 オブジェクト指向開発の神髄と匠の技

*2:

UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

*3:

RESTful Webサービス

RESTful Webサービス

*4:

マイクロサービスアーキテクチャ

マイクロサービスアーキテクチャ