Google Cloud への .NET アプリのデプロイ

Last reviewed 2022-01-19 UTC

この記事では、Google Cloud で .NET アプリをデプロイする方法の概要と、アプリに適したデプロイ方法の選び方について説明します。

はじめに

Microsoft .NET フレームワークには、アプリ開発のための豊富なツールとライブラリが用意されています。Windows で Docker がサポートされ、Linux で .NET Core アプリを実行できるようになったため、.NET アプリはさまざまなデプロイ ターゲットをサポートできるようになりました。

開発とテストを効率的に行うために、アプリのデプロイを自動化し、それを継続的インテグレーション / 継続的デリバリー(CI / CD)パイプラインの一部にすることができます。ただし、適切なツールを選択して CI / CD パイプラインを構築するには、最初にアプリを本番環境で実行する方法と、取るべきデプロイ方法を特定する必要があります。

Google Cloud で .NET アプリをデプロイする方法は 1 つだけです。最適なデプロイ方法は、アプリと要件によって異なります。たとえば、アプリが完全な .NET Framework を必要とする場合、または IIS 上で実行する必要がある場合、デプロイは Windows 上で行われます。一方、.NET Core でサポートされている機能を使用してアプリを実行できる場合は、Linux 環境でデプロイできます。

この記事では、各オプションが適しているときの条件など、Google Cloud で .NET アプリを実行してデプロイするさまざまな方法について説明します。また最後に、ディシジョン ツリーにデプロイ オプションをまとめています。.NET アプリに最適な Google Cloud コンポーネントとアプローチを決定する際にお役立てください。

デプロイモデル

アプリの自動デプロイを行う場合、2 つの基本的な方法があります。デプロイ パッケージをアプリサーバーに push する方法と、アプリサーバーでアプリ パッケージを既知の場所から pull する方法です。次のセクションで、これらの 2 つのモデルの違いについて説明します。

push ベースのデプロイ

push ベースのデプロイでは、最初はデプロイ サーバーでのみデプロイ アーティファクト(zip ファイル、NuGet パッケージ、その他のアーティファクト)を使用できます。デプロイ サーバーには、CI システムの前提となる専用マシンまたはロールを使用できます。

デプロイを行うには、デプロイ サーバー上のプロセスがアプリサーバーに接続し、デプロイ アーティファクトをコピーして、そのインストールを開始します。複数のアプリサーバーが存在する場合、このプロセスは並列的に繰り返されることもありますが、多くの場合は直列的に繰り返され、すべてのアプリサーバーにアーティファクトをデプロイします。

次の図は、この流れを表しています。

push ベースのデプロイ

このようなデプロイを自動化できる、さまざまな構成管理ツールが用意されています。これらのツールの一部は、デプロイ手順の順序をスクリプト形式の方法で定義する命令形アプローチに従います。このアプローチは直感的ですが、構成のブレが生じる可能性が高くなります。つまり、一定の時間が経過すると、複数のマシンの状態が同一ではなくなる場合があり、意図した状態を完全には反映できなくなる場合があります。そのため、多くのツールでは、目標の状態を定義してツールに渡し、その状態を実現するために必要な手順をツール側で見つけ出します。

Windows では、このデプロイモデルのために次のツールが一般的に使用されます。

よく使用されるオープンソース ツールとして、AnsibleChef InfraPuppet などがあります。これらのツールは主に Linux を対象としていますが、Windows を対象としたデプロイも行うことができます。

セキュリティ

デプロイ サーバーがアプリサーバーにデプロイを push するためには、バックチャネルが使用可能である必要があります。たとえば、Web Deploy と Octopus Deploy ではこのタスクにカスタム プロトコルとポートが使用され、一方 Ansible では SSH が使用されます。

ツールで使用されるプロトコルにかかわらず、通信がセキュリティ保護されていることが重要であり、それにより攻撃者がバックチャネルを使用して悪意のあるアプリをデプロイすることを防ぐことができます。安全な通信には、何よりもまず、デプロイ サーバーがアプリサーバーで認証できる必要があります。

SSH では、公開鍵認証を使用できます。適切な IAM 構成を使用すると、アプリサーバーへの SSH に使用される公開鍵が自動的に配布されるようになります。ただし、IAM を使用していない場合は、Google Cloud による鍵の管理がされないため、このタスクをご自身で管理する必要があります。

1 つの選択肢は、Active Directory です。デプロイ サーバーとアプリサーバーの両方が Windows を実行し、Active Directory ドメインのメンバーである場合、認証は Kerberos を使用して処理されます。ただし、フォールト トレラント Active Directory 環境を実行するには、ドメイン コントローラを実行するために少なくとも 2 つの追加 VM インスタンスが必要になります。構成で自動スケーリングを使用する場合は、すべてのサーバーがドメインに動的に参加する必要もあり、これによりサーバーの起動プロセスが遅くなります。また自動スケーリングでは、古いコンピュータ オブジェクトがディレクトリに蓄積することになり、追加のスカベンジング ロジックが必要になることもあります。クラウドベースの環境で Active Directory を使用する場合は、これらの追加要素を考慮する必要があります。

Active Directory が存在しない場合は、NTLM を使用するか、HTTP 基本認証などの他の手段を使用して認証を処理する必要があります。どちらの方法でも、認証情報をデプロイ サーバーとアプリサーバーの間で同期させ、安全に保存する必要があります。これらのタスクはどちらも難易度が高くなる場合があります。

