こんにちは、アーキテクトの小林です。
さて、前回は .NET Core 3.0 で gRPC の統合が素晴らしい件をご紹介しました。
今回は gRPC をコンテナー化して Azure にデプロイしようと思います。
※本記事は下記の記事の続編となっています。記事後半の手順は、前回の記事で作成した成果物をつかっていますので、実際に手を動かしてみたい方は、前回の記事からはじめてください。
blog.ecbeing.tech
Azure App Serivce の gRPC 対応は現在進行中
ここで少しばかり残念なお知らせがあります。 gRPC はまだ IIS や Azure App Serivce に乗せることができません。あまりに質問が多かったのか、Microsoft Docs にドドーンと書いてあります。
“gRPC not supported on Azure App Service”
docs.microsoft.com
どうやら gRPC が依存している「HTTP response trailing headers」に Http.sys が対応していないためだそうです。 Http.sys は Windows のカーネルモードドライバーですので、これに依存している IIS では gRPC が動作しない、Azure App Serivce は IIS をつかっているので動作しない、という話の流れなっています。
GitHub の Issue でこの件に関してやりとりが継続して行われています。
Host grpc service in iis or as an app service?
github.com
Windows のカーネルを変更するのは Windows Update を伴うので Microsoft のスローなプロセスで展開されるため、リリース時期は未定とのこと。このやりとりの中で、Microsoft の Sourabh Shirhatti さんが以下のようなコメントを投稿しています。
This is currently not supported on App Service for Linux. We are currently working with App Service to enable this, but I don't have any ETA to share
Azure App Service on Linux は IIS に依存していないはずですので、こちらは Http.sys というカーネルモードドライバーの話ではなく App Service の作りの問題かと思います。リリース時期については言及を避けていますが、2019年11月には .NET Core 3.1 がLTS 版としてリリースされますので、その頃までにはきっとやり遂げてくれるのではないかな...、と期待しています。 この Issue を定期的にウォッチしていきたいと思います。
gRPC を Azure にホストする場合の推奨は Kubernetes
ということで、2019年10月現在で Azure にホストする方法は、AKS(Azure Kubernetes Service)が推奨されています。
でも Kubernetes なんです。敷居が高いんですよね。僕の脳みそはまだ Kubernetes を受け入れる準備ができていないため、苦難の道のりになりそうだな…と思っていました。
実は Azure にはコンテナーのホストするのに対応したサービスが他にもいろいろ提供されており、コンテナーのニーズによって選択できるようになっています。
- Azure Kubernetes Service Kubernetes のデプロイ、管理、運用を簡略化する
- Azure App Service Web およびモバイル向けのパワフルなクラウド アプリを短期間で作成
- Azure Container Instances サーバーを管理することなく Azure でコンテナーを簡単に実行
- Azure Batch クラウド規模のジョブ スケジュール設定とコンピューティング管理
- Azure Service Fabric Windows または Linux でのマイクロサービスの開発とコンテナーのオーケストレーション
ちなみに Azure Container Instances は AKS と組み合わせて利用することが可能で、AKS からは Virtual Kubelet という概念でラッピングされています。コンテナーオーケストレーターとして Kubernetes が、コンテナーの実行インスタンスとして Azure Container Instances という構成ができるそうです。
AKS を使いこなせるようになれば完璧なのですが、今回は App Service が gRPC に対応するまでのつなぎという位置づけなので、もっとも簡単に Docker コンテナーをホストできる Azure Container Instances でやってみることにしました!
なお、AWS には Fargate という同じくお手軽にコンテナーを実行できるサービスがありまして、こっちは Azure Container Instances よりも高機能で扱いやすいです。今すぐ .NET Core の gRPC をコンテナー化してクラウドにホストしたいということでしたら、AWS Fargate が楽そうに思います。
Docker Desktop community のインストール
前回の記事で作成した gRPC サーバーアプリケーションを Linux コンテナーのイメージ化を行います。 この記事ではイメージ作成作業を Windows 環境で行いますので Docker Desktop のインストールが必要です。 Docker Desktop for Windows のインストールには前提条件があります。以下は必要な前提条件です。
- Windows 10 Pro (64bit)
- Hyper-V(Windows の機能の有効化)
- CPUの仮想化機能が有効になっていること
Docker Desktop for Windows
https://hub.docker.com/
インストールできたら docker -v コマンドで確認します。
D:\Work\dotnetcore\MyFirstGrpc\src\MyFirstGrpc.Grpc>docker -v Docker version 19.03.2, build 6a30dfc
※これ以降のコマンドはすべて前回の記事で作成した gRPC サーバーアプリケーションをカレントディレクトリにして実行してください。
動作確認したバージョンは以下の通りです。
サーバー証明書の準備
クラウド上で gRPC を実行するためにはサーバー証明書が必要です。 Visual Studioでデバッグ実行する際は勝手にローカルPCに自己署名証明書をインストールしてくれましたが、Azure にデプロイする場合は、コンテナーのイメージ内に証明書が必要になりますし、DNS 名も localhost では都合が悪いため、自己署名証明書を実際にホストする名前で作成してみたいと思います。
自己署名証明書は PowerShell で簡単に作成することができます。 Windows のスタートボタンを右クリックして Windows PowerShell を起動して以下のコマンドを実行してください。
New-SelfSignedCertificate -CertStoreLocation Cert:\CurrentUser\My ` -DnsName "ここに自分が命名したい名前を入れてください.japaneast.azurecontainer.io" ` -KeyExportPolicy Exportable -KeyLength 2048 ` -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1") ` -NotAfter (Get-Date).AddYears(5)
このコマンドで、有効期限が5年間の自己署名サーバー証明書を作成しています。
DnsName には Azure Container Instances で使用可能な名前をつかっています。すでに存在する名前を使えませんのでかぶりにくい名前を命名してください。
なお、引数の詳細については New-SelfSignedCertificate コマンドのヘルプをご覧いただければと思います。
コマンドを実行すると個人の証明書の中に証明書が作成されます。
証明書の管理画面はタスクバーの検索から「証明書」と検索すれば出てきます。
以下のように証明書が作成されています。
この証明書を「信頼されたルート証明機関」...「証明書」にドラッグアンドドロップしてください。
警告が出ますが問題ありません。「はい」と答えましょう。
「信頼されたルート証明機関」内に証明書が移動できたら、その証明書をダブルクリックして「詳細」を選び「ファイルにコピー」をクリックします。
証明書のエクスポートウィザードがはじまります。
ここでは秘密キーのエクスポートを選択してください。
この画面はデフォルトのままでOKです。
秘密キーのパスワードを入力する必要があります。
通常は十分に長い文字列を指定しますが、一旦 password でOKです。
ファイルを置く場所は前の記事で作成した gRPC サーバーアプリケーションのフォルダ直下にします。
ファイル cert.pfx が生成されていることを確認してください。
gRPCサーバーに証明書を設定
gRPC サーバーアプリケーションのプログラムを調整して、先ほど発行した自己署名証明書を Kestrel が使用し、Dockerのイメージに証明書が入るようにする必要があります。
最初に、Visual Studioのソリューションエクスプローラーで cert.pfx のプロパティから「出力ディレクトリにコピー」を「新しい場合はコピーする」にします。
つづいて、Program.csを開いて、以下の設定を追加します。
webBuilder.ConfigureKestrel(options => { options.Listen(IPAddress.Any, 443, listenOptions => { listenOptions.Protocols = HttpProtocols.Http2; listenOptions.UseHttps("cert.pfx", "password"); // ここに証明書のエクスポートで入力したパスワードを指定します }); });
※ここでは便宜上パスワードをハードコーディングしていますが、パスワードのような重要な情報をソースコード内にハードコーディングしてはいけません。コンテナー実行環境では環境変数を通じて引き渡すようにします。
Program.csのできあがりは以下のようになります。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Hosting; namespace MyFirstGrpc.Grpc { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } // Additional configuration is required to successfully run gRPC on macOS. // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682 public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel(options => { options.Listen(IPAddress.Any, 443, listenOptions => { listenOptions.Protocols = HttpProtocols.Http2; listenOptions.UseHttps("cert.pfx", "password"); }); }); webBuilder.UseStartup<Startup>(); }); } }
Dockerfile を作成
引き続き、gRPC サーバーアプリケーションの直下に Dockerfile をというファイル名のテキストファイルを作成します。
Dockerfile に記述する内容は以下のようになります。
FROM mcr.microsoft.com/dotnet/core/aspnet:3.0.0-alpine AS base WORKDIR /app EXPOSE 443 FROM mcr.microsoft.com/dotnet/core/sdk:3.0.100 AS publish WORKDIR /src COPY . ./ RUN dotnet publish -c Release -o publish FROM base AS final WORKDIR /app COPY --from=publish /src/publish . ENTRYPOINT ["dotnet", "MyFirstGrpc.Grpc.dll"]
それぞれの命令について解説していきます。
FROM mcr.microsoft.com/dotnet/core/aspnet:3.0.0-alpine AS base WORKDIR /app EXPOSE 443
この命令は Alpine Linux(非常に軽量な Linux ディストリビューション)に ASP.NET Core の実行に必要な Runtime のみを入れた公式イメージをベースにするように指定しています。 作業ディレクトリは app で 443 ポートをリッスンすることを Docker に伝えています。
FROM mcr.microsoft.com/dotnet/core/sdk:3.0.100 AS publish WORKDIR /src COPY . ./ RUN dotnet publish -c Release -o publish
この命令は .NET Core SDK の入った Debian の公式イメージ上でプログラムを発行(パブリッシュ)しています。 .NET Core SDK の入った公式イメージはとてもサイズが大きいので、可搬性に優れていません。SDK の入った公式イメージは発行の命令に使うだけで、最終的な出力イメージには含まれません。
FROM base AS final WORKDIR /app COPY --from=publish /src/publish . ENTRYPOINT ["dotnet", "MyFirstGrpc.Grpc.dll"]
ここで最初の命令で宣言した Alpine Linux の公式イメージに発行したプログラムをコピーしています。 ENTRYPOINTではコンテナー起動時に実行されるプロセスを指定します。ここで gRPC のプロジェクトのDLLを指定します。
なお、Visual Studio で新しくプロジェクトを作成する際に「Docker サポートを有効にする」を選んだ場合でもこの Dockerfile を生成してくれます。ただし、Visual Studio が作成する Dockerfile には、プロジェクト名に由来する情報がたっぷり含まれてしまうため、これらを含まないように削除して、なるべくシンプルになるように構成しています。
docker build でコンテナーのイメージを作成する
イメージを作成する準備が整いましたので、コマンドプロンプトから以下のように docker build コマンドを実行してイメージをビルドしてください。
docker build -t myfirstgrpc.grpc .
しばらくたつとビルドが完了しますので、docker images コマンドを実行して確認してください。
docker images REPOSITORY TAG IMAGE ID CREATED SIZE myfirstgrpc.grpc latest ee163a01bcf4 11 minutes ago 106MB <none> <none> d9ee996792e4 11 minutes ago 770MB mcr.microsoft.com/dotnet/core/sdk 3.0.100 4422e7fb740c 2 weeks ago 689MB mcr.microsoft.com/dotnet/core/aspnet 3.0.0-alpine 1cb704cc94a0 2 weeks ago 104MB
イメージが出来たらこれを Azure コンテナー レジストリにアップロードする必要があります。
Azure コンテナー レジストリは事前に作成する必要がありますので、Azure ポータルにアクセスして、コンテナーレジストリを追加してください。
ここではmyfirstgrpcというレジストリ名を付けました。このあとの操作でレジストリ名から付与される myfirstgrpc.azurecr.io という名前が頻繁に使われますので、ご自身で付けたレジストリ名に置き換えて実行してください。
また、Docker からイメージを簡単にアップロードできるようになりますので管理者ユーザーを「有効」を選択してください。
コンテナーレジストリーが作成できたら、ユーザー名とパスワードをメモ帳にコピーしておきましょう。
コマンドプロンプトで以下のように docker login コマンドを実行して、メモ帳にコピーしたユーザー名とパスワードでログインしてください。
docker login myfirstgrpc.azurecr.io Username: myfirstgrpc Password: Login Succeeded
つづいて Docker イメージにコンテナーレジストリーの名前に沿ったタグをつけます。
docker tag myfirstgrpc.grpc myfirstgrpc.azurecr.io/myfirstgrpc.grpc
そして、タグをつけたイメージをコンテナーレジストリーにdocker pushしてください。
docker push myfirstgrpc.azurecr.io/myfirstgrpc.grpc The push refers to repository [myfirstgrpc.azurecr.io/myfirstgrpc.grpc] 0e9991190867: Pushed d289e195f534: Pushed fb059cbc2153: Pushed f5db7d650ebc: Pushed 54fc4b3d1f37: Pushed f1b5933fe4b5: Pushed latest: digest: sha256:4bc04357c365a37504d89f167369c5676af3ed460624c1d9d5befffd8d0e0b91 size: 1578
Azureポータル上でアップロードできていることが確認できました。
リポジトリをクリックして、latestのタグを選ぶと「実行インスタンス」というメニューを選ぶことができます。
ここからでも Azure Container Instances を作成して実行することができるのですが、実はここから作ると微妙に詳細な設定ができないため注意が必要です。
この「実行インスタンス」を選ばず、Azure ポータルの「すべてのサービス」から「コンテナーインスタンス」を選択してください。
追加ボタンをクリックしてください。
コンテナー名は任意の名前で結構です。
その他は以下のような感じで入力・選択してください。地域は東日本を選んでください。
コンテナーレジストリでメモ帳にとっておいた情報も必要です。
ネットワークタブの「DNS名ラベル名」には自己署名証明書の作成時に付けた名前を設定してください。
ポートは TCP 443 のみを指定してあるようにしてください。
このような確認画面になりますので作成します。
しばらくたってコンテナーが出来上がったら、該当コンテナーのリソースに移動して、コンテナーのログを見てください。
443ポートでリスニングを開始していたら成功です。
前回の記事で作成した gRPC クライアントアプリケーションの接続先をコンテナーインスタンスの作成時設定した URL に打ち換えて実行してみましょう。無事に接続できることを確認できましたか?
まとめ
今回は .NET Core の gRPC サーバーアプリケーションを Azure Container Instances で実行するまでを手順をご紹介しました。
自己署名証明書の作成から、Docker イメージの作成、Docker イメージの Azure コンテナーレジストリへのアップロード、コンテナーインスタンスの起動と、結構な手順が必要になってしまいましたが、これを AKS で実行する場合、最後のコンテナーインスタンスの起動の手順が AKS に代わるだけ(だろうと…)思います。
.NET Core のアプリケーションをコンテナー化して、クラウドで実行するために必要な手順はほぼ共通していますので、一回やってみて覚えてしまえば、この知識は Azure でも AWS でも活用できる知識となります。
皆さんもぜひ .NET Core で gRPC サーバーアプリケーションの Docker イメージを作成し、クラウドで実行するまでの手順を体験してみていただければと思います。
それではまた。
~ecbeingでは .NET Core アプリケーションのコンテナー化に興味のある、イケてるエンジニアを大募集中です!~ careers.ecbeing.tech