Зберігання в Linux: опція монтування, що може спотворити ваші очікування

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

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

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

Опція монтування, що спотворює очікування: async (і її «друзі»)

Якщо змусити мене вибрати одну опцію монтування, яка регулярно перетворює продакшен на імпровізований театр, це async.

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

На локальних файлових системах рідко явно монтують з async, бо зазвичай це поведінка кешу сторінок: записи буферизуються і скидаються пізніше. Небезпека виникає, коли люди поєднують припущення про буферизований ввід/вивід з опціями, що послаблюють порядок і стійкість, а потім вважають, що fsync() їх врятує. Іноді рятує. Іноді рятує «віртуозно».

Очікування, яке спотворюється

Більшість інженерів—розумних, працюючих інженерів—припускають одне з цього:

  • «Якщо мій застосунок отримує успішний код повернення від write(), дані в безпеці.»
  • «Якщо мій застосунок викликає fsync(), дані обовʼязково в безпеці.»
  • «Якщо файловa система журналює, вона не може втратити зафіксовані дані.»

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

Невеликий набір регуляторів, що викликають найбільше жалю

Ось звичні підозрювані:

  • NFS async на боці сервера (і іноді вибір клієнта), що підтверджує до коміту.
  • ext4 data=writeback — послаблення впорядкування між журналюванням метаданих і даними файлу.
  • barrier=0 / nobarrier — відключення flush/FUA, що забезпечують порядок через летючі кеші запису.
  • Вимкнення write barriers через політики пристрою/контролера («швидше») при довірі до fsync() («це безпечно»).
  • commit= — збільшення максимального часу, протягом якого метадані можуть залишатися в ОЗП перед фіксацією в журналі (більше вікно болю при відключенні живлення).

Окремо кожен може бути виправданий у певному контексті. Разом вони — груповий проєкт, де оцінка «неповоротно».

Жарт 1: «Найшвидша» система збереження — та, що нічого не записує. Вона також дуже надійна, бо нічого й втратити.

Стійкість даних: чого ви очікуєте проти того, що гарантує ядро

IO в Linux має шари, і кожен шар має свої припущення.

У загальному: застосунки пишуть у кеш сторінок; ядро планує writeback; файлова система оновлює метадані і, можливо, журналює їх; блочний шар ставить запити в чергу; пристрій може брехати з летючим кешем; і тоді відбувається фізика. Або ні. Неприємні сюрпризи майже завжди в «можливо».

Три вислови, що звучать схоже, але різні

  • Дані видимі: інший процес може їх прочитати (з кешу), навіть якщо вони не на диску.
  • Дані стійкі: вони переживуть збій/втрату живлення.
  • Файлова система консистентна: після збою метадані відновлюються й структури коректні.

Журналювання насамперед про консистентність метаданих. Деякі режими додають сильніші гарантії щодо даних файлу. Але «консистентна» не означає «містить найсвіжіші успішні записи». Це означає «можна змонтувати без проведення уїк-енду з fsck».

Де знаходиться fsync() — і де ні

fsync(fd) просить ядро скинути брудні сторінки та повʼязані метадані для цього файлу на стабільне сховище. Це контракт. Проблема — що означає «стабільне сховище», коли:

  • диск має летючий write cache і ігнорує flush-і,
  • RAID-контролер переставляє записи і бреше про стан батареї,
  • гіпервізор транслює flush-и як «best effort»,
  • мережевий сервіс підтверджує до коміту.

Також потрібно викликати його правильно. Багато систем вимагають fsync() також для директорії після створення/перейменування файлів, щоб гарантувати, що запис у директорію стійкий. Файл може бути в безпеці, а імʼя, що на нього вказує — ні. Це дуже по-Linux’івськи: дані існують, але знайти їх неможливо.

Цитата, яку варто записати в книжку для on-call

«Надія — це не стратегія.» — Gen. Gordon R. Sullivan

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