Linux または Windows のいずれを使用している場合も、デプロイとアプリサーバー間の通信を保護するには、IAM とは別のメカニズムが必要になります。しかし、複数のメカニズムを使用してシステムへのアクセスを制御すると、全体的に複雑になり、予想外の構成ミスが起きるリスクが高くなります。

オペレーティング システムの更新

アプリサーバーに新しいバージョンのアプリ パッケージを効率的にデプロイできることが重要ですが、それらのサーバーで基礎となるオペレーティング システムの保守も重要です。つまり、セキュリティ パッチをインストールすることです。大規模なサーバー群の場合、このプロセスを自動化し、リスクを最小限に抑え、更新中に使用できないサーバーの数も最小限に抑える必要があります。

また、オペレーティング システムの更新に push アプローチを使用することもできます。このアプローチでは、デプロイ サーバーがアプリサーバーの OS の更新をトリガーします。Linux では、SSH を使用してリモートで更新コマンドを実行するのが一般的です。Windows では、PowerShell Remoting(WinRM に依存)を選択するのが一般的です。どちらのメカニズムも、安全に認証を行い、安全に認証情報を保存できる必要があります。

自動スケーリング

アプリサーバーの数が変更されない静的環境では、デプロイ サーバーはすべてのデプロイ ターゲットを事前に把握しています。クラウド環境では、アプリサーバー数の自動スケーリングが有効な場合がよくあります。push ベースのデプロイを使用する場合、これにより次の 2 つの課題が発生します。

  • 新しいアプリサーバーを追加する場合、その新しいサーバーが今後のデプロイに含まれるようにするために、デプロイ サーバーに登録します。
  • 新しいサーバーは初期デプロイを受け取る必要があります。

自動スケーリング イベントは、デプロイ サーバーによって開始されません。代わりに、基盤となるマネージド インスタンス グループによって開始されます。このグループは、デプロイ サーバーの下位レベルで機能します。

新しいアプリサーバー インスタンスは、デプロイ サーバーに自身を登録し、デプロイをトリガーする必要があります。デプロイ後、新しいアプリサーバーはリクエストを処理できます。次の図に、このプロセスを示します。

push ベースのデプロイによる自動スケーリング

このアプローチが機能するには、デプロイ サーバーがアプリサーバーに接続し、認証できるだけでは不十分です。アプリサーバーもデプロイ サーバーに接続し、認証する必要があります。

最後に、新しく起動したサーバーにも最新の OS セキュリティ パッチが必要です。自動スケーリング処理中に更新を開始すると、処理が大幅に遅れてしまいます。そのため、アプリサーバー VM の作成元のイメージには、事前に更新をインストールしておく必要があります。これは次の 2 つの方法で対処できます。

  • Google Cloud によって提供される公開イメージを使用する。このイメージは Google によって常に最新の状態に保たれています。これらのイメージには OS しか含まれていないため、起動スクリプトを使用して、またはアプリデプロイの一環として、カスタマイズ処理(アプリコード、ユーティリティ、OS の構成)を行う必要があります。
  • カスタム OS イメージを維持し、常に最新の状態に保つ。これにより、イメージにカスタマイズを適用できますが、デプロイの管理は全体的に複雑になります。

push ベースのデプロイは直感的に行うことができますが、セキュリティ、OS の更新、自動スケーリングを考慮すると、かなり複雑になってしまう可能性があります。次のセクションでは、よりクラウド ネイティブなデプロイ方法である、pull ベースのデプロイについて説明します。

pull ベースのデプロイ

pull ベースのデプロイでは、デプロイは間接的に実行されます。CI システムにより新しいバージョンのデプロイ アーティファクトが作成された後、そのアーティファクトがリポジトリにパブリッシュされます。次の図は、この流れを表しています。

pull ベースのデプロイ

デプロイを実行する際(アーティファクトがパブリッシュされた直後や、後の段階)は、デプロイ サーバーが実際のデプロイをトリガーします。ここでも、デプロイ サーバーは、CI システムで前提となる個別システムまたは役割の場合があります。デプロイ サーバーはデプロイをトリガーすると、アプリサーバーに接続し、アプリサーバーに中央リポジトリからデプロイ アーティファクトを pull してインストールさせます。

push ベースのモデルと pull ベースのモデルの違いは、最初はわずかであるように感じられますが、pull ベースのデプロイを実行することには、次のような重要な意味があります。

  • アプリサーバーをトリガーしてデプロイ アーティファクトを pull しますが、これはアプリレベルまたは OS レベルで行う必要はありません。代わりに、デプロイ サーバーは、Compute Engine で VM を再起動または置き換えることによって pull オペレーションをトリガーできます。これにより、push ベースのデプロイに伴うセキュリティ上の課題を克服できます。
  • デプロイ アーティファクトは、アプリファイルを単に格納するのではなく、Docker イメージや VM イメージにできます。そうすることで、アプリと OS の更新を適用するプロセスを統一できます。

セキュリティ

デプロイ サーバーは、特定の種類のデプロイについてはアプリサーバーとまったく対話する必要がありません。たとえば、デプロイ アーティファクトが次のいずれかである場合、対話は必要ありません。

  • VM イメージ。
  • Google Kubernetes Engine にデプロイされる Docker イメージ。
  • App Engine にデプロイされるパッケージ。

代わりに、デプロイ サーバーは、デプロイを開始するために Google Cloud APIs とのみ対話する必要があります。これは、デプロイ プロセスが IAM によって提供される認証メカニズムを活用できることを意味します。したがって、キーや認証情報を管理する必要がなくなります。

