ZFS інкрементальна передача: резервні копії без повторного копіювання

Було корисно?

Першого разу, коли ви спостерігаєте, як інкрементальна передача ZFS завершується за кілька секунд — після того як нічне повне резервне копіювання тривало годинами — виникає рідкісне відчуття в інфраструктурі:
щось одночасно елегантне й практичне. Знімки ZFS дають незмінні точки у часі, а zfs send/zfs receive можуть реплікувати
лише те, що змінилося між цими знімками. Вигода очевидна: швидші бекапи, менший трафік мережі і процес відновлення, що не починається з «вибачте, ми ще копіюємо».

Але інкрементальна реплікація — не магія. Це контракт: ваш ланцюжок знімків, властивості набору даних і стан приймача повинні збігатися. Коли вони не збігаються,
ZFS відмовиться вгадувати. Це добре з інженерної точки зору, і іноді поганий день для тих, хто вважав, що «інкрементально» означає «просто спрацює».

Що насправді означає «інкрементальна передача»

«Стрім передавання» ZFS — це серіалізоване представлення набору даних (або знімка), яке можна відновити на іншому пулі за допомогою zfs receive.
Повний (full) стрім передає весь знімок. Інкрементальний (incremental) передає лише блоки, що змінилися між двома знімками — «з A до B».

Інкрементальні відправлення не є дифами файлів по файлу. ZFS працює на рівні блоків. Якщо файл змінюється так, що перезаписуються блоки, ці блоки будуть передані.
Якщо нічого не змінилося — нічого не буде передано. Якщо файл був повністю перезаписаний (наприклад, образ диска VM з невеликою правкою, що спричиняє масивні шаблони перезапису),
інкрементально все одно можна передати багато даних. «Інкрементально» про змінені блоки, а не про змінені наміри.

Існує два основні інкрементальні режими, які ви будете використовувати:

  • Інкремент між знімками: zfs send -i pool/fs@old pool/fs@new передає зміни з @old до @new.
  • Інкрементальна реплікація (т.з. «базовий знімок»): zfs send -I pool/fs@base pool/fs@new може включати проміжні знімки у стрім.

Це -I (велика літера i) важливе, коли ви хочете, щоб приймач мав той самий набір знімків — не лише останній. У більшості операцій бекапу історія знімків — частина продукту.

Жарт №1: Інкрементальні бекапи схожі на дієту — якщо ви «згрішите», видаливши базовий знімок, математика перестає працювати, і ви миттєво це відчуєте.

Цікаві факти та контекст (те, що змінює ваші рішення)

  1. Знімки ZFS дешеві, бо це метадані, а не копії. Вартість проявляється пізніше, коли блоки розходяться і їх потрібно зберігати для старих знімків.
  2. Стріми відправлення детерміністичні для заданого стану набору даних, але не обов’язково стабільні при зміні функцій. Увімкнення нових функцій пула/набору даних може вплинути на сумісність зі старими приймачами.
  3. OpenZFS уніфікував декілька платформних лінійок. «ZFS», який ви запускаєте на Linux сьогодні, походить від Solaris ZFS і має роки розвитку функцій і оперативного загартування.
  4. Великі розміри запису можуть зробити інкременти дивовижно великими. Якщо у вашому наборі даних встановлено recordsize=1M і навантаження перезаписує невеликі області випадково, ви можете перемішувати великі блоки.
  5. Resumable send існує, бо мережі ненадійні. Токени відновлення дозволяють продовжити receive після переривання замість перезапуску терабайтного стріму.
  6. Реплікація ZFS може зберігати властивості, ACL та xattrs — але тільки якщо ви цього попросите. Прапорці як -p і -x змінюють, що реплікується і що перезаписується.
  7. Bookmark-и введені, щоб зберігати інкрементальні бази без збереження повних знімків. Вони легкі і вказують на стан знімка, корисні у конвеєрах реплікації.
  8. Зашифрована реплікація має два режими: raw і non-raw. «Raw» реплікація зберігає шифрування недоторканим і уникає дешифрування/повторного шифрування на відправнику.
  9. zfs receive свідомо суворий щодо походження. Якщо останній знімок приймача не збігається з базовим знімком відправника, ZFS не буде «робити найкраще» з ваших даних.

Операційна ментальна модель: знімки, стріми та походження

