yum update するとdocker.serviceがデフォルトに戻る問題への対処

## 結論
書き換わってほしくない設定は、`docker.service` ではなく `/etc/docker/daemon.json` に書きましょう。

## 前提
* CentOS Linux release 7.8.2003 (Core)
* Docker version 19.03

## 概要
yum updateをcronで走らせていたところ、ある日のアップデート後にDockerイメージがすっからかんになっていました。  
原因はDockerデーモンの実行時のルートをサービスファイル内で書き替えていたためでした。 
yum updateのタイミングで`docker.service`が新しいバージョンのデフォルト設定に置き換わり、実行時のルートが既定値(`/var/lib/docker`)に戻ってしまっていたわけですね。

## 再現手順
もともとdocker-ceが入っていたマシンで`yum remove docker-ce` した後を想定しています。

### ちょっと前のバージョンのDockerをインストールする
ちょっと前のバージョンは以下のコマンドで確認できます。

```
# yum list --showduplicates | grep docker-ce.x86_64
docker-ce.x86_64                       17.03.0.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64                       17.03.1.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64                       17.03.2.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64                       17.03.3.ce-1.el7        docker-ce-stable
docker-ce.x86_64                       17.06.0.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64                       17.06.1.ce-1.el7.centos docker-ce-stable
(以下略)
```
アーキテクチャが`x86_64`でない場合は適宜読み替えてください。適当に `19.03.10`でもインストールします。(最新でなければ何でもいいです。)

```
# yum install -y docker-ce-19.03.10-3.el7
```

### `docker.service`を書き替える
もとのファイルは以下の通りです。長いので書き替える予定の[Service]の部分だけ抜粋します。

```
[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:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
```

これを以下のように変更します。`/opt/docker` はあらかじめ作っておきます。

```
[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:// --graph /opt/docker --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
```

`hello-world`を実行し、イメージが`/opt/docker`配下に行ったことを確認します。

```
# systemctl start docker
# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
(省略)

# ll /opt/docker
total 0
drwx------. 2 root root  24 Nov  2 04:46 builder
drwx--x--x. 4 root root  92 Nov  2 04:46 buildkit
drwx------. 3 root root  78 Nov  2 04:46 containers
drwx------. 3 root root  22 Nov  2 04:46 image
drwxr-x---. 3 root root  19 Nov  2 04:46 network
drwx------. 6 root root 261 Nov  2 04:46 overlay2
drwx------. 4 root root  32 Nov  2 04:46 plugins
drwx------. 2 root root   6 Nov  2 04:46 runtimes
drwx------. 2 root root   6 Nov  2 04:46 swarm
drwx------. 2 root root   6 Nov  2 04:46 tmp
drwx------. 2 root root   6 Nov  2 04:46 trust
drwx------. 2 root root  25 Nov  2 04:46 volumes

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              bf756fb1ae65        10 months ago       13.3kB
```

良いですね。


### yum update する
2020/11/02時点で最新の `19.03.13`に更新します。

```
# yum update -y docker-ce
# docker --version
Docker version 19.03.13, build 4484c46d9d
```

サービスファイルを確認します。([Service]のみ抜粋)

```
# cat /usr/lib/systemd/system/docker.service
[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:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
```

先ほど設定したはずの `--graph=/opt/docker`が消えていますね。
以下のように `/opt/docker`には元のファイルが残っているにもかかわらず、イメージ一覧はからっぽになりました。

```
# ll /opt/docker/
total 0
drwx------. 2 root root  24 Nov  2 04:46 builder
drwx--x--x. 4 root root  92 Nov  2 04:46 buildkit
drwx------. 3 root root  78 Nov  2 04:46 containers
drwx------. 3 root root  22 Nov  2 04:46 image
drwxr-x---. 3 root root  19 Nov  2 04:46 network
drwx------. 6 root root 261 Nov  2 04:46 overlay2
drwx------. 4 root root  32 Nov  2 04:46 plugins
drwx------. 2 root root   6 Nov  2 04:46 runtimes
drwx------. 2 root root   6 Nov  2 04:46 swarm
drwx------. 2 root root   6 Nov  2 04:46 tmp
drwx------. 2 root root   6 Nov  2 04:46 trust
drwx------. 2 root root  25 Nov  2 04:46 volumes

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
```

## 解決方法
`/etc/docker/daemon.json`を利用します。

デフォルトで入る`docker.service`の`execStart`の内容は以下の通り

```
/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
```

ここに設定されているオプション以外はすべてデフォルト値が利用されます。[公式ガイド](https://docs.docker.jp/engine/reference/commandline/dockerd.html?highlight=dockerd)をもとに、関係ありそうなオプションをチェックしましょう。

| オプション | 既定値 | 説明 |
|:--|:--|:--|
| -g, --graph  | /var/lib/docker  | Docker 実行時の Docker ルート |
| --config-file | /etc/docker/daemon.json | デーモン設定ファイル |

`--graph` は更新時に入るサービスファイルでは未指定のため、どうあがいても`/var/lib/docker`に戻ってしまいます。なのでデフォルトで `--config-file` として指定されている `/etc/docker/daemon.json` を編集すれば良いです。 
Dockerインストール時には存在しないファイルなので、新しく作成します。 
以下の内容になるようファイルを編集しましょう。

```
# cat /etc/docker/daemon.json
{
    "graph": "/opt/docker"
}
```

## 確認
一度docker-ceをアンインストールし、もう一度アップデートを試します。確認のためDocker実行時のルートの中身も消しておきます。

```
# yum remove docker-ce
# rm -rf /var/lib/docker/*
# rm -rf /opt/docker/*
```

・過去バージョンのインストール

```
# yum -y yum install -y docker-ce-19.03.10-3.el7
# systemctl stard docker
```

・設定ファイルの確認

```
# cat /usr/lib/systemd/system/docker.service
[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:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

# cat /etc/docker/daemon.json
{
        "graph": "/opt/docker"
}
```

・hello-world実行

```
# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
(以下省略)

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              bf756fb1ae65        10 months ago       13.3kB

# ll /opt/docker/
total 0
drwx------. 2 root root  24 Nov  2 05:14 builder
drwx--x--x. 4 root root  92 Nov  2 05:14 buildkit
drwx------. 3 root root  78 Nov  2 05:14 containers
drwx------. 3 root root  22 Nov  2 05:14 image
drwxr-x---. 3 root root  19 Nov  2 05:14 network
drwx------. 6 root root 261 Nov  2 05:14 overlay2
drwx------. 4 root root  32 Nov  2 05:14 plugins
drwx------. 2 root root   6 Nov  2 05:14 runtimes
drwx------. 2 root root   6 Nov  2 05:14 swarm
drwx------. 2 root root   6 Nov  2 05:14 tmp
drwx------. 2 root root   6 Nov  2 05:14 trust
drwx------. 2 root root  25 Nov  2 05:14 volumes

```

・yum update 実行

```
# yum -y update docker-ce
# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              bf756fb1ae65        10 months ago       13.3kB
```

これでcronでDockerを更新しても大丈夫ですね！

## 終わりに
`daemon.json` はサービスファイルの `execStart`で設定できる部分はおそらく全てカバーできるはずです。`docker.service` はノータッチで、`daemon.json`を更新しましょう。 
ちなみに僕はこれにしばらく気づかずcronのyum updateの後処理でsedかけてました。そんなゴリ押しはやめましょう。(自戒)

## 参考
* [Docker-docs-ja daemon](https://docs.docker.jp/engine/reference/commandline/dockerd.html?highlight=dockerd)  
 デーモン起動時のオプションの日本語リファレンス