アプリファイルとバイナリのみを含む zip パッケージや NuGet パッケージなどのデプロイ アーティファクトを使用すると、次のような方法でデプロイをトリガーできます。

  • オペレーティング システムの起動時に最新のデプロイ アーティファクトを pull してインストールするようにサーバーが構成されている場合、Google Cloud で VM を再起動することで更新をトリガーできます。再起動に時間がかかるように感じられる場合がありますが、これにより、デプロイ サーバーをアプリサーバーで認証する必要がなくなります。
  • push ベースのデプロイと同様に、デプロイ サーバーはバックチャネル経由でリモートから更新をトリガーできます。しかし、この方法も push ベースのデプロイで認証情報を管理する場合と同様のセキュリティへの影響と課題があります。
  • デプロイ サーバーは、新しいデプロイ アーティファクトのリポジトリをモニタリングするエージェントを実行できます。新しいアーティファクトが検出されると、サーバーはそれを自動的に適用できます。発生する可能性がある問題は、複数のアプリサーバーが同時に更新をインストールすることになり、それらのサーバーが使用不可になることです。これを回避するために、エージェントはリポジトリ内のサーバーの状態を追跡でき、その情報を使用して、更新のロールアウトを制御できます。

これらの各ケースでは、サーバーが不正なパッケージを pull してインストールするのを防ぐために、リポジトリへの書き込みアクセスを制御する必要があります。

オペレーティング システムの更新

Docker イメージまたは VM イメージがデプロイ アーティファクトとして使用される場合、これらのアーティファクトによってアプリファイルと依存関係が結合されます。これにより、オペレーティング システムの更新とアプリの更新に同じデプロイ メカニズムを使用できます。この場合、新しいデプロイ アーティファクトを 2 つの別個のケースで作成、パブリッシュできるようにする必要があります。1 つは、新しいアプリのバージョンが利用可能になったときです。もう 1 つは、オペレーティング システムまたは他の依存関係に対する新しいセキュリティの更新がリリースされたときです。

デプロイ アーティファクトにアプリファイルのみが含まれる場合は、オペレーティング システムを最新の状態に保つための別個のタスクとなります。したがって、push ベースのデプロイのコンテキストで説明したものと同じ影響があります。

自動スケーリング

アプリサーバーでデプロイ アーティファクトを pull することは、自動スケーリングの概念によく合致しています。これにより、自動スケーリングと push ベースのデプロイを組み合わせることで発生する複雑さが大幅に軽減されます。自動スケーリング イベントにより新しいアプリサーバーが起動されるたびに、サーバーはリポジトリに接続し、最新のデプロイ パッケージを pull してインストールします。

VM イメージまたは Docker イメージを使用する場合、イメージを pull するためのメカニズムは Google Cloud によって提供されます。zip アーカイブや NuGet アーカイブなどの他のパッケージを使用する場合、起動後にデプロイを開始するようにアプリサーバーを構成する必要があります。VM イメージをカスタマイズするか、起動スクリプトを使用することで、これを行うことができます。

デプロイ ターゲット

これまで、.NET アプリは Windows 上でのみ動作し、Windows ではコンテナがサポートされていませんでした。このため、アプリを実行する環境について選択肢はほとんどありませんでした。

.NET Core の登場により、アプリを Windows と Linux のどちらで実行するか決定できるようになりました。どちらのオペレーティング システムもコンテナをサポートしているため、どの環境をターゲットにするか、複数の選択肢を持てるようになりました。

OS

Mono は長年にわたって Windows 以外のプラットフォームに .NET アプリをデプロイする方法を提供してきましたが、.NET Core のリリースによってようやく Microsoft 開発スタックのプラットフォームとして Linux が完全サポートされるようになりました。

.NET Core では、.NET Framework の機能のサブセットのみ提供されます。したがって、.NET Core をターゲットにすると、アプリに一定の制限が課せられます。既存のアプリにとってさらに重要な点は、.NET Framework から .NET Core への移植は、必ずしも簡単でコスト効率がよいとは言えない点です。場合によっては、移植がまったく不可能なこともあります。

したがって、デプロイモデルとターゲットを選択するとき、Linux を使用するか(.NET Core が必要)、または Windows を使用するか(.NET Core と .NET Framework のどちらもサポートされる)を原則として考慮する必要があります。

Linux で .NET アプリを実行する場合、次のような利点があります。

  • フルマネージド環境である App Engine フレキシブル環境を使用できます。
  • コンテナ オーケストレーションをサポートするマネージド環境である GKE を使用できます。
  • Windows ライセンスに関連するプレミアム Compute Engine イメージの追加コストを回避できます。

これらの長所と、Linux で .NET Core を使用する場合の次の短所を比較して検討する必要があります。

  • 既存の .NET アプリを .NET Core に移植するのに労力を要するため、コスト削減が実現しない場合があります。また前述したように、既存の .NET アプリを .NET Core に移植することが単純に不可能な場合もあります。
  • Linux は IIS をサポートしていません。.NET Core のウェブサーバーである Kestrel は非常に優れたパフォーマンスを発揮しますが、IIS と同じ機能セットは提供しません。したがって、Kestrel を Nginx のようなウェブサーバーと組み合わせて使用する必要があります。
  • Linux には、Windows サービスと直接的に同じものはありません。通常、Windows サービスをデーモンとして実行できる Linux コンソール アプリに変換できますが、この変換は必ずしも容易ではありません。
  • Linux で .NET Core アプリをトラブルシューティングおよびデバッグする場合、Windows で .NET を使用する場合とは異なるツールとスキルが必要になります。チームが Linux をあまり使用したことがない場合、労力を要することがあります。