Якщо ви запам’ятаєте одну концепцію — запам’ятайте походження (lineage). Інкрементальна передача працює лише тоді, коли приймач має знімок, що байт-в-байт такий самий, як базовий знімок відправника.
Саме тому правила іменування знімків та політики утримання — не «приємний додаток» — вони структурні.

1) Ланцюжок: база → зміни → новий

Коли ви виконуєте:

cr0x@server:~$ zfs send -i tank/app@2025-12-24_0000 tank/app@2025-12-25_0000 | zfs receive -u backup/app

ZFS вираховує, які блоки відрізняються між цими знімками і потік їх. На приймачі ці блоки застосовуються, щоб відтворити @2025-12-25_0000.
Якщо backup/app ще не має @2025-12-24_0000 з відповідною GUID-походженням, receive завершиться з помилкою.

2) Набори знімків проти «тільки останнього»

Частою метою бекапу є: «тримати тиждень погодинних і місяць денних». З ZFS це часто означає, що ви хочете, щоб приймач також мав ці знімки,
а не лише актуальний стан. Тут допомагає -I: воно може передати діапазон, який включає проміжні знімки, зберігаючи набір.

3) Властивості, поведінка монтування і чому ваш бекап-сервер раптово змонтував продакшн-дані

Receive може створювати набори даних і знімки. За замовчуванням отриманий файловий простір може монтуватись відповідно до успадкованих властивостей. На хості резервного копіювання зазвичай
ви хочете, щоб репліковані файлові системи залишалися немонтованими, щоб уникнути випадкового доступу, індексації або «корисних» агентів, які їх сканують.

Тому ви бачитимете zfs receive -u в більшості пристойних сценаріїв: воно приймає набір даних, але не монтує його.

Проектування робочого процесу реплікації, що переживе реальне життя

Робочий дизайн реплікації відповідає на чотири питання:

  • Яка одиниця реплікації? Одиночний набір даних чи піддерево з нащадками (наприклад, tank/services та всі нащадки).
  • Як ми називаємо знімки? Щоб автоматизація могла обчислити «останній спільний знімок», а люди могли швидко розбиратися з помилками.
  • Як ми обробляємо переривання? Резюмація receives, таймаути та моніторинг.
  • Які наші інваріанти? «Бекапи немонтовані», «ми реплікуємо властивості», «ми не реплікуємо тимчасові набори» тощо.

Іменування знімків, яке оператори можуть розібрати о 03:00

Уникайте креативу. Використовуйте сортувальні часові позначки і стабільний префікс. Наприклад:
auto-2025-12-25_0100, auto-2025-12-25_0200.
Якщо у вас кілька політик, включіть ім’я політики:
hourly-2025-12-25_0200, daily-2025-12-25.

Raw зашифрована реплікація: коли бекапи не повинні бачити plaintext

Якщо ваші набори даних зашифровані і ви хочете, щоб хост для бекапів зберігав шифртекст (і не вимагав ключів), вам потрібно raw-відправлення:
zfs send -w (або --raw, залежно від платформи). Це зберігає подання шифрування на диску.
Це величезна операційна перевага: вашу систему бекапів можна розглядати як «зберігання», а не як «доверий обчислювальний вузол».

Реплікація нащадків: один стрім, багато наборів даних

Використовуйте -R (replication stream), коли хочете реплікувати набір даних та його нащадків, плюс властивості і знімки. Це опція «принести все дерево».
Це також опція, яка може здивувати, якщо ви не планували реплікувати щось вкладене, створене кимось для «тимчасового тестування».

Пропускна здатність та CPU: ваш конвеєр — це система

zfs send генерує стрім; як ви його транспортуєте — важливо. SSH-шифрування може стати вузьким місцем раніше за диски.
Стиснення може допомогти, якщо ваші дані стискаються і CPU дешевший за WAN. Буферизація може згладити сплески і тримати обидві сторони завантаженими.

Жарт №2: SSH чудовий, поки ви не зрозумієте, що ваш «пристрій бекапів» насправді обігрівач з мережевим портом.

Практичні завдання: команди, які ви справді виконуватимете

Команди нижче передбачають два хости: джерело (prod) і приймач бекапу (backup).
Налаштуйте імена пулів і наборів даних відповідно до вашого середовища.

Завдання 1: Підтвердити макет набору даних і базові властивості

