編集部 – 全7回の記事シリーズが完結しました:
- マイクロサービス入門
- マイクロサービスの構築:API Gatewayの使用
- マイクロサービスの構築:マイクロサービスアーキテクチャにおけるプロセス間通信
- マイクロサービスアーキテクチャにおけるサービスディスカバリ
- マイクロサービス向けイベント駆動型データ管理
- マイクロサービスのデプロイ戦略の選択(本記事)
- モノリスからマイクロサービスへのリファクタリング
一連の記事全文と、NGINX Plusを使用したマイクロサービスの実装に関する情報をまとめた電子書籍「マイクロサービス:設計からデプロイまで」もダウンロードできます。また、「マイクロサービスリファレンスアーキテクチャ」シリーズと「マイクロサービスソリューションのページ」もご覧ください。
本記事は、マイクロサービスを使用したアプリケーション構築について解説するシリーズの第6回です。第1回の記事では、マイクロサービスアーキテクチャパターンを紹介し、マイクロサービスを使用するメリットとデメリットについて説明しています。以降の記事では、マイクロサービスアーキテクチャのさまざまな側面として、API Gatewayの使用、プロセス間通信、サービスディスカバリ、およびイベント駆動型データ管理について説明しています。本記事では、マイクロサービスのデプロイ戦略について解説します。
背景
Deploying a モノリシックアプリケーションをデプロイする場合、通常は大規模な単一アプリケーションの同一コピーを複数実行します。一般的には、N台のサーバー(物理または仮想)をプロビジョニングし、各サーバーでアプリケーションのインスタンスをM個実行します。モノリシックアプリケーションのデプロイは必ずしも単純ではありませんが、マイクロサービスアプリケーションのデプロイよりはるかに簡単です。
A マイクロサービスアプリケーションは、数十、場合によっては数百ものサービスで構成されます。サービスは、さまざまな言語やフレームワークで記述されます。各サービスは小規模なアプリケーションであり、デプロイ、リソース、スケーリング、監視について、それぞれ固有の要件があります。たとえば、各サービスの需要に応じて、一定数のインスタンスを実行する必要があります。また、各サービスインスタンスには、適切なCPU、メモリ、I/Oリソースを割り当てる必要があります。さらに難しいのは、こうした複雑さがあるにもかかわらず、サービスを迅速かつ確実に、費用対効果の高い方法でデプロイしなければならない点です。
マイクロサービスのデプロイパターンには、いくつかの種類があります。まずは、ホストごとに複数のサービスインスタンスを配置するパターンを見ていきましょう。
ホストごとに複数のサービスインスタンスを配置するパターン
マイクロサービスをデプロイする方法の1つは、「ホストごとに複数のサービスインスタンス」パターンを使用することです。このパターンでは、1台以上の物理ホストまたは仮想ホストをプロビジョニングし、各ホストで複数のサービスインスタンスを実行します。多くの点で、これは従来型のアプリケーションデプロイ手法です。各サービスインスタンスは、1台以上のホスト上にある既知のポートで実行されます。通常、ホストマシンはペットのように扱われます。
次の図は、このパターンの構造を示しています。

