概要
ASP.NET CoreのサンプルWebアプリケーションを題材として、docker-compose による Dockerアプリケーション の動作環境を構築する例を紹介します。
更新履歴
- 2023/07/01:各種、ソフトウェア・ミドルウェアのバージョンを更新しました。
環境
- Windows11
- Visual Studio 2022
- ASP.NET Core (.NET6)
- Docker for Windows
サンプルアプリケーション
題材にしたサンプルアプリケーションです。
https://github.com/tYoshiyuki/dotnet-core-web-sample
単純なToDoのWebアプリケーションです。トランザクションデータはデータベース (SQL Server) に保存しています。
これを Docker を利用して コンテナ化 してみましょう。
システム構成
今回は、Docker for Windows を利用して、ローカルPC上にDocker環境を構築します。ローカル開発時は、IIS Express と LocalDB を利用して開発を行いましたが、これを変更します。フロントエンドは Nginx、データベースは SQL Express の Dockerイメージを利用し、アプリケーションのコンテナを含めて3つのコンテナで構築します。構成イメージは下記の通りです。
通常、ASP.NET Core アプリケーションを IIS でホストする場合には、ASP.NET Coreのミドルウェアにより、アプリケーションはIISと統合的に動作します。Nginxをリバースプロキシとしてフロントエンドに構築する場合は、ASP.NET Core アプリケーションは Kestrel で動作させておき、そこに対してHTTP通信を転送します。
Dockerfile
1. ASP.NET Core アプリケーション
まずは、ASP.NET Core アプリケーションをコンテナ化するために、Dockerfileを作成します。Visual Studio > プロジェクト > 右クリック Dockerサポート > ターゲットOS Linux
から追加できる Dockerfile をベースにして編集を行います。
FROM mcr.microsoft.com/dotnet/aspnet:6.0.1-bullseye-slim AS base
WORKDIR /app
# EXPOSE 80
# EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:6.0.101-bullseye-slim AS build
WORKDIR /src
COPY ["DotNetCoreWebSample.Web.csproj", "./"]
RUN dotnet restore "DotNetCoreWebSample.Web.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "DotNetCoreWebSample.Web.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "DotNetCoreWebSample.Web.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENV ASPNETCORE_URLS http://*:5000
ENTRYPOINT ["dotnet", "DotNetCoreWebSample.Web.dll"]
Dockerfileの解説についてはここでは省略しますが、デフォルトでマルチステージビルドを行い、Dockerイメージサイズを削減するような設定になっています。ポイントとしては [ENV ASPNETCORE_URLS http://*:5000]
で、Kestrelが待ち受けを行うポート番号を指定している部分です。後々、Nginxでリクエストを転送する際における、送信先ポート番号になります。
また、ソースコードも公式ドキュメントに従い、若干ですが変更を行います。
https://docs.microsoft.com/ja-jp/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ・・・一部省略・・・
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
// ・・・一部省略・・・
リバースプロキシを経由した際における、X-Forwarded-For・X-Forwarded-Protoヘッダの転送を行うようにします。
これは、IIS上でASP.NET Coreアプリケーションを動作させた場合は、既定で有効になっているので注意が必要です。
公式ドキュメントにも記載があるため、目を通しておくことをお勧めします。
https://docs.microsoft.com/ja-jp/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-3.1
2. Nginx
次にNginx用の設定を作成します。
FROM nginx:latest
COPY ./nginx.conf /etc/nginx/nginx.conf
COPY ./site.conf.template /etc/nginx/templates/site.conf.template
CMD [ "nginx", "-g", "daemon off;" ]
worker_processes 1;
events { worker_connections 1024; }
http {
sendfile on;
include /etc/nginx/conf.d/site.conf;
}
upstream web-app {
server ${BACKEND_HOST};
}
server {
listen 80;
server_name $hostname;
location / {
proxy_pass http://web-app;
proxy_redirect off;
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $server_name;
}
}
nginx 1.19 の Dockerイメージ より、環境変数を template を用いることで注入することが出来るようになりました。
この機能を用いて、アプリケーションサーバの宛先 (BACKEND_HOST) を外部注入可能にしておきます。これに関しては、後ほど docker-compose.yml で解説を行います。
3. SQL Server Express
最後に SQL Server Express です。サンプルアプリケーションの機能としてはLocalDBで十分なのですが、公式にDockerイメージが存在しないようなので、SQL Server Expressを利用します。尚、実際にはDBデータを永続化する設定が必要ですが、今回は省略しています。
FROM mcr.microsoft.com/mssql/server:2017-latest-ubuntu
docker-compose.yml
作成した各コンテナを docker-compose を利用して起動してみましょう。
version: '3.4'
services:
app:
build:
context: ./DotNetCoreWebSample.Web
dockerfile: Dockerfile
environment:
ConnectionStrings__DefaultConnection: "Server=sqlexpress;Database=master;User ID=sa;Password=P@ssw0rd;initial catalog=dotnetcorewebsample;MultipleActiveResultSets=True;App=EntityFramework;"
expose:
- 5000
depends_on:
- sqlexpress
sqlexpress:
build:
context: ./docker/sqlexpress
dockerfile: Dockerfile
environment:
MSSQL_PID: "Express"
ACCEPT_EULA: "Y"
SA_PASSWORD: "P@ssw0rd"
ports:
- 1433:1433
web:
build:
context: ./docker/nginx
environment:
BACKEND_HOST: "app:5000"
ports:
- 80:80
記載が長いですが、何点かポイントを解説します。
まず、 ConnectionStrings__DefaultConnection: "Server=sqlexpress;Database=master;User ID=sa;Password=P@ssw0rd;initial catalog=dotnetcorewebsample;MultipleActiveResultSets=True;App=EntityFramework;"
の部分は、SQL Server Express への接続先情報を設定しています。コンテナ起動時に appsettings.json で設定している内容を上書きします。ネストされたJSONの要素 (ConnectionStrings > DefaultConnection) を表現する場合に、アンダーバー2つ (__) を使用します。
BACKEND_HOST: "app:5000"
の部分で、ASP.NET Core アプリケーションへの送信先を設定しています。docker-composeでは、サービス名で各コンテナの宛先を解決出来るため、上記のような設定になります。
docker-composeコマンドを実行し、各コンテナを起動します。
docker-compose up -d
実行後 http://localhost
でアクセスし、動作を確認します。
まとめ
docker-composeを用いて、Dockerアプリケーションを構築する例を紹介させていただきました。
クロスプラットフォームを特徴とする .NET Core なので、Dockerを用いた開発も盛り上がると良いなと思います。
ASP.NET Core と Azure で開発する場合、Web Apps や SQL Database といった優れた PaaS を利用することで、手軽にスケーラブルなアプリケーションを構築することが出来ます。そのため、Docker のようなコンテナ技術を利用するシーンは、あまり多く無いかも知れません。
ですが、ASP.NET Coreのアプリケーションをコンテナ化することで、AWS や GCP といった別クラウドサービスへの転用や、Kubernetesに代表される コンテナ オーケストレーション の恩恵を受けることが出来るようになります。システムアーキテクチャの選択肢として、考慮する価値は十分にあるのではないのでしょうか。