Storage drivers: overlay2, btrfs, zfs, vfs
Под капотом каждой команды docker run есть конкретная реализация union filesystem. На Linux это overlayfs (через storage driver overlay2), на macOS Docker Desktop крутит VM и тоже использует overlay2 внутри VM, на CI-runner’ах часто видим overlay2 или специальные drivers под btrfs/zfs если кто-то так настроил. Этот выбор влияет на производительность: время старта контейнера, скорость copy-up, износ диска.
В этом уроке: что такое storage driver, как узнать какой используется, что выбрать в каких случаях.
ext4 vs xfs vs btrfs vs zfs — когда какую
Концепция: storage driver
Docker отделяет логику слоёв от физического хранилища через абстракцию storage driver. Driver отвечает за:
- Распаковку tar-blob’ов в директории
- Создание union mount (overlay/btrfs subvolume/zfs filesystem)
- Управление copy-up при записи
- Очистку при
docker rm
Все drivers реализуют один интерфейс, но разными низкоуровневыми механизмами.
Список основных drivers:
| Driver | Тип FS под ним | Production-ready | Особенности |
|---|---|---|---|
overlay2 | ext4, xfs (ftype=1) | Да | Default на Linux. Самый быстрый, наиболее проверенный. |
btrfs | btrfs | Да | Использует субволумы и снапшоты btrfs. Хорош для копий, но медленнее на запись. |
zfs | zfs | Да | Аналогично btrfs, но через ZFS. Требует ZFS на хосте, не везде доступен. |
vfs | любая | Нет (testing only) | Без union — просто копирует все слои. Очень медленно, много диска. |
aufs | любая | Deprecated | Старый default до overlay2. Не используется в современных ядрах. |
devicemapper | block device | Deprecated | Тонко-provisioned LVM. Был popular на RHEL/CentOS, теперь deprecated. |
fuse-overlayfs | любая | Да (rootless) | Userspace-реализация для rootless Docker / Podman, медленнее. |
Как узнать какой driver используется
$ docker info | grep -A 5 "Storage Driver"
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Using metacopy: false
Native Overlay Diff: true
userxattr: false
Что важно:
- Storage Driver — имя драйвера.
- Backing Filesystem — на какой fs лежит
/var/lib/docker(extfs = ext4, xfs, btrfs). - Supports d_type — поддержка типа файла в dirent (нужна overlayfs). На xfs нужно создавать с
mkfs.xfs -n ftype=1, иначе overlayfs не запустится. - Native Overlay Diff — оптимизация в современных ядрах.
На Docker Desktop / OrbStack значение будет тот же overlay2, но это говорит о состоянии Linux VM, не хоста (macOS/Windows). Из самого macOS нельзя «видеть» эту fs напрямую.
overlay2: default на Linux
overlay2 это рекомендованный driver для большинства случаев. Использует ядерный overlayfs, который мы разбирали в уроке 01.
Преимущества:
- Очень быстрый старт контейнера (миллисекунды)
- Эффективный layer sharing (один read-only слой на хосте независимо от количества контейнеров)
- Низкая нагрузка на CPU (всё в ядре)
- Хорошая поддержка inotify, fsync, всех POSIX-операций
Ограничения:
- Файлы, изменённые в верхнем слое, копируются целиком (copy-up) — больно для больших файлов
- Не поддерживает hardlinks между слоями (минорная проблема для редких сценариев)
rename(2)между слоями требует copy-up
Требования к backing filesystem:
- ext4 работает из коробки.
- xfs требует
ftype=1(создаётся при mkfs или черезxfs_repair -t). - btrfs, zfs — overlay2 поверх них работает, но плохая идея (двойная union).
Если ты на сервере с RHEL 7 / CentOS 7 — там был storage driver devicemapper по умолчанию (RHEL 8+ перешёл на overlay2). Если видишь его — нужно мигрировать: docker daemon просто использует разный backend, переключение требует docker stop + изменения /etc/docker/daemon.json + удаления /var/lib/docker/devicemapper.
btrfs и zfs: для специальных случаев
btrfs и zfs — copy-on-write filesystems на уровне самой файловой системы. Docker может использовать их встроенные механизмы (subvolumes для btrfs, datasets для zfs) вместо overlayfs.
Преимущества:
- Instant clone при создании контейнера (snapshot subvolume — практически бесплатная операция)
- Compression на уровне fs (zstd, lz4 — можно сжать слои на лету)
- Checksumming — встроенная проверка целостности
- Snapshots / send-receive — удобно для backup/restore слоёв
Недостатки:
- btrfs не всегда стабилен в кучных-write workload (исторические репутационные проблемы — улучшилось в последние годы)
- zfs не входит в mainline kernel (лицензия CDDL), нужно ставить отдельно (zfsutils-linux)
- Производительность на write зависит от настроек и дополнительной нагрузки на CPU
Где использовать:
- btrfs: домашняя dev-машина с btrfs, желание иметь snapshots контейнеров
- zfs: NAS / storage сервер, на котором уже есть zfs pool, и Docker должен жить рядом
- На обычном production-сервере — overlay2 + ext4/xfs, никаких сюрпризов
vfs: только для тестов
vfs (Virtual File System) — это не union filesystem. Он просто копирует все слои в отдельные директории при создании контейнера.
/var/lib/docker/vfs/dir/
├── layer-1/ # все файлы слоя 1
├── layer-2/ # все файлы слоёв 1 + 2 (дублирование!)
├── layer-3/ # все файлы слоёв 1 + 2 + 3
└── container-A/ # все файлы образа + контейнер
Запуск контейнера = cp -r всего образа в новую директорию. На образе 500MB и 10 контейнерах — 5GB на диске. Старт контейнера = секунды/десятки секунд вместо миллисекунд.
Почему vfs всё-таки существует? Потому что overlay2 не работает в некоторых nested-docker сценариях (Docker внутри Docker без правильного volume-mount для /var/lib/docker), и vfs — fallback, который работает «везде». Использовать его в продакшене НЕЛЬЗЯ.
Если на твоей машине storage driver vfs — это либо мисконфигурация, либо ты внутри nested-docker без правильного volume. На macOS Docker Desktop в редких случаях падает в vfs если что-то сломалось — лечится reset to factory defaults.
Где живут слои
На Linux с overlay2:
$ sudo ls /var/lib/docker/overlay2/
a1b2c3d4... # layer A
b2c3d4e5... # layer B
c3d4e5f6... # layer C
...
$ sudo ls /var/lib/docker/overlay2/a1b2c3d4.../
diff/ # фактическое содержимое слоя
lower # текстовый файл с цепочкой нижних слоёв
link # короткий ID для mount-параметров
work/ # служебная директория overlayfs
На macOS с Docker Desktop эти директории внутри Linux VM по пути ~/Library/Containers/com.docker.docker/Data/vms/0/data/Docker.raw — это disk image VM. Доступ только через саму VM.
На OrbStack аналогично — внутри Linux VM. OrbStack экономит место за счёт sparse files (disk image растёт по мере использования, в отличие от Docker Desktop где предварительно аллоцируется).
На Windows + WSL2 — внутри WSL2-дистрибутива (docker-desktop-data).
# Размер слоёв на Linux
sudo du -sh /var/lib/docker/overlay2 | head -1
# 12G /var/lib/docker/overlay2
# Размер всех Docker-данных
sudo du -sh /var/lib/docker
# 18G /var/lib/docker
Почему на mac/win всё внутри VM
Контейнеры это Linux-технология. Технически: namespaces, cgroups, overlayfs, seccomp — это ядерные фичи Linux. На macOS и Windows нет этих ядерных API (у Windows есть свои контейнеры через Hyper-V Containers, но это другая технология).
Docker Desktop и OrbStack крутят минимальную Linux VM (на macOS это VirtualBox/QEMU/Apple Virtualization Framework — зависит от версии). Внутри VM работает Docker daemon, который и использует overlay2.
Файлы хоста (bind mounts) пробрасываются в VM через специальные shared filesystem-протоколы:
- Docker Desktop: gRPC FUSE (новый) или osxfs (старый, медленный)
- OrbStack: virtio-fs (нативный, быстрый)
- Rancher Desktop: 9P или sshfs
Это объясняет, почему на macOS bind-mount часто заметно медленнее, чем на Linux: данные идут через слой между VM и хостом.
Попробуй сам
Проверь конфигурацию своего Docker:
docker info | grep -A 10 "Storage Driver"
# Размеры на диске
docker system df
# Подробная информация
docker system df --verbose
# Список слоёв с их id (на Linux)
# sudo ls /var/lib/docker/overlay2/ | head -10
# Проверим, что overlayfs смонтирован для активного контейнера
docker run -d --name probe alpine:3.21 sleep 3600
docker inspect probe --format '{{.GraphDriver}}'
# {Data:map[LowerDir:/var/lib/docker/overlay2/.../diff:...
# MergedDir:/var/lib/docker/overlay2/.../merged
# UpperDir:/var/lib/docker/overlay2/.../diff
# WorkDir:/var/lib/docker/overlay2/.../work]
# Name:overlay2}
# Cleanup
docker rm -f probe
Если хочешь поэкспериментировать с другим driver — это требует изменения /etc/docker/daemon.json и пересоздания всех образов/контейнеров (нельзя на лету переключить). Не делай на dev-машине, где есть рабочие образы.