Docker CE 18.09からssh経由でリモートのdockerデーモンに接続できるようになるってよ

# はじめに
MacでDocker使うの重いですよね？ 

手元のローカル開発環境のノートPCはMacで、Dockerコンテナという構成は、小さなアプリケーションであれば快適です。
が、そこそこ大きなモノリシックなアプリケーションとかを動かそうとすると、クッソ重くてストレスフルです。
Dockerは新しめのLinux Kernelで動かすと効率的なんだけど、Macで動かそうとするとIOとか遅いし、なんなのかもっさりします。
かと言って、LinuxをノートPCに使うのは茨の道です。（個人の見解です）

そんな今日このごろ、Docker CE 18.09からssh経由でリモートのdockerデーモンに接続できるようになるようです。

https://github.com/docker/cli/pull/1014

mobyメンテナの須田さん、マジ神 :pray: 

つまり、dockerコマンドだけ手元のMacで動かして、dockerデーモンはAWSのEC2上にLinux立てたところで動かすみたいなことができるのでわ？？？
って思って、これを書いてる 2018/10/26時点で、Docker CE 18.09はまだベータリリースですが、待ちきれないので、試してみた。

結論から言うと、docker単体なら動くんだけど、docker-compose（厳密に言うとdocker-py）がまだサポートしてなくて、docker-composeなローカル開発環境の負荷をリモートにオフロードするには、まだちょっとパーツが足りなそうなかんじです。しょんぼり(´・ω・｀)

# 検証
## 環境

### dockerクライアント
* Mac OSX 10.14 (Mojave)
* Docker for Mac Edge (2.0.0.0-beta1-mac75 2018-09-14)
* Docker CE 18.09.0-ce-beta1
* docker-compose 1.22.0

### dockerデーモン
* AWS EC2
* Ubuntu 18.04 LTS
* Docker CE 18.09.0-beta5

## インストール

### dockerクライアント

Docker for MacのEdge版を入れます。

Edge版は以下からダウンロードできます。

https://docs.docker.com/docker-for-mac/edge-release-notes/

検証時点の最新版の「Docker Community Edition 2.0.0.0-beta1-mac75 2018-09-14」をダウンロードしてインストールしました。

```
$ docker version
Client: Docker Engine - Community
 Version:           18.09.0-ce-beta1
 API version:       1.39
 Go version:        go1.10.4
 Git commit:        78a6bdb
 Built:             Thu Sep  6 22:41:53 2018
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.0-ce-beta1
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.3
  Git commit:       78a6bdb
  Built:            Thu Sep  6 22:49:35 2018
  OS/Arch:          linux/amd64
  Experimental:     true
```

```
$ docker-compose version
docker-compose version 1.22.0, build f46880f
docker-py version: 3.4.1
CPython version: 3.6.4
OpenSSL version: OpenSSL 1.0.2o  27 Mar 2018
```

### dockerデーモン
検証用にAWS EC2に適当にUbuntu 18.04のインスタンスを立てました。

```
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.1 LTS"

$ uname -a
Linux ip-10-194-2-73 4.15.0-1021-aws #21-Ubuntu SMP Tue Aug 28 10:23:07 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
```

ベータリリースは `test.docker.com` から配信されているので、こいつを入れます。

