🎃

Rider on Windows 開発環境構築 (コンテナだけWSL)

2022/11/30に公開

§ 概要

JetBrains社のRiderによる ASP.NETの開発環境を作成します。
まず、WSL(Ubuntu)環境を構築し、その上にDocker Engineを構築します。
さらに、Windows上にDocker CLIをインストールし、RiderからはそのDocker CLIを介して、WSL上のDocker Engineを操作します。
これにより、Linux環境で動作する.NETアプリケーションを、Windows単体で開発・デバグできることを目的としています。

また、Windows上にminikubeをインストールし、Kubernetes上の.NETアプリをデバグする方法や、docker-composeを使った構築も行っています。

なお、Rider on Windows 開発環境構築(全部WSL上)では、JetBrainsのGatewayを使用した全部WSL上で開発する方法で構築しています。

https://zenn.dev/dynamitecoolguy/articles/9907255c1025ea

§ セットアップ手順

◆ お品書き

今回セットアップするものと記事執筆時のバージョンは以下の通りです。

リビジョン
Windows 11 22H2
WSL2 1.00.00
Ubuntu 22.04.01 LTS
JetBrains Rider 2022.3 EAP9
Docker CLI (Windows) 20.10.21
Docker Engine (Linux) 20.10.21

◆ Windowsの準備

OS

systemdに対応したWSLをインストールするため、OSはWindows11 22H2以降である必要があります。

仮想化可能かどうかの確認

以下の手順で、PCが仮想化可能かどうか調べます。

仮想化が無効になっていれば、Windowsを再起動し、PCのUEFIメニューから仮想化を有効にします(PCによりUEFIメニューの呼び出し方や、仮想化の有効方法が異なります)。

既存のWSLの無効化

既存のWSLが有効になっている場合は、Windowsの機能ダイアログを表示し、"Linux用Windowsサブシステム"を無効にします。

.NET SDK

.NET 6.0 SDKおよび.NET 7.0 SDKの最新版をインストールします。

https://dotnet.microsoft.com/ja-jp/download


◆ WSLインストール

Github上のmicrosoft/WSLページから、1.00.00のAssetsの"Microsoft.WSL_1.00.0.0_x64_ARM64.msixbundle"をダウンロードします。
そして、PowerShellを管理者モードで起動してインストールします。

PowerShell(管理者モード)
> Add-AppxPackage Microsoft.WSL_1.00.0.0_x64_ARM64.msixbundle

◆ Windows Terminal

Ubuuntuをインストールする前に、Windows Terminalをインストールしておきます。


◆ Docker Desktopの設定

Docker Desktopをインストールしている場合は、"Enable integration with my default WSL distro"の設定を無効にしてください。
後からDocker Desktopをインストールする場合も同様です。


◆ Ubuntu-20.04

WSLコマンドによるインストール

Ubuntu 20.04 LTSをインストールします。

PowerShell
> wsl --install Ubuntu-20.04

途中、Ubuntuコンソールが起動し、デフォルトで使用するユーザアカウント、パスワードを聞かれるので入力します。ここで入力するUNIXユーザアカウントは、Windowsのユーザアカウントと一致する必要はありません (下記の例では、UNIXユーザアカウントは"scott"にしています)。

Ubuntu
Installing, this may take a few minutes...
Please create a default UNIX user accout. The username does not need to match your Windows username.
For more information visit: https://aka.ms/wslusers
Enter new UNIX username: scott         # 任意のアカウント名
New password: ****                     # 上記アカウントのパスワード
Retype new password: ****              # 確認のためもう一度

wsl.conf ファイルの設定

WSL用の設定ファイルを作成するために、エディタを起動します。

Ubuntu
$ sudo nano /etc/wsl.conf              # wsl.confファイルを新規作成
/etc/wsl.conf
[boot]
systemd = true

[network]
generateResolvConf = false

[interop]
appendWindowsPath = false

[user]
default = scott

各項目の説明です。

  • systemd(true)は、PID:1でsystemdプロセスを起動させるためのオプションです。これにより、systemctlコマンドが動作するようになり、デーモンの管理などが楽になります。
  • generateResolvConf(false)は、/etc/resolv.confファイルを起動時に自動更新させないようにします。これは、DNSによる名前解決できない不具合を回避するためのものです。/etc/resolv.confファイルは、後で手動で作成します。
  • appendWindowsPath(false)は、PATH変数にWindowsのPathを引き継がないようにするものです。
  • default(管理ユーザアカウント)は、ログイン時のユーザを指定するものです。ログイン時のユーザはレジストリにも書き込まれますが、ファイルのバックアップなどをおこなった際にその情報が失われてしまうため、明示的に指定します。

wsl.confの設定を反映させるために、exitコマンドでいったんUbuntuコンソールを終了します。

Ubuntu
$ exit                                  # Ubuntuから抜ける
logout

Powershellを起動し、WSLを再起動します。

> wsl --shutdown                        # WSLをシャットダウン
> wsl -d Ubuntu-20.04                   # WSLを起動

DNS設定ファイルの作成

/etc/resolv.confファイルを作成します。

Ubuntu
$ sudo rm -f /etc/resolv.conf           # 念のため古いものを削除
$ sudo nano /etc/resolv.conf            # 新規作成
/etc/resolv.conf
nameserver 8.8.8.8

google.comへのpingが通れば成功です (結果内のアドレス等は実行環境依存です)。

Ubuntu
$ ping google.com -c 1
PING google.com (172.217.161.78) 56(84) bytes of data.
64 bytes from nrt20s20-in-f14.1e100.net (172.217.175.78): icmp_seq=1 ttl=115 time=34.1 ms

最新状態に更新

Ubuntu 20.04を最新状態にした後、いったん、WSLを終了させます。

