Dockerのoverlayfsをコンテナと関係なくアクセスする

# 概要
Dockerコンテナー内のファイルを操作するには、docker execでbashを使用するのが一般的です。
ただし、コンテナが実行されていない限り、このメソッドは操作できません。
したがって、docker inspectの出力内容に基づいて、overlayfsディレクトリをコンテナとは別にマウントし、Dockerを使用せずにファイル操作を実行する方法を検討しました。

# 環境
- Ubuntu 18.04 on Hyper-V
- XFS + overlay2 (c.f. https://qiita.com/haniokasai/items/87bbc2a7f48d777f95c8#_reference-fb9595a078fe57047287)

```
・・・
 Server Version: 19.03.4
 Storage Driver: overlay2
  Backing Filesystem: xfs
  Supports d_type: true
  Native Overlay Diff: true
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
・・・
 Kernel Version: 4.15.0-70-generic
 Operating System: Ubuntu 18.04.3 LTS
 OSType: linux
 Architecture: x86_64
・・・
```

# マウントのしくみ
「LowerDir」はイメージなどコンテナ上で新たに作られたファイルより下層を保持します。[^3]読み取り専用。[^2]
「WorkDir」はUpperdir内のファイルが原子性（ファイルが不完全ではない）ことを保つためのキャッシュ的な領域です。[^1] 
「Upperdir」はコンテナ作成以後の変更点が記録され、コンテナ独自のデータが保管されます。[^3]
「マウントポイントまたは"MergedDir"」は上記３つのフォルダを統合し、ファイル操作の拠点となります。ここではファイルシステムを意識せずに純粋に読み書きができます。[^3]

マウントのコマンドは以下のようにすれば問題ありません。[^4]

```bash
sudo mount -t overlay -o lowerdir=「"LowerDir"」,upperdir=「"UpperDir"」
,workdir=「"WorkDir"」 overlay 「マウントポイントまたは"MergedDir"」
```

Dockerのoverlayfsの上記４つのディレクトリの位置はdocker inspectコマンドで判明します。

```
docker inspect コンテナ
・・・
            "CpusetCpus": "",
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/1109be508e00968c34c73a5cd2c787ad6d3c4951f7b387700ace2b12daae8ec8-init/diff:/var/lib/docker/overlay2/35d8c38f712194bd9d57b9a433955c1141c1974c2b07bfb86b44bee0feb1f0d4/diff:/var/lib/docker/overlay2/018ec8b5d317fddd6fdb9634bf01d869bfd03f9122477a853317efb8d8ff2d86/diff:/var/lib/docker/overlay2/f7671e8be670d1ab91d5a63166374862d2c972448d7b1c4037cee15e1a7628da/diff:/var/lib/docker/overlay2/4fea42daa23e6120c2956ca1d2397d3829fdb8e9f03336ed2b4bd36c895fa53e/diff:/var/lib/docker/overlay2/a15984dba40e4afa1df1c5088b6d3a366e91c74c04ef9a19f7a9e061e6e59bea/diff:/var/lib/docker/overlay2/6f61ebd10ebd7fd6e07b0226b0ac71602d3152a8f9a09744c774998e3c0fdc47/diff:/var/lib/docker/overlay2/ae7ad5c9ce4f3051ad28d3fdc8270c8f7b2d11f63a1446156485e5aa1e9adb64/diff:/var/lib/docker/overlay2/8242e55feaed0c250e9919273671ff051b136853ce2b0e7cf4a499f68388e588/diff:/var/lib/docker/overlay2/fcfb7edf288d40aa1673e09955b1affc748561f5c4d55f01ea9dc3d4fdd418ab/diff",
                "MergedDir": "/var/lib/docker/overlay2/1109be508e00968c34c73a5cd2c787ad6d3c4951f7b387700ace2b12daae8ec8/merged",
                "UpperDir": "/var/lib/docker/overlay2/1109be508e00968c34c73a5cd2c787ad6d3c4951f7b387700ace2b12daae8ec8/diff",
                "WorkDir": "/var/lib/docker/overlay2/1109be508e00968c34c73a5cd2c787ad6d3c4951f7b387700ace2b12daae8ec8/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/smx/docker_minecraft_res",
                "Destination": "/minecraft/resources",
                "Mode": "",
                "RW": false,
                "Propagation": "rprivate"
            }

・・・

```


#マウント方法
## マウント
「マウントのしくみ」の章のように、マウントポイントを私たちは利用したいのです。

下記がコマンド実行例です。
 /mnt/test　にマウントされます。

```
mount -t overlay overlay -o lowerdir="/var/lib/docker/overlay2/1109be508e00968c34c73a5cd2c787ad6d3c4951f7b387700ace2b12daae8ec8-init/diff:/var/lib/docker/overlay2/35d8c38f712194bd9d57b9a433955c1141c1974c2b07bfb86b44bee0feb1f0d4/diff:/var/lib/docker/overlay2/018ec8b5d317fddd6fdb9634bf01d869bfd03f9122477a853317efb8d8ff2d86/diff:/var/lib/docker/overlay2/f7671e8be670d1ab91d5a63166374862d2c972448d7b1c4037cee15e1a7628da/diff:/var/lib/docker/overlay2/4fea42daa23e6120c2956ca1d2397d3829fdb8e9f03336ed2b4bd36c895fa53e/diff:/var/lib/docker/overlay2/a15984dba40e4afa1df1c5088b6d3a366e91c74c04ef9a19f7a9e061e6e59bea/diff:/var/lib/docker/overlay2/6f61ebd10ebd7fd6e07b0226b0ac71602d3152a8f9a09744c774998e3c0fdc47/diff:/var/lib/docker/overlay2/ae7ad5c9ce4f3051ad28d3fdc8270c8f7b2d11f63a1446156485e5aa1e9adb64/diff:/var/lib/docker/overlay2/8242e55feaed0c250e9919273671ff051b136853ce2b0e7cf4a499f68388e588/diff:/var/lib/docker/overlay2/fcfb7edf288d40aa1673e09955b1affc748561f5c4d55f01ea9dc3d4fdd418ab/diff",upperdir="/var/lib/docker/overlay2/1109be508e00968c34c73a5cd2c787ad6d3c4951f7b387700ace2b12daae8ec8/diff",workdir= "/var/lib/docker/overlay2/1109be508e00968c34c73a5cd2c787ad6d3c4951f7b387700ace2b12daae8ec8/work" /mnt/test

```

いちいちディレクトリをコピペしたくないのでスクリプトを作りましょう。
あるいは、下記のシェルスクリプトのようにまとめればわかりやすくマウントできます。

```sh:test.sh
#!/bin/sh
CONTAINERNAME=コンテナ名
DOCKERHOST=unix:///var/run/docker.sock(各自で確認すること）
LOWERDIR=`docker -H ${DOCKERHOST} inspect --format='{{ .GraphDriver.Data.LowerDir }}' ${CONTAINERNAME}`
UPPERDIR=`docker -H ${DOCKERHOST} inspect --format='{{ .GraphDriver.Data.UpperDir}}' ${CONTAINERNAME}`
WORKDIR=`docker -H ${DOCKERHOST} inspect --format='{{ .GraphDriver.Data.WorkDir}}' ${CONTAINERNAME}`
MOUNTPOINT=/mnt/test


sudo mount -t overlay -o lowerdir=${LOWERDIR},upperdir=${UPPERDIR},workdir=${WORKDIR} overlay ${MOUNTPOINT}

```

実行結果

```
# umount /mnt/test
# ls /mnt/test
# ./test.sh
# ls /mnt/test
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  minecraft  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
```

## アンマウント
簡単です。マウントポイントを指定します。

```
umount /mnt/test
```

# まとめ
これまで、Dockerコンテナが起動に失敗すると、そのコンテナを回復することは非常に困難でした。ですが、この方法を使用すると、コンテナーの起動状態に関係なく操作を実行できます。
[MiRm](https://mirm.jp)の開発に戻ります。。。

# メモ
- Docker起動中にDocker側でのファイル編集はマウントポイントに反映されます
- storage-optのコンテナ毎の容量制限はマウントポイント内でも機能しています
- Dockerのオンオフは無視して問題なく、Docker内のmergedと同一の内容になります。

#Ref
https://github.com/thomasweaver/docker-scanner/blob/e822bd8873b7225ce5c46f38226d232ea94f8124/scanner/Scanner/Mount.py
https://github.com/b00kwrm/tddd/blob/29edb508cf47c19872aa43a6b1558ad609205c69/tools/get-docker-fs.py
https://github.com/att/docker-forensics/blob/944db5600bc9e7126f1d20e10499b4724bce4740/docker-mount.py
https://github.com/docker-forensics-toolkit/toolkit/blob/3fa4e46dbce7aef871ac2eec90caeed6cd921263/src/dof/model/container.py
https://qiita.com/awakia/items/af9b46c322905cdce1d7
https://qiita.com/ryuichi1208/items/0bd0284dcf8a09299504

#面白そうな記事
https://my1cdlinux.eksd.jp/createbootablelivecd_cdrommountandoverlayfs.html

#脚注
[^1]: https://unix.stackexchange.com/questions/324515/linux-filesystem-overlay-what-is-workdir-used-for-overlayfs
[^2]: https://www.clear-code.com/blog/2015/6/15.html
[^3]: http://docs.docker.jp/v1.9/engine/userguide/storagedriver/overlayfs-driver.html
[^4]: https://wiki.archlinux.jp/index.php/Overlayfs