Когда просто бэкап — это скучно, ZFS, Asterisk и немного магии

Когда просто бэкап — это скучно, ZFS, Asterisk и немного магии

Однажды на работе мне начальник поставил задачу создать резервную копию сервера телефонии Asterisk, чтобы в случае отказа сервер телефонии можно было поднять на виртуалке. Проблема оказалась, что Asterisk находится физически на сервере в Екатеринбурге и установлен на FreeBSD 13.1, а я нахожусь в Тюмени. Т. е. прийти в серверную сделать холодный backup не получится. Я пообщался с коллегами, они сказали: «Никакой проблемы нет, используй ZFS snapshots для резервного копирования».

Так я узнал о существовании файловой системы ZFS. Это файловая система, которая сама следит за целостностью данных, умеет экономить место и защищать файлы от потерь при сбоях. Её главная идея — надёжность и удобство в управлении большими объёмами данных.

Основные технологии ZFS:

  • Copy-on-Write (COW) — вместо перезаписи данных создаются новые копии, старые остаются нетронутыми.
  • Снапшоты (Snapshots) — быстрые «фотографии» состояния файловой системы в любой момент. 
  • Компрессия данных — автоматическое сжатие для экономии места.
  • Дедупликация — удаление повторяющихся данных для оптимизации хранения.
  • Интегрированное управление томами — управление дисками и пулами без внешних менеджеров типа LVM.
  • Шифрование для защиты данных.

Любые Ваши фантазии для работы с жесткими дисками уже реализованы на уровне файловой системы.

Вот хорошее обзорное видео про эту файловую систему «ZFS — Лучшая! Файловая? Система»

ZFS - Лучшая! Файловая? Система

Сначала для меня всё было покрыто магией. И было уйма вопросов. Начал разбираться, магии никакой нет.

  • Почему быстро создаётся снапшот?
    ZFS использует Copy-on-Write (COW), чтобы при изменении данных не перезаписывать их на месте, а записывать новую версию в новое место. Снапшот фиксирует указатели на блоки в момент времени — это мгновенная «точка сохранения», не дублирующая сами данные. Благодаря COW снапшоты не занимают лишнего места до изменений и мгновенно создаются.
  • А как происходит сжатие на уровне файловой системы?
    ZFS поддерживает прозрачное сжатие данных на уровне блочной записи. При записи данных ZFS пытается сжать каждый блок с использованием выбранного алгоритма (lz4, gzip и др.). Если сжатый блок меньше исходного — сохраняется сжатый вариант, иначе — оригинал. Это уменьшает объём хранения, повышает скорость ввода-вывода (меньше данных читается с диска) и экономит место без вмешательства приложений.
  • А как происходит дедупликация?
    Дедупликация в ZFS устраняет дублирующиеся блоки данных на уровне всей файловой системы. При записи нового блока ZFS вычисляет его хэш (обычно SHA-256) и проверяет, есть ли такой уже на диске. Если есть — сохраняется только ссылка, а не сам блок. Это экономит место, особенно при хранении похожих данных, но требует много оперативной памяти и ресурсов для хэш-таблиц.
    Хорошее видео «Моя лекция о COW-файловых системах (ZFS, BTRFS)» про то, что происходит у ZFS «под капотом».
Моя лекция о COW-файловых системах (ZFS, BTRFS)

Но ZFS далеко не идеальна:

  • Лицензия ZFS не позволяет добавлять модуль в ядро Linux напрямую. Необходимо немного потанцевать с «бубном».
  • Требуется 64-битная операционная система и минимум 2 ГБ оперативной памяти. И чем больше памяти, тем лучше. На моём сервере Asterisk в пике может легко «сожрать» до 30Gb, в обычном режиме около 13Gb хватает при объёме дискового пула в 1Tb.
  • Не рекомендуется использовать аппаратные RAID с ZFS. ZFS любит управлять дисками напрямую. Очень много «фишек» сжатие, шифрование будут недоступны.
  • ZFS не любит недостаток свободного места на диске. Решение: создание дополнительного dataset для резервирования пространства. Резервируется около 5% для быстрого освобождения дискового пространства.
  • Высокий порог вхождения. Во всех технологиях ZFS надо сидеть и разбираться.

Итак, как создать и передать снапшот всей файловой системы Asterisk на удалённый сервер.

# Создание снапшота
zfs snapshot zfspool@today
# Передача снапшота по SSH на удалённый сервер
zfs send zfspool/data@today | root@192.168.0.2 zfs receive -F -v zfspool

На удалённом сервере

# Проверка после передачи
zfs list -t snapshot
# Смонтировать
mkdir -p /mnt/zfsrmount
mount -t zfs -o ro zfspool@today /mnt/zfsmount

Ура! Мы осуществили резервное копирование сервера, все могут расходится. Но не всё так просто. Возникли две проблемы:

  • На удалённом сервере файловая система ext4, которая даже не подозревает о существовании ZFS.
  • Как автоматизировать создание и пересылку снапшотов?

Но нет ничего невозможного для человека с интеллектом.
Когда просто бэкап — это скучно, ZFS, Asterisk и немного магии

Как принять снапшот?

Что принять снапшот на удалённом сервере тоже должна быть ZFS, ext4 не подходит. Нет, впихнуть невпихуемое, конечно, можно. Только сил, ресурса и душевных мук это потребует неоправданно. Я пошёл более простым путём, создал виртуалку Debian 12 с ZFS. Но опять не всё так просто, в Linux ZFS как бы есть, но там какая-то дивная чехарда с лицензиями. Она не достаточно свободная, поэтому в официальную поставку Linux-дистрибутивов её не включают (кроме Ubuntu), и надо ставить вручную. Но есть хорошая инструкция «Debian Bookworm Root on ZFS» https://openzfs.github.io/openzfs-docs/Getting%20Started/Debian/Debian%20Bookworm%20Root%20on%20ZFS.html. Если вкратце, надо загрузится с LiveCD, разметить диск, установить загрузчик, скопировать операционку и провести её настройки из chroot.

