マイクロサービスを蝕む負の力学

マイクロサービスを蝕む負の力学

マイクロサービスアーキテクチャでは、全体を分割し小さいチームとサービスにすることでチームの責任範囲を小さくし、 限られた範囲内で独自にPDCAを回すことで人数の拡大に対しても生産性を落とさないのが利点の一つになります。 これにより個々のマイクロサービスの関心事は小さく保たれるため、構成はシンプルになり実装しやすくなります。 一方でサービス間の依存関係が複雑になったり、通信量コストがとても増えるといったサービス間をまたいだところへの問題、 データの整合性や分散コンピューティングの落とし穴といった分散システム特有の問題、通信先のマイクロサービスの信頼性、安定性といった問題など、マイクロサービスアーキテクチャ全体としての複雑性は上がります。 そして、こういった複雑性や一部のマイクロサービスの不安定さは、そのマイクロサービスを構成する全員に悪影響を与えます。 つまり、個々のマイクロサービスの複雑性がマイクロサービスアーキテクチャ全体に転化し、より複雑になるといったイメージです。 このマイクロサービス全体の問題はdeeeetさんのなぜMicroservicesか?の「Why NOT Microservices?」あたりが詳しいです。

当然この状態を放っておくわけにはいかないので対策が必要になりますが、ここでマイクロサービスの負の力学が牙を向きます。 モノリシックを複数のチームで触っている場合、依存関係や影響度といった原因で問題の難易度が上がりますが、 コードベースは共有しており開発・QAの参加ハードルは低く、またパフォーマンスといった問題は直接自分のチームに影響を与えるため、他のチーム起源の問題であっても解決に動きやすいです。 また、通信の問題などはモノリシックであればメソッド呼び出しに変わり安定するといったように、そもそも起きない事項もあります。

マイクロサービスにした場合は完全に別々のシステムであるため問題を発見するのが難しくなり、問題そのものも複雑化します。 また、見つけたとしても違うサービス、違うチームと協調して修正を行うのはモノリシックのそれに比べるとかなり大変です。 例えばマイクロサービスにおいては通信周りの重要性がとても高く、サービスメッシュやApache Kafkaといった様々なミドルウェアを入れて解決を図ろうとすることが多いです。 ですがその場合、それらを適切に設定・運用するためのリソースに加え、各サービスを管理しているチームに導入してもらうといった協調作業が必要になり、モノリシックに比べるとコストが大きくなります。 また、マイクロサービス全体に関わる問題解決は全体としては嬉しいですが、ある特定のサービスチームに対しての嬉しさは分散され小さくなるため、 特定のチームの目標に落とし込むのは難しく、チームとして解決に動くインセンティブが生まれにくいです。

マイクロサービスの問題には手を出しにくい

特にチームにエンジニアがチームに少ない状態でマイクロサービスにした場合、 マイクロサービス全体の問題解決をしている間はチームの作業は止まってしまい、目標達成に効果的な手を打つという観点から見るとリソースを割り当て辛いです。 もちろんそういった影響が無いように頑張るという手段もありますが、 マイクロサービスの問題は難易度が高く、複数のチームと協調して動く必要があることが多いために片手間でやるに少々重いです。 そのため直接的な被害が起きるまで、場合によっては起きてさえ解決に動くのが難しくなります。 このようにマイクロサービスでは個々のサービスの複雑性は抑えられますが、 マイクロサービス全体の問題は難易度は上がり、かつリソースを割くインセンティブは下がってしまうため、 問題を放置する方向に強い力学が生まれます。 なお、ここで"力学"という言葉を使っているのは、これはある個人の意志や能力によって起きるもの・解決できるものではなく、 チームや組織全体としてそういう方向に選択をさせる無形有形の力が発生しているためです。

負の力学に対抗する

このマイクロサービスの負の力学に抗うにはどうすればいいでしょうか。 おそらくですが解決方法は二通りあり、問題解決する力を増やすか、問題解決を簡単にするのどちらかだと思います。

例えば各チームに複数人のエンジニアがいれば、 マイクロサービス全体の問題解決にリソースを避いたとしてもチームの生産性への影響は相対的には少なく、 問題解決にリソースを割きやすくなります。 また、十分なリソースがあればマイクロサービス全体の問題を解決することがメインの目標となる基盤チームを作るといった取り組みもできるため、 マイクロサービスの負の力学に打ち勝つ事ができます。 ただし、これは要するに専任で問題解決を行うリソースを割り当てるということなので、やはり人数がある程度いないとそれを捻出することは難しく、 そもそも十分な人数がいない場合は効果が薄いです。 そういった場合は、マイクロサービスの数を抑えて問題解決の難易度を下げるのも一つの手だと思います。 例えば10個のマイクロサービスと2個のマイクロサービスを比較するなら、明らかに後者のほうが影響する場所やチームの数も少なくなり、 問題が複雑になりにくいです。 そのため問題の難易度は下がり、必要なリソースが減るために負の力学が強くならず解決できるようになります。