cr0x@prod:~$ zfs list -o name,used,avail,refer,mountpoint,compression,recordsize -r tank/app
NAME              USED  AVAIL  REFER  MOUNTPOINT        COMPRESS  RECSIZE
tank/app          220G   1.4T   180G  /srv/app          lz4       128K
tank/app/db        40G   1.4T    40G  /srv/app/db       lz4       16K
tank/app/uploads   60G   1.4T    60G  /srv/app/uploads  lz4       128K

Інтерпретація: рішення про реплікацію приймаються на рівні набору даних. Тут db налаштовано з меншим recordsize.
Якщо ви реплікуєте батька з -R, ви отримаєте й нащадків — зручно, коли це заплановано.

Завдання 2: Створити знімок (один набір даних)

cr0x@prod:~$ SNAP=auto-2025-12-25_0000
cr0x@prod:~$ zfs snapshot tank/app@${SNAP}

Інтерпретація: знімки миттєві. Набір даних продовжує змінюватися після знімка; знімок — це узгоджене представлення у часі.

Завдання 3: Створити рекурсивні знімки (набір даних + нащадки)

cr0x@prod:~$ SNAP=auto-2025-12-25_0000
cr0x@prod:~$ zfs snapshot -r tank/app@${SNAP}

Інтерпретація: кожен нащадок отримує знімок з тією самою назвою. Така симетрія спрощує скрипти реплікації.

Завдання 4: Зробити першу повну реплікацію на порожній приймач

cr0x@prod:~$ SNAP=auto-2025-12-25_0000
cr0x@prod:~$ zfs send -R tank/app@${SNAP} | ssh backup 'zfs receive -u -F backup/app'

Інтерпретація: -R реплікує дерево наборів даних. receive -u залишає їх немонтованими. -F примусово робить rollback на приймачі, якщо потрібно.
Використовуйте -F тільки коли розумієте радіус ураження; воно може скасувати зміни на приймачі.

Завдання 5: Виконати інкрементальну реплікацію до наступного знімка

cr0x@prod:~$ OLD=auto-2025-12-25_0000
cr0x@prod:~$ NEW=auto-2025-12-25_0100
cr0x@prod:~$ zfs snapshot -r tank/app@${NEW}
cr0x@prod:~$ zfs send -R -i tank/app@${OLD} tank/app@${NEW} | ssh backup 'zfs receive -u backup/app'

Інтерпретація: це відправляє лише зміни з @OLD, зберігаючи структуру реплікованого набору даних.
Якщо приймач не має @OLD (або має інше походження), це завершиться помилкою.

Завдання 6: Використати -I, щоб включити проміжні знімки

cr0x@prod:~$ BASE=auto-2025-12-25_0000
cr0x@prod:~$ HEAD=auto-2025-12-25_0600
cr0x@prod:~$ zfs send -R -I tank/app@${BASE} tank/app@${HEAD} | ssh backup 'zfs receive -u backup/app'

Інтерпретація: приймач отримає кожен знімок між @BASE і @HEAD, що існує на відправникові, а не лише @HEAD.
Так ви вирівнюєте історію знімків.

Завдання 7: Підтвердити наявність знімків і їх відповідність на обох кінцях

cr0x@prod:~$ zfs list -t snapshot -o name,creation -s creation -r tank/app | tail -n 5
tank/app@auto-2025-12-25_0200  Wed Dec 25 02:00 2025
tank/app@auto-2025-12-25_0300  Wed Dec 25 03:00 2025
tank/app@auto-2025-12-25_0400  Wed Dec 25 04:00 2025
tank/app@auto-2025-12-25_0500  Wed Dec 25 05:00 2025
tank/app@auto-2025-12-25_0600  Wed Dec 25 06:00 2025
cr0x@backup:~$ zfs list -t snapshot -o name,creation -s creation -r backup/app | tail -n 5
backup/app@auto-2025-12-25_0200  Wed Dec 25 02:01 2025
backup/app@auto-2025-12-25_0300  Wed Dec 25 03:01 2025
backup/app@auto-2025-12-25_0400  Wed Dec 25 04:01 2025
backup/app@auto-2025-12-25_0500  Wed Dec 25 05:01 2025
backup/app@auto-2025-12-25_0600  Wed Dec 25 06:01 2025

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

Завдання 8: Оцінити розмір send перед виконанням (корисно для WAN)