コンテナ

コンテナは、単一のプロセスで実行されるアプリに特に適しています。次に例を示します。

  • Windows サービス
  • デーモンとして動作する Linux コンソール アプリ
  • セルフホストの WCF サービス
  • Kestrel がホストする ASP.NET MVC アプリまたはウェブ API アプリ

多くの .NET アプリが IIS をターゲットとしています。IIS は一般的に、個別の仮想ディレクトリ内およびアプリプール内の複数のアプリを管理するために使用されます。このため、単一プロセス パターンとは一致しない場合があります。

IIS ベースのセットアップをコンテナに移動する場合、次のようなさまざまな方法があります。

  • microsoft/iis イメージをベースとして使用し、すべての仮想ディレクトリとアプリプールがある IIS を単一の Windows ベースの Docker イメージに配置します。この方法では、アプリを個別に更新およびデプロイできないため、アプリが密結合されていない限り、この方法は一般的におすすめできません。
  • アプリごとに Windows ベースの個別 Docker イメージを使用し、それぞれで IIS を実行します。これにより、アプリを個別に管理できます。ただし、これらのコンテナを多数運用する必要がある場合、IIS で無視できないオーバーヘッドが発生し、それが重大な問題となる可能性があります。
  • IIS から Kestrel に一部またはすべてのアプリを移行します。Kestrel は Windows ベースのコンテナまたは Linux ベースの Docker コンテナのいずれかにデプロイできるため、この方法ではコンテナを個別に管理できます。

IIS では、複数のウェブアプリを 1 つのウェブサイトで実行して、単一のドメイン名を共有できます。アプリを個別のコンテナにパッケージ化する場合、コンテンツ ベースの負荷分散を使用して同じ機能を実現できます。同様に、Google HTTP ロードバランサを使用すると、Kestrel サーバーの前にカスタム リバース プロキシをデプロイする必要がなくなります。

ほとんどのアプリはコンテナ化できます。できないことはめったにありません。ただし、コンテナ化のシナリオの一部には、次のような課題があります。

  • IIS で管理されているアプリの場合、アプリのデプロイはすでに自動化されていることが一般的です。ただし、IIS を構成する手順(アプリプール、バインディングなどの作成)は手動で実行します。コンテナに移動するとき、これらの初期手順もすべて自動化する必要があります。
  • 構成ファイルまたはディスク上のデータに依存するアプリは、変更が必要な場合があります。たとえば、構成情報は環境変数から取得でき、関連するファイルとフォルダはボリュームとしてマウントできます。これにより、イメージはステートレスとなり、環境固有の構成は不要となります。

最後に、Windows ベースの Docker コンテナを使用する場合、Google Cloud は現在 Hyper-V をサポートしていないため、Hyper-V コンテナを実行できません。したがって、Google Cloud では Windows Server コンテナのみをデプロイできます。Windows Server コンテナは Hyper-V コンテナよりも軽量で、分離の異なるレベルが異なります。

デプロイの制約

このセクションで説明するように、アプリの構築方法の特定要因により、使用するデプロイ方法に制約が課せられる場合があります。

アプリ アーキテクチャ

デプロイのターゲットとモデルを選択するときに考慮する主な要素は、アプリのアーキテクチャです。一方では、アプリがモノリシック アーキテクチャ パターンに従う場合があります。この場合、すべてのアプリロジックが単一のコードベースで実装され、単一のプロセスまたは IIS アプリプールで実行されます。もう一方では、アプリがマイクロサービス パターンに従う場合があります。この方法では、アプリは、個別のプロセス、個別の IIS アプリプールで、または個別の Windows サービスとして独立して実行される多数のサービスで構成されます。

最後に、一律のデプロイ戦略を使用してデプロイする、複数の独立したアプリを用意できます。これらの各アプリは、それ自体がモノリシックである場合があります。ここでは、この方法はマイクロサービス シナリオと同等と見なすことができます。

マイクロサービス アーキテクチャでは、サービスを分離して独立して管理できるようにしながら、アプリをコスト効率よく運用することが望ましいです。そのためには、各サービスに専用の VM を割り当てることができます。これにより、それらのサービスを個別に管理およびデプロイできます。だだし、この方法では、多数の VM が十分に活用されない場合があり、不要なコストが発生する場合があります。このようなアプリの場合、(特にコンテナベースのモデルで)より緊密なパッキングを可能にするデプロイモデルの方がコスト効率に優れている場合があります。

状態とステートレス

クラウド用のアプリを設計する場合は、アプリをステートレスに保ち、Google Cloud ベースのストレージ サービスを使用して外部で状態を管理するようにします。ステートレス アプリには、次のような多くの利点があります。

  • 可用性を向上させ、容量を増やすために重複してデプロイできます。
  • リクエストを複数のインスタンスに自由に分散させることができます。
  • 自動スケーリングを適切に行うことができます。
  • 障害が発生した場合、データを損失するリスクを負うことなく環境(コンテナまたは VM)を再作成できます。

アプリをステートレスになるように設計することは必ずしも容易ではないため、多くの古いアプリではこれは実践されていません。それでも、アプリをステートレスにできるかどうか分析することは価値があります。