Режими журналювання, що змінюють реальність: ext4 data=ordered, writeback, journal

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

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

  • data=ordered (за замовчуванням у багатьох дистрибутивах): метадані журналюються. Дані файлу не журналюються, але ext4 намагається гарантувати, що блоки даних файлу записані на диск перед тим, як метадані, які на них вказують, будуть зафіксовані. Після збою ви не повинні бачити старий мотлох у нових блоках файлу (в більшості випадків). Ви все ще можете втратити недавні записи, але це відбувається більш передбачуваним способом.
  • data=writeback: метадані журналюються, але ext4 не примушує порядок між записами даних і комітами метаданих. Після збою метадані можуть вказувати на блоки, вміст яких застарілий. Це режим «файлова система консистентна, але ваші файли дивують».
  • data=journal: і дані, і метадані журналюються. Це сильніше щодо консистентності після збою, зазвичай повільніше і не безкоштовно. Також збільшується write amplification: дані пишуться в журнал, а потім у фінальне місце.

Чому data=writeback спотворює очікування

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

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

Отже, чи ніколи не використовувати data=writeback?

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

Барʼєри, write cache і чому «стабільне сховище» — це політика

Барʼєри (та їхні сучасні еквіваленти: flush-и і FUA) — це огорожі, які говорять пристроям «цей порядок важливий». Без них стек зберігання стає «скринькою для пропозицій».

Write cache: великий прискорювач і велика брехуха

Більшість дисків (і багато віртуальних блочних пристроїв) використовують кеш із write-back. Записи завершуються швидко, бо спочатку потрапляють у летючу памʼять. Якщо живлення втрачається, ці записи випаровуються. Якщо пристрій підтверджує записи з летючого кеша як «завершені» і ігнорує запити flush, ОС не зможе гарантувати стійкість, якою б щиро не викликала вона fsync().

Що насправді означає barrier=0 / nobarrier

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

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

Цікаві факти й історичний контекст (у зберігання є квитанції)

  1. ext3 запровадив журналювання в мейнстримі Linux на початку 2000-х, розширивши ext2, переважно щоб скоротити тривалість fsck після збою.
  2. delayed allocation в ext4 підвищив продуктивність, але також змінив поведінку після збою; стало важливіше правильно викликати fsync().
  3. Колись write barriers були опційними й дорогими на деяких стеках; адміністратори іноді відключали їх після читання гайдів з тюнінгу продуктивності для іншого апаратного покоління.
  4. Деякі ранні споживчі SSD прославилися ігноруванням flush-команд, роблячи поведінку «sync» більш бажаною, ніж реальною.
  5. XFS віддавав перевагу масштабованості та паралелізму і історично рекомендував «використовуйте правильне обладнання з захистом від втрати живлення» замість того, щоб прикидатися, що може виправити брехливі пристрої.
  6. Дебати NFS sync vs async старі; адміністратори міняли надійність на швидкість ще до того, як «хмара» стала професією.
  7. POSIX дозволяє буферизовані записи; успішний write() не означає персистентність, і це дивує інженерів щонайменше один раз у карʼєрі.
  8. Бази даних додавали налаштування стійкості (наприклад, відключення fsync), бо користувачі просили швидкість — а потім звинувачували базу даних у наслідках.

Файлові системи: особливі підводні камені (ext4, XFS, btrfs)

ext4: безпечні налаштування за замовчуванням, небезпечні підміни

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

Також звертайте увагу на:

  • commit=: збільшує інтервал між комітами журналу. Більші значення збільшують вікно втрати метаданих після збою. Чудово для бенчмарків; гірше для таймінгів інцидентів.
  • errors=remount-ro: звична опція за замовчуванням; хороша для безпеки, але може перетворити приховану проблему з диском на раптову кореневу файлову систему в режимі лише для читання. Це функція, а не зрада.

XFS: надійний, але фізику все одно потрібно поважати

XFS відмінно працює при паралельних навантаженнях і великих файлових системах. Воно використовує журналювання для метаданих; дані не журналюються. XFS покладається на порядок записів і поведінку flush-ів для забезпечення консистентності.

Типова «корупція очікувань» з XFS менше повʼязана з прапорами монтування і більше з тим, що люди припускають, що підлеглий пристрій поважає flush-и. Якщо ви запускаєте XFS на стеку, який бреше про flush-и, після збою ви отримаєте неконсистентності, що виглядають як «XFS зʼїв мої дані», а реальний винуватець — контролер, що підтвердив запис, який не зміг зберегти.

btrfs: контрольні суми, copy-on-write і інші компроміси

btrfs приносить сквозні контрольні суми та семантику copy-on-write, що змінює спосіб виявлення корупції. Воно може виявляти тиху корупцію, що добре. Воно також може збільшувати обʼєм записів і поводитись відмінно при певних навантаженнях. Невідповідності очікувань тут частіше про тюнінг продуктивності (стискання, CoW для баз даних), ніж про базову семантику стійкості. Все одно: опції монтування важливі.