cr0x@prod:~$ zfs send -n -v -i tank/app@auto-2025-12-25_0500 tank/app@auto-2025-12-25_0600
send from @auto-2025-12-25_0500 to tank/app@auto-2025-12-25_0600 estimated size is 3.14G
total estimated size is 3.14G

Інтерпретація: -n — це сухий прогін; -v друкує оцінки. Розглядайте це як приблизну оцінку — стиснення, recordsize і вміст стріму можуть змінити реальність.

Завдання 9: Додати стиснення та буферизацію в транспортний конвеєр

cr0x@prod:~$ OLD=auto-2025-12-25_0500
cr0x@prod:~$ NEW=auto-2025-12-25_0600
cr0x@prod:~$ zfs send -R -i tank/app@${OLD} tank/app@${NEW} \
  | lz4 -z \
  | ssh backup 'lz4 -d | zfs receive -u backup/app'

Інтерпретація: якщо ваші дані вже стиснуті (медіа, бекапи бекапів), це може марнувати CPU і зменшити пропускну здатність.
Якщо ваш вузький горлечко — WAN, це може дати великий виграш. Вимірюйте, не вгадуйте.

Завдання 10: Реплікація зашифрованого набору даних з raw send

cr0x@prod:~$ zfs get -H -o property,value encryption tank/secret
encryption	on
cr0x@prod:~$ SNAP=auto-2025-12-25_0000
cr0x@prod:~$ zfs snapshot tank/secret@${SNAP}
cr0x@prod:~$ zfs send -w tank/secret@${SNAP} | ssh backup 'zfs receive -u backup/secret'

Інтерпретація: приймач зберігає зашифровані дані і не потребує ключа для їх зберігання.
Ваш workflow відновлення має врахувати, де зберігаються ключі і як їх завантажувати.

Завдання 11: Використати bookmark-и, щоб зберегти інкрементальну базу без зберігання старих знімків

cr0x@prod:~$ zfs snapshot tank/app@auto-2025-12-25_0700
cr0x@prod:~$ zfs bookmark tank/app@auto-2025-12-25_0700 tank/app#base-0700
cr0x@prod:~$ zfs list -t bookmark -o name,creation -r tank/app
NAME                   CREATION
tank/app#base-0700      Wed Dec 25 07:00 2025

Інтерпретація: bookmark-и — легкі «якорі» для стану знімка. Вони можуть служити інкрементальними базами у багатьох робочих процесах,
дозволяючи видаляти старі знімки, зберігаючи неперервність реплікації — коли ваша платформа підтримує це наскрізь.

Завдання 12: Обробити перерваний receive з токенами відновлення

cr0x@backup:~$ zfs get -H -o value receive_resume_token backup/app
1-7d3f2b8c2e-120-789c...
cr0x@prod:~$ ssh backup 'zfs get -H -o value receive_resume_token backup/app'
1-7d3f2b8c2e-120-789c...
cr0x@prod:~$ TOKEN=$(ssh backup 'zfs get -H -o value receive_resume_token backup/app')
cr0x@prod:~$ zfs send -t ${TOKEN} | ssh backup 'zfs receive -u backup/app'

Інтерпретація: ви відновлюєте стрім з того місця, де він зупинився, замість перезапуску. Це одна з тих функцій, яких ви не оціните,
поки ненадійне з’єднання не впаде на 97%.

Завдання 13: Безпечно обрізати знімки на відправнику після успішної реплікації

cr0x@prod:~$ KEEP=auto-2025-12-25_0600
cr0x@prod:~$ zfs list -H -t snapshot -o name -s creation -r tank/app | head
tank/app@auto-2025-12-24_0000
tank/app@auto-2025-12-24_0100
tank/app@auto-2025-12-24_0200
cr0x@prod:~$ zfs destroy tank/app@auto-2025-12-24_0000
cr0x@prod:~$ zfs destroy -r tank/app@auto-2025-12-24_0100

Інтерпретація: видалення знімків — незворотне. У продакшні обрізка має бути автоматизована, але обережна, бажано з умовою:
«приймач має знімок X» і «остання реплікація пройшла успішно». Якщо ви видалите єдину спільну базу, наступний інкремент стане повним.

Завдання 14: Перевірити цілісність за допомогою scrub і перевірити помилки

cr0x@backup:~$ zpool scrub backup
cr0x@backup:~$ zpool status -v backup
  pool: backup
 state: ONLINE
status: scrub in progress since Wed Dec 25 09:12:48 2025
  1.23T scanned at 2.11G/s, 410G issued at 701M/s, 3.02T total