このパターンにはいくつかのバリエーションがあります。その1つは、各サービスインスタンスをプロセスまたはプロセスグループとして実行するものです。たとえば、JavaサービスインスタンスをWebアプリケーションとしてApache Tomcatサーバーにデプロイできます。Node.jsサービスインスタンスは、1つの親プロセスと1つ以上の子プロセスで構成される場合があります。
このパターンのもう1つのバリエーションは、同じプロセスまたはプロセスグループで複数のサービスインスタンスを実行する方法です。たとえば、同じApache Tomcatサーバーに複数のJava Webアプリケーションをデプロイしたり、同じOSGIコンテナで複数のOSGIバンドルを実行したりできます。
ホストごとに複数のサービスインスタンスを配置するパターンには、利点と欠点の両方があります。主な利点の1つは、リソースを比較的効率よく使用できることです。複数のサービスインスタンスが、サーバーとそのオペレーティングシステムを共有します。1つのプロセスまたはプロセスグループで複数のサービスインスタンスを実行する場合は、さらに効率が向上します。たとえば、複数のWebアプリケーションで同じApache TomcatサーバーとJVMを共有する場合です。
このパターンには、サービスインスタンスを比較的短時間でデプロイできるという利点もあります。サービスをホストにコピーして起動するだけです。サービスがJavaで記述されている場合は、JARファイルまたはWARファイルをコピーします。Node.jsやRubyなど、その他の言語の場合は、ソースコードをコピーします。いずれの場合も、ネットワーク経由でコピーされるデータ量は比較的少なくなります。
また、オーバーヘッドがないため、通常はサービスを非常に短時間で起動できます。サービスが独立したプロセスである場合は、そのプロセスを起動するだけです。それ以外の場合、つまり同じコンテナプロセスまたはプロセスグループ内で実行される複数のインスタンスの1つである場合は、コンテナに動的にデプロイするか、コンテナを再起動します。
魅力的ではあるものの、Multiple Service Instances per Hostパターンには重大な欠点がいくつかあります。主な欠点の1つは、各サービスインスタンスが個別のプロセスでない限り、サービスインスタンス間の分離がほとんど、またはまったくないことです。各サービスインスタンスのリソース使用状況は正確に監視できますが、各インスタンスが使用するリソースを制限することはできません。正常に動作していないサービスインスタンスが、ホストのメモリやCPUをすべて消費する可能性があります。
複数のサービスインスタンスが同じプロセスで実行される場合、インスタンス間はまったく分離されません。たとえば、すべてのインスタンスが同じJVMヒープを共有する可能性があります。正常に動作しないサービスインスタンスによって、同じプロセスで実行されている他のサービスが容易に停止するおそれがあります。さらに、各サービスインスタンスが使用するリソースを監視する方法もありません。
このアプローチには、サービスをデプロイする運用チームが、その具体的な手順を把握しておく必要があるという、もう1つの重大な問題があります。サービスはさまざまな言語やフレームワークで記述できるため、開発チームは多くの詳細情報を運用チームと共有しなければなりません。この複雑さにより、デプロイ時にエラーが発生するリスクが高まります。
このように、Multiple Service Instances per Hostパターンはよく知られているものの、いくつかの重大な欠点があります。次に、こうした問題を回避できる、マイクロサービスの別のデプロイ方法を見ていきましょう。
ホストごとのサービスインスタンスパターン
マイクロサービスをデプロイするもう1つの方法は、ホストごとのサービスインスタンスパターンです。このパターンでは、各サービスインスタンスをそれぞれ専用のホスト上で分離して実行します。このパターンには、仮想マシンごとのサービスインスタンスとコンテナごとのサービスインスタンスという2つの異なる特殊化があります。
仮想マシンごとのサービスインスタンスパターン
When you use 仮想マシンごとのサービスインスタンスパターンを使用する場合、各サービスをAmazon EC2 AMIなどの仮想マシン(VM)イメージとしてパッケージ化します。各サービスインスタンスは、そのVMイメージを使用して起動されるVM(EC2インスタンスなど)です。次の図は、このパターンの構造を示しています。