Ubuntu
$ sudo apt update
...()...
$ sudo apt full-upgrade -y
...()...
$ exit
Logout
> wsl --shutdown                        # WSLをシャットダウン

◆ ディストリビューション名の変更

将来的にWSL上に別のUbuntuサーバーを構築することを考え、ディストリビューション名の"Ubuntu-20.04"を変更します。

まず、今の"Ubuntu-20.04"ディストリビューションのバックアップを作成します (バックアップ先のパスとファイル名は適当に付けてください)。

PowerShell
> wsl --export "Ubuntu-20.04" D:\backup\backup.tar

ディストリビューションを格納するディレクトリを作成します。下の例では、ユーザーフォルダー直下にwslimagesというディレクトリを作成しています。

PowerShell
> mkdir $Env:USERPROFILE\wslimages

次に、作成したバックアップイメージを読み込みます。wsl --importの各引数の意味は順番に以下の通りです(backendは例ですので、任意の名前にしてください)。

  • 新しいディストリビューション名 (下記の例ではbackend)
  • ディストリビューションのイメージファイル (下記の例では$Env:USERPROFILE\wslimages\backend)
  • wsl --exportコマンドで作成したバックアップイメージ
PowerShell
> wsl --import "backend" $Env:USERPROFILE\wslimages\backend D:\backup\backup.tar

wslコマンドでは、起動するディストリビューションを"-d <<ディストリビューション名>>"で指定できます。
しかし、毎回入力するのは面倒なので、"-d"オプションを省略したときのデフォルトのディストリビューションを指定することができます。
以下のコマンドで、デフォルトのディストリビューションを指定します。

PowerShell
> wsl --set-default "backend"

作成したバックアップファイルはもう不要ですので、削除してもかまいません。

PowerShell
> rm D:\backup\backup.tar

◆ Ubuntu 22.04にアップグレード

do-release-upgrade

Ubuntuも最新のLTSである22.04にアップグレードします。
まず、Windows Terminalで作成したUbuntuのディストリビューションを起動します。
立ち上がったら、do-release-upgradeコマンドで、OSをアップグレードします。
なお、途中画面が切り替わり、"Restart services during package upgrades without asking?"と聞かれたら"Yes"と答えておきます。

Ubuntuコンソール
$ sudo do-release-upgrade                 # 最新のディストリビューションに更新
...()...
Installing the upgrade can take several hours. Once the download has
finished, the process cannot be canceled.

 Continue [yN]  Details [d] y             # 処理を続行するか聞かれるのでyを入力

Fetching
...()...
Building dependency tree
Reading state information... Done

Searching for obsolete software
Reading state information... Done

Remove obsolete packages?

70 packages are going to be removed.

 Continue [yN] Details [d] y             # 使わないパッケージ消していいか聞かれるのでyを入力
...()...
System upgrade is complete.

Action required

Exit all other instances of Ubuntu WSL before continuing.

Unsaved progress may otherwise be lost.

To continue please press [ENTER]         # ENTERキーを入力

WSL restart required

Exit this instance of Ubuntu WSL.

The upgrade will then be complete.

To continue please press [ENTER]         # ENTERキーを入力
...()...

$ exit                                   # いったんUbuntuから抜ける

ここで、WSLを再起動します。Windows TerminalでPowerShellを開き、以下のコマンドを実行します。

PowerShell
> wsl --shutdown                         # WSLをいったん終了
(終了後、Windows TerminalでUbuntu立ち上げ)

◆ systemdサービスの不具合対応

systemdを有効にしただけでは、起動に失敗するサービスがいくつかあります。その設定を変更します。なお、"systemctl --failed"コマンドを実行することで、起動に失敗したサービスを調べることができます。

Ubuntu
$ systemctl --failed
  UNIT                       LOAD   ACTIVE SUB    DESCRIPTION
* systemd-remount-fs.service loaded failed failed Remount Root and Kernel File Systems
* systemd-sysusers.service   loaded failed failed Create System Users

systemd-remount-fs.service

Ubuntuをインストールした際に、通常、ルートのファイルシステムには"cloudimg-rootfs"が付けられるのですが、WSLの場合にはこのラベルが付きません。そのため、systemd-remount-fs.serviceの起動に失敗します。

Ubuntu
$ cat /etc/fstab
LABEL=cloudimg-rootfs   /      ext4    defaults            0 0

ルートファイルシステムにこのラベルを付けることで、エラーを回避します。
まず、"/"をマウントしているデバイスを調べます。以下の結果の場合、デバイスは"/dev/sdc"です(WSLがインストールされている環境により値は異なります)。

Ubuntu
$ mount | grep ext4
/dev/sdc on / type ext4 (rw,relatime,discard,errors=remount-ro,data=ordered)
...()...

以下のコマンドでラベル名を付けます。

Ubuntu
$ sudo e2label /dev/sdc cloudimg-rootfs

systemd-sysusers.service

これは、/usr/lib/systemd/system/systemd-sysusers.service 内のLoadCredentialでWSLでロードされていないモジュールを使用するオプションが指定されているのが原因です。

/usr/lib/systemd/system/systemd-sysusers.service
# Optionally, pick up a root password and shell for the root user from a
# credential passed to the service manager. This is useful for importing this
# data from nspawn's --set-credential= switch.
LoadCredential=passwd.hashed-password.root
LoadCredential=passwd.plaintext-password.root
LoadCredential=passwd.shell.root

直接このファイルを修正するのではなく、以下のコマンドで上書きするオプションを記述します。

Ubuntu
$ sudo systemctl edit systemd-sysusers.service

"Service"と"LoadCredential="の二行を追加します。追加する場所は、必ず"### Anything~"と"### Lines below this~"の間に記述してください。

/etc/systemd/system/systemd-sysusers.service.d/override.conf
### Editing /etc/systemd/system/systemd-sysusers.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file
[Service]
LoadCredential=

### Lines below this comment will be discarded