config:

        NAME        STATE     READ WRITE CKSUM
        backup      ONLINE       0     0     0
          raidz1-0  ONLINE       0     0     0
            sda     ONLINE       0     0     0
            sdb     ONLINE       0     0     0
            sdc     ONLINE       0     0     0
errors: No known data errors

Інтерпретація: реплікація переміщує дані; scrub перевіряє їх у спокої. Бекапи без періодичних перевірок цілісності — це просто дорога надія.

Швидкий план діагностики (знайти вузьке місце за хвилини)

Коли реплікація повільна або падає, ваше завдання — уникнути 90-хвилинного відхилення, під час якого всі сперечаються про «мережу», а справжній винуватець — один CPU,
що завантажений SSH. Ось послідовність, яку я використовую, бо вона швидко сходиться до рішення.

По-перше: чи відвалюється через походження?

Перевірте повідомлення про помилку і підтвердьте, що приймач має базовий знімок (або правильний ланцюжок знімків).

cr0x@backup:~$ zfs list -t snapshot -o name -r backup/app | tail -n 10
backup/app@auto-2025-12-25_0300
backup/app@auto-2025-12-25_0400
backup/app@auto-2025-12-25_0500
backup/app@auto-2025-12-25_0600

Якщо приймача не вистачає базового знімка, виправлення — не «повторити сильніше». Це «відправити повний» або «відправити інкремент від останнього спільного знімка».

По-друге: чи блокує приймач диск I/O?

Реплікація інтенсивно пише на приймачі. Якщо пул бекапу повільний, всі спроби налаштування на відправнику — косметика.

cr0x@backup:~$ zpool iostat -v 1 5
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
backup                       3.2T  5.1T      0    820      0  210M
  raidz1-0                   3.2T  5.1T      0    820      0  210M
    sda                          -      -      0    275      0   70M
    sdb                          -      -      0    280      0   71M
    sdc                          -      -      0    265      0   69M
--------------------------  -----  -----  -----  -----  -----  -----

Якщо пропускна здатність запису низька і латентність висока (ви побачите це в системних інструментах), приймач — ваш обмежувач.
Поширені причини: накладні витрати RAIDZ при малих записах, SMR-диски, завантажений пул або неправильно підібраний SLOG для синхронно-навантажених робіт.

По-третє: чи блокується відправник на читанні?

cr0x@prod:~$ zpool iostat -v 1 5
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                         5.8T  2.1T    620      5  640M   2.1M
  mirror-0                   1.9T   700G   210      2  220M   900K
    nvme0n1                      -      -   210      2  220M   900K
    nvme1n1                      -      -   210      2  220M   900K
--------------------------  -----  -----  -----  -----  -----  -----

Якщо читання повільні, перевірте, чи під навантаженням пул відправника (додатки, компакти, scrubs) або чи відбувається thrashing ARC.

По-четверте: чи транспортний конвеєр — вузьке місце (SSH, стиснення, буферизація)?

Дивіться за CPU і пропускною здатністю під час send. Класичний симптом — один CPU ядеро завантажене під зав’язку, а потік обмежений на підозріло стабільному рівні.

cr0x@prod:~$ ps -eo pid,comm,%cpu,args | egrep 'zfs|ssh|lz4' | head
21433 zfs      35.2 zfs send -R -i tank/app@auto-2025-12-25_0500 tank/app@auto-2025-12-25_0600
21434 lz4      98.7 lz4 -z
21435 ssh      64.1 ssh backup lz4 -d | zfs receive -u backup/app

Якщо стиснення навантажує CPU і не зменшує байти значимо, видаліть його. Якщо SSH завантажує CPU, розгляньте швидші шифри (де це дозволено політикою)
або перенесення реплікації на виділену мережу з апаратним прискоренням IPsec — усе, що перемістить витрати на крипто.

По-п’яте: чи застрягло через частковий receive?

cr0x@backup:~$ zfs get -H -o property,value receive_resume_token backup/app
receive_resume_token  1-7d3f2b8c2e-120-789c...

Якщо існує resume token, ймовірно, у вас був перерваний receive, який треба відновити або скасувати перед продовженням нової реплікації.

Три короткі історії з корпоративного життя

Коротка історія 1: Інцидент через хибне припущення

