2017/04/01

Dockerのストレージにbtrfsを使用する場合には空き領域に注意

ある日突然 Docker のストレージとして使用していた btrfs の領域がディスクフルになってデーモンが起動しなくなってしまったので対応した時のメモです。

Why docker on btrfs?

Docker を Debian GNU/Linux 8 環境で使用していますが、ストレージドライバとして LVM Thin を使用できないことが分かりました。

デフォルトのループバックデバイスを使用した devicemapper では性能が悪そうですし、overlay や zfs は Debian 8 では使用できないので、まともに運用したことはありませんが消去法で btrfs を試していました。
# lvcreate -L 30G -n docker vg
# mkfs.btrfs -f /dev/mapper/vg-docker

突然の no space left on device

そんなある日 OS を起動してみると docker デーモンが動いていません。

systemd[1]: Starting Docker Application Container Engine...
dockerd[23670]: chmod /var/lib/docker: no space left on device
systemd[1]: docker.service: main process exited, code=exited, status=1/FAILURE
systemd[1]: Failed to start Docker Application Container Engine.
systemd[1]: Unit docker.service entered failed state.
/var/lib/docker の空きが足りない? dfコマンドの出力を見ると不足しているようには見えませんが。。。
# df -hT /var/lib/docker
ファイルシス          タイプ サイズ  使用  残り 使用% マウント位置
/dev/mapper/vg-docker btrfs     30G   17G   13G   57% /var/lib/docker
エラーメッセージ「chmod /var/lib/docker: no space left on device」で検索すると以下の記事が見つかりました。 どうやら btrfs の領域は df コマンドでは正確には表示できず、btrfs filesystem サブコマンドを使用しなければいけないことが分かりました。 btrfs filesystem df の出力をみると total や used の値からは df の出力とそれほど変わらないように見えますが、
# btrfs filesystem df /var/lib/docker
Data, single: total=27.22GiB, used=14.55GiB
System, DUP: total=8.00MiB, used=16.00KiB
System, single: total=4.00MiB, used=0.00B
Metadata, DUP: total=1.38GiB, used=1.03GiB
Metadata, single: total=8.00MiB, used=0.00B
GlobalReserve, single: total=352.00MiB, used=0.00B
btrfs filesystem show の出力を見ると size と used が同じ値でした。ということで何かの理由で空き領域が無いようです。
# btrfs filesystem show
Label: none  uuid: d5d3aaa0-b798-4dc5-9969-5dfd9807a089
        Total devices 1 FS bytes used 15.58GiB
        devid    1 size 30.00GiB used 30.00GiB path /dev/mapper/vg-docker

Balanceによる領域の開放

先の記事では btrfs のディスクフル問題の解決として以下の記事を参照していました。

これによると実データの使用量によってディスクフルなっていなければ、データまたはメタデータの平準化(balance)を行えば解決できそうです。 しかし今回は balance を行うために必要な領域も残っていない状態でした。
# btrfs balance start -dusage=55 /var/lib/docker
ERROR: error during balancing '/var/lib/docker' - No space left on device
# btrfs balance start -v -dusage=0 /var/lib/docker
Dumping filters: flags 0x1, state 0x0, force is off
  DATA (flags 0x2): balancing, usage=0
ERROR: error during balancing '/var/lib/docker' - No space left on device
There may be more info in syslog - try dmesg | tail
最後の手段として btrfs に一時作業用のデバイスを追加する方法が紹介されていました。
One trick to get around this is to add a device (even a USB key will do) to your btrfs filesystem. This should allow balance to start, and then you can remove the device with btrfs device delete when the balance is finished.
例では dd で作成したファイルをループバックマウントしたものを btrfs に追加していましたが、面倒なので今回は論理ボリュームを追加しました。
# lvcreate -L5g -n btrfs_tmp vg
  Logical volume "btrfs_tmp" created

# btrfs device add /dev/vg/btrfs_tmp /var/lib/docker
Performing full device TRIM (5.00GiB) ...

# btrfs fi show
Label: none  uuid: d5d3aaa0-b798-4dc5-9969-5dfd9807a089
        Total devices 2 FS bytes used 15.58GiB
        devid    1 size 30.00GiB used 30.00GiB path /dev/mapper/vg-docker
        devid    2 size 5.00GiB used 0.00B path /dev/mapper/vg-btrfs_tmp
そして balance を実行すると今度は正常に終了しました。
# btrfs balance start -v -dusage=0 /var/lib/docker
Dumping filters: flags 0x1, state 0x0, force is off
  DATA (flags 0x2): balancing, usage=0
