Docker のボリュームが丸ごと消えた話（と、復元した話）

# まとめ

- ボリュームを short syntax で指定すると、バインドマウントとボリュームマウントが意図通りに設定されないことがある
- long syntax を使うようにしよう

> docker-compose の bind mount を1行で書くな

https://zenn.dev/sarisia/articles/0c1db052d09921

- Docker のボリュームの中身が消えてしまった場合、`/var/lib/docker/volumes` に残っているかもしれない

# 発生した現象

最初、以下のようなボリュームを使用していました。これは、ホストマシンの `./data` ディレクトリをコンテナ内の `/home/yuinore/data` にバインドマウントするという意味になります。(①)

```yml:docker-compose.yml
volumes:
  - ./data:/home/yuinore/data
```

ここで、ボリュームマウントを試してみようと思い、以下のように書き換えました。(②)

```yml:docker-compose.yml
volumes:
  - /home/yuinore/data
```

しかし、やっぱり元のバインドマウントの方が良いなと思い、元のように書き換えました。(③)

```yml:docker-compose.yml
volumes:
  - ./data:/home/yuinore/data
```

数日後、PC を再起動した際に突然データが消えてしまいました。しかも、ホストの `data` ディレクトリにもデータは残っていませんでした。

# 裏で何が起きていたか

名前のないボリュームマウントの場合、ボリューム名は機械的に生成されるものですので、なにかの拍子でデータが消えてしまっても（消えたように見えてしまっても）不思議ではありません。（復元方法は記事の下の方に書いておきます。）

しかし、バインドマウントに戻したはずなので、データは消えないはずです。

ですが、どうやら ② の段階でボリュームマウントにした後 ③ のように `docker-compose.yml` を書き換えても、バインドマウントには戻らず、ボリュームマウントのままになってしまうようでした。このことは、`docker inspect` で確認できます。

```bash
$ docker inspect my_app | grep Mounts -A 11
        "Mounts": [
            {
                "Type": "volume",
                "Name": "0123abcd0123abcd0123abcd0123abcd0123abcd0123abcd0123abcd0123abcd",
                "Source": "/var/lib/docker/volumes/0123abcd0123abcd0123abcd0123abcd0123abcd0123abcd0123abcd0123abcd/_data",
                "Destination": "/home/yuinore/data",
                "Driver": "local",
                "Mode": "rw",
                "RW": true,
                "Propagation": "",
            }
        ],
```

上記ようにボリュームマウントになった状態で数日間作業を続けていたということになります。その状態で、何らかの拍子でボリューム名（ハッシュ値）が変わり、データが消えてしまったように見えたのです。

# 対処法

これは、short syntax がバインドマウントとボリュームマウントを区別できないことによるものだと予想されます。そこで、これを long syntax に書き換えます。

```yml:docker-compose.yml
volumes:
  - type: bind
    source: ./data
    target: /home/yuinore/data
```

こうするとバインドマウントになり、マウント元が `./data` ディレクトリに戻ります。ホストマシンからも同期されたファイルが見えるようになります。

```bash
$ docker inspect my_app | grep Mounts -A 5
            "Mounts": [
                {
                    "Type": "bind",
                    "Source": "/home/yuinore/sagyo/docker-test/data",
                    "Target": "/home/yuinore/data",
                }
--
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/home/yuinore/sagyo/docker-test/data",
                "Destination": "/home/yuinore/data",
                "Mode": "",
```

# おまけ：消失したデータの復元

必要なデータがあれば、復元しましょう。ボリュームマウントしたデータは `/var/lib/docker/volumes` のどこかに残っているはずですので、その中から必要なものを探しましょう。（こういった場合に名前付きボリュームにしておくと恐らく大変便利です。）

https://zenn.dev/ciffelia/articles/docker-volume-backup-restore

```bash
$ for d in $(sudo ls /var/lib/docker/volumes) ; do \
>     echo -e "\n---- ${d} ----\n" ; \
>     sudo ls "/var/lib/docker/volumes/${d}/_data" ; \
> done

---- 0123abcd0123abcd0123abcd0123abcd0123abcd0123abcd0123abcd0123abcd ----

not_important_file.pdf

---- f0f0f0f0e1e1e1e1f0f0f0f0e1e1e1e1f0f0f0f0e1e1e1e1f0f0f0f0e1e1e1e1 ----

very_very_important_file.pdf
login_password.txt
my_credit_card_number.txt
R-18.zip
```

ファイルが見つかったら復旧しましょう。

```bash
$ sudo mv data data_old
$ sudo cp -R \
    /var/lib/docker/volumes/f0f0f0f0e1e1e1e1f0f0f0f0e1e1e1e1f0f0f0f0e1e1e1e1f0f0f0f0e1e1e1e1/_data \
    data
```

# 追伸

それにしても、ボリュームのハッシュ値 (`0123abcd...`) が変わってしまったのはなぜだったのでしょうか。再起動が原因かなと思いましたが、特に再現性はありませんでした。