ZFS на Linux для серверів: пул, снапшоти, дедуплікація і ротація через systemd

ZFS на Linux для серверів: пул, снапшоти, дедуплікація і ротація через systemd
Категорії: Системне адміністрування

Якщо ви будуєте сервер Linux під дані або бекапи, OpenZFS — майже мрія: перевірки цілісності, миттєві снапшоти, стиснення й навіть дедуплікація. Нижче — мій дружній, але технічно точний гайд, де ми створимо пул, ввімкнемо потрібні властивості, налаштуємо снапшоти й зробимо автоматичну ротацію через systemd timers. Поїхали! 🚀

Що знадобиться і важливі застереження

Потрібен сучасний дистрибутив з підтримкою OpenZFS (Ubuntu/Debian/RHEL-похідні). Для продакшену бажано мати ECC-пам'ять, але це не жорстка вимога. Пам’ятайте: дедуплікація ZFS дуже вимоглива до RAM, увімкніть її тільки за підтвердженої вигоди (типово для VM-образів чи шаблонів контейнерів). І, так — резервне копіювання системи ніхто не скасовував.

Встановлення ZFS

Ubuntu / Debian

sudo apt update
# Ubuntu
sudo apt install -y zfsutils-linux
# Debian (за потреби ядро/модуль DKMS)
sudo apt install -y zfs-dkms zfsutils-linux
# Перевірка
modprobe zfs
zpool version
zfs version

RHEL / Rocky / AlmaLinux (через репозиторій OpenZFS)

# Додайте офіційний репозиторій OpenZFS для вашої версії дистрибутива (див. інструкції OpenZFS)
# Приклад далі припускає, що репозиторій уже підключено:
sudo dnf install -y kernel-devel zfs
sudo modprobe zfs
zpool version

How-to: створюємо пул, датасети, снапшоти і ротацію

Підготовка дисків і стабільні ідентифікатори

Рекомендовано використовувати шляхи /dev/disk/by-id/ — вони стабільні навіть після перезавантаження.

ls -l /dev/disk/by-id | grep -E "(sdb|sdc)$"

Якщо диск новий або ви згодні стерти таблицю розділів:

sudo wipefs -a /dev/sdb
sudo wipefs -a /dev/sdc

Створення пулу ZFS із корисними властивостями

Приклад: дзеркальний пул tank з автотрімом, стисненням lz4, вимкненим atime й коректними xattr/ACL:

sudo zpool create \
  -o ashift=12 -o autotrim=on \
  -O compression=lz4 -O atime=off -O xattr=sa -O acltype=posixacl \
  tank mirror \
  /dev/disk/by-id/XXXXXXXX_sdb \
  /dev/disk/by-id/XXXXXXXX_sdc

# Перевірка стану
zpool status -v tank
zfs list -r tank

Параметр ashift=12 підходить для сучасних дисків із 4K-секторами. Змінити його після створення пулу не можна.

Датасети: гнучкі точки монтування

# Створимо датасет під дані застосунку
sudo zfs create -o mountpoint=/srv/data tank/data
# Для VM-образів — окремий датасет
sudo zfs create -o mountpoint=/srv/vmstore tank/vmstore

Дедуплікація (обережно!)

Увімкніть дедуплікацію лише там, де реально є дублікати блоків і достатньо RAM (орієнтир — гігабайти на кожен терабайт унікальних даних). Краще вмикати на конкретному датасеті, а не на всьому пулі:

# Ввімкнути дедуплікацію лише для VM-сховища
sudo zfs set dedup=on tank/vmstore
# Перевірити
zfs get dedup tank/vmstore

Снапшоти вручну

Снапшоти в ZFS миттєві й копіюють лише метадані. Створимо рекурсивний снапшот для всього піддерева:

# Ім'я включає час для сортування
sudo zfs snapshot -r tank/data@manual-$(date +%F-%H%M)
# Список снапшотів
zfs list -t snapshot -r tank/data
# Відкат до останнього снапшоту (обережно, це зітре новіші зміни)
sudo zfs rollback -r tank/data@manual-2026-07-22-1200