Мережеве сховище: NFS і спокуслива брехня async

Якщо локальне сховище складне, мережеве — складніше і ще з латентністю.

NFS сервер async: швидкі підтвердження, повільні жалюзі

У NFS найнебезпечнішою «опцією монтування» часто є не опція клієнта — а параметр експорту сервера async. З async сервер може відповісти клієнту «OK» на запис до того, як зафіксував його на стабільному сховищі. Якщо сервер NFS впаде, клієнт вірить, що запис був стійким, бо отримав підтвердження. Реальність інша.

Іноді команди виправдовують async тим, що «сервер має UPS». Класно. UPS — це план живлення, а не доказ коректності. Kernel panic, баг прошивки, відмова подвійного живлення, людська помилка: вони не питають у вашого UPS дозволу.

Жарт 2: «Ми змонтували async заради швидкості» — це еквівалент зберігання фрази «я зняв датчик диму, бо він був гучним».

NFS sync — не безкоштовний, але чесний

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

Три корпоративні міні-історії з фронту зберігання

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

Середня компанія вела внутрішнє репозиторій артефактів: бінарники, логи збірки та маніфести розгортання. Нічого «критичного» — допоки не стало. Команда мігрувала його на новий кластер ВМ і підʼєднала швидкий мережевий блочний пристрій. У плані міграції була швидка правка монтування: data=writeback на ext4, бо бенчмарк показав помітне поліпшення при паралельних завантаженнях.

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

Потім хост гіпервізора впав під час патчу. VM перезавантажилася чисто. ext4 відтворив журнал. Файлова система змонтувалася без скарг. Сервіс запустився. Всі видихнули.

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

Виправлення не було героїчним. Вони перемонтували з data=ordered, забезпечили правильну роботу fsync у шляху застосунку і додали перевірку цілісності критичних артефактів після збою. Урок засвоєно: семантика зберігання не підлягає торгам лише тому, що ваш сервіс не «клієнтський».

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

Аналітична платформа мала гарячий шлях ingеst, що писав у локальний XFS на NVMe. Під час пікових годин затримки стрибали. Хтось зробив класичний крок: підчепив опції монтування і налаштування I/O scheduler у продакшені, бо графіки кричали. Вони відключили барʼєри (конкретна опція залежала від ядра та файлової системи) і змінили поведінку комітів, щоб рідше робити метадані.

Це спрацювало. Затримки знизилися. Дашборди заспокоїлись. Зміна закріпилася в образі, бо успіх заразний.

Через тижні стійка втратила живлення у стійці. Вузли повернулись. Більшість сервісів відновилися. Частина вузлів встала з повідомленнями «replayed journal» і потім тихо обслуговувала часткові дані ingest. Платформа не впала; вона деградувала у спосіб гірший за падіння: генерувала неповні аналітичні дані, що виглядали правдоподібно.

Постмортем був незручним, бо ніхто нічого не «ламав». Оптимізація зробила саме те, для чого була призначена: підвищила швидкість, послабивши припущення про порядок. Відкат став необхідним, бо їхній pipeline покладався на fsync-подібну поведінку від компонента, який не виконував fsync у всіх потрібних місцях. Твік монтування розширив вікно, у якому відсутні flush-и мали значення.

Вони відкотили зміни, додали явні межі стійкості в компонентах ingest і — що важливо — ввели chaos-тест, що симулює різке відключення живлення для staging-кластера. Налаштування продуктивності не припинилися. Вони просто перестали бути стрибком віри.

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

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

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

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

Вони відстежили це до оновлення прошивки сховища, що змінило поведінку flush-ів при певних глибинах черги. Нічого в логах ОС не кричало. Файлова система виглядала чистою. Тільки тест на стійкість спіймав це. Вони заблокували розгортання прошивки, перейшли на пристрої з належним захистом від втрати живлення для того рівня і залишили нудну політику в силі.

Коли в дата-центрі справжня подія живлення сталася, їхній сервіс був тим, якому не потрібна була екстрена кімната кризового реагування. Не тому, що вони були розумніші. Тому що вони були впертіші щодо семантики.

Швидкий план діагностики

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

