Docker Desktop for Macのボリュームのデータはどこに保存されるのか

# ボリュームとは

- Dockerにおけるボリュームとは、コンテナ内で作成されたデータをホストマシンに保存して永続化するための仕組み(例外あり)
- コンテナが再作成された場合等でも同じボリュームをマウントすることでデータを再利用することが可能となる

# ボリュームの種類

- ボリュームには大きく3種類ある
  
### ボリューム

- **本記事で触れるものはこれ**
- データは `/var/lib/docker/volumes/` 以下に保存される
- Dockerの管理下でストレージ領域を確保する
- 名前付きボリューム(ユーザが名前を指定するもの)と匿名ボリューム(名前が自動生成されたハッシュ値のもの)の2種類がある
- 他のプロセスから参照することができないため安全である
- 基本的にはこの方式を使用すれば良い


### バインドマウント

- ホスト側のディレクトリをコンテナ内のディレクトリと共有する

### tmpfs

- メモリ上に一時的なストレージ領域を確保する
- 機密性の高い情報のマウントに使用されることがあるそう

# ボリュームの作成・確認

```bash
# ボリュームの作成
$ docker volume create example
example

# ボリューム一覧の確認
# ボリュームが作成されていることが分かる
$ docker volume ls
DRIVER    VOLUME NAME
local     example

# ボリュームの詳細情報の確認
# Mountpointを見るとボリュームのデータが /var/lib/docker/volumes/example/_data に保存されていることが分かる
$ docker volume inspect example
[
    {
        "CreatedAt": "2023-01-30T12:33:00Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/example/_data",
        "Name": "example",
        "Options": {},
        "Scope": "local"
    }
]

# しかし上記のMountpointで出力されたパスを確認しても存在しない
$ ls /var/lib/docker/volumes/example/
"/var/lib/docker/volumes/example/": No such file or directory (os error 2)
```

- ちなみに `docker run` する際に `--volume` / `-v` や `--mount` オプションを指定することで、 `docker volume create` をしなくても自動でボリュームが作成される

# なぜ存在しないのか

- Docker Desktop for Macでは、HyperKitを用いたVMの上でLinuxを起動し、その上でさらにDockerが動作している
    - GitHub: https://github.com/moby/hyperkit
- そのため、VMの中でボリュームが作成されており、ホストマシンから直接参照することができない
- こういった仕組みになっている理由は、DockerがLinux上で動作させることを前提としている(名前空間の機能等)ため(だと思われる)
- ちなみにHyperKitはHypervisor.frameworkに対応した仮想化ツールでbhyveやxhyveをベースにしている
    - GitHub: https://github.com/churchers/vm-bhyve
    - GitHub: https://github.com/machyve/xhyve
- ちなみにHyperKitの他にもVPNKitやDataKitといったDocker Desktop for Macを構成するために必要な機能があり、こちらもOSSとして公開されている
    - GitHub: https://github.com/moby/vpnkit
    - GitHub: https://github.com/moby/datakit
- ちなみにDocker Desktop for Apple siliconではAppleがVirtualization.frameworkに移行したことからHyperKitは使用されていないそう(Docker Desktop for Macもこちらに移行するかもしれそうだが詳細は不明)

# 今度こそボリュームを確認する

- VMに入るには `nsenter` を使用すると良い
- `nsenter` は `enter into namespaces` が由来
- 他の名前空間に入ってプログラムを実行するためのCLI
    - Manページ: https://man7.org/linux/man-pages/man1/nsenter.1.html
- `util-linux` パッケージに含まれている
- 内部的に使用しているシステムコールは `setns`
    - nsenterの実装のコメントにも記述がある: https://github.com/util-linux/util-linux/blob/master/sys-utils/nsenter.c#L2
- `nsenter` を簡単に利用するためのDockerイメージが用意されているため、今回はこちらを利用する
    - `PID=1` のプロセスの名前空間へ `nsenter` するための最小イメージを提供するもの
    - Docker Desktop for Macにおいては、結果的にVMに入ることができるということ
    - GitHub: https://github.com/justincormack/nsenter1

```bash
# --privileged は特権モードで、ホストへフルアクセスするための設定
# --pid=host はホストのプロセスIDをコンテナ内から認識できるようにする設定
$ docker run -it --rm --privileged --pid=host justincormack/nsenter1
Unable to find image 'justincormack/nsenter1:latest' locally
latest: Pulling from justincormack/nsenter1
5bc638ae6f98: Pull complete
Digest: sha256:e876f694a4cb6ff9e6861197ea3680fe2e3c5ab773a1e37ca1f13171f7f5798e
Status: Downloaded newer image for justincormack/nsenter1:latest

# ボリューム以外にも様々なデータが保存されている
/ # ls /var/lib/docker/
buildkit    image       overlay2    runtimes    tmp         volumes
containers  network     plugins     swarm       trust

# 先程作ったボリュームの実体はこれ
/ # ls /var/lib/docker/volumes/example/
_data
```

# ついでにVMの情報を確認する

- どうやらHyperKit上でLinuxKit(コンテナランタイム特化のLinux Subsystem)が動作している模様
    - GitHub: https://github.com/linuxkit/linuxkit

```bash
# ホスト名の確認
/ # hostname
docker-desktop

# カーネルのリリース番号の表示
/ # uname -r
5.10.104-linuxkit
```