ZFS Реплікація: Бездрамний спосіб обробки перейменувань і переміщень наборів даних

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

Ви налаштовуєте реплікацію ZFS як доросла людина. Знімки, інкрементали, можливо шифрування, можливо трохи компресії.
А потім команда робить «невелике прибирання»: перейменування набору даних, переміщення піддерева або перетасування точок монтування.
Реплікація, яка працювала місяцями, тепер ламається з криптичною помилкою «does not match incremental source» або приймає дані в дивне місце.

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

Ментальна модель: що ламається і чому

Реплікація ZFS — це не «скопіювати папку по SSH». Це відтворення транзакцій файлової системи з одного графа зберігання
в інший за допомогою потоку, створеного zfs send і застосованого zfs receive.
Знімок — це іменована точка часу для набору даних. Інкрементальний потік — це «зміни від знімка A до знімка B».
Якщо на приймачі немає точно знімка A на цільовому наборі даних, він не зможе застосувати різницю.

Ось пастка: люди трактують імена наборів даних як стабільні ідентифікатори. Вони пишуть скрипти реплікації так:
«відправити pool/app в резерв як backup/app». Потім хтось перейменовує pool/app в
pool/apps/app. На стороні джерела лінія знімків усе ще існує, але тепер вона живе під новою назвою.
На стороні приймача ваша процедура все ще намагається застосувати інкрементали до старого цільового набору даних. Бум.

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

Жарт №1: Перейменування набору даних — це як змінити своє відображуване ім’я в Slack і чекати, що зарплата піде за ним. Це не та система.

Що означають «перейменування» і «переміщення» в термінах ZFS

  • Перейменування змінює ім’я набору даних (наприклад, tank/apptank/apps/app), зберігаючи знімки і дочірні набори.
  • Переміщення зазвичай те ж саме: ви перейменовуєте піддерево під новий батьківський шлях.
  • Зміна mountpoint змінює місце його появи в ОС (mountpoint property), а не ідентичність набору даних.
  • Promotion змінює походження клонів. Це може зруйнувати припущення про те, який знімок є «базовим» для інкременталів.

Коли реплікація насправді ламається

Реплікація ламається, коли відбувається принаймні одне з цих:

  • Ви надсилаєте інкрементали зі знімка X, але приймач не має знімка X на цільовому наборі даних.
  • Ви отримуєте в інший набір даних, ніж минулого разу (часто через те, що скрипти неправильно відображають після перейменування).
  • Приймач розійшовся записами (хтось змонтував його на запис і змінив), тож швидке застосування інкременталу неможливе без відкату.
  • Ви змінили режим реплікації (наприклад, з «plain» на «raw encrypted») посеред потоку без повторного базового відправлення.
  • Ви використовували zfs recv без -u і властивості mountpoint спричинили конфлікти, через що receive зазнає помилки або монтується в продакшн-шляхи.

Тема: ZFS послідовний. Люди — креативні.

Факти й історичний контекст, що важливі в реальному житті

  1. ZFS snapshots дешеві, бо це просто метадані, що посилаються на існуючі блоки; лише змінені блоки займають додатковий простір.
  2. Інкрементальний send залежить від спільного походження; це не «різниця за іменем», це «різниця за лінією знімків». Відсутність базового знімка — фатальна.
  3. Перейменування набору даних зберігає знімки і дочірні елементи; об’єкти знімків не зникають лише через зміну мітки.
  4. Закладки (bookmarks) існують, щоб зберегти інкрементальну базу без збереження повного знімка; це легкі вказівники на групу транзакцій.
  5. ZFS receive транзакційний; у нормальному випадку неуспішний receive не застосовує половину даних. Ось чому це надійний примітив для автоматизації.
  6. Властивості можуть бути включені або виключені при реплікації (наприклад, через zfs send -p), що впливає на mountpoint і може вас здивувати.
  7. Raw encrypted sends зберігають шифрування і не відкривають відкритий текст на хості-джерелі, але вони потребують сумісної підтримки фіч.
  8. Один-до-багатьох реплікація масштабується краще, ніж хаотичне багато-до-одного; потоки ZFS детерміновані, але ваша імпровізована логіка мапінгу — ні.

Одна операційна цитата, що добре збереглася: Hope is not a strategy. — Gene Kranz.

