Check! systemd 起動の docker で、 daemon.json の hosts を指定するとエラーになるのを解消する

こんにちは、 @dz_ こと大平かづみです。

Prologue - はじめに
====
Docker を `systemctl`(sytemd) で起動してる場合に陥ったエラーの解決メモです。

Docker デーモンの設定は `daemon.json` に書くのですが、そこで `hosts` を指定すると以下のようにエラーになってしまいました。どうやら、 `docker.service` の起動スクリプトとコンフリクトしてるみたいです。今回はそれを回避した tips を書き留めました。

正攻法かわからないのですが、何かの参考になれば幸いです…！（ご参考の際は、ご自身の判断でお願いいたします。）

```bash:エラーの様子
# daemon.json の hosts を設定して～、
sudo vi /etc/docker/daemon.json

# docker をリスタートするも、エラー… (´・ω・`)
sudo systemctl restart docker

Job for docker.service failed because the control process exited with error code. See "systemctl status docker.service" and "journalctl -xe" for details.
```

参考
----

- [Control and configure Docker with systemd | Docker Documentation](https://docs.docker.com/engine/admin/systemd/)
- [Daemon configuration file | dockerd | Docker Documentation](https://docs.docker.com/engine/reference/commandline/dockerd//#daemon-configuration-file)

手順
====
`docker.service` の設定を確認する
----
まず、 `docker.service` の場所を確認します。

```bash
# ロードされている docker.service の場所を確認する
sudo systemctl status docker | grep Loaded

   Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
```

この `docker.service` の `ExecStart` を上書きするので、現在の設定値を確認しておきましょう。

`[Service]` > `ExecStart` には、 `dockerd` が `-H`（ホストを指定するオプション）付きで指定されています。

```bash
# ExecStart を確認する（前5行も合わせて表示）
cat /lib/systemd/system/docker.service | grep -B 5 ExecStart

[Service]
Type=notify
# 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://
```

`docker.service` の設定を上書きする
----

それでは `docker.service` の drop-in ファイルを編集します。（ nano で開かれます。）

```bash
# drop-in の上書き用ファイルを編集する
sudo systemctl edit docker.service
```

以下のように記入します。 `ExecStart` には、 `-H` オプションを除いた `dockerd` だけを指定します。

```ini
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd
```

- ※1 `ExecStart` は、最初の `ExecStart=` を書くことで値を上書きできるそうです。
- ※2 この設定は、 `/etc/systemd/system/docker.service.d/override.conf` に保存されます。

編集し終えたら、 `systemctl` に設定をリロードしておきます。

```bash
# 編集した *.service ファイルをリロードする
sudo systemctl daemon-reload
```

`daemon.json` を編集する
----

それから、 `daemon.json` を開き、適宜編集します。 `hosts` には、上記で消した `fd://` を追記します。

```bash
sudo vi /etc/docker/daemon.json
```
```json
{
  ...

  "hosts": [
    "tcp://<sample ip address>:2375",
    "fd://"
  ],

  ...
}
```

- ※3 余談ですが、2375ポートでの運用は本番には適さないので、2376 ポート x TLS を採用するとよいと思います。

- [Protect the Docker daemon socket | Docker Documentation](https://docs.docker.com/engine/security/https/)

`docker` をリスタートする
----

`daemon.json` の編集が終わったら、 docker をリスタートしてみましょう～

```bash
#docker をリスタートする
sudo systemctl restart docker
```

これで、エラーなくリロードされ、指定したホストを listen していれば大丈夫だと思います。

```bash
# docker の状態を確認する
$ sudo systemctl status docker | grep listen
Nov 15 12:56:41 xxx dockerd[51426]: time="2017-11-15T12:56:41.875937579+09:00" level=info msg="API listen on /var/run/docker.sock"
Nov 15 12:56:41 xxx dockerd[51426]: time="2017-11-15T12:56:41.876027580+09:00" level=info msg="API listen on <sample ip address>:2375"
```

以上です！

備考
====

トラブルシューティング
----
docker をリスタートしたとき、以下のようなエラー（最下段）が出た場合は、上記の `docker.service.d/override.conf` を見直してみてください。※1 の
 `ExecStart=` が必要です。

```bash
sudo systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: error (Reason: Invalid argument)
  Drop-In: /lib/systemd/system/docker.service.d
           └─exec-start.conf
   Active: active (running) since Wed 2017-11-15 15:43:41 JST; 10min ago
     Docs: https://docs.docker.com
 Main PID: 49176 (dockerd)
   CGroup: /system.slice/docker.service
           ├─49176 /usr/bin/dockerd -H fd://

...

Nov 15 15:43:41 xxx systemd[1]: Started Docker Application Container Engine.
Nov 15 15:53:36 xxx systemd[1]: docker.service: Service has more than one ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.
```

謝辞
----
systemd の drop-in について、コメント欄で @yuanying さんが教えてくださいました！ありがとうござます！

また、これらの systemd の設定の上書きについては、こちらのページを参考にしました！ありがとうございます！

- [systemdで既存のunitを編集する2つの方法 - Qiita](https://qiita.com/nvsofts/items/529e422bb8a326401c39)

Prologue - おわりに
====

このおかげで、Docker の理解が深まりました～（負け惜しみ (/・ω・)/