Автоматична ротація через systemd

Ми реалізуємо автоматизацію задач: щогодини/щодня/щотижня створюємо снапшоти і видаляємо старі понад ліміт. Це зручно, надійно та краще, ніж cron — адже cron та systemd timers у systemd мають опцію Persistent=true (надолужують виконання після простою).

Позначаємо датасети для автоснапів

sudo zfs set com.kernelka:autosnap=on tank/data
sudo zfs set com.kernelka:autosnap=on tank/vmstore

Скрипт ротації /usr/local/bin/zfs-snap-rotate

sudo tee /usr/local/bin/zfs-snap-rotate >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
CLASS="${1:-hourly}"  # hourly|daily|weekly
HOURLY_KEEP="${HOURLY_KEEP:-24}"
DAILY_KEEP="${DAILY_KEEP:-14}"
WEEKLY_KEEP="${WEEKLY_KEEP:-8}"
PREFIX="${PREFIX:-autosnap}"

keep() {
  case "$CLASS" in
    hourly) echo "$HOURLY_KEEP";;
    daily)  echo "$DAILY_KEEP";;
    weekly) echo "$WEEKLY_KEEP";;
    *) echo "Use: hourly|daily|weekly" >&2; exit 2;;
  esac
}

datasets=$(zfs get -H -o name,value com.kernelka:autosnap 2>/dev/null | awk '$2=="on"{print $1}')
[ -n "$datasets" ] || { echo "No datasets tagged for autosnap"; exit 0; }

 ts=$(date +%F-%H%M)
for ds in $datasets; do
  zfs snapshot -r "${ds}@${PREFIX}-${CLASS}-${ts}"
  snaps=$(zfs list -H -t snapshot -o name -S creation | grep "^${ds}@${PREFIX}-${CLASS}-" || true)
  cnt=0
  while read -r snap; do
    [ -n "$snap" ] || continue
    cnt=$((cnt+1))
    if [ $cnt -gt "$(keep)" ]; then
      zfs destroy -r "$snap"
    fi
  done <<< "$snaps"
Done
done

echo "Done $CLASS snapshots: $(date -Is)"
EOF
sudo chmod +x /usr/local/bin/zfs-snap-rotate

Сервіс і три таймери systemd

# Сервіс-шаблон
sudo tee /etc/systemd/system/zfs-snap@.service >/dev/null <<'EOF'
[Unit]
Description=ZFS autosnap (%i)
Requires=zfs.target
After=zfs.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/zfs-snap-rotate %i

[Install]
WantedBy=multi-user.target
EOF

# Таймери
sudo tee /etc/systemd/system/zfs-snap@hourly.timer >/dev/null <<'EOF'
[Unit]
Description=ZFS autosnap hourly

[Timer]
OnCalendar=hourly
Persistent=true
Unit=zfs-snap@hourly.service

[Install]
WantedBy=timers.target
EOF

sudo tee /etc/systemd/system/zfs-snap@daily.timer >/dev/null <<'EOF'
[Unit]
Description=ZFS autosnap daily

[Timer]
OnCalendar=daily
Persistent=true
Unit=zfs-snap@daily.service

[Install]
WantedBy=timers.target
EOF

sudo tee /etc/systemd/system/zfs-snap@weekly.timer >/dev/null <<'EOF'
[Unit]
Description=ZFS autosnap weekly

[Timer]
OnCalendar=Sun 03:00
Persistent=true
Unit=zfs-snap@weekly.service

[Install]
WantedBy=timers.target
EOF

# Активуємо
sudo systemctl daemon-reload
sudo systemctl enable --now zfs-snap@hourly.timer zfs-snap@daily.timer zfs-snap@weekly.timer

# Перевірка
systemctl list-timers 'zfs-snap@*.timer'
journalctl -u zfs-snap@hourly.service --since today -e

