現職において Monolith アーキテクチャから Microservices アーキテクチャへの移行とその基盤の構築に関わって 2 年近くが経った.未だ道半ばであるがこれまでの経験や日々のインプットをもとにいろいろ書いておこうという気持ちになった.本記事ではそもそも Microservices アーキテクチャとは何かを整理し,なぜやるべきか?・なぜ避けるべきかを整理する.
Microservices?
Microservices アーキテクチャとは「Single purpose,High cohesion,そして Loosly Couploed なサービスを組み合わせてシステムを構築する」アーキテクチャ手法である.それぞれの原則をまとめると以下のようになる.
- Single purpose: 一つのことに集中しておりそれをうまくやること
- Loose coupling: サービスは依存するサービスについて最小限のことを知っていること.あるサービスの変更に他のサービスの変更が必要でないこと.
- High cohesion: それぞれのサービスが関連する振る舞いやデータをカプセル化していること. ある機能を作るときに全ての変更が一つのサービスにまとまっていること.
Microservice Architecture at Medium
Microservices アーキテクチャをモデル化するときはこれら 3 つを「全て」満たす必要がある.これによって Microservices アーキテクチャの利点を最大限に活かすことができる.一つでも欠けると崩壊する.
- Single purpose を満たさないとそれぞれのサービスは多くのことをやることになる.つまり複数の Monolith が存在することになる
- Loose coupling を満たさないと一つのサービスの変更が他のサービスに影響を与えることになる.そのため(Microservices 化のメリットである)素早く安全にリリースをすることができなくなる.密結合するとデータの不整合やデータロストなどが起こる
- High cohesion を満たさないと分散 Monolith になる.つまり一つの機能の開発のために複数のサービスを変更しないといけなくなる
コードの行数が少ないから・細かなタスクを扱うから Microservice ではない.Microservices アーキテクチャのゴールはできる限り多くの小さなサービスを持たないことである.また新しいテクノロジーを使っているから Microservice ではない.Kubernetes 上のコンテナとして動いているから Microservice ではない.
Why Microservices?
「Microservices は組織論」と言われるように Microservices アーキテクチャの究極的な成果物は新たな組織図である.新たなアーキテクチャに基づく新たなチームの編成,組織の再構成を狙うのが大きな目的である(逆コンウェイの戦略,Inverse Conway Maneuverなどと呼ばれる).
What We Got Wrong: Lessons from the Birth of Microservices
組織を再編する大きなモチベーションはサービス成長に伴う組織の拡大(エンジニアの増加)に起因することが多い.組織の拡大はそのパフォーマンスの低下を引き起こす可能性がある.Accelerateは 2013 年から 2017 年の 4 年間を通してスタートアップを含む 2000 以上の企業から「いかに組織のパフォーマンスを加速させるか」という視点で聞き取り調査を行った本である.この調査結果の一つに以下のグラフがある.
このグラフはエンジニアの数とそのパフォーマンスを組織の違いによってマッピングしたものである.グラフの縦軸はここではパフォーマンスの指標として一日あたりのエンジニアあたりのデプロイ数を示している.この調査結果から,組織を拡大しても(エンジニアが増えても)パフォーマンスは必ずしも高まるわけではなくむしろその低下をもたらすことがあることがわかる.一方で指数関数的にそのパフォーマンスが高まった組織があることもわかる.パフォーマンスに起因する要素は様々だがアーキテクチャとチーム編成が与える影響は大きい.このアーキテクチャとして近年デファクトになりつつあるのが Microservices アーキテクチャである.
以下は Microservices アーキテクチャが可能にすることを端的に表した図である.
The microservice architecture is a means to an end: enabling continuous delivery/deployment
Microservices アーキテクチャによって可能になるのは小さく自立・独立した Cross functional なチームを各サービスに配置することである.そしてそのチームに対して適切な権限を与えて構成する「組織」と Microservices「アーキテクチャ」が可能にするのは Continuous Delivery という「プロセス」である.この「プロセス」が「組織としてのパフォーマンスを最大化すること」を可能にする.以下の Airbnb の KubeCon 2018 North America での Keynote Developing Kubernetes Services at Airbnb Scaleのスライドがとてもシンプルにこれを伝えていた.
https://github.com/warmchang/KubeCon-North-America-2018
(例えば 1 時間あたり 1 回デプロイできるとする.Monolith アーキテクチャの場合は業務時間内にリリースできる回数は 1 日 8 回が限度になる.これが 10 つのサービスに分割された Microservices アーキテクチャなり各チームが「独立して」リリースを行える場合その回数は単純計算で 10 倍の 80 回になる)
まとめると Microservices アーキテクチャに移行する最大の理由は「組織の拡大においても小さな自立独立したチームによる組織を編成し組織としてのパフォーマンスを最大化する」ことである(2000 年初頭において Google は Computer Science の問題を解決するために今でいう Microservices アーキテクチャへ移行したという話 What We Got Wrong: Lessons from the Birth of Microservicesがあるが現在ではこの理由で Microservices をやる理由は少ないと思う).
他にも利点として「サービスごとに独立した Scalability を確保すること」「障害の局所化により Availability を向上すること」「小さなコンテキストで開発することによる生産性の向上・On-boarding コストを減少させること」などの利点が挙げられるが「パフォーマンスの最大化」が最も大きなモチベーションであるべきだと思う(現職では更に先を考えているところに自分はやりがいを感じている).
Microservices アーキテクチャのプラクティスには「サービスごとにデータを持つこと」「データの非正規化を許容すること」「セルフサービス化」など Monolith アーキテクチャでは考えられないプラクティスが多く存在するがこれらはすべてこの「組織のパフォーマンスの最大化」につながる.Microservices アーキテクチャを進める上で多くの意思決定を行う必要があるがこの初期衝動は常に頭においておきブレてはならない.
Why NOT Microservices?
Microservices アーキテクチャには「いつ採用するべきか?」について正確なアルゴリズムが存在しないという大きな問題がある.私的な意見を言えば,運用まで含めて各サービスに十分な人員を当てられない・将来的に当てられる予定がない,専用の基盤を構築する余裕がないなら Microservices はやるべきではないと思う.むしろ負担・オーバーヘッドになってしまう.要するに規模は重要なファクターである.
Microservices は上述したメリットに対するデメリットが多い.Microservices エコシステムはそのデメリットを補うものに過ぎない.例えば Service mesh などという複雑なシステムは Microservices をやらない限りほとんど必要ない.以下にデメリットと難しさ(とそれを補うエコシステム)をいくつか挙げる.
- Observability の難しさ.Monolith アーキテクチャに対して複数のサービスが関わる Microservices アーキテクチャにおいては Observability の確保がとても難しい.個々のサービスの Observability を整備するのは当然のこと,サービスにまたがった Performance 問題を検出するために Distributed tracing を整備し,サービス間での Debuggability を高めるために統一的なログフォーマットを準備したり...を行う必要がある
- ネットワークの難しさ.Monolith のプロセス通信とは異なり Microservices ではネットワーク越しの通信が当たり前になる.ネットワーク越しのリクエストが前提となる Microservices は分散システムである.Fallacies of distributed computing(分散コンピューティングの落とし穴)にあるように「ネットワークは信頼できる」と思ってはいけない.リクエストが失敗したときに Back-off つきで Retry を行うこと,Timeout を設定すること,適切な Rate-limit をつけ異常なリクエストをブロックすること,対象のサービスが何らかの障害で死んでしまっても Circuit breaking でそれを回避することといったプラクティスの整備が求められる.これらを各サービスが整備しないといけない.これらの問題を解決しようとしているのが Istio を代表とする Service mesh である.詳しくは"Service mesh とは何か" に書いた
- セルフサービス化の難しさ.Microservices の利点を最大限に活かすには各チームが独立して開発からリリース・運用を行える,つまりセルフサービス化が必須である.セルフサービス化を補助するツールは多く存在しているが,それらは「Operational maturity」の問題を解決しない.サービスチームが自分たちで運用を行えるようにするようには,Incident の Training を実施したり,Production readiness check のようなドキュメントを準備したり,DiRT や Gameday を実施したり...を行う必要がある.これらに関してはThe human scalability of "DevOps"やService Ownership @Slack,Optimizing SRE Effectiveness at The New York Timesが詳しい.自分がやっていることはMicroservices Platform at Mercariで話した.各社がこの問題に取り組んでいる.
- データの一貫性の確保の難しさ.各チームが独立して開発からリリース・運用を行うために各サービスごとに自分たちのデータストアを持つことになるが,それはサービスの特性にあったデータベースが選択される.Heterogeneous なデータベース界では分散トランザクションは実質不可能になる.このような状態での一貫性の確保では Online Event Prcessing のような仕組みが必要になる.また考え方も更新する必要がある.詳しくは ACM Queue のOnline Event Processing: Achieving consistency where distributed transactions have failedが詳しい
- サービス分割の難しさ.上述した Microservices の原則を満たすサービスのモデル化は容易ではない.特に既存のシステムからの移行はもとのデータ構造に引きずられることも多くそれらを丁寧に分解していく必要がある
- セキュリティの担保の難しさ.各チームが独立して開発からリリース・運用を行う一方で各サービスごとにセキュリティレベルを統一的に担保しないといけない(もちろん高いレベルが求められるサービスもあれば最低限を満たしておけば良いサービスもある).そのために統一的な Audit をする,CI/CD パイプラインにおいてセキュリティのチェックを強制するといった仕組みが必要になる.コンテナ界隈でのエコシステムとしては Grafeas や Kritis がこれらの問題を解決しようとしている.
これらの問題を理解しつつそのコストを払ってでも Microservices アーキテクチャによる「組織のパフォーマンスの最大化」に利点を感じないといけない.
Misconception
Microservices ではそれぞれのチームが自分たちの Requirements や Workload に最も適した技術や言語(やフレームワーク)を自由に選択できるという利点がある,と言われる.しかしこれは完全なる自由を意味しない.完全な自由は逆に生産性の低下を引き起こす.そうではなく,共通レイヤー(基盤)の上に Extensibility を許可するといったことが必要になる.Observability や Monitoring,セキュリティや認証・認可,CI/CD といった直接ビジネス的な価値を出さない Cross-cutting concern には統一的な方法があるべきである.そしてこれらはそれぞれの開発チームではなく専用の基盤チームが取り組むべきである.開発チームはビジネス的な価値に注力し,基盤チームはその開発チームの Productivity に注力する(Design Microservice Architectures the Right Way,10 Tips for failing badly at Microservices)
「Microservices で Polygrot は避けるべき」なのはこの共通レイヤー(基盤)の構築のコストに直結するからである.専用のライブラリやツールの提供やドキュメントの整備やサポートなど 1 つの言語のエコシステムを作るには多大なコストがかかる.特に Microservices の初期は統一化のための模索が必要だが複数の言語やフレームの乱立は障壁になる.これは Google や Netflix などを見てみてもわかると思う.あの組織規模でもメインで使われる言語は限定されている.利用する言語は組織の大きさ(基盤にさけるリソース)や Microservices の成熟度によって増やしていくのが良いと思う.
References
- Building Microservices
- Production-Ready Microservices
- A Philosophy of Software Design
- Monolith First by Martin Fowler
- Microservice Architecture at Medium
- Microservices at Spotify
- Adopting Microservices at Netflix
- Microservices at Netflix Scale
- Lessons Learned on Uber's Journey into Microservices
- How we ended up with microservices at SoundCloud
- From Monorail to Monorepo at Airbnb
- Design patterns for microservices
- Microservices on AWS
- Go + microservices = Go kit
- Testing in Production, the safe way
- Design Microservice Architectures the Right Way
- 10 Tips for failing badly at Microservices