Done, had to relocate 7 out of 36 chunks
syslog には次のような出力がありました。
kernel: BTRFS info (device dm-10): relocating block group 30496784384 flags 1
kernel: BTRFS info (device dm-10): relocating block group 29423042560 flags 1
kernel: BTRFS info (device dm-10): relocating block group 28349300736 flags 1
kernel: BTRFS info (device dm-10): relocating block group 27275558912 flags 1
kernel: BTRFS info (device dm-10): relocating block group 26201817088 flags 1
kernel: BTRFS info (device dm-10): relocating block group 25128075264 flags 1
kernel: BTRFS info (device dm-10): relocating block group 24054333440 flags 1
kernel: BTRFS info (device dm-10): relocating block group 22980591616 flags 1
kernel: BTRFS info (device dm-10): relocating block group 30727471104 flags 36
balance 後のファイルシステムの状態をみると used の値を足しても 23GB なので 7GB ほど解放されたようです。
# btrfs fi show
Label: none  uuid: d5d3aaa0-b798-4dc5-9969-5dfd9807a089
        Total devices 2 FS bytes used 15.58GiB
        devid    1 size 30.00GiB used 22.79GiB path /dev/mapper/vg-docker
        devid    2 size 5.00GiB used 256.00MiB path /dev/mapper/vg-btrfs_tmp
元々あったデバイスに空き領域が確保できたので作業用として btrfs に追加した論理ボリュームは削除します。
# btrfs device delete /dev/vg/btrfs_tmp /var/lib/docker

# lvremove -f vg/btrfs_tmp
  Logical volume "btrfs_tmp" successfully removed
作業後の btrfs の状態。
# btrfs fi show
Label: none  uuid: d5d3aaa0-b798-4dc5-9969-5dfd9807a089
        Total devices 1 FS bytes used 15.58GiB
        devid    1 size 30.00GiB used 23.04GiB path /dev/mapper/vg-docker

# btrfs fi df /var/lib/docker
Data, single: total=20.01GiB, used=14.55GiB
System, DUP: total=8.00MiB, used=16.00KiB
System, single: total=4.00MiB, used=0.00B
Metadata, DUP: total=1.50GiB, used=1.03GiB
Metadata, single: total=8.00MiB, used=0.00B
GlobalReserve, single: total=352.00MiB, used=0.00B
作業前と比較すると Data の total が -7.21GiB、Metadata DUP の total が -0.12GiB となりました。
# btrfs filesystem df /var/lib/docker
Data, single: total=27.22GiB, used=14.55GiB
System, DUP: total=8.00MiB, used=16.00KiB
System, single: total=4.00MiB, used=0.00B
Metadata, DUP: total=1.38GiB, used=1.03GiB
Metadata, single: total=8.00MiB, used=0.00B
GlobalReserve, single: total=352.00MiB, used=0.00B
空き領域が確保できたので無事に docker デーモンも起動できました。
# docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 70
Server Version: 17.03.1-ce
Storage Driver: btrfs
 Build Version: Btrfs v3.17
 Library Version: 101
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
 Volume: local
 Network: bridge host macvlan null overlay
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 4ab9917febca54791c5f071a9d1f404867857fcc
runc version: 54296cf40ad8143b62dbcaa1d90e520a2136ddfe
init version: 949e6fa
Kernel Version: 3.16.0-4-amd64
Operating System: Debian GNU/Linux 8 (jessie)
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 15.71 GiB
Name: microserver.myhome
ID: JNLR:5HTY:XMAU:CASU:OHUW:FUYU:3KYE:GGEM:O3SH:QYJK:PKT5:7LYE
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
No Proxy: localhost,127.0.0.1
Registry: https://index.docker.io/v1/
WARNING: No kernel memory limit support
WARNING: No cpu cfs quota support
WARNING: No cpu cfs period support
Experimental: false
Insecure Registries:
 127.0.0.0/8
Live Restore Enabled: false

おわりに

あらためて Docker のドキュメントをみてみると btrfs を使う場合のプラクティスとして balance を cron で定期的に実行するように書いてある事に気づきました。

  • Docker and Btrfs in practice - Docker Documentation
    Enable a cronjob to rebalance your BTRFS devices. e.g. Spread the subvolume’s blocks evenly across your raid devices, and reclaim unused blocks. Without doing this, snapshots and subvolumes that docker removes will leave allocated blocks fillingup the BTRFS root volume
Docker ではイメージレイヤーを一つの btrfs サブボリュームとして作成しますので、多数のコンテナの生成・破棄を繰り返すような使い方をする場合には定期的な balance の実行が必須だと思いました。
# btrfs subvolume list /var/lib/docker | wc -l
739

ZFS のような使い勝手を目指したファイルシステム、という程度の認識で btrfs を使っていますが、Copy on Write という特性による領域の割り当てなど、運用で気をつけなければいけない点があることを身をもって知りました。

あと btrfs の操作はほぼ全て btrfs コマンドのサブコマンドのようですが、最初に打ち込む btrfs が QWERTY 配列のキーボードだと全て左手でタイプしなければいけない文字で構成されているので、ZFS における zfs コマンドや zpool コマンドと比べると長くて打ちづらいな、と今回の作業を通じて強く感じました。

0 件のコメント:

コメントを投稿