NuxtとASP.NET Coreで開発したアプリケーションをコンテナ化してみた！

# はじめに
冬休みなので勉強がてらTODOアプリを開発してコンテナ化してみようと思い、苦戦しながらやってみたので備忘録代わりに記事にしてみます。

# 前提
開発環境は以下の通りです。
- Nuxt
    - Ubuntu on Windows
    - node: v18.12.1
    - Nuxt: 2.15.8
- ASP.NET Core
    - Windows 10
    - .NET 5.0

# Dockerfileを作成する
## Nuxt編
ルートディレクトリ直下に、以下の内容のDockerfileを作成しました。
```dockerfile
FROM node:18.12.1-alpine3.16 AS build

WORKDIR /app

WORKDIR /app
COPY . ./
RUN yarn install
RUN yarn build

FROM nginx:stable-alpine as production
COPY --from=build /app/dist/ /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```

同じくルート直下に`.dockerignore`を作成し、一部ファイルをイメージ作成の対象から除外します。
```.dockerignore
node_modules
dist
Dockerfile*
```

また、`package.json`を以下のように修正しました。
```json:package.json
  "scripts": {
    "dev": "NODE_OPTIONS='--openssl-legacy-provider' nuxt",
    "build": "NODE_OPTIONS='--openssl-legacy-provider' nuxt build",
    "start": "NODE_OPTIONS='--openssl-legacy-provider' nuxt start",

```
nuxtコマンドの前に、`NODE_OPTIONS='--openssl-legacy-provider'`を追加しています。
これは、opensslのバージョンの互換性の影響でこのコマンド無しにビルドすると下記のエラーが発生するため、nuxtコマンド実行前に実行するようにしています。
```
RpcIpcMessagePortClosedError: Cannot send the message - the message port has been closed for the process 9800.
```

Dockerfileについて説明します。
まずビルドステージのベースイメージに`node:18.12.1-alpine3.16`を使用します。
いったんすべてのファイルをapp配下にコピーし、`yarn build`を実行してビルドファイルを生成します。
実際にコンテナとして作成されるイメージにはビルドファイルのみを配置したいので、ビルドステージから成果物のみをコピーし（成果物は/app/dist/下に作成されます）、nginxが参照するフォルダ配下(/usr/share/nginx/html)に配置します。
80番ポートを開放し、nginxを実行します。

## .NET編
ほぼ公式のまんまですが、.slnファイルと同じディレクトリに以下のdockerfileを作成します。
```dockerfile
# https://hub.docker.com/_/microsoft-dotnet
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /source

# copy csproj and restore as distinct layers
COPY *.sln .
COPY TodoApp/*.csproj ./TodoApp/
RUN dotnet restore

# copy everything else and build app
COPY TodoApp/. ./TodoApp/
WORKDIR /source/TodoApp
RUN dotnet publish -c release -o /app --no-cache --no-restore

# final stage/image
FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
COPY --from=build /app ./
ENTRYPOINT ["dotnet", "TodoApp.dll"]
```
`.dockerignore`には、以下のように記載しました。
```.dockerignore
# directories
**/bin/
**/obj/
**/out/

# files
Dockerfile*
**/*.trx
**/*.md
**/*.ps1
**/*.cmd
**/*.sh
```
dockerfileについて説明します。
まず`.sln`ファイルや`.csproj`ファイルをコピーし、`dotnet restore`を実行します。
`dotnet restore`はプロジェクトの依存関係とツールを復元するコマンドです。
そしてディレクトリを移動し、`dotnet publish`コマンドを実行します。
`--no-restore`を指定することで、`dotnet publish`が実行されるときに暗黙的に`dotnet restore`が実行されるのを防ぎます。

最後のステージで、生成された`TodoApp.dll`を実行します。

# コンテナ化する
dockerfileが作成できたので、それぞれのイメージをビルドし、実際にコンテナ化します。
それぞれのdockerfileがあるディレクトリに移動し、以下のコマンドを実行します。

***Nuxt***
```
docker build -t nuxt-app:v1 .
```

***.NET***
```
docker build -t dotnet-app:v1 .
```

`docker images`コマンドを実行して、イメージが作成できているか確認します。
作成できていたら、以下のコマンドでコンテナを起動します。

***Nuxt***
```
docker run -it -p 8080:80 nuxt-app:v1
```

***.NET***
```
docker run -it -p 5000:5000 dotnet-app:v1
```

これでコンテナを起動できます！
ブラウザで`http://localhost:8080`にアクセスすると、作成したNuxtアプリが表示されるはずです。

# ハマったところ
## .NETのリクエストを受け付けるURLの設定
.NET Coreで作成したAPIはデフォルトで`https://localhost:5001`をリッスンしますが、コンテナ内でlocalhost:5001をリッスンしていると、コンテナの外からのリクエストを受け付けられないそうです。
なので`Program.cs`内でリクエストURLを指定しました。（ハードコーディングはあまりよくないですが、今回は勉強がてらなので許してください、、）
```cs:Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    webBuilder.UseUrls("http://*:5000");

                });
```
このようにすることでコンテナ外からのリクエストを受け取ることができます。

# さいごに
コンテナ化むずかしい、、、

# 参考
- [ASP.NET Core 向けの Docker イメージ](https://learn.microsoft.com/ja-jp/aspnet/core/host-and-deploy/docker/building-net-docker-images?view=aspnetcore-7.0)
- [dotnet restore](https://learn.microsoft.com/ja-jp/dotnet/core/tools/dotnet-restore)
- [dotnet publish](https://learn.microsoft.com/ja-jp/dotnet/core/tools/dotnet-publish)
- [[NuxtJS]Dockerfileの作り方](https://codelab.website/nuxtjs-dockerfile/)
- [NuxtアプリをDocker化する](https://qiita.com/qianer-fengtian/items/6166f8118370c774adea)