Dockerコンテナのファイル実体と肥大化する/var/lib/docker/overlay2の正体 #docker
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
はじめに
先日にクリエーションラインが実施した Docker Fundamental Training にて、Dockerコンテナに含まれるファイルの実体についてご質問を頂きました。トレーニングの中で回答を差し上げたのですが、意外とまとまった情報がないなと思いまして、こちらで紹介します。
頂いた質問は下記のようなものです(一部抜粋/編集しています)。
前提として、Storage Driver には overlay2 を採用しています。
コンテナを起動し、その中に大きな容量のファイルを保存した場合、実際にはどこのディスク容量が消費されるのでしょうか?
複数のコンテナを起動した時に、イメージレイヤのファイルがそれぞれ /var/lib/docker/overlay2 に書き込まれますか?それともコンテナレイヤの中身に書き込まれたデータだけが /var/lib/docker/overlay2 に作られますか?
Dockerコンテナは多層のイメージレイヤの重ね合わせで構成されている...という説明に対して頂いた質問です。ファイルの実体とディスク容量について、実際のところどうなっているのか、ということですね。
Storage Driver と overlay2 について
まず、Docker コンテナは複数の Readonly なイメージレイヤと、Writable なコンテナレイヤで構成されています。
これによって、ベースとなる Docker Image に影響を与えることなく、安全にかつ効率的にコンテナ内部でファイルを読み書きすることが出来ます。
この仕組みを実現するのが Storage Driver です。overlay, aufs, device mapper, overlay2 などの選択肢があります。現在の Docker v19.03 では overlay2 がデフォルトかつ利用が推奨されています。overlay2 はカーネルの機能である overlayfs を利用しています。
Docker with overlay2 の挙動について
Storage Driver に overlay2 を利用した場合に、Dockerコンテナはイメージレイヤを LowerDir, コンテナレイヤをUpperDirとした overlayfs で構成されます。
以下では、実際にDockerイメージをビルドし、コンテナを実行した際のファイルの挙動を見てみます。
まず、ここでは下記の Dockerfile から Docker イメージを作成します。
FROM alpine RUN touch aaa.txt RUN mkdir bbb WORKDIR bbb RUN touch ccc.txt ENTRYPOINT ["sleep", "10000"]
これをビルドして docker image inspect すると、利用されているイメージレイヤのパスが表示されます。Docker イメージは、イメージレイヤ単位で /var/lib/docker/overlay2/ 以下に保存されます。
vagrant@workstation:~/overlay2test$ docker image inspect overlay2test -f "{{json .GraphDriver.Data}}" | jq . { "LowerDir": "/var/lib/docker/overlay2/f952dda500cd5fe53365d14c27498f39bcd4ba19b65f75e12a7d1acbc33d3f60/diff:/var/lib/docker/overlay2/58511fe87bd9872209b26843d021449bc89c512bcf45d5ebad0c45a7fe27ec4c/diff:/var/lib/docker/overlay2/2f3fa524adcbbc7c5fbb8450f4ba277bbae76f6cd8d4c09d3740633ef2f0eeed/diff", "MergedDir": "/var/lib/docker/overlay2/5da5f5376d357ba13dc6a5a60562fb2ebce3077758262ad5f0cd273b62e4cacc/merged", "UpperDir": "/var/lib/docker/overlay2/5da5f5376d357ba13dc6a5a60562fb2ebce3077758262ad5f0cd273b62e4cacc/diff", "WorkDir": "/var/lib/docker/overlay2/5da5f5376d357ba13dc6a5a60562fb2ebce3077758262ad5f0cd273b62e4cacc/work" }
/var/lib/docker/overlay2/{hash値} のフォルダが作られています。各hash値について整理すると、下記となります。
- 5da5f5376d357ba13dc6a5a60562fb2ebce3077758262ad5f0cd273b62e4cacc が overlay2test イメージの最上位レイヤであり、 MergedDir, UpperDir, WorkDir に設定されている
- f952dda500cd5fe53365d14c27498f39bcd4ba19b65f75e12a7d1acbc33d3f60, 58511fe87bd9872209b26843d021449bc89c512bcf45d5ebad0c45a7fe27ec4c, 2f3fa524adcbbc7c5fbb8450f4ba277bbae76f6cd8d4c09d3740633ef2f0eeed の3レイヤが LowerDir となっている
それぞれ中を見てみます。
まずは最上位の 5da5f5376d357ba13dc6a5a60562fb2ebce3077758262ad5f0cd273b62e4cacc から。
root@workstation:~# ls -liR /var/lib/docker/overlay2/5da5f5376d357ba13dc6a5a60562fb2ebce3077758262ad5f0cd273b62e4cacc/ /var/lib/docker/overlay2/5da5f5376d357ba13dc6a5a60562fb2ebce3077758262ad5f0cd273b62e4cacc/: total 16 1080163 -rw------- 1 root root 0 Jun 26 07:42 committed 1338827 drwxr-xr-x 3 root root 4096 Jun 26 07:42 diff 1080198 -rw-r--r-- 1 root root 26 Jun 26 07:42 link 1080199 -rw-r--r-- 1 root root 86 Jun 26 07:42 lower 1338828 drwx------ 2 root root 4096 Jun 26 07:42 work /var/lib/docker/overlay2/5da5f5376d357ba13dc6a5a60562fb2ebce3077758262ad5f0cd273b62e4cacc/diff: total 4 1338831 drwxr-xr-x 2 root root 4096 Jun 26 07:42 bbb /var/lib/docker/overlay2/5da5f5376d357ba13dc6a5a60562fb2ebce3077758262ad5f0cd273b62e4cacc/diff/bbb: total 0 1338832 -rw-r--r-- 1 root root 0 Jun 26 07:42 ccc.txt /var/lib/docker/overlay2/5da5f5376d357ba13dc6a5a60562fb2ebce3077758262ad5f0cd273b62e4cacc/work: total 0
diff 配下に bbb/ccc.txt が存在しています。また、実は MergedDir は存在してません。
他のレイヤも diff 配下を見ていきます。
root@workstation:~# ls -liR /var/lib/docker/overlay2/f952dda500cd5fe53365d14c27498f39bcd4ba19b65f75e12a7d1acbc33d3f60/diff/ /var/lib/docker/overlay2/f952dda500cd5fe53365d14c27498f39bcd4ba19b65f75e12a7d1acbc33d3f60/diff/: total 4 1080190 drwxr-xr-x 2 root root 4096 Jun 26 07:42 bbb /var/lib/docker/overlay2/f952dda500cd5fe53365d14c27498f39bcd4ba19b65f75e12a7d1acbc33d3f60/diff/bbb: total 0
bbb のディレクトリが。
root@workstation:~# ls -liR /var/lib/docker/overlay2/58511fe87bd9872209b26843d021449bc89c512bcf45d5ebad0c45a7fe27ec4c/diff /var/lib/docker/overlay2/58511fe87bd9872209b26843d021449bc89c512bcf45d5ebad0c45a7fe27ec4c/diff: total 0 1080154 -rw-r--r-- 1 root root 0 Jun 26 07:42 aaa.txt
aaa.txt が。
root@workstation:~# ls -li /var/lib/docker/overlay2/2f3fa524adcbbc7c5fbb8450f4ba277bbae76f6cd8d4c09d3740633ef2f0eeed/diff total 68 1025519 drwxr-xr-x 2 root root 4096 May 29 14:20 bin 1025636 drwxr-xr-x 2 root root 4096 May 29 14:20 dev 1025637 drwxr-xr-x 15 root root 4096 May 29 14:20 etc 1025711 drwxr-xr-x 2 root root 4096 May 29 14:20 home 1025712 drwxr-xr-x 7 root root 4096 May 29 14:20 lib 1025730 drwxr-xr-x 5 root root 4096 May 29 14:20 media 1025734 drwxr-xr-x 2 root root 4096 May 29 14:20 mnt 1025735 drwxr-xr-x 2 root root 4096 May 29 14:20 opt 1025736 dr-xr-xr-x 2 root root 4096 May 29 14:20 proc 1025737 drwx------ 2 root root 4096 May 29 14:20 root 1025738 drwxr-xr-x 2 root root 4096 May 29 14:20 run 1025739 drwxr-xr-x 2 root root 4096 May 29 14:20 sbin 1025801 drwxr-xr-x 2 root root 4096 May 29 14:20 srv 1025802 drwxr-xr-x 2 root root 4096 May 29 14:20 sys 1025803 drwxrwxrwt 2 root root 4096 May 29 14:20 tmp 1025804 drwxr-xr-x 7 root root 4096 May 29 14:20 usr 1026026 drwxr-xr-x 12 root root 4096 May 29 14:20 var
これは alpine ですね。ということで、各レイヤにファイルが格納されていることがわかります。
では、コンテナを docker container run して作ってみます。
root@workstation:~# docker container run -d overlay2test bd036f606aa46144276ed5d4bdf6c4eafb26dbc5134529a90ce7a334c5591ff4 root@workstation:~# docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bd036f606aa4 overlay2test "sleep 10000" 47 seconds ago Up 46 seconds modest_chatterjee
docker container inspect でコンテナレイヤを確認してみます。
root@workstation:~# docker container inspect modest_chatterjee -f "{{json .GraphDriver.Data}}" | jq . { "LowerDir": "/var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init/diff:/var/lib/docker/overlay2/5da5f5376d357ba13dc6a5a60562fb2ebce3077758262ad5f0cd273b62e4cacc/diff:/var/lib/docker/overlay2/f952dda500cd5fe53365d14c27498f39bcd4ba19b65f75e12a7d1acbc33d3f60/diff:/var/lib/docker/overlay2/58511fe87bd9872209b26843d021449bc89c512bcf45d5ebad0c45a7fe27ec4c/diff:/var/lib/docker/overlay2/2f3fa524adcbbc7c5fbb8450f4ba277bbae76f6cd8d4c09d3740633ef2f0eeed/diff", "MergedDir": "/var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09/merged", "UpperDir": "/var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09/diff", "WorkDir": "/var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09/work" }
LowerDir に、先ほどイメージで確認したディレクトリが格納されています。
80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09 と 80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init が今回のコンテナレイヤに関わるディレクトリです。
中を見る前に、コンテナに書き込んでみます。
root@workstation:~# docker container exec modest_chatterjee touch ddd.txt
では、中を見ていきます。まず、80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init から。
root@workstation:~# ls -liR /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init/ /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init/: total 16 1338886 -rw------- 1 root root 0 Jun 26 08:49 committed 1338867 drwxr-xr-x 4 root root 4096 Jun 26 08:49 diff 1338868 -rw-r--r-- 1 root root 26 Jun 26 08:49 link 1338870 -rw-r--r-- 1 root root 115 Jun 26 08:49 lower 1338869 drwx------ 3 root root 4096 Jun 26 08:49 work /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init/diff: total 8 1338874 drwxr-xr-x 4 root root 4096 Jun 26 08:49 dev 1338873 drwxr-xr-x 2 root root 4096 Jun 26 08:49 etc /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init/diff/dev: total 8 1338876 -rwxr-xr-x 1 root root 0 Jun 26 08:49 console 1338877 drwxr-xr-x 2 root root 4096 Jun 26 08:49 pts 1338879 drwxr-xr-x 2 root root 4096 Jun 26 08:49 shm /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init/diff/dev/pts: total 0 /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init/diff/dev/shm: total 0 /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init/diff/etc: total 0 1338875 -rwxr-xr-x 1 root root 0 Jun 26 08:49 hostname 1338883 -rwxr-xr-x 1 root root 0 Jun 26 08:49 hosts 1338878 lrwxrwxrwx 1 root root 12 Jun 26 08:49 mtab -> /proc/mounts 1338881 -rwxr-xr-x 1 root root 0 Jun 26 08:49 resolv.conf /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init/work: total 4 1338872 d--------- 2 root root 4096 Jun 26 08:49 work /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init/work/work: total 0
hostname などの個別のコンテナに必要なファイルが生成され、 LowerDir としてレイヤが出来ていることがわかります。
続いて、80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09 を見ていきます。
root@workstation:~# ls -li /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09/ total 20 1338882 drwxr-xr-x 3 root root 4096 Jun 26 08:49 diff 1338884 -rw-r--r-- 1 root root 26 Jun 26 08:49 link 1338887 -rw-r--r-- 1 root root 144 Jun 26 08:49 lower 1338867 drwxr-xr-x 1 root root 4096 Jun 26 08:49 merged 1338885 drwx------ 3 root root 4096 Jun 26 08:49 work root@workstation:~# ls -li /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09/merged/ total 72 1080154 -rw-r--r-- 1 root root 0 Jun 26 07:42 aaa.txt 1338831 drwxr-xr-x 1 root root 4096 Jun 26 08:54 bbb 1025519 drwxr-xr-x 2 root root 4096 May 29 14:20 bin 1338874 drwxr-xr-x 1 root root 4096 Jun 26 08:49 dev 1338873 drwxr-xr-x 1 root root 4096 Jun 26 08:49 etc 1025711 drwxr-xr-x 2 root root 4096 May 29 14:20 home 1025712 drwxr-xr-x 7 root root 4096 May 29 14:20 lib 1025730 drwxr-xr-x 5 root root 4096 May 29 14:20 media 1025734 drwxr-xr-x 2 root root 4096 May 29 14:20 mnt 1025735 drwxr-xr-x 2 root root 4096 May 29 14:20 opt 1025736 dr-xr-xr-x 2 root root 4096 May 29 14:20 proc 1025737 drwx------ 2 root root 4096 May 29 14:20 root 1025738 drwxr-xr-x 2 root root 4096 May 29 14:20 run 1025739 drwxr-xr-x 2 root root 4096 May 29 14:20 sbin 1025801 drwxr-xr-x 2 root root 4096 May 29 14:20 srv 1025802 drwxr-xr-x 2 root root 4096 May 29 14:20 sys 1025803 drwxrwxrwt 2 root root 4096 May 29 14:20 tmp 1025804 drwxr-xr-x 7 root root 4096 May 29 14:20 usr 1026026 drwxr-xr-x 12 root root 4096 May 29 14:20 var root@workstation:~# ls -li /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09/merged/bbb/ total 0 1338832 -rw-r--r-- 1 root root 0 Jun 26 07:42 ccc.txt 1338890 -rw-r--r-- 1 root root 0 Jun 26 08:54 ddd.txt root@workstation:~# ls -liR /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09/diff/ /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09/diff/: total 4 1338864 drwxr-xr-x 2 root root 4096 Jun 26 08:54 bbb /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09/diff/bbb: total 0 1338890 -rw-r--r-- 1 root root 0 Jun 26 08:54 ddd.txt
diff 配下に先ほど追加した ddd.txt が配置されています。また、 ddd.txt のinode番号が diff と merge で一致していること、 aaa.txt のinode番号が先のイメージレイヤのディレクトにあるものと一致している事がわかります。
mount -l | grep overlay で、overlayfs がそれぞれのディレクトリに使われていることが確認できます。
root@workstation:~# mount -l | grep overlay overlay on /var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/WZ4ETM3JIXL4SWNFGKGDOPFE6I:/var/lib/docker/overlay2/l/4CRVZPACWNPDHU6VIJIXFHIK3A:/var/lib/docker/overlay2/l/QKWVGM2CLRHUX2Q7Y33UU4TSL3:/var/lib/docker/overlay2/l/VV6S7BCGVK3XRTOIHCJYSZ5PXH:/var/lib/docker/overlay2/l/XRB6JZMFMNR4PT4HEWGX7WQTEN,upperdir=/var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09/diff,workdir=/var/lib/docker/overlay2/80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09/work) lrwxrwxrwx 1 root root 77 Jun 26 08:49 /var/lib/docker/overlay2/l/WZ4ETM3JIXL4SWNFGKGDOPFE6I -> ../80493e0448ace915e6e345f03f19bf67350f17de852b949716138a6dc3d28d09-init/diff root@workstation:~# ls -l /var/lib/docker/overlay2/l/4CRVZPACWNPDHU6VIJIXFHIK3A lrwxrwxrwx 1 root root 72 Jun 26 07:42 /var/lib/docker/overlay2/l/4CRVZPACWNPDHU6VIJIXFHIK3A -> ../5da5f5376d357ba13dc6a5a60562fb2ebce3077758262ad5f0cd273b62e4cacc/diff root@workstation:~# ls -l /var/lib/docker/overlay2/l/QKWVGM2CLRHUX2Q7Y33UU4TSL3 lrwxrwxrwx 1 root root 72 Jun 26 07:42 /var/lib/docker/overlay2/l/QKWVGM2CLRHUX2Q7Y33UU4TSL3 -> ../f952dda500cd5fe53365d14c27498f39bcd4ba19b65f75e12a7d1acbc33d3f60/diff root@workstation:~# ls -l /var/lib/docker/overlay2/l/VV6S7BCGVK3XRTOIHCJYSZ5PXH lrwxrwxrwx 1 root root 72 Jun 26 07:42 /var/lib/docker/overlay2/l/VV6S7BCGVK3XRTOIHCJYSZ5PXH -> ../58511fe87bd9872209b26843d021449bc89c512bcf45d5ebad0c45a7fe27ec4c/diff root@workstation:~# ls -l /var/lib/docker/overlay2/l/XRB6JZMFMNR4PT4HEWGX7WQTEN lrwxrwxrwx 1 root root 72 Jun 8 02:56 /var/lib/docker/overlay2/l/XRB6JZMFMNR4PT4HEWGX7WQTEN -> ../2f3fa524adcbbc7c5fbb8450f4ba277bbae76f6cd8d4c09d3740633ef2f0eeed/diff
肥大化する /var/lib/docker/overlay2/ について
しばしば Docker コンテナのディスク消費量を調べてようと du -sh /var/lib/docker/overlay2/ をしてとんでもない大きさになっていることがあります。
overlay2 を利用した場合には、du -s の値を信用してはいけません。
du -s は overlayfsによるマウントを区別しません。
コンテナレイヤを調べた場合に、実体は UpperDir のみがそのレイヤで消費されているファイルの実体ですが、 MergedDir も含めて、つまりはイメージサイズを含めて算出してしまいます。
/var/lib/docker/overlay2 が容量を圧迫していることを調べたいのであれば、実体である UpperDir の値を集計するか、あるいは df の値で確認してください。または docker system df のコマンドでも確認することができます。
root@workstation:~# du -sh /var/lib/docker/overlay2/ 4.8G /var/lib/docker/overlay2/ root@workstation:~# df -h /dev/sda1 Filesystem Size Used Avail Use% Mounted on /dev/sda1 30G 20G 9.4G 68% / root@workstation:~# for i in {1..50};do docker container run -d centos:7 sleep 1000; done fd503cfe6506f1862a6965a398c81cf0f9af584f61a503370de87e81efd7f9b7 7e8586b176ffd05860b45889c12755338e14fa460c70ff8c543c6de6b1e7a01e ccdf3c7e3081b8fdcfab4e46f880acdad20f2e7eda31a77269a48bdcfbd9eb93 260d7c805b7bb36d8c16b05e11e3db0397a52db1b14bbacac6b3455a1a96962a ... 650193c83c7086e3d155980b3ab54d27746256b64529fe44419f27c79f8e1982 1df926bc75ae07e52908b5aab30e6121a768d7ba3995e37ea143a1f905cc2035 6df317c9aa84353ec358f72df8090aaf25879224f771f16079ae4aa9daa031c5 root@workstation:~# du -sh /var/lib/docker/overlay2/ 16G /var/lib/docker/overlay2/ root@workstation:~# df -h /dev/sda1 Filesystem Size Used Avail Use% Mounted on /dev/sda1 30G 20G 9.4G 68% / root@workstation:~# docker system df TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 27 3 4.416GB 3.813GB (86%) Containers 53 50 100.45MB 100.45MB (100%) Local Volumes 0 0 0B 0B Build Cache 0 0 0B 0B
df -h の値は変わっていないのに、 du -sh の値が激増していることが確認できます。
イメージサイズの大きなコンテナを1ノードで大量に起動していると、こういった事象に直面することがあります。イメージサイズが大きくなくても、例えば GitLab CI/CD で Docker Executor を利用して大量にジョブを回している場合など、終了したコンテナが大量に溜まることで発生します。Kubernetesを利用している場合は終了したコンテナが自動で削除されていきますので、あまり見かけないかもしれません。
何にせよ使わなくなったコンテナが溜まっている状態は良くありませんので、定期的に docker prune で片づけるようにしましょう。
まとめ
ということで、質問に対する回答は下記となります。
コンテナを起動し、その中に大きな容量のファイルを保存した場合、実際にはどこのディスク容量が消費されるのでしょうか?
/var/lib/docker/overlay2 にマウントされたディスクの容量が消費されます。
複数のコンテナを起動した時に、イメージレイヤのファイルがそれぞれ /var/lib/docker/overlay2 に書き込まれますか?それともコンテナレイヤの中身に書き込まれたデータだけが /var/lib/docker/overlay2 に作られますか?
コンテナ起動時にはイメージレイヤのファイルは書き込まれません。イメージレイヤのファイルは build や pull のタイミングで書き込まれます。コンテナレイヤの中身に書き込まれたデータだけが /var/lib/docker/overlay2 に書き込まれます。
おわりに
本記事では、弊社クリエーションラインの開催するDocker Fundamental Trainingで頂いた質問と回答についてご紹介させて頂きました。クリエーションラインでは、Mirantis社公認のDocker Trainingを提供しています。Dockerについて、イメージの作成からSwarmやKubernetesによるオーケストレーションまで、ハンズオン形式で基礎を網羅的に学習できる2日間のトレーニングです。
開催のご相談を承っておりますので、興味がありましたらDockerトレーニングコースをご参照ください。