Спочатку: підтвердіть, що ви фактично змонтували й експортували

  • Перевірте поточні опції монтування (findmnt, /proc/mounts).
  • Якщо задіяно NFS, перевірте опції експорту сервера (exportfs -v) і опції монтування клієнта (nfsstat -m).

Друге: валідируйте ланцюг стійкості (flush-и і write cache)

  • Чи ввімкнено write cache блочного пристрою? Чи він захищений?
  • Чи підтримуються і виконуються flush-и (настільки, наскільки можна зробити висновок)?
  • Чи відключені барʼєри/flush-и на рівні файлової системи або device-mapper?

Третє: визначте, чи це проблеми з консистентністю метаданих чи з порядком записів

  • Проблеми з метаданими: помилки монтування, цикли відтворення журналу, відсутні іноди, помилки файлової системи.
  • Проблеми з порядком: файлова система монтується чисто, але порушені інваріанти на рівні застосунку (відсутні останні транзакції, неправильний вміст файлів за правильними іменами).

Четверте: відтворіть контрольований тест аварійного завершення (якщо можливо)

  • Запустіть невелике навантаження write+fsync.
  • Змусьте некоректне завершення роботи.
  • Перевірте, що пережило і що ні.

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

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

Task 1: Подивіться, що фактично змонтовано (не те, що ви вважаєте в fstab)

cr0x@server:~$ findmnt -no SOURCE,TARGET,FSTYPE,OPTIONS /var/lib/postgresql
/dev/nvme0n1p2 /var/lib/postgresql ext4 rw,relatime,errors=remount-ro,data=ordered

Значення: Це правда для працюючого ядра. Тут ext4 у data=ordered, що загалом безпечніший стандарт.

Рішення: Якщо ви бачите data=writeback, nobarrier або незвично великий commit= на шляху, що вимагає стійкості, позначте це і плануйте зміну.

Task 2: Перевірте /proc, щоб виявити bind-монти та сюрпризи

cr0x@server:~$ grep -E ' /var/lib/postgresql ' /proc/mounts
/dev/nvme0n1p2 /var/lib/postgresql ext4 rw,relatime,errors=remount-ro,data=ordered 0 0

Значення: Та сама історія; корисно, коли findmnt відфільтровано інструментами.

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

Task 3: Перегляньте дефолти superblock ext4 і фічі (стан журналу тощо)

cr0x@server:~$ sudo tune2fs -l /dev/nvme0n1p2 | egrep 'Filesystem features|Default mount options|Filesystem state|Journal'
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum
Default mount options:    user_xattr acl
Filesystem state:         clean
Journal features:         journal_incompat_revoke journal_64bit journal_checksum_v3

Значення: Підтверджує наявність журналювання (has_journal) і контрольних сум метаданих (metadata_csum), що допомагає виявляти певні корупції.

Рішення: Якщо після інциденту файлову систему неможливо змонтувати чисто, заплануйте вікно офлайн fsck. Якщо журналювання відсутнє на томі, який ви вважали журналюваним — розглядайте це як проєктну помилку.

Task 4: Перевірте погляд ядра на відтворення журналу і попередження ext4

cr0x@server:~$ dmesg -T | egrep -i 'ext4|jbd2|xfs|btrfs' | tail -n 20
[Tue Feb  4 10:42:12 2026] EXT4-fs (nvme0n1p2): mounted filesystem with ordered data mode. Quota mode: none.
[Tue Feb  4 10:42:12 2026] EXT4-fs (nvme0n1p2): re-mounted. Opts: (null)

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

Рішення: Якщо бачите повторні відтворення журналу або «Errors detected», припускайте проблеми з IO або небезпечне кешування. Ескалюйте до команди апаратного/віртуалізаційного рівня і зменшіть ризикові опції запису.

Task 5: Перевірте налаштування write cache диска (і не робіть безпечних припущень)

cr0x@server:~$ sudo hdparm -W /dev/nvme0n1
/dev/nvme0n1:
 write-caching =  1 (on)

Значення: Write cache увімкнено. Це нормальне явище, але піднімає питання: чи захищений він від втрати живлення, чи батарейкою-кешем, чи контролером, що виконує flush-и?

Рішення: Якщо це комерційне обладнання без PLP і вам важлива стійкість, забезпечте барʼєри/flush-и і розгляньте зміну пристроїв для критичних шарів.

Task 6: Перевірте наявність летючого кешу й підтримку flush-ів на блоці

