WSL2 on Ubuntu on Docker で Windows 上の VSCode からコンテナに接続する

# はじめに

Docker for Windows と VSCode の[リモートコンテナ拡張機能](https://code.visualstudio.com/docs/remote/containers)を利用した開発体験はとても素晴らしく、Windows 環境を汚さず開発チーム内で同じ開発環境を瞬時に用意できます。
ただし、Docker for Desktop をやめ、WSL2 上の Ubuntu で実行しているコンテナにアタッチしてデバックするには少しだけ準備が必要です。

この記事では次の2つの方法について説明します。

- WSL2 上で VSCode を起動し WSL2 から改めて Docker コンテナにアタッチする
- Windows で VSCode を起動し、WSL2 上の Docker コンテナにアタッチする

記事を寝かしていたら、Rancher Desktop が出て来て、SSH の設定も個別の docker のインストールもいらないし、あれ？もうあっちじゃいんじゃね？ってなったけれど。せっかくなので放流します。

https://qiita.com/moritalous/items/14d4099023981dcf4fd2

# WSL2 上で VSCode を起動し WSL2 から改めて Docker コンテナにアタッチする

VSCode のコマンドパレットから、`Rmote-WSL: New WSL Window` コマンドを実行すると、Windows 側に新しい VSCode が起動し WSL2 上の Ubuntu にリモートで接続します。WSL2 側で `code` と入力してもよいですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36535/b7b191e1-71e4-dbb1-7793-50af78cac739.png)
Windows 側の VSCode の拡張機能と Ubuntu 側の拡張機能はそれぞれ異なるものとして管理されているので、Ubuntu 側の VSCode にも[リモートコンテナ拡張機能](https://code.visualstudio.com/docs/remote/containers)をインストールしてあげれば、Windows と同じように Ubuntu 上で実行している Docker に対し VSCode からコンテナにアタッチして開発することができます。

VSCode の左下には接続先が表示されています。Ubuntu に接続してそのうえで実行中のコンテナの一覧が表示されることと、Docker のコンテナとして動いている.NET 6.0 のコンテナに接続していることが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36535/c6b7c7f5-2195-1689-fad8-52c1f3f3329a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36535/e947763d-10a8-7457-bd05-0017b8ecd0ed.png)

特に Windows 上からの操作にこだわらなければ、この方法がお手軽で良いですね。

# Windows で VSCode を起動し、WSL2 上の Docker コンテナにアタッチする

Windows 上の VSCode から直に Docker コンテナにアタッチするのはちょっと手順が複雑です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36535/bb31f675-7738-c4f1-19e6-108a6e2f1217.png)
VSCode のリモートコンテナ拡張機能は、ローカルの docker context の内容を元に接続先の Docker ホストを解決しています。なので、SSH 経由で Ubuntu 上で動作している Docker に接続できるようにした後に、Windows 上に docker コマンドを用意して docker context を作成します。

## Windows で利用できる docker コマンドをダウンロードする

Windows で利用できる docker コマンドのバイナリは下記のサイトで配布されています。
ZIP ファイルをダウンロードして展開し、パスを通します。

https://download.docker.com/win/static/stable/x86_64/

スクリプトでやるならこんな感じでしょうか。`c:\tools\docker` 配下に展開されます。

```powershell
❯ Invoke-WebRequest -Uri "https://download.docker.com/win/static/stable/x86_64/docker-20.10.11.zip" -Outfile ".\docker-20.10.11.zip"
❯ Expand-Archive -Path ".\docker-20.10.11.zip" -DestinationPath c:\tools
❯ [Environment]::SetEnvironmentVariable("Path", $env:Path + ";c:\tools\docker", "User")
```

docker コマンドが使えるようになりましたが、接続先が無いのでこんな感じでエラーになります。

```powershell
❯ docker ps
error during connect: This error may indicate that the docker daemon is not running.: Get "http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.24/containers/json": open //./pipe/docker_engine: The system cannot find the file specified.
```

## Windows から WSL2 の Ubuntu に SSH できるようにする

WSL2 上の Ubuntu に Windows から公開鍵で SSH 接続できるようにします。
SSH の設定は次の記事がとても分かりやすかったです。

https://qiita.com/yuta-katayama-23/items/fad6928f37badf3391f2

