- マイクロサービスを本番環境で運用するには、サービス、データ、耐障害性、契約について慎重な設計が必要となる。
- Kubernetes/OpenShift、CI/CD、GitOpsは、大規模なデプロイメント、スケーリング、運用を自動化することを可能にします。
- ゼロトラストセキュリティ、堅牢な構成管理、そしてOpenTelemetryによる可観測性は、このプラットフォームの柱となる要素です。
- 製品チームの組織体制と分散型ガバナンスは、選定する技術と同じくらい重要です。

実環境でマイクロサービスアーキテクチャを採用するということは、単にモノリシックなシステムを小さな部分に分割するだけではありません。インフラストラクチャ、チーム、プロセス、データ、セキュリティ、運用など、あらゆる側面を見直す必要があります。システムが理論段階から本番環境へと移行する際には、サービスディスカバリ、チーム間の契約、CI/CD、可観測性、回復力、スケーラビリティといった問題が生じます。これらの問題に適切に対処しなければ、マイクロサービスは分散型カオスへと陥ってしまう可能性があります。
幸いなことに、現在ではNetflix、Amazon、Googleをはじめとする、数百ものマイクロサービスを本番環境で運用している大企業などから、豊富な経験が蓄積されています。これらの経験と、KubernetesやOpenShiftを用いたエンタープライズ環境におけるベストプラクティスを基盤とすることで、制御を失うことなく、マイクロサービスを大規模に設計、デプロイ、運用するための非常に堅牢なアプローチを開発できます。
マイクロサービスを本番環境にデプロイする理由(そして、デプロイする価値がない場合)
適切に設計されたマイクロサービスアーキテクチャでは、エンドツーエンドのサービス全体を担う、小規模で自律的なクロスファンクショナルチームと連携できます。各チームは明確に定義されたコンテキスト内で活動し、頻繁にデプロイを行い、サービスに対する全責任を負うことで、開発サイクル時間を短縮し、新機能の提供を加速できます。
もう一つの重要な利点は、サービスごとに独立したスケーリングが可能なことです。カタログ、チェックアウト、または公開APIのみにトラフィックの急増が見られる場合、アプリケーション全体を過剰に拡張する必要はありません。各マイクロサービスは、負荷パターンに応じて水平方向または垂直方向に調整でき、各機能のコストを正確に測定し、特定の領域で消費が急増した場合でも可用性を維持できます。
これらのサービスがパッケージ化され、デプロイされる方法により、継続的かつ低リスクな実装が容易になります。各マイクロサービスを個別にリリースすることで、新しいアイデアのテストや問題のあるバージョンのロールバックがはるかに簡単になります。カナリアデプロイメント、ブルー/グリーンロールバック、自動ロールバックにより、障害発生時のコストが削減され、実験の余地が生まれます。
技術的な観点から見ると、マイクロサービスは各サービスごとに言語、フレームワーク、データベースを自由に選択できるという利点があります。すべてのニーズが同じ技術スタックに収まるわけではありません。例えば、ビジネスサービスは.NETやJava、データ処理はScala/Spark、特殊サービスはPythonやF#、AIマイクロサービスはRといったように、用途に応じて使い分けることができます。このように多様性を意識的に活用することで、アプリケーション全体をグローバルな技術変革に追い込むことなく、それぞれのケースに最適なツールを選択できるのです。
さらに、システムを小さく明確に定義された部分に分割することで、機能を構成要素として再利用しやすくなります。より大きな機能の一部として最初に作成されたマイクロサービスは、ロジックを書き直すことなく、システムの他の部分の依存関係として後で再利用できます。また、サービスが分離されているため、耐障害性が最初から設計に組み込まれていれば、いずれかのサービスに障害が発生しても、通常はシステム全体の停止ではなく、部分的な機能低下で済みます。
建築設計および設備設計

