VS CodeのDevContainerをVMなどssh接続先で使う

# tl;dr

- 環境変数`DOCKER_HOST`、設定`docker.host`は**使わない**
- docker contextを使ってSSH接続先のdockerを使えるようにする 
  - `docker context create remote --docker 'host=ssh://user@remote-host`
  - `docker context use remote`
- コマンド "Remote-Containers: Open Repository in Container..."でリポジトリを直接開く

# 目的

macOSではDocker Desktop for macが多少性能が辛いので、やはり外部マシンやVM（以降、リモートホストと呼ぶ）上のDockerを使いたくなる。
Docker Desktop を使っている場合、コマンド"Remote-Containers: Reopen in Container"を実行すればすぐに利用できるが、リモートホストのDockerを使う場合の方法をまとめる。

# 一次情報

Googleで検索すると、環境変数 `DOCKER_HOST` や、設定 `docker.host`を使った方法が見つかるが、現在はそれは推奨されていない様子。

https://code.visualstudio.com/docs/containers/ssh#_set-up-ssh-tunneling

# 環境準備

以下の準備が整っているとする。

- リモートホストにsshで認証鍵を使ってアクセスできる状態であること。
- リモートホストでdockerがrootで実行され、そのdockerにログインユーザの権限でアクセスできること。
  - Rootlessは未対応だった
  - sudoを付けずにdockerコマンドが使えること（`sudo usermod -aG docker $USER`が実行されていること）
- ローカルにdockerクライアントがインストールされていること（Docker Desktopが、起動はしていなくともインストールされていること）
- リポジトリはgithub.comでほすとされていること
- リモートホストからリポジトリにアクセスできること

# 手順

## docker contextでリモートホストのdockerにアクセスできるようにする

リモートホストのDockerを使う場合、パラメータ`-H`を使って使う先のDockerデーモンを指定する。
この時、必ずuser名を指定する（リモートホストと同じユーザ名出会っても必要）。

```
docker -H ssh://user@remote-host ps
```

これを、docker contextを使って、この設定をコンテキスト`remote`として保存する。

```
docker context create remote --docker 'host=ssh://user@remote-host'
```

コンテキストのリストはdocker context lsで確認できる。

```
docker context ls

NAME                DESCRIPTION                               DOCKER ENDPOINT                     KUBERNETES ENDPOINT   ORCHESTRATOR
default             Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                               swarm
remote *                                                      ssh://nnyn@remote-host                                         
rootless                                                      unix:///run/user/1000/docker.sock  
```

コンテキストを有効にするため、docker context useを実行する。すると、パラメータ`-H`なしにリモートホストのDockerデーモンにアクセスできるようになる。

```
docker context use remote
docker ps
```

リモートホストに複数のDockerデーモンがある場合、一度SSHでログインし、その中でdocker contextで指定する。

```
ssh remote-host
docker context ls

NAME                DESCRIPTION                               DOCKER ENDPOINT                         KUBERNETES ENDPOINT                 ORCHESTRATOR
default             Current DOCKER_HOST based configuration   unix:///var/run/docker.sock             https://104.198.114.144 (default)   swarm
rootless *                                                    unix:///home/nnyn/var/run/docker.sock  

docker use default
```

## .devcontainer/devcontainer.json を作成する

一旦ローカルでリポジトリをチェックアウトし、リポジトリにDevContainerの設定である.devcontainer/devcontainer.jsonを追加し、コミットし、リポジトリにpushする。

このサンプルは https://github.com/microsoft/vscode-dev-containers/tree/master/containers のリポジトリで公開されているため、このリポジトリからDockerfile含めてコピーしてくると良い（後から編集可能）。

## リモートホスト上でDevContainerを開く

ローカルのVS Code（リモートSSH状態ではない）で、コマンド"Remote-Containers: Open Repository in Container..."を実行し、リポジトリを指定する。github.comの場合、`74th/vscode-typescript-handson`のようにgithub.comは省略する。

![vscode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3413/882eb765-933b-62dd-7783-d8c2bea42d18.png)

![vscode2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3413/07b0ca74-e4fe-1ed9-5572-68cbf8733985.png)

すると、リモートホスト上でリポジトリのチェックアウトが行われ、DevContainerとして起動する。

![vscode3.1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3413/18877fa2-3f8e-8cac-c0b0-06b2d4dca6c0.png)

## 終了する

VS CodeのWindowを閉じると、接続は切れるが、Dockerコンテナ自体は起動したままになっている。

```
ssh remote-host
docker ps                                                                                                                    

CONTAINER ID        IMAGE                                                            COMMAND                  CREATED             STATUS              PORTS               NAMES
a5a03aabd34c        vsc-vscode-typescript-handson-766edd70c4c481fbe7625792ad38d7e2   "/bin/sh -c 'echo Co…"   48 minutes ago      Up 48 minutes                           loving_bassi
```

そのため、docker stopで停止させる。

```
docker stop loving_bassi
```

## 続きから開発を行う

停止させた状態から、DevContainerを起動して開発を始めるには、リモートタブから目的のコンテナを選択する。
これで、コンテナの起動から接続まで自動で行ってくれる。

![vscode3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3413/cfb7a1e8-0c11-c6a3-ec19-c7a52a03a26b.png)