Learning Platform
Глоссарий Troubleshooting
Урок 10.04 · 22 мин
Начальный
FilesystemsMountfstabNamespacesDocker

Mount, /etc/fstab и mount namespaces

mount — одна из тех команд Linux, которые кажутся простыми («монтируй диск»), но скрывают огромную глубину: десятки опций, концепция bind mount, mount namespaces, propagation modes, и связь с контейнерами. Понимание mount это понимание того, как Linux строит своё единое дерево из десятков отдельных файловых систем.

В современных Linux всё построено на mount-ах: контейнеры — через mount namespaces, system-d-сервисы — через PrivateTmp/ProtectHome, Docker volumes — через bind mount, /tmp в RAM — через tmpfs mount. Понимание этих механизмов делает контейнерные технологии прозрачными.

В этом уроке: классический mount disk, опции (ro, noexec, nosuid), /etc/fstab для автоматики, bind mount как способ показать одно место в двух точках дерева, mount namespaces — основа контейнеров.


Mount disk — классика

# Подключить ext4 раздел в /mnt/data:
sudo mount /dev/sdb1 /mnt/data

# С опциями:
sudo mount -t ext4 -o ro,noexec,nosuid /dev/sdb1 /mnt/data
#          ^^^^^^^ ^^^^^^^^^^^^^^^^^^^
#          явный type   опции (read-only, no execute, no suid)

# Отмонтировать:
sudo umount /mnt/data

# Если занято -- посмотреть кто держит:
sudo umount /mnt/data
# umount: /mnt/data: target is busy.

sudo lsof /mnt/data
sudo fuser -m /mnt/data

# Принудительно (только для NFS / dead processes):
sudo umount -f /mnt/data

# Lazy unmount -- отмонтировать когда освободится:
sudo umount -l /mnt/data

Опции mount — это десятки параметров, влияющих на безопасность и производительность:

Полезные mount-опции
ro / rwRead-only или read-write. Read-only часто для system partitions, base images, backup-снимков. Защищает от случайной записи
noexecЗапретить execute файлов на этой ФС. Защита от malware в /tmp и других user-writable местах. Multi-tenant безопасность
nosuidОтключить эффект setuid/setgid битов. Программы с setuid на этой ФС не получают повышенные права при запуске. Стандарт для /tmp, /home, removable media
nodevЗапретить использование device-файлов. Программы не могут открыть /dev/sda1 через эту ФС. Стандарт для не-системных партиций
noatime / relatimenoatime: не обновлять access time при каждом read -- большой performance win. relatime: обновлять только если atime < mtime, компромисс. Современный default -- relatime
discard / nodiscarddiscard: посылать TRIM-команды SSD при удалении файлов (полезно для долгоживущих SSD). nodiscard: не посылать (вместо этого periodic fstrim)
user_xattr / aclВключить поддержку extended attributes / POSIX ACL. По умолчанию обычно включены в ext4
errors=remount-roПри ошибке (corruption) переключиться в read-only вместо paniс. Защищает данные. Default для большинства дистрибуций

noexec + nosuid + nodev — стандартный triplet для пользовательских и временных директорий. systemd ставит их автоматически для PrivateTmp.


/etc/fstab — автоматический mount при boot

/etc/fstab — описание всех mount-точек, которые должны быть автоматически смонтированы при загрузке системы:

cat /etc/fstab
# <device>                                 <mount point>  <fstype> <options>             <dump> <pass>
# UUID=12345678-9abc-def0-1234-56789abcdef0 /              ext4     defaults,relatime     0      1
# UUID=fedcba98-7654-3210-fedc-ba9876543210 /home          ext4     defaults,relatime     0      2
# UUID=11111111-2222-3333-4444-555555555555 none           swap     sw                    0      0
# tmpfs                                     /tmp           tmpfs    defaults,size=4G      0      0
# nas:/export/share                         /mnt/nas       nfs      defaults,_netdev      0      0

Поля:

  1. Device — путь к устройству или специальный токен. Лучше UUID или LABEL, чем /dev/sdaX (последние могут меняться).
  2. Mount point — куда монтировать.
  3. Filesystem type — ext4, xfs, tmpfs, nfs, btrfs, vfat…
  4. Options — через запятую. defaults = rw,suid,dev,exec,auto,nouser,async.
  5. Dump — для бэкапа через dump (legacy, обычно 0).
  6. Pass — fsck order. 1 для root, 2 для остальных, 0 — не проверять.
# Получить UUID для записи в fstab:
sudo blkid
# /dev/nvme0n1p1: UUID="ABCD-1234" TYPE="vfat"   (EFI partition)
# /dev/nvme0n1p2: UUID="..." TYPE="ext4"          (root)
# /dev/sdb1:      UUID="..." TYPE="ext4" PARTUUID="..."

