概要
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」はイメージなどコンテナ上で新たに作られたファイルより下層を保持します。1読み取り専用。2
「WorkDir」はUpperdir内のファイルが原子性(ファイルが不完全ではない)ことを保つためのキャッシュ的な領域です。3
「Upperdir」はコンテナ作成以後の変更点が記録され、コンテナ独自のデータが保管されます。1
「マウントポイントまたは"MergedDir"」は上記3つのフォルダを統合し、ファイル操作の拠点となります。ここではファイルシステムを意識せずに純粋に読み書きができます。1
マウントのコマンドは以下のようにすれば問題ありません。4
sudo mount -t overlay -o lowerdir=「"LowerDir"」,upperdir=「"UpperDir"」
,workdir=「"WorkDir"」 overlay 「マウントポイントまたは"MergedDir"」
Dockerのoverlayfsの上記4つのディレクトリの位置は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
いちいちディレクトリをコピペしたくないのでスクリプトを作りましょう。
あるいは、下記のシェルスクリプトのようにまとめればわかりやすくマウントできます。
# !/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の開発に戻ります。。。
メモ
- 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
面白そうな記事