```
$ curl -fsSL https://test.docker.com -o test-docker.sh
$ sudo sh test-docker.sh
# Executing docker install script, commit: 36b78b2
+ sh -c apt-get update -qq >/dev/null
+ sh -c apt-get install -y -qq apt-transport-https ca-certificates curl >/dev/null
+ sh -c curl -fsSL "https://download.docker.com/linux/ubuntu/gpg" | apt-key add -qq - >/dev/null
Warning: apt-key output should not be parsed (stdout is not a terminal)
+ sh -c echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic test" > /etc/apt/sources.list.d/docker.list
+ [ ubuntu = debian ]
+ sh -c apt-get update -qq >/dev/null
+ sh -c apt-get install -y -qq --no-install-recommends docker-ce >/dev/null
+ sh -c docker version
Client:
 Version:           18.09.0-beta5
 API version:       1.39
 Go version:        go1.10.4
 Git commit:        e1910c5
 Built:             Tue Oct 23 22:25:31 2018
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.0-beta5
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.4
  Git commit:       e1910c5
  Built:            Tue Oct 23 21:50:22 2018
  OS/Arch:          linux/amd64
  Experimental:     false
If you would like to use Docker as a non-root user, you should now consider
adding your user to the "docker" group with something like:

  sudo usermod -aG docker your-user

Remember that you will have to log out and back in for this to take effect!

WARNING: Adding a user to the "docker" group will grant the ability to run
         containers which can be used to obtain root privileges on the
         docker host.
         Refer to https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface
         for more information.
```

sshしているubuntuユーザでdockerソケットにアクセスできるように、dockerグループに入れておきます。

```
$ sudo usermod -aG docker ubuntu
```

グループに追加したら、一旦ログアウト/ログインしなおして、 sshしているユーザで、 `docker ps` コマンドが使えるか確認しておきます。

```

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
```

## リモートdockerデーモンとssh経由で接続してみる

以下のPullRequestのコメントを読んでたら、

https://github.com/docker/cli/pull/1014

`docker -H ssh://me@server` という指定で、リモートのdockerデーモンに接続できるらしいんですが、SSH鍵の指定方法が分からず、
 `~/.ssh/config` でのオプション指定は有効なようなので、 手元の `~/.ssh/config` に以下を設定してみました。

```
Host docker-18-09-test
  IdentityFile ~/.ssh/aws-dev.pem
  User ubuntu
  Hostname XXX.XXX.XXX.XXX
```

sshできるか念のため確認しておきます。

```
$ ssh docker-18-09-test
Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-1021-aws x86_64)

$ exit
```

でわ、手元のdockerクライアントから、リモートのdockerデーモンに接続してみましょう。

```
$ docker -H ssh://docker-18-09-test version
Client: Docker Engine - Community
 Version:           18.09.0-ce-beta1
 API version:       1.39
 Go version:        go1.10.4
 Git commit:        78a6bdb
 Built:             Thu Sep  6 22:41:53 2018
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.0-beta5
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.4
  Git commit:       e1910c5
  Built:            Tue Oct 23 21:50:22 2018
  OS/Arch:          linux/amd64
  Experimental:     false

```

お、リモートは `18.09.0-beta5` になってて、Ubuntu側につながってそうですね。

手元からリモートにdocker runしてみます。

```
$ docker -H ssh://docker-18-09-test run -d --name hoge nginx:alpine
Unable to find image 'nginx:alpine' locally
alpine: Pulling from library/nginx
4fe2ade4980c: Pull complete
c3f09dfaf47d: Pull complete
83283d0e9bb9: Pull complete
e2e530da9538: Pull complete
Digest: sha256:ae5da813f8ad7fa785d7668f0b018ecc8c3a87331527a61d83b3b5e816a0f03c
Status: Downloaded newer image for nginx:alpine
8eda9ca571dde9f752b12989fcdd7b344fec9f28e1a403bf46da4da457ae4794
```

リモート側で見てみると、たしかにコンテナが生まれています。よさそう。

```
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
8eda9ca571dd        nginx:alpine        "nginx -g 'daemon of…"   16 seconds ago      Up 14 seconds       80/tcp              hoge
```

手元からdocker execしてみます。

```
$ docker -H ssh://docker-18-09-test exec -it hoge /bin/sh
/ # ls
bin    dev    etc    home   lib    media  mnt    proc   root   run    sbin   srv    sys    tmp    usr    var
/ # exit

$
```

ちゃんとコンテナの標準入出力も取れますね。最高です。

## DOCKER_HOSTの設定

毎回 dockerソケット指定するのめんどくさいですが、ローカルのdockerコマンドを使いたい場合もあるので、aliasかなんかを適当に設定しておくか、

