マイクロサービスはアンチパターン

takezaki
vtecx
Published in
Mar 21, 2022

--

マイクロサービスの矛盾

先日、「マイクロサービス?モノリス?SaaSアーキテクチャのPros/Cons | SaaS.tech #1」(youtube)(togetter)というイベントを観ました。これは、マイクロサービスのイベントだったのですが、新規のシステム開発において積極的にマイクロサービス化をすすめる方が少なかったのが意外でした。

マイクロサービスの開発経験のある優秀なエンジニアであっても、まずはモノリスからはじめてプロダクト境界を意識して設計していき、functionベースで切り出せるもの(認証や通知、メール送信、バッチ等)は切り出しつつ、必要であればモジュラーモノリスかマイクロサービス化を慎重に検討するということでした。素晴らしい冷静な判断です。

というのは、ドメインの専門知識がないうちにマイクロサービスの複雑なシステムを設計するのは、多くのソフトウェアプロジェクトが陥りやすいリスクの高い行動だからです。

Martin Fowler氏によると、

「私が聞いたことのあるほとんどすべてのケースでは、ゼロからマイクロサービスシステムとして構築されたシステムは深刻なトラブルに見舞われています…たとえアプリケーションが十分に価値のあるものになると確信していても、マイクロサービスで新しいプロジェクトを始めるべきではありません」とのことです。

新規のシステムだけではありません。既存のシステムが次第に肥大化して開発スピードなどに弊害が現れてくると、各チームを小さく保つことでスピードやアジリティを維持する目的のためにマイクロサービス化が候補に挙がってくるわけですが、安易にマイクロサービスによる解決を図ろうとはしてはいけません。

上記のイベント登壇者は主に自社Webサービスの開発者の方々だったのですが、マイクロサービスをバズワードだということを認識しつつ、既存の大きくなりすぎたシステムを冷静にリファクタリングやリアーキテクチャリングといったアプローチで取り組んでいるのが印象的でした。

「大きくてツライ」状態ではあるものの、必ずしも「小さくて嬉しい」とはなりません。
大きいものを分けたいとなったときにモノリス、モジュラモノリス、またはマイクロサービスという観点ではなく、分けたいものが「コード」「インフラ」「デプロイ単位」「チーム」など、どれなのかを考えて技術選択するようにしています。

彼らがいうには、新規であれば特に従来の慣れた開発の方がアジリティが高く、迅速にお客様に届けることに注力できるとのことでした。

ここで私はある違和感を感じました。

そもそも、マイクロサービスにすることのメリットは、

「開発対象を小さくして開発のスピードを高める」「サービスを分離して障害時の影響を極小化する」「開発を分離してコストを管理または最適化する」・・マイクロサービスの失敗は技術だけの問題か

などといわれてきたはずです。
つまり、モジュールの独立性を高くして柔軟に変更できるようにすることでアジリティを高め、ビジネス変化に迅速に追随できるようになるというわけです。

しかし、マイクロサービスを構築するよりも、これまでどおりの慣れた開発の方がアジリティは高いとはどういうことでしょう?

モノリスでスタートした一番の理由は「スタートアップはスピードが命」。今手元にある道具で最速で価値を届ける、そのために今手元にある道具で最適なアーキテクチャをとることが最善だといいます。マイクロサービス化することでシステム・組織的に考慮しなければならない様々なことを考えると開発スピードは遅くなる。特に初期のフェーズでは適していないのです。

