コンテナのレイヤ構造を調べた時の備忘録

https://gihyo.jp/book/2020/978-4-297-11837-2

を読んで、レイヤ構造の実現方法について興味がわいたので少し詳しく調べてみた。
こちらの本は、Dockerの内部で使われている技術について簡潔に説明されていておすすめ。

- コンテナのイメージはコンテナ実行時のrootfsとその設定ファイル。
- イメージのrootfs部はレイヤ構造で管理されている
- イメージにはポータビリティがあり、イメージのフォーマットはOCI Image Specificationで定義されている
    - フォーマットは特定のファイルシステム(overlayやbtrfsなど）には依存しない形で定義されている
    - レイヤごとにそのディレクトリ構造がtarで固められているような感じ
    - docker saveで出力されるフォーマットと似ているけれどちょっと違う
    - イメージを配布するときの仕様としてOCI Image Distribution Specificationがある。Docker HubなどのコンテナイメージレジストリのAPIを定義したもの。
- コンテナを起動するときには、イメージがファイルシステムに展開されてコンテナが使うrootfsになる。どうやって展開するかはコンテナランタイムがイメージ展開に使うファイルシステム（ストレージドライバ）次第。
    - イメージはレイヤごとにデータが別れている(tarになっている）ので、それを重ね合わせて一つのrootfsにする必要がある。そのため、レイヤ構造に対応したoverlay2やbtrfsなどのファイルシステムの仕組みが使われる。
    - overlay2がデフォルト。overlay2は複数のディレクトリを重ね合わせて一つのディレクトリに見せることができる。重ね合わせるディレクトリがイメージのレイヤに１対１で対応する形となり、直感的でわかりやすい。最上位のReadWrite可能なCoWな層が来る。
    - btrfsがつかわれることも増えている？ overlayは最上位にCoWの層を被せる形だが、btrfsはファイルシステム全体でCoWな動作をする。サブボリュームとそのスナップショットという概念があり、スナップショットがイメージのレイヤに対応する。
- ファイルシステムに展開したrootfsを使って、コンテナ（＝プロセスが実行できる閉鎖環境）が作成されて、その中でプロセスが実行される
- コンテナの実行環境、すなわちコンテナランタイムは高レベルと低レベルの２層に分かれている。
    - イメージの作成や展開、配布は高レベルランタイムの役割
    - 高レベルランタイムから渡される展開済みのrootfsや実行時設定を用いて、プロセスの実行環境を作って実行開始するのが低レベルランタイムの役割
    - 高レベルランタイムで有名なのはdocker、containerd。
    - 低レベルランタイムではrunc。
    - 高レベルランタイムから低レベルランタイムへrootfsを渡すときの仕様は、OCI Runtime Filesystem bundleに定義されている

以下では、ストレージドライバとしてoverlayとbtrfsを使ったときの、イメージの作成と展開の内部動作（レイヤデータがどのように管理されるか）を比較してみてみる。

# 環境

- [M1 iMac](https://qiita.com/kizitorashiro/items/9edceea492a7ab0d3155#multipass)
- [multipass](https://multipass.run)でubuntu20.04をインストール

# overlay2の動作

overlay2単体の動作を確認しておく。

- マウントする際にlowerdirとuppderdirを指定する。
    - lowerdirはreadonlyな層。複数のディレクトリを設定することができる。
    - upperdirはCoWな層。
- マウント先のディレクトリは、lowerdirとuppderdirが重ね合わされた状態にみえるようになる
    - マウント先のディレクトリに書き込みを行うとupperdirに実体が書き込まれる
    - lowerdirにあるファイルを変更すると、uppderdirにコピーされて変更した実体が書き込まれる（CoW = Copy on Write)
    - このときlowerdirのファイルは変更されずそのままの状態が維持される

レイヤとして重ね合わさせるディレクトリを二つ用意する。

```bash
$ mkdir 1st_layer
$ mkdir 2nd_layer
$ echo hello > ./1st_layer/hello.txt
$ echo bye > ./2nd_layer/bye.txt
```

CoW層にするディレクトリ(upper)と、作業ディレクトリ(work)、重ね合わせ先のディレクトリ(merged)を用意する。

```bash
$ mkdir upper work merged
```

overlay fsでマウントする

```bash
$ mount -t overlay overlay -olowerdir=1st_layer:2nd_layer,upperdir=upper,workdir=work merged
```

マウントしたディレクトリをみると1stと2ndのディレクトリが重ね合わせられていることがわかる。

```bash
$ ls merged/
bye.txt  hello.txt
```

マウントしたディレクトリに新しいファイルを書き込む。

```bash
$ echo goodmorning > ./merge/goodmorning.txt
$ ls ./merged/
bye.txt  goodmorning.txt  hello.txt
```

実体がupperディレクトリに保存されたことがわかる。

```bash
$ ls ./upper/
goodmorning.txt
```

マウントしたディレクトリに1st_layerにあるファイルの内容を変更して保存する。

```bash
$ echo 'hello,world!' > ./merge/hello.txt
```

upperディレクトリには新しいhello.txtが書き込まれ、1st_layerには古いhello.extが維持されることがわかる。

```bash
$ cat ./upper/hello.txt
hello,world!
$ cat ./1st_layer/hello.txt
hello
```

アンマウントするとmergedディレクトリの内容は見えなくなるが、upperディレクトリの内容を維持され、再度マウントすればmergedディレクトリの内容は復元される。

```bash
$ sudo umount merged
$ ls merged/
$ ls upper/
goodmorning.txt  hello.txt

$ sudo mount -t overlay overlay -olowerdir=1st_layer:2nd_layer,upperdir=upper,workdir=work merged
$ ls ./merged/
bye.txt  goodmorning.txt  hello.txt
```

# btrfsの動作

btrfsの単体動作を確認しておく。

マウントするためのループバックデバイスファイルを作る。

```bash
$ dd if=/dev/zero of=btrfs.img count=2097152
```

btrfsでフォーマットしてマウントする。

```bash
$ mkfs.btrfs btrfs.img
$ mkdir btrfs
$ mount -t btrfs -o loop btrfs.img ./btrfs
```

サブボリュームを作成する。

```bash
$ cd ./btrfs
$ btrfs subvolume create vol
```

サブボリュームにファイルを書き込んでスナップショットを撮る。

```bash
$ echo hello > ./vol/hello.txt
$ btrfs subvolume snapshot vol 1st_layer
Create a snapshot of 'vol' in './1st_layer'
$ ls ./1st_layer
hello.txt
```

さらにファイルを書き込んでスナップショットを撮る。

```bash
$ echo bye > ./vol/bye.txt
$ btrfs subvolume snapshot vol 2nd_layer
Create a snapshot of 'vol' in './2nd_layer'
```

さらにファイルを書き込む。

```bash
$ echo goodmorning > ./vol/goodmorning.txt
$ ls ./vol
bye.txt  goodmorning.txt  hello.txt
```

1st_layerとして撮ったスナップショットに含まれるファイルを変更して上書き保存する。

```bash
$ echo 'hello,world!' > ./vol/hello.txt
$ cat ./vol/hello.txt
hello,world!
```

1st_layerと2nd_layerはスナップショットの時のまま。

```bash
$ ls ./1st_layer/
hello.txt
$ cat ./1st_layer/hello.txt 
hello
$ ls ./2nd_layer/
bye.txt  hello.txt
$ cat ./2nd_layer/hello.txt 
hello
```

# Dockerのストレージドライバーにoverlay2を利用した時の動作

alphineをベースに、ファイルを２つ足したイメージを作成する。

```Dockerfile
FROM alpine:latest
ADD hello.sh /hello.sh
ADD bye.sh /bye.sh
ENTRYPOINT [ "/hello.sh" ]
```

/var/libldocker/overlay2の初期状態

```bash
$ sudo tree /var/lib/docker/overlay2
/var/lib/docker/overlay2
└── l

1 directory, 0 files
```

イメージをビルドした後の/var/lib/docker/overlay2。

- alpineのレイヤと、ADD hello.txtしたレイヤと、ADD bye.txtをしたレイヤの３つのレイヤが存在する。

```bash
$ sudo tree -L 3 /var/lib/docker/overlay2
/var/lib/docker/overlay2
├── 0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02
│   ├── committed
│   ├── diff
│   │   └── hello.sh
│   ├── link
│   ├── lower
│   └── work
├── 0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb
│   ├── committed
│   ├── diff
│   │   └── bye.sh
│   ├── link
│   ├── lower
│   └── work
├── a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63
│   ├── committed
│   ├── diff
│   │   ├── bin
│   │   ├── dev
│   │   ├── etc
│   │   ├── home
│   │   ├── lib
│   │   ├── media
│   │   ├── mnt
│   │   ├── opt
│   │   ├── proc
│   │   ├── root
│   │   ├── run
│   │   ├── sbin
│   │   ├── srv
│   │   ├── sys
│   │   ├── tmp
│   │   ├── usr
│   │   └── var
│   └── link
└── l
    ├── AG7NYAURZHT73HZEZSGJ3XGQ5B -> ../0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb/diff
    ├── PDTAA47PQXCBIZBATEN6E4PKP3 -> ../0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02/diff
    └── WGD67YG7S35SU3FMVJCQUNUZIO -> ../a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63/diff

```

イメージをdocker saveで書き出してみる。

- alpineとADD hello.shとADD bye.shの3つのレイヤに対応するディレクトリが存在する。

```bash
$ docker save myimage:v1 | tar -xC ./myimage_v1.save/
$ tree ./myimage_v1.save/
./myimage_v1.save/
├── 27fd369832323dc013779a181a41732ad0b7d66c97c04af0c32025607263dec9.json
├── 387e5af651d6cd8c93ab10a7ad0a8fcc3e8d35b667a435e04e9a4606e209e571
│   ├── VERSION
│   ├── json
│   └── layer.tar
├── 4f6401c78ad9c5468f2a129c43be7f15d2293b3c11a9aec190120cf7411e14a1
│   ├── VERSION
│   ├── json
│   └── layer.tar
├── 60a4e2ecf7404dad03beac1f1e2110e045541774463678407f3e62c7664a5653
│   ├── VERSION
│   ├── json
│   └── layer.tar
├── manifest.json
└── repositories

3 directories, 12 files

```

- tarファイルの中を見ると、各レイヤのファイルが入っていることが確認できる

```bash
$ tar -tf ./myimage_v1.save/387e5af651d6cd8c93ab10a7ad0a8fcc3e8d35b667a435e04e9a4606e209e571/layer.tar 
bye.sh
$ tar -tf ./myimage_v1.save/4f6401c78ad9c5468f2a129c43be7f15d2293b3c11a9aec190120cf7411e14a1/layer.tar
hello.sh
```

docker runしてみる。

/var/lib/docker/overlay2は下記のようになる。

- b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3にmergedディレクトリがあり、これがコンテナ実行時に参照するrootfsディレクトリと思われる。

```
$ sudo tree -L 3 /var/lib/docker/overlay2
/var/lib/docker/overlay2
├── 0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02
│   ├── committed
│   ├── diff
│   │   └── hello.sh
│   ├── link
│   ├── lower
│   └── work
├── 0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb
│   ├── committed
│   ├── diff
│   │   └── bye.sh
│   ├── link
│   ├── lower
│   └── work
├── a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63
│   ├── committed
│   ├── diff
│   │   ├── bin
│   │   ├── dev
│   │   ├── etc
│   │   ├── home
│   │   ├── lib
│   │   ├── media
│   │   ├── mnt
│   │   ├── opt
│   │   ├── proc
│   │   ├── root
│   │   ├── run
│   │   ├── sbin
│   │   ├── srv
│   │   ├── sys
│   │   ├── tmp
│   │   ├── usr
│   │   └── var
│   └── link
├── b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3
│   ├── diff
│   ├── link
│   ├── lower
│   ├── merged
│   │   ├── bin
│   │   ├── bye.sh
│   │   ├── dev
│   │   ├── etc
│   │   ├── hello.sh
│   │   ├── home
│   │   ├── lib
│   │   ├── media
│   │   ├── mnt
│   │   ├── opt
│   │   ├── proc
│   │   ├── root
│   │   ├── run
│   │   ├── sbin
│   │   ├── srv
│   │   ├── sys
│   │   ├── tmp
│   │   ├── usr
│   │   └── var
│   └── work
│       └── work
├── b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3-init
│   ├── committed
│   ├── diff
│   │   ├── dev
│   │   └── etc
│   ├── link
│   ├── lower
│   └── work
│       └── work
└── l
    ├── 5BPXIBWUPBY4RMSRNRB6CUNVOU -> ../b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3/diff
    ├── AG7NYAURZHT73HZEZSGJ3XGQ5B -> ../0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb/diff
    ├── PDTAA47PQXCBIZBATEN6E4PKP3 -> ../0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02/diff
    ├── WGD67YG7S35SU3FMVJCQUNUZIO -> ../a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63/diff
    └── ZEXDBFNALOHVMEXO27IWF2RQNA -> ../b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3-init/diff

59 directories, 17 files
```

mount状態を確認してみる。

```bash
mount | grep overlay
overlay on /var/lib/docker/overlay2/b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/ZEXDBFNALOHVMEXO27IWF2RQNA:/var/lib/docker/overlay2/l/AG7NYAURZHT73HZEZSGJ3XGQ5B:/var/lib/docker/overlay2/l/PDTAA47PQXCBIZBATEN6E4PKP3:/var/lib/docker/overlay2/l/WGD67YG7S35SU3FMVJCQUNUZIO,upperdir=/var/lib/docker/overlay2/b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3/diff,workdir=/var/lib/docker/overlay2/b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3/work,xino=off)
```

下記のようになっている。b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3にコンテナ実行時にupperdir, workdir, マウントポイント(merged)が含まれている。

- マウントポイント：b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3/merged
- lowerdir: 
    - ZEXDBFNALOHVMEXO27IWF2RQNA (/devと/etc)
    - AG7NYAURZHT73HZEZSGJ3XGQ5B（bye.sh)
    - PDTAA47PQXCBIZBATEN6E4PKP3 (hello.sh)
    - WGD67YG7S35SU3FMVJCQUNUZIO (alpine)
- upperdir:
    - b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3/diff
- workdir: 
    - b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3/work

コンテナを停止してみる。

- overlayでのマウントは解除されている。
- /var/lib/docker/overlay2のディレクトリはそのまま残っている（umountされたのでmergedディレクトリは空）


```bash
$ mount | grep overlay
$ sudo tree -L 3 /var/lib/docker/overlay2
/var/lib/docker/overlay2
├── 0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02
...
├── 0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb
...
├── a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63
...
├── b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3
│   ├── diff
│   ├── link
│   ├── lower
│   └── work
│       └── work
├── b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3-init
│   ├── committed
│   ├── diff
│   │   ├── dev
│   │   └── etc
│   ├── link
│   ├── lower
│   └── work
│       └── work
└── l
    ├── 5BPXIBWUPBY4RMSRNRB6CUNVOU -> ../b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3/diff
    ├── AG7NYAURZHT73HZEZSGJ3XGQ5B -> ../0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb/diff
    ├── PDTAA47PQXCBIZBATEN6E4PKP3 -> ../0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02/diff
    ├── WGD67YG7S35SU3FMVJCQUNUZIO -> ../a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63/diff
    └── ZEXDBFNALOHVMEXO27IWF2RQNA -> ../b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3-init/diff

41 directories, 15 files
```

コンテナを削除してみる。

- コンテナ起動時に作成されたディレクトリ(b7009e00bf2ce43b3d377a368c0a096939d05861c9e0db9b91959520a3327bc3)が削除された。

```bash
$ sudo tree -L 3 /var/lib/docker/overlay2
/var/lib/docker/overlay2
├── 0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02
...
├── 0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb
...
├── a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63
...
└── l
    ├── AG7NYAURZHT73HZEZSGJ3XGQ5B -> ../0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb/diff
    ├── PDTAA47PQXCBIZBATEN6E4PKP3 -> ../0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02/diff
    └── WGD67YG7S35SU3FMVJCQUNUZIO -> ../a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63/diff

29 directories, 10 files
```

もう一度コンテナを起動し、ファイル(goodmorning.txt)を追加してみる。

- goodmorning.txtがmerged(mountポイント)とdiff(upperdir)のディレクトリに追加されている

```
/var/lib/docker/overlay2
...
├── c4b29c99de3813c94ab2054dfa840e2cbe116f9f90dd2ea530f322e5bd4ec326
│   ├── diff
│   │   ├── goodmorning.txt
│   │   └── root
│   ├── link
│   ├── lower
│   ├── merged
│   │   ├── bin
│   │   ├── bye.sh
│   │   ├── dev
│   │   ├── etc
│   │   ├── goodmorning.txt
│   │   ├── hello.sh
│   │   ├── home
│   │   ├── lib
│   │   ├── media
│   │   ├── mnt
│   │   ├── opt
│   │   ├── proc
│   │   ├── root
│   │   ├── run
│   │   ├── sbin
│   │   ├── srv
│   │   ├── sys
│   │   ├── tmp
│   │   ├── usr
│   │   └── var
│   └── work
│       └── work
...
```


コンテナを止めて

```bash
├── c4b29c99de3813c94ab2054dfa840e2cbe116f9f90dd2ea530f322e5bd4ec326
│   ├── diff
│   │   ├── goodmorning.txt
│   │   └── root
│   ├── link
│   ├── lower
│   └── work
│       └── work
```


commitしてみる。

- fde1c170aea126638e876aa80ffd33d746d7a12f70c82ee1042c5fea7111ed96が増えている。イメージとcommit時のコンテナ状態の差分ファイル(goodmorning.txt)が入っている

```
$ sudo tree -L 3 /var/lib/docker/overlay2
/var/lib/docker/overlay2
├── 0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02
...
├── 0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb
...
├── a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63
...
├── c4b29c99de3813c94ab2054dfa840e2cbe116f9f90dd2ea530f322e5bd4ec326
│   ├── diff
│   │   ├── goodmorning.txt
│   │   └── root
│   ├── link
│   ├── lower
│   └── work
│       └── work
├── c4b29c99de3813c94ab2054dfa840e2cbe116f9f90dd2ea530f322e5bd4ec326-init
│   ├── committed
│   ├── diff
│   │   ├── dev
│   │   └── etc
│   ├── link
│   ├── lower
│   └── work
│       └── work
├── fde1c170aea126638e876aa80ffd33d746d7a12f70c82ee1042c5fea7111ed96
│   ├── diff
│   │   ├── goodmorning.txt
│   │   └── root
│   ├── link
│   ├── lower
│   └── work
└── l
    ├── 7FDDE7YVUJGP4B2HAKI4GOD6E3 -> ../fde1c170aea126638e876aa80ffd33d746d7a12f70c82ee1042c5fea7111ed96/diff
    ├── AG7NYAURZHT73HZEZSGJ3XGQ5B -> ../0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb/diff
    ├── EHOQLPR52IOX6LHJ22RY3AK2U4 -> ../c4b29c99de3813c94ab2054dfa840e2cbe116f9f90dd2ea530f322e5bd4ec326/diff
    ├── PDTAA47PQXCBIZBATEN6E4PKP3 -> ../0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02/diff
    ├── WGD67YG7S35SU3FMVJCQUNUZIO -> ../a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63/diff
    └── WIAJFBDXFEMTIV7GACBMAJ5AOK -> ../c4b29c99de3813c94ab2054dfa840e2cbe116f9f90dd2ea530f322e5bd4ec326-init/diff
```

コンテナを削除する。

- コンテナ実行時に作成されたc4b29c99de3813c94ab2054dfa840e2cbe116f9f90dd2ea530f322e5bd4ec326は削除されている
- commit時に新たに作成されたfde1c170aea126638e876aa80ffd33d746d7a12f70c82ee1042c5fea7111ed96は残っている

```
$ sudo tree -L 3 /var/lib/docker/overlay2
/var/lib/docker/overlay2
├── 0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02
...
├── 0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb
...
├── a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63
...
├── fde1c170aea126638e876aa80ffd33d746d7a12f70c82ee1042c5fea7111ed96
│   ├── diff
│   │   ├── goodmorning.txt
│   │   └── root
│   ├── link
│   ├── lower
│   └── work
└── l
    ├── 7FDDE7YVUJGP4B2HAKI4GOD6E3 -> ../fde1c170aea126638e876aa80ffd33d746d7a12f70c82ee1042c5fea7111ed96/diff
    ├── AG7NYAURZHT73HZEZSGJ3XGQ5B -> ../0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb/diff
    ├── PDTAA47PQXCBIZBATEN6E4PKP3 -> ../0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02/diff
    └── WGD67YG7S35SU3FMVJCQUNUZIO -> ../a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63/diff

34 directories, 13 files
```

まとめると、

- /var/lib/docker/overlay2に各レイヤに対応するディレクトリが保存される
    - レイヤで管理しているファイルはdiffサブディレクトリに保存される
- イメージからコンテナを起動すると、/var/lib/docker/overlay2にコンテナ用のディレクトリが増える
    - overlayのupperdir, workdir, マウントポイント(merged)に対応するサブディレクトリがある
    - lowerdirには、イメージを構成するレイヤのディレクトリのリストが設定される
    - コンテナにはmergedサブディレクトリがrootfsとして渡される
    - コンテナ実行中に追加、変更されたファイルはupperdirに保存される
    - コンテナを削除するとこのディレクトリは削除される
- コンテナからイメージを作成すると、uppderdirの内容があらたなレイヤが作成されて、/var/lib/docker/overlay2に追加される
- イメージをdocker saveでエクスポートすると、各レイヤがtarファイルになって出力される（これはどんなストレージドライバでも同じ）

# Dockerのストレージドライバーにbtrfsを利用した時の動作

ストーレジドライバーをbtrfsに切り替える

https://docs.docker.com/storage/storagedriver/btrfs-driver/

```bash
$ sudo systemctl stop docker
$ sudo systemctl stop docker.socket

$ sudo cp -au /var/lib/docker /var/lib/docker.bk
$ sudo rm -rf /var/lib/docker/*

$ dd if=/dev/zero of=btrfs.img count=2097152
$ mkfs.btrfs btrfs.img 
$ sudo mount -t btrfs -o loop btrfs.img /var/lib/docker  

$ sudo cp -au /var/lib/docker.bk/* /var/lib/docker/
 
$ sudo systemctl start docker
$ sudo systemctl start docker.socket
```

/var/lib/docker/btrfsの初期状態 

- /var/lib/docker/btrfsフォルダが作成されている
- /var/lib/docker/btrtfフォルダの中身は空
- overlay2を検証した時のoverkay2フォルダは残っているが、おそらく参照されていない

```bash 
$ sudo tree -L 2 /var/lib/docker/
/var/lib/docker/
├── btrfs
├── buildkit
│   ├── cache.db
│   ├── containerdmeta.db
│   ├── content
│   ├── executor
│   ├── metadata_v2.db
│   └── snapshots.db
├── containers
├── image
│   ├── btrfs
│   └── overlay2
├── network
│   └── files
├── overlay2
│   ├── 0e6729eab769f9cc6d5c2a551a7f8fe0b46776cf156e1f9340c28a71b8a83b02
│   ├── 0fa76ee07bcf4f15be0480d4d656c7e1a859b2867f9878ab3c2d884ecfe7f7fb
│   ├── a52ae8547e061a5ba99f94c7be162210fd37e3f7384e1aa6052cb67a020cba63
│   ├── fde1c170aea126638e876aa80ffd33d746d7a12f70c82ee1042c5fea7111ed96
│   └── l
├── plugins
│   ├── storage
│   └── tmp
├── runtimes
├── swarm
├── tmp
├── trust
└── volumes
    ├── backingFsBlockDev
    └── metadata.db

24 directories, 6 files
```

念の為、dockerdに認識されているイメージはないことを確認する。

```bash
$ docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE
```

イメージをビルドする。

- btrfsにsubvolumesフォルダが作成されている
- alpine, ADD hello.sh, ADD bye.shの３つのレイヤに対応するサブボリュームがある

```bash
$ sudo tree -L 3 /var/lib/docker/btrfs
/var/lib/docker/btrfs
└── subvolumes
    ├── 1e2fe26e24b54bddd9de09e8794d0c5db5dfdd2dee6c54ba13c39984822ca54e
    │   ├── bin
    │   ├── bye.sh
    │   ├── dev
    │   ├── etc
    │   ├── hello.sh
    │   ├── home
    │   ├── lib
    │   ├── media
    │   ├── mnt
    │   ├── opt
    │   ├── proc
    │   ├── root
    │   ├── run
    │   ├── sbin
    │   ├── srv
    │   ├── sys
    │   ├── tmp
    │   ├── usr
    │   └── var
    ├── 39a89c087e417a1aba16c3c400b36fa4247b351226417f7c193bcdaa37287319
    │   ├── bin
    │   ├── dev
    │   ├── etc
    │   ├── hello.sh
    │   ├── home
    │   ├── lib
    │   ├── media
    │   ├── mnt
    │   ├── opt
    │   ├── proc
    │   ├── root
    │   ├── run
    │   ├── sbin
    │   ├── srv
    │   ├── sys
    │   ├── tmp
    │   ├── usr
    │   └── var
    └── 74cbac2e8c61e866ac8c07d849ea81383099c1dcf2ab092de7265569bb41375e
        ├── bin
        ├── dev
        ├── etc
        ├── home
        ├── lib
        ├── media
        ├── mnt
        ├── opt
        ├── proc
        ├── root
        ├── run
        ├── sbin
        ├── srv
        ├── sys
        ├── tmp
        ├── usr
        └── var
        
55 directories, 3 files
```

docker saveでイメージをエクスポートしてみる。

- overlay2の時と同じように、３つのレイヤがそれぞれtarファイルになっている
- エクスポートするとストレージドライバーに依存した情報はなくなる

```bash
$ tree myimage_v1.btrfs.save/
myimage_v1.btrfs.save/
├── 0c371df2ed57d0f8d21cd25798912caada2b111800f4fca2c82d1a02942d393c
│   ├── VERSION
│   ├── json
│   └── layer.tar
├── 4f6401c78ad9c5468f2a129c43be7f15d2293b3c11a9aec190120cf7411e14a1
│   ├── VERSION
│   ├── json
│   └── layer.tar
├── 60a4e2ecf7404dad03beac1f1e2110e045541774463678407f3e62c7664a5653
│   ├── VERSION
│   ├── json
│   └── layer.tar
├── 89beb19f75feed20696df179293902464a54f736fa55d4728ea953bc6596dc90.json
├── manifest.json
└── repositories

3 directories, 12 files
```

```bash
$ tar -tf myimage_v1.btrfs.save/0c371df2ed57d0f8d21cd25798912caada2b111800f4fca2c82d1a02942d393c/layer.tar 
bye.sh
ubuntu@ubuntu:~/getstarted/docker$ tar -tf myimage_v1.btrfs.save/4f6401c78ad9c5468f2a129c43be7f15d2293b3c11a9aec190120cf7411e14a1/layer.tar 
hello.sh
```

btrfsのサブボリュームのリストを確認する

- btrfs上でたしかに3つのサブボリュームが認識されていることがわかる

```bash
$ sudo btrfs subvolume list /var/lib/docker
ID 256 gen 35 top level 5 path btrfs/subvolumes/74cbac2e8c61e866ac8c07d849ea81383099c1dcf2ab092de7265569bb41375e
ID 259 gen 35 top level 5 path btrfs/subvolumes/39a89c087e417a1aba16c3c400b36fa4247b351226417f7c193bcdaa37287319
ID 261 gen 35 top level 5 path btrfs/subvolumes/1e2fe26e24b54bddd9de09e8794d0c5db5dfdd2dee6c54ba13c39984822ca54e
```

コンテナを起動する。

- サブボリュームが二つ増えている。末尾に-initがついているものと、ついていないもの
- initがついていない方がコンテナのrootfsとして使われるボリュームと思われる

```bash
$ sudo ls /var/lib/docker/btrfs/subvolumes
1e2fe26e24b54bddd9de09e8794d0c5db5dfdd2dee6c54ba13c39984822ca54e
39a89c087e417a1aba16c3c400b36fa4247b351226417f7c193bcdaa37287319
3f31c3567b855de1a60d23ea0967ca8f3d18ea402c1168d5463ab78f54da0e8c
3f31c3567b855de1a60d23ea0967ca8f3d18ea402c1168d5463ab78f54da0e8c-init
74cbac2e8c61e866ac8c07d849ea81383099c1dcf2ab092de7265569bb41375e
```

```bash
$ sudo tree -L 2 /var/lib/docker/btrfs/subvolumes
/var/lib/docker/btrfs/subvolumes
├── 1e2fe26e24b54bddd9de09e8794d0c5db5dfdd2dee6c54ba13c39984822ca54e
│   ├── bin
│   ├── bye.sh
│   ├── dev
│   ├── etc
│   ├── hello.sh
│   ├── home
│   ├── lib
│   ├── media
│   ├── mnt
│   ├── opt
│   ├── proc
│   ├── root
│   ├── run
│   ├── sbin
│   ├── srv
│   ├── sys
│   ├── tmp
│   ├── usr
│   └── var
├── 39a89c087e417a1aba16c3c400b36fa4247b351226417f7c193bcdaa37287319
│   ├── bin
│   ├── dev
│   ├── etc
│   ├── hello.sh
│   ├── home
│   ├── lib
│   ├── media
│   ├── mnt
│   ├── opt
│   ├── proc
│   ├── root
│   ├── run
│   ├── sbin
│   ├── srv
│   ├── sys
│   ├── tmp
│   ├── usr
│   └── var
├── 3f31c3567b855de1a60d23ea0967ca8f3d18ea402c1168d5463ab78f54da0e8c
│   ├── bin
│   ├── bye.sh
│   ├── dev
│   ├── etc
│   ├── hello.sh
│   ├── home
│   ├── lib
│   ├── media
│   ├── mnt
│   ├── opt
│   ├── proc
│   ├── root
│   ├── run
│   ├── sbin
│   ├── srv
│   ├── sys
│   ├── tmp
│   ├── usr
│   └── var
├── 3f31c3567b855de1a60d23ea0967ca8f3d18ea402c1168d5463ab78f54da0e8c-init
│   ├── bin
│   ├── bye.sh
│   ├── dev
│   ├── etc
│   ├── hello.sh
│   ├── home
│   ├── lib
│   ├── media
│   ├── mnt
│   ├── opt
│   ├── proc
│   ├── root
│   ├── run
│   ├── sbin
│   ├── srv
│   ├── sys
│   ├── tmp
│   ├── usr
│   └── var
└── 74cbac2e8c61e866ac8c07d849ea81383099c1dcf2ab092de7265569bb41375e
    ├── bin
    ├── dev
    ├── etc
    ├── home
    ├── lib
    ├── media
    ├── mnt
    ├── opt
    ├── proc
    ├── root
    ├── run
    ├── sbin
    ├── srv
    ├── sys
    ├── tmp
    ├── usr
    └── var

90 directories, 7 files
```

コンテナを停止する

- ボリュームはそのまま

```bash
$ sudo ls /var/lib/docker/btrfs/subvolumes
1e2fe26e24b54bddd9de09e8794d0c5db5dfdd2dee6c54ba13c39984822ca54e
39a89c087e417a1aba16c3c400b36fa4247b351226417f7c193bcdaa37287319
3f31c3567b855de1a60d23ea0967ca8f3d18ea402c1168d5463ab78f54da0e8c
3f31c3567b855de1a60d23ea0967ca8f3d18ea402c1168d5463ab78f54da0e8c-init
74cbac2e8c61e866ac8c07d849ea81383099c1dcf2ab092de7265569bb41375e
```

コンテナを削除

- コンテナを起動した時に作られた2つのボリュームが削除されている

```bash
$ sudo ls /var/lib/docker/btrfs/subvolumes
1e2fe26e24b54bddd9de09e8794d0c5db5dfdd2dee6c54ba13c39984822ca54e
39a89c087e417a1aba16c3c400b36fa4247b351226417f7c193bcdaa37287319
74cbac2e8c61e866ac8c07d849ea81383099c1dcf2ab092de7265569bb41375e
```

もう一度コンテナを起動して、今度はファイルを追加してみる。

- 7febがコンテナのrootfsと思われる

```bash
$ sudo ls /var/lib/docker/btrfs/subvolumes
1e2fe26e24b54bddd9de09e8794d0c5db5dfdd2dee6c54ba13c39984822ca54e
39a89c087e417a1aba16c3c400b36fa4247b351226417f7c193bcdaa37287319
74cbac2e8c61e866ac8c07d849ea81383099c1dcf2ab092de7265569bb41375e
7feb15948d4cec72eb67541739fc8e3281b041f2ad4de9737de781e88a413fa7
7feb15948d4cec72eb67541739fc8e3281b041f2ad4de9737de781e88a413fa7-init
```

goodmorning.txtを追加する

- たしかに7febにgoodmorning.txtに追加されている

```bash
$ sudo tree -L 1 /var/lib/docker/btrfs/subvolumes/7feb15948d4cec72eb67541739fc8e3281b041f2ad4de9737de781e88a413fa7
/var/lib/docker/btrfs/subvolumes/7feb15948d4cec72eb67541739fc8e3281b041f2ad4de9737de781e88a413fa7
├── bin
├── bye.sh
├── dev
├── etc
├── goodmorning.txt
├── hello.sh
├── home
├── lib
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin
├── srv
├── sys
├── tmp
├── usr
└── var

17 directories, 3 files
```

コンテナを停止するして、docker commitして新たなイメージを作る。

- 新しいサブモジュール(6f5a)が一つ増えている

```bash
$ sudo ls /var/lib/docker/btrfs/subvolumes
1e2fe26e24b54bddd9de09e8794d0c5db5dfdd2dee6c54ba13c39984822ca54e
39a89c087e417a1aba16c3c400b36fa4247b351226417f7c193bcdaa37287319
6f5a4d262345aa7825136834fc851ba001de00ebb5ba1bb0c40fc53ccb2cff9a
74cbac2e8c61e866ac8c07d849ea81383099c1dcf2ab092de7265569bb41375e
7feb15948d4cec72eb67541739fc8e3281b041f2ad4de9737de781e88a413fa7
7feb15948d4cec72eb67541739fc8e3281b041f2ad4de9737de781e88a413fa7-init
```

```bash
$ sudo tree -L 1 /var/lib/docker/btrfs/subvolumes/6f5a4d262345aa7825136834fc851ba001de00ebb5ba1bb0c40fc53ccb2cff9a
/var/lib/docker/btrfs/subvolumes/6f5a4d262345aa7825136834fc851ba001de00ebb5ba1bb0c40fc53ccb2cff9a
├── bin
├── bye.sh
├── dev
├── etc
├── goodmorning.txt
├── hello.sh
├── home
├── lib
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin
├── srv
├── sys
├── tmp
├── usr
└── var

17 directories, 3 files
```

コンテナを削除する。

- コンテナ用のrootfsと使われていたボリューム(7feb)は削除されている
- docker commitして追加されたボリューム(6f5a)は残っている

```bash
$ sudo ls /var/lib/docker/btrfs/subvolumes
1e2fe26e24b54bddd9de09e8794d0c5db5dfdd2dee6c54ba13c39984822ca54e
39a89c087e417a1aba16c3c400b36fa4247b351226417f7c193bcdaa37287319
6f5a4d262345aa7825136834fc851ba001de00ebb5ba1bb0c40fc53ccb2cff9a
74cbac2e8c61e866ac8c07d849ea81383099c1dcf2ab092de7265569bb41375e
```

コンテナ起動時にrootfsとして使われているのは、コンテナ起動時に作成されて、コンテナ削除時に削除されるボリュームだと思われるが、本当にそうか、コンテナ内のプロセスのmountinfoを見て確かめる。

コンテナを再度起動する。

- おそらくrootfsはcf4d

```bash
$ sudo ls /var/lib/docker/btrfs/subvolumes
1e2fe26e24b54bddd9de09e8794d0c5db5dfdd2dee6c54ba13c39984822ca54e
39a89c087e417a1aba16c3c400b36fa4247b351226417f7c193bcdaa37287319
6f5a4d262345aa7825136834fc851ba001de00ebb5ba1bb0c40fc53ccb2cff9a
74cbac2e8c61e866ac8c07d849ea81383099c1dcf2ab092de7265569bb41375e
cf4d6cec6764c68058b216099115f1485e146272a44ea69aa2d22c848e805307
cf4d6cec6764c68058b216099115f1485e146272a44ea69aa2d22c848e805307-init
```

lsnsでnamespaceの一覧を表示する。

```bash
$ sudo lsns
        NS TYPE   NPROCS   PID USER             COMMAND
4026531835 cgroup    136     1 root             /sbin/init splash
4026531836 pid       135     1 root             /sbin/init splash
4026531837 user      136     1 root             /sbin/init splash
4026531838 uts       132     1 root             /sbin/init splash
4026531839 ipc       135     1 root             /sbin/init splash
4026531840 mnt       128     1 root             /sbin/init splash
4026531860 mnt         1    21 root             kdevtmpfs
4026531888 net       135     1 root             /sbin/init splash
4026532140 mnt         1   410 root             /lib/systemd/systemd-udevd
4026532141 uts         1   410 root             /lib/systemd/systemd-udevd
4026532142 mnt         1   540 systemd-timesync /lib/systemd/systemd-timesyncd
4026532143 uts         1   540 systemd-timesync /lib/systemd/systemd-timesyncd
4026532144 mnt         1   589 systemd-network  /lib/systemd/systemd-networkd
4026532145 mnt         1   591 systemd-resolve  /lib/systemd/systemd-resolved
4026532148 mnt         1  7950 root             sleep infinity
4026532149 uts         1  7950 root             sleep infinity
4026532150 ipc         1  7950 root             sleep infinity
4026532151 pid         1  7950 root             sleep infinity
4026532153 net         1  7950 root             sleep infinity
4026532202 mnt         1   638 root             /usr/sbin/irqbalance --foreground
4026532203 mnt         1   646 root             /lib/systemd/systemd-logind
4026532204 uts         1   646 root             /lib/systemd/systemd-logind
```

sleep infinityなプロセスがコンテナ内プロセスなので、このプロセスのマウント情報を/procで確認する。

- たしかにマウントポイント "/"に、cf4dのボリュームがマウントされている

```bash
$ cat /proc/7950/mountinfo | grep btrfs
754 709 0:55 /btrfs/subvolumes/cf4d6cec6764c68058b216099115f1485e146272a44ea69aa2d22c848e805307 / rw,relatime master:245 - btrfs /dev/loop3 rw,space_cache,subvolid=271,subvol=/btrfs/subvolumes/cf4d6cec6764c68058b216099115f1485e146272a44ea69aa2d22c848e805307
```

まとめると、

- /var/lib/docker/btrfs/subvolumesに各レイヤに対応するサブボリュームが保存される
- イメージからコンテナを起動すると、/var/lib/docker/btrfs/subvolumesにコンテナ用のサブボリュームが増える
    - コンテナ実行中に追加、変更されたファイルはこのサブモジュールに保存される
    - このサブモジュールはコンテナを削除すると消える
    - コンテナからイメージを作成すると、このサブモジュールからスナップショットが撮られて、新たなサブモジュールが追加される
- イメージをdocker saveでエクスポートすると、各レイヤがtarファイルになって出力される（これはどんなストレージドライバでも同じ）