Альтернативні способи

  • Sanoid/Syncoid: готовий інструмент політик снапшотів і реплікацій. На Debian/Ubuntu: sanoïd у репозиторіях. Мінімум команд — максимум контролю, зручно для сервер Linux у продакшені.
  • zrepl: сучасна реплікація через потоки з YAML-конфігами — чудово для офсайт-бекапів.
# Приклад швидкого старту Sanoid (Debian/Ubuntu)
sudo apt install -y sanoid
sudo cp /usr/share/sanoid/sanoid.conf.example /etc/sanoid/sanoid.conf
# Відредагуйте політики для tank/data, потім:
sudo systemctl enable --now sanoid.timer

GUI-спосіб (якщо хочеться «віконець»)

Для повноцінного графічного керування ZFS найзручніше використати TrueNAS SCALE — це окрема Linux-система з OpenZFS і веб-інтерфейсом для пулів, снапшотів і реплікацій. На існуючому дистрибутиві можна встановити Cockpit, щоб мати веб-доступ до логів і сервісів (у т.ч. наших таймерів), а сам ZFS адмініструвати з терміналу Linux — це компроміс між зручністю і контролем.

# Cockpit для огляду системи і юнітів systemd
sudo apt install -y cockpit || sudo dnf install -y cockpit
sudo systemctl enable --now cockpit.socket
# Веб: https://<ip-сервера>:9090

FAQ

Чи вмикати дедуплікацію скрізь?

Ні. Увімкніть лише там, де вигода очевидна (VM-образи, шаблони). Дедуп потребує багато RAM, а вигода на мішаних даних невелика. Спочатку покладіться на compression=lz4.

Яке значення ashift обрати?

Для сучасних HDD/SSD зазвичай ashift=12 (4K). Змінити пізніше не можна, тож краще одразу правильно.

Як замінити диск у mirror?

zpool status -v tank
# Додаємо новий диск і замінюємо старий
a) sudo zpool replace tank <old-disk-by-id> <new-disk-by-id>
# Після resilver перевіряємо стан
zpool status -v tank

Як розширити пул?

Додайте новий vdev (наприклад, ще один mirror) або послідовно замініть диски на більші у vdev з подальшим розширенням. Просте додавання одного диска до mirror неможливе — це інша топологія.

Як робити офсайт-бекапи?

Ставте реплікацію: zfs send | zfs receive, або скористайтеся syncoid/zrepl. Це доповнює локальні снапшоти і зміцнює резервне копіювання системи.

Моніторинг стану?

zpool status -x      # проблеми пулів
zpool list           # ємність, фрагментація
zfs list -o space    # розподіл простору за датасетами
sudo zpool scrub tank # раз на місяць — корисно для перевірки цілісності

Пул не змонтувався після перезавантаження

Переконайтеся, що модуль zfs завантажується і служби ввімкнені. Перевірте імпорт:

sudo zpool import -aN
systemctl status zfs-import-cache.service zfs-mount.service

Помилка "unsupported feature" при імпорті

Імпортуйте лише для читання, збережіть дані, оновіть OpenZFS на цільовій системі:

sudo zpool import -o readonly=on tank

Порада від Kernelka

🧠 Давайте снапшотам чіткий префікс (autosnap-hourly-YYYY-MM-DD-HHMM), вмикайте автоснапи тільки на потрібних датасетах (через властивість com.kernelka:autosnap=on) і не поспішайте з дедуплікацією — спершу зберіть метрики: RAM, профіль даних, реальну економію від compression=lz4. Менше магії — більше контрольованого результату.

Підсумок

  • Встановили OpenZFS і створили пул із правильними опціями.
  • Організували дані у датасетах для зручного керування політиками.
  • Налаштували снапшоти та безпечний відкат змін.
  • Зробили автоматичну ротацію через systemd timers із прозорими лімітами.
  • Розглянули альтернативи (Sanoid/zrepl) та варіанти з GUI.
  • Не забуваємо про scrub і зовнішню реплікацію для справжньої надійності. 🙂

Прокоментувати

На сайті відображається лише твоє ім'я та коментар. Електронна пошта використовується виключно для зв'язку з тобою за потреби.