Правила проєктування: реплікація, що витримує перейменування

Правило 1: Виберіть корінь реплікації і ніколи не міняйте його призначення

Створіть стабільний «replication root» набір даних з обох боків і ставтеся до нього як до межі іменного простору.
Приклад: усе, що ви реплікуєте, живе під tank/replica-src на джерелі і під bkp/replica на цілі.
Датаcети додатків можуть переміщатися в продакшні, але реплікація завжди орієнтується на стабільний кореневий шлях на кожній системі.

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

Правило 2: Відділяйте «що реплікувати» від «де це зараз живе»

Ім’я джерела може змінитися. Те, що не повинно змінюватися — це намір: «це набір даних, що обслуговує сервіс X».
Кодуйте намір властивістю або файлом мапінгу, а не парсингом імен наборів даних.

Хороший шаблон — тегування:
org.example:replicate=true і org.example:replica-name=svc-x.
Ваша автоматизація знаходить набори даних за властивістю, потім реплікує їх у фіксований шлях призначення типу
bkp/replica/svc-x. Перейменовуйте джерело скільки завгодно; ідентичність реплікації лишається стабільною.

Правило 3: Тримайте послідовну схему іменування знімків під час переміщень

Імена знімків — частина вашого контракту. Якщо ви реплікуєте інкрементали, обидві сторони повинні мати однакові імена знімків
для бази й цілі. Виберіть схему й не ускладнюйте:
replica-YYYYMMDD-HHMM або replica-auto-###.

Якщо вам треба змінити схему, переініціалізуйте з повного send один раз і перезапустіть інкрементали. Не намагайтеся «мостити» магією.

Правило 4: Використовуйте holds і bookmarks свідомо

Якщо ви покладаєтеся на базовий знімок для інкременталів, захистіть його. Використовуйте zfs hold, щоб запобігти видаленню
його до того, як приймач наздожене. Якщо ви не хочете зберігати старі знімки вічно, використовуйте закладки (bookmarks):
вони дозволяють робити інкрементали від «запам’ятаного моменту», не утримуючи весь знімок.

Правило 5: Ніколи не дозволяйте цілій відгалужуватися (поки ви цього не хочете)

Вашу ціль реплікації слід трактувати як тільки для читання. Забезпечте це соціально (дозволи), операційно (не монтуйте автоматично),
і технічно (розгляньте readonly=on на отриманих наборах даних). Відгалуження — причина №1, через яку ви в кінці кінців
використовуєте zfs recv -F під тиском, що є еквівалентом «я просто перезапущу продакшн, що найгіршого може статися?»

Правило 6: Визначте стратегію receive mapping заздалегідь

У вас є три поширені шаблони:

  • Дзеркалити імена: tank/appbkp/tank/app. Просто, поки не перейменують. Тоді потрібна логіка обробки перейменувань.
  • Стабільні імена призначення: тегувати джерела і мапити в bkp/replica/<service>. Найкраще для стійкості до перейменувань/переміщень.
  • Отримувати під батьком з zfs recv -d: зберегти макет піддерева під виділеним коренем. Добре для масової міграції, але все ще залежить від імен.

Для «без драм» виграють стабільні імена призначення. Вони вимагають трохи налаштувань і рятують багато ночей.

Завдання: команди, вивід і рішення (12+)

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

Завдання 1: Визначити, що змінилося (ознаки перейменування/переміщення)

cr0x@server:~$ zfs list -t filesystem -o name,creation -r tank | head
NAME                 CREATION
tank                 Mon Jan  8 09:12 2024
tank/apps            Tue Apr  2 13:41 2024
tank/apps/app1       Tue Apr  2 13:44 2024
tank/oldapp          Wed Mar  6 10:18 2024

Що це значить: дає поточні імена й приблизний хронологічний порядок.
Якщо набір даних «перемістився», ви побачите новий батьківський шлях; час створення зазвичай залишається тим самим для самого набору.

Рішення: Якщо ім’я набору даних змінилося, припиніть покладатися на мапінг за іменем. Перейдіть на мапінг за властивістю або явно перейменуйте ціль.

Завдання 2: Підтвердити наявність імен знімків на джерелі