Как автоматизировать пересылку снапшотов?

Всё, что может быть автоматизировано, должно быть автоматизировано. Есть программа Zrepl https://zrepl.github.io/ Она не только их создаёт и передаёт по расписанию, но и очищает старые.

На FreeBSD (asterisk) был создан следующий конфиг

global:
  logging:
    - type: "stdout"
      level:  "error"
      format: "human"
    - type: "syslog"
      level:  "warn"
      format: "logfmt"

jobs:
  - name: snapshots
    type: snap
    filesystems:
      '<': true
    snapshotting:
      type: cron
      prefix: zrepl_
      cron: "0 0 * * *"
    pruning:
      keep:
        - type: regex
          negate: true
          regex: '^zrepl_'
        - type: grid
          grid: 24x1h(keep=all) | 60x1d
          regex: '^zrepl_'

  - name: target_nas
    type: source
    serve:
      type: tcp
      listen: "192.168.0.1:8888"
      listen_freebind: true
      clients: {
        "10.72.3.110" : "aster-backup"
      }
    filesystems: {
      "<": true,
    }
    snapshotting:
      type: manual

На FreeBSD есть 2 задания:

  • snapshots: ежедневно в полночь по cron делает ZFS-снимки всех файловых систем с префиксом zrepl_, хранит часовые за последние 24 ч и по одному за каждый из предыдущих 60 дней, удаляя более старые снимки по сеточной политике.
  • target_nas: запускает TCP-сервер на 192.168.0.1:8888 для клиента 10.72.3.110 (aster-backup), выставляет все файловые системы и ждёт ручного создания снимков (auto-snapshots отключены).

А на Debian 12 (aster-backup)

global:
  logging:    
    - type: "stdout"
      level:  "error"
      format: "human"
    - type: syslog
      format: human
      level: "warn"

jobs:
- name: pull_aster
  type: pull
  connect:
     type: tcp
     address: "192.168.0.1:8888"

  root_fs: rpool/backups
  interval: 1h

  recv:
    placeholder:
      encryption: off
    properties:
      inherit:
        - mountpoint
        - canmount

  conflict_resolution:
    initial_replication: all

  pruning:
    keep_sender:
      - type: regex
        regex: '.*'

    keep_receiver:
      - type: regex
        negate: true
        regex: '^zrepl_'
      - type: grid
        grid: 1x1h(keep=all) | 12x1h | 60x1d | 3x30d
        regex: '^zrepl_'

Задание pull_aster: каждый час тянет снимки по TCP с 192.168.0.1:8888 в /rpool/backups, приём без шифрования, при конфликте забирает всё с источника, при очистке на отправителе хранит всё, на приёмнике удаляет всё, что не начинается с zrepl_, и для своих zrepl_-снимков держит 1×1ч, затем 12×1ч, 60×1д и 3×30д.

Почему используются задания pull, а не push? 

Из-за централизованного управления. Расписание бэкапов задаётся только на стороне «сервер-бэкапа» — не нужно настраивать cron на каждом клиенте и следить за тем, что они вовремя завершили push. Задания pull могут становиться в очередь.

Запускаем получение снапшота с удалённого сервера командой:

zrepl signal wakeup pull_aster

И ждём, у меня получение первого снапшота размером 500Gb при ширине канала 100Mb/s заняло около 10 часов. Следующие инкрементальные в разы быстрее, они объёмом 80-100Mb.

За основу этой схемы резервного копирования было взято видео «ZFS как архитектурное решение для резервного копирования хостинга».

ZFS как архитектурное решение для резервного копирования хостинга / Алексей Афошин (Timeweb)

Для zrepl есть ряд полезных команд.

#проверить статус zrepl
zrepl status
#проверить конфиг zrepl
zrepl configcheck

# перезапустить zrepl на FreeBSD
systemctl restart zrepl

#просмотр журнала на Debain
journalctl -u zrepl.service
journalctl -u zrepl.service -n 40

#наблюдать за файлом журнала
journalctl -fu zrepl

#просмотр списка снапшотов
zfs list -t snapshot

#удаление снапшота, если что-то пошло не так
zfs destroy -r rpool/backups/zfspool/var/log
#если снапшот не удаляется, надо определить кто его "держит" и освободить.
zfs holds rpool/backups/zfspool/var/log@zrepl_20250512_110640_000         zfs release zrepl_last_received_J_pull_aster rpool/backups/zfspool/var/log@zrepl_20250512_110640_000

#посмотреть свойства датасета
zfs get all rpool/backups

#удалить созданное дерево
zfs destroy -rf rpool/backups/zfspool

Когда просто бэкап — это скучно, ZFS, Asterisk и немного магии

Теперь автоматизация резервного копирования настроена, снапшоты стабильно передаются на удалённый сервер, хранятся по расписанию и очищаются без моего вмешательства. Но на этом работа не заканчивается. Важно не просто делать бэкапы, а проверять, можно ли из них восстановить рабочую систему. Без этой проверки вся схема — иллюзия безопасности. В следующей статье https://infoblog72.ru/blog/kak-vosstanovit-asterisk-iz-snapshota-poshagovaya-instrukcziya.html расскажу, как из снапшота развернуть полноценную копию сервера Asterisk на виртуальной машине.