ZeroFS — это журналируемая (log-structured) файловая система для S3. Она предоставляет S3-совместимые бакеты как POSIX-файловые системы по протоколам NFS и 9P, а также как сырые блочные устройства через NBD. Все три сервера работают в едином пользовательском процессе. Перед загрузкой данные сжимаются и шифруются.
ZeroFS отличается от других проектов категории «файловая система поверх S3» следующим:
-
Соответствует стандарту POSIX
-
Обеспечивает пропускную способность, близкую к прямому S3, при работе с большими файлами, и масштабируется до рабочих нагрузок из сотен миллионов мелких файлов
-
Обрабатывает десятки тысяч запросов в секунду
-
Не требует внешней базы данных — всё хранится в S3
-
Хорошо протестирована: в CI запускаются pjdfstest, xfstests, сборка ядра Linux, stress-ng, ZFS, Jepsen local-fs, Jepsen HA и другие тесты
Файловый доступ |
Серверы NFS и 9P. При наличии точного пакета используется родной клиент ядра; в остальных случаях — |
Блочный доступ |
NBD-устройства с поддержкой TRIM. Ответы FLUSH и FUA возвращаются только после того, как данные надёжно сохранены. |
Шифрование |
Экстенты (extents) шифруются алгоритмом XChaCha20-Poly1305. Ключ данных оборачивается через Argon2id. |
Сжатие |
zstd или lz4, применяется до шифрования. Кодек можно менять в любой момент без миграции данных. |
Кэширование |
Двухуровневый кэш: оперативная память и диск. |
Высокая доступность |
Необязательная схема лидер/резерв поверх одного бакета с автоматическим переключением при сбое. |
Веб-интерфейс |
Файловый менеджер, панель мониторинга, терминал в браузере. |
Бэкенды |
Amazon S3, Google Cloud Storage, Azure Blob, любое S3-совместимое хранилище, локальный диск. |
Быстрый старт
apt / dnf
# Debian / Ubuntu
curl -fsSL https://pkgs.zerofs.net/zerofs.gpg | sudo gpg --dearmor -o /usr/share/keyrings/zerofs.gpg
echo "deb [signed-by=/usr/share/keyrings/zerofs.gpg] https://pkgs.zerofs.net/deb stable main" | sudo tee /etc/apt/sources.list.d/zerofs.list
sudo apt update && sudo apt install zerofs
# Fedora / RHEL / Rocky
curl -fsSL https://pkgs.zerofs.net/zerofs.repo | sudo tee /etc/yum.repos.d/zerofs.repo
sudo dnf install zerofs
Пакеты также устанавливают systemd-сервис (zerofs.service, по умолчанию отключён) и шаблон конфигурации в /etc/zerofs/. Задайте ZEROFS_PASSWORD и учётные данные в /etc/zerofs/zerofs.env, укажите URL в секции [storage] файла /etc/zerofs/config.toml, затем выполните sudo systemctl enable --now zerofs. Подробности — в packaging/README.md.
Скрипт установки
curl -sSfL https://sh.zerofs.net | sh
# Указать конкретную версию и установить без root
curl -sSfL https://sh.zerofs.net | VERSION=v1.2.5 INSTALL_DIR=$HOME/.local/bin sh
Скрипт скачивает архив с релизом, проверяет опубликованную контрольную сумму SHA-256 и устанавливает готовый бинарный файл. Поддерживаемые платформы: Linux (amd64, arm64), macOS (x86_64, aarch64), FreeBSD (amd64). Полная матрица платформ — в руководстве по быстрому старту.
Docker
docker pull ghcr.io/barre/zerofs:latest
# Сгенерировать стартовый конфиг на хосте ("-" выводит в stdout)
docker run --rm ghcr.io/barre/zerofs:latest init - > zerofs.toml
$EDITOR zerofs.toml
docker run --rm -v "$PWD/zerofs.toml:/zerofs.toml" \
ghcr.io/barre/zerofs:latest run -c /zerofs.toml
Контейнер запускается от имени UID 1001. Примонтированная директория кэша должна быть доступна для записи пользователю UID 1001. Чтобы серверы были доступны с хоста, привяжите адреса к 0.0.0.0 и пробросьте порт для каждого включённого сервера: 2049 (NFS), 5564 (9P), 10809 (NBD).
Запуск
zerofs init # Создать zerofs.toml
$EDITOR zerofs.toml # Указать S3-учётные данные
zerofs run -c zerofs.toml
Родной клиент ядра Linux
Директория kernel/ содержит внешний (out-of-tree) VFS-модуль, который говорит на приватном диалекте 9P2000.L.Z поверх TCP или AF_UNIX без использования FUSE или Linux v9fs. Поддерживаются архитектуры x86-64 и little-endian arm64. Подробности — в руководстве по родному клиенту ядра.
Тестирование
-
pjdfstest: 8 662 POSIX-сценария (pjdfstest_nfs), запускаемых отдельно для каждого протокола: NFS, 9P, FUSE. Списки исключений по протоколам находятся в директории
.github/. -
xfstests: стандартный регрессионный набор для файловых систем, запускается поверх NFS, 9P и FUSE.
-
Сборка ядра: ядро Linux компилируется командой
make -j$(nproc)при монтировании через NFS, 9P и FUSE. -
stress-ng: стрессовые нагрузки на файловые операции запускаются параллельно против живых точек монтирования.
-
ZFS: пул ZFS создаётся поверх блочных устройств ZeroFS; затем выполняются распаковка исходников ядра и команда scrub.
-
Jepsen local-fs: случайные истории операций против монтирования по 9P, проверяемые на соответствие эталонной модели (local-fs). В режиме сбоя сервер убивается в середине прогона, после чего проверяется, что восстановление соответствует последнему fsync.
-
Jepsen HA: пара лидер/резерв поверх MinIO под управлением «немезиды», которая убивает или приостанавливает узлы; ни одна подтверждённая запись не должна быть потеряна, воскрешена или повреждена в ходе проверяемых переключений при сбое. Средство проверки модели local-fs также запускается с инжектируемыми переключениями.
-
Детерминированное моделирование: пути обработки данных, пространства имён, сборщика мусора сегментов и уплотнения выполняются в симулированной среде (
zerofs/tests/dst): виртуальное время, задержки хранилища с заданным зерном и переходные сбои, падения в произвольных точках ожидания или узких окнах точек останова (failpoint). Восстановление проверяется на соответствие байтовым и пространственным эталонным моделям, выполняется полное сканирование согласованности метаданных, сверка учёта сегментов и авторитетное сканирование следа данных. Одно зерно — одно точное расписание, поэтому сбой воспроизводится идентично.
Веб-интерфейс
[servers.webui]
addresses = ["127.0.0.1:8080"]
uid = 1000 # Идентификатор POSIX для файловых операций из браузера; обязателен
gid = 1000 # Обязателен
Файловый менеджер общается по протоколу 9P через WebSocket. Поддерживается перетаскивание файлов и папок целиком для загрузки.
Панель мониторинга передаёт статистику в реальном времени через gRPC-web, а также включает трассировщик файловых операций.
Терминал загружает Linux-VM через v86, при этом файловая система монтируется в /mnt по тому же 9P WebSocket. Гостевая машина не имеет сетевого устройства.
Архитектура
Серверы NFS, 9P, NBD и веб-интерфейс совместно используют единый уровень файловой системы. Содержимое файлов разбивается на экстенты по 32 КиБ; каждый экстент сжимается, шифруется и упаковывается как фрейм в неизменяемые объекты-сегменты (до 256 МиБ). Метаданные (инода, записи директорий и 32-байтовый указатель на каждый экстент) хранятся в базе данных с LSM-деревом (LSM-tree) в том же объектном хранилище. Подробности — в документации по архитектуре.
graph TB
subgraph "Client Layer"
NFS[NFS Client]
P9[9P Client]
NBD[NBD Client]
WEB[Web Browser]
end
subgraph "ZeroFS Core"
NFSD[NFS Server]
P9D[9P Server]
NBDD[NBD Server]
WEBUI[Web UI]
VFS[Virtual Filesystem]
SEG[Segment Store<br/>file data as compressed, encrypted frames]
SLATE[LSM tree<br/>metadata + 32-byte extent pointers]
CACHE[Local Cache]
NFSD --> VFS
P9D --> VFS
NBDD --> VFS
WEBUI --> VFS
VFS --> SEG
VFS --> SLATE
SEG --> CACHE
SLATE --> CACHE
end
subgraph "Storage Backend"
SEGOBJ[Immutable segment objects<br/>segments/shard/epoch/counter]
SSTS[Metadata SSTs + manifest]
S3[S3 Object Store]
CACHE --> SEGOBJ
CACHE --> SSTS
SEGOBJ --> S3
SSTS --> S3
end
NFS --> NFSD
P9 --> P9D
NBD --> NBDD
WEB --> WEBUI
Высокая доступность
Секция [replication] запускает пару лидер/резерв, использующих один бакет, — при этом не нужно создавать вторую долговечную копию данных. Пока узлы соединены (Connected), резервный узел получает каждую мутацию до того, как лидер отправит ответ, включая незафлашенные фреймы файлов. Если репликация недоступна, лидер продолжает работу в автономном режиме (Solo mode) с окном долговечности автономного режима. Захват роли лидера (takeover) использует поэтапное фиксированное выдвижение с выделением эпохи записи (writer-epoch fencing) до того, как новый лидер начинает обслуживать запросы. Схема работы, гарантии, конфигурация и клиенты с несколькими целевыми адресами описаны в разделе высокой доступности.
Конфигурация
Файл конфигурации в формате TOML с подстановкой переменных окружения вида $VAR/${VAR}; все упомянутые переменные должны быть заданы. Секции [cache], [storage] и [servers] обязательны. Полный справочник параметров — в руководстве по конфигурации.
[cache]
dir = "${HOME}/.cache/zerofs"
disk_size_gb = 10.0
memory_size_gb = 1.0 # Необязательно, по умолчанию 0.25
[storage]
url = "s3://my-bucket/zerofs-data"
encryption_password = "${ZEROFS_PASSWORD}"
[filesystem]
max_size_gb = 100.0 # Необязательно; записи сверх квоты возвращают ENOSPC (по умолчанию 16 ЭиБ)
compression = "zstd-3" # Необязательно: "zstd-{1-22}" (по умолчанию "zstd-3") или "lz4"
[servers.nfs]
addresses = ["127.0.0.1:2049"]
[servers.ninep]
addresses = ["127.0.0.1:5564"]
unix_socket = "/tmp/zerofs.9p.sock" # Необязательно
[servers.nbd]
addresses = ["127.0.0.1:10809"]
unix_socket = "/tmp/zerofs.nbd.sock" # Необязательно
[servers.rpc]
addresses = ["127.0.0.1:7000"] # Нужен для zerofs checkpoint, flush, monitor, fatrace, otrace
[aws]
access_key_id = "${AWS_ACCESS_KEY_ID}"
secret_access_key = "${AWS_SECRET_ACCESS_KEY}"
# endpoint = "https://s3.us-east-1.amazonaws.com" # Для S3-совместимых сервисов
# default_region = "us-east-1"
# allow_http = "true" # Для не-HTTPS эндпоинтов (например, MinIO)
# conditional_put = "redis://localhost:6379" # Для хранилищ без поддержки conditional-put
Бэкенды
url = "s3://bucket/path" # + учётные данные [aws]
url = "azure://container/path" # + [azure] storage_account_name / storage_account_key
url = "gs://bucket/path" # + [gcp] service_account, или ADC из окружения на GCP VM/GKE
url = "file:///path/to/storage" # Локальный диск; учётные данные не нужны
Дополнительные схемы (s3a://, abfs://, host-routed https://, memory://) описаны в руководстве по конфигурации.
ZeroFS требует условных записей (put-if-not-exists, атомарная запись при отсутствии объекта) для механизма fencing. AWS S3 поддерживает это нативно; для хранилищ без такой поддержки укажите URL Redis в параметре conditional_put.
Необязательный параметр storage_class в секции [storage] передаётся бэкенду как есть (S3 x-amz-storage-class, GCS x-goog-storage-class, Azure x-ms-access-tier). Используйте горячий, стандартный класс доступа: архивные уровни делают том непригодным для использования, а уровни нечастого доступа (infrequent-access) тарифицируют каждое чтение — а ZeroFS читает постоянно, что обычно оборачивается значительными расходами.
Монтирование
При работе по 9P fsync возвращает управление только после того, как данные достигли стабильного хранилища; семантика NFS COMMIT позволяет fsync возвращаться раньше. Если вам важна гарантия долговечности fsync, используйте монтирование по 9P.
Родной клиент ядра
sudo mount -t zerofs 127.0.0.1:5564 /mnt/zerofs
# Unix-сокет
sudo mount -t zerofs /tmp/zerofs.9p.sock /mnt/zerofs
Пакеты для точных версий ядра, вопросы совместимости и параметры монтирования описаны в разделе родной клиент ядра.
zerofs mount (запасной вариант через FUSE)
zerofs mount 127.0.0.1:5564 /mnt/zerofs # TCP
zerofs mount /tmp/zerofs.9p.sock /mnt/zerofs # Unix-сокет
Стандартный Linux v9fs
mount -t 9p -o trans=tcp,port=5564,version=9p2000.L,cache=mmap,access=user 127.0.0.1 /mnt/9p
# Unix-сокет
mount -t 9p -o trans=unix,version=9p2000.L,cache=mmap,access=user /tmp/zerofs.9p.sock /mnt/9p
NFS
ZeroFS сообщает клиентам NFS, что записи устойчивы, пока они ещё буферизованы; протестированные клиенты (macOS, Linux) не отправляют COMMIT при fsync. Там, где важна долговечность fsync, используйте монтирование по 9P.
# macOS
mount -t nfs -o async,nolocks,rsize=1048576,wsize=1048576,tcp,port=2049,mountport=2049,hard 127.0.0.1:/ mnt
# Linux
mount -t nfs -o async,nolock,rsize=1048576,wsize=1048576,tcp,port=2049,mountport=2049,hard 127.0.0.1:/ /mnt
Параметры монтирования, постоянные точки монтирования, Windows — в разделе доступ по NFS.
NBD-блочные устройства
Файлы устройств в директории .nbd подключаются как сырые блочные устройства:
# Создать устройства через любую точку монтирования файловой системы
mkdir -p /mnt/zerofs/.nbd
truncate -s 1G /mnt/zerofs/.nbd/device1
# Подключить (рекомендуется: -persist, -timeout 600 для задержек S3, -connections 4)
nbd-client 127.0.0.1 10809 /dev/nbd0 -N device1 -persist -timeout 600 -connections 4
# Unix-сокет
nbd-client -unix /tmp/zerofs.nbd.sock /dev/nbd1 -N device1 -persist -timeout 600 -connections 4
mkfs.ext4 /dev/nbd0
# или
zpool create mypool /dev/nbd0
Хэндшейк анонсирует поддержку FLUSH, FUA и множественных соединений. Ответы FLUSH и FUA возвращаются только после того, как данные надёжно сохранены, а FLUSH на любом соединении распространяется на все соединения — благодаря этому барьеры записи работают корректно для пулов ZFS и баз данных. Подробности — в разделе NBD-устройства.
Новые файлы устройств подхватываются в режиме реального времени. Размер фиксируется при создании: чтобы изменить его, нужно отключить устройство, удалить файл и создать заново. Чтобы удалить устройство, сначала отключите клиент (nbd-client -d /dev/nbd0), затем выполните rm для файла.
TRIM
fstrim /mnt/block # Вручную
mount -o discard /dev/nbd0 /mnt/block # Автоматически (файловые системы)
zpool set autotrim=on mypool # Автоматически (ZFS)
TRIM удаляет указатели на экстенты и уменьшает счётчик живых байтов каждого сегмента; сборщик мусора, запускаемый каждые 60 секунд, удаляет мёртвые сегменты и переупаковывает фрагментированные, освобождая место в S3.
Ограничения
-
Максимальный размер файла: 16 ЭиБ
-
Максимальный размер файловой системы: 16 ЭиБ
-
Файлов за время жизни файловой системы: 2^64
-
Жёстких ссылок (hardlinks) на файл: 2^32
Это форматные ограничения (64-битные поля инод и размеров, экстенты по 32 КиБ), а не проверенные на практике; на деле первыми вступают в силу ограничения провайдера и стоимость хранения. Подробности — в описании архитектуры.
Лицензирование
Двойная лицензия: GNU AGPL v3 (полная функциональность, для открытого ПО) и коммерческая лицензия.