🦒

Google App Engine で .NET 5 を動かす

2021/08/22に公開

やりたいこと

任意のDockerイメージをGoogle App Engine Flexible (以降GAEと表記) で動かす

その一例として、今回は .NET 5 (ASP.NET Core 5) のWebAPIアプリケーションをGAEで動かすことを目標とします。

きっかけ

.NET Core 3.1までは、GCPのContainer Registry (GCR) でイメージが公開されています。aspnetcoreに限らず他の言語も含め、ここにあるイメージを使うのが失敗しにくくお勧めです。
https://console.cloud.google.com/gcr/images/google-appengine/global/aspnetcore

しかし.NET 5が出て半年以上経ちます[1] が、いつになってもGCRにはやってきません。しびれを切らしたので、GCRにはないイメージを使ってみようという次第です。

なお、GAEではなくCloud Runを使えばいいじゃないかという話は至極ごもっともですが、本記事ではスルーします。[2]

先に結論

ポート8080を受け付けるようにしましょう

EXPOSE 8080
ENV ASPNETCORE_URLS=http://*:8080

最小のサンプルをこちらに置きました。
https://github.com/shimat/AppEngineDotnet5Sample

開発手順

先に熟読

https://cloud.google.com/appengine/docs/flexible
https://cloud.google.com/appengine/docs/flexible/custom-runtimes/build?hl=ja
https://cloud.google.com/appengine/docs/flexible/dotnet/reference/app-yaml?hl=ja

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の設定ファイルです。ファイルを新規作成します。再掲ですがリファレンスを参考に設定します。

app.yaml
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が自動で追加されていて、この雛形が既にあると思います。雛形から変えたのはコメントで示した箇所のみです。

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 --from=publish /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は次のようになります。

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にしています。

deploy.ps1
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
脚注
  1. Microsoftの開発ブログ: https://devblogs.microsoft.com/dotnet/announcing-net-5-0/ ↩︎

  2. Cloud Runに.NET 5のWebアプリをデプロイする方法については、.NET 5が出て間もなく公式ブログに載りました: https://cloud.google.com/blog/ja/products/gcp/net-50-google-cloud ↩︎

  3. ネイティブ・OSに依存する処理があるなど凝ったことをしていない限りは ↩︎

  4. 最近だとICU周りではまることがあります: https://docs.microsoft.com/en-us/dotnet/core/extensions/globalization-icu ↩︎

Discussion