Docker 環境で C# ASP.NET Core Blazor Webアプリを起動する (WebAssembly)

# Docker 環境で C# ASP.NET Core Blazor Webアプリを起動する (WebAssembly)

こんにちは、@studio_meowtoon です。今回は、WSL Ubuntu 22.04 の Docker 環境で C# ASP.NET Core Blazor Web アプリケーションをコンテナとして起動する方法を紹介します。
![blazor_on_docker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/92ca2fcd-f3d7-fa05-7b46-547b21c15b5f.png)

## 目的

Windows 11 の Linux でクラウド開発します。

こちらから記事の一覧がご覧いただけます。

https://qiita.com/studio_meowtoon/items/6119ab05d8b13527efd8

## 実現すること

ローカル環境の Ubuntu の Docker 環境で、Dockerfile からビルドした C# ASP.NET Core Blazor Web アプリのカスタムコンテナを起動します。

## 技術トピック

### Dockerfile とは？  

<details>
<summary>こちらを展開してご覧いただけます。</summary>

### [Dockerfile リファレンス](https://docs.docker.jp/engine/reference/builder.html#dockerfile)

:::note info
Dockerfile は、Docker イメージをビルドするためのファイルであり、Docker コンテナの構成情報を記述するためのテキストファイルです。Dockerfile には、ベースイメージ、パッケージのインストール、環境変数の設定、ファイルのコピーなどの命令が含まれます。

また、Docker コンテナに使用するベースイメージとして、Ubuntu を選択することが一般的です。そのため、Ubuntu の知識があると Dockerfile を理解しやすくなります。
:::

</details>

## 開発環境

+ Windows 11 Home 22H2 を使用しています。  

