Что это и как отличается от symlink
Bind mount делает один и тот же набор inode'ов доступным через два пути. Не дублирование данных, не link на уровне ФС - это операция на уровне VFS-ядра.
sudo mount --bind /var/log /home/serge/logs
ls /var/log/syslog
ls /home/serge/logs/syslog # тот же файл, тот же inode
Чем отличается от symbolic-link:
| Свойство | symlink | bind mount |
|---|---|---|
| Что это | inode-указатель в FS | VFS-маунт |
| После reboot | сохраняется | пропадает (нужен fstab) |
chroot видит | сломанный, если цель снаружи | работает, цель уже «внутри» |
realpath | резолвит | НЕ резолвит (виден как target-путь) |
| Нужны права root | нет | ДА (mount(2)) |
| Можно read-only | нет | да (-o ro) |
Главная сила bind mount: внутри chroot или namespaces видно только реальный путь - symlink на путь снаружи был бы сломан.
Базовое использование
# 1. Каталог в каталог
sudo mount --bind /var/log /mnt/logs
# 2. Файл поверх файла (мощный приём)
sudo mount --bind /tmp/my-fake-resolv.conf /etc/resolv.conf
▸теперь /etc/resolv.conf указывает на наш файл, оригинал жив
# 3. Размонтировать
sudo umount /mnt/logs
Подмена через bind файла поверх файла - частая фича в контейнерах и hot-fix'ах: «не править оригинал, наложить bind на лету».
Read-only bind - двухшаговый
Опция -o ro на самом mount'е игнорируется ядром при первом bind.
Нужно отдельным шагом сделать remount:
sudo mount --bind /opt/data /srv/ro-data
sudo mount -o remount,ro,bind /srv/ro-data # вот теперь действительно RO
Или одной командой через --mkdir + -o:
sudo mount --bind -o ro /opt/data /srv/ro-data # на util-linux >= 2.27 работает напрямую
Проверить:
findmnt /srv/ro-data
# TARGET SOURCE FSTYPE OPTIONS
# /srv/ro-data /dev/sda1[/opt/data] ext4 ro,relatime
Колонка SOURCE с [/...] в скобках - признак bind mount'а.
--bind vs --rbind
sudo mount --bind / /mnt/host # bind: НЕ перенесёт submounts
sudo mount --rbind / /mnt/host # rbind: рекурсивно с /proc, /sys, /dev и т.д.
--rbind критичен когда исходный каталог содержит вложенные точки
монтирования (классический пример - bind-mount всей корневой ФС
для chroot'а). Без r ты получишь пустые /proc, /sys, /dev внутри.
Mount propagation - про что это
Когда внутри bind-mount'а делают новый mount - должен ли он быть виден снаружи? Это управляется флагом propagation:
| Флаг | Поведение |
|---|---|
shared | новые mounts видны в обе стороны (default на systemd-системах) |
slave | mounts из master видны у нас, но не наоборот |
private | mounts невидимы в обе стороны |
unbindable | вообще нельзя bind'ить |
Установка:
sudo mount --make-private /srv/jail
sudo mount --make-rslave / # рекурсивно для всего дерева
Зачем это знать - Docker и systemd активно играют propagation'ом. Если у тебя
в /etc/fstab что-то с bind и оно ведёт себя странно при reboot -
смотри findmnt -o TARGET,PROPAGATION.
Пример: mount --bind / для chroot'а сделает то же propagation что у /. Если
потом размонтировать что-то внутри chroot'а - это размонтируется и снаружи
(если propagation = shared). Не очевидно. Решение: --make-rprivate
на bind-копии.
fstab - постоянный bind
# /etc/fstab
/var/log /home/serge/logs none bind 0 0
/opt/data /srv/ro-data none bind,ro 0 0
/home /chroot/home none rbind 0 0
/tmp/conf /etc/myapp.conf none bind 0 0
Тип ФС - none (не относится к bind'у). Опции через bind или rbind.
Чтобы systemd-fstab-generator подхватил без reboot:
sudo systemctl daemon-reload
sudo mount -a
Применение на практике
chroot и rescue
for fs in proc sys dev dev/pts run; do
sudo mount --bind /$fs /mnt/$fs
done
sudo chroot /mnt
Без bind'ов /proc и /dev многие команды внутри chroot упадут.
Перенос данных без смены пути
Допустим /var/lib/postgres забивает корневую ФС, нужно перенести на
отдельный диск без правки конфига Postgres'а:
sudo systemctl stop postgresql
sudo rsync -aHAX /var/lib/postgres/ /data/postgres/
sudo mv /var/lib/postgres /var/lib/postgres.old
sudo mkdir /var/lib/postgres
echo '/data/postgres /var/lib/postgres none bind 0 0' | sudo tee -a /etc/fstab
sudo mount -a
sudo systemctl start postgresql
Postgres продолжает читать /var/lib/postgres, но физически данные на /data.
Read-only слой в контейнере
sudo mount --bind /etc /run/jail/etc
sudo mount -o remount,ro,bind /run/jail/etc
systemd PrivateTmp
Когда сервис имеет PrivateTmp=yes, systemd под капотом делает
bind+namespace: создаёт пустой каталог и bind-монтирует его поверх
/tmp для этого сервиса. Сервис не видит /tmp хоста.
Ограничения
- Файл нельзя bind'нуть на каталог и наоборот. Только файл-в-файл, каталог-в-каталог.
- Цель должна существовать. Файл - пустой создать, каталог -
mkdir. umountснимает только bind, оригинал остаётся:bashsudo umount /mnt/logs # /var/log жив
duпосчитает дважды если оба пути попадут в обход:bashdu -sh /var/log /mnt/logs # суммарно как 2× размер
Дебаг
findmnt --types=none # все bind/rbind монты
findmnt -o TARGET,SOURCE,OPTIONS,PROPAGATION /srv/jail
cat /proc/self/mountinfo # сырой дамп всех mount'ов
mount | grep bind # старая школа
Bind-маунты в mountinfo имеют формат <src-fs>[/sub/path] в SOURCE -
именно отсюда findmnt и узнаёт что это bind.