マイクロサービスが本番環境でうまく機能するためには、サービス境界と責任範囲を慎重に設計することから始めることが不可欠です。実際には、これは通常、既存のモノリス内の粗粒度サービス、つまり既に論理的に分離されている大規模な機能領域やビジネスドメイン(注文、カタログ、ユーザー、請求など)を特定することから始まります。
これらの大きな構成要素から出発し、設計を洗練させて、一貫性のあるデータセット上で動作し、独自のモデルを持ち、他のサービスから読み書きする必要のある内容を正確に把握する、きめ細かなマイクロサービスを作成するプロセスです。このプロセスは通常、ドメイン駆動設計(DDD)の概念と境界コンテキストに依存しており、マイクロサービスが「ミニモノリス」になるのを防ぎます。
これらのサービスを提供するAPIは、明確に定義された安定した契約を持つ必要があります。そのためには、厳密なドキュメント作成(OpenAPIを使用したREST、.protoファイルを使用したgRPCなど)、明示的なバージョン管理、可能な限り後方互換性の維持、そして本番環境に影響を与える前に破壊的な変更を検出するための契約検証の自動化が不可欠です。
数十または数百ものサービスが存在する環境では、設計段階から耐障害性パターンを組み込むことが不可欠です。これにより、システムが部分的な障害にも対応できるようになります。サーキットブレーカー、バックオフ付きリトライ、明確に定義されたタイムアウト、バルクヘッド、バックプレッシャーなどのパターンは、1つのサービスの障害が他のサービスに影響を及ぼすのを防ぐのに役立ちます。ChaosMonkeyやGremlinなどのカオスエンジニアリングツールは、シミュレーションされた障害発生時にプラットフォームがどのように動作するかを実際にテストするのに役立ちます。
多くの複雑なシステムは、比較的シンプルなCRUDサービスと、変化するビジネスルールに対応するより高度なサービスを組み合わせて構成されています。すべてのマイクロサービスが複雑な内部アーキテクチャを必要とするわけではありません。基本的なデータアクセスを備えたシンプルなHTTPコントローラーで済むものもあれば、注文サービスや請求サービスのように、より高度なパターン(DDD、CQRS、ドメインイベントなど)を活用できるものもあります。
運用インフラ:クラウド、コンテナ、Kubernetes/OpenShift
実際の運用経験から、マイクロサービスは、独立した仮想マシン上よりも、コンテナとオーケストレーションを備えたクラウドインフラストラクチャ上にデプロイした方がはるかに優れたパフォーマンスを発揮することがわかっています。KubernetesやOpenShiftといったプラットフォームは、サービスをコンテナとしてパッケージ化し、スケーリング、アップデート、負荷分散、高可用性の管理を行うために必要な基本機能を提供します。
通常、各マイクロサービスは、インフラストラクチャチームが管理する企業共通のベースイメージ(例えば、Javaサービスの場合はOpenJDK 21)を基にしたコンテナイメージにパッケージ化されます。このベースイメージはセキュリティパッチで常に最新の状態に保たれており、新しいバージョンがリリースされると、開発チームは対応する環境でサービスを再構築および再デプロイする責任を負います。
Kubernetes/OpenShiftでは、基本的なデプロイメント単位はPodであり、Podは1つ以上のコンテナをカプセル化します。通常、マイクロサービスはPodタイプに対応し、Deployment(ステートレスサービスの場合)やStatefulSet(関連付けられた状態がある場合)などのリソースを使用してデプロイされます。テスト環境、プレプロダクション環境、およびプロダクション環境が、それぞれの重要度に応じた可用性レベルを確保できるよう、環境ごとに最小レプリカ数が最初から定義されています。
自動スケーリングは、 HorizontalPodAutoscaler(HPA)を使用して実装されます。HPAは、CPU、メモリ、その他のカスタムメトリックなどのメトリックに基づいてレプリカの数を調整します。また、プラットフォームは、同じサービスのレプリカを異なるノードに分散させるためのPodアンチアフィニティルールを設定する必要があります。これにより、単一ノードの障害によってすべてのインスタンスが停止するのを防ぎます。
垂直方向のサイズ設定に関しては、resources.requestsとresources.limitsを使用して、Podが消費できるCPUとメモリの範囲を定義します。たとえば、Javaサービスに対して、CPUを最低100MB、メモリを256MB確保し、それぞれ最大500MBと2GBまで許可し、JVM(Xms、Xmx、Xss)を調整してコンテナのリソースを有効活用します。
状態管理:ステートレスマイクロサービスとステートフルマイクロサービス
ほとんどのビジネス向けマイクロサービスは、ステートレスサービスとして設計されています。つまり、Podは再起動後も保持する必要のある情報を保存せず、状態は外部データベース、メッセージキュー、またはその他のストレージに永続化されます。このアプローチにより、どのレプリカでもあらゆるリクエストを処理できるため、動的な水平スケーリングとスムーズなデプロイメントが可能になります。
しかし、ステートフルなマイクロサービスを永続ボリュームでサポートする以外に選択肢がないシナリオも存在します。これは、一部のデータベース、分散ファイルシステム、またはローカルデータの維持を必要とするコンポーネントに当てはまります。これらのポッドは通常、StatefulSetsを使用してデプロイされ、PersistentVolumeClaimsを使用してPersistentVolumesにリンクされ、水平方向ではなく垂直方向にスケーリングされます。
マイクロサービスが永続ストレージを必要とする場合、サイズ、アクセスモード、および使用目的を指定したPersistentVolumeClaim(PVC)が要求され、運用チームがプラットフォームポリシーに従ってそれをプロビジョニングします。このPVCはデプロイメントマニフェストで参照され、Podにマウントされるため、サービスはデータを永続的に読み書きできるようになります。
特定のケースではステートフルモデルが必要になる場合もありますが、一般的には、可能な限り多くのサービスをステートレスに保つことが推奨されます。これにより、デプロイ、スケーリング、耐障害性、災害復旧が簡素化され、マイクロサービスが多数存在する環境における運用上の複雑さが軽減されます。
データ分散化とサービス主権
従来のインフラストラクチャでは、効率を最大化するためにデータベースとストレージを集中管理するのが一般的です。しかし、マイクロサービスでは、このアプローチはチームの自律性と疎結合性に反します。多くのサービスが同じリレーショナルスキーマを共有している場合、構造的な変更によって複数のチームが影響を受け、意図せず互換性が損なわれる可能性があります。
したがって、推奨される方法は、各マイクロサービスが独自のデータモデルとデータベースを所有することです。ただし、開発環境では、デプロイメントを簡素化するために、そのデータベースはクラスタ内のコンテナとして実行されます。本番環境では、通常、クラウド管理インスタンスまたはその他の高可用性データベースサーバーが使用され、常に明確な所有権の境界が維持されます。
これはデータ統合がないという意味ではなく、サービス間の整合性をイベントと非同期メッセージングで管理し、妥当な場合には結果整合性を受け入れるという意味です。マイクロサービス間で状態変化を伝播するためにイベントバス(RabbitMQ、Azure Service Bus、Kafkaなど)を使用するのが一般的で、これにより単一のデータベースへの強い依存を軽減できます。
クラウドプラットフォームを利用することで、チームは単一のテクノロジーに縛られることなく、各サービスに最適なデータベースタイプ(リレーショナル、ドキュメント、キーバリュー、時系列など)を容易に選択できます。重要なのは、他のサービスとの契約を損なうことなくスキーマや構造を移行できる可能性を考慮した設計になっていること、そして各マイクロサービスのドメイン境界に沿ってデータに関する決定が行われていることです。
分散型ガバナンス、チーム、組織
組織構造を変えずにマイクロサービスに移行することは、トラブルを招く恐れがあります。ネットワーク、システム、データベース、開発、運用といった従来の機能別組織ではなく、製品チームを基盤とした構造が推奨されます。これにより、開発、品質保証、DevOps、そして必要に応じてビジネスアナリストやデータアナリストといった人材を結集させることができます。
各チームは、同一の機能ドメイン内の1つ以上のマイクロサービスを担当し、開発と運用(構築と運用)の両方を担います。つまり、チームはCI/CDパイプラインを管理し、特定のニーズに応じてインフラストラクチャと連携し、監視とインシデント対応に参加します。インフラストラクチャとクラウドプラットフォームは、共通かつ標準化されたサービスの提供に重点を置いています。
この分散型ガバナンスが無秩序状態に陥るのを防ぐためには、軽量な標準規格と共有カタログを定義することが不可欠です。具体的には、承認済みのベースイメージ、デプロイメントパターン、名前空間とサービスの命名規則、APIガイドライン、DockerfileおよびKustomizeテンプレートなどです。これらのガイドラインは、チームの意思決定能力を阻害することなく、方向性を示す「ガードレール」として機能します。
多くの企業環境では、プロジェクトやドメインごとに個別の名前空間が使用され、環境(開発、プレプロダクション、プロダクション)ごとに少なくとも1つの名前空間が設けられます。大規模なプロジェクトでは、内部通信が適切に構成され、セキュリティ規則が遵守されている限り、マイクロサービスを複数の名前空間に分散させることができます。
CI/CD、自動化、そしてGitOpsモデル
アーキテクチャが数十、数百ものマイクロサービスで構成されている場合、それらを安定稼働させる唯一の方法は、エンドツーエンドの自動化に多大な投資を行うことです。これには、一貫性のあるCI/CDパイプライン、宣言的なデプロイメント定義、自動テスト、および自動ロールバックメカニズムが含まれます。
一般的な継続的インテグレーションおよびデリバリーのパイプラインでは、コードのコンパイル、テストの実行、SonarQubeなどのツールを使用した品質分析、企業Dockerfileからのコンテナイメージの構築、デプロイメントマニフェストの更新などが行われます。その後、ArgoCDなどのシステムがGitOpsの手法を用いて変更内容をクラスタに適用します。
各マイクロサービスリポジトリには通常、標準化されたDockerfile、パイプライン構成ファイル(例:ci.json)、品質分析用のプロパティ、および環境ごとに分けられたKubernetes定義(KustomizeまたはHelm)を含むデプロイメントディレクトリが含まれています。リポジトリのWebhookは、タグのプッシュやマージリクエストなどのイベントが発生すると、パイプラインをトリガーします。
GitOpsパターンでは、インフラストラクチャとデプロイメントに関する真の情報源としてGitリポジトリが確立されます。デプロイメント、サービス、ConfigMap、PVC、SealedSecrets、その他のリソースのマニフェストはそこでバージョン管理され、特定のツールがクラスタの状態をGitで定義された内容と同期させます。これにより、トレーサビリティ、プルリクエストのレビュー、および容易なロールバック機能が実現されます。
設定、秘密情報、セキュリティ
成熟したマイクロサービスプラットフォームでは、構成管理は機密性の低いパラメータにはConfigMapを、機密情報にはSecretを使用します。各マイクロサービスは通常、環境固有のConfigMapを持ち、依存サービスのURL、機能フラグ、チューニングパラメータなどのプロパティを格納します。
機密情報(認証情報、鍵、トークン、証明書)は、厳格なセキュリティポリシーに基づいて取り扱われます。重要度の低い環境では、開発チームが管理する平文で保管しても問題ない場合もありますが、プレプロダクション環境および本番環境では、Sealed Secretsなどのツールや、特定のクラウドベースの外部管理ツールを使用して暗号化することをお勧めします。
複数のサービス間で秘密情報を共有する必要がある場合(例えば、OTEL Collectorの認証情報や共通のキーストアなど)、名前空間ごとに構成リポジトリに一元管理することができます。その名前空間を共有するプロジェクトは連携して必要に応じてリポジトリを更新し、これらのリソースの読み取りや変更権限を管理します。
通信セキュリティに関して言えば、主流のパターンはゼロトラストです。つまり、トラフィックが「内部」だからといって、何も当然のこととはみなされません。内部サービスと外部サービス間のすべての呼び出しは、認証と認可が必要であり、理想的にはmTLS、JWTトークン、またはその他の同等のメカニズムを使用します。マイクロサービスは、セキュリティをAPIマネージャーやネットワークに盲目的に委ねるのではなく、独自のチェックも実行します。
マイクロサービス、API、メッセージング間の通信
成熟したマイクロサービスアーキテクチャでは、通信レイヤーは複数のケースに分かれています。クライアント(ブラウザ、モバイルアプリ、サードパーティ)からバックエンドへのトラフィックには、APIマネージャによって管理される公開APIが使用されます。これらのAPIは通常RESTful(多くの場合OpenAPIを使用)ですが、場合によってはゲートウェイを介して公開されるgRPCもあります。
同一ネームスペース内、あるいは同一プロジェクト内の複数のネームスペース間におけるマイクロサービス間の呼び出しは、通常、内部DNSを備えたKubernetes内部サービスによって処理されます。これらの呼び出しはパブリックAPIマネージャをバイパスしますが、セキュリティ、認証、および認可ポリシーに準拠します。このようなシナリオでは、共通ポリシーを適用するサービスメッシュまたは内部ゲートウェイを使用できます。
マイクロサービスが異なる機能ドメインやプロジェクトに属する場合、組織レベルでは通信は「公開」とみなされます。このような場合、契約、割り当て、セキュリティ、バージョン管理、監査などを管理し、独立したクラスタや名前空間間の直接的な結合を防ぐために、APIマネージャや相互運用バスを使用するのが一般的です。
レガシーシステムや外部システムとの統合に関しては、これらのシステムが必ずしも最新のAPIを公開しているとは限らないため、相互運用バス上の特定のコネクタを利用するのが一般的です。この方法により、マイクロサービスは共通の言語(例えば、イベントや内部REST API)で通信し、コネクタがレガシーシステムとの間でデータの変換を処理します。この際、セキュリティは常に強化されます。
同期通信に加えて、非同期メッセージングも重要な役割を果たします。これは、プロセスの分離、負荷の急増への対応、サービス間のビジネスイベントの伝播、および耐障害性の向上に利用されます。各イベントは通常、明確に定義されバージョン管理されたスキームを持ち、プロデューサーとコンシューマー間の障害発生を防ぐための追跡メカニズムを備えています。
可観測性、OTELコレクター、および運用
多数のマイクロサービスで構成されるシステムでは、十分な可観測性なしに問題を診断することはほぼ不可能です。そのため、設計段階からメトリクス、集中ログ、分散トレースを統合し、サービスレベルとプラットフォームレベルの両方で何が起こっているかを把握できるようにしています。
このスキームの中核となるコンポーネントは、OpenTelemetry Collector(OTEL Collector)です。これは、ネームスペース内または中央にデプロイされ、すべてのコンポーネントからメトリクス、ログ、トレースを収集します。マイクロサービスは、テレメトリをCollectorに送信する必要があることだけを知っていればよく、Collectorは、サービスが詳細を知る必要なく、それをオブザーバビリティシステム(Prometheus、Grafana、Jaeger、Elasticなど)に転送します。
インフラストラクチャ層では、ノードレベルのコレクターとエクスポーターを使用して、Pod から CPU、メモリ、ディスク、ネットワーク、ログのメトリックを収集し、それぞれ Prometheus と Elasticsearch に送信します。Grafana や Kibana などのツールを使用して、これらの情報を視覚化し、ダッシュボードを作成し、スマートなしきい値と関連するランブックを使用してアラートを定義します。
プロジェクトがメトリクスやトレースに対して非常に特殊な処理を必要とする場合、運用上の承認を得ており、かつ本番環境の保守モデルが明確であれば、プロジェクトは独自のネームスペースにOTEL Collectorのインスタンスをデプロイすることができます。
テスト戦略、契約、および現地開発の経験
分散型マイクロサービスアーキテクチャのテストには、モノリス型アーキテクチャのテストよりも高度なテスト戦略が必要です。単体テストは依然として不可欠ですが、契約テスト(APIとイベント)、サービス間の統合テスト、および完全なフローを網羅するエンドツーエンドテストの重要性がますます高まっています。
互換性の問題を回避するために、顧客がAPIの要件を定義し、サービスプロバイダーがそれを満たす、顧客指向の契約テストなどの手法が用いられます。すべての契約変更はCIパイプライン内で自動テストを受け、既知の顧客に影響を与えるようなデプロイメントを防ぎます。
サービスの数が100を超えると、システム全体をローカルで複製することは非現実的になります。そのため、開発では依存サービスのシミュレーションやリモート環境へのトンネリングが用いられます。開発者は通常、マイクロサービスのごく一部のみを起動し、残りはモック、フェイク、シミュレーターでスタブ化するか、特定の呼び出しを共有統合環境にリダイレクトします。
エンドツーエンドテストでは、機能ブランチから作成される一時的な環境、いわゆる「プレビュー」への依存度が高まっています。プレビューは、その機能に関連するサービスのみを備えた隔離された環境を構築します。これにより、チーム間の摩擦を最小限に抑え、「自分のマシンでは動作する」という誤解を解消し、本番環境のようなコストのかかる環境に移行する前に統合上の問題を検出できます。
マイクロサービスの運用環境における導入パターン
Kubernetes以外にも、本番環境で使用されているマイクロサービスのデプロイメントパターンはいくつかあり、それぞれ分離性、コスト、成熟度といった異なるシナリオに対応しているため、知っておく価値があります。最も古いパターンの1つは、ホストごとに複数のサービスインスタンスを実行するもので、単一の物理ホストまたは仮想ホスト上で、通常は共有アプリケーションサーバー上で、異なるサービスの複数のインスタンスが実行されます。
VMごとのサービスインスタンスパターンでは、各サービスがVMイメージ(例えばEC2 AMI)としてパッケージ化され、それぞれ専用のインスタンス上で実行されます。この方式は、リソース消費量の増加と起動時間の遅延というデメリットはあるものの、強力な分離性を実現します。Packerなどのツールやクラウドプロバイダー固有のソリューションを利用すれば、本番環境に対応したVMイメージを簡単に生成できます。
現在最も普及しているパターンは、コンテナごとのサービスインスタンスです。これは、各マイクロサービスをコンテナイメージとして構築し、オーケストレーター(Kubernetes、OpenShiftなど)にデプロイするものです。コンテナは仮想マシンよりも軽量で、起動が非常に速く、サービスに必要なすべてをパッケージ化できるため、デプロイが簡素化され、自動スケーリングが可能になります。
最後に、AWS Lambdaなどのサーバーレスアプローチが人気を集めています。これらのサービスは、HTTPリクエストや他のサービス(S3、DynamoDB、キューなど)からのイベントに応答する関数をパッケージ化し、ユーザーは使用した分だけ料金を支払います。このパターンは、非常に小規模なマイクロサービスや短時間で終了するイベント駆動型タスクに特に適していますが、可観測性、コールドスタート、実行制限などに関する追加の考慮事項が生じます。
実際には、多くの組織はハイブリッドなエコシステムを採用することになります。システムのコア部分はコンテナとオーケストレーター上で動作し、補助的なコンポーネントはサーバーレス関数または専用の仮想マシンとして実装されます。いずれの場合も、明確なインターフェースと明確に定義されたプロトコルによって、システム全体に統合されます。
これらすべてを本番環境に導入する際に重要なのは、選択したテクノロジーだけでなく、障害耐性があり、必要に応じて拡張でき、自動的にデプロイされ、監視可能なアーキテクチャを構築することです。製品と連携したチーム、適切に管理された契約、分散型データ、そして堅牢なクラウドプラットフォームがあれば、マイクロサービスは単なる構想から、複雑なアプリケーションを長年にわたって進化させるための効果的かつ持続可能な方法へと変わります。