# Тестировать новую запись fstab без перезагрузки:
sudo mount -a
# Монтирует всё в fstab, что ещё не смонтировано. Если строка плохая -- ошибка

_netdev опция — ключевая для сетевых ФС (NFS, CIFS): говорит systemd подождать сеть перед монтированием. Без неё система может зависнуть на boot, ожидая сеть в init scripts.

# Если fstab сломан и не загружается:
# Boot в emergency mode, в grub добавить:
# systemd.unit=emergency.target
# Или: nofail в опциях fstab -- не блокировать boot

Bind mount — одна директория в двух местах

mount --bind src dst — показать содержимое src в dst. Это не копия и не symlink — VFS буквально показывает inode-структуру src в точке dst.

# Простой пример:
mkdir /tmp/work
echo "data" > /tmp/work/file.txt
mkdir /tmp/mounted

sudo mount --bind /tmp/work /tmp/mounted

ls /tmp/mounted/
# file.txt
cat /tmp/mounted/file.txt
# data

# Запись через любой путь видна с другого:
echo "more" > /tmp/mounted/another.txt
ls /tmp/work/
# file.txt  another.txt

# В отличие от symlink, bind mount -- настоящий новый mount point:
mount | grep work
# /dev/nvme0n1p2 on /tmp/mounted type ext4 (rw)

sudo umount /tmp/mounted

Зачем bind mount, если есть symlinks? Несколько ситуаций:

  1. Symlinks не работают в chroot, если target вне chroot. Bind mount работает.
  2. Поведение в некоторых программах. Например, sudo с symlinks может ругаться, с bind mount — нет.
  3. Read-only override. mount --bind -o ro /etc/sensitive /etc/sensitive сделает её read-only даже для root.
  4. Different mount points. Bind mount можно отмонтировать без удаления.
  5. Container volumes. Docker volumes реализуются через bind mount.

mount --rbind (recursive bind) — bind включая все sub-mounts. Например, --rbind / принесёт все вложенные mount-точки.


Mount namespaces — основа контейнеров

Mount namespace — одна из главных Linux намespace-фишек. Каждый namespace имеет своё дерево mount-ов. Процессы в разных namespaces видят разные mount-точки.

# Стандартный namespace системы:
mount | wc -l
# 25  -- 25 mounts видит обычный процесс

# Создать новый mount namespace через unshare:
sudo unshare --mount --propagation private bash

# Внутри:
mount -t tmpfs none /mnt
ls /mnt   # пусто, новая tmpfs
echo "in namespace" > /mnt/test

# В другом терминале (хост):
ls /mnt   # пусто или старое содержимое, tmpfs не виден
mount | grep '/mnt'   # ничего

# Вернуться в namespace shell:
cat /mnt/test
# in namespace

# При exit shell namespace умирает, mount исчезает
exit

Это и есть основа Docker, LXC, podman — они создают mount namespace и внутри него выстраивают своё дерево ФС:

Mount namespaces в контейнерах
Host mount namespaceГлавный namespace системы. Видит все mount-точки физических дисков, tmpfs, nfs, и других. Это то, что вы видите в обычной shell
Container 1 namespaceСоздан Docker-ом через unshare CLONE_NEWNS. Внутри: image слои через overlayfs, mount volumes из host через --rbind, изолированный /proc, /sys, /dev
Container 2 namespaceСовершенно отдельный namespace. Свой /tmp, свой /var, свой root. Файлы одного контейнера невидимы для другого
overlayfs / layersDocker использует overlayfs: lower layer (read-only image), upper layer (writable container changes). Иллюзия independent FS, физически дедуплицированной между контейнерами
bind mount volumesdocker run -v /host/data:/data делает rbind mount /host/data в /data контейнера. Файлы реально хранятся на host, контейнер их видит

systemd использует mount namespaces для изоляции сервисов:

# Посмотреть unit-файл:
systemctl cat nginx | head
# ProtectHome=yes      -- /home недоступен через свой mount namespace
# ProtectSystem=full   -- /usr, /etc read-only
# PrivateTmp=yes       -- свой /tmp в отдельном tmpfs
# PrivateDevices=yes   -- свой /dev только с минимумом

Эти настройки — через mount namespaces + bind mount с разными permissions. Сервис в своём namespace видит limited view.


Propagation modes — как меняются mounts

Когда вы делаете mount в одном namespace, он может (или не может) распространиться на другие namespaces. Это контролируется propagation modes:

  • shared — mount распространяется во все «shared» namespaces. Default в большинстве дистрибуций.
  • private — mount не распространяется никуда. Изоляция.
  • slave — получает propagation от master, но не отправляет своим.
  • unbindable — нельзя сделать bind mount (защита от рекурсии).