cr0x@server:~$ lsblk -D -o NAME,ROTA,DISC-GRAN,DISC-MAX,DISC-ZERO
NAME    ROTA DISC-GRAN DISC-MAX DISC-ZERO
nvme0n1    0      512B       2G         0

Значення: Підтримка discard і гранулярність. Не є прямим сигналом стійкості, але допомагає зрозуміти клас пристрою й поведінку.

Рішення: Використовуйте як підказку: SSD/NVMe зазвичай мають write caching і внутрішній FTL; налаштовуйте і валідируйте відповідно, особливо щодо семантики flush-ів.

Task 7: Шукайте відключені барʼєри/flush-и в опціях монтування

cr0x@server:~$ findmnt -no TARGET,OPTIONS | egrep -i 'barrier|nobarrier|data=writeback|commit='
/mnt/fast rw,relatime,data=writeback,commit=60

Значення: Цей монт явно послаблює порядок (data=writeback) і збільшує інтервал комітів.

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

Task 8: Підтвердіть поведінку NFS клієнта (що клієнт думає, що робить)

cr0x@server:~$ nfsstat -m
/var/lib/shared from nfs01:/export/shared
 Flags: rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.0.2.10,local_lock=none

Значення: Показує версію NFS і опції клієнта. Примітка: опції клієнта не перекривають серверне async у підтвердженнях.

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

Task 9: Перевірте експорти NFS на наявність async (реальна підковзна гілка)

cr0x@server:~$ sudo exportfs -v | sed -n '1,120p'
/export/shared
	10.0.0.0/16(rw,async,wdelay,hide,no_subtree_check,sec=sys,secure,root_squash)

Значення: Експорт має async. Сервер може підтверджувати записи до коміту.

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

Task 10: Валідируйте фактичний тиск writeback і ліміти dirty (оцінка вікна ризику)

cr0x@server:~$ sysctl vm.dirty_background_ratio vm.dirty_ratio vm.dirty_expire_centisecs
vm.dirty_background_ratio = 10
vm.dirty_ratio = 20
vm.dirty_expire_centisecs = 3000

Значення: Брудні сторінки можуть накопичуватися до 20% ОЗП перед тротлінгом; час життя ≈30 секунд. Це впливає на те, скільки даних може бути «підтверджено» системою, але не на диску.

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

Task 11: Подивіться, що файлова система думає про delayed allocation і writeback (приклад ext4)

cr0x@server:~$ cat /proc/mounts | grep ' ext4 ' | head -n 2
/dev/nvme0n1p2 / ext4 rw,relatime,errors=remount-ro,data=ordered 0 0
/dev/nvme0n1p3 /var ext4 rw,relatime,data=ordered 0 0

Значення: Підтверджує режим даних і оброблення помилок.

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

Task 12: Заміряйте, чи ваш застосунок фактично виконує flush-и (перевірка реальності)

cr0x@server:~$ sudo strace -f -e trace=fdatasync,fsync,openat,rename -p 1423 -s 80
strace: Process 1423 attached
fsync(17)                                = 0
rename("tmpfile", "current")             = 0
fsync(5)                                 = 0

Значення: Процес викликає fsync() і також синхронізує дескриптор директорії (часто необхідно після rename() для стійких метаданих). Це те, як «правильно» виглядає для багатьох навантажень.

Рішення: Якщо ви не бачите fsync/fdatasync там, де очікуєте (бази даних, WAL-райтери, черги), ваша стійкість ймовірно — бажана. Виправте застосунок/конфігурацію, перш ніж звинувачувати файлову систему.

Task 13: Перевірте IO-помилки й таймаути на блоці (побратим тихої корупції)

cr0x@server:~$ sudo journalctl -k -b | egrep -i 'I/O error|blk_update_request|nvme|reset|timed out' | tail -n 20
Feb 04 10:38:21 server kernel: nvme nvme0: I/O 42 QID 7 timeout, aborting
Feb 04 10:38:21 server kernel: nvme nvme0: Abort status: 0x371

Значення: У вас є таймаути на рівні пристрою. Це може спричинити відтворення журналу, remount-ro події і часткові записи залежно від обробки помилок.

Рішення: Розглядайте це як проблему надійності апаратного/віртуального диска. Припиніть «тюнінг» опцій монтування і почніть план заміни, прошивки або інфраструктурних змін.

Task 14: Перегляньте налаштування черги, щоб зрозуміти піки затримки (не прямо про стійкість, але часто суміжне)