これは、Netflixが動画ストリーミングサービスのデプロイに使用している主要なアプローチです。Netflixは、各サービスをAminatorを使用してEC2 AMIとしてパッケージ化します。実行中の各サービスインスタンスはEC2インスタンスです。
独自のVMを構築するために、さまざまなツールを利用できます。継続的インテグレーション(CI)サーバー(例:Jenkins)からAminatorを呼び出し、サービスをEC2 AMIとしてパッケージ化するように設定できます。Packer.ioも、VMイメージを自動作成するための選択肢の1つです。Aminatorとは異なり、EC2、DigitalOcean、VirtualBox、VMwareなど、さまざまな仮想化テクノロジーに対応しています。
企業のBoxfuseは、以下で説明するVMの欠点を克服する、優れたVMイメージ構築方法を提供しています。Boxfuseは、Javaアプリケーションを最小構成のVMイメージとしてパッケージ化します。これらのイメージは短時間で構築でき、迅速に起動します。また、外部に公開される攻撃対象領域が限定されるため、セキュリティも向上します。
企業の「CloudNative」は、EC2 AMIを作成するためのSaaSサービス「Bakery」を提供しています。CIサーバーを設定し、マイクロサービスのテストに合格した後にBakeryを呼び出すことができます。その後、BakeryがサービスをAMIとしてパッケージ化します。BakeryのようなSaaSサービスを利用すれば、AMI作成インフラストラクチャのセットアップに貴重な時間を費やす必要がありません。
Service Instance per Virtual Machineパターンには、いくつかの利点があります。VMの大きな利点は、各サービスインスタンスが完全に分離された状態で実行されることです。各インスタンスには一定量のCPUとメモリが割り当てられ、他のサービスのリソースを奪うことはできません。
マイクロサービスをVMとしてデプロイするもう1つの利点は、成熟したクラウドインフラストラクチャを活用できることです。AWSなどのクラウドは、負荷分散や自動スケーリングといった便利な機能を提供しています。
サービスをVMとしてデプロイするもう1つの大きなメリットは、サービスの実装テクノロジーをカプセル化できることです。サービスをVMとしてパッケージ化すると、内部実装が隠蔽され、ブラックボックスになります。VMの管理APIが、サービスをデプロイするためのAPIとなります。これにより、デプロイが大幅に簡素化され、信頼性も向上します。
ただし、Service Instance per Virtual Machineパターンにはいくつかの欠点があります。その1つは、リソースの利用効率が低いことです。各サービスインスタンスには、オペレーティングシステムを含むVM全体のオーバーヘッドが伴います。さらに、一般的なパブリックIaaSではVMのサイズが固定されているため、VMのリソースが十分に活用されない可能性があります。
さらに、一般にパブリックIaaSでは、VMが稼働中かアイドル状態かにかかわらず料金が発生します。AWSなどのIaaSにはオートスケーリング機能がありますが、需要の変化に迅速に対応することは困難です。そのため、多くの場合はVMを過剰にプロビジョニングする必要があり、導入コストが増加します。
このアプローチには、サービスの新しいバージョンのデプロイに通常時間がかかるというデメリットもあります。VMイメージはサイズが大きいため、通常、ビルドに時間がかかります。また、VMもサイズが大きいため、通常、インスタンス化に時間がかかります。さらに、オペレーティングシステムの起動にも通常ある程度の時間がかかります。ただし、Boxfuseで構築されたVMのような軽量VMも存在するため、必ずしもすべてに当てはまるわけではありません。
Service Instance per Virtual Machineパターンには、通常、自社(または組織内の誰か)が、差別化につながらない多くの煩雑な作業を担わなければならないという欠点もあります。VMの構築と管理に伴う負担を処理するBoxfuseのようなツールを使用しない限り、これらの作業は自社で行う必要があります。こうした作業は必要ではあるものの時間がかかり、コアビジネスへの注力を妨げます。
ここでは、VMの多くの利点を維持しながら、より軽量にマイクロサービスをデプロイする別の方法を見ていきます。
コンテナごとのサービスインスタンスパターン
「コンテナごとのサービスインスタンス」パターンを使用すると、各サービスインスタンスはそれぞれ専用のコンテナで実行されます。コンテナは、オペレーティングシステムレベルの仮想化メカニズムです。コンテナは、サンドボックス内で実行される1つ以上のプロセスで構成されます。各プロセスから見ると、専用のポート名前空間とルートファイルシステムが存在します。コンテナが使用できるメモリとCPUリソースを制限できます。一部のコンテナ実装では、I/Oレートも制限できます。コンテナ技術の例としては、DockerやSolaris Zonesがあります。
次の図は、このパターンの構造を示しています。