cr0x@server:~$ zfs list -t snapshot -o name,creation -s creation -r tank/apps/app1 | tail -5
tank/apps/app1@replica-20240130-0200  Tue Jan 30 02:00 2024
tank/apps/app1@replica-20240131-0200  Wed Jan 31 02:00 2024
tank/apps/app1@replica-20240201-0200  Thu Feb  1 02:00 2024
tank/apps/app1@replica-20240202-0200  Fri Feb  2 02:00 2024
tank/apps/app1@replica-20240203-0200  Sat Feb  3 02:00 2024

Що це значить: у вас є послідовна серія знімків.

Рішення: Виберіть останній успішно реплікований знімок як інкрементальну базу. Якщо не можете його ідентифікувати, перевірте ціль.

Завдання 3: Підтвердити наявність знімків на цільовому наборі даних, який ви вважаєте правильним

cr0x@server:~$ ssh bkp1 zfs list -t snapshot -o name -r bkp/replica/app1 | tail -5
bkp/replica/app1@replica-20240130-0200
bkp/replica/app1@replica-20240131-0200
bkp/replica/app1@replica-20240201-0200
bkp/replica/app1@replica-20240202-0200
bkp/replica/app1@replica-20240203-0200

Що це значить: Ціль має ті самі знімки, тож інкрементали повинні працювати.

Рішення: Якщо знімки не співпадають, або ви отримуєте в неправильний набір даних, або потрібно повторне базове відправлення/reseeding.

Завдання 4: Перевірити дивергенцію (записи на цілі)

cr0x@server:~$ ssh bkp1 zfs get -H -o property,value,source readonly bkp/replica/app1
readonly	off	local

Що це значить: Ціль записувана. Це не доказ дивергенції, але запрошення до неї.

Рішення: Встановіть readonly=on на цілях реплікації, якщо у вас немає навмисного workflow для відновлення.

Завдання 5: Знайти останній спільний знімок швидко

cr0x@server:~$ comm -12 \
  <(zfs list -H -t snapshot -o name -r tank/apps/app1 | sed 's/^.*@/@/' | sort) \
  <(ssh bkp1 zfs list -H -t snapshot -o name -r bkp/replica/app1 | sed 's/^.*@/@/' | sort) | tail -3
@replica-20240201-0200
@replica-20240202-0200
@replica-20240203-0200

Що це значить: Імена знімків співпадають між джерелом і ціллю. Останній рядок — ваш кандидат на базовий знімок інкременталу.

Рішення: Використайте цю базу з zfs send -i (або -I, якщо надсилаєте діапазон, що включає проміжні знімки).

Завдання 6: Прикинути розмір send (сухий запуск)

cr0x@server:~$ zfs send -nPv -i tank/apps/app1@replica-20240203-0200 tank/apps/app1@replica-20240204-0200
send from @replica-20240203-0200 to tank/apps/app1@replica-20240204-0200 estimate: 1.42G
total estimated size is 1.42G

Що це значить: Ви збираєтесь відправити ~1.4 GiB. Якщо ви очікували 10 MiB, щось не так (або ваш додаток став дуже активним).

Рішення: Якщо оцінка несподівано велика, перевірте churn (логи, tmp, бази даних) і подумайте про виключення або зміну структури набору даних.

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

cr0x@server:~$ zfs send -w -i tank/apps/app1@replica-20240203-0200 tank/apps/app1@replica-20240204-0200 | \
  ssh bkp1 zfs recv -u bkp/replica/app1

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

Рішення: Якщо receive падає через відсутність базового знімка, зупиніться і звірте набори даних. Якщо через конфлікти mountpoint, тримайте -u і виправляйте властивості.

Завдання 8: Підтвердити, що receive зайшов туди, куди ви думаєте

cr0x@server:~$ ssh bkp1 zfs list -o name,used,refer,mountpoint bkp/replica/app1
NAME             USED  REFER  MOUNTPOINT
bkp/replica/app1 8.21G 8.21G  /bkp/replica/app1

Що це значить: Набір даних існує і mountpoint — те, що ціль вважає правильним.

Рішення: Якщо mountpoint вказує в продакшн-шлях, зупиніться і встановіть mountpoint=none або отримуйте з -u та налаштуйте перед монтуванням.

Завдання 9: Проаналізувати помилки receive і частковий стан

cr0x@server:~$ ssh bkp1 zpool status -x
all pools are healthy

Що це значить: Пул не деградований. Якщо реплікація повільна або падає, проблема ймовірно не в активному падінні vdev.