セッション状態

ASP.NET アプリと ASP.NET MVC アプリでは、一般的に、セッションを使用してユーザーの状態が追跡され、アプリがステートフルになります。ただし、セッションの影響を制限するオプションが複数あります。

  • セッション データの量が少ない場合は、代わりに暗号化または署名された Cookie に状態を保存できます。
  • デフォルトの InProc セッション状態プロバイダを使用する代わりに、SQLServer プロバイダを使用できます。ただし、これには SQL Server インスタンスが必要です。SQL Server インスタンスを使用すると、追加コストがかかり、アプリのレイテンシや可用性に影響が及ぶ場合があります。
  • Cloud Load Balancing でセッション アフィニティを活用できます。この機能を使用すると、単一のクライアントからのすべてのリクエストを同じアプリ インスタンスにルーティングできます。ただし、セッション アフィニティを使用すると、負荷分散の公平性に悪い影響が及ぶ可能性があります。つまり、特定のアプリ インスタンスが他のアプリ インスタンスよりも多くのリクエストを受け取る可能性があります。さらに、なんらかの理由でアプリ インスタンスが終了した場合、そのインスタンスによって処理されたセッションはすべて失われ、エンドユーザーに影響が及ぶ可能性があります。このため、セッション アフィニティに依存することは理想的な解決策ではありませんが、堅牢性とコストの間で選択できる妥協点として使用されることがよくあります。

メモリ内キャッシュ

アプリでは、メモリ内キャッシュを使用して、冗長な計算やデータベースの検索を回避することが一般的です。ただし、これには問題があり、アプリの複数のインスタンスが同時に実行されている場合、キャッシュが不整合になる可能性があります。

不整合を回避するには、直接的に、または IDistributedCache インターフェースを使用して分散キャッシュを使用します。Redis や Memcached などのキャッシュ サーバーは、通常、比較的少ないリソースで使用できますが、これらを使用すると全体的に複雑になります。

保存

イメージ、添付ファイル、メディア ファイルの形式のデータは、通常はディスクに保存されます。このような目的で VM 上の永続ディスクを使用するという選択肢は通常は取りません。なぜなら、複数のマシン間でデータを共有できなくなるため、VM インスタンスが再作成されるとデータが失われる危険性があるためです。代わりに、次のいずれかの方法を使用できます。

  • データをファイル共有サーバーに移動します。これにより、アプリへの影響を最小限に抑えられます。ただし、可用性が高い SMB サーバーまたは NFS サーバーを運用するには、追加のコストとメンテナンスが必要になります。
  • データを Cloud Storage に移動します。これを行うにはアプリの変更が必要となりますが、Cloud Storage は可用性が高く、ファイル サーバーを実行するよりもコスト効率が非常によく、メンテナンスの手間がかかりません。
  • データを Filestore ファイル サーバーに移動します。このアプローチでは、アプリにいくつかの変更を加える必要がありますが、プロビジョニング後に、ダウンタイムなしで必要に応じてインスタンスの容量をスケーリングできます。Filestore では、同じファイル システムに同時にアクセスする複数のアプリケーション インスタンスも同時にサポートされます。
  • データを Cloud Volumes Service に移動します。Cloud Volumes Service を使用すると、ファイルベースのアプリを Google Cloud に移行でき、NFS ボリュームと SMB ボリュームがサポートされます。アプリを再設計する必要はありません。また、アプリ用の永続ストレージを複雑さなく活用できます。

デプロイ戦略

新しいバージョンのアプリをデプロイする場合、リスクやエンドユーザーへの影響を最小限に抑える必要があります。これを実現する最も一般的な戦略は、再作成、Blue / Green、ローリング デプロイの 3 つです。

再作成戦略

再作成戦略では、すべてのサーバーで実行中のアプリを停止して新しいバージョンをデプロイし、アプリを起動します。この戦略には、サービスを中断するという明白な欠点がありますが、アプリの 2 つの異なるバージョンが一時的に共存し、共通データにアクセスすることで発生する可能性のある問題を回避できます。

Blue / Green 戦略

Blue / Green 戦略(Red / Black とも呼ばれます)では、新しいアプリ バージョンを新しいサーバーセットにデプロイします。デプロイが完了した後、古いサーバーセットから新しいサーバーセットにすべてのトラフィックを切り替えます。この方法では、本番環境で必要になるサーバー台数の最大 2 倍のサーバーが一時的に必要になりますが、サービスの中断は回避できます。

この戦略の前提条件は、アプリの 2 つのバージョンが一時的に共存でき、互いに干渉しないことです。データベースにアクセスするアプリの場合、データベース スキーマに対して繰り返し行われる各変更に、少なくとも前のバージョンとの下位互換性がなければなりません。

ローリング デプロイ戦略

ローリング デプロイでは、サーバーを 1 つずつ順番に更新します。Blue / Green 戦略と同様に、この戦略では、アプリの異なる 2 つのバージョンが一定の期間共存することになります。ただし、Blue / Green デプロイとは異なり、古いバージョンから新しいバージョンへトラフィックを徐々に移行します。更新されたサーバーの数が増えると、新しいバージョンにルーティングされるユーザーの数も増えます。最後のサーバーが更新されると、すべてのユーザーが新しいバージョンを使用します。この方法の主な利点は、潜在的な問題を早期に、つまり、すべてのユーザーが影響を受ける前に発見でき、全体的なリスクを軽減できる点です。