◆ NVIDIA CUDAドライバーの不具合対応

Windows側でNVIDIAドライバーを使用している場合、グラフィックドライバーがうまくロードできない不具合が発生します。
これを回避するために、以下の対策を行います。なお、ビデオドライバーを更新すると、libcuda.soが再インストールされる可能性があり、その場合はこの作業をもう一度行う必要があります。

libcuba.so.1の不具合回避

WSLの不具合を以下の手順で回避します。

まず、PowerShellを管理者として実行します。

まず、シンボリックリンクになっているかどうかを確認します。下のように、libcuda.soとlibcuda.so.1がシンボリックリンクでは無いためエラーが発生します。

PowerShell(管理者)
PS> ls C:\Windows\System32\lxss\lib

    Directory: C:\Windows\System32\lxss\lib

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---          2022/11/13    21:23         149912 libcuda.so
-a---          2022/11/13    21:23         149912 libcuda.so.1
-a---          2022/11/13    21:23         149912 libcuda.so.1.1

エラーの原因であるファイルをシンボリックリンクに変更します。

PowerShell(管理者)
PS> cd C:\Windows\System32\lxss\lib
PS> rm libcuda.so
PS> rm libcuda.so.1
PS> New-Item -Value libcuda.so.1.1 -Path . -Name libcuda.so -ItemType SymbolicLink
PS> New-Item -Value libcuda.so.1.1 -Path . -Name libcuda.so.1 -ItemType SymbolicLink
PS> ls

    Directory: C:\Windows\System32\lxss\lib

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
la---          2022/11/17    18:54              0 libcuda.so -> libcuda.so.1.1
la---          2022/11/17    18:54              0 libcuda.so.1 -> libcuda.so.1.1
-a---          2022/11/13    21:23         149912 libcuda.so.1.1

Ubuntu側でエラーの原因となるパッケージを再インストールし、WSLを再起動します。

Ubuntu
$ sudo apt reinstall libc-bin
(この後、Ubuntuを再起動)

◆ Docker

Docker Engineをインストールします。

GPGキー追加

公式マニュアルにならい、まず、Dockerの公式GPGキーを追加します。

Ubuntu
$ sudo mkdir -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

aptリポジトリへの追加

Dockerリポジトリをインストール用ソースに追加します(アーキテクチャ(amd64)、Ubuntuバージョン(jammy)は固定で書いてしまっています)。

Ubuntu
$ echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu jammy stable" | sudo tee /etc/apt/sources.list.d/docker.list

aptコマンドでDocker Engineをインストールします。

Ubuntu
$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y

接続証明書作成

DockerEngineに接続するための証明書を生成します。

まず、証明書作成の作業するディレクトリに移動します。

Ubuntu
$ mkdir -p ~/.docker
$ cd ~/.docker

サーバー証明書とクライアント証明書を発行する認証局を作成します。途中入力するパスフレーズはそれぞれの証明書に署名するときに使用します。

Ubuntu
$ openssl genrsa -aes256 -out ca-key.pem 4096
Enter PEM pass phrase: ****
Verifying - Enter PEM pass phrase: ****

$ openssl req -new -x509 -days 3650 -key ca-key.pem -sha256 -out ca.pem
Enter pass phrase for ca-key.pem: ****
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Hokkaido
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:For Test
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:

サーバー証明書を発行します。ca-key.pemのパスフレーズは、認証局を作成したときのパスフレーズを入力してください。

Ubuntu
$ openssl genrsa -out server-key.pem 4096
$ openssl req -subj "/CN=localhost" -sha256 -new -key server-key.pem -out server.csr

$ echo subjectAltName = DNS:localhost,IP:127.0.0.1 >> extfile.cnf
$ echo extendedKeyUsage = serverAuth >> extfile.cnf
$ openssl x509 -req -days 3650 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
Certificate request self-signature ok
subject=CN = localhost
Enter pass phrase for ca-key.pem: ****

クライアント証明書を発行します。ca-key.pemのパスフレーズは、認証局を作成したときのパスフレーズを入力してください。

Ubuntu
$ openssl genrsa -out key.pem 4096
$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr
$ echo extendedKeyUsage = clientAuth > extfile-client.cnf
$ openssl x509 -req -days 3650 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile-client.cnf
Certificate request self-signature ok
subject=CN = client
Enter pass phrase for ca-key.pem: ****

不要なファイルを消し、秘密鍵と公開鍵のパーミッションを適切なものに変更します。

Ubuntu
$ rm client.csr server.csr extfile.cnf extfile-client.cnf
$ chmod 0400 ca-key.pem key.pem server-key.pem
$ chmod 0444 ca.pem server-cert.pem cert.pem

認証局の公開鍵と、サーバー証明書の公開鍵と秘密鍵を、rootの.dockerディレクトリにコピーします。

Ubuntu
$ sudo mkdir /root/.docker
$ sudo cp ca.pem server-cert.pem server-key.pem /root/.docker
$ sudo chown -R root:root /root/.docker

認証局の公開鍵と、クライアント証明書の公開鍵と秘密鍵を、Windows側のホームディレクトリ下の.dockerディレクトリにコピーします。なお、<<Windowsのユーザ>>は、この作業を行っているユーザのWindows上のアカウント名です。(つまりは、ユーザーのホームディレクトリ)。

Ubuntu
$ mkdir -p /mnt/c/Users/<<Windowsのユーザ>>/.docker
$ cp ca.pem cert.pem key.pem /mnt/c/Users/<<Windowsのユーザ>>/.docker

設定変更

Windows側からリモートでDocker Engineを操作するために待ち受けポートを追加します。

Ubuntu
$ sudo systemctl edit docker.service

"### Lines below this comment will be discard"の前に、以下の設定を追加します。