Команда розгорнула реплікацію ZFS для флоту build-серверів. Ідея була правильна: знімати робочий простір погодинно, реплікувати на бекап-хост
і тримати тиждень історії. Вони протестували повний send, інкрементальний — і оголосили перемогу.

Через два місяці один build-сервер впав. День відновлення настав з впевненістю прописаного runbook — поки zfs receive не відмовив
застосувати інкремент. Приймач мав знімки, так. Але не ті потрібні: хтось «почистив старі бекапи» на хості бекапів, щоб звільнити місце, і видалив знімок,
що ще був базою для наступної серії інкрементів.

Хибне припущення було тонким: «якщо я видалю старі знімки на приймачі, відправник все одно зможе посилати інкременти з того, що має».
Так ZFS не працює. Інкременти вимагають спільного предка. Видаліть предка — і відправник не зможе обчислити дельту, яку приймач зможе застосувати.
ZFS не вигадає нову базу.

Відновлення вимагало повного send через канал, для якого він не був спроектований. Сервіс повернувся, але урок закріпився: політика утримання на приймачі
не є автономною. Якщо ви хочете незалежне утримання, потрібні незалежні бази — bookmark-и можуть допомогти, а також політика, яка обрізає лише після підтвердження
наявності відповідної бази на обох сторонах.

Після інциденту вони додали перевірку «останнього спільного знімка» в автоматизацію і заблокували видалення на приймачі, поки відповідний знімок
не буде обрізаний на відправнику (або поки не існує bookmark). Це не було блискучою інновацією, але перетворило реплікацію з «працює поки не перестане»
на систему.

Коротка історія 2: Оптимізація, що повернулась бумерангом

Інша організація мала віддалений сайт, підключений через скромний WAN. Хтось дійшов ідеї «стискати все» в конвеєрі реплікації, бо на папері стиснення зменшує ширину смуги.
Вони додали стадію стиснення і відзначили успіх, коли ранні тести з текстовими логами показали великі виграші.

Потім вони спрямували той самий конвеєр на VM-набори даних. Віртуальні машини містили бази даних, кеші і багато вже стиснутих об’єктів. Результатом став падіння пропускної здатності
і зростання відставання реплікації. Вікно бекапу технічно перестало бути «вікном» — воно стало «періодом між зараз і вічністю».

Наслідок був класичним: CPU став вузьким місцем. Стиснення жувало цикли і давало мало скорочення. SSH також шифрував вже стиснутий потік,
збільшуючи навантаження на CPU з обох сторін. Стореджі сиділи без діла, поки CPU пеклися.

Виправлення не було героїчним. Вони виміряли. Видалили стиснення для наборів, які не отримували вигоди, залишили його для тих, де воно допомагало, і запланували реплікацію
у непікові години, щоб не конкурувати з CPU додатків. Для кількох критичних наборів вони зробили raw зашифровані відправлення, зменшивши накладні витрати від зайвих трансформацій.

Оптимізація не була принципово неправильною. Вона була неправильною як загальне правило. У інженерії бекапів універсальні правила породжують універсальний біль.

Коротка історія 3: Нудна, але правильна практика, що врятувала день

Компанія, що дбала про безпеку, використовувала зашифровані набори даних для даних клієнтів і реплікувала їх у окреме середовище бекапів. Середовище бекапів вважалося
напівдовіреним: хороші фізичні контролі, але не достатньо довірене, щоб зберігати ключі. Тож вони використовували raw send і тримали ключі в контрольованому місці.

В одну середу контролер сховища на первинній системі почав видавати періодичні помилки. Нічого катастрофічного — достатньо, щоб посіяти сумнів. Вони ініціювали контрольований failover: відновили останні знімки на стендбай кластер і переключили трафік.
Це не був перший раз; вони практикували процедуру щоквартально, як дорослі.

Відновлення пройшло гладко, бо бекапи були не просто «присутні», вони були передбачуваними: отримані набори не були змонтовані, властивості були консистентні,
і набір знімків був повним. Найважливіше — у них був регулярний scrub на пулі бекапу і моніторинг помилок контрольних сум як серйозна метрика.
Коли настав час довіритися бекапам, вони не покладалися на надію.

Команда пізніше жартувала, що найцікавіше в інциденті — те, наскільки нудним було відновлення. Це правильний результат. Якщо ваша історія DR захоплююча, вона, ймовірно, ще й дорога.

Поширені помилки, симптоми та виправлення