cr0x@server:~$ cat /sys/block/nvme0n1/queue/scheduler
[mq-deadline] kyber none

Значення: Показує використаний scheduler. Тюнінг затримки іноді змушує людей відключати поведінку, пов’язану з flush-ами; не плутайте ці верстви.

Рішення: Налаштовуйте планування окремо від семантики стійкості. Збережіть барʼєри/flush-и коректними, а потім оптимізуйте пропускну здатність/латентність у межах цих обмежень.

Типові помилки: симптом → корінь проблеми → виправлення

1) «Файлова система монтується чисто, але база даних втратила зафіксовані транзакції»

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

Корінь: Підтверджені записи не були стійкими: NFS сервер async, барʼєри вимкнені, брехливий write cache або застосунок не виконує fsync для WAL/даних правильно. ext4 data=writeback може посилити дивність.

Виправлення: Переконайтесь, що експорти NFS — sync для стійких навантажень; тримайте барʼєри/flush-і ввімкненими; валідуйте, що пристрій шанує flush-и; встановіть файлову систему в безпечніший режим журналювання; виправте конфіг застосунку, щоб fsync був у правильних межах.

2) «Після втрати живлення файли існують, але містять старіший вміст»

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

Корінь: ext4 data=writeback або проблеми з барʼєрами/flush-ами, що дозволяють метаданим зафіксуватися перед тим, як блоки даних стануть стабільними.

Виправлення: Використовуйте data=ordered (або data=journal, коли це виправдано); тримайте flush-и увімкненими; валідуйте семантику flush у контролера/пристрою; зменшіть ризикові твіки.

3) «Все стало лише для читання на ходу»

Симптоми: Застосунки почали падати з помилками «Read-only file system»; логи ядра показують помилки файлової системи; відбувся remount.

Корінь: Помилки IO на нижньому рівні або корупція метаданих; errors=remount-ro зробив свою роботу. Це не баг опції монтування — це ядро, що захищає вас.

Виправлення: Дослідіть здоровʼя пристрою; зберіть логи; виконайте перевірки файлової системи в режимі обслуговування; замініть підозріле обладнання; за потреби відновіть з відомих добрих бекапів.

4) «Ми ввімкнули опцію для продуктивності, і тепер відновлення після збою займає вічність»

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

Корінь: Збільшений commit=, великий backlog writeback, або навантаження, що створює величезний набір неприбраних метаданих; в NFS — серверна черга.

Виправлення: Зменшіть інтервал комітів; забезпечте адекватну IO-ємкість; уникайте буферизації величезних брудних наборів; для NFS тримайте експорти синхронними для критичних даних і масштабуйтe сховище відповідно.

5) «Ми лише змінили опції монтування, чому цілісність даних стала «випадковою»?»

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

Корінь: Ви змінили семантику порядку/стійкості. Навантаження випадково покладалося на стару поведінку (таймінги, порядок, flush-и) і тепер порушує власні інваріанти.

Виправлення: Поверніть безпечні семантики; потім явно визначте й забезпечте межі стійкості в застосунку і дизайні зберігання.

Чеклісти / покроковий план

Чекліст 1: Перш ніж торкатися опцій монтування в продакшені

  1. Класифікуйте дані: кеш/генеровані заново vs стійкі/авторитетні.
  2. Запишіть обіцянку стійкості простими словами: «Після того, як застосунок повідомив про успіх, дані переживуть втрату живлення.»
  3. Перевірте поведінку застосунку: чи викликає він fsync/fdatasync правильно (включно з fsync директорії після rename/create для файлових робочих сценаріїв)?
  4. Перевірте поведінку сховища: чи пристрій/контролер/гіпервізор шанує flush-и?
  5. Проведіть зміну на стенді з навантаженням, подібним до продакшену, і запустіть тест некоректного завершення.
  6. Майте план відкату: швидко перемонтувати або відновити попередню конфігурацію.

Чекліст 2: Безпечні налаштування, які я рекомендую (і коли відхилятися)

  • ext4: віддавайте перевагу data=ordered, барʼєри/flush увімкнені (за замовчуванням), уникайте data=writeback для всього стійкого.
  • XFS: тримайте дефолти, якщо немає конкретної, протестованої причини. Витрачайте зусилля на валідацію поведінки flush у пристрою.
  • NFS для стійких навантажень: віддавайте перевагу експорту сервера sync. Якщо потрібна продуктивність на рівні async, перегляньте архітектуру (локальний WAL + реплікація або спеціалізоване сховище).
  • Інтервали commit: не збільшуйте їх необдумано. Більші вікна — більші втрати.