/etc/systemd/system/docker.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376 --containerd=/run/containerd/containerd.sock \
  --tlsverify \
  --tlscacert=/root/.docker/ca.pem \
  --tlscert=/root/.docker/server-cert.pem \
  --tlskey=/root/.docker/server-key.pem

### Lines below this comment will be discarded

一般ユーザーでの実行

sudo無しでdockerを起動させるために、システムグループ"docker"を追加し、今ログイン中のユーザをそのグループに追加します。追加後、いったん再起動します。

Ubuntu
$ sudo adduser $USER docker
$ sudo usermod -aG docker $USER
(Ubuntu再起動)

インストール確認

Docker Engineが正常にインストールできたかどうか、以下のコマンドで確認できます。
"Server: Docker Engine"が表示されていれば、設定成功です。

Ubuntu
$ docker -H tcp://127.0.0.1:2376 --tlsverify version
Client: Docker Engine - Community
 Version:           20.10.21
 API version:       1.41
 Go version:        go1.18.7
 Git commit:        baeda1f
 Built:             Tue Oct 25 18:01:58 2022
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.21
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.18.7
  Git commit:       3056208
  Built:            Tue Oct 25 17:59:49 2022
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.10
  GitCommit:        770bd0108c32f3fb5c73ae1264f7e503fe7b2661
 runc:
  Version:          1.1.4
  GitCommit:        v1.1.4-0-g5fd4c4d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

◆ Chocolatey

Windows用パッケージマネージャのChocolateyをインストールします。

PowerShellを管理者モードで起動し、以下のコマンドを実行します。

PowerShell(管理者モード)
> Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

インストールが完了したら、いったん、PowerShellを閉じます。


◆ Docker-CLI

chocolateyによるインストール

Windows用のdockerコマンドをインストールします。
PowerShellを管理者モードで起動し、以下のコマンドを実行します。

PowerShell(管理者モード)
> choco install docker-cli -y
Chocolatey v1.2.0
Installing the following packages:
docker-cli
By installing, you accept licenses for the packages.
Progress: Downloading docker-cli 20.10.21... 100%

docker-cli v20.10.21 [Approved]
docker-cli package files install completed. Performing other installation steps.
 ShimGen has successfully created a shim for docker.exe
 The install of docker-cli was successful.
  Software installed to 'C:\ProgramData\chocolatey\lib\docker-cli'

Chocolatey installed 1/1 packages.
 See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).

環境変数設定

WindowsからWSL内のDocker Engineにアクセスするためには、以下の環境変数を設定しておきます。

PowerShell
> [Environment]::SetEnvironmentVariable("DOCKER_HOST", "tcp://localhost:2376", [EnvironmentVariableTarget]::User)
> [Environment]::SetEnvironmentVariable("DOCKER_TLS_VERIFY", "1", [EnvironmentVariableTarget]::User)

設定を反映させるために、PowerShellをいったん終了します。


◆ 開発用自己署名証明書作成

.NET開発で使用する自己署名証明書を作成します。PowerShellで以下のコマンドを実行します。
なお、ここで入力したパスワードは、プロジェクト作成毎に必要となります。

PowerShell
> cd ~
> mkdir certificates
> cd certificates
> dotnet dev-certs https -ep localhost.pfx -p <<任意のパスコード>>
> dotnet dev-certs https -ep localhost.crt --format pem

◆ Skaffold

インストール

ビルドツールSkaffoldをchocoを使ってインストールします。

PowerShell(管理者モード)
> choco install skaffold -y
Chocolatey v1.2.0
Installing the following packages:
skaffold
By installing, you accept licenses for the packages.
Progress: Downloading skaffold 2.0.2... 100%

skaffold v2.0.2 [Approved]
skaffold package files install completed. Performing other installation steps.
Progress: 100% - Completed download of C:\ProgramData\chocolatey\lib\skaffold\tools\skaffold.sha256 (93 B).
Download of skaffold.sha256 (93 B) completed.
Downloading skaffold 64 bit
  from 'https://github.com/GoogleContainerTools/skaffold/releases/download/v2.0.2/skaffold-windows-amd64.exe'
Progress: 100% - Completed download of C:\ProgramData\chocolatey\lib\skaffold\tools\skaffold-windows-amd64.exe (68.39 MB).
Download of skaffold-windows-amd64.exe (68.39 MB) completed.
Hashes match.
C:\ProgramData\chocolatey\lib\skaffold\tools\skaffold-windows-amd64.exe
Added C:\ProgramData\chocolatey\bin\skaffold.exe shim pointed to '..\lib\skaffold\tools'.
 ShimGen has successfully created a shim for skaffold.exe
 The install of skaffold was successful.
  Software install location not explicitly set, it could be in package or
  default install location of installer.

Chocolatey installed 1/1 packages.
 See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).

これで、C:\ProgramData\chocolatey\bin\skaffold.exe がインストールされました。


◆ git

インストール

git CLIをchocoを使ってインストールします。

PowerShell(管理者モード)
> choco install git -y
Chocolatey v1.2.0
Installing the following packages:
git
By installing, you accept licenses for the packages.
Progress: Downloading git.install 2.38.1... 100%
Progress: Downloading git 2.38.1... 100%

git.install v2.38.1 [Approved]
git.install package files install completed. Performing other installation steps.
The package git.install wants to run 'chocolateyInstall.ps1'.
Note: If you don't run this script, the installation will fail.
Note: To confirm automatically next time, use '-y' or consider:
choco feature enable -n allowGlobalConfirmation
Do you want to run the script?([Y]es/[A]ll - yes to all/[N]o/[P]rint):
Timeout or your choice of '' is not a valid selection.
Exiting chocolatey abnormally. Please manually clean up anything that
 was not finished.
PS C:\Users\kodama> choco install git -y
Chocolatey v1.2.0
Installing the following packages:
git
By installing, you accept licenses for the packages.

git v2.38.1 [Approved]
git package files install completed. Performing other installation steps.
 The install of git was successful.
  Software installed to 'C:\ProgramData\chocolatey\lib\git'

Chocolatey installed 1/1 packages.
 See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).

◆ minikube

インストール

Kubernetes実行環境Minikubeをchocoを使ってインストールします。なお、インストール時にKubernetesのCLI (kubectl) も同時にインストールされます。

PowerShell(管理者モード)
> choco install minikube -y
Chocolatey v1.2.0
Installing the following packages:
minikube
By installing, you accept licenses for the packages.
Progress: Downloading Minikube 1.28.0... 100%

kubernetes-cli v1.25.4 [Approved]
kubernetes-cli package files install completed. Performing other installation steps.
The package kubernetes-cli wants to run 'chocolateyInstall.ps1'.

Extracting 64-bit C:\ProgramData\chocolatey\lib\kubernetes-cli\tools\kubernetes-client-windows-amd64.tar.gz to C:\ProgramData\chocolatey\lib\kubernetes-cli\tools...
C:\ProgramData\chocolatey\lib\kubernetes-cli\tools
Extracting 64-bit C:\ProgramData\chocolatey\lib\kubernetes-cli\tools\kubernetes-client-windows-amd64.tar to C:\ProgramData\chocolatey\lib\kubernetes-cli\tools...
C:\ProgramData\chocolatey\lib\kubernetes-cli\tools
 ShimGen has successfully created a shim for kubectl-convert.exe
 ShimGen has successfully created a shim for kubectl.exe
 The install of kubernetes-cli was successful.
  Software installed to 'C:\ProgramData\chocolatey\lib\kubernetes-cli\tools'

Minikube v1.28.0 [Approved]
minikube package files install completed. Performing other installation steps.
 ShimGen has successfully created a shim for minikube.exe
 The install of minikube was successful.
  Software installed to 'C:\ProgramData\chocolatey\lib\Minikube'

Chocolatey installed 2/2 packages.
 See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).

これで、 C:\ProgramData\chocolatey\bin\kubectl.exe と C:\ProgramData\chocolatey\bin\minikube.exe がインストールされました。

minikube初期化

PowerShellでminikubeを初期化します。PowerShellで以下のコマンドを実行します。

PowerShell
> minikube start
😄  Microsoft Windows 11 Enterprise 10.0.22623 Build 22623 上の minikube v1.28.0
❗  Kubernetes 1.25.0 has a known issue with resolv.conf. minikube is using a workaround that should work for most use cases.
❗  追加の詳細情報はこちらを参照してください: https://github.com/kubernetes/kubernetes/issues/112135
🆕  Kubernetes 1.25.3 が利用可能です。アップグレードしたい場合、--kubernetes-version=v1.25.3 を指定してください
✨  既存のプロファイルを元に、docker ドライバーを使用します
👍  minikube クラスター中のコントロールプレーンの minikube ノードを起動しています
🚜  ベースイメージを取得しています...
🤷  docker 「 minikube 」 container がありません。再生成します。
🔥  docker container (CPUs=2, Memory=1984MB) を作成しています...
❗  外部 Docker ホスト localhost 上で 0.0.0.0 をリッスンしています。ご承知おきください
🐳  Docker 20.10.17 で Kubernetes v1.25.0 を準備しています...
🔎  Kubernetes コンポーネントを検証しています...
    ▪ gcr.io/k8s-minikube/storage-provisioner:v5 イメージを使用しています
🌟  有効なアドオン: storage-provisioner, default-storageclass
🏄  終了しました!kubectl がデフォルトで「minikube」クラスターと「default」ネームスペースを使用するよう設定されました

途中、"❗ Kubernetes 1.25.0 has a known issue with resolv.conf. minikube is using a workaround that should work for most use cases."、"🆕 Kubernetes 1.25.3 が利用可能です。アップグレードしたい場合、--kubernetes-version=v1.25.3 を指定してください"というメッセージが出た場合は、kubernetesに不具合があるバージョンですので、いったん、minikubeを停止し、"--kubernetes-version"オプションを付けて、起動します。

minikubeを停止します。

PowerShell
> minikube stop
✋  「minikube」ノードを停止しています...
🛑  SSH 経由で「minikube」の電源をオフにしています...
🛑  1 台のノードが停止しました。

kubernetesのバージョンを指定して、minikubeの起動を行います。なお、次回以降はバージョン指定は不要です (今回指定した1.25.3が使用されます)。

PowerShell
> minikube start --kubernetes-version=1.25.3
😄  Microsoft Windows 11 Enterprise 10.0.22623 Build 22623 上の minikube v1.28.0
✨  既存のプロファイルを元に、docker ドライバーを使用します
👍  minikube クラスター中のコントロールプレーンの minikube ノードを起動しています
🚜  ベースイメージを取得しています...
🔄  「minikube」のために既存の docker container を再起動しています...
🐳  Docker 20.10.17 で Kubernetes v1.25.3 を準備しています...
🤦  クラスターを再起動できません (リセットします): apiserver healthz: apiserver process never appeared
    ▪ 証明書と鍵を作成しています...
    ▪ コントロールプレーンを起動しています...
    ▪ RBAC のルールを設定中です...
🔎  Kubernetes コンポーネントを検証しています...
    ▪ gcr.io/k8s-minikube/storage-provisioner:v5 イメージを使用しています
🌟  有効なアドオン: storage-provisioner, default-storageclass
🏄  終了しました!kubectl がデフォルトで「minikube」クラスターと「default」ネームスペースを使用するよう設定されました

◆ JetBrains Rider

インストール

まず、JetBrains Toolboxをインストールします。

インストール後、Toolboxを起動し、入社可能内にあるRiderのメニューから入手可能バージョンを選択します。

バージョン内から、"2022.3 EAP9"を選びインストールします。

インストール完了後、"Install JetBrains ETW Host Service"をインストールするかどうか聞かれますので、"Apply"をクリックします。

起動

ToolboxからRiderを起動します。初回起動時にUSER AGREEMENTが表示されますので、"Continue"をクリックします。

また、JetBrainsのライセンスがあればライセンスの登録を行ってください。

設定

  • Riderから接続するDocker Engineの設定
設定
Name (任意)
Connect to Docker daemon with: WSL
WSL backend
  • Docker CLIのパス指定
設定
Docker executable C:\ProgramData\chocolatey\bin\docker.exe
Use compose V2 チェックする

  • CloudCodeの設定
設定
Cloud SDK Allow the plugin to manage and install the Cloud SDK
automatically update the SDK チェックする
Deployment Tools Use custom specified dependencies
Path to Skaffold executable C:\ProgramData\chocolatey\bin\skaffold.exe
Path to Kubectl executable C:\ProgramData\chocolatey\bin\kubectl.exe
Path to Minikube executable C:\ProgramData\chocolatey\bin\minikube.exe



§ サンプルプロジェクト

◆ 準備

新規ソリューション作成

サンプルとして、ASP.NETのWebAPIアプリを作成します。
Riderを起動し、New Solutionを選択し、.NET > ASP.NET Core Web Applicationを選択します。

以下の内容でプロジェクトを作成します。

項目名
Solution name apisample
Project name apisample
Solution directory C:\Users<Windowsのアカウント>\RiderProjects
SDK 6.0 or 7.0
Type Web API
Language C#
Auth No authentication
Docker Support Linux

このテンプレート(ASP.NET Core Web Application)は、API (/weatherforecast) を呼び出すと、5件の日付、気温、天気を示すJsonを返すというサンプルが自動的に生成されます。


◆ スタンドアローン

初期状態では、コンテナを使用しないで、.NET内部のWeb Server (Kestrel) が有効になっている設定が用意されています。

実行とデバグ

まず、テストのためControllerのところにブレークポイントを設定します。
Controllers/WeatherForecastController.csを開き、Get()メソッドにブレークポイントを設定します。

そして、デバグ開始ボタンを押すと、デバグ実行が開始されます。

SwaggerのUIが表示されたら、"GET" > "Try it" > "Execute"の順にクリックします。
すると、先ほど指定したブレークポイントで停止するはずです (左側のResume Programを押すと実行が再開されます)。


◆ Docker: Dockerfile

このセクションでは、Dockerコンテナで.NETアプリケーションを起動してみます。

証明書コピー

以前作成した開発用自己署名証明書をソリューション下のプロジェクトディレクトリにコピーします。

PowerShell
$ cp ~/certificates/localhost.pfx ~/RiderProjects/apisample/apisample

実行構成

Dockerfile用の実行構成はデフォルトでは用意されませんので、新規に追加します。
実行メニューからEdit configuration...を選択します。

左上端の"+"をクリックし、Docker > Dockerfileの構成を追加します。
まず、Build横のModify optionsのContext folderを有効にします。

そして、Context folderにプロジェクトのルートディレクトリ(/home/scott/RiderProjects/apisample)を指定します (Context folderには"."と表示されます)。

そして、以下のように値を設定します。

Field Value Memo
Name Docker(WSL) 構成メニュー一覧に表示される名前です
Dockerfile apisample\Dockerfile
Image tag apisample docker imagesコマンドを実行したときに分かりやすくなります
Container name apisample docker psコマンドを実行したときに分かりやすくなります

そして、Run横のModifyメニューからBind portsを選び、以下のポートを入力します。なお、Host port側の値は下の例では5000, 5001になっていますが、別に、1024以上の任意の値ならなんでも構いません。

Host port Container port Protocol
5000 80 tcp
5001 443 tcp

最終的には、以下のような設定になります。

Dockerfile

テンプレートによってDockerfileが自動的に生成されています。
このDockerfileに対して、以下の点を修正します。

  • https接続のための証明書をビルド用にコピー (COPY localhost.pfx)
  • DockerのコンテキストをDockerfileのあるディレクトリから、一つ親のディレクトリに変更したことによるかCOPYコマンドのパスの変更 (各COPY)
  • コンテナイメージ内で証明書をインポート (dotnet dev-certs --import)
  • dotnet buildとdotnet publishのConfiguraitonを"Release"から"Debug"に変更
  • ASPNETCORE_ENVIRONMENTとASPNETCORE_URLS環境変数の追加
  • dotnet buildでインポートされた証明書データのコピー (COPY cryptography/x509stores)

修正した結果は、以下の通りです。
なお、<<パスワード>>の部分には、開発用自己署名証明書作成で入力したパスワードを指定します。

Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["apisample/apisample.csproj", "apisample/"]
RUN dotnet restore "apisample/apisample.csproj"
COPY ["apisample/localhost.pfx", "apisample/"]
COPY ["apisample/", "apisample/"]
WORKDIR "/src/apisample"
RUN dotnet dev-certs https --clean --import localhost.pfx -p <<パスワード>>
RUN dotnet build "apisample.csproj" -c Debug -o /app/build

FROM build AS publish
RUN dotnet publish "apisample.csproj" -c Debug -o /app/publish

FROM base AS final
WORKDIR /app
ENV ASPNETCORE_ENVIRONMENT "Development"
ENV ASPNETCORE_URLS "https://+:443;http://+:80"
COPY --from=publish /root/.dotnet/corefx/cryptography/x509stores/my/* /root/.dotnet/corefx/cryptography/x509stores/my/
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "apisample.dll"]

ビルドと実行

構成を今回作成した実行構成("Docker(WSL)")にしてデバグボタンを押すと、ビルドと実行が開始されます。なお、実行するには、DockerEngineをインストールしたWSLが起動している必要があります。

アクセス先は、"https://localhost:5001/swagger/index.html" になります。
なお、スタンドアローン環境と同様にブレークポイントを設定するなどのデバグを行うことも可能です。

WSL側からの確認

dockerコマンドを使用することで、コンテナの動作を確認することもできます。

まず、docker imagesで、apisampleイメージがビルドされていることが確認できます。

Ubuntu
$ docker images
REPOSITORY                        TAG       IMAGE ID       CREATED         SIZE
apisample                         latest    26ae20867dff   3 minutes ago   212MB
mcr.microsoft.com/dotnet/sdk      6.0       0ff04a23cb9e   2 days ago      737MB
mcr.microsoft.com/dotnet/aspnet   6.0       33a9c85ed2f6   2 days ago      208MB
s

また、docker psで、コンテナが起動していることが確認できます (デバグ中に試してください)。

Ubuntu
$ docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS      NAMES
34570a50a11c   26ae20867dff   "/riderDebugger/linu…"   4 minutes ago   Up 4 minutes   0.0.0.0:5000->80/tcp, :::5000->80/tcp, 0.0.0.0:5001->443/tcp, :::5001->443/tcp, 0.0.0.0:57000->57100/tcp, :::57000->57100/tcp, 0.0.0.0:57200->57300/tcp, :::57200->57300/tcp   apisample

◆ Cloud Code: Kubernetes

このセクションでは、Cloud Codeプラグインを使って、Kubernetes上のコンテナとしてプログラムを動作させます。なお、この設定を行う際には、Docker: Dockerfileで作成したDockerfileが必要です。

minikube

Cloud Code: Kubernetesをローカルで実行するためには、minikubeを動作させておきます。

PowerShell
> minikube start

skaffold init

最初にskaffold用の設定ファイルを作成します。PowerShellを開き、プロジェクトディレクトリでskaffold initコマンドを実行します (javaがインストールされていない場合は警告が出ますが無視してください)。

PowerShell
> cd ~\RiderProjects\apisample
> skaffold init --generate-manifests
time="2022-11-22T23:59:59+09:00" level=warning msg="Skipping Jib: no JVM: [java -version] failed: exec: \"java\": executable file not found in %PATH%" subtask=-1 task=DevLoop

skaffoldファイルの生成元としてDockerを選択します。カーソルでDockerを選び、スペースキーを押してからEnterを押します。

PowerShell
? Which builders would you like to create kubernetes resources for? [Use arrows to move, space to select, <right> to all, <left> to none, type to filter]
> [ ]  Buildpacks (sample1/sample1.csproj)
> [x]  Docker (sample1/Dockerfile)

kubernetesを開くポートを聞かれますので空白のままにします。

PowerShell
? Select port to forward for sample1 (leave blank for none):

skaffold.yamlに書き込まれる内容が表示され、実際に書き込むか聞かれますので"y"を入力します。

PowerShell
apiVersion: skaffold/v4beta1
kind: Config
metadata:
  name: apisample
build:
  artifacts:
  - image: apisample
    context: apisample
    docker:
      dockerfile: Dockerfile
manifests:
  rawYaml:
  - apisample\deployment.yaml

apisample\deployment.yaml - apiVersion: v1
kind: Service
metadata:
  name: apisample
  labels:
    app: apisample
spec:
  clusterIP: None
  selector:
    app: apisample
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: apisample
  labels:
    app: apisample
spec:
  replicas: 1
  selector:
    matchLabels:
      app: apisample
  template:
    metadata:
      labels:
        app: apisample
    spec:
      containers:
      - name: apisample
        image: apisample

? Do you want to write this configuration, along with the generated k8s manifests, to skaffold.yaml? (y/N) y

sample1\deployment.yamlとskaffold.yamlが生成された旨が表示されます。

PowerShell
Generated manifest apisample\deployment.yaml was written
Configuration skaffold.yaml was written
You can now run [skaffold build] to build the artifacts
or [skaffold run] to build and deploy
or [skaffold dev] to enter development mode, with auto-redeploy

skaffold.yaml修正

出力されたskaffold.yamlがそのままでは動作しないので、以下の点を変更します。
なお、エクスプローラウィンドウ上、Solutionモードではskaffold.yamlが表示されないので、File Systemモードにします。

変更点は以下の通りです。

  • Dockerを実行するContextを、apisampleディレクトリではなく一つ上のソリューション全体のディレクトリにしているので、その指定部分であるContextとdockerfileを変更します
skaffold.yaml
apiVersion: skaffold/v4beta1
kind: Config
metadata:
  name: apisample
build:
  artifacts:
  - image: apisample
    context: .
    docker:
      dockerfile: apisample\Dockerfile
manifests:
  rawYaml:
  - apisample\deployment.yaml

deployment.yaml修正

apisample内のdeployment.yamlを修正します。
Docker用のdeployment.yamlからの変更点は以下の通りです。

  • デバグ用のSSHポートとして、Deploymentのポートに22を追加
  • Serviceのportsに5022:22のマッピングを追加
apisample\deployment.yaml
apiVersion: v1
kind: Service
metadata:
  name: apisample
  labels:
    app: apisample
spec:
  type: ClusterIP
  clusterIP: None
  selector:
    app: apisample
  ports:
    - port: 5100
      targetPort: 80
      name: http
    - port: 5143
      targetPort: 443
      name: https
    - port: 5022
      targetPort: 22
      name: dotnet-debug
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: apisample
  labels:
    app: apisample
spec:
  replicas: 1
  selector:
    matchLabels:
      app: apisample
  template:
    metadata:
      labels:
        app: apisample
    spec:
      containers:
      - name: apisample
        image: apisample
        ports:
          - containerPort: 443
          - containerPort: 80
	  - containerPort: 22

Dockerfile

現在のバージョンのCloud CodeプラグインとRiderでは、そのままではデバグできません。デバグするためには、作成されたコンテナとSSH経由でリモートデバグを行います。

そのための設定をDockerに追加します。具体的に追加した点は、以下の通りです。

  • デバグツールをインストールするためのツールとSSHサーバをインストールします (apt-get install)
  • rootにパスワードでSSHログインできるようにします (下の例ではパスワードは"hogefuga")
  • ENTRYPOINTで、SSHサーバを起動した後で、完成したdllを起動します

修正済みのDockerファイルは以下の通りになります。

Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
RUN apt-get update \
    && apt-get install -y unzip procps curl bash openssh-server \
    && rm -rf /var/lib/apt/lists/*
RUN echo "root:hogefuga" | chpasswd
RUN echo "PasswordAuthentication yes" >> /etc/ssh/sshd_config
RUN echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
RUN update-rc.d ssh enable
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["apisample/apisample.csproj", "apisample/"]
RUN dotnet restore "apisample/apisample.csproj"
COPY ["apisample/localhost.pfx", "apisample/"]
COPY ["apisample/", "apisample/"]
WORKDIR "/src/apisample"
RUN dotnet dev-certs https --clean --import localhost.pfx -p hogehoge
RUN dotnet build "apisample.csproj" -c Debug -o /app/build

FROM build AS publish
RUN dotnet publish "apisample.csproj" -c Debug -o /app/publish

FROM base AS final
WORKDIR /app
ENV ASPNETCORE_ENVIRONMENT "Development"
ENV ASPNETCORE_URLS "https://+:443;http://+:80"
COPY --from=publish /root/.dotnet/corefx/cryptography/x509stores/my/* /root/.dotnet/corefx/cryptography/x509stores/my/
COPY --from=publish /app/publish .
#ENTRYPOINT ["dotnet", "apisample.dll"]
ENTRYPOINT ["bash", "-c", "/etc/init.d/ssh start && dotnet apisample.dll"]

実行構成

新規実行構成として、Cloud Code: Kubernetesを選択します。
左上端の"+"をクリックし、Cloud Code: Kubernetesを選択します。

Nameには任意の名前を入れ、DeploymentにはSwitch context and deploy toを選び、"minikube"を選択します。

ビルドと実行

構成を今回作成した実行構成("Kubernetes")にしてデバグボタンを押すと、ビルドと実行が開始されます。

アクセス先は、"https://localhost:5143/swagger/index.html"になりますが、前述した通り、このままではデバグできません。(ブレークポイントなどが使用できません)。

SSH接続

実行されたコンテナとSSH接続するための設定を作成します。
設定の"Tools" > "SSH Configurations"を選択します。

SSH接続先として、以下の接続先を追加します。

Field Value Memo
Visible only for this project チェック Kubernetes上でコンテナが動作しているときだけ接続できますので、このプロジェクト内でのみ有効にします
Host localhost
Port 5022 上述のdeployment.yamlに記載したSSHポート
Username root Dockerfileに記述されたユーザ(root)
Authentication Type password Dockerfileに記述されたログイン方法
Password hogefuga Dockerfileに記述されたパスワード

"Test Connection"をクリックして、"Successfully connected!"と表示されればokです。

リモートデバグ

デバグボタンで実行した後、そのプロセスにリモートで接続します。
"Run" > "Attach To Remote Process.."を選択します。

接続先を上記で追加したSSH接続先を指定します。
なお、"Remote debugger tools are not loaded to the remove host"と表示された場合は、まだデバグツールがロードされていませんので、"click to load"をクリックして、デバグツールのロードを開始します。

デバグツールがロードされれば、接続先のプロセス一覧が表示されますので、"dotnet (apisample.dll)"を選び、"Attach with .NET Core Debugger"をクリックします。すると、デバグが開始されます。


◆ Docker: docker compose

Docker Composeを使うこともできます。

Dockerfile

Dockerfileは Cloud Code: Kubernetes と同じものを使用します。

docker-compose.ymlファイル

プロジェクト直下にファイル(docker-compose.yml)を作成します。
プロジェクト直下にファイルを作成するために、左側のエクスプローラーの表示を"Solution"から"File System"にします。

新規ファイルを作成するために、ルートの"apisample"の上で右クリックし、"Add" > "File"を選択します。

ファイル名は、"docker-compose.yaml"にします。

docker-compose.yaml
version: "3"

services:
  apisample-dc:
    image: ${DOCKER_REGISTRY-}apisample-dc
    build:
      context: .
      dockerfile: apisample/Dockerfile
    networks:
      - private_net
    ports:
      - "5280:80"
      - "5243:443"
      - "5222:22"

networks:
  private_net:

実行構成

新規実行構成として、Docker: Docker-composeを選択します。
左上端の"+"をクリックし、Docker > Docker-composeの構成を追加します。
そして、Compose-filesに、追加したdocker-compose.yamlを指定します。

リモートデバグ

Kubernetesと同様に、デバグボタンを押すだけではデバグできません。リモートデバグを使用します。
リモートデバグの方法は Cloud Code: Kubernetes と同じですが、接続先のSSHのポートは、docker-compose.yamlで定義したポート(5222)になります。



変更履歴

変更内容 説明 日付
DOCKER_HOSTへの設定を"localhost:2376"から"tcp://localhost:2376"に変更 "localhost:2376"だとdocker-composeコマンドが解釈できなかったため 2022/12/01
COMPOSE_CONVERT_WINDOWS_PATHの設定追加 Windows上でdocker compose(CLI)を行うとエラーになるのを回避 2022/12/05
dockerを一般ユーザで動作させる際のコマンドsudo addgroup --system groupを削除 dockerと間違えてgroupというグループを追加していた。そもそもこのコマンドは不要 2022/12/06
Docker Desktopの設定追記 WSL側のDockerと競合が発生しやすい設定の無効化 2022/12/07

Discussion

ログインするとコメントできます