Помилка 1: Видалення базового знімка (або розходження приймача)

Симптоми: інкрементальний receive падає з повідомленнями про відсутні знімки або «does not exist», або «incremental source is not earlier than destination.»

Виправлення: знайдіть останній спільний знімок і реплікуйте з нього. Якщо спільного нема — зробіть повний send. Запобігайте повторенню, координуючи політики утримання і/або використовуючи bookmark-и.

Помилка 2: Використання zfs receive -F як рефлекс

Симптоми: «працювало», але на приймачі набір даних втратив новіші знімки або був несподівано відкатаний.

Виправлення: резервуйте -F для випадків, коли приймач — строго ціль реплікації і ви приймаєте семантику відкату. Віддавайте перевагу чистому простору і контрольованому промоушену.

Помилка 3: Реплікація монтувань у неймспейс хоста бекапів

Симптоми: набори даних монтуються на хості бекапів; індексатори/AV-агенти споживають I/O; адміністратори випадково переглядають і модифікують копії бекапів.

Виправлення: завжди отримуйте з -u, і розгляньте встановлення canmount=off на коренях реплікації на приймачі.

Помилка 4: Плутанина між -i та -I

Симптоми: приймач має лише останній знімок, а не проміжну історію; політики утримання не збігаються; відновлення не має очікуваної точки в часі.

Виправлення: використовуйте -I, коли хочете включити проміжні знімки. Використовуйте -i, коли дійсно бажаєте лише дельту між двома знімками.

Помилка 5: Припущення, що «інкрементально» означає «маленько» (особливо для VM образів)

Симптоми: інкременти такими ж великими, як повні sends; реплікація відстає; мережі несподівано насичуються.

Виправлення: вимірюйте оцінки (zfs send -n -v), налаштовуйте recordsize набору даних, і розгляньте розділення навантажень (окремі набори даних для інтенсивно змінюваних даних).

Помилка 6: Відсутність планування переривань

Симптоми: часткові receives блокують подальшу реплікацію; повторні перезапуски витрачають години; відставання бекапу зростає.

Виправлення: використовуйте resumable receive і моніторте receive_resume_token. Додайте автоматизацію для відновлення або чистого скасування за політикою.

Помилка 7: Сюрпризи через розбіжність версій / функцій

Симптоми: receive падає з повідомленнями про не підтримувані функції або версії стріму.

Виправлення: стандартизуйте версії OpenZFS між відправником і приймачем, де можливо, або обмежуйте відправлення сумісними функціями через опції платформи і послідовні оновлення.

Контрольні списки / покроковий план

Контрольний список A: Побудувати розумну базову реплікацію (перший запуск)

  1. Виберіть корінь набору даних для реплікації (наприклад, tank/app) і вирішіть, чи включаються нащадки.
  2. Визначте іменування знімків: префікс + сортувальний час.
  3. На приймачі створіть виділений пул/неймспейс наборів даних (наприклад, backup/app) і переконайтеся, що там вистачає місця для утримання.
  4. Встановіть налаштування приймача: canmount=off на корені і плануйте завжди використовувати zfs receive -u.
  5. Створіть перший рекурсивний знімок: zfs snapshot -r tank/app@auto-....
  6. Зробіть перший повний send з -R і отримайте немонтовано.
  7. Підтвердіть наявність знімків на обох кінцях.
  8. Визначте політику утримання і впровадьте її обережно (обрізайте після підтвердження наявності бази на приймачі).

Контрольний список B: Щоденні операції (рутина «не дзвоніть на пейджер»)

  1. Створюйте знімки за розкладом (годинні/денні).
  2. Реплікуйте інкременти з останнього реплікованого знімка до нового.
  3. Після реплікації перевірте: останній знімок існує на приймачі і receive_resume_token пустий.
  4. Обрізайте старі знімки на відправнику (і за потреби на приймачі) відповідно до політики, не ламаючи ланцюжок.
  5. Періодично запускайте scrub на приймачі і тримайте алерти на помилки контрольних сум.
  6. Проводьте drills відновлення: змонтуйте клон отриманого знімка і перевіряйте цілісність на рівні додатків.

Контрольний список C: Процедура відновлення (файлова система)

  1. Визначте знімок, який потрібно відновити.
  2. Клонуйте його в новий набір даних на цілі відновлення (уникайте перезапису живих даних під час розслідування).
  3. Змонтуйте клон, перевірте вміст, потім підніміть або скопіюйте на місце відповідно до потреб додатку.

