Google App Engine で .NET 5 を動かす
やりたいこと
任意のDockerイメージをGoogle App Engine Flexible (以降GAEと表記) で動かす
その一例として、今回は .NET 5 (ASP.NET Core 5) のWebAPIアプリケーションをGAEで動かすことを目標とします。
きっかけ
.NET Core 3.1までは、GCPのContainer Registry (GCR) でイメージが公開されています。aspnetcoreに限らず他の言語も含め、ここにあるイメージを使うのが失敗しにくくお勧めです。
しかし.NET 5が出て半年以上経ちます[1] が、いつになってもGCRにはやってきません。しびれを切らしたので、GCRにはないイメージを使ってみようという次第です。
なお、GAEではなくCloud Runを使えばいいじゃないかという話は至極ごもっともですが、本記事ではスルーします。[2]
先に結論
ポート8080を受け付けるようにしましょう
EXPOSE 8080
ENV ASPNETCORE_URLS=http://*:8080
最小のサンプルをこちらに置きました。
開発手順
先に熟読
app.yaml のリファレンスについては、Standard環境のものを参照しないよう注意です。
筆者の環境
- .NET 5 (5.0.400)
- 任意: Visual Studio 16.11.1
- 他のIDE・エディタや、コマンドラインだけでも十分可能です。
- 任意: Docker for Windows
- GAEへのデプロイの際はCloud Build にてdocker buildしてくれるので、必須ではありません。ローカルで試したいなら入れましょう。
プロジェクト作成
新規プロジェクトを作成します。Visual Studioであれば以下のような選択をして進めました。
もしコマンドラインで行う場合は以下のようにします。オプションは下記URLを参照します。https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-new-sdk-templates#webapi
dotnet new webapi --framework net5.0 --auth None --no-https
app.yaml
GAEの設定ファイルです。ファイルを新規作成します。再掲ですがリファレンスを参考に設定します。
runtime: custom
env: flex
service: dotnet5-sample
resources:
cpu: 2
memory_gb: 4
Dockerfile
私のDocker力が低いだけかもしれませんが、Dockerfileとapp.yamlをどこに置けばよいか、悩ましい問題があります。
- app.yaml と Dockerfile は、同じディレクトリ階層に置いておかないとダメらしい。
- (Visual Studioの場合) 特定のWebAPIプロジェクトの下にDockerfileが自動生成されるが、そうすると上記と相まって、他のプロジェクト参照をしているようなケースに適用しづらい (上の階層の
COPY
はできない)。かといってDockerfileの場所を動かすと、Visual Studioのデバッグ設定が壊れる。
これらを踏まえつつ、2つのDockerfile構成案を考えたので示します。Docker公式のリファレンスを参考にしています。
なおいずれにせよ、先に述べたように、ポート8080を受け付けるようにしましょう。 リファレンスに記載がありました。
ポート 8080 のリッスン
App Engine のフロントエンドは、受信リクエストをポート 8080 の適切なモジュールにルーティングします。アプリケーション コードが 8080 でリッスンしていることを確認する必要があります。
案1. コンテナ内部でビルドする
コンテナ内で dotnet publish
など実行バイナリを構築する方法です。
Visual Studioで始めた場合はDockerfileが自動で追加されていて、この雛形が既にあると思います。雛形から変えたのはコメントで示した箇所のみです。
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
# !!! 以下を変更 !!!
#EXPOSE 80
EXPOSE 8080
ENV ASPNETCORE_URLS=http://*:8080
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["AppEngineDotnet5Sample/AppEngineDotnet5Sample.csproj", "AppEngineDotnet5Sample/"]
RUN dotnet restore "AppEngineDotnet5Sample/AppEngineDotnet5Sample.csproj"
COPY . .
WORKDIR "/src/AppEngineDotnet5Sample"
RUN dotnet build "AppEngineDotnet5Sample.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "AppEngineDotnet5Sample.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY /app/publish .
ENTRYPOINT ["dotnet", "AppEngineDotnet5Sample.dll"]
そしてDockerfileとapp.yamlを、以下のように各プロジェクトのディレクトリの1つ上 (典型的には.slnと同じ場所) に置きます。COPY . .
を成功させる意図です。
Solution/
├─ AppEngineDotnet5Sample/
│ ├─ AppEngineDotnet5Sample.csproj
│
├─ ClassLibrary1/
│ ├─ ClassLibrary1.csproj
│
├─ AppEngineDotnet5Sample.sln
├─ Dockerfile
├─ app.yaml
GAEへのデプロイは、Solution
の直下にいるとして次のようなコマンドです。
gcloud app deploy app.yaml --project "your-project-name"
この方法の場合、私が未解決の問題として、Visual StudioからDockerを使用したデバッグをまだ動かせていません。Dockerfileを動かしたことが悪いのかもしれません。
案2. コンテナ外部でビルドしたバイナリをコンテナ内に送り込む
ホスト側でビルドして、DLLなどの成果物をCOPY
またはADD
する作戦です。中間言語ですから、どこでビルドしたDLLでも関係ないわけです[3]。
開発・ビルド時と実行時の環境がずれるので、稀にその齟齬に悩むことがあるかもしれません [4]。 ですが幾分楽で、Docker力が低くても運用しやすいです。手元にDocker環境を用意できないケースにもお勧めです。
Dockerfileは次のようになります。
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:5.0
COPY bin/Release/net5.0/publish app/
WORKDIR /app
EXPOSE 8080
ENV ASPNETCORE_URLS=http://*:8080
ENTRYPOINT ["dotnet", "AppEngineDotnet5Sample.dll"]
上記では bin/Release/net5.0/publish
に発行済みバイナリが置かれている前提になっています。これを準備しましょう。デプロイ処理をまとめたスクリプトを作ります。何でも良いですがここではPowerShellにしています。
dotnet clean --configuration Release
dotnet build --configuration Release
dotnet publish --no-build --configuration Release
gcloud app deploy app.yaml --project "your-project-name"
dotnet publish
によって bin/Release/net5.0/publish
にバイナリが固められ、それをコンテナ内にコピーすることができました。
ディレクトリ階層は以下のようにしました。
Solution/
├─ AppEngineDotnet5Sample/
│ ├─ AppEngineDotnet5Sample.csproj
│ ├─ Dockerfile
│ ├─ app.yaml
│ ├─ deploy.ps1
│
├─ ClassLibrary1/
│ ├─ ClassLibrary1.csproj
│
├─ AppEngineDotnet5Sample.sln
GAEへのデプロイは、Solution/AppEngineDotnet5Sample
にてデプロイスクリプトを実行するだけです。
PS1> .\deploy.ps1
-
Microsoftの開発ブログ: https://devblogs.microsoft.com/dotnet/announcing-net-5-0/ ↩︎
-
Cloud Runに.NET 5のWebアプリをデプロイする方法については、.NET 5が出て間もなく公式ブログに載りました: https://cloud.google.com/blog/ja/products/gcp/net-50-google-cloud ↩︎
-
ネイティブ・OSに依存する処理があるなど凝ったことをしていない限りは ↩︎
-
最近だとICU周りではまることがあります: https://docs.microsoft.com/en-us/dotnet/core/extensions/globalization-icu ↩︎
Discussion