Найдорожчі інциденти, з якими я працював, ніколи не були спричинені «складністю». Вони були спричинені впевненістю.
Хтось змінив один рядок — іноді прапорець, іноді значення за замовчуванням, іноді «тимчасове» обхідне рішення — і система зробила саме те, що їй наказали.
Ось у чому проблема.
У великих продакшн‑середовищах вам не дістається «часткової» відмови як поблажки. Ви отримуєте каскадну поведінку, підсилену автоматизацією, кешами, повторними спробами і добрими намірами самовідновлення.
Помилка в одному рядку перетворюється на подію для флоту, а платіж приходить у вигляді втраченого доходу, розгніваних користувачів і тижня судової археології.
Чому «один рядок» небезпечний у великих системах
Міф: один рядок не може бути таким уже поганим. Реальність: один рядок часто — єдине, що стоїть між вашою системою й її найгіршими інстинктами.
Великі системи насичені мультиплікативними факторами: масштаб, автоматизація, повтори, розподілена координація, спільна інфраструктура і «корисні» значення за замовчуванням.
Крихітна помилка в конфігурації ламає не одну машину; вона змінює поведінку всюди, де застосовується цей рядок.
Зміна в одному рядку рідко має однорядковий ефект. Вона змінює таймінги. Таймінги змінюють черги. Черги змінюють латентність.
Латентність запускає таймаути. Таймаути викликають повтори. Повтори збільшують навантаження. Навантаження збільшує латентність. Вітаю: ви створили зворотний зв’язок.
Більшість постмортемів рано чи пізно визнають ту саму невтішну істину: система технічно була «здоровою», доки люди не навчили її новому, гіршому правилу.
І оскільки зміна була мала, вона пройшла повз фільтр скептицизму. Малі зміни відчуваються безпечними. Вони такими не є.
Операційне правило, яке збереже вам гроші: ставтеся до малих змін як до високого ризику, коли радіус ураження великий.
Зміна одного рядка в глобальному сховищі конфігурацій — це не «мало». Це трансляція.
Єдиний рядок, який має значення — це той, який виконує система
Люди сперечаються про намір. Комп’ютери сперечаються про таблиці істин.
Ви можете «мати на увазі» встановити таймаут у 30 секунд, а випадково встановити 30 мілісекунд. Система виконає точно, як сказано, миттєво, ніби відданий стажер без контексту.
Жарт №1 (короткий, релевантний): Продакшн — це місце, куди ваші припущення йдуть здаватися реальності — у масштабі.
Чому інженери сховищ нервують через «просто підлаштування конфігу»
Сховище перебуває на критичному шляху майже всього: баз даних, логів, черг, контейнерів та невидимої машини стану.
Сховище також має свої особливості: write amplification, поведінка кешу, семантика fsync та планувальник I/O ядра. Один рядок може перевернути всю модель поведінки:
синхронні vs асинхронні записи, direct I/O vs buffered, стиснення вкл/викл, recordsize, опції монтування, глибина черги або один sysctl, що змінює пороги dirty page.
Гірше: відмови сховища часто розвиваються в уповільненому режимі. Система не падає; вона повзе.
Саме так ви витрачаєте гроші: тримаєте флот у роботі, поки він тихо спалює CPU на повтори і чекає I/O.
Факти й історичний контекст: малі зміни — великі наслідки
Інженерія надійності довго вчиться одній і тій самій премудрості в різних образах.
Ось кілька конкретних фактів і контекстних пунктів, які важливі, коли ви вирішуєте, чи заслуговує «незначна» зміна на серйозний процес.
-
Аварії Therac-25 (1980‑ті) — класичний приклад колапсу програмних і безпекових припущень. Невеликі програмні помилки в поєднанні з робочим процесом призвели до смертельних передозувань.
Це не «один рядок», але це «мала логіка, масивний вплив». - Mars Climate Orbiter (1999) загубили через невідповідність одиниць (імперські vs метричні). Це канонічний приклад помилки «хибного припущення»: значення виглядали правдоподібно, доки фізика не погодилася.
- DNS‑відмови неодноразово виводили з ладу великі сервіси, бо DNS — маленька залежність із величезним радіусом ураження. Зміна TTL або конфігурації резолвера може швидко стати глобальним інцидентом.
- Чорний‑аут на північному сході США 2003 року включав програмні та алертні збої, які приховали проблему, поки вона не каскадувала. Збої в спостережуваності — це «кузен» інцидентів з одного рядка: не можна виправити те, чого не бачиш.
- Поведінка writeback у Linux еволюціонувала роками, бо значення за замовчуванням можуть викликати або спайки латентності, або колапс пропускної здатності під навантаженням. Один sysctl може перенести біль з диску на користувачів.
- RAID write hole та політики кешування — уроки десятиліть: одна настройка контролера (write‑back vs write‑through) змінює, чи буде втрата живлення подією продуктивності або втратою даних.
- «Шторми повторів» — відомий патологічний сценарій розподілених систем. Вони перетворюють транзитну повільність у стійке перевантаження. Крихітні значення таймаутів/повторів можуть озброїти ваших клієнтів проти ваших серверів.
- Configuration‑as‑code стала популярною здебільшого тому, що ручний дріфт конфігурацій викликав непередбачувану поведінку; вирішення — відтворюваність. Іронічно, це також полегшило розгортання поганого рядка всюди одразу.
Одне висловлювання, яке операційники схильні засвоювати після багатьох безсонних ночей:
«Hope is not a strategy.»
— James Cameron
Це не романтична цитата. Це цитата для ру‑буку. Якщо ваша історія безпеки — «ймовірно буде гаразд», ви вже ведете переговори з відмовою.
Фізика підсилення: як крихітні помилки стають відмовами
1) Розподілені системи не відмовляють чемно
Відмови на одній вузлі чіткі. Розподілені відмови — розмиті.
Тонка помилка конфігурації може породити часткові таймаути, нерівномірне навантаження та дивну конкуренцію.
Балансувальник навантаження продовжує шлити трафік. Автоскейлер бачить латентність і додає вузли. База даних бачить більше з’єднань і починає фрікати.
Графік виглядає як повільна лавина.
2) Черги ховають проблеми, поки не перестають
Черги — чудові. Вони розв’язують виробників і споживачів.
Вони також поводяться як кредитні картки: відкладають біль і додають відсотки.
Зміна в одному рядку, яка уповільнює споживачів на 20%, може не викликати тривоги годинами, допоки беклог не досягне порога і все не почне таймаутити.
3) Кеші перетворюють коректність на ймовірність
Кеш приховує латентність, підсилює навантаження і змушує забути, як виглядає холодний старт.
Один рядок може зруйнувати коефіцієнт попадань: зміна формату ключа кеша, політики евікції, TTL або введення кардинальності.
Раптом ваш origin (часто база даних) робить роботу, яку не виконував місяцями. Він ніколи не був для цього спроєктований. Тепер він вузьке місце і цап‑відбувайло.
4) Сховище — місце, де «незначне» стає вимірюваним
У сховищі дрібні регулювання важливі, бо вони змінюють I/O‑патерни:
випадкове vs послідовне, sync vs async, малий I/O vs великий, багато метаданих vs стрімінг, buffered vs direct, стиснюване vs несжатне.
Розбіжність recordsize на ZFS або опція монтування файлової системи може перетворити здорову базу даних у генератор затримок.
Жарт №2 (короткий, релевантний): Латентність сховища — як поганий жарт: важливий таймінг, і ви завжди відчуваєте її в паузі.
5) «За замовчуванням» не означає «безпечний»
Значення за замовчуванням — компроміс між робочими навантаженнями, апаратурою та апетитом до ризику. Ваш продакшн не «середній».
Значення за замовчуванням, що підходить веб‑серверу, може бути жахливим для write‑heavy бази даних.
Гірше, значення за замовчуванням змінюються між версіями. Оновлення пакета одним рядком може неявно змінити п’ять поведінок, які ви ніколи не зафіксували.
Три корпоративні міні‑історії (анонімізовано, болісно правдоподібні)
Міні‑історія №1: Інцидент через хибне припущення
Середня SaaS‑компанія перемістила частину флоту на інший тип інстансів. Та сама сімейство CPU, «схожий» NVMe, новіше ядро.
Старший інженер зробив невелику зміну в маніфесті деплою: підняв час очікування з’єднання для внутрішнього gRPC‑клієнта, бо «мережа тепер швидша».
Припущення: швидша мережа означає меншу латентність; отже менший таймаут — «безпечно».
Що вони пропустили: сервіс за gRPC залежав від кластера Postgres, який час від часу мав fsync‑затримки під сплесковим навантаженням.
Ці затримки зазвичай поглиналися старим, більш щедрим таймаутом. З новим малим таймаутом клієнт швидко почав таймаутити і агресивно повторювати запити.
Повтори збільшили навантаження на сервіс, що збільшило конкуренцію в базі, що збільшило fsync‑затримки. Класичний позитивний фідбек.
На виклику бачили «мережеві помилки» і ганялись за втратою пакетів. Мережа була в порядку.
Тим часом графіки бази даних показували зростання кількості з’єднань, зростання часу очікування блокувань і спайки I/O‑латентності.
Інцидент не був одиничною відмовою; це був новий режим осциляції, введений з доброї волі зміною таймауту.
Виправлення було жорстким у своїй простоті: відкатити таймаут, обмежити повтори з джиттером і додати circuit breaker.
Висновок був важливішим: латентність — не скаляр. Це розподіл. Коли ви налаштовуєте таймаути, ви обираєте, з яким хвостом можете жити.
Міні‑історія №2: Оптимізація, що відкотилась
Інша організація експлуатувала великий кластер Elasticsearch для логів і безпекової аналітики. Інгест був інтенсивним, сховище дороге, і хтось вирішив «оптимізувати диск».
Вони змінили налаштування, пов’язані зі стисненням, і підкоригували опції файлової системи на вузлах даних — один рядок у репозиторії конфігурацій, розгорнуто поступово.
Зміна виглядала розумною в лабораторному тесті з синтетичними даними.
У продакшні набір даних мав зовсім іншу форму. Деякі індекси добре стиснулися; інші — ні.
Використання CPU зросло. Не трохи — настільки, щоб розтягнути цикли GC і збільшити латентність індексації.
Латентність індексації призвела до збільшених bulk‑запитів від шиперів. Повтори збільшили навантаження на інгест. Інгест збільшив злиття сегментів.
Злиття сегментів підвищило write amplification на диск. Тепер диски були зайняті більше, ніж до «оптимізації диска».
Графіки розповідали заплутану історію: використання диску зросло, CPU зросло, латентність зросла. Що спричинило що?
Відповідь: так.
Зміна, спрямована на зниження вартості диску, збільшила вартість CPU, що збільшило тиск на злиття, що збільшило вартість диску. Система знайшла нову рівновагу: гіршу.
Вони зробили відкат. Потім провели справжній канар з продакшн‑подібними даними, сфокусувавшись на хвостовій латентності та рівнях злиття, а не на середній пропускній здатності.
Урок не був «не оптимізувати». Він був «не оптимізуйте наосліп». Ваше навантаження — це тест.
Міні‑історія №3: Нудна, але правильна практика, що врятувала ситуацію
Фінансова платформа мала репутацію болісно консервативної. Інженери скаржилися на вікна змін, канарі та «занадто багато чек‑лістів».
Потім постачальник сховища випустив оновлення прошивки для виправлення відомої проблеми. Оновлення вимагало підвищення драйвера на хості та невеликої зміни правила udev.
Один рядок. Безпечний, правда?
Їхній процес примусив розгортання початися на одному некритичному вузлі з синтетичним навантаженням плюс реальним shadow‑трафіком.
За кілька хвилин латентність на тому вузлі показала періодичні спайки. Недостатньо для падіння, але достатньо, щоб побачити в p99.
Вони зупинили розгортання до того, як воно торкнулося основного флоту.
Корінь виявився взаємодією глибини черги між новим драйвером і їхнім планувальником I/O ядра.
Старий драйвер мовчазно обмежував кількість одночасних I/O; новий дозволив глибші черги, що покращило пропускну здатність, але погіршило хвостову латентність під мішаним чит/запис навантаженням.
Їхнє навантаження цінувало p99 більше, ніж пропускну здатність.
Вони явно підлаштували глибину черги й прив’язали планувальник під пристрій. Потім повторили канар і безпечно розгорнули.
Ніхто не писав героїчний постмортем. Вони просто уникнули відмови. Ось як виглядає «нудно й правильно».
Швидкий план діагностики: що перевіряти першим/другим/третім
Коли все йде не так, часу на філософію немає. Потрібна послідовність, яка знайде вузьке місце до того, як кімната наповниться думками.
Це playbook, який я використовую для інцидентів «система повільна / таймаути / періодичні помилки», особливо коли може бути задіяне сховище.
Перше: підтвердьте симптом і його форму (латентність vs помилки vs насичення)
- Глобально це чи обмежено однією AZ/рейкою/пулом вузлів?
- p50 в нормі, а p99 погано (хвостова латентність), чи погано й середнє?
- Чи корелюють помилки з таймаутами (на боці клієнта) або з відмовами на боці сервера?
Друге: знайдіть клас вузького місця (CPU, пам’ять, I/O, мережа, блокування)
- CPU: високе %usr/%sys, черга виконання, тротлінг, %steal.
- Пам’ять: reclaim, swap, OOM‑kill, хаотичний page cache.
- I/O: iowait, дискова латентність, глибина черги, filesystem stalls, fsync‑спайки.
- Мережа: втрата пакетів, retransmits, ліміти conntrack, DNS‑таймаути.
- Блокування: очікування блокувань у базі, contention м’ютексів, паузи GC.
Третє: визначте, чи ви бачите причину чи наслідок
- Високий CPU може бути через стиснення, шифрування, повтори або логінгові цикли.
- Високий I/O може бути через компактизацію, злиття, чекпоінтинг або шторм промахів кеша.
- Мережеві помилки можуть бути реальними, або це таймаути через повільні сервери.
Четверте: шукайте «один рядок» як тригер
- Нещодавні деплои, зміни конфігів, feature flag, оновлення ядра/прошивки.
- Зміни на боці клієнта (таймаути, повтори, конкурентність) часто виновники.
- Параметри, пов’язані зі сховищем (sync, cache, опції монтування, scheduler), тихі, але рубають різко.
П’яте: зменшіть радіус ураження перед тим, як добитися повного розуміння
- Призупиніть розгортання, заморозьте зміни автоскейлінгу, вимкніть агресивні повтори.
- Відкрийте/закрийте провали свідомо в залежності від бізнес‑впливу.
- Краще відкатути зміну, ніж дебажити на живій системі, якщо відкат безпечний.
Практичні завдання: команди, виводи та рішення (12+)
Це реальні завдання, які ви можете виконати під час інциденту. Кожне містить команду, приклад виводу, що це означає і яке рішення далі.
Не зубріть їх. Зберігайте в ру‑буку і використовуйте послідовно.
Завдання 1: Перевірте базове навантаження, uptime і чи вузол не thrash‑ить
cr0x@server:~$ uptime
14:22:10 up 31 days, 2:03, 3 users, load average: 22.14, 18.09, 12.77
Що це означає: Load average значно вище кількості CPU часто означає runnable tasks (CPU contention) або uninterruptible sleep (I/O wait).
Рішення: Негайно перевірте насичення CPU і I/O wait (Завдання 2 і 3). Якщо load швидко зростає, почніть знижувати трафік або дренувати вузли.
Завдання 2: Подивіться, чи CPU — вузьке місце, чи «idle» ховає I/O wait
cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.2.0 (server) 01/22/2026 _x86_64_ (16 CPU)
12:22:12 PM CPU %usr %nice %sys %iowait %irq %soft %steal %idle
12:22:13 PM all 12.4 0.0 6.1 41.8 0.0 0.7 0.0 39.0
Що це означає: Високе %iowait свідчить, що CPU чекає на диск, а не «завалений обчисленнями».
Рішення: Розглядайте це як I/O‑інцидент допоки не доведено інше. Перейдіть до iostat і перевірок I/O по процесах.
Завдання 3: Швидко перевірте латентність диску й черги
cr0x@server:~$ iostat -x 1 3
Linux 6.2.0 (server) 01/22/2026 _x86_64_ (16 CPU)
Device r/s w/s rkB/s wkB/s await aqu-sz %util
nvme0n1 220.0 1100.0 8800.0 64000.0 38.2 9.40 99.7
Що це означає: await ≈38ms з %util близько 100% вказує, що пристрій насичений і запити ставляться в чергу.
Рішення: З’ясуйте, хто генерує I/O (Завдання 4/5). Розгляньте обмеження швидкості, переміщення навантаження або відкат зміни, що підвищила write amplification.
Завдання 4: Знайдіть топ‑процеси за I/O (швидка триаж)
cr0x@server:~$ iotop -o -b -n 3
Total DISK READ: 2.10 M/s | Total DISK WRITE: 85.30 M/s
PID PRIO USER DISK READ DISK WRITE SWAPIN IO% COMMAND
18722 be/4 elastic 0.00 B/s 62.40 M/s 0.00 45.6 elasticsearch
9421 be/4 postgres 0.00 B/s 18.70 M/s 0.00 21.0 postgres: checkpointer
Що це означає: Два процеси формують запис; один — checkpointer бази даних (импульсний), інший — Elasticsearch (стабільні важкі записи).
Рішення: Якщо це збігається з недавнім конфіг/деплоєм, зробіть відкат. Якщо ні, шукайте upstream‑зміни поведінки (промахи кеша, повтори, сплески інгесту).
Завдання 5: Підтвердіть, чи процеси в стані D (I/O) і яка syscall зависла
cr0x@server:~$ ps -eo pid,stat,wchan:32,cmd | awk '$2 ~ /D/ {print}' | head
18722 D io_schedule /usr/share/elasticsearch/jdk/bin/java ...
9421 D jbd2_log_wait_commit postgres: checkpointer
Що це означає: Процеси в стані D заблоковані в очікуванні I/O. Канал очікування підказує шляхи ядра (journaling, scheduler).
Рішення: Дослідіть тиск на файлову систему/журнал, стан пристрою та чи не відбулася зміна опцій монтування або планувальника.
Завдання 6: Перевірте місце на файловій системі та витрачання inode (нудне, часте)
cr0x@server:~$ df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4 900G 865G 35G 97% /
Що це означає: 97% заповнення — небезпечна територія: фрагментація, повільні алокації і ризик досягти 100% під сплесковими записами.
Рішення: Зупиніть кровотечу: обертайте логи, видаліть тимчасові файли або додайте ємність. Також перевірте inode:
cr0x@server:~$ df -ih
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/nvme0n1p2 56M 56M 0 100% /
Що це означає: Витрачення inode викликає «No space left on device», навіть коли байтів ще залишилось.
Рішення: Знайдіть вибух малих файлів (temp каталоги, кеші) і виправте навантаження. Витрачення inode часто симптом runaway‑процесу або неправильної політики збереження.
Завдання 7: Виявити тиск на пам’ять ядра та шторм реклайму
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
12 6 0 10240 12000 80000 0 0 120 9800 8200 9900 12 6 38 44 0
Що це означає: Високе b (blocked), високе wa (I/O wait) і великий bo вказують на writeback‑тиск.
Рішення: Якщо один sysctl змінив dirty‑ліміти або таймінги writeback, відкатуйте його. Інакше знайдіть процес‑письменник і знизьте його швидкість.
Завдання 8: Перевірте недавні перезавантаження, OOM‑kill і помилки сховища в journal
cr0x@server:~$ journalctl -p warning -S -2h | tail -n 20
Jan 22 13:41:08 server kernel: nvme nvme0: I/O 123 QID 4 timeout, aborting
Jan 22 13:41:08 server kernel: EXT4-fs warning (device nvme0n1p2): ext4_end_bio:343: I/O error 10 writing to inode 262911
Що це означає: Таймаути і помилки I/O файлової системи — це не «проблеми продуктивності». Це інциденти надійності.
Рішення: Почніть апаратний триаж: перевірте SMART/NVMe логи (Завдання 9), розгляньте виведення вузла з сервісу і уникайте повторних примусових перезапусків, що можуть погіршити корупцію.
Завдання 9: Перевірте стан NVMe і лічильники помилок
cr0x@server:~$ sudo nvme smart-log /dev/nvme0
SMART/Health Information (NVMe Log 0x02)
critical_warning : 0x00
temperature : 57 C
available_spare : 100%
percentage_used : 12%
media_errors : 3
num_err_log_entries : 27
Що це означає: Нульове значення media_errors — погано; зростання записів у журналі помилок свідчить про реальні проблеми з пристроєм, а не про налаштування.
Рішення: Дренуйте і замініть пристрій/вузол. Якщо це почалося після оновлення прошивки, відкотіть прошивку на інших вузлах і відкрийте кейс у постачальника.
Завдання 10: Перевірте опції монтування, що змінюють поведінку щодо надійності або латентності
cr0x@server:~$ findmnt -no TARGET,SOURCE,FSTYPE,OPTIONS /var/lib/postgresql
/var/lib/postgresql /dev/nvme0n1p2 ext4 rw,relatime,data=writeback,barrier=0
Що це означає: barrier=0 вимикає write barriers; data=writeback змінює семантику журналювання. Це може пришвидшити, але знищити вам тиждень.
Рішення: Якщо це ненавмисно, відкатайте до безпечніших опцій (для ext4 бар’єри зазвичай увімкнені; режим журналювання залежить від профілю ризику). Підтвердіть вимоги до надійності сховища.
Завдання 11: Перевірте TCP‑retransmits і втрачені пакети (не звинувачуйте сховище зарано)
cr0x@server:~$ ss -s
Total: 19432 (kernel 20110)
TCP: 14210 (estab 1200, closed 12650, orphaned 8, timewait 3200)
Transport Total IP IPv6
RAW 0 0 0
UDP 90 70 20
TCP 1560 1400 160
INET 1650 1470 180
FRAG 0 0 0
cr0x@server:~$ netstat -s | egrep -i 'retrans|listen|drops' | head
12847 segments retransmitted
320 listen queue overflows
Що це означає: Retransmits і переповнення listen queue можуть імітувати повільність сховища, породжуючи шторм таймаутів.
Рішення: Якщо retransmits стрибнули після зміни (TLS‑налаштування, MTU, балансувальник), спочатку виправляйте мережу. Якщо переповнення відбуваються, налаштуйте backlog і знизьте contention у циклі accept.
Завдання 12: Виявити проблеми DNS або резолвера, що виглядають як «додаток повільний»
cr0x@server:~$ resolvectl status | sed -n '1,80p'
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Current DNS Server: 10.0.0.53
DNS Servers: 10.0.0.53 10.0.0.54
cr0x@server:~$ dig +stats example.internal A | tail -n 5
;; Query time: 1200 msec
;; SERVER: 10.0.0.53#53(10.0.0.53) (UDP)
;; WHEN: Thu Jan 22 14:24:10 UTC 2026
;; MSG SIZE rcvd: 86
Що це означає: 1.2s для DNS‑запиту зробить усе виглядати зламаним, особливо сервіси, що часто резолвлять.
Рішення: Зупиніть негайне кровотечу: кешуйте DNS результати на клієнтах де доречно, зменшіть частоту резолвів або переключіть резолвер. Потім виправляйте проблему DNS.
Завдання 13: Перевірте systemd на предмет шторму перезапусків (множник відмов через одну рядок)
cr0x@server:~$ systemctl status api.service
● api.service - Example API
Loaded: loaded (/etc/systemd/system/api.service; enabled)
Active: activating (auto-restart) since Thu 2026-01-22 14:21:02 UTC; 3s ago
Process: 23110 ExecStart=/usr/local/bin/api --config /etc/api/config.yaml (code=exited, status=1/FAILURE)
cr0x@server:~$ journalctl -u api.service -S -10m | tail -n 10
Jan 22 14:21:01 server api[23110]: FATAL: config: unknown field "reties"
Jan 22 14:21:02 server systemd[1]: api.service: Scheduled restart job, restart counter is at 58.
Що це означає: Одна опечатка спричиняє безперервні перезапуски, що додає навантаження (логи, churn з’єднань) і витрачає ресурси.
Рішення: Зупиніть шторм перезапусків (disable або встановіть backoff), відкатайте конфіг і додайте валідацію схеми в CI, щоб опечатки не потрапляли в prod.
Завдання 14: Для ZFS‑середовищ перевірте здоров’я пулу і підозрілі налаштування латентності
cr0x@server:~$ sudo zpool status
pool: tank
state: ONLINE
scan: scrub repaired 0B in 02:11:33 with 0 errors on Wed Jan 21 03:00:14 2026
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
errors: No known data errors
cr0x@server:~$ sudo zfs get -o name,property,value -H compression,recordsize,atime,sync tank/db
tank/db compression zstd
tank/db recordsize 128K
tank/db atime off
tank/db sync standard
Що це означає: Стан пулу в порядку; тепер подивіться властивості датасету. Невідповідність recordsize або налаштування sync може суттєво змінити латентність бази даних.
Рішення: Якщо навантаження — база даних з малими випадковими записами, розгляньте налаштування recordsize і перевірте очікування щодо sync. Не вмикайте sync=disabled, якщо не любите пояснювати втрату даних.
Типові помилки: симптом → корінь → виправлення
Тут ми припиняємо вдавати, що ці відмови рідкісні.
Більшість інцидентів «один рядок» повторюються за відтворюваними схемами. Вивчіть запах проблеми — і ви зловите їх раніше.
1) Раптові таймаути після «зниження таймаутів для швидшого UX»
Симптоми: Більше 499/504, сплеск повторів клієнтів, сервери виглядають «ок», але p99 вибухає.
Корінь: Таймаут менший, ніж хвіст латентності залежності; поведінка повторів підсилює навантаження.
Виправлення: Збільшіть таймаути, щоб покрити реалістичний p99.9 з запасом; обмежте повтори з експоненційним backoff і джиттером; додайте circuit breakers і bulkheads.
2) «Диск сповільнився» відразу після вмикання стиснення або шифрування
Симптоми: CPU піднявся, паузи GC зросли, глибина черги I/O зросла, пропускна здатність може впасти або стати спайковою.
Корінь: CPU став прихованим вузьким місцем; фонові процеси (merges/compaction) збільшили write amplification.
Виправлення: Робіть канар з продакшн‑даними. Вимірюйте p95/p99 і фонову активність. Розгляньте апаратне прискорення, інші алгоритми або вибіркове стиснення.
3) Fsync‑спайки бази даних після «продуктивної» опції монтування
Симптоми: Збільшення часу підтвердження транзакцій, чекпоінти зависають, таймаути застосунку згруповані.
Корінь: Опції монтування змінили семантику журналювання/бар’єрів, невідповідність планувальника або налаштування writeback нашкодили хвостовій латентності.
Виправлення: Відкатати небезпечні опції монтування; прив’язати планувальник; налаштувати глибину черги для латентності; забезпечити, щоб налаштування надійності відповідали вимогам.
4) Флот‑шторм перезапусків після тривіальної правки конфігу
Симптоми: Інстанси флапають, логи вибухають, upstream‑сервіси бачать churn з’єднань.
Корінь: Синтаксична невідповідність конфігу/поле; systemd або оркестратор агресивно перезапускають.
Виправлення: Додайте валідацію конфігів в CI, реалізуйте backoff для перезапусків і вимагайте канар + smoke‑тести перед повним розгортанням.
5) «Все повільно», але лише при холодному старті або після ротації вузлів
Симптоми: Висока латентність читання після деплоїв, зниження хит‑рейту кеша, бази даних розігріваються.
Корінь: Евікція кеша або зміна ключів; локальні кеші вузла не прогріті; агресивний churn підрозділів скидає робочі набори.
Виправлення: Зберігайте кеші коли можна, прогрівайте критичні кеші, зменшуйте churn і моніторте hit rate як метрику SLO.
6) Сховище виглядає нормально, але клієнти таймаутять і ставлять в чергу
Симптоми: Метрики сервера виглядають стабільно; клієнти бачать періодичні збої; retransmits зростають.
Корінь: Втрата мережі, невідповідний MTU або перевантажені ліміти listen/conntrack; «повільно» насправді — «не можна з’єднатись надійно».
Виправлення: Перевірте retransmits, drops, conntrack; підтвердіть MTU від кінця до кінця; налаштуйте backlog; зменшіть churn з’єднань.
7) «Ми нічого не міняли» (ні, ви міняли)
Симптоми: Інцидент збігається з відсутністю деплоїв додатків, але поведінка змінилася.
Корінь: Оновлення ядра/прошивки, зміни базового образу, оновлення залежностей, дріфт конфігів або зміна значення за замовчуванням.
Виправлення: Відстежуйте зміни по всьому стеку. Ставтесь до «платформних» розгортань так само серйозно, як до змін додатків.
Чек‑лісти / покроковий план для безпечніших змін в один рядок
Якщо ви хочете менше відмов — перестаньте покладатися на пам’ять і добрі наміри.
Використовуйте явні шлюзи. Системі байдуже, що ви були зайняті.
Покроково: як зробити зміну в один рядок, не отримавши тижневий інцидент
-
Класифікуйте радіус ураження.
Запитайте: цей рядок застосовується до одного вузла, одного сервісу, одного регіону чи всієї компанії?
Якщо він глобальний — ставтеся до нього як до деплоя коду. -
Назвіть режим відмови, який готові прийняти.
Зниження таймауту означає, що ви погоджуєтеся на більше повторів і відмов під час повільності.
Зміна налаштувань надійності означає, що ви погоджуєтеся на можливу втрату даних. Скажіть це вголос. -
Запишіть план відкату до зміни.
Якщо відкат вимагає «пізніше», у вас немає плану відкату. -
Побудуйте чесний канар.
Один вузол без реального трафіку — не канар; це лабораторія.
Використовуйте реальний трафік (shadow якщо потрібно) і вимірюйте p95/p99. -
Визначте «сигнали зупинки».
Приклади: p99 latency > X протягом Y хвилин, error rate > Z, глибина черги > Q, disk await > A. -
Розгорніть зміну з інструментами спостережуваності.
Додайте метрики/логи, що підтверджують очікуваний ефект зміни.
Якщо ви не можете це виміряти — не чіпайте в продакшн. -
Розгортайте поступово, з паузами.
Використовуйте staged rollout: 1 вузол → 1% → 10% → 50% → 100%.
Зупиняйтесь між етапами достатньо довго, щоб побачити хвостову поведінку. -
Захищайтеся від штормів повторів.
Забезпечте бюджети повторів на боці клієнта. Додайте джиттер. Обмежте конкурентність. -
Задокументуйте рішення.
Покладіть мотивацію поруч із рядком у коментарях коду або в описі зміни.
Майбутній ви буде втомленим і підозрілим. -
Перевірка після зміни.
Підтвердіть не лише «воно запущено», а що цільова метрика рухнула у бажаному напрямку без регресій хвоста.
Чого уникати (суб’єктивно, бо я люблю спати)
- Не міняйте таймаути без зміни політики повторів.
- Не тимчасово вимикайте бар’єри надійності заради дедлайну.
- Не розгортайте платформні оновлення без application‑aware канарів.
- Не довіряйте середнім. Дивіться p95/p99 і глибину черги.
- Не сприймайте репозиторій конфігів як «безпечний» лише тому, що це не код. Це виконуваний намір.
FAQ
1) Чому малі зміни конфігурації спричиняють більше відмов, ніж великі зміни коду?
Тому що конфіги зазвичай глобальні, швидко застосовуються і погано тестуються. Зміни коду часто проходять через CI, рев’ю та staged deploy.
Зміна конфігу може оминути все це і при цьому вплинути на все.
2) Який найшвидший спосіб зрозуміти, чи повільність пов’язана зі сховищем?
Перевірте mpstat на високий iowait, потім iostat -x на await, aqu-sz і %util.
Далі використайте iotop, щоб виявити записувача/читача. Якщо диск насичений — сховище принаймні частина історії.
3) Чи завжди погано мати повтори?
Повтори потрібні, але неконтрольовані повтори — це DDoS‑атака, яку ви непередбачувано запускаєте самі проти себе.
Використовуйте бюджети повторів, експоненційний backoff, джиттер і circuit breakers.
4) Чи слід за замовчуванням відкотити одразу?
Якщо інцидент почався відразу після зміни і відкат безпечний — так.
Дебаг у живій системі привабливий, але часто повільніший, ніж відкат. Винятки: міграції схем, односпрямовані трансформації даних або інциденти безпеки.
5) Як підбирати таймаути без гадань?
Використовуйте продакшн‑розподіли латентності залежностей. Встановлюйте таймаути вище реалістичного p99.9 плюс запас.
Потім переконайтесь, що політика повторів не множить навантаження під деградацією.
6) Яка найпоширеніша «однорядкова» помилка зі сховищем?
Зміна семантики надійності (write barriers, sync‑поведінка) заради продуктивності.
Це може чудово виглядати в бенчмарках і перетворитися на корупцію або втрату даних після крешу чи відключення живлення.
7) Чому вмикання стиснення іноді робить використання диску гіршим?
Стиснення може підвищити навантаження на CPU, що сповільнює індексацію/компакти, збільшуючи write amplification і тимчасовий простір для сегментів/компактацій.
Також несжимні дані все одно додають метадані й накладні витрати на обробку.
8) Що таке «радіус ураження» у практичному сенсі?
Це скільки користувачів і систем постраждають, коли зміна піде не так.
Один вузол — це подряпина. Глобальна конфігурація — це лісова пожежа. Плануйте відповідно.
9) Як робити зміни конфігів безпечнішими, не сповільнюючи все?
Ставтеся до конфігів як до коду: валідація, канарі, staged rollout, автоматичні тригери відкату і журнал аудиту.
Ви рухатиметесь швидше загалом, бо проводитимете менше часу в інцидентах.
10) Що робити, якщо не можна зробити канар, бо зміна працює лише глобально?
Тоді потрібен синтетичний канар: дублікат трафіку, shadow‑читання або паралельне середовище, що віддзеркалює ключові залежності.
Якщо ви справді не можете безпечно валідовати, зміна за своєю суттю ризикована — плануйте її як таку.
Висновок: наступні кроки, що реально зменшують кількість відмов
Великі системи не карають вас за поганий код. Вони карають за відправлення неперевірених припущень у машину, що масштабно їх підсилює.
«Один рядок» — не лиходій; лиходій — невпорядкований радіус ураження у поєднанні зі слабкими зворотними зв’язками.
Практичні кроки:
- Виберіть одну високоризикову поверхню конфігурації (таймаути, повтори, опції монтування, кешування) і поставте її за staged rollout.
- Додайте автоматичну валідацію конфігів у CI/CD, щоб опечатки і невідповідності схем не дійшли до проду.
- Інструментуйте хвостову латентність і глибину черги всюди, де є межа залежностей.
- Напишіть (і програйте) процедури відкату, які не вимагають героїзму.
- Нормалізуйте «нудну правильність»: канарі, паузи і сигнали зупинки. Це дешевше за адреналін.
Якщо ви нічого не візьмете: ставтеся до малих змін як до потенційно великих подій, бо в продакшні масштаб — множник, а час — податок.
Заплатіть наперед.