:::note info
[WSL](https://learn.microsoft.com/ja-jp/windows/wsl/about) の Ubuntu を操作していきますので macOS の方も参考にして頂けます。  
:::

<details>

WSL (Microsoft Store アプリ版) [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/2aaa6579cb8e4fa1d874)

```sh
> wsl --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
```

Ubuntu [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/2aaa6579cb8e4fa1d874)

```sh
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
```

.NET SDK [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/d68031c91462f6808dff)

```sh
$ dotnet --list-sdks
7.0.202 [/usr/share/dotnet/sdk]
$ dotnet --version
7.0.202
```

Docker [※ こちらの関連記事からインストール方法をご確認いただけます](https://qiita.com/studio_meowtoon/items/3bceee25ac67cc73ebbe)

```sh
$ docker --version
Docker version 23.0.1, build a5ee5b1
```

</details>

:::note warn
この記事では基本的に **Ubuntu のターミナルで操作**を行います。**Vim を使用してコピペする方法**を初めて学ぶ人のために、以下の記事で手順を紹介しています。ぜひ挑戦してみてください。
:::

https://qiita.com/studio_meowtoon/items/7771ad48676926e41463

## 作成する Web アプリケーションの仕様

|No|エンドポイント|HTTPメソッド|MIME タイプ|
|:--|:--|:--|:--|
|1|/|GET|text/html|

## Hello World を表示する手順 

### C# ASP.NET Core Blazor Web アプリの作成  

こちらの関連記事で手順がご確認いただけます。

https://qiita.com/studio_meowtoon/items/16d5d58e758beed8dd27

:::note info
ここまでの手順で、ローカル環境の **Ubuntu** でアプリの **DLL ファイル**を起動することができました。
:::

### プロジェクトフォルダに移動

プロジェクトフォルダに移動します。  
※ ~/tmp/BlazorApp をプロジェクトフォルダとします。

```sh
$ cd ~/tmp/BlazorApp
```

### ライブラリの削除

静的ビルドのために、 Microsoft.AspNetCore.Components.WebAssembly.DevServer を削除します。

```sh
$ dotnet remove package Microsoft.AspNetCore.Components.WebAssembly.DevServer
```

### アプリのビルド

プロジェクトの成果物を一旦削除します。

```sh
$ rm -rf bin obj out
```

アプリケーションをビルドして出力します。
※ out/wwwroot/_framework/BlazorApp.dll が作成されます。

```sh
$ dotnet publish -c Release -o out
```

:::note info
ここまでの手順で、ローカル環境の **Ubuntu** に **DLL ファイル形式**のアプリをビルドすることができました。
:::

### コンテナイメージの作成

:::note warn
**WebAssembly** として**静的ビルド**した Web アプリをホストするため、**Web サーバー**として **Nginx** を使用します。
:::

nginx.conf を作成します。

```sh
$ vim nginx.conf
```

ファイルの内容

```json:nginx.conf
events { }
http {
    include mime.types;
    server {
        listen 80;
        location / {
            root /usr/share/nginx/html;
            try_files $uri $uri/ /index.html = 404;
        }
    }
}
```

Dockerfile を作成します。

```sh
$ vim Dockerfile
```

ファイルの内容

```Dockerfile:Dockerfile
# from the base image of a nginx on alpine linux.
FROM nginx:alpine

# create a work dir.
WORKDIR /usr/share/nginx/html

# copy a wasm app.
COPY /out/wwwroot .

# copy an nginx conf.
COPY nginx.conf /etc/nginx/nginx.conf
```

Docker デーモンを起動します。

```sh
$ sudo service docker start
 * Starting Docker: docker    [ OK ]
```

:::note warn
Docker 環境をお持ちでない場合は、以下の関連記事から Docker Engine のインストール手順をご確認いただけます。
:::

https://qiita.com/studio_meowtoon/items/3bceee25ac67cc73ebbe

コンテナイメージをビルドします。

```sh
$ docker build --no-cache -t app-hello-blazor-wasm .
```

コンテナイメージを確認します。

```sh
$ docker images | grep app-hello-blazor-wasm
REPOSITORY              TAG       IMAGE ID       CREATED          SIZE
app-hello-blazor-wasm   latest    fa24acc86d22   9 seconds ago    55.9MB
```

:::note info
ここまでの手順で、ローカル環境の **Docker** にアプリの**カスタムコンテナイメージ**をビルドすることができました。
:::

### コンテナを起動

ローカルでコンテナを起動します。

```sh
$ docker run --rm \
    --name app-local \
    -p 8080:80 app-hello-blazor-wasm
```

:::note info
ここまでの手順で、ローカル環境の **Docker** でアプリの**カスタムコンテナ**を起動することができました。
:::

### コンテナの動作確認

Web ブラウザで以下の URL にアクセスします。

```sh
http://localhost:8080/
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/cdc0e743-d70d-0e25-6ac5-6ffb6cebda85.png)

:::note info
Web ブラウザに **Hello World!"** と表示されました。 
::: 

コンテナの状態を確認してみます。

```sh
$ docker ps
CONTAINER ID   IMAGE                   COMMAND                   CREATED         STATUS         PORTS                                   NAMES
6af2bd419965   app-hello-blazor-wasm   "/docker-entrypoint.…"   5 minutes ago   Up 5 minutes   0.0.0.0:8080->80/tcp, :::8080->80/tcp   app-local
```

### コンテナに接続

別ターミナルからコンテナに接続します。  
※ コンテナを停止するときは ctrl + C を押します。

```sh
$ docker exec -it app-local sh
```

コンテナに接続後にディレクトリを確認します。
※ コンテナから出るときは ctrl + D を押します。

```
# pwd
/usr/share/nginx/html
# ls -lah
total 32K
drwxr-xr-x    1 root     root        4.0K Mar 27 05:08 .
drwxr-xr-x    1 root     root        4.0K Feb 11 10:03 ..
-rw-r--r--    1 root     root         497 Dec 13 18:23 50x.html
drwxr-xr-x    2 root     root       12.0K Mar 27 05:08 _framework
-rw-r--r--    1 root     root         248 Mar 27 04:21 index.html
```

<details>
<summary>_framework ディレクトリの中身を確認します。</summary>

```
# cd _framework
# apk add tree
# tree
.
├── BlazorApp.dll
├── BlazorApp.dll.br
├── BlazorApp.dll.gz
├── BlazorApp.pdb.gz
├── Microsoft.AspNetCore.Components.Web.dll
├── Microsoft.AspNetCore.Components.Web.dll.br
├── Microsoft.AspNetCore.Components.Web.dll.gz
├── Microsoft.AspNetCore.Components.WebAssembly.dll
├── Microsoft.AspNetCore.Components.WebAssembly.dll.br
├── Microsoft.AspNetCore.Components.WebAssembly.dll.gz
├── Microsoft.AspNetCore.Components.dll
├── Microsoft.AspNetCore.Components.dll.br
├── Microsoft.AspNetCore.Components.dll.gz
├── Microsoft.Extensions.Configuration.Abstractions.dll
├── Microsoft.Extensions.Configuration.Abstractions.dll.br
├── Microsoft.Extensions.Configuration.Abstractions.dll.gz
├── Microsoft.Extensions.Configuration.Json.dll
├── Microsoft.Extensions.Configuration.Json.dll.br
├── Microsoft.Extensions.Configuration.Json.dll.gz
├── Microsoft.Extensions.Configuration.dll
├── Microsoft.Extensions.Configuration.dll.br
├── Microsoft.Extensions.Configuration.dll.gz
├── Microsoft.Extensions.DependencyInjection.Abstractions.dll
├── Microsoft.Extensions.DependencyInjection.Abstractions.dll.br
├── Microsoft.Extensions.DependencyInjection.Abstractions.dll.gz
├── Microsoft.Extensions.DependencyInjection.dll
├── Microsoft.Extensions.DependencyInjection.dll.br
├── Microsoft.Extensions.DependencyInjection.dll.gz
├── Microsoft.Extensions.Logging.Abstractions.dll
├── Microsoft.Extensions.Logging.Abstractions.dll.br
├── Microsoft.Extensions.Logging.Abstractions.dll.gz
├── Microsoft.Extensions.Logging.dll
├── Microsoft.Extensions.Logging.dll.br
├── Microsoft.Extensions.Logging.dll.gz
├── Microsoft.Extensions.Options.dll
├── Microsoft.Extensions.Options.dll.br
├── Microsoft.Extensions.Options.dll.gz
├── Microsoft.Extensions.Primitives.dll
├── Microsoft.Extensions.Primitives.dll.br
├── Microsoft.Extensions.Primitives.dll.gz
├── Microsoft.JSInterop.WebAssembly.dll
├── Microsoft.JSInterop.WebAssembly.dll.br
├── Microsoft.JSInterop.WebAssembly.dll.gz
├── Microsoft.JSInterop.dll
├── Microsoft.JSInterop.dll.br
├── Microsoft.JSInterop.dll.gz
├── System.Collections.Concurrent.dll
├── System.Collections.Concurrent.dll.br
├── System.Collections.Concurrent.dll.gz
├── System.Collections.dll
├── System.Collections.dll.br
├── System.Collections.dll.gz
├── System.ComponentModel.dll
├── System.ComponentModel.dll.br
├── System.ComponentModel.dll.gz
├── System.Linq.dll
├── System.Linq.dll.br
├── System.Linq.dll.gz
├── System.Memory.dll
├── System.Memory.dll.br
├── System.Memory.dll.gz
├── System.Net.Http.dll
├── System.Net.Http.dll.br
├── System.Net.Http.dll.gz
├── System.Net.Primitives.dll
├── System.Net.Primitives.dll.br
├── System.Net.Primitives.dll.gz
├── System.Private.CoreLib.dll
├── System.Private.CoreLib.dll.br
├── System.Private.CoreLib.dll.gz
├── System.Private.Uri.dll
├── System.Private.Uri.dll.br
├── System.Private.Uri.dll.gz
├── System.Runtime.InteropServices.JavaScript.dll
├── System.Runtime.InteropServices.JavaScript.dll.br
├── System.Runtime.InteropServices.JavaScript.dll.gz
├── System.Runtime.dll
├── System.Runtime.dll.br
├── System.Runtime.dll.gz
├── System.Text.Encodings.Web.dll
├── System.Text.Encodings.Web.dll.br
├── System.Text.Encodings.Web.dll.gz
├── System.Text.Json.dll
├── System.Text.Json.dll.br
├── System.Text.Json.dll.gz
├── blazor.boot.json
├── blazor.boot.json.br
├── blazor.boot.json.gz
├── blazor.webassembly.js
├── blazor.webassembly.js.br
├── blazor.webassembly.js.gz
├── dotnet.7.0.4.11onvycki2.js
├── dotnet.7.0.4.11onvycki2.js.br
├── dotnet.7.0.4.11onvycki2.js.gz
├── dotnet.timezones.blat
├── dotnet.timezones.blat.br
├── dotnet.timezones.blat.gz
├── dotnet.wasm
├── dotnet.wasm.br
├── dotnet.wasm.gz
├── icudt.dat
├── icudt.dat.br
├── icudt.dat.gz
├── icudt_CJK.dat
├── icudt_CJK.dat.br
├── icudt_CJK.dat.gz
├── icudt_EFIGS.dat
├── icudt_EFIGS.dat.br
├── icudt_EFIGS.dat.gz
├── icudt_no_CJK.dat
├── icudt_no_CJK.dat.br
└── icudt_no_CJK.dat.gz
```

</details>

top コマンドで状況を確認します。

```
# top
Mem: 6738496K used, 1210924K free, 134652K shrd, 149600K buff, 5228784K cached
CPU:   0% usr   0% sys   0% nic  99% idle   0% io   0% irq   0% sirq
Load average: 0.00 0.02 0.03 1/410 37
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
   30     1 nginx    S     7816   0%   2   0% nginx: worker process
    1     0 root     S     7576   0%   7   0% nginx: master process nginx -g daemon off;
   31     0 root     S     1692   0%   4   0% sh
   37    31 root     R     1620   0%  10   0% top
```

コンテナの情報を表示してみます。

```
# cat /etc/*-release
3.17.2
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.17.2
PRETTY_NAME="Alpine Linux v3.17"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
```

:::note warn
このコンテナは Alpine Linux をベースに作成されています。つまり、Alpine Linux と同じように扱うことができます。
:::

## まとめ

WSL Ubuntu の Docker 環境で、Dockerfile からビルドした C# ASP.NET Core Blazor Web アプリのカスタムコンテナを起動することができました。

:::note warn
クラウド開発においては、**Dockerfile の理解**は重要です。自動ビルドツールもありますが、手動で書く必要があるケースもあります。**Ubuntu** を使うと **Linux の知識**も身に付きます。最初は難しく感じるかもしれませんが、徐々に進めていけば自信を持って書けるようになります。
:::

どうでしたか？ WSL Ubuntu で、C# ASP.NET Core Blazor Web アプリケーションを Docker 環境でコンテナとして手軽に起動することができます。ぜひお試しください。今後も .NET の開発環境などを紹介していきますので、ぜひお楽しみにしてください。

## 推奨コンテンツ

https://qiita.com/studio_meowtoon/items/5115205eaa1bfd786df3