TT Lab
はじめる
学ぶ 学習パス コース

ストレージとマウント

マウント名前空間と伝播の方式

TT Labで続きを見る

一言でいうと

/proc/self/mountinfoは、「このプロセスが見るマウントツリー全体」を持っており、そこには、mountコマンドが見せてくれない伝播タイプ(propagation)まで書かれています。

なぜ必要なのか

コンテナを使い始めると、マウントが急に難しくなります。ホストで取り付けたボリュームがコンテナに見えなかったり、コンテナの中で取り付けたものがホストに漏れたり、コンテナを消したのにマウントが残っていたりします。これらの現象は、すべてマウント名前空間と伝播タイプで説明できます。

どう動くのか

mountinfoを読む

36 25 0:31 / /sys/fs/cgroup rw,nosuid,nodev,noexec shared:9 - cgroup2 cgroup2 rw
[1][2][3] [4]    [5]              [6]                [7]   [8]  [9]     [10]  [11]
フィールド 意味
1 マウントID
2 親マウントID
3 major:minorのデバイス番号
4 元の中でのルート。バインドマウントなら、ここは/ではありません
5 マウントポイント
6 マウントオプション
7 任意フィールド: 伝播タイプ(shared:、master:、propagate_from:、なければprivate)
8 区切り文字-
9–11 ファイルシステムのタイプ、ソース、上位オプション

フィールド4が、バインドマウントを見分けるカギです。同じデバイスのサブディレクトリを取り付けたなら、そこに/の代わりにそのサブパスが書かれます。

伝播タイプ4種類

タイプ mountinfoの表示 動作
private (表示なし) このマウントの変化が、どこにも伝播しない
shared shared:N 双方向の伝播。ここでマウントすればペアにも見え、逆も同じ
slave master:N 単方向。マスターの変化は受け取るが、自分の変化は送らない
unbindable unbindable バインドマウントの元になれない

コンテナランタイムがボリュームを取り付けるときにrslaveやrprivateを使う理由が、ここにあります。コンテナの中で誤って取り付けたマウントがホストに漏れると困り、逆に、ホストが取り付けたものは見えなければならない場合が多いからです。

tmpfs

メモリ上のファイルシステムです。再起動すると消えます。

tmpfs  /var/cache/app  tmpfs  size=256M,mode=1777,noexec,nosuid,nodev  0  0

systemdの.mountユニット

fstabの代わりに、ユニットファイルでもマウントを定義できます。ユニット名は、マウントポイントのパスをエスケープしたものでなければなりません。

# /etc/systemd/system/var-cache-app.mount
[Unit]
Description=Application cache (tmpfs)

[Mount]
What=tmpfs
Where=/var/cache/app
Type=tmpfs
Options=size=256M,mode=1777,noexec,nosuid,nodev

[Install]
WantedBy=local-fs.target

/var/cache/app → var-cache-app.mountです。先頭のスラッシュは除き、残りのスラッシュはハイフンに変えます。名前がパスと合わないと、systemdがユニットを拒否します。systemd-escape --path /var/cache/appで、正確な名前を得られます。

マウントが消えたり見えなかったりするとき

バインドマウントとtmpfsは単純に見えますが、伝播と名前空間のために、予想と違う動作をする場合があります。

コンテナの中では、あとから取り付けたマウントが見えません。ホストで/mnt/dataに新しいディスクをマウントしても、すでにそのパスをバインドしてあるコンテナには現れません。伝播タイプがprivateだと、以降のマウントが伝わらないからです。KubernetesでmountPropagation: HostToContainerを使う理由が、これです。

findmnt -o TARGET,SOURCE,PROPAGATION /mnt/data

tmpfsはメモリを使います。dfにはファイルシステムとして見えますが、実際にはRAMで、コンテナではそのメモリがcgroupの上限に含まれます。/tmpをtmpfsにして大きなファイルを書くと、OOMで死にます。サイズは必ず決めます。

tmpfs /tmp tmpfs size=512M,mode=1777,nosuid,nodev 0 0

空のディレクトリにマウントしないと、元の内容が隠れます。そのファイルは消えたのではなく、下に敷かれたまま容量を占めます。取り戻すには、マウントを外すか、別の場所にバインドして見ます。

mkdir /mnt/under && mount --bind / /mnt/under
ls /mnt/under/mnt/data       # 가려진 원래 내용

umountできないときは、誰が使っているかを見ます。

lsof +f -- /mnt/data
fuser -vm /mnt/data
umount -l /mnt/data          # 마지막 수단: 이름만 떼고 나중에 정리한다

-l(lazy)は、開いているファイルが閉じられるまで実際には取り付けられたままなので、ディスクを物理的に取り外す状況では安全ではありません。

バインドマウントに付けるオプションは、2回指定する必要があります。mount --bind -o roは、Linuxでは読み取り専用になりません。マウントしたあとに、もう一度指定する必要があります。

mount --bind /src /dst
mount -o remount,ro,bind /dst

現場での姿

コンテナを消したのにマウントが残ります。伝播タイプがsharedの状態でコンテナがマウントを作ると、ホストの名前空間に漏れ出します。コンテナが死んでも、そのマウントはホストに残ってディスクを握り続けます。findmntで探して、手動で片づける必要があります。

tmpfsのサイズを決めずにOOMが起きます。size=なしで作ったtmpfsにログがたまり、メモリを食いつくしました。ディスクではなくメモリだという事実を、忘れやすいです。

次のラボですること

/proc/self/mountinfoを直接パースしてマウントツリーと伝播タイプを表にし、バインドマウントとtmpfsのfstabの行を作成し、.mountユニットファイルを正確な名前で作成します。