vscode で Win 10 Home -> VirtualBox Ubuntu Container にリモートアクセスする！

## 要件

- ホスト機は Windows 10 Home Edition 1809+
- コンテナ動作機は [VirtualBox](https://www.virtualbox.org/) 6.0.10 に入れた [Ubuntu 18.04 日本語 Remix](https://www.ubuntulinux.jp/News/ubuntu1804-ja-remix)
- vscode の [Visual Studio Code Remote - Containers](https://code.visualstudio.com/docs/remote/containers) 拡張機能を使う
    - この拡張機能を使うことで、ホスト機(Win)に Language Server やデバッグなどで必要な node.js や PHP などの実行環境をインストールする必要がなくなる！
- ファイルの直接マウントは出来ないので、 [sftp](https://marketplace.visualstudio.com/items?itemName=liximomo.sftp) 拡張機能を経由する
- docker 19.03
- 本質にかかわらない細かい部分は省略しつつ手順を書いていきます

## ホスト機(Win)の準備

### OpenSSH をインストール

[Windows の OpenSSH のインストール | Microsoft Docs](https://docs.microsoft.com/ja-jp/windows-server/administration/openssh/openssh_install_firstuse)

今は Windows 公式に OpenSSH を入れられるようになったのでありがたい世の中ですね。

```powershell:管理者権限付きPowerShell
> Get-WindowsCapability -Online | ? Name -like 'OpenSSH*'

Name  : OpenSSH.Client~~~~0.0.1.0
State : NotPresent
Name  : OpenSSH.Server~~~~0.0.1.0
State : NotPresent
> Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0

Path          :
Online        : True
RestartNeeded : False
> Get-WindowsCapability -Online | ? Name -like 'OpenSSH*'

Name  : OpenSSH.Client~~~~0.0.1.0
State : Installed

Name  : OpenSSH.Server~~~~0.0.1.0
State : NotPresent
> ssh -V
OpenSSH_for_Windows_7.7p1, LibreSSL 2.6.5
```

これで PowerShell, cmd.exe で `ssh` コマンドをたたけるようになりました。

### docker cli 各種をインストール

Windows 10 Pro+ であれば Hyper-V を用いた Docker for Windows を利用することが出来ますが、 Windows 10 Home Edition では出来ないので、今回は VirtualBox に用意した Ubuntu に docker をインストールし、そこにリモートで接続することで実現してみようと思います。

cli の用意が面倒なので、 [Scoop](https://github.com/lukesampson/scoop) を使ってインストールします。

```powershell
> scoop install docker docker-compose docker-machine
> docker version
Client:
 Version:           19.03.1
 API version:       1.40
 Go version:        go1.12.7
 Git commit:        74b1e89e8a
 Built:             Wed Jul 31 15:18:18 2019
 OS/Arch:           windows/amd64
 Experimental:      false
error during connect: Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.40/version: open //./pipe/docker_engine: The system cannot find the file specified. In the default daemon configuration on Windows, the docker client must be run elevated to connect. This error may also indicate that the docker daemon is not running.
> docker-compose version
docker-compose version 1.24.1, build 4667896b
docker-py version: 3.7.3
CPython version: 3.6.8
OpenSSL version: OpenSSL 1.0.2q  20 Nov 2018
> docker-machine version
docker-machine.exe version 0.16.1, build cce350d7
```

これで PowerShell, cmd.exe で `docker` `docker-compose` `docker-machine` コマンドをたたけるようになりました。しかしローカル環境には docker engine(実体)がないのでエラーが出ていますね。ここではひとまず無視します。

## ゲスト機(Ubuntu)の準備

`docker-machine` コマンドを用いて VirtualBox をワンコマンドで立ち上げることも可能ですが、今回は手で Ubuntu Desktop イメージから作ることにします。

1. [Ubuntu 18.04 日本語 Remix](https://www.ubuntulinux.jp/News/ubuntu1804-ja-remix) の iso ファイルをダウンロード
2. VirtualBox で新規追加で適当に値を埋める
3. ディスクサイズは 40 GB くらいあった方が嬉しい(20GB 以下だとたくさん Docker レイヤーが増えると死ぬ)
4. iso を設定して起動して、 Ubuntu をインストール
5. インストールが終わったら再起動して普通に起動してデスクトップに入る

### パッケージ更新

```bash
$ sudo apt update \
 && sudo apt full-upgrade -y \
 && sudo apt install openssh-server -y \
 && sudo systemctl start sshd \
 && sudo apt autoremove -y
```

あたりでパッケージを更新して SSH サーバを用意して再起動。

### VirtualBox GuestAddition 用意

GuestAddition を入れてさらに再起動

### docker 用意

[ここを見ながら](https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-using-the-repository) docker をインストール

```bash
$ docker run --rm hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:6540fc08ee6e6b7b63468dc3317e3303aae178cb8a45ed3123180328bcc1d20f
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

...
```

これでゲスト機で `docker` コマンドをたたけるようになりました。

リモートからこの docker デーモンを操作出来るように、デフォルトではローカルしか受け付けていない所を上書きして、 TCP 2375 番で受け付けれるようにします。

[このあたり(Configuring remote access with systemd unit file)](https://docs.docker.com/install/linux/linux-postinstall/#configuring-remote-access-with-systemd-unit-file)

```bash
$ sudo systemctl edit docker.service
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375 --containerd=/run/containerd/containerd.sock
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker.service
```

これで任意の外部から `tcp://このゲスト機のIP:2375` 経由で `docker` を操作出来るようになりました **(もちろん外部から自由に叩けるということでセキュリティには注意！)** 。

### ホストからゲストに対して `docker` コマンドをたたいてみる

ホスト機から叩けるか確認してみます。

```powershell
> docker -H tcp://ゲスト機のIP:2375 run --rm hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

...
```

が出れば Windows 上から `docker` コマンドをちゃんと叩けるようになりました。環境変数からホスト名を指定することも出来ます。

```powershell
> $env:DOCKER_HOST="tcp://ゲスト機のIP:2375"
> docker version

...
```

## vscode の準備

ようやく vscode が出てきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332028/dc5b31ed-8e77-7efb-fec4-689f308a44aa.png)

[Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) 拡張機能をインストールします。

`docker.host` 設定に `tcp://ゲスト機のIP:2375` を設定します。

左のアクティビティバーの Docker アイコンをクリックして、 `Successfully connected, but no items found.` と出れば vscode での docker 接続成功です。やったぜ。

### dev container - node を試してみる

[Quick start: Try a dev container](https://code.visualstudio.com/docs/remote/containers#_quick-start-try-a-dev-container)

こちらを試してみます。今回は node.js の開発用コンテナを用意してみます。

```powershell
> git clone https://github.com/Microsoft/vscode-remote-try-node
> code vscode-remote-try-node
```

Docker for Windows や Docker for Mac であればこのままリモートコンテナにローカルのファイルをバインド出来るのですが、 Windows 10 Home さんではそれがかないません。

そこで面倒ですが一個中継を設けます。

### sftp でファイル転送

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332028/bf92c89f-6bc4-681c-4c93-1d31d9c7ca0c.png)

[sftp](https://marketplace.visualstudio.com/items?itemName=liximomo.sftp) 拡張機能をインストールします。

コマンドパレットで `sftp: config` とすると設定出来るようになるので、これを使って SFTP プロトコルでファイルをゲスト機に転送します。

```json:.vscode/sftp.json
{
  "name": "ubuntu1804",
  "host": "ゲスト機のIP",
  "protocol": "sftp",
  "port": 22,
  "username": "m-yamagishi",
  "remotePath": "/home/m-yamagishi/repos/vscode-remote-try-node",
  "ignore": [
    ".git",
    "node_modules"
  ]
}
```

設定は各々調整するようにしてください。個人設定が入るので、 `.git/hooks/exclude` に `.vscode/sftp.json` を追加して git から除外しておくのも大事です。

設定を正しく行ってコマンドから `sftp: sync local -> remote` を実行すれば、リモートにファイルが転送されます。

ではリモートのコンテナマウントを、今転送した上記の `remotePath` の所に変えましょう。 `.devcontainer/devcontainer.json` を修正します。

```json:.devcontainer/devcontainer.json
{
	"name": "Node.js Sample",
	"dockerFile": "Dockerfile",
	"appPort": 3000,
	"extensions": [
		"dbaeumer.vscode-eslint"
	],
	"settings": {
		"terminal.integrated.shell.linux": "/bin/bash"
	},
	"postCreateCommand": "yarn install",
	// Comment out the next line to run as root instead. Linux users, update
	// Dockerfile with your user's UID/GID if not 1000.
	"runArgs": [ "-u", "node" ],
	// この二行を追加する
	"workspaceFolder": "/workspace",
	"workspaceMount": "src=/home/m-yamagishi/repos/vscode-remote-try-node,dst=/workspace,type=bind,consistency=cached"
}
```

変更したら左下の >< を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332028/e7036815-82f6-9a7e-3e2f-3774d9f75d7b.png)

`Remote-Containers: Open Folder in Container` をコマンドパレットから選択すると、リモートで今のフォルダを開くように動きだします。最初はイメージのビルドが走るので遅いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332028/8f8903be-6067-05ea-a333-3d674c0817ea.png)

リモートコンテナの展開に成功すると、普通にファイル一覧が出てきます。このファイル一覧は「ローカルにあるもの」を「リモートに sftp でコピーしたもの」を「docker mount したもの」です(難しい)。

この状態で F5 を押すとデバッグが開始されます。ブレイクポイントなども正常に動いているようですね。やったぜ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332028/aca25f75-d99c-8c82-1cfa-fd49ebf8a38f.png)


このサンプルでは eslint しか extension を入れていませんが、 `.devcontainer/devcontainer.json` に書き加えて、 `Dockerfile` もそのエクステンションが動くように変更していけば他の extension も動くはずです。

また、 php など他の実行環境も同様の作業で完全リモートランタイムで動作させることが出来るようになるでしょう。凄い。これでローカルには vscode さえインストールしてしまえば OK! という世界になります。

Docker for Windows や Docker for Mac, Linux などであれば sftp などを使わなくても良いのでもっと簡単にこの環境が実現可能です。皆さんも是非やってみてください。

## 参考文献

- [Developing inside a Container using Visual Studio Code Remote Development](https://code.visualstudio.com/docs/remote/containers)
- [Windows の OpenSSH のインストール | Microsoft Docs](https://docs.microsoft.com/ja-jp/windows-server/administration/openssh/openssh_install_firstuse)
- [Advanced Container Configuration - Developing inside a container on a remote Docker host](https://code.visualstudio.com/docs/remote/containers-advanced#_developing-inside-a-container-on-a-remote-docker-host)