ローリング デプロイ戦略では、アプリの 2 つのバージョンを共存させる必要があるため、ユーザーが各バージョンを行き来しないよう、通常はロードバランサ構成も必要になります。

デプロイのオプション

ここまで、デプロイモデル、ターゲット、戦略について説明してきました。以下のセクションでは、Google Cloud に .NET アプリをデプロイするための特定のオプションについて説明します。

GKE(Windows または Linux)

GKE は、フルマネージド Kubernetes 環境を提供します。GKE は、Kubernetes のオーケストレーション機能があるため、多数のコンテナで構成される複雑なマイクロサービス アプリを実行するのに特に適しています。ただし、マイクロサービスのパターンに従わないアプリでも、GKE を使用すれば、リソース効率と保守の容易さを兼ね備えた方法で、共有インフラストラクチャで多数のコンテナを実行できます。

GKE を使用する場合、アプリのすべてのパーツを Docker コンテナとしてパッケージ化する必要があります。Linux ベースのコンテナには、.NET Core と Linux ベースの環境を使用する必要があります。CI システムが Windows ベースの場合、Linux のコンテナを作成することは困難です。ただし、Azure Pipelines / Team Foundation Server と Cloud Build はどちらも、.NET Core アプリを作成するため、および Linux ベースのコンテナ イメージを作成してパブリッシュするための組み込みサポートを提供しています。

GKE は、ステートレスなアプリに最大限の柔軟性を提供します。ステートフル設定と永続的なボリュームを使用することで、特定の種類のステートフル アプリを GKE で実行することもできます。

GKE クラスタには、ノードと呼ばれる多数の VM インスタンスが含まれ、これらのノードにコンテナがスケジュールされます。マルチゾーンまたはリージョン クラスタでは、GKE によりノードとワークロードを複数のゾーンに分散させて高可用性を実現できます。

料金は、実行されているノードの数によって決まります。したがって、GKE は、ノードが十分に活用されている場合に費用対効果が最大になります。同じクラスタで、または必要に応じて自動的にノードの数を調整することで、大規模なワークロードを実行できます。

kubectl コマンドを使用した pull ベースのデプロイ

アプリを GKE にデプロイするには、次の 2 つの手順を行います。

  1. docker push または他の手段を使用して、Docker イメージを Artifact Registry または外部 Docker レジストリにパブリッシュします。この手順は、通常は CI システムによって処理されます。
  2. kubectl を使用してデプロイをトリガーします。この手順は、CI システムで処理することも個別に処理することもできます。デプロイはリモートで開始されるため、kubectl が Linux で実行されても Windows で実行されても問題ありません。

GKE には、再作成戦略とローリング デプロイ戦略の組み込みサポートが含まれています。デプロイを制御するプリミティブには、他のデプロイ戦略を可能にするだけの柔軟性がありますが、他の戦略を使用するには追加のツールやスクリプトが必要になります。

Spinnaker を使用した pull ベースのデプロイ

GKE に組み込まれているオーケストレーションのデプロイ機能では目的を十分に達成できない場合は、GKE と Spinnaker を組み合わせることができます。Spinnaker には GKE の組み込みサポートが含まれており、Blue / Green デプロイなどの高度なデプロイ戦略を実装できます。

Spinnaker はマネージド サービスではないため、個別にデプロイして管理する必要があります。Spinnaker は、個別の Linux VM インスタンスまたは GKE クラスタにデプロイできます。

Knative と Cloud Run

ステートレス .NET Core コンテナ、Knative、そのマネージド バージョンである Cloud Run には、サーバーレス コンテナ環境が用意されています。サーバーレス コンテナには、インフラストラクチャの管理オーバーヘッドなしでプロビジョニング、自動スケーリング、ロード バランシングなどを行えるメリットがあります。

Kubernetes クラスタにコンテナをデプロイするために、Knative には Kubernetes より上位で小規模な API サーフェスが用意されています。Knative を使用することで Kubernetes の複雑さを回避でき、コンテナのデプロイが容易になります。

Cloud Run は Knative API に従いますが、Google インフラストラクチャで実行されるため、Kubernetes クラスタは必要なくなります。Cloud Run によってコンテナのサーバーレス オプションが提供されます。デフォルトでは、Cloud Run のコンテナは自動スケーリングされ、リクエストの期間に課金されます。デプロイ時間は秒単位です。 Cloud Run には、リビジョンやトラフィック分割などの便利な機能も用意されています。

Cloud Run for Anthos は、Cloud Run のより柔軟なバージョンで、Kubernetes の運用上の柔軟性とともに Knative と Cloud Run のシンプルさを提供します。たとえば、Cloud Run on Anthos では、コンテナを実行している基盤となるインスタンスに GPU を追加することや、アプリケーションを多数のコンテナにスケールアップできます。

Cloud Run は、Pub/Sub、Cloud Scheduler、Cloud Tasks などの他のサービス、および Cloud SQL などのバックエンドと統合されています。自動スケーリングされたウェブ フロントエンドや、イベントがトリガーする内部マイクロサービスの両方で使用できます。

Compute Engine(Windows または Linux)

Compute Engine を使用すると、VM インスタンスを作成および管理できます。Windows Server のさまざまなバージョンと Linux ディストリビューションに加えて、サイジングと構成のオプションもサポートしています。この柔軟性により、Compute Engine VM インスタンスは幅広いワークロードに使用できます。