Рішення: Якщо zpool status показує resilvering, помилки або degraded vdev, виправте стан сховища перед тим, як звинувачувати ZFS send/recv.

Завдання 10: Знайти приховані блокери: утримання, що перешкоджають обрізанню

cr0x@server:~$ zfs holds tank/apps/app1@replica-20240203-0200
NAME                                    TAG             TIMESTAMP
tank/apps/app1@replica-20240203-0200     repl-base       Sat Feb  3 02:05 2024

Що це значить: Існує hold. Добре, коли навмисно; дратує, коли забуто.

Рішення: Тримайте holds на останній реплікованій базі до підтвердження отримання на цілі. Знімайте утримання як частину post-replication hook.

Завдання 11: Використати закладку як інкрементальний якір (коли потрібне обрізання знімків)

cr0x@server:~$ zfs bookmark tank/apps/app1@replica-20240203-0200 tank/apps/app1#repl-anchor
cr0x@server:~$ zfs list -t bookmark -o name -r tank/apps/app1
tank/apps/app1#repl-anchor

Що це значить: Закладка існує. Вона може слугувати «from» стороною інкременталів навіть якщо ви потім видалите знімок.

Рішення: Використовуйте закладки, коли політика зберігання агресивна, але реплікації потрібна стабільна база.

Завдання 12: Перевірити фічі та сумісність шифрування (щоб уникнути неприємних сюрпризів)

cr0x@server:~$ zpool get -H -o name,property,value feature@encryption tank
tank  feature@encryption  active
cr0x@server:~$ ssh bkp1 zpool get -H -o name,property,value feature@encryption bkp
bkp  feature@encryption  active

Що це значить: Обидва пула підтримують фічу шифрування. Raw sends не впадуть через відсутність підтримки фіч.

Рішення: Якщо ціль не підтримує потрібні фічі, або оновіть, або уникайте raw/шифрованих workflow, поки можливості не вирівняються.

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

