Arch Linux に Docker をインストールしてリモートで動かす

# インストール

[docker](https://www.archlinux.jp/packages/?name=docker) パッケージをインストールします。

```console
# pacman -Syu docker
# systemctl enable docker
# systemctl restart docker
```

インストールが完了し、docker デーモンが起動していることを確認します。

```console
# docker info
```

# Hello World

初めてのコンテナを実行します。以下のような出力が得られたら成功です。

```console
# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:fc6a51919cfeb2e6763f62b6d9e8815acbf7cd2e476ea353743570610737b752
Status: Downloaded newer image for hello-world:latest

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

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
```

# リモートアクセス (平文)

## サーバーの設定

リモートホストから docker を利用できるようにするため、ポートを指定して Remote API を公開します。慣習的に通信を暗号化しない場合のポート番号は 2375 です。

```console
# systemctl edit docker
```

```config:/lib/systemd/system/docker.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375
```

```console
# systemctl daemon-reload
# systemctl restart docker
```

## クライアントの設定

以下の環境変数を設定します。

```console
% export DOCKER_HOST=a300:2375
```

## 使い方

クライアント上の docker コマンドが常にサーバー上で実行されます。

```console
% docker version
Client: Docker Engine - Community
 Version:           19.03.5
 API version:       1.40
 Go version:        go1.12.12
 Git commit:        633a0ea
 Built:             Wed Nov 13 07:22:34 2019
 OS/Arch:           darwin/amd64
 Experimental:      false

Server:
 Engine:
  Version:          19.03.6-ce
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.7
  Git commit:       369ce74a3c
  Built:            Thu Feb 13 18:14:54 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.3.3.m
  GitCommit:        d76c121f76a5fc8a462dc64594aea72fe18e1178.m
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
```

# リモートアクセス (TLS)

TLS で通信を暗号化するには証明書が必要です。ここでは自己署名証明書を作成する前提で説明します。

## サーバー証明書

まずは認証局 (CA) の秘密鍵と公開鍵を生成します。

```console
# mkdir /etc/docker/certs.d
# cd /etc/docker/certs.d
# openssl genrsa -out ca-key.pem 4096
# openssl req -new -x509 -days 3650 -key ca-key.pem -sha256 -out ca.pem
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:a300
Email Address []:
```

サーバーの秘密鍵と CSR (Certificate Signing Request) を作成します。

```console
# openssl genrsa -out server-key.pem 4096
# sudo openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr
```

サーバー証明書を生成します。ここでは、ホスト名あるいは IP アドレスでアクセスできるように設定しています。

```config:/etc/docker/certs.d/extfile.cnf
subjectAltName = DNS:a300,IP:192.168.0.16,IP:127.0.0.1
extendedKeyUsage = serverAuth
```

```console
# openssl x509 -req -days 3650 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
# rm server.csr extfile.cnf
```

秘密鍵には root (Docker) だけがアクセスできるように、証明書には誰でもアクセスできるようにパーミッションを設定します。

```console
# chmod -v 0400 ca-key.pem server-key.pem
# chmod -v 0444 ca.pem server-cert.pem
```

## クライアント証明書

クライアントの秘密鍵と CSR (Certificate Signing Request) を作成します。

```console
# openssl genrsa -out key.pem 4096
# sudo openssl req -subj '/CN=mbp2015' -new -key key.pem -out client.csr
```

クライアント証明書を生成します。

```config:/etc/docker/certs.d/extfile-client.cnf
extendedKeyUsage = clientAuth
```

```console
# openssl x509 -req -days 3650 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile-client.cnf
# rm client.csr extfile-client.cnf
```

root 権限でサーバーに ssh ログインできない場合、scp でクライアント証明書と秘密鍵を取得するには home ディレクトリに移動して管理者を変更する必要があります。

```console
# mv key.pem ~
# mv cert.pem ~
# cp ca.pem ~
# chown $USER key.pem cert.pem ca.pem
```

```console
% mkdir -p ~/.docker/certs.d
% scp a300:~/key.pem ~/.docker/certs.d
% scp a300:~/cert.pem ~/.docker/certs.d
% scp a300:~/ca.pem ~/.docker/certs.d
% chmod 400 ~/.docker/certs.d/key.pem
% chmod 444 ~/.docker/certs.d/cert.pem ~/.docker/certs.d/ca.pem
```

サーバーからは必ずクライアント秘密鍵を削除してください。この秘密鍵を使用すると sudo なしで docker を実行できます。これはそのユーザーに root 権限を与えることと同じことです。

```console
# rm ~/key.pem ~/cert.pem ~/ca.pem
```

## サーバーの設定

リモートホストから docker を利用できるようにするため、ポートを指定して Remote API を公開します。慣習的に通信を暗号する場合のポート番号は 2376 です。

```console
# systemctl edit docker
```

```config:/lib/systemd/system/docker.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/etc/docker/certs/ca.pem --tlscert=/etc/docker/certs/server-cert.pem --tlskey=/etc/docker/certs/server-key.pem -H fd:// -H tcp://0.0.0.0:2376
```

```console
# systemctl daemon-reload
# systemctl restart docker
```

## クライアントの設定

以下の環境変数を設定します。

```console
% export DOCKER_CERT_PATH=~/.docker/certs.d
% export DOCKER_HOST=a300:2376
% export DOCKER_TLS_VERIFY=1
```

## 使い方

クライアント上の docker コマンドが常にサーバー上で実行されます。

```console
% docker version
Client: Docker Engine - Community
 Version:           19.03.5
 API version:       1.40
 Go version:        go1.12.12
 Git commit:        633a0ea
 Built:             Wed Nov 13 07:22:34 2019
 OS/Arch:           darwin/amd64
 Experimental:      false

Server:
 Engine:
  Version:          19.03.6-ce
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.7
  Git commit:       369ce74a3c
  Built:            Thu Feb 13 18:14:54 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.3.3.m
  GitCommit:        d76c121f76a5fc8a462dc64594aea72fe18e1178.m
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
```

# 自動補完

## zsh

.zshrc で fpath に定義ファイルを置くディレクトリのパスを指定します。

```zsh
fpath=($ZDOTDIR/completion $fpath)
autoload -Uz compinit
compinit -i -d "$ZCACHEDIR/.zcompdump"
```

docker と docker-compose の定義ファイルを取得し、シェルを再起動してください。

```console
mkdir $ZDOTDIR/completion
curl -L https://raw.githubusercontent.com/docker/cli/master/contrib/completion/zsh/_docker > $ZDOTDIR/completion/_docker
curl -L https://raw.githubusercontent.com/docker/compose/master/contrib/completion/zsh/_docker-compose > $ZDOTDIR/completion/_docker-compose
exec $SHELL -l
```