```
$ alias rdocker="docker -H ssh://docker-18-09-test"
```

```
$ rdocker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
8eda9ca571dd        nginx:alpine        "nginx -g 'daemon of…"   4 minutes ago       Up 4 minutes        80/tcp              hoge
```

もしくは、 `DOCKER_HOST` 環境変数でも設定できるようなので、 direnvかなにかで切り替えるとよんじゃないかな。

```
$ DOCKER_HOST=ssh://docker-18-09-test docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                NAMES
cd79f640e557        nginx:alpine        "nginx -g 'daemon of…"   17 minutes ago      Up 17 minutes       0.0.0.0:80->80/tcp   hoge
```

## ポートフォワード

リモートのdocker上のnginxにHTTPで接続したい場合は、80番ポートをリモートでexposeしておいて、

```
$ docker -H ssh://docker-18-09-test run -d --name hoge -p 80:80 nginx:alpine
```

リモートのサーバに普通にHTTP接続するか、もしくはHTTP接続が通信許可されていない場合は、

SSHポートフォワードでトンネルを掘り、リモートの80番をローカルの8080番ポートにマッピングしておき、

```
$ ssh -L 8080:127.0.0.1:80 docker-18-09-test
```

ローカルの8080番ポートにアクセスすれば、おーけー。

```
$ curl -I http://127.0.0.1:8080/
HTTP/1.1 200 OK
Server: nginx/1.15.5
Date: Fri, 26 Oct 2018 05:01:19 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 02 Oct 2018 19:21:37 GMT
Connection: keep-alive
ETag: "5bb3c541-264"
Accept-Ranges: bytes
```

## docker-composeと一緒に使う

普通にローカル開発していると、素のdockerコマンドだけじゃなくて docker-composeも使いたくなります。

結論から言うと、やってみたらエラーが出て、まだサポートされていないっぽい。

```
$ cat docker-compose.yml
version: '3'
services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
```

```
$ docker-compose -H ssh://docker-18-09-test up -d
Traceback (most recent call last):
  File "docker-compose", line 6, in <module>
  File "compose/cli/main.py", line 71, in main
  File "compose/cli/main.py", line 124, in perform_command
  File "compose/cli/command.py", line 41, in project_from_options
  File "compose/cli/command.py", line 121, in get_project
  File "compose/cli/command.py", line 92, in get_client
  File "compose/cli/docker_client.py", line 127, in docker_client
  File "site-packages/docker/api/client.py", line 118, in __init__
  File "site-packages/docker/utils/utils.py", line 256, in parse_host
docker.errors.DockerException: Invalid bind address protocol: ssh://docker-18-09-test
[8412] Failed to execute script docker-compose
```

```
$ DOCKER_HOST=ssh://docker-18-09-test docker-compose up -d
Traceback (most recent call last):
  File "docker-compose", line 6, in <module>
  File "compose/cli/main.py", line 71, in main
  File "compose/cli/main.py", line 124, in perform_command
  File "compose/cli/command.py", line 41, in project_from_options
  File "compose/cli/command.py", line 121, in get_project
  File "compose/cli/command.py", line 92, in get_client
  File "compose/cli/docker_client.py", line 127, in docker_client
  File "site-packages/docker/api/client.py", line 118, in __init__
  File "site-packages/docker/utils/utils.py", line 256, in parse_host
docker.errors.DockerException: Invalid bind address protocol: ssh://docker-18-09-test
[8460] Failed to execute script docker-compose
```

docker-compose が ssh:// をサポートしてないことに気づいて、調べたら依存している `docker-py` の問題っぽいので、とりあえずissueだけ立てて報告しておいた。

https://github.com/docker/docker-py/issues/2159

というわけで、docker-compose（厳密に言うとdocker-py）がまだサポートしてなくて、docker-composeなローカル開発環境の負荷をリモートにオフロードするには、まだちょっとパーツが足りなそうなかんじです。しょんぼり(´・ω・｀)