OverlayFS появился в ядре как способ собрать одну файловую систему из нескольких слоёв: нижние доступны только для чтения, верхний принимает изменения. Именно на этой идее держатся слои образов контейнеров, загрузка live-систем и схемы с неизменяемым корнем. Идея объясняет, почему изменения в контейнере исчезают вместе с ним и куда уходит место.
Три каталога и одна точка монтирования
Нужны нижний слой, верхний слой, рабочий каталог и точка сборки. Рабочий каталог обязан быть пустым и находиться на той же файловой системе, что и верхний: ядро использует его для атомарных операций при переименовании. Проверить поддержку просто: тип overlay должен быть в списке файловых систем ядра.
grep overlay /proc/filesystems
sudo mkdir -p /ov/{lower,upper,work,merged}
sudo mount -t overlay overlay -o lowerdir=/ov/lower,upperdir=/ov/upper,workdir=/ov/work /ov/merged
findmnt -no FSTYPE,OPTIONS /ov/merged
touch /ov/lower/hello.txt && echo ok > /ov/merged/new.txt
После монтирования hello.txt виден в точке сборки, а new.txt физически лежит в верхнем слое. Нижний слой при этом не изменился ни на байт, а вся раскладка занимает четыре каталога и удаляется одной командой. Ошибка «wrong fs type» обычно означает отключённый модуль ядра или вложенные каталоги.
Порядок нижних слоёв
Слоёв может быть много, они перечисляются через двоеточие. Порядок читается справа налево: правый — нижний, левый — ближний к верхнему и перекрывает остальные. Так устроен и образ контейнера: базовый слой внизу, приложение сверху.
sudo mount -t overlay overlay -o lowerdir=/layers/app:/layers/base,upperdir=/upper,workdir=/work /mnt/rootfs
ls -la /mnt/rootfs/etc/os-release
cat /mnt/rootfs/etc/os-release
stat -c '%i %n' /mnt/rootfs/etc/os-release
При совпадении файлов побеждает слой ближе к верху, а каталоги объединяются: файлы снизу видны, пока их не перекрыли или не удалили. Поэтому обновление пакета в контейнере добавляет файл наверх, а не переписывает образ.
Copy-up: почему правка файла занимает место
Любое изменение файла из нижнего слоя требует копирования наверх целиком: меняется один байт — копируется весь файл с правами и атрибутами. Поэтому контейнер с базой данных быстро раздувается.
stat -c '%i %s %n' /ov/lower/hello.txt
stat -c '%i %s %n' /ov/merged/hello.txt
echo change >> /ov/merged/hello.txt
stat -c '%i %s %n' /ov/merged/hello.txt
du -sh /ov/upper
До записи inode совпадает с нижним слоем, после записи он меняется, а du показывает прирост. Для больших редко меняющихся файлов это приемлемо; базы данных и журналы лучше выносить на отдельный том.
Whiteouts и непрозрачные каталоги
Удаление файла из нижнего слоя не может стереть его физически, поэтому ядро создаёт в верхнем слое специальный маркер — whiteout. Это символьное устройство с номерами 0:0, которое скрывает нижележащий объект. Каталог, полностью перекрывающий нижний, помечается расширенным атрибутом непрозрачности.
sudo rm /ov/merged/hello.txt
ls -la /ov/upper/
stat /ov/upper/hello.txt
sudo getfattr -d -m - /ov/upper
В выводе ls маркер выглядит как файл с типом c и размерами 0, 0. Атрибут trusted.overlay.opaque со значением y означает, что содержимое нижнего каталога больше не подмешивается. Создавать их вручную не нужно: это работа ядра.
Опции, которые стоит знать
Несколько параметров меняют расход места и совместимость. metacopy=on копирует наверх только метаданные, если изменились права или время, а данные остаются внизу. redirect_dir=on позволяет переименовывать каталоги из нижнего слоя, создавая вместо полной копии перенаправление. index=on включает индекс и заодно разрешает экспорт через NFS. xino=on выдаёт уникальные номера inode, что важно для некоторых сборщиков и систем индексации.
Опция volatile отключает синхронизацию при размонтировании: вариант для временных сред.
OverlayFS без root
Начиная с ядра 5.11 непривилегированный overlay разрешён внутри пользовательского пространства имён. Это удобно для сборок и тестов: не нужен root на хосте, а все изменения остаются в вашем каталоге.
unshare -Ur -m bash -c '
mkdir -p /tmp/o/{l,u,w,m}
mount -t overlay overlay -o lowerdir=/tmp/o/l,upperdir=/tmp/o/u,workdir=/tmp/o/w,userxattr /tmp/o/m
touch /tmp/o/m/probe && ls /tmp/o/m
'
Внутри вы выглядите как root, но только для своих процессов. Если монтирование не удаётся, проверьте kernel.unprivileged_userns_clone: некоторые дистрибутивы запрещают такие пространства имён.
Контейнеры: где здесь слои
Docker с драйвером overlay2 раскладывает образ на каталоги diff, а точку сборки показывает в инспекции. Это помогает понять, сколько места съел контейнер и что изменилось внутри.
docker info --format '{{.Driver}}'
docker image inspect --format '{{json .GraphDriver.Data}}' nginx:alpine | jq
docker history --no-trunc nginx:alpine | head -20
sudo du -sh /var/lib/docker/overlay2/* | sort -h | tail
Верхний слой контейнера живёт, пока контейнер существует: удаление контейнера выбрасывает и его. Именно поэтому данные монтируют томом, а не хранят в файловой системе контейнера. Rootless Podman в ряде случаев использует fuse-overlayfs, если ядро или настройки не позволяют нативный overlay.
Ограничения и диагностика
Верхний и рабочий каталоги должны поддерживать расширенные атрибуты и корректный тип записи каталога; NFS в этой роли не поддерживается. Наблюдение за изменениями через inotify для нижних слоёв не работает: события появятся только после copy-up. Права проверяются на объединённой точке, а не на исходных каталогах.
findmnt -t overlay
cat /proc/self/mountinfo | grep overlay
dmesg | tail -20
sudo umount /ov/merged
Если размонтирование говорит «target is busy», найдите держателей через lsof +D или fuser -vm. Верхний слой после этого можно удалить: нижний останется нетронутым.
Чек-лист перед монтированием
- Нижний слой подготовлен и не будет меняться.
- Верхний и рабочий каталоги на одной файловой системе, рабочий пуст.
- Для непривилегированной сборки доступны пользовательские пространства имён.
- Изменяемые данные вынесены на отдельный том.
Итог
OverlayFS — простая идея с важными деталями: порядок слоёв, обязательный пустой workdir, copy-up целыми файлами и скрытые маркеры удаления. Эти четыре вещи объясняют почти всё поведение контейнеров и live-систем. Для экспериментов достаточно четырёх каталогов и команды mount; в продакшене выносите изменяемые данные за пределы верхнего слоя.