今回は、Docker クライアントをリモートの Docker ホストに SSH Port Forward 経由で接続させてコンテナを操作する方法を試してみる。
まず、Docker クライアントの環境は次のとおり。 macOS に Docker for Mac をインストールしてある。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.15.7 BuildVersion: 19H2 $ docker version Client: Docker Engine - Community Cloud integration 0.1.18 Version: 19.03.13 API version: 1.40 Go version: go1.13.15 Git commit: 4484c46d9d Built: Wed Sep 16 16:58:31 2020 OS/Arch: darwin/amd64 Experimental: false Server: Docker Engine - Community Engine: Version: 19.03.13 API version: 1.40 (minimum version 1.12) Go version: go1.13.15 Git commit: 4484c46d9d Built: Wed Sep 16 17:07:04 2020 OS/Arch: linux/amd64 Experimental: false containerd: Version: v1.3.7 GitCommit: 8fba4e9a7d01810a393d5d25a3621dc101981175 runc: Version: 1.0.0-rc10 GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd docker-init: Version: 0.18.0 GitCommit: fec3683
リモートの Docker ホストの環境は次のとおり。 Ubuntu 18.04 LTS に Docker をインストールしてある。
$ cat /etc/*-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=18.04 DISTRIB_CODENAME=bionic DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS" NAME="Ubuntu" VERSION="18.04.4 LTS (Bionic Beaver)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 18.04.4 LTS" VERSION_ID="18.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=bionic UBUNTU_CODENAME=bionic $ sudo docker version Client: Version: 19.03.6 API version: 1.40 Go version: go1.12.17 Git commit: 369ce74a3c Built: Fri Feb 28 23:45:43 2020 OS/Arch: linux/amd64 Experimental: false Server: Engine: Version: 19.03.6 API version: 1.40 (minimum version 1.12) Go version: go1.12.17 Git commit: 369ce74a3c Built: Wed Feb 19 01:06:16 2020 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.3.3-0ubuntu1~18.04.2 GitCommit: runc: Version: spec: 1.0.1-dev GitCommit: docker-init: Version: 0.18.0 GitCommit:
ようするに、上記の macOS を Docker クライアント、Ubuntu を Docker サーバとして動作させたい、ということ。
Docker デーモンをローカルホストの TCP:2376 で待ち受けるようにする
デフォルトでは、Docker デーモンは Unix ドメインソケット経由で制御されるように設定されている。 しかし、これだとリモートから扱う上で都合が悪い。 そこで、まずは Docker ホストのデーモンを TCP で待ち受けるように変更する。
まずは、リモートにある Docker ホストにログインする。 この段階では、シェルの操作ができさえすれば良いので、別にログインの方法は何でも構わない。
$ ssh <hostname>
はじめに、Docker の設定が入った systemd のコンフィグをバックアップしておく。
$ sudo cp /lib/systemd/system/docker.service{,.orig}
そして、次のように dockerd
のオプションとしてローカルホストの 2376 ポートで待ち受けるようにオプションを追加する。
$ diff -u /lib/systemd/system/docker.service{.orig,} --- /lib/systemd/system/docker.service.orig 2020-10-10 14:22:10.482817997 +0000 +++ /lib/systemd/system/docker.service 2020-10-10 14:22:56.297714001 +0000 @@ -11,7 +11,7 @@ # the default is not to use systemd for cgroups because the delegate issues still # exists and systemd currently does not support the cgroup feature set required # for containers run by docker -ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock +ExecStart=/usr/bin/dockerd -H fd:// -H tcp://127.0.0.1:2376 --containerd=/run/containerd/containerd.sock ExecReload=/bin/kill -s HUP $MAINPID TimeoutSec=0 RestartSec=2
ちなみに、上記は APT でインストールしたパッケージが管理しているファイルなので、直接編集するのはあまりお行儀が良くない。
$ dpkg-query -L docker.io | grep systemd
/lib/systemd
/lib/systemd/system
/lib/systemd/system/docker.service
/lib/systemd/system/docker.socket
編集したらサービスを再起動する。
$ sudo systemctl daemon-reload $ sudo systemctl restart docker
これで Docker デーモンがローカルホストの TCP:2376 ポートで待ち受けるようになる。
$ ss -tlnp | grep 2376 LISTEN0 128 127.0.0.1:2376 0.0.0.0:*
これでリモートの Docker ホストの準備は整った。
クライアントからリモートの Docker デーモンを操作する
ここからは Docker クライアントの操作になる。
リモートの Docker ホストに、SSH Port Forward を有効にしてあらためてログインする。
$ ssh -L 2376:localhost:2376 <hostname>
これで、ローカルの TCP:2376 ポートにアクセスすると、リモートの TCP:2376 ポートにつながることになる。
$ lsof -i -P | grep -i listen | grep 2376 ssh 7028 amedama 5u IPv6 0xda00d64e20c3a761 0t0 TCP localhost:2376 (LISTEN) ssh 7028 amedama 6u IPv4 0xda00d64e24baa8d1 0t0 TCP localhost:2376 (LISTEN)
あとは、シェル変数の DOCKER_HOST
に tcp://127.0.0.1:2376
を指定して docker
コマンドを使うだけ。
$ DOCKER_HOST=tcp://127.0.0.1:2376 docker version Client: Docker Engine - Community Cloud integration 0.1.18 Version: 19.03.13 API version: 1.40 Go version: go1.13.15 Git commit: 4484c46d9d Built: Wed Sep 16 16:58:31 2020 OS/Arch: darwin/amd64 Experimental: false Server: Engine: Version: 19.03.6 API version: 1.40 (minimum version 1.12) Go version: go1.12.17 Git commit: 369ce74a3c Built: Wed Feb 19 01:06:16 2020 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.3.3-0ubuntu1~18.04.2 GitCommit: runc: Version: spec: 1.0.1-dev GitCommit: docker-init: Version: 0.18.0 GitCommit:
上記から、クライアントとサーバで Docker のバージョンが異なっていることが確認できる。
また、シェル変数を有効にしたときとしないときで uname -a
の結果が変わっていることもわかる。
コンテナはホストのカーネルを共有するため、このようなことになる。
$ docker container run -it alpine:latest uname -a Linux 829155145f04 4.19.76-linuxkit #1 SMP Tue May 26 11:42:35 UTC 2020 x86_64 Linux $ DOCKER_HOST=tcp://127.0.0.1:2376 docker container run -it alpine:latest uname -a Linux f4981d8001f3 4.15.0-111-generic #112-Ubuntu SMP Thu Jul 9 20:32:34 UTC 2020 x86_64 Linux
これで、ローカルの docker
コマンドを使って、リモートにある Docker ホスト上のコンテナを操作できるようになった。
注意点
ただし、このやり方にはいくつか注意点もある。
たとえば、このやり方ではコンテナが動いているのはあくまでリモートのホストになる。 そのため、ホストのボリュームをコンテナでマウントしようとしたときに使われるディレクトリはリモートのものになる。
確認しておこう。 まずはリモートにある Docker ホストにディレクトリを作ってファイルを用意する。
$ mkdir -p /tmp/mnt $ echo "Remote" > /tmp/mnt/loc.txt
そして、ローカルの Docker クライアントにも同じようにディレクトリを作って区別できるようにファイルを用意する。
$ mkdir -p /tmp/mnt $ echo "Local" > /tmp/mnt/loc.txt
上記で作ったディレクトリをマウントしたコンテナを起動してみよう。
$ export DOCKER_HOST=tcp://127.0.0.1:2376 $ docker container run \ -v /tmp/mnt:/mnt \ -it ubuntu:latest \ bash
そして、ファイルの中身を確認する。
# cat /mnt/loc.txt
Remote
うん、リモートだね。
あとは、受け付けるアドレスをローカルホストに絞っているとはいえ、これだとホストにログインできるユーザは誰でも Docker が使える。 もし、それが好ましくない状況であれば、以下のようにクライアントからのアクセス制御をした方が良いと思われる。
いじょう。
- 作者:もみじあめ
- 発売日: 2020/02/29
- メディア: Kindle版