別の力学の基盤チーム

マイクロサービスアーキテクチャは個々のサービス単位で見ると複雑性が抑えられ、影響範囲が小さくなるためチームのことをするにはとても良いです。 ですがチームを超えた領域に複雑性が寄せられていき、かつそういった領域の複雑性は個々のチームにとっては解決しづらい力学が働くため放置されやすくなります。 十分なリソースがあれば基盤チームを作って複雑な問題に抗うこともできますが、そうでないのであれば問題を複雑にしないようにマイクロサービスの規模を抑えるといったのも一つの方法ではないかと思います。 もちろん、AWSやNetflixみたいなきちんと分割された大量のマイクロサービスは最終的には正しい状態といえますが、 それを3人のスタートアップで運用するのは無謀です。 最終的に正しい状態が現在の状態にとっても正しいわけではないので、自分のチームや組織全体の状態に照らし合わせて適応する必要があります。 では具体的にどれくらいの人数でどれくらいマイクロサービスにすればいいかについてはおそらく解は無いのですが、 マイクロサービス全体の問題にリソースを割く余裕が各チームにある、ある程度の規模を超えるなら選任の基盤チームが置けるというのが一つの指標ではないかと思います1 2

補足事項

マイクロサービスにおいては複雑性をアーキテクチャ全体に寄せるので、それを構成する個々のサービス自体は複雑になりにくいです。 そのため、Railsアプリケーションであればそんなに複雑になることは少なく、またWeb API経由でしか依存が発生しないので、内部のリファクタリングも比較的やりやすく、十分なリソースが避けるのであれば特定のサービス間の問題は比較的かんたんに解決できます。 そのため、マイクロサービス全体の問題を避けるために巨大なモノリシックにするよりかは、適切な数のマイクロサービスのほうが優れていると考えられます。 ただし、例えばデータ整合性といったマイクロサービス特有の問題は発生し、かつしっかりとそれに対応するのはかなりのリソースが必要となるため3、数以外にも分割する内容など様々な要素を考慮する必要があります。

マイクロサービス化を抑えるという事はそのコストを後から支払う必要があり、多くの場合後からマイクロサービスにするのはコストがとてもかかるため、 初めから最適解のマイクロサービスをやるよりも余計なコストがかかります。 ただ、マイクロサービスの分割はエンジニアリング部分が大きいためある程度コントロール下に置きやすいですが、 ビジネスの成功に関しては他の職種の影響がとても多くコントロールするのが難しく、かつビジネスが成功しないと最適解たどり着く前に倒産してしまうため、 後者の問題を避けた方が良いというバイアスがかかっています。 そのため、うまくビジネス側の問題もコントロール下に置ける組織体制が整っているならば、私が考えているより攻めたマイクロサービスにするのはありだと思います。

退職しますエントリで、「マイクロサービスにおいては各サービスのサーバサイドエンジニアはそれほど高度な問題に直面しない」といったことや、 「本来ならマイクロサービス全体の複雑性に立ち向かうような行動にシフトするのが正しいのですが…様々な理由からそれは難しかった」と書いたのはこれが理由です。 FiNCではOKRを採用しているため、プロジェクト全体のOKRが設定され、それを達成するために細かく分解されたものがチームの、最終的には個人のOKRに設定され、やること・やらないことが決まります。 このとき、マイクロサービスの各サービス側のエンジニア(ここではアプリケーションエンジニアと呼びます)の目的はチームの目標達成のための行動がメインとなり、そうでない活動に対しては動きにくくなる設定になります。 前述のように同規模のモノリシックと比べると、分割されたマイクロサービスは格段に複雑になりにくく、アプリケーションエンジニアが高度な問題にあたることは上手に作っていくと少なくなります4

