はじめに
.NET 7 からは、Dockerfileが無くてもDockerコンテナがビルドできるということで、どんな動きになるのかを確認していきます。
ただ、.NET 7はまだPreview状態なので、コンテナ内から親のWSLのDockerホストに接続してコンテナをビルドしてみます。
.NET 7の起動
とりあえず.NET 7.0のコンテナを起動しておきます。
$ docker run -it mcr.microsoft.com/dotnet/sdk:7.0
root@5d0192433df9:/#
コンテナ内からWSLのDockerホストに接続する
今回はDockerで起動した.NETのコンテナからWSLで起動しているDockerHostに接続するので、前準備としてコンテナ内からWSLにSSHできるように設定を行います。
まずは、コンテナ内の.NETのコンテナに必要なものをインストールしてSSHするための秘密鍵と公開鍵を作ります。
$ apt update
$ apt install -y ssh vim
$ curl -fsSL https://get.docker.com/ | sh
$ ssh-keygen -f docker1
$ ls -la ~/.ssh
total 16
drwx------ 2 root root 4096 Sep 12 04:16 .
drwx------ 1 root root 4096 Sep 12 04:16 ..
-rw------- 1 root root 2602 Sep 12 04:15 docker1
-rw-r--r-- 1 root root 571 Sep 12 04:15 docker1.pub
Docker内で作ったカギをWSL側で取り出し、WSL側のauthorized_keysに追記します。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5d0192433df9 mcr.microsoft.com/dotnet/sdk:7.0 "bash" 10 minutes ago Up 10 minutes lucid_mclean
$ docker cp 5d0192433df9:/root/.ssh/docker1.pub ~/.ssh/
$ cat ~/.ssh/docker1.pub >> ~/.ssh/authorized_keys
Docker側から見た、WSL側のIPアドレスを確認します。
WSL側のIPアドレスは172.17.0.1になっているようです。
$ ip a show docker0
8: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:78:57:e9:8f brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:78ff:fe57:e98f/64 scope link
コンテナの中から、WSLにSSHできるかを確認します。
$ ssh -i ~/.ssh/docker1 y-sugiyama@172.17.0.1
コンテナ内にSSH用の設定ファイルを作ります。
host wsl
hostname 172.17.0.1
user y-sugiyama
port 22
identityfile ~/.ssh/docker1
identitiesonly yes
同様にconfigを使ってSSH接続できることを確認します。
$ ssh wsl
無事接続できましたね。
続いてコンテナ内のDockerコマンドの接続先を親のWSLにします。
$ docker context create wsl --docker "host=ssh://wsl"
$ docker context use wsl
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5d0192433df9 mcr.microsoft.com/dotnet/sdk:7.0 "bash" 49 minutes ago Up 49 minutes lucid_mclean
6be44e5722c5 portainer/portainer-ce:2.11.0 "/portainer" 4 months ago Up 3 hours 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 0.0.0.0:9443->9443/tcp, :::9443->9443/tcp, 9000/tcp portainer
これでDockerコンテナ内のDockerコマンドでは、親のWSLで起動するDockerホストを利用するようになりました。
.NET 7 のプロジェクトをコンテナとしてビルドして実行する
MVCのサンプルアプリを作り、Microsoft.NET.Build.Containers
パッケージを追加してコンテナをビルドします。
$ dotnet new mvc -o SampleMvc
$ cd SampleMvc
$ dotnet add package Microsoft.NET.Build.Containers
$ dotnet publish --os linux --arch x64 -p:PublishProfile=DefaultContainer
MSBuild version 17.4.0-preview-22368-02+c8492483a for .NET
Determining projects to restore...
All projects are up-to-date for restore.
/usr/share/dotnet/sdk/7.0.100-preview.7.22377.5/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.RuntimeIdentifierInference.targets(219,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy [/root/SampleMvc/SampleMvc.csproj]
SampleMvc -> /root/SampleMvc/bin/Debug/net7.0/linux-x64/SampleMvc.dll
SampleMvc -> /root/SampleMvc/bin/Debug/net7.0/linux-x64/publish/
/root/.nuget/packages/microsoft.net.build.containers/0.1.8/build/Microsoft.NET.Build.Containers.targets(45,9): warning CONTAINER001: ContainerImageName was not a valid container image name, it was normalized to samplemvc [/root/SampleMvc/SampleMvc.csproj]
Pushed container 'samplemvc:1.0.0' to registry 'docker://'
$ docker images | grep sample
samplemvc 1.0.0 5f38dac6fb96 2 minutes ago 219MB
Dockerfileを記述せずに新しいsamlemvc:1.0.0
というコンテナがローカルのリポジトリに登録されました。
本来コンテナの名前は小文字の英数、ハイフン、アンダースコアしか含められません。この機能でコンテナをビルドした場合、デフォルトでは.NETのアセンブリ名を小文字に変換したものが利用されます。
コンテナの名前を変更したい場合、MSBuildプロパティーのContainerImageName
で変更できます。
$ dotnet publish --os linux --arch x64 \
-p:PublishProfile=DefaultContainer \
-p:ContainerImageName=sample-mvc
MSBuild version 17.4.0-preview-22368-02+c8492483a for .NET
Determining projects to restore...
All projects are up-to-date for restore.
/usr/share/dotnet/sdk/7.0.100-preview.7.22377.5/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.RuntimeIdentifierInference.targets(219,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy [/root/SampleMvc/SampleMvc.csproj]
SampleMvc -> /root/SampleMvc/bin/Debug/net7.0/linux-x64/SampleMvc.dll
SampleMvc -> /root/SampleMvc/bin/Debug/net7.0/linux-x64/publish/
Pushed container 'sample-mvc:1.0.0' to registry 'docker://'
# docker images | grep sample
sample-mvc 1.0.0 a0c7b28acd7c 5 seconds ago 219MB
samplemvc 1.0.0 5f38dac6fb96 11 minutes ago 219MB
sample-mvcという名前でコンテナが作成されたことが確認できます。
実際に動かしてみましょう。今回作ったコンテナはWSLのDockerホストに作成されているのでWindows側からもアクセスできるはずです。
$ docker run --rm -p 5000:80 sample-mvc:1.0.0
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
No XML encryptor configured. Key {e35ac4a2-42b3-4a77-8736-8fe4130f7afd} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app
無事表示されました。
いろいろいじってみる
とりあえずビルドされたコンテナの中に入って、現在の内容を確認してみます。
デフォルトのイメージとしてはmcr.microsoft.com/dotnet/aspnet
が利用されるようなので、Debian 11のイメージが動いていることがわかります。
$ docker run --rm -it --entrypoint=sh -p 5000:80 sample-mvc:1.0.0
$ cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
$ dotnet --list-sdks
$ dotnet --list-runtimes
Microsoft.AspNetCore.App 7.0.0-preview.7.22376.6 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 7.0.0-preview.7.22375.6 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
$ set
ASPNETCORE_URLS='http://+:80'
ASPNET_VERSION='7.0.0-preview.7.22376.6'
DOTNET_RUNNING_IN_CONTAINER='true'
DOTNET_VERSION='7.0.0-preview.7.22375.6'
... 略 ...
ディストリビューションを変更したい場合は、ContainerBaseImageプロパティーを変更すればよさそうです。
以前のDebianのコンテナとは別に作りたいので、ContainerImageTagプロパティーでタグも一緒に付与します。
$ dotnet publish --os linux --arch x64 \
-p:PublishProfile=DefaultContainer \
-p:ContainerImageName=sample-mvc \
-p:ContainerImageTag=1.0.0-jammy \
-p:ContainerBaseImage=mcr.microsoft.com/dotnet/runtime:7.0-jammy
MSBuild version 17.4.0-preview-22368-02+c8492483a for .NET
Determining projects to restore...
All projects are up-to-date for restore.
/usr/share/dotnet/sdk/7.0.100-preview.7.22377.5/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.RuntimeIdentifierInference.targets(219,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy [/root/SampleMvc/SampleMvc.csproj]
SampleMvc -> /root/SampleMvc/bin/Debug/net7.0/linux-x64/SampleMvc.dll
SampleMvc -> /root/SampleMvc/bin/Debug/net7.0/linux-x64/publish/
Pushed container 'sample-mvc:1.0.0-jammy' to registry 'docker://'
$ docker run --rm -it --entrypoint=sh -p 5000:80 sample-mvc:1.0.0-jammy
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.1 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
Ubuntu 22.0.4.1 LTS で動いているのが確認できますね。
制限
下記のドキュメントに色々書いてありますが、基本dockerコマンドで実行できるものは取り込んでいく方針のようです。
現時点では次のサポートが予定されています。
- エントリポイント
- エントリポイント引数
- ポート
- 環境変数
- ラベル
また、RUNコマンドで実行するようなものはサポートされないようなので、コンテナに追加でインストールような命令がある場合個別にDockerfileを作成するか、必要なモジュールをインストールしたベースイメージをあらかじめ作成しておき、ContainerBaseImageでそのイメージを指定するような使い方になりそうです。
(aptやapkなどでのインストールは↑の方法で良いけれど、NPMやEF Coreのマイグレーション、Libmanなどはプロジェクトの成果物(package.jsonやcsprojなど)を利用するので、今のところDockerfile書くしかないってことかな??)
おわりに
他のエコシステムの動きを追っていなかったのですが、JavaのJibや、Goのkonetなんかでも同様の流れがあるんですね。細かい動作を定義しようとしたり、ネイティブモジュールをインストールしたりするとDockrefileのお世話になりそうですが、お手軽にコンテナをビルドするには面白い選択肢が増えたのはうれしいですね。
今回はWSLと、コンテナ内の.NETを行ったり来たりするし、コンテナ内からWSLのDockerを触ったりと、だいぶ混乱しました