Windows から Ubuntu に SSH 接続できることを確認したら、Windows 側の ~/.ssh/config に WSL2 の Ubuntu への接続情報を定義します。WSL2 では Windows からローカルの WSL2 のサーバーに SSH する場合は localhost で接続できるので、IP アドレスを調べるなどの手間がなくなってだいぶ楽になりましたね。

```%USER_RPFILE%/.ssh/config
Host wsl
    HostName localhost
    IdentityFile ~/.ssh/WSLのユーザー名
    User WSLのユーザー名
    TCPKeepAlive yes
```

Windows 側から接続できるか確認します。

```
❯ ssh wsl
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.10.60.1-microsoft-standard-WSL2 x86_64)

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

## WSL2 上の ssh の自動起動

不完全ではありますが WSL2 で Docker のデーモンを自動起動するのと同じ方法で、SSH のデーモンも自動起動するようにします。

visudo で service ssh start のパスワード入力を不要にして、

```sh
$ sudo visudo
#includedir /etc/sudoers.d
WSLのユーザー名 ALL=NOPASSWD: /usr/sbin/service docker start, /usr/sbin/service ssh start
```

`~/.bashrc` でユーザーが Ubuntu にログインしたときに ssh が起動していなかったら起動するスクリプトを仕込みます。

```sh:.bashrc
... 略 ...

if [ $(service ssh status | awk '{print $4}') = "not" ]; then
  sudo service ssh start > /dev/null
fi

... 略 ...
```

一度 wsl を停止し、ログイン後に ssh が起動していれば OK です。

```powershell
❯ wsl --shutdown
❯ bash
$ service ssh status
 * sshd is running
```

## Windows 側で docker コマンドの向け先を WSL2 の Docker にする

SSH 経由で WSL2 に接続する Docker のコンテキストを作成し有効にします。

```powershell
❯ docker context create wsl --docker "host=ssh://wsl"
wsl
Successfully created context "wsl"
❯ docker context use wsl
wsl
Current context is now "wsl"
```

Windows 側で docker コマンドが使えるようになります。

```powershell
❯ docker info
Client:
 Context:    wsl
 Debug Mode: false
 Plugins:
  app: Docker App (Docker Inc., v0.9.1-beta3)
  buildx: Docker Buildx (Docker Inc., v0.7.1-docker)
  compose: Docker Compose (Docker Inc., v2.2.2)
  scan: Docker Scan (Docker Inc., 0.9.0)

Server:
 ... 略 ...

 Kernel Version: 5.10.60.1-microsoft-standard-WSL2
 Operating System: Ubuntu 20.04.3 LTS
 OSType: linux

 ... 略 ...
```

もちろん docker の他のコマンドも使えます。

```powershell
❯ docker ps
CONTAINER ID   IMAGE                           COMMAND        CREATED       STATUS       PORTS                                                                                            NAMES
ea5cfe4ebac6   portainer/portainer-ce:2.11.0   "/portainer"   13 days 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 run --rm -it ubuntu 
root@7e55bc2db5b5:/#
```

ただ、ボリュームマウントするときは注意が必要です。あくまで WSL2 側の Docker を Windows 側から触っているだけなので、ボリュームマウントを行う場合は、WSL2 側から見たパスを指定する必要があります。

```powershell
❯ docker run --rm -it -v C:\Temp\html\:/html ubuntu
docker: Error response from daemon: invalid mode: /html.
See 'docker run --help'
❯ docker run --rm -it -v /mnt/c/Temp/html/:/html ubuntu
root@992079578b12:/# ls /html
index.html
```

## Windows 側の VSCode から WSL2 上で動いている Docker コンテナに接続する

VSCode の コマンドパレットで `Remote-Containers: Attach to Running Container` コマンドを実行すれば、次のようにコンテナの一覧が表示されるはずです。接続したいコンテナを選択して接続してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36535/bb31f675-7738-c4f1-19e6-108a6e2f1217.png)

# おわりに

一度 WSL2 に接続した後に Docker コマンドを利用するのに比べ、Windows から直に Docker コマンドを実行するのは少し手順が複雑になります。
ボリュームマウントのパスの解釈が Windows と Ubuntu で異なるため使いにくかったり、一度 Ubuntu にログインしないと  docker が起動しなかったりと少しハマりポイントも残っています。
ただ、一度設定してしまえば慣れた Windows 上で操作できるのはうれしいですね。