コンテナーを利用した .NET 開発
前提条件
.NET アプリケーションのコンテナー化 を完了していること。
概要
本節ではコンテナー化したアプリケーションに対する開発環境の構築方法を学びます。 ここでは以下を行います。
- ローカルデータベースを追加してデータを保存します。
- Compose の設定を通じて、コード編集および保存とともに、実行中の Compose サービスが自動的に更新されるようにします。
- .NET コア SDK ツールとその依存パッケージを含んだ開発用コンテナーを生成します。
アプリケーションの更新
この節では docker-dotnet-sample
リポジトリ内の別ブランチを利用します。
そこには修正された .NET アプリケーションが含まれています。
その修正アプリケーションとは、
.NET アプリケーションのコンテナー化 にてクローンしたリポジトリ内の add-db
ブランチにあります。
修正されたコードを入手するために、add-db
ブランチをチェックアウトします。
.NET アプリケーションのコンテナー化 において加えた修正は、ここでスタッシュしておきます。
端末上から docker-dotnet-sample
ディレクトリに移動して以下のコマンドを実行します。
それまでの変更をスタッシュします。
$ git stash -u
修正アプリケーションが含まれている新たなブランチをチェックアウトします。
$ git checkout add-db
add-db
ブランチにおいて修正されているのは .NET アプリケーションのみです。
Docker アセットについてはまだ修正されていません。
docker-dotnet-sample
ディレクトリにて以下を実行します。
├── docker-dotnet-sample/
│ ├── .git/
│ ├── src/
│ │ ├── Data/
│ │ ├── Models/
│ │ ├── Pages/
│ │ ├── Properties/
│ │ ├── wwwroot/
│ │ ├── appsettings.Development.json
│ │ ├── appsettings.json
│ │ ├── myWebApp.csproj
│ │ └── Program.cs
│ ├── tests/
│ │ ├── tests.csproj
│ │ ├── UnitTest1.cs
│ │ └── Usings.cs
│ ├── .dockerignore
│ ├── .gitignore
│ ├── compose.yaml
│ ├── Dockerfile
│ ├── README.Docker.md
│ └── README.md
ローカルデータベースの追加とデータ保存
コンテナーでは、ローカルサービスとしてたとえばデータベースを構築することができます。
本節では compose.yaml
ファイルを編集して、データベースサービスを追加してデータ保存のためのボリュームを定義します
IDE またはテキストエディターを使って compose.yaml
ファイルを開きます。
中身を見てみれば PostgreSQL データベースとボリュームに関する命令文が、すでにコメントアウトされて書かれているのがわかります。
IDE またはテキストエディターを使って docker-dotnet-sample/src/appsettings.json
ファイルを開きます。
そこにはデータベース情報に関する接続文字列が示されています。
compose.yaml
ファイルにはその情報がすでにあるのですが、それはコメントアウトされています。
そこで compose.yaml
ファイル内のそのデータベース命令文のコメントをはずします。
修正した compose.yaml
ファイルは以下です。
services:
server:
build:
context: .
target: final
ports:
- 8080:80
depends_on:
db:
condition: service_healthy
db:
image: postgres
restart: always
user: postgres
secrets:
- db-password
volumes:
- db-data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
expose:
- 5432
healthcheck:
test: ["CMD", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:
secrets:
db-password:
file: db/password.txt
メモ
Compose ファイル内の各命令文の詳細は Compose ファイルリファレンス を参照してください。
Compose を使ってアプリケーションを実行するにあたって、まずこの Compose ファイルが secrets
を利用しており、データベースパスワードを password.txt
というファイルに指定している点を確認してください。
そのファイルはソースリポジトリには含まれていないため、ここで生成する必要があります。
docker-dotnet-sample
ディレクトリにおいて新たに db
というディレクトリを生成し、さらにその中に password.txt
という名前のファイルを IDE またはテキストエディターを使って生成します。
そして以下のようなパスワードを記述します。
パスワードは 1 行内に記述する必要があり、これ以外の行を含めてはなりません。
example
password.txt
ファイルを保存して閉じます。
docker-dotnet-sample
ディレクトリ内は以下のようになったはずです。
├── docker-dotnet-sample/
│ ├── .git/
│ ├── db/
│ │ └── password.txt
│ ├── src/
│ ├── tests/
│ ├── .dockerignore
│ ├── .gitignore
│ ├── compose.yaml
│ ├── Dockerfile
│ ├── README.Docker.md
│ └── README.md
以下のコマンドを実行してアプリケーションを起動します。
$ docker compose up --build
ブラウザーを開いて
http://localhost:8080 にアクセスしてアプリケーションを確認します。
単純なウェブアプリケーションが起動し、Student name is
という文字列が表示されたはずです。
アプリケーションは名前を表示していません。 単純にデータベースの中身が空だからです。 このアプリケーションでは、データベースにアクセスしてレコードを追加しておくことが必要であったわけです。
データベースへのレコード追加
このサンプルアプリケーションにおいては、データベースに直接アクセスしてサンプルレコードを生成することが必要です。
データベースコンテナー内にてコマンド実行するには docker exec
コマンドを利用します。
このコマンドを実行する前に、まずデータベースコンテナーの ID を取得します。
新たな端末画面を開いて、以下のコマンドを実行し起動中のコンテナー一覧を確認します。
$ docker container ls
以下のような出力が得られます。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cb36e310aa7e docker-dotnet-server "dotnet myWebApp.dll" About a minute ago Up About a minute 0.0.0.0:8080->80/tcp docker-dotnet-server-1
39fdcf0aff7b postgres "docker-entrypoint.s…" About a minute ago Up About a minute (healthy) 5432/tcp docker-dotnet-db-1
上の例でコンテナー ID は 39fdcf0aff7b
となっています。
以下のコマンドを実行して、コンテナー内の postgres データベースに接続します。
コンテナー ID 部分は、各自のコンテナー ID に置き換えてください。
$ docker exec -it 39fdcf0aff7b psql -d example -U postgres
そしてデータベースに対してレコードをインサートします。
example=# INSERT INTO "Students" ("ID", "LastName", "FirstMidName", "EnrollmentDate") VALUES (DEFAULT, 'Whale', 'Moby', '2013-03-20');
以下のような出力が得られます。
INSERT 0 1
データベースへの接続を閉じ、exit
を実行してコンテナーシェルから抜け出ます。
example=# exit
データベース内のデータ確認
ブラウザーを開いて
http://localhost:8080 にアクセスしてアプリケーションを確認します。
単純なウェブアプリケーションが起動し、Student name is Whale Moby
という文字列が表示されたはずです。
端末画面から ctrl
+c
を押下してアプリケーションを停止します。
端末から docker compose rm
を実行してコンテナーを削除します。
そして再度 docker compose up
を入力して、もう一度アプリケーションを実行します。
$ docker compose rm
$ docker compose up --build
ブラウザーにて http://localhost:8080 の表示を更新します。 そして student name (生徒の名前) が残っているのを確認してください。 つまりコンテナーをいったん削除してから起動し直した後でも、データは残っていたことになります。
端末画面から ctrl
+c
を押下してアプリケーションを停止します。
サービスの自動更新
Compose Watch を利用すると、自分が作成して実行している Compose サービスを自動的に更新できるようになります。 Compose Watch に関する詳細は Use Compose Watch を参照してください。
IDE またはテキストエディターを使って compose.yaml
ファイルを開きます。
そして Compose Watch 命令を追加します。
以下は修正した compose.yaml
ファイルです。
services:
server:
build:
context: .
target: final
ports:
- 8080:80
depends_on:
db:
condition: service_healthy
develop:
watch:
- action: rebuild
path: .
db:
image: postgres
restart: always
user: postgres
secrets:
- db-password
volumes:
- db-data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
expose:
- 5432
healthcheck:
test: ["CMD", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:
secrets:
db-password:
file: db/password.txt
以下のコマンドを実行して Compose Watch を使ったアプリケーション起動を行います。
$ docker compose watch
ブラウザーを開き、 http://localhost:8080 へのアクセスによってアプリケーションが動作していることを確認します。
ローカルマシン内のアプリケーションソースファイルに加えた変更は、起動中のコンテナーに即座に反映されています。
IDE またはテキストエディターを使って docker-dotnet-sample/src/Pages/Index.cshtml
を開きます。
おっして 13 行目にある student name (学生名) の文字列部分を Student name is
から Student name:
に変えます。
- <p>Student Name is @Model.StudentName</p>
+ <p>Student name: @Model.StudentName</p>
Index.cshmtl
への変更を保存します。
アプリケーションが再ビルドされるまでしばらく待ちます。
ブラウザー上の
http://localhost:8080 を再表示して、文字列が更新されたことを確認します。
端末画面から ctrl
+c
を押下してアプリケーションを停止します。
開発用コンテナーの生成
ところでコンテナー化されたアプリケーションを起動したときには .NET ランタイムイメージが利用されています。
イメージが小さければ本番環境として適当かもしれませんが、開発時には SDK ツールやその依存パッケージが必要となるのに、それが含まれていません。
また開発中には、おそらく dotnet publish
を実行する必要もまたありません。
そこで開発環境用、本番環境用のいずれに対しても同一の Dockerfile を使って、マルチステージビルドによってステージをビルドするということを行います。
詳しくは
マルチステージビルド を参照してください。
Dockerfile に新たに開発用ステージを追加します。
そしてローカル開発環境用として、このステージを利用できるように compose.yaml
ファイルを修正します。
以下は修正した Dockerfile ファイルです。
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS build
ARG TARGETARCH
COPY . /source
WORKDIR /source/src
RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \
dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app
FROM mcr.microsoft.com/dotnet/sdk:6.0-alpine AS development
COPY . /source
WORKDIR /source/src
CMD dotnet run --no-launch-profile
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine AS final
WORKDIR /app
COPY --from=build /app .
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
USER appuser
ENTRYPOINT ["dotnet", "myWebApp.dll"]
以下は修正した compose.yaml
ファイルです。
services:
server:
build:
context: .
target: development
ports:
- 8080:80
depends_on:
db:
condition: service_healthy
develop:
watch:
- action: rebuild
path: .
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_URLS=http://+:80'
db:
image: postgres
restart: always
user: postgres
secrets:
- db-password
volumes:
- db-data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
expose:
- 5432
healthcheck:
test: ["CMD", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:
secrets:
db-password:
file: db/password.txt
コンテナー化したアプリケーションは、今は mcr.microsoft.com/dotnet/sdk:6.0-alpine
イメージを用いるようになりました。
ここには dotnet test
のような開発ツールが含まれています。
続けて次の節では dotnet test
の実行方法について学びます。
まとめ
この節では Compose ファイルに対してローカルデータベースを追加し、データ保存を行うための設定を見てきました。 またソースコードの更新時には Compose Watch を利用した、コンテナーの自動再ビルドと実行方法について学びました。 そして開発作業において必要となる SDK ツールとその依存パッケージを含んだ開発用コンテナーを生成する方法について学びました。
関連情報
次のステップ
次の節では Docker を使ったユニットテストの実行方法について学びます。