Снапшоти — це валюта реплікації в ZFS. Ви їх створюєте, передаєте по мережі за допомогою zfs send/zfs receive, і іноді роняєте один о 2 ранку, бо завдання з утримання стало «креативним». Коли це трапляється, ваш інкрементний ланцюг ламається, наступна реплікація перетворюється на повний відправлення, і ваш WAN-канал починає диміти, як тостер під дощем.
Закладки ZFS — це тихий механізм, який робить ці катастрофи знову нудними. Вони не є снапшотами. Вони не містять даних. Це крихітні вказівники на «стан файлової системи в момент цього снапшоту», і цей вказівник може підтримувати інкрементний потік живим, навіть якщо сам снапшот видалено. Закладки — те, що ви б хотіли, щоб було налаштовано в останній раз, коли хтось сказав: «Ми можемо агресивно очищати; реплікація буде в порядку.»
Що таке закладки ZFS (і чого вони не роблять)
Закладка ZFS — це іменований посилання на GUID снапшоту та позицію у групі транзакцій (TXG). Думайте про неї як про «точку збереження», яка каже: цей датасет виглядав так у момент снапшоту X — без збереження видимого простору імен цього снапшоту. Це лише метадані: нема дерева файлів, нема змонтованого вигляду, нема перегляду директорій.
Закладки існують з однієї основної причини: інкрементні відправлення потребують спільного предка з обох сторін. Зазвичай цим предком є снапшот на відправнику й отримувачу. З закладками приймач може зберігати предка як закладку навіть якщо снапшот пізніше знищено, і ZFS все ще може приймати інкременти, які посилаються на ту точку в історії.
Закладки не чарівні:
- Вони не дозволяють відновлювати файли; їх не можна змонтувати.
- Вони самі по собі не зберігають блоки даних; вони не фіксують блоки так, як це роблять снапшоти.
- Вони не можуть воскресити інкрементний потік, якщо відправник втратив предка і не може згенерувати потрібний потік.
Але для конвеєрів реплікації — де отримувачу часто потрібно пам’ятати «останню хорошу точку», а відправнику — продовжувати від неї — закладки роблять різницю між «інкремент продовжується» і «нам знову треба відправити 40 ТБ».
Жарт №1 (коротко і практично): Закладка — як записати номер свого номера в готелі. Якщо ви загубили картку-ключ (снапшот), ви все ще можете сказати ресепшену, де ви мали бути.
Чому інкрементальна реплікація ламається
Інкрементний zfs send працює, передаючи лише змінені блоки між двома снапшотами (або між снапшотом і закладкою, залежно від сторони). Важливо, щоб обидві сторони погоджувалися щодо точки «звідки». Якщо вони цього не роблять, ZFS відмовляється застосувати потік, бо не може довести, що результатом буде той самий стан файлової системи.
Більшість зламаних ланцюгів — самоспричинені. Поширені причини:
- Очищення снапшотів на приймачі: хтось видаляє старі снапшоти, щоб заощадити місце, не розуміючи, що реплікації потрібен принаймні один спільний снапшот (або закладка) як опора для інкрементів.
- Очищення снапшотів на відправнику: відправник втрачає «звідки» снапшот і не може згенерувати інкремент, який очікує отримувач.
- Реплікація в неправильний датасет: ціль була пересоздана, відкочена або замінена. Тепер «останній снапшот» на приймачі фактично не пов’язаний з відправником.
- Непослідовні припущення про іменування: скрипти вважають, що «найновіший снапшот — це найновіший», але часові пояси, розбіжність годин або зміни іменування роблять «найновіший» не тим, який реплікація останнього використовувала.
Коли ланцюг ламається, оператори часто обирають шлях найменшого опору: роблять повний відправлення. Це працює — зрештою. Але воно ж ковтає пропускну здатність, IOPS і терпіння. Закладки дозволяють зберегти спадкоємність на стороні приймача навіть коли ви видаляєте снапшоти, і це змінює рішення з «повний відправлення зараз» на «інкремент продовжується, як планувалось».
Цікаві факти та контекст
Трохи контексту допомагає, бо закладки — одна з тих функцій, які люди вважають новими, екзотичними або «тільки для великих компаній». Це не так.
- Закладки спроектовані спеціально для гігієни реплікації: збереження інкрементного походження без тримання повних снапшотів вічно.
- Вони лише метадані: створення закладки швидке і дешево по місцю, зазвичай вимірюється як «похибка округлення» порівняно з простором снапшоту.
- Снапшоти фіксують блоки; закладки — ні: закладка не перешкоджає звільненню місця, коли блоки застарівають. Це зроблено навмисно: походження реплікації без роздування утримання.
- Інкрементні відправлення можуть посилатися на закладки (наприклад, відправити від закладки до нового снапшоту), що корисно, коли ви хочете обрізати снапшоти на приймачі.
- Сторона отримувача може зберігати закладку для останнього реплікованого снапшоту, потім видалити цей снапшот і все одно приймати майбутні інкременти, що посилаються на ту точку — за умови, що відправник все ще може їх згенерувати.
- Закладки мають власний простір імен: їх перераховують за допомогою
zfs list -t bookmarkі керують ними окремо від снапшотів. - Багато інструментів реплікації реалізували закладки пізніше за снапшоти, тому старі скрипти і обгортки можуть їх ігнорувати, якщо не налаштовані явно.
- Сучасний OpenZFS підтримує закладки як мейнстрім на поширених платформах, але суміш версій все ще спотикається через прапори функцій і опції потоків send.
Основні механіки: як закладки зберігають інкременти
Ось ментальна модель, яка витримує тиск інциденту:
- Снапшот — це повна, переглядна контрольна точка датасету. ZFS використовує її для обчислення різниць і для забезпечення стабільного вигляду.
- Закладка — це не-переглядна контрольна точка, що посилається на точну точку історії снапшоту (його GUID та пов’язані метадані).
- Інкрементний потік — це перетворення зі стану A у стан B. Для безпеки отримувач має підтвердити, що він наразі в стані A, перш ніж застосовувати перетворення.
Отже, як закладки допомагають, коли щось йде не так?
Уявімо, що на приймачі був снапшот pool/fs@replica_2025-12-01, і ви робили новіші снапшоти та реплікували інкрементно від нього. Якщо хтось видаляє @replica_2025-12-01 на приймачі, ви видалили опору «стан A». З закладкою ви робите так:
- Створюєте закладку
#replica_2025-12-01, яка вказує на GUID цього снапшоту. - За бажанням видаляєте снапшот
@replica_2025-12-01, щоб позбутися захаращення простору імен (і можливо місця, якщо він фіксував блоки). - Продовжуєте отримувати інкременти, що посилаються на той стан, бо ZFS може перевірити «звідки» проти закладки.
Ключове обмеження: відправник має досі мати «звідки» снапшот (або еквівалент), щоб обчислити інкрементні різниці. Закладки не можуть створити дельти з нічого. Вони вирішують проблему спадкоємності на стороні отримувача, а не на стороні відправника.
Жарт №2 (коротко і доречно): Реплікація ZFS — як сімейна терапія: вона працює тільки якщо обидві сторони погоджуються, що сталося минулого разу.
Практичні завдання з командами (і що означає вивід)
Нижче — реальні завдання, які ви можете виконати сьогодні. Вони написані у «голосі рукопису»: команда, приклад виводу й що це означає. Підлаштуйте імена датасетів під своє середовище.
Задача 1: Підтвердити підтримку закладок і здоров’я датасету
cr0x@server:~$ zpool status -x
all pools are healthy
cr0x@server:~$ zfs get -H -o value -s local,received,default all pool/fs | head -n 1
on
Тлумачення: Почніть з питання «чи хворий пул?» перш ніж звинувачувати реплікацію. Якщо пул деградований, ви можете ганятися за симптомами продуктивності через resilver або помилки контрольних сум, а не через закладки.
Задача 2: Перерахувати снапшоти та закладки окремо (не припускайте, що бачите обидва)
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,mountpoint -s creation pool/fs | tail -n 3
pool/fs@replica_2025-12-20 0B 1.20T -
pool/fs@replica_2025-12-21 0B 1.21T -
pool/fs@replica_2025-12-22 0B 1.22T -
cr0x@server:~$ zfs list -t bookmark -o name,createtxg,guid -s createtxg pool/fs | tail -n 3
pool/fs#replica_2025-12-19 19548312 17084256651437522314
pool/fs#replica_2025-12-20 19591288 5721345558355301722
pool/fs#replica_2025-12-21 19634801 15884259650518200641
Тлумачення: Снапшоти і закладки живуть в різних списках. Якщо ваше ПЗ дивиться лише на снапшоти, воно може сказати «немає спільного снапшоту», тоді як ZFS може мати закладку-якор.
Задача 3: Створити закладку з існуючого снапшоту
cr0x@server:~$ zfs bookmark pool/fs@replica_2025-12-22 pool/fs#replica_2025-12-22
Тлумачення: Це основна операція. Ви іменуєте опору реплікації. Ім’я закладки може слідувати вашій конвенції іменування снапшотів; лише пам’ятайте, що використовується #, а не @.
Задача 4: Безпечно видалити старий снапшот після створення закладки (очищення на приймачі)
cr0x@server:~$ zfs destroy pool/fs@replica_2025-12-20
cr0x@server:~$ zfs list -t bookmark pool/fs#replica_2025-12-20
NAME CREATETXG GUID
pool/fs#replica_2025-12-20 19591288 5721345558355301722
Тлумачення: Ви можете видалити снапшот, зберігаючи вказівник спадкоємності. Це хід «зберегти інкременти, коли політика утримання перегрівається».
Задача 5: Перевірити, що на приймачі залишається спільна база після очищення
cr0x@server:~$ zfs get -H -o name,value type pool/fs
pool/fs filesystem
cr0x@server:~$ zfs list -t snapshot,bookmark -o name -s name pool/fs | grep replica_2025-12-20
pool/fs#replica_2025-12-20
Тлумачення: Якір ланцюга присутній, хоча снапшоту немає. Це те, що зберігає можливість майбутніх інкрементів.
Задача 6: Відправити інкрементний потік, використовуючи закладку як «from»
cr0x@sender:~$ zfs send -nv -i pool/fs#replica_2025-12-20 pool/fs@replica_2025-12-22
send from pool/fs#replica_2025-12-20 to pool/fs@replica_2025-12-22 estimated size is 18.4G
cr0x@sender:~$ zfs send -w -i pool/fs#replica_2025-12-20 pool/fs@replica_2025-12-22 | ssh backup1 zfs receive -u -F pool/fs
receiving full stream of pool/fs@replica_2025-12-22 into pool/fs@replica_2025-12-22
received 18.4G stream in 00:06:12 (50.7M/sec)
Тлумачення: Dry-run (-n) каже, чи ZFS розпізнає закладку як дійсну базу і оцінює розмір передачі. Реальний send використовує -w (raw) коли це доречно у вашому середовищі; якщо ви не використовуєте шифрування/raw-потоки, видаліть його.
Задача 7: Діагностика «incremental source is not earlier than destination» (поширений конфлікт)
cr0x@sender:~$ zfs send -i pool/fs@replica_2025-12-22 pool/fs@replica_2025-12-21
cannot send 'pool/fs@replica_2025-12-21': incremental source (pool/fs@replica_2025-12-22) is not earlier than destination (pool/fs@replica_2025-12-21)
Тлумачення: Ви намагаєтеся відправити «назад у часі». Зазвичай це баг скрипта, який вибрав неправильний «останній». Виправіть логіку вибору снапшоту, не застосовуйте грубу силу повним відправленням.
Задача 8: Показати, що приймач думає, що у нього є (снапшоти vs закладки)
cr0x@backup1:~$ zfs list -t snapshot -o name -s creation pool/fs | tail -n 5
pool/fs@replica_2025-12-18
pool/fs@replica_2025-12-19
pool/fs@replica_2025-12-22
cr0x@backup1:~$ zfs list -t bookmark -o name -s createtxg pool/fs | tail -n 5
pool/fs#replica_2025-12-19
pool/fs#replica_2025-12-20
pool/fs#replica_2025-12-21
Тлумачення: Цей приймач втратив снапшоти 20–21, але зберіг закладки. Це нормальний шаблон для «тримати кілька точок відновлення, зберігати багато якорів».
Задача 9: Підтвердити GUID-походження (коли підозрюєте реплікацію в неправильну ціль)
cr0x@sender:~$ zfs get -H -o value guid pool/fs@replica_2025-12-20
5721345558355301722
cr0x@backup1:~$ zfs list -t bookmark -o name,guid pool/fs | grep replica_2025-12-20
pool/fs#replica_2025-12-20 5721345558355301722
Тлумачення: Збіг GUID — сильний доказ, що ви вирівняні по походженню. Якщо вони не збігаються, ви не говорите про ту саму історію, незалежно від того, наскільки схожі імена.
Задача 10: Автоматично перетворити «останній реплікований снапшот» в закладку
cr0x@backup1:~$ last=$(zfs list -H -t snapshot -o name -s creation pool/fs | tail -n 1)
cr0x@backup1:~$ echo "$last"
pool/fs@replica_2025-12-22
cr0x@backup1:~$ zfs bookmark "$last" "${last/@/#}"
Тлумачення: Це типовий шаблон: після кожного успішного receive зафіксуйте отриманий снапшот як закладку, тоді політика утримання може видаляти снапшоти без розриву ланцюга.
Задача 11: Безпечно знищувати закладки (очищення якорів, які більше не потрібні)
cr0x@backup1:~$ zfs destroy pool/fs#replica_2025-12-19
cr0x@backup1:~$ zfs list -t bookmark -o name pool/fs | grep replica_2025-12-19 || echo "bookmark removed"
bookmark removed
Тлумачення: Закладки можуть накопичуватись. Якщо ви зберігаєте по одній закладці на кожний снапшот назавжди, простір імен у підсумку виглядатиме як календар, що вибухнув. Можна обрізати і закладки — робіть це усвідомлено.
Задача 12: Оцінити розмір передачі перед запуском (щоб уникнути несподіваних повних відправлень)
cr0x@sender:~$ zfs send -nv -i pool/fs@replica_2025-12-21 pool/fs@replica_2025-12-22
send from pool/fs@replica_2025-12-21 to pool/fs@replica_2025-12-22 estimated size is 1.7G
Тлумачення: Dry-run з -n — дешевий саніті-чек. Якщо ви очікуєте 1–3 GB, а він каже 1.2 TB, зупиніться і з’ясуйте чому, перш ніж наситити ваш канал реплікації.
Задача 13: Перевірити тиск по місцю на приймачі і чи насправді видалення звільняють місце
cr0x@backup1:~$ zfs list -o name,used,avail,refer,mountpoint pool
NAME USED AVAIL REFER MOUNTPOINT
pool 61.2T 3.8T 192K /pool
cr0x@backup1:~$ zfs get -o name,property,value -s local,received usedbysnapshots,usedbydataset pool/fs
NAME PROPERTY VALUE
pool/fs usedbysnapshots 9.4T
pool/fs usedbydataset 51.6T
Тлумачення: Якщо ви видаляєте снапшоти бо закінчується місце, виміряйте, чи насправді снапшоти є винуватцями. Закладки не звільнять місце (вони не тримають блоки), але очищення снапшотів може — якщо тільки клоні, холди або активні посилання не фіксують блоки.
Задача 14: Прийом з обережністю: розумійте, що робить -F перед тим, як застосовувати
cr0x@backup1:~$ ssh sender zfs send -i pool/fs#replica_2025-12-20 pool/fs@replica_2025-12-22 | zfs receive -u -F pool/fs
Тлумачення: zfs receive -F може відкотити цільовий датасет, щоб узгодитись з вхідним потоком, знищуючи новіші снапшоти на приймачі. Іноді це саме те, що вам потрібно для суворої репліки — а іноді це крок, що суттєво шкодить, якщо на приймачі також є локальні снапшоти для відновлення. Вирішіть, що ви робите: строгий реплікатор або бекуп з локальною історією.
Три міні-історії з корпоративного світу
Міні-історія 1: Інцидент через неправильне припущення
Припущення було простим і неправильним: «Якщо на приймачі є снапшот з останньою назвою, інкременти завжди працюватимуть». Скрипт реплікації писав компетентний інженер, який ніколи не бачив, як політики утримання снапшотів стикаються з графіком реплікації. Вони використовували імена снапшотів з датами, сортували їх лексикографічно, брали останній і називали його базою. Це працювало місяцями.
Потім команда сховища змінила іменування, додавши суфікс часового поясу через аудит, який попросив «явний UTC». Відправник почав створювати @replica_2025-12-01Z, а приймач ще мав старіші снапшоти без суфіксу. Скрипт — все ще сортував за ім’ям — вибрав неправильну базу. ZFS зробив правильну річ і відмовився прийняти інкремент із повідомленням, що звучало як філософська суперечка: потік не відповідає поточному стану датасету.
Оператори зробили те, що роблять під тиском: спробували з -F, що відкотило приймач і видалило тиждень локальних «про всяк випадок» снапшотів, які не були частиною реплікації. Тепер у них було дві проблеми: реплікація все ще періодично падала, і служба підтримки отримувала запити на відновлення, які не можна було виконати.
Виправлення не було героїчним. Вони перестали вважати імена істинними і почали використовувати походження через GUID. Приймач фіксує закладку останнього успішно прийнятого снапшоту (за іменем, так, але перевірену через GUID), а відправник використовує цю закладку як інкрементну базу. Коли знову змінилася конвенція іменування (бо конвенції іменування завжди знову змінюються), реплікація не переймалася. Вона просто продовжувала відправляти дельти з правильної історії.
Міні-історія 2: Оптимізація, що зіграла злий жарт
Це почалося з гарної ідеї: «Ми можемо заощадити місце, агресивно обрізавши снапшоти на бекап-цілі. Це репліка, а не музей». Вони зменшили збереження снапшотів на приймачі з 30 днів до 3 днів. Графік місця виглядав чудово. Сповіщення зберігання припинили турбувати. Усі вітали одне одного на зустрічі, яка мала бути листом.
Два тижні по тому вікно обслуговування мережі затягнулося, і реплікація призупинилася на 36 годин. Це не драматично саме по собі. Драма була в роботі утримання: вона видалила останній спільний снапшот на приймачі під час паузи. Коли реплікація відновилася, відправник намагався послати інкременти від снапшоту, якого приймач більше не мав. ZFS відмовив у прийомі, правильно, бо не міг підтвердити базу.
Наступна «оптимізація» команди — примусити повні відправлення для цього датасету «поки не стабілізується». Повні відправлення стабілізували процес: вони перетворили його на нічну передачу, яка наштовхувалася на робочий день і насичувала шлях запису масиву. Почалися скарги на затримки з боку баз даних. Система бекапу непомітно стала проблемою продуктивності продакшна.
Висновок постмортему практично нудний: вони мали б використовувати закладки на приймачі весь час. Закладки дозволили б обрізати снапшоти без розриву інкрементного ланцюга. Вони переробили конвеєр так: після кожного успішного receive приймач створює закладку для цього снапшоту, потім видаляє снапшоти відповідно до локальної політики утримання. Наступного разу, коли пауза сталася, реплікація відновилася інкрементно, ніби нічого не трапилось. «Оптимізація» стала реальною оптимізацією замість повільного інциденту.
Міні-історія 3: Нудна, але правильна практика, що врятувала день
Інша компанія, інша атмосфера. Їхня команда зберігання була алергічна до хитростей. Вони писали рукописи. Вони щоквартально практикували відновлення. У них була непоказна правило: «Кожен реплікований датасет повинен мати машинно-зчитувану «останню реплікацію» на приймачі, незалежно від утримання снапшотів». Тим ярликом була закладка з фіксованою назвою, наприклад pool/fs#last-received, яку оновлювали після кожної успішної реплікації.
Одного дня інженер, що розслідував використання місця, запустив скрипт очищення в неправильній вкладці історії shell. Він видалив блок старих снапшотів на приймачі — саме ту людську помилку, якої ніхто не визнає, поки не покажеш журнали аудиту. Команда помітила це, бо моніторинг сповістив «збільшення відставання реплікації» і «відсутня база для інкременту».
Вони не панікували. Рукопис говорив: перевірити фіксовану закладку; підтвердити GUID збігається з очікуваною базою; відновити реплікацію з закладки. Закладка була на місці, все ще вказуючи на останній отриманий стан. Їм не потрібні були видалені снапшоти, щоб продовжити інкременти; їм потрібен був лише вказівник походження. Реплікація відновилася.
Було дещо наслідків, але вони були обмеженими: менше точок відновлення на приймачі на той тиждень і невелика проблема з відповідністю політикам. Основна система залишилася здоровою. Ніякого WAN-катастрофи, ніякого повного повторного відправлення, ніякої «чому база даних повільна» таємниці. Ось як виглядає хороша операційна гігієна: не запобігаючи кожній помилці, а роблячи помилки дешевими.
Швидкий план діагностики
Це «у вас 10 хвилин до наступної наради і 30 хвилин до насичення каналу» чекліст. Він упорядкований, щоб швидко знайти вузьке місце і уникнути руйнівних дій.
1) Спочатку: чи це взагалі проблема ланцюга реплікації, чи проблема системи?
cr0x@backup1:~$ zpool status -x
all pools are healthy
cr0x@backup1:~$ zfs list -o name,used,avail pool
NAME USED AVAIL
pool 61.2T 3.8T
Тлумачення: Якщо пул деградований, майже повний або активно робить resilver, реплікація буде повільною або падатиме так, ніби це помилки send/receive. Виправте здоров’я пулу спочатку.
2) Далі: яка саме помилка від send/receive?
cr0x@backup1:~$ ssh sender zfs send -nv -i pool/fs@replica_2025-12-20 pool/fs@replica_2025-12-22 | zfs receive -nvu pool/fs
cannot receive incremental stream: incremental source (replica_2025-12-20) does not exist
Тлумачення: Це відсутня база на стороні отримувача. Саме тут закладки зазвичай вирішують проблему — якщо вони були створені заздалегідь.
3) Третє: чи має приймач закладку, що відповідає GUID базового снапшоту відправника?
cr0x@sender:~$ zfs get -H -o value guid pool/fs@replica_2025-12-20
5721345558355301722
cr0x@backup1:~$ zfs list -t bookmark -o name,guid pool/fs | grep 5721345558355301722
pool/fs#replica_2025-12-20 5721345558355301722
Тлумачення: Якщо у вас є закладка з правильним GUID, ви, ймовірно, зможете відновити інкременти без відтворення снапшотів на приймачі.
4) Четверте: якщо інкременти валідні, чому це повільно?
cr0x@sender:~$ zpool iostat -v 1 3
capacity operations bandwidth
pool alloc free read write read write
---------------------------- ----- ----- ----- ----- ----- -----
pool 48.1T 12.3T 210 980 92.1M 311M
raidz2-0 48.1T 12.3T 210 980 92.1M 311M
sda - - 30 140 12.9M 42.7M
sdb - - 28 139 12.3M 42.0M
...
Тлумачення: Якщо записи на приймачі або читання на відправнику зашкалюють, ваш вузьке місце — сховище, а не логіка ZFS. Якщо ні, то, можливо, мережа, CPU (сжаття/шифрування) або серіалізована задача реплікації.
5) П’яте: перевірте логіку вибору «бази» і «цілі» у вашому інструменті
cr0x@sender:~$ zfs list -H -t snapshot -o name -s creation pool/fs | tail -n 5
pool/fs@replica_2025-12-18
pool/fs@replica_2025-12-19
pool/fs@replica_2025-12-20
pool/fs@replica_2025-12-21
pool/fs@replica_2025-12-22
Тлумачення: Правильна база — «останній успішно реплікований снапшот», а не «найновіший снапшот за ім’ям» і не «вчора». Закладки полегшують явне збереження цього стану.
Поширені помилки, симптоми та виправлення
Помилка 1: Вважати закладки снапшотами
Симптом: Хтось намагається змонтувати або переглянути закладку, або очікує, що з неї можна відновити файл.
Виправлення: Закладки — лише маркери походження. Якщо вам потрібні точки відновлення, потрібні снапшоти (або клоні) на приймачі. Використовуйте закладки, щоб зберегти інкременти під час очищення снапшотів, а не замінюйте ними снапшоти повністю.
Помилка 2: Бронювання лише на приймачі, а очищення на відправнику
Симптом: Приймач має правильні закладки, але відправник помиляється з «cannot send incremental: snapshot does not exist» або ваше ПЗ все одно йде на повні відправлення.
Виправлення: Відправник має зберігати достатньо снапшотів, щоб згенерувати інкременти. Закладки на приймачі допомагають приймачу приймати потоки; вони не допомагають відправнику обчислити дельти. Узгодьте політики утримання: відправник зберігає принаймні «останню репліковану» базу плюс те, що потрібно для вашого RPO.
Помилка 3: Використовувати zfs receive -F як рефлекс
Симптом: Реплікація «знову працює», але приймач втратив новіші снапшоти або локальні точки відновлення. Потім запити на відновлення не виконуються.
Виправлення: Вирішіть, чи ціль — сувора репліка (відкат прийнятний), чи репозиторій бекапів (локальна історія має зберігатись). Якщо це останнє, уникайте -F або використовуйте окремі датасети для репліки і для бекапів.
Помилка 4: Приписувати походження іменам снапшотів
Симптом: На обох кінцях є снапшоти з однаковими іменами, але інкременти все одно падають з повідомленням «does not match incremental source».
Виправлення: Перевірте GUID. Імена можуть співпадати, бути відтворені або застосовані до непов’язаних датасетів після відкатів. Використовуйте zfs get guid для підтвердження походження.
Помилка 5: Завдання утримання, що видаляють останню спільну базу першою
Симптом: Інкременти падають відразу після очищення, зазвичай після паузи або накопичення беклогу реплікації.
Виправлення: Політика утримання має захищати якір бази. Загальний підхід: підтримувати закладку з фіксованою назвою для «останніх отриманих» і/або ніколи не видаляти снапшоти новіші за цей якір, поки якір не буде зміщено вперед.
Помилка 6: Надмірне створення закладок без стратегії очищення
Симптом: Тисячі закладок захаращують операції; перелік займає більше часу; інструменти повільні або заплутані.
Виправлення: Зберігайте закладки для потрібного вікна (наприклад, «останні 60 якорів») або тримайте фіксовану закладку «last-received» плюс періодичні «тижневі якорі». Обрізайте інші цілеспрямовано.
Помилка 7: Плутати «поліпшення використання місця» з «покращенням безпеки реплікації»
Симптом: Ви обрізаєте снапшоти, щоб звільнити місце, реплікація пізніше ламається, і хтось пропонує «просто обрізати більше».
Виправлення: Розділіть питання: використовуйте закладки для походження реплікації, снапшоти — для точок відновлення, і моніторьте usedbysnapshots, щоб зрозуміти, що насправді фіксує простір.
Контрольні списки / покроковий план
Це практичний план, який ви можете прийняти без переписування всієї системи реплікації. Він передбачає, що ви вже робите реплікацію на основі снапшотів і хочете посилити її за допомогою закладок.
Контрольний список A: Впровадити закладки «останньої реплікації» на приймачі
- Після кожного успішного receive створюйте/оновлюйте закладку з фіксованою назвою, що вказує на отриманий снапшот.
- Залиште існуючу політику утримання снапшотів для відновлень, але дозвольте їй бути агресивнішою, бо закладка захищає інкрементну спадкоємність.
- Переконайтесь, що ваш скрипт/інструмент реплікації може використовувати закладки як бази при генерації або перевірці інкрементів.
cr0x@backup1:~$ snap="pool/fs@replica_2025-12-22"
cr0x@backup1:~$ zfs destroy -r pool/fs#last-received 2>/dev/null || true
cr0x@backup1:~$ zfs bookmark "$snap" pool/fs#last-received
cr0x@backup1:~$ zfs list -t bookmark -o name,guid pool/fs#last-received
NAME GUID
pool/fs#last-received 15884259650518200641
Тлумачення: Стабільна назва закладки дає стабільну «базу», незалежно від очищення снапшотів або змін у іменуванні.
Контрольний список B: Зробіть утримання усвідомленим щодо реплікації (уникнути відрізання гілки під собою)
- Визначте снапшот, що відповідає
#last-received(або зберігайте як снапшот, якщо віддаєте перевагу). - Видаляйте старіша снапшоти першими; ніколи не видаляйте опорний снапшот, поки якір не переміститься вперед.
- Обрізайте закладки також, але зберігайте принаймні фіксовану закладку.
cr0x@backup1:~$ zfs list -t bookmark -o name,createtxg -s createtxg pool/fs | tail -n 5
pool/fs#replica_2025-12-20 19591288
pool/fs#replica_2025-12-21 19634801
pool/fs#replica_2025-12-22 19678110
pool/fs#last-received 19678110
Тлумачення: Якщо #last-received відстежує останній, ваше утримання має уникати видалення всього, що «новіше ніж» його TXG-еквівалент. На практиці ви захищаєте найновіший реплікований снапшот(и) і якірні закладки.
Контрольний список C: Процедура відновлення, коли снапшот на приймачі було видалено
- Зупиніть автоматичні повторні спроби реплікації (вони можуть викликати відкат або повні відправлення).
- Перевірте, чи має приймач закладку з GUID бази.
- Якщо має — відновіть інкременти, використовуючи цю закладку як базу.
- Якщо ні — вирішіть між: відтворити спільну базу (рідко можливо), відкотити ціль (небезпечне), або повний ресенд (дорого, але надійно).
cr0x@sender:~$ base="pool/fs@replica_2025-12-20"
cr0x@sender:~$ next="pool/fs@replica_2025-12-22"
cr0x@sender:~$ zfs send -nv -i "$base" "$next"
send from pool/fs@replica_2025-12-20 to pool/fs@replica_2025-12-22 estimated size is 18.4G
cr0x@backup1:~$ zfs list -t bookmark -o name,guid pool/fs | grep "$(ssh sender zfs get -H -o value guid "$base")"
pool/fs#replica_2025-12-20 5721345558355301722
Тлумачення: Це підхід «доведи, перш ніж діяти». Якщо ви можете зіставити GUID, зазвичай можна відновити інкрементний потік без руйнівних опцій receive.
Контрольний список D: Перевірка продуктивності для інкрементних потоків
- Оцінюйте розмір з
zfs send -nv. - Перевіряйте пропускну здатність читання на відправнику і запису на приймачі (
zpool iostat). - Підтверджуйте навантаження CPU, якщо використовуєте стиснення або шифрування потоків.
- Переконайтесь, що датасет не трясеться іншими навантаженнями під час реплікації.
Часті запитання
1) Чи можуть закладки замінити снапшоти для бекапів?
Ні. Закладки не містять переглядна точки часу файлової системи і не дозволяють безпосередньо відновлювати файли. Вони маркери походження реплікації, а не точки відновлення.
2) Чи споживають закладки місце?
Вони споживають невелику кількість метаданих. Вони не тримають блоки даних у житті так, як це роблять снапшоти, тому не викликатимуть того ж ефекту утримання місця.
3) Якщо я видаляю снапшот, але зберігаю закладку, чи можу я все ще зробити інкремент з цієї точки?
На боці приймача — так: він все ще може перевіряти інкременти, що посилаються на ту базу (закладку). На боці відправника вам все ще потрібен дійсний базовий снапшот (або еквівалентне походження), щоб згенерувати інкрементну дельту.
4) Яка операційна найкраща практика: одна закладка на снапшот чи фіксована закладка?
У виробництві фіксована закладка на кшталт #last-received — це нудна, але правильна відправна точка. Деякі команди додають періодичні «якорні» закладки (тижневі/місячні) для безпеки, але уникайте необмеженого зростання.
5) Чому я все ще отримую «does not exist», коли на приймачі є закладка?
Тому що базове посилання потоку має збігатись з тим, що є на приймачі. Якщо ваша команда send посилається на @snapname, а на приймачі є тільки #snapname, це може бути прийнятно — але лише якщо метадані потоку send/receive узгоджуються. Перевірте GUID і розгляньте використання закладки явно як бази у команді send, де це підтримується.
6) Чи безпечні закладки при відкатах і пересозданні датасетів?
Вони безпечні в тому сенсі, що залишаються точними посиланнями на історію датасету, з якого їх створили. Вони не безпечні в сенсі «будуть застосовні до датасету, який був знищений і пересозданий з тим самим ім’ям». Використовуйте перевірку GUID, щоб уникнути реплікації в непов’язаний датасет.
7) Чи допомагають закладки, якщо відправник втратив снапшоти через очищення?
Не безпосередньо. Якщо відправник більше не має базового снапшоту, потрібного для обчислення інкременту, можливо, доведеться робити повне відправлення або інший підхід відновлення. Закладки головним чином захищають здатність приймача приймати інкременти без збереження старих снапшотів.
8) Чи варто використовувати zfs receive -F щоб «виправити» помилки інкрементів?
Тільки якщо ціль — сувора репліка і ви погоджуєтеся на втрату новіших снапшотів на приймачі. Якщо приймач також служить репозитарієм бекапів з локальним утриманням, -F може непомітно видалити саме ті точки відновлення, які вам потрібні.
9) Як закладки взаємодіють з шифруванням і raw-потоками?
Закладки відстежують походження незалежно від того, чи відправляєте ви raw-потоки, але шифрування додає обмеження: ключі і властивості мають бути сумісні з тим, як ви приймаєте. Використовуйте dry-run і тестуйте шлях відновлення, а не лише «реплікація вдалася».
10) Яка найпростіша «можу впровадити сьогодні» версія?
На приймачі: після кожного успішного receive створюйте/оновлюйте #last-received, що вказує на отриманий снапшот. Потім налаштуйте утримання снапшотів так, щоб воно ніколи не видаляло найновіший реплікований снапшот, поки закладка не переміститься вперед.
Висновок
Закладки ZFS не гламурні, і в цьому їх суть. Це невелика метаданна функція, яка перетворює клас збоїв реплікації на рутинне технічне обслуговування. Коли снапшоти зникають — випадково, через утримання або через людський фактор — закладки можуть зберегти інкрементну спадкоємність на стороні приймача, а це часто означає уникнення дорогих повних пересилань і каскадного падіння продуктивності, що за цим слідує.
Якщо ви запускаєте реплікацію ZFS у виробництві, ставтесь до закладок як до ременів безпеки: ви не встановлюєте їх тому, що плануєте аварію. Ви встановлюєте їх, бо знайомі з людьми, крон та квартальними замороженнями змін.