FAQ

1) У чому різниця між zfs send -i і -I?

-i відправляє дельту між точно двома знімками. -I відправляє діапазон реплікації і може включати проміжні знімки, зберігаючи історію.
Якщо вам важливо зберегти багато точок відновлення на приймачі, -I зазвичай правильний інструмент.

2) Чи можна робити інкрементальні sends без зберігання старих знімків назавжди?

Потрібна хоча б якась спільна база. У багатьох середовищах bookmark-и можуть служити легкими базами, дозволяючи видаляти знімки і зберігати неперервність реплікації.
Чи підходить це — залежить від вашої версії OpenZFS і робочого процесу. Без бази (знімка або bookmark) ви робите повний send.

3) Чому zfs receive відмовляє, хоча імена знімків збігаються?

Бо важливе походження, а не лише імена. Приймач повинен мати той самий базовий вміст і GUID-походження. Якщо знімок приймача створено незалежно
або набір даних був відкотений/змінений і розійшовся, ZFS відмовить застосувати інкремент.

4) Чи варто реплікувати властивості?

Часто — так, бо властивості як recordsize, compression і acltype можуть вплинути на те, як поводиться відновлення. Але будьте цілеспрямовані:
на цілях бекапу ви можете захотіти інше поведінку монтування (canmount=off, readonly=on), ніж у продакшні.

5) Чи безпечно виконувати реплікацію під час запису додатка?

Так, бо ви реплікуєте знімок, який консистентний. Живий набір даних продовжує змінюватися, але знімок — стабільний. Ключ — знімати на правильному рівні: для баз даних узгоджені знімки можуть вимагати pre/post хуків (flush, freeze або checkpoint).

6) Як зрозуміти, що SSH-шифрування — мій вузький горлечко?

Якщо пропускна здатність обмежена і використання CPU високе на одному ядрі під час send, підозрюйте SSH. Перевірте використання CPU процесів ssh
під час реплікації і порівняйте продуктивність на локальному лінку або з іншими налаштуваннями транспорту, дозволеними вашою політикою безпеки.

7) Що насправді показує zfs send -n -v?

Воно оцінює розмір стріму без відправлення. Добре для планування і виявлення «чому цей інкремент такий великий», але не є інструментом білінгу.
Завжди розглядайте його як орієнтир, а не як остаточну міру.

8) Чи мають бекапи бути змонтовані на сервері бекапів?

У більшості продакшн-налаштувань — ні. Тримайте їх немонтованими, щоб зменшити випадкові читання/записи, індексацію і людську цікавість. Монтуйте лише під час відновлення або тестування.
Використовуйте zfs receive -u і розгляньте canmount=off на коренях реплікації.

9) Чи можу реплікувати зашифрований набір даних на недовірений хост бекапів?

Так, за допомогою raw send. Хост бекапів зберігає шифртекст і не потребує ключів для отримання. Процес відновлення має доступ до ключів у довіреній системі.

10) Як часто треба робити scrub пулу бекапу?

Достатньо часто, щоб виявити приховану корупцію до того, як вона стане вашою історією відновлення. Багато операторів роблять щомісяця на великих пулах і частіше на менших,
з сигналізацією на будь-які помилки контрольних сум. «Правильний» інтервал залежить від типу дисків, розміру пулу і від того, наскільки ви цінуєте спокій.

Висновок

Інкрементальна передача ZFS — один із небагатьох механізмів резервного копіювання, що масштабується від «одного сервера в шафі» до «флоту з реальними вимогами відповідності»
без зміни своїх фундаментів. Знімки створюють стабільні точки у часі. Інкрементальні стріми переміщують лише змінені блоки. Приймачі відтворюють історію
з перевірками цілісності, вбудованими в саму файлову систему.

Операційний нюанс — у походженні: інкременти вимагають спільної бази, і ваші політики утримання, правила іменування і автоматизація повинні це враховувати.
Виконайте цю роботу, і ви отримаєте бекапи, що швидкі, перевіряються і зручні для відновлення — без повторного копіювання всього щоразу.

← Попередня
Резервні копії MySQL і PostgreSQL у контейнерах: як уникнути фейкових бекапів
Наступна →
DNS: Проблеми MTU можуть порушити DNS — як це довести і виправити

Залишити коментар