# Посмотреть propagation:
findmnt -o TARGET,PROPAGATION

# Сделать private (контейнерный подход):
sudo mount --make-private /

# Это то, что Docker делает при старте:
# 1. unshare --mount --propagation private
# 2. mount overlay для image
# 3. mount bind для volumes
# 4. chroot или pivot_root в новое дерево
Как Docker строит файловую систему контейнера

Loop devices — mount файла как ФС

Иногда нужно работать с disk image-файлом как с реальным диском:

# Создать image-файл и форматировать как ext4:
dd if=/dev/zero of=/tmp/disk.img bs=1M count=100
mkfs.ext4 /tmp/disk.img

# Mount файла как ФС через loop:
sudo mount -o loop /tmp/disk.img /mnt/image

# Теперь /mnt/image -- ext4 ФС из файла /tmp/disk.img
ls /mnt/image
# lost+found

# Создать файл, посмотреть -- работает как обычная ФС:
echo "data" | sudo tee /mnt/image/test
sudo cat /mnt/image/test

# Отмонтировать:
sudo umount /mnt/image

Loop devices — основа cloud images, ISO-mount, sparse files в виртуализации.


Попробуй сам

Проверьте все mount-точки своей системы:

findmnt
# Иерархический список всех mount-точек
# Видны типы (ext4, tmpfs, sysfs, proc, cgroup2) и опции

# Сколько mount-точек:
findmnt -n | wc -l

# Все bind mounts:
findmnt -t none --types bind 2>/dev/null
# Часто systemd создаёт bind mounts для PrivateTmp

Создайте свой mount namespace:

# В одном терминале:
sudo unshare --mount bash
mount -t tmpfs -o size=50M none /tmp
df -h /tmp     # видит 50 МБ tmpfs

# Создать файлы только в этом namespace:
for i in {1..10}; do echo $i > /tmp/file$i; done
ls /tmp/   # видим 10 файлов

# В другом терминале (host):
ls /tmp/   # обычное /tmp, файлов нет

# Exit -- namespace умирает:
exit

Эксперимент с bind mount:

# Создать структуру:
mkdir -p /tmp/src /tmp/dst
echo "in src" > /tmp/src/file.txt

# Bind mount:
sudo mount --bind /tmp/src /tmp/dst
ls /tmp/dst   # file.txt

# Запись в dst -- видна в src:
echo "from dst" > /tmp/dst/another.txt
ls /tmp/src/  # file.txt another.txt

# Сделать /tmp/dst read-only без изменения src:
sudo mount -o remount,ro,bind /tmp/dst
echo "test" > /tmp/dst/new.txt
# bash: /tmp/dst/new.txt: Read-only file system

# /tmp/src ещё writable:
echo "test" > /tmp/src/new.txt
# OK

# Cleanup:
sudo umount /tmp/dst
rm -rf /tmp/src /tmp/dst

Проверка знанийKnowledge check
Docker контейнер запускается с 'docker run -v /host/data:/data:ro alpine'. Что физически происходит в kernel при этом mount-е и каковы ограничения?
ОтветAnswer
Полная цепочка событий: 1) Docker daemon вызывает unshare CLONE_NEWNS для создания mount namespace контейнера. 2) В новом namespace настраивает root FS контейнера через overlayfs (lower = image layers, upper = writable layer). 3) Делает chroot/pivot_root в новый root. 4) Для volume mount: mount --bind /host/data /data внутри namespace, плюс remount с ro option для read-only. 5) Затем mount --make-private чтобы избежать propagation между контейнерами. Ограничения: 1) Bind read-only внутри -- но host может писать. Контейнер не блокирует host. 2) Если /host/data -- директория из tmpfs или другой mount-точки, могут быть тонкости с propagation. 3) Permissions inside container: UID 1000 в контейнере != UID 1000 на host (если не настроен user namespace mapping). Файл созданный root в /data внутри будет owned by host root -- может быть проблема. 4) SELinux / AppArmor labels: на host могут не позволить контейнеру читать (нужен :z или :Z в bind для relabeling). 5) При остановке контейнера mount unmounted автоматически (namespace умирает). 6) overlayfs не работает на некоторых FS (например, NFS) -- volume может не смочь mount-ться. 7) Performance: bind mount имеет zero overhead, тот же FS, но операции метаданных могут быть медленнее из-за overlayfs над. 8) Безопасность: контейнер видит и может прочитать всё, что в /data на host -- если там sensitive data, нужно ro и тщательно контролировать что туда монтируется.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Зачем использовать UUID вместо device-path в /etc/fstab?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5