アプリを個別にデプロイおよび保守できるようにするには、VM インスタンスごとに 1 つのアプリまたはサービスのみをデプロイします。高可用性を実現するには、アプリごとに少なくとも 2 つの VM インスタンスを実行します。各インスタンスは異なるゾーンに配置します。したがって、予想される負荷にかかわらず、デプロイするアプリやサービスの数の 2 倍の数の VM インスタンスが必要であると想定できます。

Compute Engine は、マネージド インスタンス グループを通じて自動スケーリングを実装する簡単な方法を提供します。マネージド インスタンス グループは、この記事の後半で説明するように、ローリング デプロイを実装する方法も提供します。

Compute Engine は VM インスタンスによって価格が決まるため、アプリがかなりの負荷を受けた場合、つまり、VM インスタンスの利用率が高い場合、Compute Engine でアプリを実行する場合の費用対効果が最も高くなると言えます。対照的に、サービスとアプリの数は多いが平均利用率は低い場合は、GKE などの他のデプロイ オプションのほうが経済的になりやすいです。これらのオプションでは、ワークロードの分離を断念することなく複数のアプリで共通のインフラストラクチャを使用できるためです。

Windows VM インスタンスを実行するには、プレミアム イメージを使用する必要があります。これらのイメージには、Windows のライセンス付きコピーが含まれるため、追加料金がかかります。このため、ライセンス料がかからない CentOS や Debian などの Linux ディストリビューションを使用する VM に比べて、Windows VM は一般的に費用対効果が低くなります。

手動でアプリをデプロイしたり、最初のデプロイ用のマシンを準備するために必要な初期構成を処理したりするために、SSH または RDP を使用して VM インスタンスを手動で設定できます。ただし、これにより、他の VM インスタンスとは異なる固有の構成を持つマシンが存在することになります。長期的にみると、VM インスタンスを手動で設定することは複雑で労力がかかると言えます。そのため、プロセスを自動化して反復可能にすることをおすすめします。

Compute Engine でアプリのデプロイを自動化する場合は、次のタスクを行います。

  1. アプリの最初のデプロイのために VM インスタンスをプロビジョニングして準備します。
  2. アプリのデプロイを行います。
  3. OS の保守を行います(セキュリティ アップデートをインストールします)。

次の 2 つのセクションでは、pull ベースのデプロイ方法を使用して 3 つすべての手順を統一的に処理する方法について説明します。これらのセクションで説明されている方法では、異なるメカニズムとツールを使用していますが、一般的な考え方は、GKE を使用してコンテナベースのアプリをデプロイする方法と類似しています。

マネージド インスタンス グループを使用した pull ベースのデプロイ

マネージド インスタンス グループは、自動スケーリングを実装するために使用されることが多いですが、ローリング デプロイを処理する方法も提供しています。アプリの新しいバージョンを参照するインスタンス テンプレートが作成された後、ローリング置換機能を使用して、古いテンプレートを使用する VM インスタンスを新しいテンプレートを使用するインスタンスに置き換えることができます。

この方法の前提条件は、アプリの新しいバージョンがインスタンス テンプレートとして利用可能になっていることです。これは次の 2 つの方法で実現できます。

  • いずれかの OS 公開イメージを使用するインスタンス テンプレートを定義します。起動スクリプトを使用してシステムを構成し、Cloud Storage バケット、NuGet リポジトリ、Docker レジストリ、または別のソースからアプリをインストールします。次の図はこの方法を示しています。

    マネージド インスタンス グループと公開イメージを使用した pull ベースのデプロイ

  • CI / CD プロセスの一部としてカスタム VM イメージを作成します。このプロセスは、イメージの焼き付けと呼ばれることがあります。この方法では、いずれかの OS 公開イメージを使用して新しい VM インスタンスを作成し、最新のアプリをそのインスタンスにインストールします。さらに、そのインスタンスから VM イメージを作成し、Google Cloud プロジェクトでそのイメージを使用できるようにします。Packer などのツールを使用し、プロセス全体を完全に自動化できます。結果として生じるイメージは、インスタンス テンプレートで参照できます。次の図はこの方法を示しています。

    マネージド インスタンス グループとカスタム イメージを使用した pull ベースのデプロイ

カスタム イメージを作成すること(2 番目のオプション)の短所は、イメージの焼き付けにかなり時間がかかることであり、通常は数分かかります。この方法では、CI / CD プロセスが複雑になるだけでなく、CI / CD プロセスも遅くなります。長所は、カスタム イメージを使用した新しい VM の起動は簡単かつ迅速であることです。これは自動スケーリングを使用する場合に便利です。

起動スクリプトを使用してアプリをデプロイする場合(1 番目のオプション)、上記の反対の状況が発生します。CI / CD プロセスでのイメージの焼き付けではオーバーヘッドが発生しませんが、VM インスタンスの作成プロセスは遅くなります。さらに、起動スクリプトの信頼性が十分でない場合、またはアプリバイナリのダウンロード元のシステムの可用性が高くない場合、この方法では可用性が低下する可能性があります。

アプリに最も適した方法は、アプリ自体と構成の複雑さによって決まります。シナリオによっては、両方の方法を組み合わせることが最善となる場合があります。

  • カスタム イメージにはすべての構成と依存関係が含まれていますが、実際のアプリバイナリは含まれていません。構成や依存関係が変更されると新しいイメージが焼き付けられますが、すべてのアプリビルドが対象ではありません。これにより、アプリの CI / CD パイプラインの速度低下が回避されます。
  • アプリは、起動スクリプトを使用してインストールされます。リスクと速度低下を最小限に抑えるために、このプロセスはできるだけ簡単にする必要があります。