マイクロサービス化は、その後の運用、チーム体制、その他考慮することがたくさんある(ログどうする?デバッグどうする?結果整合性?とりあえずツライ・・「マイクロサービス?モノリス?SaaSアーキテクチャのPros/Cons | SaaS.tech #1

有名な失敗事例「マイクロにしすぎた結果がこれだよ」もあります。

これが現実だとするとマイクロサービス化する意味は本当にあるのでしょうか?

ITベンダーの罪

根本的な解決方法にならないにもかかわらず、バズワードを背景にマイクロサービスをまるで「銀の弾丸」かのように売り込んでくるITベンダーが数多く存在します。
I○Mや富○通といったITベンダーは、クラウドネイティブなアプリケーションで「変化に強いシステム」を構築し、お客様のビジネスを加速、各機能をマイクロサービスでAPI化して公開することでより拡張性を高めよう、と訴求してきます。

長年、高いパフォーマンスで安定して運用してきた既存システムがあるにもかかわらず、ITベンダーは既存のシステムをリファクタリングしたりリアーキテクチャリングしたりはしません。既存システムへのリスペクトもないままスクラッチで開発しようとします。いわゆる、「作り直した方が早い症候群」です。

対するユーザ企業は長年、変更になかなか対応できないシステムに強い不満を持ち続けており、とにかく迅速に機能追加できるアジリティの高い柔軟なシステムを渇望しています。ITベンダーの訴求に「待ってました」とばかりに飛びついて、結果的に騙されるわけです。また、どうせ5年償却だしスクラッチで開発してもらおうとなります。しかし、システムは式年遷宮ではありません。これまで優秀な技術者が心血を注いで奇跡的なパフォーマンスと安定性を実現してきた歴史ある大切な資産です。スクラッチで同じものを簡単に作れるわけはありません。

特にITベンダーの罪が深いと思うのは、マイクロサービスが持つ負の側面を正しくユーザ企業に説明していないことです。

マイクロサービスで新規に開発した結果、工数が増えトータルの納期が長くなり、パフォーマンスの低い分散デカ泥団子のシステムが出来上がるわけです。これは作ってしまった後で問題の大きさに気づくので非常にやっかいな話です。ユーザ企業は負の側面があることに気づきません。既存システムが普通に動いているんだからまさか新システムが酷いことになるなんて夢にも思わないわけです。
ITベンダーはとにかくバズワードを使って売れればよいと思っているらしく、将来どんな酷いことになったとしても関係ないという態度に見えます。しかし、システムのリプレースに失敗した時、立ち直れないほどのダメージを負うのはユーザ企業の方です。

特に気をつけなければならないのはITベンダーの中にいる一部の熱狂的な技術者で、技術の自己実現のためには苦労を厭わない人たちです。マイクロサービスがバズワード化したのは、新しいアーキテクチャーに取りみたい一部の情熱的なエンジニアによる一時的なブームがあったからでした。
マイクロサービスが流行っているからやってみたけれど、予想通りうまくいかなかった、テヘ (;^^)ヘ.. ってな感じでやられてみてください。目も当てられません。

マイクロサービスがバズワードとなって久しいですが、いまは脊髄反射的にいきなりマイクロサービス作る人は指をさされて笑われると思った方がいいですね。もうそういう時代なのです。

いつでも元に戻せるように進化させるべき

マイクロサービスはアプリケーションアーキテクチャとインフラアーキテクチャが複雑に絡み合ったシステムで技術的難易度が非常に高く、適切に構築できなければ「分散されたモノリス」と呼ばれるアンチパターンに陥ります。分散デカ泥団子ですね。

“分解したい”というのが一般的なクライアントの要望ですが、分解(decompose)の意味はひとつではありません。アプリケーションの分解を望んだとしても、それでモジュラリティが保証される訳ではなく、単に混乱を拡散するだけになることもあるのです。リリースボードや時代遅れのプロセスのように、足止めをするような外部的制約があるならば、それらを解決しない限り、アプリケーションを分解しても意味はないのです。・・microservices-seven-fail

問題は、アンチパターンに陥ったときに後戻りできないということです。それはマイクロサービスでは組織が関係してくるからです。

マイクロサービスは、小さな独立した複数のサービスでソフトウェアを構成する、ソフトウェア開発に対するアーキテクチャ的、組織的アプローチです。 各サービスは、正確に定義された API を通じてやり取りします。 これらのサービスは、小規模の自己完結できるチームが所有します。・・マイクロサービスの概要

私達はかつて大規模なマイクロサービスのシステムを構築したことがありました。

各サービスにおいて組織を構成しお互いにAPIのI/Fだけをみて開発します。
サービス同士の境界を設けることでお互いの内部実装を隠蔽できた結果、開発の並行度が高まり、開発生産性に大きく寄与したのを覚えています。(追記:実は、私達が経験した高い開発生産性は、マイクロサービスだったからではなく、スキーマ駆動開発だったからです。これは、マイクロサービスではなく、後述するモジュラモノリスにおいても適用可能な開発手法です。)

しかし、正しいモジュラリティでサービスを分割できていればよいのですが、分散モノリスに陥った場合は深刻な問題になります。ドメイン境界の設定は単純ではなく、適切な切り方を知るには深いドメイン知識が必要で、間違いに後から気づくことが多いという現実もあります。

また、組織は簡単には直せないため、間違いが発覚しても「後戻りできない」システムになってしまいます。これは、一時的な開発生産性よりも憂慮すべき問題です。なぜなら、「後戻りできない」システムでは結果的にトータルコストが大きくなるからです。

で、モノリスからマイクロサービスに切り出そうとしたときに、どう切ればいいかなんて誰も分からないじゃないですか。
この切り方で良いのかなんて分かんないけど、とりあえず切る。マイクロサービスと組織は相似形でやりたいから組織も同じように切る。でも、後で「この切り方は間違いだったね」なんて話もあるわけです。
すると、ソフトウエアは100歩譲って直せばいいですけど、組織は簡単には直せないじゃないですか。「チームAの一部をチームBに合流させよう」と言ったって、そこにはいろいろな調整が発生して、数週間、ときには数カ月を要することもあります。・・「マイクロサービスは銀の弾丸じゃない」

マイクロサービスの切り出し方については、Microservices分割大全などに詳しくまとめられていますが、そもそも後戻りできないリスクの高い設計をなぜするの?という疑問が残ります。

上記イベントでも、リファクタリングやリアーキテクチャリングをする際に「後戻りできること」は非常に重要であると強調されていました。安易にマイクロサービス化はせず、常に元に戻せるように進化させるべきであると。

アジリティを実現するのは継続的デリバリー

迅速な開発ができていないのはほとんどの場合モノリスが原因ではありません。実は継続的デリバリーと継続的デプロイの問題です。

・・リリースボードが年2回だということは、リリースのケイデンスは6か月ということになります。マイクロサービスが個別にデプロイ可能な状態になったとしても、それは変わりません。これではアジリティの実現は不可能です。
この銀行に本当に必要なのは、技術的な支援ではありません。リスクに関する考え方、運用の方法を変える必要があります。リリース計画にも完全にオーバーホールしなくてはなりませんし、自動化の必要な部分もたくさんあります。足枷となっているのはCOBOLではなく、継続的デリバリの理念が欠如していることなのです。・・microservices-seven-fail

マイクロサービスベースのアーキテクチャでは、継続的デリバリーのソフトウェア開発プロセスに向いている(wikipedia)といわれることもありますが、実はモノリスの方が楽です。なぜなら、複数のサービスにわたる大規模なリファクタリングは面倒で、依存するすべてのサービスにおける変更やデプロイの調整が必要になるからです。
また、マイクロサービスからモノリスに「戻す」話で、サービスの継続的改善ができるようになった事例もあります。

一見するとなんの変哲もないように見える変更によって、無関係のテストが連鎖的に失敗する可能性がありました。サービス間の通信はネットワークを介することになります。よって、呼び出しが行われるたびに遅延は増加し、信頼性は低下します。
また、デプロイメントラインが複数になるということは 複数のWebサービスを開発・運用するということになります。このコストは無視できません。・・Shopifyにおけるモジュラモノリスへの移行

また、マイクロサービス、モジュラモノリスのいずれにしても、継続的なドメイン設計・コンテキスト境界の探求は継続的デリバリーにおいて非常に重要です。全く違うドメイン・コンテキストが制限もなく開発が続けられれば、いずれ密結合になってしまい、継続的デリバリーを困難にしてしまうでしょう。

モジュラモノリスによる解決策

モノリスでは局所的にリリースできないのかというとそうではありません。マイクロサービスにしなくてもモジュラモノリスで解決できるからです。モジュラモノリスは、意識的・強制的にコンテキスト分割を可能にする設計を実現します。また、モジュラモノリスでわけたコンテキストをマイクロサービス化することは比較的簡単です。

モジュラモノリス

  • デプロイ単位が一つのアプリケーション
  • ある機能(コンテキスト)単位でモジュールの境界を分ける
  • 越境するにはお互いの公開APIを介する(ことで依存関係を明らかにする)

モジュラモノリスという言葉は最近定着してきましたが、私はかつて、モノリスの「非干渉化したモジュール群」という言い方をしてきました。こちらに詳しく書いています。

また、Shopifyのモジュラモノリスアーキテクチャに移行した話が有名です。

最終的に、私たちは Shopify をモジュラモノリスアーキテクチャに移行することを選択しました。それは、すべてのコードを1つのコードベースに保持しつつ、異なるコンポーネント間の境界が定義され、尊重されるようにすることを意味します。
また、インフラストラクチャの追加は障害点を増やすことにつながり、アプリケーションのレジリエンシーとセキュリティを低下させます。
複数に分割されたサービスではなくモノリシックアーキテクチャを選択する最も魅力的な利点の1つは、Web APIを介した通信を必要とせずに他のコンポーネントを直接呼び出すことができるということです。これは、APIのバージョン管理や下位互換性、遅延の可能性のある呼び出しについて心配する必要がないことを意味します。・・Shopifyにおけるモジュラモノリスへの移行

つまり、モジュラモノリスにすることで、アジリティを実現できかつパフォーマンスの優れたシステムを構築できるようになるということです。

また、小さくトライでき、駄目だとわかればもとに戻すことが可能で、将来的にマイクロサービス化もしやすいというメリットもあります。

分散化とスケーラビリティ

モノリスを分解してマイクロサービス化する理由は、デプロイ独立性以外にもスケーラビリティを高めたいというのもあるのだろうと思います。柔軟性をもたせたいだけならモジュラーモノリスにすべきですが、さらにスケーラビリティを高めたいなら分散化するのも一つの手です。

また、最近ではインフラアーキテクチャにKubernetes(k8s)を使うケースも増えてきました。k8sを使うことで分散システムを構築する手間は圧倒的に楽になります。システムの冗長性の確保や負荷分散の自動化、オートスケール、コンテナのデプロイや、そのイメージ管理を自前で仕組みを構築することなく、自動化することができるからです。他にも、セキュリティ、可用性、監視、バックアップなども面倒みてくれます。

k8sとクラウドネーティブアプリケーションの普及がマイクロサービス流行の土台となっていることは否めません。分散環境が容易に構築できるようになったおかげで、マイクロサービスが目的化される原因となっている面もあります。特にITベンダーは、k8s基盤を売りたいのが本音で、売り文句にマイクロサービスを使っているだけなのかもしれません。

しかし、マイクロサービスはアプリケーションアーキテクチャとインフラアーキテクチャが複雑に絡み合ったシステムで技術的難易度が非常に高いことが問題です。適切に構築できなければ分散モノリスアンチパターンに陥ることは必然でしょう。
アンチパターンに陥らないためには、インフラアーキテクチャをアプリケーションから隠蔽すると同時に、アーキテクチャ自体がアプリケーションに依存せずに自由に変化できる必要があります。

インフラアーキテクチャをアプリケーションから隠蔽する技術として、Istioなどのサービスメッシュなどが有名ですが、これは、主にネットワークに由来するこれらの困難さをアプリケーションから隠蔽するためにプロキシなどの手法が用いられるもので、アプリケーション開発者がインフラアーキテクチャを完全に意識しなくてもいいかというとそうではありません。逆にインフラ技術者に負担を与える一因となっている面もあります。

そこで、私達が取ったアプローチがBaaS(vte.cx)です。下図のようにk8sの分散システムであるにもかかわらず、複数のアプリケーションがマルチテナントで独立して動作するプラットフォームになっています。

BaaSではインフラを意識せずに論理的にサービスを追加でき自由にAPIを作ることができます。この仕組であれば、アプリケーションのドメインをどう切り出すかなど悩む必要はありません。そもそもコンテキスト境界がサービスの単位にならないからです。

また、アプリ開発者にインフラのことを完全に隠蔽しつつも、裏では、システムの冗長性の確保や負荷分散の自動化、オートスケールが実行されています。これこそが、アーキテクチャ自体がアプリケーションに依存せずに自由に変化できる仕組みなのです。

私はマイクロサービス黎明期においては肯定的な意見をいっていましたが、その弊害がわかってくるにつれ、ココココなどで否定的な記事を書いてきました。今ははっきりいえます。マイクロサービスはアンチパターンであると。

まとめ

  • 新規システムでマイクロサービスを作るべきではない。既存システムをリスペクトしてリファクタリングかモジュラモノリスで開発すべき
  • アジリティを高めたいならマイクロサービスではなくCI/CD基盤を拡充させるべき
  • スケーラビリティを高めたいなら分散化もありだがインフラの複雑さをアプリから隠蔽すべき
  • マイクロサービスはアンチパターン。

--

--

顔がアイコンに似てますねといわれると喜びます。/ (株)コントラーズCTO/(有)バーチャルテクノロジー代表 / フロントエンドが主役のシステム開発を実現するバックエンドサービス vte.cxを作っています