Чекліст 3: Перевірка після інциденту (після некоректного завершення)

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

Часті запитання (FAQ)

1) Чи завжди data=writeback — це «корупція»?

Ні. Файлова система може залишатися структурно консистентною. «Корупція» часто семантична: метадані можуть посилатися на дані, які не були записані в тому порядку, який ви очікували. Якщо ви очікували «новий файл вказує на нові байти», writeback може це порушити після збою.

2) Якщо мій застосунок використовує fsync(), я в безпеці?

Безпечніше, але не автоматично в безпеці. Також потрібен весь стек зберігання, що поважає flush-и, і ви повинні викликати fsync на правильних речах (включно з директоріями після rename/create, залежно від шаблону). І якщо ви на NFS з серверним async, підтвердження все одно можуть випереджати фактичний коміт.

3) Чому люди взагалі використовують небезпечні опції?

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

4) У чому різниця між «консистентністю файлової системи» і «стійкістю даних»?

Консистентність означає, що структури файлової системи після відновлення коректні. Стійкість означає, що підтверджені записи переживуть збій. Журналювання здебільшого зорієнтоване на консистентність. Стійкість вимагає правильного порядку і поведінки flush-ів по всьому стеку.

5) Чи може NFS бути придатним для баз даних?

Іноді, за умови правильних налаштувань сервера (sync), стабільного підкладу зберігання і бази даних, що це підтримує. Але це високоризикова архітектура, якщо ви не контролюєте весь ланцюг. Багато команд обирають локальне сховище для WAL/основних записів і реплікують для надлишковості.

6) Чи варто вимкнути кеш запису диска для безпеки?

Вимкнення write cache може зменшити ризик на незахищених пристроях, але також може сильно знизити продуктивність і не завжди підтримується або ефективне. Краща відповідь: використовуйте пристрої з захистом від втрати живлення для критичних шарів і тримайте семантику flush/барʼєрів увімкненою.

7) Яка опція монтування найбільше підвищує безпеку?

Зазвичай «не вимикати засоби безпеки» — переможна стратегія: не використовуйте data=writeback для стійких даних, не відключайте барʼєри/flush-и і уникайте серверного NFS async там, де важлива стійкість. Безпека часто — це відсутність «оптимізацій».

8) Як я можу довести, що моє сховище стійке?

Ви тестуєте це fault injection-ом у стилі втрати живлення під реалістичним навантаженням. Запустіть навантаження, що робить підтверджені записи з межами fsync, аварійно завершіть, перезавантажте і валідуйте інваріанти. Усе інше — теорія.

9) Моя файлова система ext4 з data=ordered. Чи все ще я можу втратити дані?

Так. Ви можете втратити найсвіжіші записи, що ще були в кеші, і можете втратити підтверджені записи, якщо пристрій бреше про flush-и або застосунок неправильно викликає fsync. ordered зменшує деякі класи пост-креш сюрпризів; воно не скасовує фізики.

10) Чи є data=journal відповіддю на все?

Ні. Воно може бути повільнішим і збільшує write amplification. Це інструмент для конкретних випадків, а не універсальна налаштування. Багато систем працюють краще з правильною логікою на рівні застосунку (WAL) і безпечними дефолтами файлової системи.

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

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

Зробіть ці кроки:

  1. Інвентаризуйте монти на критичних шляхах за допомогою findmnt. Запишіть усе нестандартне і обґрунтуйте.
  2. Забороніть data=writeback для стійких даних, якщо нема задокументованого, протестованого винятку.
  3. Аудитуйте експорти NFS на предмет async. Якщо знайдете його для стійких навантажень — вважайте це ризиком рівня sev.
  4. Валідуйте ланцюг flush-ів: поведінка кеша пристрою, політики контролера, семантика віртуалізаційного шару.
  5. Додайте один тест некоректного завершення у staging для принаймні одного критичного сервісу. Зробіть це рутинним, а не героїчним.

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

← Попередня
IOMMU пояснено: єдиний перемикач у BIOS, що робить (або ламає) GPU passthrough
Наступна →
Linux: PostgreSQL на малому VPS — налаштування, що запобігають OOM

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