はじめに
冬休みなので勉強がてらTODOアプリを開発してコンテナ化してみようと思い、苦戦しながらやってみたので備忘録代わりに記事にしてみます。
前提
開発環境は以下の通りです。
- Nuxt
- Ubuntu on Windows
- node: v18.12.1
- Nuxt: 2.15.8
- ASP.NET Core
- Windows 10
- .NET 5.0
Dockerfileを作成する
Nuxt編
ルートディレクトリ直下に、以下の内容の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
を作成し、一部ファイルをイメージ作成の対象から除外します。
node_modules
dist
Dockerfile*
また、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を作成します。
# 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
には、以下のように記載しました。
# 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を指定しました。(ハードコーディングはあまりよくないですが、今回は勉強がてらなので許してください、、)
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseUrls("http://*:5000");
});
このようにすることでコンテナ外からのリクエストを受け取ることができます。
さいごに
コンテナ化むずかしい、、、