cr0x@server:~$ iostat -xz 1 3
Linux 6.6.0 (src1)  02/04/2026  _x86_64_  (16 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.10    0.00    3.20   18.40    0.00   66.30

Device            r/s     w/s   rkB/s   wkB/s  await  svctm  %util
nvme0n1         82.0   120.0  98000.0  62000.0  8.20  0.45  91.0

Що це значить: Високий %util і підвищений await вказують, що вузьке місце — диск (або він сильно завантажений).

Рішення: Якщо диски завантажені, обмежте реплікацію, заплануйте її в поза-піковий час або налаштуйте recordsize/compression для робочого навантаження.

Завдання 14: Переконатися, що ви випадково не змінюєте властивості через реплікацію

cr0x@server:~$ zfs get -H -o property,value,source mountpoint,canmount tank/apps/app1
mountpoint	/var/lib/app1	local
canmount	on	default
cr0x@server:~$ ssh bkp1 zfs get -H -o property,value,source mountpoint,canmount bkp/replica/app1
mountpoint	/bkp/replica/app1	local
canmount	noauto	local

Що це значить: Резервний набір даних не буде автомонтуватися (noauto). Це здорово для репліки.

Рішення: Примусьте цілі бекапу бути інертними за замовчуванням: canmount=noauto, контролюйте монтовання лише під час тестів відновлення.

Обробка перейменувань і переміщень наборів даних (безпечні шаблони)

Шаблон A: Ідентичність на основі властивостей зі стабільними іменами призначення (рекомендовано)

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

Як це працює:

  1. На джерельному наборі даних (де б він зараз не знаходився) встановіть властивість типу org.example:replica-name=app1.
  2. Ваш інструмент реплікації знаходить набори даних за властивістю, а не за шляхом.
  3. Всі потоки потрапляють в bkp/replica/app1, незалежно від того, чи джерело tank/app1 чи tank/apps/app1.

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

Що все ще може зламатися: хитрощі з promotion/clone, зміни режиму шифрування або дозвіл цілій відгалужуватися.

Шаблон B: Якщо ви дзеркалите імена, розглядайте перейменування як подію, що реплікується

Деякі середовища хочуть, щоб шлях на цілі дзеркалив шлях джерела для зручності перегляду.
Добре. Але тоді ви повинні реплікувати перейменування як зміну першого класу.

ZFS не присилає «події перейменування» як окремі речі. Ваша автоматизація повинна виявляти перейменування (або переміщення)
і застосовувати відповідні перейменування на стороні приймача перш ніж надсилати інкрементали.

Мінімальний підхід:

  • Тримайте файл мапінгу GUID джерела набору даних → шлях цілі.
  • На кожному запуску порівнюйте поточне ім’я джерела для того GUID. Якщо воно змінилося, перейменуйте цільовий набір даних відповідно.
  • Потім запускайте інкрементали як зазвичай.

Це працює, тому що перейменування набору даних не змінює його знімків або GUID. Ваше завдання — тримати шлях набору даних приймача вирівняним з тим, де ваша автоматика очікує його бачити.

Шаблон C: Використовуйте zfs recv -d під стабільним коренем для переміщень піддерева

Якщо ви хочете зберегти відносні імена, але не абсолютні, приймайте в стабільний корінь з -d.
Приклад: джерело відправляє tank/apps/app1, ціль приймає під bkp/replica з -d,
створюючи bkp/replica/tank/apps/app1 або подібне залежно від send stream і опцій цілі.

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

Що робити під час вікна перейменування

Якщо перейменування/переміщення заплановані, можна зробити це нудно:

  1. Коротко призупиніть реплікацію (або принаймні зупиніть автоматичне обрізання).
  2. Зробіть знімок безпосередньо перед перейменуванням: @replica-pre-rename.
  3. Перейменуйте/перемістіть набір даних.
  4. Підтвердіть, що знімки все ще існують під новою назвою.
  5. Оновіть мапінг/властивість, якщо потрібно.
  6. Відновіть реплікацію, використовуючи останній спільний знімок.

Суть не в тому, щоб «уникати перейменувань». Суть — не створювати автоматизацію, яка припускає, що імена ніколи не змінюються.

Жарт №2: Якщо ваш скрипт реплікації використовує cut -d/ -f2 щоб ідентифікувати набори даних, це не автоматизація — це крик про допомогу.

Три корпоративні міні-історії з реального життя

Міні-історія 1: Інцидент через неправильне припущення

Середня компанія виконувала реплікацію ZFS з продакшн-файла в бекап кожні 15 хвилин. Скрипт реплікації був простим:
він перелічував набори даних під tank/prod, а потім дзеркалив той самий шлях під bkp/prod.
Усі вважали шлях набору даних ідентичністю даних. Він був стабільний роками, тож чому б і ні?

Команда платформи переструктурувала дерево наборів даних відповідно до нової організаційної структури. Нічого зловмисного, нічого поспішного:
tank/prod/finance стало tank/prod/business/finance. Набори даних були перейменовані правильно через ZFS,
знімки і діти залишились. Вони навіть розіслали повідомлення про зміну. Реплікація, однак, не знала, що
finance «переїхав». Вона спробувала надіслати інкрементали для tank/prod/business/finance в
bkp/prod/business/finance, якого ще не існувало. Тож вона створила його, прийняла повну базову копію і продовжила.

На бекапі вийшло два копії: стара bkp/prod/finance за вчорашній останній знімок і нова
bkp/prod/business/finance з сьогоднішніми знімками. Ніхто не помітив, бо моніторинг перевіряв лише
що «робота реплікації успішна» і що «останній знімок десь існує».

Потім знадобилося відновлення. On-call взяв очевидну назву набору — bkp/prod/finance — і знайшов її застарілою.
Паніка, ескалація, war room, звинувачення. Дані були присутні, просто під новим шляхом. Відновлення пройшло з затримкою,
і урок був жорсткий: шлях набору даних ніколи не був ідентичністю; він був зручністю.

Виправлення було нудним: мапінг на основі властивостей до стабільної назви призначення. Вони зберегли дзеркальне дерево для людей,
але реплікація таргетилась на стабільний набір даних «service ID» і публікувались читабельні клони для зручності.
Це усунуло цілий клас тихих збоїв.

Міні-історія 2: Оптимізація, що злетіла вбік

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

Перше, що вдарило — CPU. Компресія в потоці конфліктувала з шифруванням, контрольними сумами і нормальним навантаженням.
Реплікація стала швидшою, але все інше стало повільнішим. Вони також отримали дивні стрибки латентності, бо роботи реплікації збігалися з пакетною обробкою додатків. Користувачі скаржились. Виправлення мало б бути «перепланувати», але команда подвоїла ставку: більше паралелізму, більше тюнінгу.

Другий бумеранг був тонший. Обрізання знімків стало настільки агресивним, що «останній спільний знімок»
іноді зникав до того, як приймач отримав інкрементали (мережевий глюк, вікно обслуговування тощо).
Система тоді падала на повні відправлення. Повні відправлення були величезними, тож вони займали більше часу, тож більше інкременталів пропускалось,
тож ще більше повних відправлень. Це був самоналагоджувальний позитивний зворотний зв’язок. Бекап-система не була зламана; її просили спринтувати, поки їй підрізають щиколотки.

Третій удар був операційною складністю. Коли набір даних перейменовувався, паралельний pipeline намагався «самолікуватись»
шляхом створення нових цільових наборів і reseed. Це приховувало справжню проблему (зміну мапінгу) за «успіхом».
Відновлення перетворилося на полювання на скарби.

Вони викинули більшість «оптимізацій» і замінили їх двома речами: holds/bookmarks, щоб гарантувати інкрементальну базу, і жорстким правилом,
що цілі реплікації мають бути стабільними та інертними (canmount=noauto, readonly=on).
Продуктивність покращилась, бо система припинила робити зайву роботу. Найдешевша оптимізація часто — не робити зайвого.

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

Компанія з регуляторними вимогами виконувала щотижневі тести відновлення. Не «ми змонтували це колись минулого року», а документований прогін:
отримане не монтували, вони клонули знімок у тимчасовий набір даних, ставили безпечний mountpoint, монтували,
виконували перевірки цілісності на рівні додатку, потім знищували клон. Щотижня. Ті самі кроки. Та сама документація.

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

Потім стався реальний інцидент: ransomware, що вимагав швидкого відновлення конкретного сервісу.
Набір даних сервісу був перейменований два тижні тому під час прибирання. Продакшн-ім’я змінилося, але властивість ідентичності реплікації залишилася.
Резервний набір даних був там, де runbook очікував його бачити: bkp/replica/svc-payments.

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

Плейбук швидкої діагностики (знайти вузьке місце швидко)

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

По-перше: доведіть походження і мапінг (найбільші випадки)

  1. Підтвердіть, що ви приймаєте в очікуваний набір даних: перелічіть цільові знімки для шляху призначення і підтвердіть, що він містить очікувану послідовність.
  2. Знайдіть останній спільний знімок швидким перетином (див. Завдання 5). Якщо немає — ви reseed-ите.
  3. Перевірте дивергенцію: чи ціль записувана, змонтована або використовується чимось? Якщо так — вважайте її розійшлася, поки не доведено інакше.

По-друге: перевірте здоров’я пулу і навантаження дисків

  1. Здоров’я пулу: zpool status -x має бути чистим. Якщо ні — припиніть вважати реплікацію головною проблемою.
  2. Насичення дисків: iostat -xz і специфічні статистики ZFS, щоб побачити, чи ви I/O-bound.
  3. Запас вільного простору: низький вільний простір ламає реплікацію дурними способами і погіршує фрагментацію.

По-третє: перевірте CPU і мережу

  1. CPU: якщо ви використовуєте компресію в пайпі, шифрування або важке чексумахування, слідкуйте за CPU.
  2. Мережа: перевірте пропускну здатність і втрати пакетів. Реплікаційні потоки чутливі до ненадійних лінків, бо повтори варті часу і бази можуть застаріти.
  3. Стрибки латентності: плануйте реплікацію поза пакетними задачами додатків, scrub-ами і resilver-ами.

Варіанти рішень

  • Якщо походження/мапінг неправильні: виправте мапінг набору даних (перейменуйте цільовий набір, змініть мапінг за властивістю), не «форсуйте receive».
  • Якщо сховище нездорове: ремонтуйте/замінюйте, потім повторіть реплікацію; форсоване отримання на нестабільні пули створює другі інциденти.
  • Якщо ресурси завантажені: обмежте реплікацію, зменшіть паралельність або перенесіть її. «Швидше» не завжди «краще», якщо це ламає RPO.

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

1) «cannot receive incremental stream: most recent snapshot does not match»

Симптом: Receive падає при застосуванні інкрементального потоку.

Корінна причина: Цільовий набір даних не має точно базового знімка, який чекає потік (неправильна ціль, обрізаний базовий знімок або розійшлася історія).

Виправлення: Знайдіть останній спільний знімок (Завдання 5). Якщо немає — reseed з повного send у правильну ціль. Перестаньте обрізати бази до підтвердження отримання репліки.

2) Інкрементали раптово стали повними відправленнями після перейменування

Симптом: Після переміщення набору даних система «дружньо» створює новий цільовий набір і запускає повні бази.

Корінна причина: Мапінг за іменем створив новий шлях призначення. Старий шлях все ще містить лінію походження.

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

3) Бекапи «успішні», але відновлення застаріле або відсутнє

Симптом: Роботи повертають успіх; при відновленні знаходять старі знімки.

Корінна причина: Реплікація потрапляє в інший набір даних, ніж очікує ваш runbook для відновлення (зазвичай через перейменування/переміщення або кілька цілей).

Виправлення: Наведіть порядок: одна авторитетна ціль на ідентичність сервісу. Перевіряйте «останній знімок існує в наборі даних X», а не «де-небудь».

4) Receive падає через mountpoint або зайнятість файлової системи

Симптом: zfs recv помилки про монтування, непорожні каталоги або конфлікти mountpoint.

Корінна причина: Отримувані властивості (або поведінка за замовчуванням) намагаються змонтуватися в шляхи, що існують або використовуються.

Виправлення: Отримуйте з -u. На цільовому хості встановіть canmount=noauto і безпечний mountpoint (або mountpoint=none) для реплік.

5) Реплікація з роками повільнішає, потім починає пропускати RPO

Симптом: Дані ті самі, лінк той самий, але вікна реплікації повзають довше.

Корінна причина: Зрослий churn (логи, tmp, бази даних), знімки зберігаються довше, або підвищена фрагментація і тиск на вільний простір пулу.

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

6) Хтось використав репліку для «швидкої аналітики» і тепер інкрементали не застосовуються

Симптом: Інкрементали падають; receive радить відкат/форс.

Корінна причина: Ціль розійшлася через локальні записи.

Виправлення: Політика: репліка — тільки для читання і не монтується за замовчуванням. Якщо вже розійшлася, вирішіть: знищити й reseed, або використовувати zfs recv -F, тільки якщо погоджуєтеся втратити локальні зміни на цілі.

7) Обрізання знімків періодично ламає реплікацію

Симптом: Деякі запуски працюють, деякі падають із відсутньою базою.

Корінна причина: Ретеншн видаляє базовий знімок до підтвердження отримання на віддаленій стороні (особливо після мережевих перерв).

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

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

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

  1. Виберіть стабільний корінь призначення в бекапі: bkp/replica.
  2. Для кожного сервісного набору даних встановіть властивості на джерелі:
    • org.example:replicate=true
    • org.example:replica-name=<service-id>
  3. На бекапі створіть цільові набори даних, що відповідають service IDs (один раз): bkp/replica/<service-id>.
  4. Застосуйте безпечні властивості до цілей бекапу: canmount=noauto, readonly=on, mountpoint=none (або безпечний шлях).
  5. Стандартизуйтесь у іменах знімків: replica-YYYYMMDD-HHMM.
  6. Запровадьте holds для останньої реплікованої бази; звільняйте їх після успішного receive.
  7. Додайте workflow тесту відновлення з клонами (не монтуйте репліку напряму).

Контрольний список B: Плановане перейменування або переміщення набору даних (зробити без ламаних інкременталів)

  1. Підтвердіть останній успішний реплікований знімок з обох сторін.
  2. Поставте hold на цей знімок (на стороні джерела), щоб запобігти обрізанню в змінне вікно.
  3. Зробіть передзмінний знімок (@replica-pre-rename).
  4. Перейменуйте/перемістіть набір даних з zfs rename (і переконайтесь, що діти рухаються правильно).
  5. Переконайтесь, що знімки існують під новою назвою.
  6. Якщо ви дзеркалите імена на цілі, перейменуйте цільовий набір відповідно; інакше переконайтесь, що властивості все ще мапляться на той самий ID призначення.
  7. Відновіть реплікацію з останнього спільного знімка; перевірте сухим запуском оцінку розміру.
  8. Звільніть holds після підтвердження нового знімка на приймачі.