共通の基本構成を持つ多数のさまざまなアプリやサービスをデプロイするシナリオでは、このハイブリッド アプローチにより、数十または数百のほぼ同じイメージを構築し、維持する必要がなくなります。

マネージド インスタンス グループを使用して、Linux ワークロードと Windows ワークロードの両方のデプロイをオーケストレートできます。Linux では、マネージド インスタンス グループを使用して VM インスタンスに Docker コンテナをデプロイすることが可能であり、プラットフォームによってサポートされています。ただし、これは利用頻度が高いアプリに対してのみおすすめします。他のケースでは、VM ごとに 1 つの Docker コンテナをデプロイすることは、GKE または App Engine フレキシブル環境を使用することに比べてほとんど利点がありません。

Windows Server コンテナを使用する場合は、次のガイドラインに沿って Compute Engine とマネージド インスタンス グループを使用してコンテナを実行してください。

  • Docker がプリインストールされたカスタムビルド イメージ、または次のいずれかの公開イメージを使用します。
    • Windows Server 2019 Datacenter Core for Containers
    • Windows Server 2019 Datacenter for Containers
  • 起動スクリプトを使用して Docker イメージを pull し、VM の起動時に Windows Server コンテナとして起動します。適切なポート マッピングを使用して、コンテナ内で実行されるサービスを公開できます。

起動スクリプトは、Docker サービスの開始前に実行される場合があります。Docker が使用可能になる前にスクリプトが実行された場合のために、スクリプトに適切な再試行ロジックを組み込んでおきます。

非クラウド環境で Windows ベースのイメージを作成する場合は、Microsoft Deployment Toolkit(MDT)または Windows Deployment Services(WDS)を使用できます。ただし、イメージの管理やカスタム イメージに基づく VM インスタンスの作成は、Compute Engine の中心的な機能であるため、この追加ツールは必要ありません。Compute Engine は、起動スクリプトだけでなく、Windows ベースの VM インスタンス用の特殊スクリプトもサポートしています。したがって、通常はカスタム unattend.xml ファイルを使用する必要はありません。ただし、イメージを作成する前に、GCESysprep を使用して Windows のインストールを一般化することは重要です。

Spinnaker を使用した pull ベースのデプロイ

マネージド インスタンス グループにより、ローリング デプロイを実装するための軽量で堅牢な方法が提供されますが、特定のアプリでは、マネージド インスタンス グループの機能では不十分な場合があります。より洗練されたデプロイ戦略とパイプラインを実装するには、Spinnaker を使用できます。

Compute Engine でのデプロイをオーケストレートするために Spinnaker で使用される基本的な方法は、前のセクションで説明した方法と類似しています。つまり、イメージの焼き付けも使用されます。このため、同じ考慮事項が適用されます。

Spinnaker はマネージド サービスではないため、アプリとは別にデプロイして管理する必要があります。Spinnaker は、個別の Linux VM インスタンスまたは GKE クラスタにデプロイできます。

push ベースのリモート デプロイ

前のセクションで説明した pull ベースのデプロイ オプションには、さまざまな利点があります。ただし、これらのオプションがあらゆる種類のアプリに適しているというわけではありません。特にステートフル アプリは一般的にこの方法には適しておらず、push ベースの方法に適しています。

push ベースの方法では、3 つのデプロイタスク(VM インスタンスのプロビジョニング、アプリデプロイの実行、OS のサービス)を個別に処理する必要があります。3 つのタスクすべてで同じツールを使用できますが、タスクごとに異なるツールを使用することもよくあります。

Terraform などの自動化ツールを使用して、他のインフラストラクチャと同じ方法でアプリサーバーの VM インスタンスをプロビジョニングできます。起動スクリプトまたは特殊スクリプトを使用して、アプリのデプロイを自動化するために必要なツールをインストールできます。たとえば、Puppet、Chef、Octopus Deploy などを使用する場合、これらのツールのエージェント ソフトウェアがインストールされていることを確認する必要があります。

セキュリティの観点からは、攻撃される範囲を削減するために、デプロイ サーバーとアプリサーバーの VM インスタンスで実行されるすべてのエージェントとの間の通信で内部ネットワークが使用されるようにする必要があります。また、使用されているポートが公共のインターネットに公開されないようにする必要があります。

自動スケーリングが使用されない環境では、構成を一元管理するために、Windows ベースのアプリサーバーを Active Directory ドメインに参加させるという方法を使用できます。Active Directory を使用すると、OS サービスなどの管理タスクも制御できます。

デプロイ オプションの選択

この記事の冒頭で述べたように、Google Cloud に .NET アプリをデプロイする最適な方法を 1 つに決めることはできません。最適なデプロイ方法は、アプリと要件によって異なります。適切なモデルを選択するために、最初に .NET Core と .NET Framework のどちらを使用するか決定し、それに応じて Linux と Windows のどちらにデプロイするか決定します。ターゲット オペレーティング システムを決定した後、次のディシジョン ツリーを使用して適切なデプロイモデルを決定します。

Linux に .NET Core アプリをデプロイする場合:

.NET Core と Linux を使用してデプロイするためのディシジョン ツリー

Windows に .NET Core または .NET Framework アプリをデプロイする場合:

Windows を使用してデプロイするためのディシジョン ツリー

次のステップ