このパターンを使用するには、サービスをコンテナイメージとしてパッケージ化します。コンテナイメージとは、サービスの実行に必要なアプリケーションとライブラリで構成されるファイルシステムイメージです。完全なLinuxルートファイルシステムで構成されるコンテナイメージもあれば、より軽量なものもあります。たとえばJavaサービスをデプロイする場合は、Javaランタイム、必要に応じてApache Tomcatサーバー、およびコンパイル済みのJavaアプリケーションを含むコンテナイメージを作成します。
サービスをコンテナイメージとしてパッケージ化したら、1つ以上のコンテナを起動します。通常、物理ホストまたは仮想ホストごとに複数のコンテナを実行します。コンテナの管理には、KubernetesやMarathonなどのクラスターマネージャーを使用できます。クラスターマネージャーは、ホストをリソースプールとして扱います。各コンテナに必要なリソースと各ホストで利用可能なリソースに基づいて、各コンテナの配置先を決定します。
Service Instance per Containerパターンには、利点と欠点の両方があります。コンテナの利点はVMと似ています。サービスインスタンスを相互に分離でき、各コンテナが消費するリソースを容易に監視できます。また、VMと同様に、コンテナはサービスの実装に使用するテクノロジーをカプセル化します。コンテナ管理APIは、サービスを管理するためのAPIとしても機能します。
ただし、VMとは異なり、コンテナは軽量なテクノロジーです。通常、コンテナイメージは非常に短時間でビルドできます。たとえば、私のノートPCでは、Spring BootアプリケーションをDockerコンテナとしてパッケージ化するのに最短で5秒しかかかりません。また、時間のかかるOSの起動処理がないため、コンテナは非常に短時間で起動します。コンテナの起動時に実行されるのはサービスです。
コンテナの使用には、いくつかの欠点があります。コンテナのインフラストラクチャは急速に成熟しつつありますが、VMのインフラストラクチャほど成熟していません。また、コンテナ同士でホストOSのカーネルを共有するため、VMほどセキュアではありません。
コンテナのもう1つの欠点は、コンテナイメージの管理に伴う、差別化につながらない煩雑な作業を自ら担う必要があることです。また、Google Container EngineやAmazon EC2 Container Service(ECS)などのホステッドコンテナソリューションを使用していない場合は、コンテナインフラストラクチャに加え、場合によってはその実行基盤となるVMインフラストラクチャも管理する必要があります。
また、コンテナはVM単位の料金体系を採用するインフラストラクチャにデプロイされることがよくあります。そのため、前述のとおり、負荷の急増に対応するためにVMを過剰にプロビジョニングすることで、追加コストが発生する可能性が高くなります。
興味深いことに、コンテナとVMの違いは曖昧になっていく可能性があります。前述のとおり、Boxfuse VMはビルドと起動を高速に行えます。Clear Containersプロジェクトは、軽量なVMの開発を目指しています。[編集者注 – 2017年12月に発表されたとおり、Clear Containersの開発は現在、オープンソースのKata Containersプロジェクトで継続されています。]また、unikernelへの関心も高まっています。Docker, Inc.は最近、Unikernel Systemsを買収しました。
さらに、比較的新しく、人気が高まりつつあるサーバーレスデプロイメントという概念もあります。これは、サービスのデプロイ先としてコンテナとVMのどちらかを選択する必要がないアプローチです。次に、これについて見ていきましょう。
サーバーレスデプロイ
AWS Lambdaは、サーバーレスデプロイ技術の一例です。Java、Node.js、Pythonのサービスをサポートしています。マイクロサービスをデプロイするには、ZIPファイルにパッケージ化してAWS Lambdaにアップロードします。また、リクエスト(別名イベント)を処理するために呼び出される関数名などを指定したメタデータも提供します。AWS Lambdaは、リクエストの処理に必要な数のマイクロサービスインスタンスを自動的に実行します。料金は、各リクエストの処理時間と消費メモリ量に基づいて請求されます。もちろん、実際には細かな点が重要であり、この後で説明するように、AWS Lambdaにも制限があります。それでも、開発者自身も組織内の誰も、サーバー、仮想マシン、コンテナについて一切気にする必要がないという考え方は、非常に魅力的です。
A Lambda functionはステートレスなサービスです。通常、AWSサービスを呼び出してリクエストを処理します。たとえば、S3 bucketに画像がアップロードされたときに呼び出されるLambda functionは、DynamoDBのimagesテーブルに項目を挿入し、Kinesisストリームにメッセージを発行して画像処理をトリガーできます。Lambda functionは、サードパーティのWebサービスを呼び出すこともできます。
Lambda関数を呼び出す方法は4つあります。
- Webサービスリクエストを使用して直接呼び出す
- S3、DynamoDB、Kinesis、Simple Email ServiceなどのAWSサービスによって生成されたイベントに応じて自動的に
- アプリケーションのクライアントからのHTTPリクエストを処理するため、AWS API Gateway経由で自動的に
- 定期的に、
cronのようなスケジュールに従って
このように、AWS Lambdaを使用すると、マイクロサービスを容易にデプロイできます。リクエストベースの料金体系のため、サービスが実際に実行した処理に対してのみ料金が発生します。また、ITインフラストラクチャを管理する必要がないため、アプリケーションの開発に集中できます。
ただし、いくつかの重大な制約があります。サードパーティのメッセージブローカーからメッセージを受信するサービスなど、長時間稼働するサービスのデプロイを目的としたものではありません。リクエストは300秒以内に完了する必要があります。理論上、AWS Lambdaではリクエストごとに別のインスタンスが実行される可能性があるため、サービスはステートレスでなければなりません。また、サポートされているいずれかの言語で記述する必要があります。サービスは迅速に起動する必要もあります。そうでない場合、タイムアウトして終了する可能性があります。
まとめ
マイクロサービスアプリケーションのデプロイは容易ではありません。さまざまな言語やフレームワークで記述されたサービスが数十、場合によっては数百も存在します。それぞれが小規模なアプリケーションであり、デプロイ、リソース、スケーリング、監視について固有の要件があります。マイクロサービスのデプロイパターンには、Service Instance per Virtual MachineやService Instance per Containerなどがあります。ほかにも、サーバーレス方式のAWS Lambdaは、マイクロサービスをデプロイするための興味深い選択肢です。本シリーズの次回となる最終回では、モノリシックアプリケーションをマイクロサービスアーキテクチャに移行する方法を取り上げます。
編集部 – 全7回の記事シリーズが完結しました:
- マイクロサービス入門
- マイクロサービスの構築:API Gatewayの使用
- マイクロサービスの構築:マイクロサービスアーキテクチャにおけるプロセス間通信
- マイクロサービスアーキテクチャにおけるサービスディスカバリ
- マイクロサービス向けイベント駆動型データ管理
- マイクロサービスのデプロイ戦略の選択(本記事)
- モノリスからマイクロサービスへのリファクタリング
一連の記事に加え、NGINX Plusを使用したマイクロサービスの実装に関する情報をまとめた電子書籍「マイクロサービス:設計からデプロイまで」もダウンロードできます。また、「マイクロサービス・リファレンス・アーキテクチャ」シリーズと「マイクロサービス・ソリューションのページ」もご覧ください。
ゲストブロガーのChris Richardson氏は、初期のAmazon EC2向けJava PaaS(Platform as a Service)である初代CloudFoundry.comの創設者です。現在は、アプリケーションの開発およびデプロイ方法の改善に向けて、組織にコンサルティングを提供しています。また、マイクロサービスについてhttps://microservices.ioで定期的にブログを執筆しています。
関連するブログ記事
チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。
AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。
JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。
F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。
NGINX PlusによるAWS Auto Scalingグループの負荷分散
新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります