Docker Desktop for Windows を使用しない Dev Container 環境を構築する

# この記事の概要

* Windows 上の VS Code で Dev Container 環境を構築
* Docker Desktop for Windows をインストールしない(※)
  * Docker は Ubuntu へ Docker Engine のみをインストールします
* Dockerfile、docker-compose.yml を使用しない。作らない
  * 手軽に Dev container を試してみる事が目的です
  * Docker 自体の使い方については触れません
* Rust の dev container で Hello, world! の動作を確認する

※ 公式には Dev Container の使用に Docker Desktop for Windows は必要の様です。

# 前提

ホストOS はクリーンインストールした Windows11 22H2 です。
Windows10 では WSL2 のインストールが成功すれば、後は多分同じはずです。
この記事では、物理PCではなく VMware Workstation 上の仮想マシンで確認しています。


# 手順

## WSL2 Ubuntu のインストール

Windows PowerShell を起動して、以下を実行します(管理者権限を要求されます)。
```
wsl --install
```
インストールが完了したら再起動させます。
```
インストール中: 仮想マシン プラットフォーム
仮想マシン プラットフォーム はインストールされました。
インストール中: Linux 用 Windows サブシステム
Linux 用 Windows サブシステム はインストールされました。
インストール中: Ubuntu
Ubuntu はインストールされました。
要求された操作は正常に終了しました。変更を有効にするには、システムを再起動する必要があります。
```
何度か再起動が繰り返されます。ログインすると、自動的に Ubuntu のウィンドウが開きます。
ユーザー名、パスワード、パスワード(確認)の順に入力します。
```
Ubuntu は既にインストールされています。
Ubuntu を起動しています...
Installing, this may take a few minutes...
Please create a default UNIX user account. The username does not need to match your Windows username.
For more information visit: https://aka.ms/wslusers
Enter new UNIX username: <ユーザー名>
New password: <パスワード>
Retype new password: <パスワード(確認)>
passwd: password updated successfully
この操作を正しく終了しました。
Installation successful!
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.90.1-microsoft-standard-WSL2 x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage


This message is shown once a day. To disable it please create the
/home/<ユーザー名>/.hushlogin file.
<ユーザー名>@dev-container:~$
```

:::note warn
以下の様にメッセージが表示された場合は、仮想化が有効になっていません。BIOS 等の設定を確認して、``wsl --install`` を再度実行します。
```
Ubuntu は既にインストールされています。
Ubuntu を起動しています...
Installing, this may take a few minutes...
WslRegisterDistribution failed with error: 0x80370102
Please enable the Virtual Machine Platform Windows feature and ensure virtualization is enabled in the BIOS.
For information please visit https://aka.ms/enablevirtualization
Press any key to continue...
```
:::

## Docker Engine のインストール

ここからは Ubuntu 上での作業です。スタートメニューから WSL (又は Ubuntu)を起動するか、コマンドプロンプトから wsl.exe を起動します。

公式サイトの [Install Docker Engine on Ubintu](https://docs.docker.com/engine/install/ubuntu/) -> [Install using the convenience script](https://docs.docker.com/engine/install/ubuntu/#install-using-the-convenience-script) にて、お手軽インストールスクリプトを使用した手順が記述されています。
以下の 2 つのコマンドを入力するだけです。
```bash
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh ./get-docker.sh
```
Docker Desktop の利用を推奨するメッセージが表示されますが、そのまましばらく(十数秒程度)放置すると、インストールが始まります。

```bash
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh ./get-docker.sh
# Executing docker install script, commit: 6d9743e9656cc56f699a64800b098d5ea5a60020

WSL DETECTED: We recommend using Docker Desktop for Windows.
Please get Docker Desktop from https://www.docker.com/products/docker-desktop/


You may press Ctrl+C now to abort this script.
+ sleep 20
+ sh -c apt-get update -qq >/dev/null
+ sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -qq apt-transport-https ca-certificates curl >/dev/null
+ sh -c install -m 0755 -d /etc/apt/keyrings
+ sh -c curl -fsSL "https://download.docker.com/linux/ubuntu/gpg" -o /etc/apt/keyrings/docker.asc
+ sh -c chmod a+r /etc/apt/keyrings/docker.asc
+ sh -c echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu jammy stable" > /etc/apt/sources.list.d/docker.list
+ sh -c apt-get update -qq >/dev/null
+ sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-ce-rootless-extras docker-buildx-plugin >/dev/null
+ sh -c docker version
Client: Docker Engine - Community
 Version:           26.1.3
 API version:       1.45
 Go version:        go1.21.10
 Git commit:        b72abbb
 Built:             Thu May 16 08:33:29 2024
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          26.1.3
  API version:      1.45 (minimum version 1.24)
  Go version:       go1.21.10
  Git commit:       8e96db1
  Built:            Thu May 16 08:33:29 2024
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.31
  GitCommit:        e377cd56a71523140ca6ae87e30244719194a521
 runc:
  Version:          1.1.12
  GitCommit:        v1.1.12-0-g51d5e94
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

================================================================================

To run Docker as a non-privileged user, consider setting up the
Docker daemon in rootless mode for your user:

    dockerd-rootless-setuptool.sh install

Visit https://docs.docker.com/go/rootless/ to learn about rootless mode.


To run the Docker daemon as a fully privileged service, but granting non-root
users access, refer to https://docs.docker.com/go/daemon-access/

WARNING: Access to the remote API on a privileged Docker daemon is equivalent
         to root access on the host. Refer to the 'Docker daemon attack surface'
         documentation for details: https://docs.docker.com/go/attack-surface/

================================================================================
```

## root 以外のユーザーから docker を使用できる様にする

```bash
sudo usermod -aG docker $USER
```

## WSL の再起動

Windows PowerShell から以下を入力します。

```
wsl --shutdown
wsl
```

## Docker Engine の起動を確認
WSL 上で docker images を実行して、特にエラーが発生しなければOKです。
(インストール直後なので、images は何も表示されません。これは正常です)
```
docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE
```

## VS Code のインストール

ここからは Windows 上での作業です。
公式サイトからダウンロード、インストールします。日本語化等はお好みで。

https://code.visualstudio.com/download

## WSL 拡張機能を追加

1. VS Code 上で Shift+Ctrl+X を押下して、拡張機能を表示します
2. 検索キーワードへ WSL と入力して、(多分)最初に出てくる WSL を選択します
2. Install を押下してインストールします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/9e4a2d2c-d3bf-845b-8ec9-30f89ec7a888.png)

しばらく待つと Get Started with WSL の画面になります。
Open the Menu ボタンを押下するか、Ctrl+Alt+O で、リモート接続先を表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/d458e448-13ab-55b0-9604-5ccfd114cdf7.png)

リストの最初にある Connect to WSL を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/f430bf2c-fd84-dc9e-f275-980cd40f043d.png)

右下の方に Starting VS Code in WSL ... と表示されます。回線の状態によっては、しばらく時間が掛かります。完了するまで待ちます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/6f4491bf-2190-4e2e-4b8e-a080272c8ba4.png)

ステータスバーの左下に WSL: Ubuntu と表示されたら完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/6ea97e78-d930-ddbc-c6a6-6a48a8f69489.png)

## Dev Containers 拡張機能を追加

1. VS Code 上で Shift+Ctrl+X を押下して、拡張機能を表示します。
2. Dev Containers を検索します。
2. Install Locally を押下してインストールします。
3. インストール完了(Install Locally が Uninstall に変わる)まで待ちます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/fca0aeeb-a98d-bce4-b435-0e2cfd1b8b9d.png)

# Dev container の使用

例として Rust の Dev container で hello world を実行してみます。

1. 一度 VS Code を終了させて、再度起動します(意味は特にありません。説明の都合です)。
1. 左下に「WSL: Ubuntu」と表示されている事を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/6d4d7920-f631-c99d-b775-c8319029be0d.png)
もし、以下の様に、接続済み状態になっていない場合はクリックして表示されるドロップダウンリストから Connect to WSL を選択すると接続できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/9b178a2f-4e7c-7e37-98b8-f572ae228ad3.png)

## ディレクトリの準備

1. ファイルを保存するディレクトリを、WSL Ubuntu 上の ``/home/<ユーザー名>/`` 以下に作っておきます。
2. Start の下の OpenFolder... を選択(または Ctrl+K, Ctrl+O を押下)します。
3. Open Folder ドロップダウンリストが開きます。(1) で作成したディレクトリを選択します。ここでは、リストの最後にある rust-dev-container-test を選択して進めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/e37f5337-99be-3f77-e766-574446f6b1c5.png)

:::note warn
Show Local ボタンを押下すると、本来は Windows 上のフォルダを選択できますが、Docker Desktop をインストールしていない場合はエラーになってしまう様です。Docker Desktop をインストール済みの状態では問題ありませんでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/7eeec0f1-ffed-4c07-f059-df0cac11186f.png)
:::
:::note warn
VS Code 1.78.1 以降から、デフォルトでは UNC パスを拒否します。以前は Ubuntu 上のディレクトリを開く際、``\\wsl.localhost\Ubuntu\...`` の様なパスを指定可能だった様です。
[Working with UNC paths](https://code.visualstudio.com/docs/setup/windows#_working-with-unc-paths) に公式の情報があります。
:::
EXPLORER が以下の様な状態になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/3568352e-e029-f308-170d-7c849664030f.png)

## Dev container の追加

F1 キーを押下して、検索キーワードへ Dev Containers: Add と入力します。ドロップダウンが表示されたら、Dev Containers: Add Dev Container Configuration Files... を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/fcd7772f-9ecd-064b-6634-2ad319387a90.png)

:::note warn
Try a Dev Container Sample... の方では、ローカルディレクトリを準備する必要なく、簡単に Dev Container を試すことができます。が、Docker Desktop がインストールされていない環境では使用できません。ログを見ると、Windows 側にインストールされる、docker(.exe) を探してしまう様です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/885ba6c6-a055-3615-1edb-840dd5e15c54.png)

Docker Desktop 未インストール、Try a Dev Container
(Windows の PATH から docker.exe を探している)
```
[550 ms] userEnvProbe PATHs:
Probe: (...省略...)
Container: None
[559 ms] Start: Run in Host: wslpath -w /run/user/1000/wayland-0
[566 ms] Start: Check Docker is running
[566 ms] Start: Run: docker version --format {{.Server.APIVersion}}
[595 ms] Exectuable 'docker' not found on PATH 'C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\Microsoft VS Code\bin;C:\Users\ain\AppData\Local\Microsoft\WindowsApps;'.
[598 ms] Docker returned an error code ENOENT, message: Exectuable 'docker' not found on PATH 'C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\Microsoft VS Code\bin;C:\Users\ain\AppData\Local\Microsoft\WindowsApps;'.
```
Docker Desktop インストール済み、Try a Dev Container
(Windows 上の docker.exe を起動できる)
```
[610 ms] userEnvProbe PATHs:
Probe: (...省略...)
Container: None
[621 ms] Start: Run in Host: wslpath -w /run/user/1000/wayland-0
[628 ms] Start: Check Docker is running
[628 ms] Start: Run: docker version --format {{.Server.APIVersion}}
[875 ms] Server API version: 1.42
[877 ms] Start: Run: docker volume ls -q
[1076 ms] Start: Run: docker volume create --label vsch.local.repository=https://github.com/Microsoft/vscode-remote-try-rust --label vsch.local.repository.unique=true vscode-remote-try-rust-f4a98917819ee8715d578a94b93e2a78688188f686e1b1dcd83fed7e6ab0d330
[1287 ms] Start: Run: tar --no-same-owner -x -f -
[1332 ms] Start: Run: docker build -f C:\Users\ain\AppData\Local\Temp\vsch\bootstrap-image\0.292.0\bootstrap.Dockerfile -t vsc-volume-bootstrap C:\Users\ain\AppData\Local\Temp\vsch\bootstrap-image\0.292.0
```
Execute in WSL オプションを on にすると、Docker Desktop がインストールされていない環境で、Try Dev container が動作する様になります。

参考記事:
https://qiita.com/ohtsuka1317/items/617a865b8a9d4fb67989#7-%E3%81%8A%E3%81%BE%E3%81%912-vscode%E3%81%8B%E3%82%89wsl%E4%B8%8A%E3%81%AEdocker%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92%E7%9B%B4%E6%8E%A5%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B

:::

次に、表示されたリストから Rust を探して選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/62791d23-a1b4-1589-4328-a4708ec03c4b.png)

そのまま Enter を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/bc1cdda0-b2a2-0787-0d80-0b4082250ed5.png)

Select Features が表示されます。ここも、そのまま Enter を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/cd1d5d9a-e840-2abd-3966-44045ee613c4.png)

devcontainer.json の中身が表示されています。右下の Reopen in Container を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/1e13f032-f746-25e9-edd1-f6facab604ae.png)

Dev Container の開始まで少し待ちます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/2be959cf-7ced-8822-0e5d-3daa880b79ba.png)

完了すると、左下の表示が Dev Container: Rust となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/5209ca8f-1db9-bd15-f16e-4a9d864e29aa.png)

## Rust Dev Container の確認

Ctrl+Shift+@ でターミナルを開くと、docker container 内の bash が表示されます。
rustc (Rust コンパイラ)のバージョンを確認してみます。
```
vscode ➜ /workspaces/rust-dev-container-test $ rustc --version
rustc 1.69.0 (84c898d65 2023-04-16)
vscode ➜ /workspaces/rust-dev-container-test $
```

## hello world の実行

### 準備

``cargo init`` を実行します。
```
vscode ➜ /workspaces/rust-dev-container-test $ cargo init
     Created binary (application) package
vscode ➜ /workspaces/rust-dev-container-test (master) $
```

フォルダの内容は以下の様になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553718/f7a35ff0-25f9-1224-8848-f70f111b5b5e.png)
src/main.rs が Hello, world! を表示する rust のコードです。
内容は以下の様になっています。
```
fn main() {
    println!("Hello, world!");
}
```

### 実行

F5 キーを押して実行します。
```
Console is in 'commands' mode, prefix expressions with '?'.
Launching: /workspaces/rust-dev-container-test/target/debug/rust-dev-container-test
Launched process 3723
Process exited with code 0.
```
なぜか初回はこの出力になりますが、気にせず、再度 F5 キーを押します。
```
Hello, world!
vscode ➜ /workspaces/rust-dev-container-test (master) $
```
表示できました。

ローカルでの開発と同じ様に、デバッグ実行も可能です。

## ファイルの保存場所について

上記の手順で起動した Rust の Dev Container に必要なファイルは、WSL Ubuntu のローカルディレクトリ ``/home/${USER}/rust-dev-container-test/`` 内に生成されます。

* ``/home/${USER}/rust-dev-container-test/`` は Docker container 上では ``/workspace/rust-dev-container-test/`` として mount されています。
  * ``/home/`` が丸ごと ``/workspace/`` として mount されている訳ではありません。``/home/`` 内の他のファイルは見えません。
* Rust の cargo install 実行等による ``/workspace/`` 外の変更は、Docker container で保持されます。当然ですが、container を削除すると変更は失われます。

Rust dev container 起動直後の container は約 214MB です。
```
$ docker ps -s
CONTAINER ID   IMAGE                                  COMMAND                  CREATED          STATUS          PORTS     NAMES                SIZE
2c4e5a734ed9   vsc-rust-dev-container-test-XXXX-uid   "/bin/sh -c 'echo Co…"   22 minutes ago   Up 22 minutes             naughty_mcclintock   214MB (virtual 2.43GB)
```
cargo install cago-watch を行った後は約 283MB となります。
```
$ docker ps -s
CONTAINER ID   IMAGE                                  COMMAND                  CREATED          STATUS          PORTS     NAMES                SIZE
2c4e5a734ed9   vsc-rust-dev-container-test-XXXX-uid   "/bin/sh -c 'echo Co…"   28 minutes ago   Up 28 minutes             naughty_mcclintock   283MB (virtual 2.5GB)
```

なお、Try a Dev Container で起動した Dev Container のファイルはローカルディレクトリ上に見えず、全て Docker で管理されます。 ``/workspace/`` は Docker volume、``/workspace/`` 外の変更は上と場合と同じく、Docker container です。
```
$docker volume ls
DRIVER    VOLUME NAME
local     vscode
local     vscode-remote-try-rust-f4a98917819ee8715d578a94b93e2a78688188f686e1b1dcd83fed7e6ab0d330
```


# 余談

## VMware 仮想マシン上での WSL 動作について

WSL の動作にはハイパーバイザーが必要です。このため、VMware 仮想マシン上の WSL は「**仮想マシン内の**ハイバーバイザー上の仮想マシン(= ネストした仮想マシン)」として動作します。VMware 仮想マシン上でのパイパーバイザーは、通常の OS には必須ではないため、デフォルトは無効の設定になっています。有効にするには、仮想マシン設定の「Intel VT-x/EPT AMD-V/RVI 仮想化」オプションを ON にする必要があります。ただし、このオプションは、ホストOS上で既にハイパーバイザーまたは VBS (仮想ベースセキュリティ)が有効の状態では動作しません。(必要は無いと思いますが)、ホスト上の WSL と、VMware 仮想マシン内の WSL は同時に使用できない、という事になります。

# 参考

https://qiita.com/nujust/items/d7cd395baa0c5dc94fc5

https://qiita.com/matarillo/items/ca1eecf8f9a3cd76f9ce

https://uncaughtexception.hatenablog.com/entry/2021/09/02/193132

https://rohhie.net/a-story-about-not-being-able-to-enable-vt-x-in-vmware-16-player/

https://qiita.com/ohtsuka1317/items/617a865b8a9d4fb67989