ssh tunnelを使ってリモートのDockerを操作する方法

# Dockerのリモート接続について
Dockerはデフォルトではネットワークを通さないUnixソケットで操作できます。オプションでHTTPソケットを使っても操作できるのですが、ネットワーク上で安全に使う方法としてTLSを有効にして、証明書を使う方法が推奨されています。
今回は、証明書を使う方法ではなく、ssh tunnelを使ってリモートのDockerを操作できるようにします。

# 実現したいこと
ラズパイで動かしているDockerをmacからリモートで操作できるようにする。

# 環境
- Dockerホスト(Raspberry Pi 3 Model B)

```shell-session
$ uname -a
Linux raspberrypi 4.14.98-v7+ #1200 SMP Tue Feb 12 20:27:48 GMT 2019 armv7l GNU/Linux
$ grep -H "" /etc/*version ; grep -H "" /etc/*release
/etc/debian_version:9.8
/etc/os-release:PRETTY_NAME="Raspbian GNU/Linux 9 (stretch)"
/etc/os-release:NAME="Raspbian GNU/Linux"
/etc/os-release:VERSION_ID="9"
/etc/os-release:VERSION="9 (stretch)"
/etc/os-release:ID=raspbian
/etc/os-release:ID_LIKE=debian
/etc/os-release:HOME_URL="http://www.raspbian.org/"
/etc/os-release:SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
/etc/os-release:BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
$ docker version
Client:
 Version:           18.09.0
 API version:       1.39
 Go version:        go1.10.4
 Git commit:        4d60db4
 Built:             Wed Nov  7 00:57:21 2018
 OS/Arch:           linux/arm
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.0
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.4
  Git commit:       4d60db4
  Built:            Wed Nov  7 00:17:57 2018
  OS/Arch:          linux/arm
  Experimental:     false
```

- Dockerクライアント(macOS Mojave)

```shell-session
$ uname -a
Darwin xxx-MacBook-Pro.local 18.2.0 Darwin Kernel Version 18.2.0: Thu Dec 20 20:46:53 PST 2018; root:xnu-4903.241.1~1/RELEASE_X86_64 x86_64
$ docker version
Client: Docker Engine - Community
 Version:           18.09.1
 API version:       1.39
 Go version:        go1.10.6
 Git commit:        4c52b90
 Built:             Wed Jan  9 19:33:12 2019
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.1
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.6
  Git commit:       4c52b90
  Built:            Wed Jan  9 19:41:49 2019
  OS/Arch:          linux/amd64
  Experimental:     true
```

# Dockerホスト(ラズパイ)側の準備
dockerデーモンの起動オプションでTCPソケット通信を使うようにすることができるのですが、今回はsocatを使います。さらにsocatはdockerコンテナ内で実行したいと思います。

```dockerfile:Dockerfile
FROM alpine:latest
RUN set -x && apk update && apk add \
    socat \
    && rm -rf /var/cache/apk/*
ENTRYPOINT [ "socat" ]
CMD [ "-v", "tcp-listen:2375,reuseaddr,fork", "unix-connect:/var/run/docker.sock" ]
```

dockerイメージをビルドします。

```shell-session
$ docker build ./ -t socat:alpine
```

socatをdockerコンテナとして実行します。

```shell-session
$ docker run --rm -d -v /var/run/docker.sock:/var/run/docker.sock -p 127.0.0.1:2375:2375 --name=socat socat:alpine
```

これでループバックアドレス(127.0.0.1)のポート2375に接続することでDockerのUnixソケットと通信できるようになります。

# Dockerクライアント（mac）側の準備
ラズパイにssh接続する時にLocalForwardingで接続します。

```shell-session
$ ssh pi@raspberrypi.local -L 2375:127.0.0.1:2375
```

.ssh/config記載例

```shell:.ssh/config
Host raspberrypi-docker
  Hostname raspberrypi.local
  User pi
  LocalForward 2375 localhost:2375
```

# Dockerクライアント（mac）側でdockerコマンドの実行
dockerコマンド実行時にオプションとして「-H 127.0.0.1」をつけて実行します。

```shell-session
$ docker -H 127.0.0.1 ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                      NAMES
55db512ab7fd        socat:alpine        "socat -v tcp-listen…"   9 minutes ago       Up 9 minutes        127.0.0.1:2375->2375/tcp   socat
$ docker -H 127.0.0.1 version
Client: Docker Engine - Community
 Version:           18.09.1
 API version:       1.39
 Go version:        go1.10.6
 Git commit:        4c52b90
 Built:             Wed Jan  9 19:33:12 2019
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.0
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.4
  Git commit:       4d60db4
  Built:            Wed Nov  7 00:17:57 2018
  OS/Arch:          linux/arm
  Experimental:     false
```

ラズパイ上のdockerホストが応答しているのが、確認できると思います。

※ホームネットワーク内など安全が確保されているであろうネットワーク内であれば、わざわざssh tunnelせずともdockerホスト側でsocatを実行する時にループバックアドレス(127.0.0.1)指定ではなく、0.0.0.0を指定すれば、dockerコマンド実行時のオプションとして「-H raspberrypi.local」を付与すれば実行できるようにもなります。