Контрольний список C: Коли потрібно безпечно reseed-ити (повний send)

  1. Зупиніть інкрементали для цього набору даних.
  2. Створіть свіжий базовий знімок на джерелі.
  3. Отримуйте в новий тимчасовий шлях, щоб не перезаписувати відомі добрі репліки.
  4. Після успіху перемкніть мапінг на новий набір (або перейменуйте його на місце), потім видаліть стару репліку відповідно до політики.
  5. Перезапустіть інкрементали від базового знімка.

ЧаПи

1) Чи видаляє перейменування набору даних знімки або ламає GUID знімків?

Ні. Перейменування змінює ім’я набору даних у просторовому імені. Знімки переміщуються разом з ним. Те, що ламається — це ваша автоматизація, якщо вона очікує старий шлях.

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

Тому що інкрементальний потік буквально «застосувати зміни від знімка A до знімка B». Якщо на приймачі немає знімка A у цільовому наборі даних, він не може безпечно застосувати дельту.

3) Чи слід використовувати zfs recv -F для виправлення невідповідностей?

Лише якщо ви погоджуєтеся, що приймач відкотиться/знищить новіші знімки (і можливо локальні зміни), щоб відповідати потоку.
Це інструмент, а не значення за замовчуванням. Використовуйте його, коли ви виправляєте контрольовану репліку, не коли не впевнені, куди потік потрапляє.

4) Чи можна реплікувати набір даних, який був promoted з клону?

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

5) Як закладки допомагають з перейменуваннями і обрізанням?

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

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

Не за замовчуванням. Тримайте їх інертними: canmount=noauto і отримуйте з -u. Для відновлень клоньте знімок і монтуйте клон у безпечному місці.

7) Чи реплікую я властивості типу mountpoint і readonly?

Будьте вибіркові. Реплікація mountpoint на хост бекапу — класична фатальна помилка. Краще встановлювати безпечні локальні властивості на приймачі і уникати унаследованого продакшн-поведінки mount.

8) Якщо мій джерельний набір даних перемістився під іншого батька, чи потрібен новий повний send?

Не обов’язково. Лінія походження лишається. Якщо ваш мапінг призначення залишається стабільним (мапінг за властивістю), ви можете продовжити відправляти інкрементали нормально.
Якщо ваш мапінг залежить від імен, ви ймовірно створите новий шлях на цілі і випадково викличете reseed.

9) Як зберегти «людяно-зручний» перегляд резерву, не прив’язуючи реплікацію до імен?

Реплікуйте в стабільні набори даних-ідентичності (service IDs). Потім створіть лише для читання клони або вторинні набори з дружніми іменами для перегляду.
Люди отримують дерево; автоматизація отримує стабільність.

10) Яка найкраща звичка, щоб запобігти сюрпризам у реплікації?

Щотижневі тести відновлення з використанням клонів і runbook, що вказує точні набори даних і імена знімків. Це виявляє зміщення мапінгу раніше — до інциденту.

Наступні кроки, які ви реально можете зробити цього тижня

  1. Виберіть схему ідентичності реплікації: мапінг за властивістю до стабільної назви призначення на сервіс. Впровадьте для одного набору даних спочатку.
  2. Зробіть репліки інертними: встановіть canmount=noauto, отримуйте з -u, розгляньте readonly=on.
  3. Стандартизуйтесь у іменах знімків і припиніть дозволяти кілька схем імен у одному середовищі.
  4. Додайте holds або bookmarks, щоб обрізання не могло видалити інкрементальну базу до завершення реплікації.
  5. Напишіть runbook тесту відновлення, що монтує лише клони, ніколи не саму репліку, і запускайте його за графіком.
  6. Інструментуйте правильні сигнали: останній спільний знімок на сервіс-ID, відставання реплікації і «отримано в очікуваний набір даних», а не лише коди виходу.

Перейменування й переміщення наборів даних — нормально. Ваше завдання зробити їх нудними. ZFS буде співпрацювати — якщо ви перестанете трактувати імена наборів даних як долю.

← Попередня
Апаратний GPU: кабелі живлення, шини і PCIe та «брехня» про сумісність
Наступна →
Proxmox: припиніть «випадкові» зависання віртуальних машин, виправивши один параметр ядра хосту

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