一方でマイクロサービス全体の複雑性は上がってくため、その領域には高度な問題が起こっていきますが、それを解決することは多くの場合目標を達成するのに直結する行動ではなく、アプリケーションエンジニアやサービスのチームとしてはより手を出さない方向への力学が働きます。 一方でアプリケーション開発が直接の目標に置かれないエンジニア(ここでは基盤エンジニアと呼びます)には阻害する力学はない、もしくは小さいため問題解決に動きやすいです。 もちろん私がやったように業務時間外の年末年始でOpenAPI 3を仕上げてそれを業務に導入するといった手により、“無理矢理"横断的な領域に攻め入る事も出来なくはないですが、 マイクロサービスを採用する際にそれを期待して作るのは難しいかなと思います。

ただし、後者の横断的課題に手を出すのが難しいというのはn=2,3人ぐらいの例であり、 OKRの設定の仕方による影響や、私の立ち位置(炎上プロジェクトに突っ込んで火消しをさせる人員として確立してしまった)、会社の風土、ビジネスの仕方などに依存しているため、前提条件よっては多少変わる可能性があります。 ですが、この記事全体で述べたようにそれを阻む力学はある程度の普遍性を持っており、適切な人員規模がないのにマイクロサービスをするのは筋が悪いな…といった感じです。

また、これは同僚のqsonaさんが書いたエントリとおそらく同じマイクロサービスアーキテクチャを意識しているにもかかわらず、正反対のような結論になっています。 これはおそらく私がアプリケーションエンジニア側からみた実際の状態からの進め方について、向こうが基盤エンジニア側からみた最終的な目的地を話しており、 違うポジションから違う時間軸の話しゆえにだと思います。 私としては、アプリケーションエンジニアに対して所属しているチームが生み出すビジネス的価値を最大にする目標が置かれ、マイクロサービス全体の問題解決よりチームの目標達成に関わる行動が行われるように力学が働くのは仕方ないかなと思います。 特にエンジニアが少ない場合より一層その力学は強くなるため、優先度の判断をした結果マイクロサービスの問題解決にはリソースを割けなくなるのは多発します。 そのため、手に余るレベルのマイクロサービスはかえって足枷になりチームの生産性が向上しない、最悪の場合はマイクロサービスが価値を生む規模に達することなく事業が成り立たなくなることがあるため、 別の目標を持った基盤チームが作れる、そういった別の活動に割けるリソースがないと難しいよねという方向になります。 ただし、これはあくまでもアプリケーションエンジニア側からみた場合の話であり、別の目標を持ち違う力学にいる基盤エンジニア側からみた場合はおそらく別です。 例えば全てのマイクロサービスで出てくる問題を解決する基盤チームはまずワークする事は無く、ある程度サービスチーム側にもそういったことができる人が必要です。 そのため、基盤チームからみた場合はサービスチームにもマイクロサービス全体の課題解決をするリソースを捻出させる力学が発生します。

このようにサービスチームはビジネス側にリソースを割く力学が生まれ、基盤チームはマイクロサービス側にリソースを割かせる力学が生まれます。 ですがリソースは限られており、またビジネスが成功しないとそもそも事業が成り立たないのでサービスチームの力学は基本的にビジネス側に強く向いています。 十分にリソースがあればビジネスを成長させつつマイクロサービスの問題も解決することができるのですが、 そうでないならば、少ないリソースでやり過ごせる規模の問題に収まるように、マイクロサービスを大きくしすぎないのが一個の答えなのかなといった感じです。 ただその場の状況や、周辺機能の進歩によって適切な規模のラインは動いていくため、一概に答えはないのでどうなのかという共通見解を作るのは難しいのですが…5


  1. このあたりはdeeeetさんの「運用まで含めて各サービスに十分な人員を当てられない・将来的に当てられる予定がない,専用の基盤を構築する余裕がないならMicroservicesはやるべきではないと思う」という意見と一致してます ↩︎

  2. 例えばcookpadとかは強い基盤チームを作って大きなマイクロサービスを進めていて、外から見た限りは上手く行ってる感じがします ↩︎

  3. 例えば決済システムだとこんな感じのシステムを構築する必要がありますが、それなりのリソースを使います。 https://tech.mercari.com/entry/2019/06/07/155849 ↩︎

  4. もちろんそのチーム固有で難しい問題が起きたり、サービス間連携といったものがあるのでゼロにはなりませんが、基本的にはそれほど起きないです ↩︎

  5. 例えば資金が潤沢なら最適解に近いマイクロサービスを作っても潰れずに生き残れる可能性が高いので無駄が少ないですし、サービスメッシュのようなツールが進化していけば問題解決を少ないリソースで行えるようになり、人数が少なくても運用できるようになるなど、様々な要因で適切なラインが変わっていきます ↩︎