Як ШІ перепише графічний конвеєр: кадр, що наполовину згенерований

У будь-якої команди з розробки графіки знайоме відчуття: демо показує 60 кадрів/с на машині провідного інженера, а на реальному залізі розсипається в ривки — саме коли
починається запис для маркетингу. Тепер додайте ШІ у конвеєр: ви вже не лише доставляєте шейдери й текстури; ви доставляєте модель, runtime, драйвери й
«корисні» евристики, які можуть перетворити один невдалий кадр на цілу секунду візуального каяття.

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

Що насправді означає «наполовину згенерований»

Коли говорять «графіка зі ШІ», зазвичай мають на увазі одне з трьох: (1) масштабування низькороздільного рендеру до високого, (2) генерація проміжних кадрів між
«реальними» кадрами, або (3) денойзінг зображення, створеного шумним рендерером (path tracing, стохастичні ефекти). Це мейнстримні, комерційні, «працює у вівторок»
випадки.

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

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

Ментальна модель, яка не підведе

Розглядайте гібридний кадр як багатостадійну транзакцію зі суворими бюджетами:

  • Вхідні дані: глибина, вектори руху, експозиція, джиттер, попередні кадри, іноді нормалі/альбедо.
  • Класичний рендер: растеризація, обчислення, можливо часткове трасування променями з меншим числом семплів/роздільністю.
  • Нейронний висновок: відновлення або синтез пропущених деталей з вхідних даних та історії.
  • Композиція: HUD/UI, елементи з альфою, пост-ефекти, що мають залишатися чіткими й стабільними.
  • Презентація: планування кадрів, VRR, генерація кадрів, наслідки для захоплення/стрімінгу.

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

Де ШІ входить у конвеєр (і де не повинен)

1) Масштабування: купуємо пікселі математикою

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

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

2) Генерація кадрів: купуємо час прогнозом

Генерація кадрів (FG) вставляє кадри, синтезовані ШІ, між «реальними» кадрами. Це не те саме, що подвоєння продуктивності. Ви міняєте затримку і
ризикуєте іноді галюцинаціями заради більш гладкого руху. Для деяких ігор це прийнятно, для інших — катастрофа, а для конкурентних продуктів — складно.

Основне питання для SRE: яке ваше SLO по затримці? Якщо ви не можете відповісти, ви фактично кидаєте кості з ввідними даними користувача. Іноді кістки покажуть
«маслянистість», іноді — «чому мій паррі не влучив?»

3) Денойзінг: купуємо семпли априорі

Денойзери — місце, де нейронні підходи виглядають неминучими. Path tracing дає фізично правдоподібне освітлення, але шумне при низькій кількості семплів. Нейронні
денойзери перетворюють кілька семплів у прийнятний результат, покладаючись на навчені апріорі. Чудово — поки апріорі не підходять для вашого контенту.

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

4) Де ШІ не повинно володіти контролем (якщо вам не подобаються пожежі)

  • UI і текст: тримайте їх у нативній роздільності, пізніше в конвеєрі. Не дозволяйте тимчасовій реконструкції розмивати вашу типографіку.
  • Зворотний зв’язок у змагальних іграх (hit feedback): якщо ШІ може створити або видалити підказку, ви отримаєте звіти про баги у формі юридичних загроз.
  • Візуалізації, критичні для безпеки: тренувальні симулятори, медична візуалізація, усе, де галюцинація стає відповідальністю.
  • Детерміновані системи відтворення: якщо ваша гра покладається на точні replays, стадії ШІ треба робити детермінованими або виключати.

Бюджети затримки: єдина істина

Старі диспути про рендеринг стосувалися fps. Нові — про таймінг і загальну затримку від вводу до фотона. ШІ зазвичай покращує середню пропускну здатність,
але погіршує хвостову затримку, бо інференс має ефекти кешування, особливості планування драйвера і іноді повільні шляхи (компіляція шейдерів, розігрів
моделі, сторінгу пам’яті, переходи станів живлення).

Продуктивний конвеєр потребує бюджетів, що виглядають як SLO:

  • Час кадру p50: нормальний випадок.
  • Час кадру p95: те, що користувач пам’ятає як «ривок».
  • Час кадру p99: те, що стрімери кліпують і роблять меми.
  • Затримка ввід→фотон: те, що відчувають конкурентні гравці в руках.
  • Резерв VRAM: те, що запобігає періодичному сторінгу і катастрофічним сплескам.

Генерація кадрів ускладнює математику, бо у вас два годинники: каденс симуляції/рендеру і каденс дисплея. Якщо ваша симуляція працює на 60, а дисплей — на 120 з
згенерованими кадрами, рух виглядає плавнішим, але затримка вводу прив’язується до каденсу симуляції плюс буферизація. Це не моральна оцінка. Це фізика плюс черги.

Надійний гібридний конвеєр робить дві речі ревно:

  1. Вимірює затримку явно (не лише fps).
  2. Тримає запас у VRAM, GPU-часі й CPU-подачі, щоб сплески не перетворювалися на відмови.

Цитата, яку варто приклеїти до монітора, бо вона тут як ніколи доречна: «Сподівання — не стратегія.» — Джин Кранц.

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

Шляхи даних і телеметрія: ставтеся до кадрів як до транзакцій

Класичне налагодження графіки вже складне: мільйон рухомих частин, «чорні ящики» драйверів і таймінг-залежні баги. ШІ додає нову категорію «мовчазно хибно»:
кадр виглядає правдоподібно, але не є вірним. Гірше: він залежить від контенту. Баг проявляється лише на певній мапі, у певний час доби, з певним ефектом частинок,
після прогріву GPU.

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

  • Часи GPU за стадіями: базовий рендер, інференс, постпроцес, презентація.
  • Глибина черги й зворотний тиск: чи накопичуються кадри десь?
  • VRAM-виділення з часом: не лише «використано», а «фрагментовано» і «виключено з пам’яті».
  • Метрики інференсу: версія моделі, режим точності, форма батчу, розігрітий/холодний стан.
  • Індикатори якості: відсоток дійсних векторів руху, рівень дисоціації, покриття реактивних масок.

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

Факти й історичний контекст, що пояснюють сучасні компроміси

  • 1) Тимчасове згладжування (TAA) популяризувало ідею, що «поточного кадру недостатньо». Сучасні апскейлери успадкували цю парадигму.
  • 2) Ранні GPU-конвеєри були з фіксованою функцією; програмованість (шейдери) перетворила графіку на програмне забезпечення, а ПО завжди притягує автоматизацію.
  • 3) Офлайн-рендерери використовували денойзінг задовго до реального часу; фільмові пайплайни довели, що можна обміняти семпли на інтелектуальну реконструкцію.
  • 4) Checkerboard-рендеринг на консолях був попередником ML-апскейлінгу: менш пікселів у рендері, реконструкція решти за допомогою патернів і історії.
  • 5) Вектори руху існували для розмиття руху і TAA до того, як стали критичними вхідними даними для ШІ; тепер неправильний буфер швидкостей — це відмова якості.
  • 6) Апаратне трасування променів зробило «шумне, але коректне» можливим; нейронні денойзери зробили «відправляємо в реліз» реалістичним у бюджеті реального часу.
  • 7) Індустрія навчилася на інцидентах зі стримінгом текстур: сплески VRAM не відпрацьовують плавно — вони провалюються немов люк під ногами.
  • 8) Консолі змусили думати про детерміновану продуктивність; ШІ вводить варіативність, якщо ви на це не спроектуєте.
  • 9) Відеокодеки вже роблять прогнозування з компенсацією руху; генерація кадрів концептуально суміжна, але мусить витримувати інтерактивність.

Нові режими відмов у гібридному рендері

Таксономія артефактів, яка вам справді знадобиться

  • Примарність (ghosting): надто велика довіра до історії; вектори руху неправильні або дисоціація не оброблена.
  • Мерехтіння (shimmering): тимчасова нестабільність; експозиція, джиттер або петля відновлення створюють шум.
  • Розмазування (smearing): інференс згладжує деталі, які мають бути високочастотними (листя, тонкі дроти).
  • Вигадані краї (hallucinated edges): апскейлер винаходить структуру; зазвичай через недостатньо чіткі входи.
  • Контамінація UI: тимчасова стадія бачить UI як сцену і тягне його через час.
  • Відчуття затримки: генерація кадрів і буферизація; іноді ускладнено неправильно налаштованими режимами низької затримки.
  • Випадкові сплески: сторінкування VRAM, розігрів моделей, компіляція шейдерів, зміни стану живлення, фонові процеси.

Пастка надійності: ШІ ховає борги рендерингу

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

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

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

Плейбук швидкої діагностики

Коли продуктивність або якість йдуть не так, не починайте сперечатися «ШІ чи растер». Почніть з виявлення вузького місця за безжальним, поетапним підходом.
Мета — визначити, який бюджет зламався: GPU-час, CPU-подача, VRAM або затримка/таймінг.

Перше: підтвердьте, що симптом — це планування, а не середній FPS

  • Перевірте сплески часу кадру p95/p99 і чи корелюють вони з переходами сцени, різкими відрізками камери або ефектами.
  • Підтвердьте, чи стрибки співпадають з тиском на VRAM або подіями компіляції шейдерів.
  • Перевірте, що шлях дисплея (VRR, vsync, лімітатор) відповідає тестовим припущенням.

Друге: ізолюйте «базовий рендер» vs «інференс ШІ» vs «present»

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

Третє: перевірте запас VRAM і сторінкування

  • Якщо VRAM у межах 5–10% від ліміту, припускайте, що під реальним навантаженням ви будете сторінгувати.
  • Шукайте періодичні сплески: вони часто співпадають із стримінгом, подібним до GC ростом виділень або фоновим захопленням.
  • Підтвердьте, що ваги моделі резидентні і не перевантажуються повторно через втрату контексту або тиск пам’яті.

Четверте: валідуйте входи й цілісність історії

  • Вектори руху: правильний простір, правильний масштаб, коректна обробка для скінінгу та частинок.
  • Глибина: стабільна точність і послідовне відображення near/far; уникайте «корисних» розбіжностей reversed-Z між проходами.
  • Скидання історії при різких переходах: якщо не скидаєте, модель спробує склеїти два не пов’язані кадри.

П’яте: контроль регресій

  • Фіксуйте версії драйверів для базових тестів QA. Не налагоджуйте два рухомі цілі одночасно.
  • Фіксуйте версії моделей і режими точності. Якщо не можете відтворити — не зможете виправити.
  • Використовуйте feature-флаги з аварійними вимикачами, якими ops можуть керувати без перебілдування.

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

Це перевірки рівня ops, які ви можете виконати на робочій станції Linux або на build-сервері. Вони не розв’яжуть ваш код шейдерів, але скажуть, чи ви боретеся з GPU,
стеком драйвера, тиском пам’яті або власним процесом.

Завдання 1: Ідентифікувати GPU і драйвер у точному середовищі

cr0x@server:~$ lspci -nn | grep -Ei 'vga|3d|display'
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation AD104 [GeForce RTX 4070] [10de:2786] (rev a1)

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

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

Завдання 2: Підтвердити версії ядра і прошивки

cr0x@server:~$ uname -r
6.5.0-21-generic

Що це означає: Оновлення ядра можуть змінити поведінку DMA, планування і налаштування IOMMU — достатньо, щоб змінити ривки.

Рішення: Фіксуйте ядро для базових тестів продуктивності. Оновлюйте свідомо, а не випадково.

Завдання 3: Підтвердити версію драйвера NVIDIA (або еквівалентного стека)

cr0x@server:~$ nvidia-smi
Wed Jan 21 10:14:32 2026
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.14              Driver Version: 550.54.14      CUDA Version: 12.4     |
|-----------------------------------------+------------------------+----------------------|
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 4070        Off |   00000000:01:00.0  On |                  N/A |
| 30%   54C    P2              95W / 200W |     7420MiB / 12282MiB |     78%      Default |
+-----------------------------------------+------------------------+----------------------+

Що це означає: Видно версію драйвера і використання VRAM. 7.4 GiB — не тривожно; 11.8/12.2 — вже так.

Рішення: Якщо VRAM постійно >90%, вважайте це ризиком сторінгу і зменште бюджети (текстури, буфери RT, розмір моделі, буфери історії).

Завдання 4: Слідкувати за VRAM і завантаженням у часі, щоб ловити сплески

cr0x@server:~$ nvidia-smi dmon -s pucm -d 1 -c 5
# gpu   pwr gtemp mtemp    sm   mem   enc   dec  mclk  pclk
# Idx     W     C     C     %     %     %     %   MHz   MHz
    0    94    55     -    81    63     0     0  9501  2580
    0   102    56     -    88    66     0     0  9501  2610
    0    73    53     -    52    62     0     0  9501  2145
    0   110    57     -    92    70     0     0  9501  2655
    0    68    52     -    45    61     0     0  9501  2100

Що це означає: Видно сплески. Якщо mem% піднімається, а потім падає — можливо, відбувається сторінг або агресивні переаліграції.

Рішення: Корелюйте сплески з подіями рушія (зони стримінгу, кат-сцени). Додавайте попереднє прогрівання або обмежуйте виділення.

Завдання 5: Підтвердити ширину/швидкість PCIe (приховані гальма)

cr0x@server:~$ sudo lspci -s 01:00.0 -vv | grep -E 'LnkCap|LnkSta'
LnkCap: Port #0, Speed 16GT/s, Width x16
LnkSta: Speed 16GT/s, Width x16

Що це означає: Ви не застрягли на x4, бо хтось використав неправильний слот або налаштував BIOS неправильно.

Рішення: Якщо лінк знижено, виправте апаратне розташування/BIOS перед тим, як «оптимізувати» рендер під прес.

Завдання 6: Перевірити масштабування частоти CPU (вбивця планування кадрів)

cr0x@server:~$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
powersave

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

Рішення: Для тестів продуктивності встановіть governor у performance і документуйте це, інакше ваші результати — вигадка.

Завдання 7: Встановити режим продуктивності під час контрольованих бенчмарків

cr0x@server:~$ sudo cpupower frequency-set -g performance
Setting cpu: 0
Setting cpu: 1
Setting cpu: 2
Setting cpu: 3

Що це означає: CPU триматиме вищі частоти більш послідовно.

Рішення: Якщо ривки зникають, у вас проблема з плануванням/живленням CPU, а не з «повільним ШІ».

Завдання 8: Перевірити тиск пам’яті і активність swap

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            62Gi        41Gi       3.1Gi       1.2Gi        18Gi        19Gi
Swap:          8.0Gi       2.4Gi       5.6Gi

Що це означає: Використання swap свідчить про те, що система сторінгує. Це може проявлятися періодичними сплесками й підвисаннями активів.

Рішення: Зменшіть слід пам’яті, виправте витоки або додайте RAM. Не прикидайтеся, що налаштування GPU виправить сторінг на хості.

Завдання 9: Виявити топ споживачів CPU (фонові інструменти захоплення часто злочинці)

cr0x@server:~$ ps -eo pid,comm,%cpu,%mem --sort=-%cpu | head
  PID COMMAND         %CPU %MEM
 4121 chrome          38.2  4.1
 9332 obs             22.7  1.9
 7771 game-bin        18.4  6.8
 1260 Xorg             9.2  0.6
 2104 pulseaudio       3.1  0.1

Що це означає: Ваш «бенчмарк» конкурує з браузером і стрімінговим інструментом.

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

Завдання 10: Перевірити затримки дискового вводу/виводу (стримінг активів і завантаження моделей)

cr0x@server:~$ iostat -xz 1 3
Linux 6.5.0-21-generic (server) 	01/21/2026 	_x86_64_	(16 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.41    0.00    3.28    2.91    0.00   81.40

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz aqu-sz  %util
nvme0n1         92.0   18240.0     0.0   0.00    3.12   198.3      44.0    5280.0     2.0   4.35    5.44   120.0   0.36  18.40

Що це означає: r_await/w_await помірні. Якщо ви бачите очікування 50–200 мс, у вас будуть підвисання незалежно від GPU.

Рішення: Якщо сховище повільне, виправте стримінг (prefetch, стиснення, пакування) перед тим, як чіпати налаштування інференсу.

Завдання 11: Перевірити місце на файловій системі (логи й кеші можуть заповнити диск під час запуску)

cr0x@server:~$ df -h /var
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2  220G  214G  6.0G  98% /

Що це означає: Ви на відстані однієї запеклої сесії логування від поганого дня.

Рішення: Звільніть місце або перенаправте кеші/логи. Повний диск може зламати кеші шейдерів, кеші моделей і запис дампів.

Завдання 12: Інспектувати лічильники помилок GPU (нестабільність апаратного/драйверного рівня)

cr0x@server:~$ sudo journalctl -k -b | grep -Ei 'nvrm|gpu|amdgpu|i915' | tail
Jan 21 09:58:11 server kernel: NVRM: Xid (PCI:0000:01:00): 31, pid=7771, name=game-bin, Ch 0000002c, intr 00000000
Jan 21 09:58:11 server kernel: NVRM: GPU at PCI:0000:01:00: GPU has fallen off the bus.

Що це означає: Це не проблема оптимізації. Це інцидент стабільності: скидання драйвера, проблема живлення або апаратний дефект.

Рішення: Припиніть тонке налаштування якості. Відтворіть під стрес-тестами, перевірте живлення, температури і відомі проблеми драйвера.

Завдання 13: Перевірити частоти GPU і причини тротлінгу

cr0x@server:~$ nvidia-smi -q -d CLOCK,PERFORMANCE | sed -n '1,80p'
==============NVSMI LOG==============

Performance State                          : P2
Clocks
    Graphics                               : 2580 MHz
    Memory                                 : 9501 MHz
Clocks Throttle Reasons
    Idle                                   : Not Active
    Applications Clocks Setting            : Not Active
    SW Power Cap                           : Not Active
    HW Slowdown                            : Not Active
    HW Thermal Slowdown                    : Not Active

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

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

Завдання 14: Підтвердити, що файли моделі не підвантажуються повторно (thrash кешу)

cr0x@server:~$ lsof -p $(pgrep -n game-bin) | grep -E '\.onnx|\.plan|\.bin' | head
game-bin 7771 cr0x  mem REG  259,2  31248768  1048612 /opt/game/models/upscaler_v7.plan
game-bin 7771 cr0x  mem REG  259,2   8421376  1048620 /opt/game/models/denoiser_fp16.bin

Що це означає: Ваги моделі змеплені в пам’ять. Добре. Якщо ви бачите повторні open/close у трейсах, ви платите за завантаження під час гри.

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

Завдання 15: Перевірити поведінку кешу шейдерів (стриби компіляції часто звинувачують ШІ)

cr0x@server:~$ ls -lh ~/.cache/nv/GLCache | head
total 64M
-rw------- 1 cr0x cr0x 1.2M Jan 21 09:40 0b9f6a8d0b4a2f3c
-rw------- 1 cr0x cr0x 2.8M Jan 21 09:41 1c2d7e91a1e0f4aa
-rw------- 1 cr0x cr0x 512K Jan 21 09:42 3f4a91c2d18e2b0d

Що це означає: Кеш існує і заповнюється. Якщо він порожній кожен запуск, ваше середовище його очищає або права невірні.

Рішення: Забезпечте збереження кешів шейдерів у тестах і продакшені. Інакше ви гонитиметеся за «випадковими» сплесками часу кадру вічно.

Завдання 16: Виміряти джиттер планування на хості (корисно для таймінгу потоку рендеру)

cr0x@server:~$ sudo cyclictest -m -Sp90 -i200 -h400 -D5s | tail -n 3
T: 0 ( 2345) P:90 I:200 C: 25000 Min:    5 Act:    7 Avg:    9 Max:  112
T: 1 ( 2346) P:90 I:200 C: 25000 Min:    5 Act:    6 Avg:    8 Max:   98
T: 2 ( 2347) P:90 I:200 C: 25000 Min:    5 Act:    6 Avg:    8 Max:  130

Що це означає: Максимальний джиттер ~100µs зазвичай прийнятний. Якщо бачите багатомілісекундний джиттер — ОС сильно вас перериває.

Рішення: Для відтворюваного профілювання ізолюйте CPU, приборкайте фоні служби і уникайте «гучних» сусідів (VM, режими живлення ноутбука).

Три корпоративні міні-історії з поля бою

Міні-історія №1: Інцидент через хибне припущення

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

За кілька годин посипалися заявки в підтримку: періодичні підвисання, переважно на середньому класі GPU з 8–10 GiB VRAM. Підвисання не з’являлися одразу. Вони
проявлялися через 20–30 хвилин, часто після кількох переходів мапи. Команда звинувачувала компіляцію шейдерів. Запахло компіляцією.

Хибне припущення: нова модель буде «приблизно того ж розміру» в VRAM, бо мала схожу роздільність входу/виходу. Але шлях інференсу рушія тихо увімкнув буфер
проміжної вищої точності для нової моделі. Додайте трохи більший буфер історії і агресивнішу реактивну маску — і запас VRAM зник.

На тих GPU драйвер почав викидати ресурси. Не завжди одні й ті ж. Патерн викиду залежав від того, що ще було резидентним: текстури, структури пришвидшення RT,
карти тіней, інструменти захоплення. «Шейдерні ривки» насправді були churn пам’яті і періодичними перевантаженнями.

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

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

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

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

Оптимізація знизила точність саме в тих місцях, на які покладався апскейлер: субпіксельні рухи й точні глибини на межах. Модель була натренована, припускаючи певний
розподіл помилок руху; нова упаковка його змінила. Не настільки, щоб ламати кожен кадр. Але достатньо, щоб ламати складні випадки.

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

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

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

Платформна команда відповідала за рантайм, що завантажував моделі, вибирав режими точності і домовлявся з графічним бекендом. Нічого ефектного. Ніхто не писав блогів.
Але в них була одна практика, що виглядала як паперова робота: кожний артефакт моделі трактували як деплой з семантичним версіонуванням і changelog, що включав
припущення про входи.

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

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

Вони перевернули аварійний вимикач, щоб вимкнути генерацію кадрів для тієї гілки драйвера, лишивши апскейлінг і денойзінг увімкненими. Гра залишилася придатною.
QA повернувся до стабільної бази. Паралельно вони працювали з вендором над мінімальним repro і перевірили його на зафіксованій матриці.

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

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

1) Симптом: Примарні сліди за рухомими персонажами

Корінь: Вектори руху неправильні для скінінгу, частинок або вершинної анімації; відсутня маска дисоціації.

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

2) Симптом: Текст UI розмивається або «ехо» під час руху камери

Корінь: UI композиціюють до тимчасової реконструкції, або UI протікає в буфери історії.

Виправлення: Компонуйте UI після апскейлінгу/денойзингу; виключайте UI-таргети з історії і проходів векторів руху.

3) Симптом: Продуктивність у бенчмарках нормальна, але жахлива через 30 хвилин

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

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

4) Симптом: Генерація кадрів плавна, але ввід відчувається повільним

Корінь: Каденс відображення роз’єднано від каденсу симуляції; зайва буферизація; неправильно налаштований режим затримки.

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

5) Симптом: Мерехтіння на листі і тонкій геометрії

Корінь: Тимчасова нестабільність від недосемплювання плюс недостатня реактивна маска; втрата точності в глибині/швидкості; агресивне підвищення різкості.

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

6) Симптом: Раптовий чорний кадр або пошкоджений кадр іноді

Корінь: Скидання драйвера GPU, відновлення типу TDR, вихід за межі в compute-проході або шлях відмови рантайму моделі, що не опрацьовано.

Виправлення: Захопіть логи ядра; додайте надійний fallback, коли інференс відмовляє; валідуйте межі і стани ресурсів; ескалюйте як інцидент стабільності, а не «якість».

7) Симптом: «Воно відбувається лише на GPU одного вендора»

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

Виправлення: Створіть базові лінії для кожного вендора; обмежте точність; тестуйте по вендору і архітектурі; не припускайте, що «один API = однакова поведінка».

8) Симптом: Артефакти з’являються після різкого переходу камери або респавну

Корінь: Історію не скинули; модель намагається зіставити нерелевантні кадри.

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

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

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

  1. Визначте бюджети: час кадру p95 і p99, цільовий резерв VRAM, ціль ввід→фотон. Запишіть їх. Зробіть їх примусовими.
  2. Версіонуйте все: версія моделі, версія рантайму, базова версія драйвера, feature-флаги. Логуйте їх по кадру в debug-збірках.
  3. Побудуйте сходи fallback: нативний рендер → класичний TAAU → ML-апскейлер → ML + генерація кадрів. Кожен крок має бути релізним.
  4. Валідуйте входи: вектори руху (всі типи геометрії), точність глибини, стабільність експозиції, обробка альфи, виявлення дисоціації.
  5. Створіть тимчасовий тестовий набір: швидкі панорами, листя, бурі частинок, різкі переходи камери, респавни, UI-оверлеї. Автоматизуйте захоплення й метрики.
  6. Резервуйте VRAM: явно бюджетуйте буфери історії і ваги моделей; не «подивимося, що станеться».
  7. Прогрійте: прекомпілюйте шейдери, ініціалізуйте інференс, попередньо виділіть RT де можливо. Сховайте це за екранами завантаження.
  8. Інструментуйте часи по стадіях: базовий рендер, інференс, пост, present; включіть глибину черг і метрики планування.
  9. Контролюйте хвостову затримку: лімітуйте найгіршу роботу; уникайте алокацій у кадрі; стежте за фоновою конкуренцією CPU.
  10. Добавте аварійні перемикачі: ops потрібні перемикачі для вимкнення FG або заміни моделі на меншу без повного ребілду.
  11. Документуйте компроміси для гравців: плавність vs затримка, режими якості vs стабільність. Якщо ви це ховаєте, гравці відкриють це голосно.
  12. Запустіть тести на витривалість: 2–4 години, кілька переходів мап, шляхи з інтенсивним стримінгом. Більшість «помилок ШІ» — насправді проблеми ресурсів, що проявляються з часом.

Контрольний список: перед тим як звинувачувати модель

  • Чи запас VRAM >10% у найважчих сценах?
  • Чи вектори руху валідні для кожного шляху рендеру (скінінг, частинки, верт. анімація)?
  • Чи ви скидаєте історію при різких переходах і некоректних кадрах?
  • Чи компонуєте UI після тимчасових стадій?
  • Чи зафіксовані версії драйвера і моделі для відтворення?
  • Чи можна відтворити з вимкненим ШІ? Якщо ні — ваше середовище вимірювань під сумнівом.

ЧаПи

1) Чи «наполовину згенерований кадр» — просто маркетинг для апскейлінгу?

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

2) Чи підвищує генерація кадрів продуктивність або просто її приховує?

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

3) Який головний операційний ризик при додаванні ШІ до рендерингу?

Хвостова затримка і поведінка пам’яті. Середній час кадру може покращитись, тоді як p99 погіршиться через викиди VRAM, розігріви, планування драйвера або рідкісні
повільні шляхи.

4) Чому артефакти часто з’являються на листі і тонкій геометрії?

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

5) Чи можна зробити стадії ШІ детермінованими для реплеїв?

Частково. Можна обмежити точність, зафіксувати seed-и, уникати недетермінованих ядер і фіксувати рантайми/драйвери. Але детермінізм через вендорів і версії драйверів
— складне завдання. Якщо детерміновані реплеї — вимога продукту, проектуйте режим детермінізму з першого дня.

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

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

7) Як тестувати «якість» без покладання на суб’єктивні скриншоти?

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

8) Що ops мають вимагати від графічних команд перед тим, як за замовчуванням увімкнути генерацію кадрів?

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

9) Чому «на моїй машині працює» стає гірше з ШІ?

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

Висновок: що робити наступного тижня

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

Наступного тижня зробіть ці практичні речі:

  • Визначте цілі p95/p99 часу кадру і ввід→фотон, і зробіть їх воротами для релізу.
  • Додайте маніфести по кадру: версії моделі/рантайму/драйвера і ключові перемикачі, логовані в debug-збірках.
  • Побудуйте протестовану сходинку fallback і підключіть її до аварійного перемикача, яким ops можуть керувати.
  • Відстежуйте запас VRAM і ризик сторінгу як первинну метрику, а не як думку після думки.
  • Автоматизуйте тимчасові «тюремні» сцени і валідуйте вектори руху, ніби від цього залежить ваша робота — бо так і є.

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

Proxmox — диски не визначаються: швидкий чекліст для HBA, BIOS і кабелів

Немає нічого веселіше для вихідних, ніж завантажити вузол Proxmox і виявити, що ваші блискучі нові диски зникли ніби привид. Інсталятор нічого не показує. lsblk — пустеля. ZFS-пули зникають. Присягаєтеся, що диски були учора.

Це польовий чекліст для людей у виробництві: інженерів збереження даних, SRE та нещасливих on-call, які отримали у спадок «просте» розширення дискового простору. Ми швидко обшукаємо домен відмов: BIOS/UEFI, прошивка та режим HBA, PCIe, дивні речі з кабелями/бекплейнами/експандерами, драйвери Linux та підводні камені, які роблять диски «присутніми», але невидимими.

Швидкий план діагностики (робіть у цій послідовності)

0) Визначте, що означає «не визначається»

  • Не в BIOS/UEFI: апаратні проблеми, живлення, кабелі, бекплейн, перелічення HBA/PCIe.
  • У BIOS, але не в Linux: драйвер ядра/модуль, особливості IOMMU, зламана прошивка, помилки PCIe AER.
  • В Linux, але не в інтерфейсі Proxmox: неправильний екран, існуючі розділи, multipath маскування, ZFS тримає пристрої, права або це під /dev/disk/by-id, але не очевидно.

1) Почніть з істини ядра

Запустіть ці три команди і поки що не імпровізуйте:

  1. dmesg -T | tail -n 200 (шукайте PCIe, SAS, SATA, NVMe, скидання ланцюга)
  2. lsblk -e7 -o NAME,TYPE,SIZE,MODEL,SERIAL,TRAN,HCTL (дивіться, що створило ядро)
  3. lspci -nn | egrep -i 'sas|raid|sata|nvme|scsi' (підтвердіть наявність контролера)

Рішення: Якщо контролера немає у lspci, перестаньте звинувачувати Proxmox. Це BIOS/усадження в слоті/розподіл ліній PCIe або карта померла.

2) Якщо контролер є — перевірте драйвер і зв’язок

  • lspci -k -s <slot> → перевірте «Kernel driver in use».
  • journalctl -k -b | egrep -i 'mpt3sas|megaraid|ahci|nvme|reset|timeout|aer' → знайдіть курця-пристрій.

Рішення: Модуль не прив’язаний? Завантажте модуль або виправте прошивку/налаштування BIOS. Скидання лінку/таймаути? підозрюйте кабелі/бекплейн/експандер/живлення.

3) Проскануйте знову перед перезавантаженням

Перескануйте SCSI/NVMe. Якщо диски з’являються після рескану — ви щось дізналися: гарячий підключення, тренування лінку або таймінг завантаження.

4) Якщо диски є, але «відсутні» в UI Proxmox

Перейдіть у CLI і використовуйте стабільні ідентифікатори. UI не бреше; він просто не ваш командир інциденту.

Рішення: Якщо вони існують у /dev/disk/by-id, але не в пулі — це історія ZFS/імпорту/розбиття на розділи, а не виявлення.

Практична модель: де диски можуть зникати

Виявлення дисків — це ланцюг. Якщо порвете будь-яке ланка — ви будете дивитися на порожній список.

Рівень 1: живлення та фізичне підключення

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

Рівень 2: інтерпозер/бекплейн/експандер

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

Рівень 3: прошивка та режим HBA/контролера

HBA можуть працювати як справжні HBA (IT-режим) або притворятися RAID-контролерами (IR/RAID). Proxmox + ZFS хоче нудного passthrough. RAID-персональність може сховати диски за віртуальними томами, блокувати SMART і ускладнювати відновлення помилок.

Рівень 4: перелічення PCIe та бюджет ліній

Сам контролер — PCIe-пристрій. Якщо материнська плата його не перелічує, Linux теж не зможе. Налаштування bifurcation, дротовка слоту і спільне використання ліній з M.2/U.2 можуть тихо зробити слот «фізично x16», але електрично x4 — або x0, якщо розсердити богів ліній.

Рівень 5: драйвери ядра Linux + створення вузлів пристроїв

Навіть коли апарат в порядку, ядро може не прив’язати правильний драйвер або udev може не створити вузли так, як ви очікуєте. Multipath може навмисно приховувати окремі шляхи. Старий initramfs може пропустити модулі. Диски можуть існувати, але під іншими іменами.

Рівень 6: презентація збереження в Proxmox

Proxmox VE — це Debian під UI. Якщо Debian не бачить — Proxmox не побачить. Якщо Debian бачить, але UI не показує там, де ви дивитесь — це проблема робочого процесу, а не апаратури.

Парафраз ідеї від John Allspaw: надійність походить від вміння реагувати на відмову, а не від імітації її відсутності.

Жарт №1: «RAID-режим зробить ZFS щасливим» — як сказати «я поставив кермо на тостер; тепер це автомобіль».

Цікаві факти та історія, що справді допомагає в усуненні неполадок

  • SCSI-сканування старе… і все ще тут. Сучасні SAS і навіть деякі SATA-стеки й досі покладаються на SCSI host scans, тому рескани можуть «знайти» диски без перезавантаження.
  • LSI SAS HBA стали де-факто стандартом у домашніх лабораторіях і підприємствах. Лінія Broadcom/Avago/LSI важлива, бо іменування драйверів (mpt2sas/mpt3sas) і інструменти для прошивки йдуть з нею.
  • IT-режим став популярним, бо файлові системи стали розумнішими. ZFS та подібні системи хочуть прямого доступу до дисків. RAID-контролери були створені для епохи, коли контролер відповідав за цілісність.
  • SFF-8087 і SFF-8643 виглядають як «просто кабелі», але це сигнальні системи. Частково втиснутий mini-SAS може живити диски й усе ж мати проблеми з лініями даних. Це не магія; це диференціальні пари і допуски.
  • PCIe-слоти іноді брешуть маркетингу. «x16 слот» часто означає «x16 конектор». Електрично він може бути x8 або x4 залежно від CPU і маршрутизації плати.
  • UEFI змінив поведінку option ROM. Деякі карти зберігання залежать від option ROM для екрану переліку під час завантаження; налаштування UEFI можуть приховати ті екрани, не змінюючи того, що бачить Linux.
  • NVMe приніс свій власний шлях виявлення. NVMe-пристрої не є «SCSI-дисками» і не показуватимуться у інструментах SAS HBA; вони використовують підсистему NVMe і тренування PCIe лінку.
  • SMART-прохідність не гарантована. На RAID-контролерах SMART-дані можуть бути заблоковані або вимагати фірмових інструментів, що змінює спосіб перевірки «диск існує».

Практичні завдання (команди + сенс + рішення)

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

Завдання 1: Підтвердіть, що контролер перелічено в PCIe

cr0x@server:~$ lspci -nn | egrep -i 'sas|raid|sata|scsi|nvme'
03:00.0 Serial Attached SCSI controller [0107]: Broadcom / LSI SAS3008 PCI-Express Fusion-MPT SAS-3 [1000:0097] (rev 02)
01:00.0 Non-Volatile memory controller [0108]: Samsung Electronics Co Ltd NVMe SSD Controller [144d:a808]

Що це означає: Материнська плата бачить HBA/NVMe контролер. Якщо цього немає, Linux ніколи не побачить диски за ним.

Рішення: Відсутній пристрій → пересадіть карту, змініть слот, перевірте налаштування PCIe в BIOS, відключіть конфліктні пристрої, перевірте живлення для riser-ів.

Завдання 2: Перевірте прив’язку драйвера в ядрі

cr0x@server:~$ lspci -k -s 03:00.0
03:00.0 Serial Attached SCSI controller: Broadcom / LSI SAS3008 PCI-Express Fusion-MPT SAS-3 (rev 02)
	Subsystem: Broadcom / LSI SAS9300-8i
	Kernel driver in use: mpt3sas
	Kernel modules: mpt3sas

Що це означає: Правильний драйвер приєднаний. Якщо «Kernel driver in use» порожнє — у вас проблема з драйвером/прошивкою/чорною списком.

Рішення: Драйвер не прив’язаний → перевірте modprobe, журнали ядра, Secure Boot, сумісність прошивки та чи ви використовуєте нестандартне ядро постачальника.

Завдання 3: Подивіться, які диски створило ядро (не довіряйте UI)

cr0x@server:~$ lsblk -e7 -o NAME,TYPE,SIZE,MODEL,SERIAL,TRAN,HCTL
NAME    TYPE  SIZE MODEL              SERIAL        TRAN HCTL
sda     disk  3.6T ST4000NM0035-1V4    ZC123ABC      sas  3:0:0:0
sdb     disk  3.6T ST4000NM0035-1V4    ZC123DEF      sas  3:0:1:0
nvme0n1 disk  1.8T Samsung SSD 990 PRO S6Z1NZ0R12345 nvme -

Що це означає: Якщо воно в lsblk, ядро його бачить. TRAN підкаже, чи це sas, sata, nvme.

Рішення: Якщо диски відсутні → спускайтесь вниз по стеку: dmesg, кабелі, експандер, живлення. Диски є, але Proxmox «відсутні» → ймовірно UI/робочий процес, multipath або імпорт ZFS.

Завдання 4: Перевірте журнали ядра на скидання/таймаути лінків

cr0x@server:~$ journalctl -k -b | egrep -i 'mpt3sas|megaraid|ahci|nvme|reset|timeout|aer|link down' | tail -n 60
Dec 26 10:12:01 server kernel: mpt3sas_cm0: log_info(0x31120101): originator(PL), code(0x12), sub_code(0x0101)
Dec 26 10:12:01 server kernel: sd 3:0:1:0: rejecting I/O to offline device
Dec 26 10:12:03 server kernel: pcieport 0000:00:1c.0: AER: Corrected error received: 0000:03:00.0
Dec 26 10:12:03 server kernel: nvme nvme0: I/O 42 QID 5 timeout, aborting

Що це означає: «offline device», «timeout», «link down», AER-спам = проблеми з цілісністю сигналу, живленням або несправним диском/контролером.

Рішення: Таймаути на кількох дисках → кабель/бекплейн/експандер/HBA. Таймаут на одному диску → цей диск або його слот.

Завдання 5: Перелічіть контролери збереження, які бачить ядро

cr0x@server:~$ lsscsi -H
[0]    ata_piix
[2]    mpt3sas
[3]    nvme

Що це означає: Підтверджує хост-адаптери. Якщо драйвер HBA завантажено, він з’являється як host.

Рішення: HBA відсутній тут, але є в lspci → драйвер не завантажився або не ініціалізувався.

Завдання 6: Перевірте SCSI-хости і перескануйте пристрої

cr0x@server:~$ ls -l /sys/class/scsi_host/
total 0
lrwxrwxrwx 1 root root 0 Dec 26 10:10 host0 -> ../../devices/pci0000:00/0000:00:17.0/ata1/host0/scsi_host/host0
lrwxrwxrwx 1 root root 0 Dec 26 10:10 host2 -> ../../devices/pci0000:00/0000:03:00.0/host2/scsi_host/host2
cr0x@server:~$ for h in /sys/class/scsi_host/host*/scan; do echo "- - -" > "$h"; done

Що це означає: Примусово сканує всі SCSI-хости. Якщо диски з’являться після цього — виявлення пов’язане з таймінгом/гарячим підключенням/поведінкою експандера.

Рішення: Якщо рескани послідовно «фіксують» проблему — перевірте налаштування BIOS для hotplug, розподіленого spin-up, прошивку експандера та прошивку HBA.

Завдання 7: Перевірте виявлення SATA/AHCI (вбудовані порти)

cr0x@server:~$ dmesg -T | egrep -i 'ahci|ata[0-9]|SATA link' | tail -n 40
[Thu Dec 26 10:10:12 2025] ahci 0000:00:17.0: AHCI 0001.0301 32 slots 6 ports 6 Gbps 0x3f impl SATA mode
[Thu Dec 26 10:10:13 2025] ata1: SATA link down (SStatus 0 SControl 300)
[Thu Dec 26 10:10:13 2025] ata2: SATA link up 6.0 Gbps (SStatus 133 SControl 300)

Що це означає: «link down» на порту з диском означає проблеми з кабелем/портом, вимкнення в BIOS або живленням.

Рішення: Якщо порти по всій дошці «link down», перевірте режим SATA в BIOS (AHCI) і чи не вимикає плата SATA, коли заповнено M.2.

Завдання 8: Перелічіть NVMe-пристрої і стан контролера

cr0x@server:~$ nvme list
Node             SN               Model                          Namespace Usage                      Format           FW Rev
---------------- ---------------- -------------------------------- --------- -------------------------- ---------------- --------
/dev/nvme0n1      S6Z1NZ0R12345    Samsung SSD 990 PRO 2TB        1         1.80  TB / 2.00  TB        512   B +  0 B   5B2QJXD7

Що це означає: NVMe присутнє як окрема підсистема. Якщо nvme list порожній, але lspci показує контролер — можливо проблема з драйвером, PCIe ASPM або тренуванням лінку.

Рішення: Порожній список → перевірте journalctl -k на помилки NVMe, налаштування BIOS щодо швидкості PCIe Gen і bifurcation слотів (для мульти-NVMe адаптерів).

Завдання 9: Підтвердіть стабільні ідентифікатори дисків (що слід використовувати для ZFS)

cr0x@server:~$ ls -l /dev/disk/by-id/ | egrep -i 'wwn|nvme|scsi' | head
lrwxrwxrwx 1 root root  9 Dec 26 10:15 nvme-Samsung_SSD_990_PRO_2TB_S6Z1NZ0R12345 -> ../../nvme0n1
lrwxrwxrwx 1 root root  9 Dec 26 10:15 scsi-35000c500a1b2c3d4 -> ../../sda
lrwxrwxrwx 1 root root  9 Dec 26 10:15 scsi-35000c500a1b2c3e5 -> ../../sdb
lrwxrwxrwx 1 root root  9 Dec 26 10:15 wwn-0x5000c500a1b2c3d4 -> ../../sda

Що це означає: Ці ID переживуть перезавантаження та перейменування пристроїв (sda стає sdb після змін апаратури).

Рішення: Якщо ваші скрипти пулу/імпорту використовують /dev/sdX, припиніть. Перейдіть на by-id/by-wwn перед наступним вікном обслуговування, щоб вас не з’їли проблеми зі зміною імен.

Завдання 10: Перевірте видимість SMART (каже, чи ви справді бачите диск)

cr0x@server:~$ smartctl -a /dev/sda | head -n 20
smartctl 7.3 2022-02-28 r5338 [x86_64-linux-6.8.12-4-pve] (local build)
=== START OF INFORMATION SECTION ===
Model Family:     Seagate Exos 7E8
Device Model:     ST4000NM0035-1V4
Serial Number:    ZC123ABC
LU WWN Device Id: 5 000c50 0a1b2c3d4
Firmware Version: SN03
User Capacity:    4,000,787,030,016 bytes [4.00 TB]

Що це означає: Якщо SMART працює — ймовірно ви маєте справжній passthrough. Якщо SMART не працює за RAID-контролером — можливо потрібні інші типи пристроїв або фірмові утиліти.

Рішення: SMART заблоковано + ви хочете ZFS → перевірте HBA IT-режим або справжній HBA, а не RAID-персональність.

Завдання 11: Визначте, чи multipath ховає ваші диски

cr0x@server:~$ multipath -ll
mpatha (3600508b400105e210000900000490000) dm-0 IBM,2810XIV
size=1.0T features='1 queue_if_no_path' hwhandler='0' wp=rw
|-+- policy='service-time 0' prio=50 status=active
| `- 3:0:0:0 sda 8:0  active ready running
`-+- policy='service-time 0' prio=10 status=enabled
  `- 4:0:0:0 sdb 8:16 active ready running

Що це означає: Multipath може представляти диски як /dev/dm-*, а сирі /dev/sdX — лише шляхи.

Рішення: Якщо це локальні диски — multipath це неправильна конфігурація. Вимкніть його або занесіть у чорний список локальні HBA, інакше Proxmox/ZFS можуть бачити «дивні» пристрої.

Завдання 12: Визначте, чи ви на RAID-контролері з віртуальними дисками

cr0x@server:~$ lspci -nn | egrep -i 'megaraid|raid'
04:00.0 RAID bus controller [0104]: Broadcom / LSI MegaRAID SAS-3 3108 [1000:005d] (rev 02)
cr0x@server:~$ lsblk -o NAME,TYPE,SIZE,MODEL
NAME  TYPE  SIZE MODEL
sda   disk  7.3T LSI MR9361-8i

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

Рішення: Для ZFS: або переключіться на HBA / прошийте контролер в IT-режим, або прийміть, що ZFS не зможе коректно керувати окремими фізичними дисками.

Завдання 13: Перевірте, чи BIOS не вимкнув слот або не перемапив лінії

cr0x@server:~$ dmidecode -t baseboard | egrep -i 'Manufacturer|Product|Version'
Manufacturer: Supermicro
Product Name: X11SPH-NCTF
Version: 1.02
cr0x@server:~$ lspci -vv -s 03:00.0 | egrep -i 'LnkCap|LnkSta'
LnkCap: Port #0, Speed 8GT/s, Width x8
LnkSta: Speed 2.5GT/s (downgraded), Width x8

Що це означає: Лінк знижено до 2.5GT/s — це ознака проблем з цілісністю сигналу, помилкового покоління слоту або поганого riser/кабелю.

Рішення: Зниження лінків з помилками → спробуйте примусово Gen3/Gen4 в BIOS, змініть слот, замініть riser, перевірте посадку.

Завдання 14: Proxmox-специфічно: підтвердіть ядро та модулі

cr0x@server:~$ uname -r
6.8.12-4-pve
cr0x@server:~$ modinfo mpt3sas | egrep -i 'filename|version|firmware'
filename:       /lib/modules/6.8.12-4-pve/kernel/drivers/scsi/mpt3sas/mpt3sas.ko
version:        44.100.00.00
firmware:       mpt3sas_fw.bin

Що це означає: Підтверджує, що ви використовуєте ядро Proxmox і модуль існує. Невідповідність між ядром/initramfs може вдарити після оновлень.

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

HBA, BIOS/UEFI та PCIe: звичне місце пригод

Режим HBA: IT проти IR/RAID (і чому Proxmox це важливо)

Якщо ви використовуєте ZFS (а багато Proxmox-майданчиків саме так), ви хочете, щоб HBA презентував кожний фізичний диск безпосередньо в Linux. Це IT-режим у термінах LSI/Broadcom. RAID-режим (IR) — інша філософія: контролер абстрагує диски у логічні томи. Ця абстракція ламає кілька речей, на які ви покладаєтесь у сучасних операціях:

  • Точні SMART/стан для кожного диска (часто блокуються або дивні).
  • Передбачувані ідентифікатори дисків (WWN можуть бути приховані або замінені).
  • Чітке відображення помилок (таймаути можуть стати «контролер каже ні»).
  • Здатність ZFS керувати надлишковістю і self-heal при повній видимості.

Також: RAID-контролери зазвичай мають write cache, BBU і політики, які корисні — доки не стануть шкідливими. ZFS вже реалізує свою історію консистентності. Вам не потрібно двох капітанів на одному кораблі — буде морська хвороба.

Налаштування UEFI, що приховано впливають на виявлення

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

  • Режим SATA: AHCI проти RAID. На серверах RAID-режим може маршрутизувати порти через шар типу Intel RST, який Linux може не обробляти як ви очікуєте.
  • Налаштування PCIe слота: примусова швидкість Gen проти Auto; bifurcation x16 → x4x4x4x4 для мульти-NVMe адаптерів.
  • Політика option ROM: UEFI-only проти Legacy. Це головним чином впливає на видимість під час завантаження і екрани управління, але помилкові налаштування можуть приховати те, що ви чекаєте побачити до завантаження.
  • IOMMU/VT-d/AMD-Vi: Зазвичай не ламають виявлення дисків, але можуть змінити поведінку пристроїв при passthrough.
  • Вимкнення вбудованого збереження: Деякі плати вимикають SATA-порти при заповненні M.2 слотів або діляться лініями з PCIe слотами.

Спільне використання ліній PCIe: сучасне «чому мій слот перестав працювати?»

Материнські плати — це регулювальники трафіку. Вставте NVMe у один M.2 слот, і ваш HBA може впасти з x8 до x4, або сусідній слот може вимкнутися. Це не «поганий дизайн». Це економіка і фізика: у CPU обмежена кількість ліній, і виробники плат мультиплексують їх так, що треба читати дрібним шрифтом.

Якщо ви бачите контролер присутнім, але нестабільним (AER помилки, link down/up), питання ліній або цілісності сигналу дуже актуальне. Riser-и особливо люблять бути «майже робочими».

Жарт №2: PCIe riser, який «працює, якщо не чіпати шасі» — це не компонент, а спосіб життя.

Кабелі, бекплейни, експандери та «все посаджено» брехні

Конектори Mini-SAS: чому часткові відмови — часті

SAS-кабелі несуть кілька ліній. Один SFF-8643 може нести чотири SAS-лінії; бекплейн може відображати лінії на окремі відсіки. Якщо одна лінія зламана, ви не завжди втрачаєте всі диски. Ви втрачаєте «деякі відсіки», часто в шаблоні, що виглядає як програмна проблема.

Практичне правило: якщо диски відсутні за повторюваним шаблоном (наприклад, відсіки 1–4 працюють, 5–8 мертві) — підозрюйте конкретний mini-SAS кабель або порт. Не витрачайте годину в udev на проблему, що живе в міді.

Бекплейни з експандерами: добре, коли працюють

Експандери дозволяють підключати багато дисків до меншої кількості портів HBA. Вони також додають шар, який може мати баги прошивки, проблеми переговору та чутливість до SATA-дисків позаду SAS-експандера. Симптоми включають:

  • Диски з’являються після завантаження, але зникають під навантаженням.
  • Переривчасті повідомлення «device offlined».
  • Тільки деякі моделі дисків поводяться некоректно.

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

Живлення та запуск у оберт

Особливо в щільних шасі, живлення може бути тихим вбивцею. Диски можуть стартувати, але «просідати» під час тренування лінку або коли кілька дисків запускаються одночасно. Деякі HBA і бекплейни підтримують staggered spin-up. Деякі ні. Деякі підтримують і приходять неправильно налаштованими.

Ознака — кілька дисків падають одночасно під час завантаження або під час scrub, а потім з’являються пізніше. Це не «річ Proxmox». Це живлення або сигнал.

Прості фізичні перевірки, що перемагають кмітливість

  • Пересадіть обидва кінці mini-SAS кабелів. Не «натискайте ніжно». Від’єднайте, огляньте, підключіть щільно.
  • Поміняйте кабелі між відомо справними і підозрілими портами, щоб побачити, чи проблема слідує за кабелем.
  • Перемістіть диск в інший відсік. Якщо диск працює в іншому місці — відсік/бекплейн/лінія підозріла.
  • Якщо можливо, тимчасово підключіть один диск безпосередньо до порту HBA (обійдіть експандер/бекплейн), щоб ізолювати рівні.

Рівень Linux/Proxmox: драйвери, udev, multipath і вузли пристроїв

Наявність драйвера — це не здоров’я драйвера

Побачити mpt3sas завантаженим — не означає, що контролер ініціалізувався коректно. Несумісність прошивки може давати часткову функціональність: контролер перелічується, але цілі не не відображаються; або цілі є, але постійно помиляються.

Журнали ядра важливіші за списки модулів. Якщо ви бачите повторні скидання, «firmware fault» або застряглі черги — ставтеся до цього як до реального інциденту: зберіть логи, стабілізуйте апарат і розгляньте оновлення прошивки.

Multipath: корисно до певного моменту

Multipath призначений для SAN і двошляхового збереження. На вузлі Proxmox з локальними SAS-дисками зазвичай він випадковий і шкідливий. Він може маскувати очікувані пристрої або створювати device-mapper вузли, які Proxmox/ZFS використовуватимуть непослідовно, якщо ви не налаштовані свідомо.

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

Іменування пристроїв: /dev/sdX — пастка

Linux призначає /dev/sdX імена в порядку виявлення. Додайте контролер, переставте кабелі або змініть налаштування BIOS — порядок зміниться. Так можна імпортувати неправильні диски, стерти не той пристрій або побудувати пул на невірних членах.

Використовуйте /dev/disk/by-id або WWN. Зробіть це політикою. Ваш майбутній себе тихо подякує.

Коли Proxmox «не показує диски», але Linux показує

Поширені реалії:

  • Диски мають старі розділи і UI Proxmox фільтрує те, що вважає «доступним».
  • ZFS вже використовує диски (вони належать імпортованому пулу або застарілому пулу). ZFS не ділиться ввічливо.
  • Ви дивитесь не в той вигляд: диски вузла проти визначень сховища проти вигляду datacenter.
  • Multipath або device-mapper представляє інші імена, ніж ви очікуєте.

Кут ZFS: чому «RAID-режим» вам не друг

Proxmox постачається з пріоритетною підтримкою ZFS. ZFS передбачає, що воно відповідає за надлишковість, контрольні суми та лікування. Апаратний RAID припускає, що воно відповідає за надлишковість і відновлення помилок. Коли ви укладаєте їх разом, отримуєте систему, де кожен шар приймає рішення без повної інформації.

Що «працює», але все ще неправильно

  • Створення одного великого RAID0/RAID10 тому і розміщення ZFS зверху: ZFS втрачає видимість по дисках і не може ізолювати несправні члени.
  • Використання кешування контролера з ZFS sync-записами: ви можете випадково обдурити ZFS щодо надійності, якщо політика кеша небезпечна.
  • Припущення, що контролер чесно показуватиме помилки дисків: він може ремапити, ретраїти або маскувати, поки не зможе.

Що варто робити натомість

  • Використовуйте HBA (або прошийте контролер в IT-режим) і презентуйте сирі диски ZFS.
  • Використовуйте стабільні ID при створенні пулів.
  • Віддавайте перевагу нудним, перевіреним комбінаціям прошивок. Cutting-edge добре для лабораторій, але не для квору кластера.

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

1) Симптом: HBA немає в lspci

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

Виправлення: Пересадіть, спробуйте інший слот, зніміть riser, перевірте «PCIe slot enable» у BIOS, перевірте спільне використання ліній з M.2/U.2, оновіть BIOS, якщо він дуже старий.

2) Симптом: HBA в lspci, але немає дисків в lsblk

Корінь: Драйвер не прив’язаний, несумісність прошивки, HBA в режимі, що вимагає вендорного стека, або зламаний кабель/бекплейн, що перешкоджає виявленню цілей.

Виправлення: Перевірте lspci -k, journalctl -k, перескануйте SCSI-хости, поміняйте кабелі, перевірте прошивку HBA і режим (IT для ZFS).

3) Симптом: Деякі відсіки відсутні в шаблоні

Корінь: Одна SAS-лінія/кабель/порт мертві; відображення бекплейну збігається з відсутнім набором.

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

4) Симптом: Диски з’являються після рескану, але зникають після перезавантаження

Корінь: Таймінг hotplug, дивні поведінки експандера, неправильний staggered spin-up, маргінальне живлення при завантаженні.

Виправлення: Оновіть прошивку HBA/бекплейна/експандера, увімкніть staggered spin-up якщо підтримується, перевірте PSU і розподіл живлення, проаналізуйте журнали завантаження на предмет скидань.

5) Симптом: NVMe не визначається, але працює в іншій машині

Корінь: Слот вимкнуто через налаштування bifurcation, PCIe Gen виставлений занадто високий/низький, спільне використання ліній з SATA або адаптер потребує bifurcation.

Виправлення: Встановіть правильну bifurcation, поставте швидкість PCIe в Auto/Gen3/Gen4 відповідно, перемістіть у слот, підключений до CPU, оновіть BIOS.

6) Симптом: GUI Proxmox не показує диски, але lsblk показує

Корінь: Існуючі розділи/LVM-метадані, ZFS вже тримає їх, multipath представлення або ви дивитесь у неправильному місці UI.

Виправлення: Використайте CLI, щоб підтвердити by-id, перевірте zpool status/zpool import, multipath -ll, стирайте сигнатури лише коли впевнені.

7) Симптом: SMART видає «cannot open device» за контролером

Корінь: RAID-контролер абстрагує; passthrough SMART вимагає спеціального типу пристрою або не підтримується.

Виправлення: Використовуйте HBA/IT-режим для ZFS; інакше використовуйте вендорні інструменти і прийміть обмеження.

8) Симптом: Диски флапають під навантаженням, ZFS бачить помилки контрольних сум

Корінь: Кабель/бекплейн/експандер — цілісність сигналу або недостатнє живлення; інколи один диск «отруює» шину.

Виправлення: Спершу замініть кабелі, ізолюйте шляхом видалення дисків, перевірте dmesg на скидання, перевірте PSU і стан бекплейна.

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

Чекліст A: «Інсталятор не бачить жодних дисків»

  1. Зайдіть у BIOS/UEFI і підтвердіть, що контролер увімкнений і видимий.
  2. Підтвердіть, що режим SATA — AHCI (хіба ви явно не потребуєте RAID для завантажувального тому).
  3. Для HBA: перевірте, що він у IT-режимі або це справжній HBA (не MegaRAID віртуальні томи), якщо хочете ZFS.
  4. Перемістіть HBA в інший PCIe слот (віддавайте перевагу слотам, приєднаним до CPU).
  5. Завантажте rescue-середовище і запустіть lspci і dmesg. Якщо там його немає — це апаратна проблема.
  6. Поміняйте mini-SAS кабелі і пересадіть роз’єми на обох кінцях.
  7. Якщо використовуєте бекплейн з експандером: спробуйте direct-attach з одним диском.

Чекліст B: «Деякі диски відсутні за HBA»

  1. Запустіть lsblk і визначте, які відсіки відсутні; шукайте шаблони.
  2. Перевірте журнали на скидання лінків і offline-пристрої.
  3. Перескануйте SCSI-хости; подивіться, чи з’являться відсутні диски.
  4. Поміняйте кабель, що подає підозрілий набір відсіків.
  5. Підключіть кабель до іншого порту HBA; перевірте, чи набір перемістився.
  6. Перемістіть один відсутній диск у відомо робочий відсік; якщо він з’являється — відсік/лінія погана.
  7. Оновіть прошивку HBA, якщо у вас відома проблемна версія.

Чекліст C: «Диски виявлені в Linux, але непридатні в Proxmox»

  1. Підтвердіть стабільні ID у /dev/disk/by-id.
  2. Перевірте, чи ZFS бачить пул, який можна імпортувати: zpool import.
  3. Перевірте наявність сигнатур: wipefs -n /dev/sdX (параметр -n — безпечний; залишайте його).
  4. Перевірте multipath: multipath -ll.
  5. Визначте свій намір: імпорт існуючих даних чи стирання і переназначення.
  6. Якщо стираєте — робіть це свідомо і документуйте, які WWN ви стерли.

Чекліст D: «NVMe не відображається»

  1. Підтвердіть контролер в lspci.
  2. Перевірте nvme list і журнали ядра на таймаути.
  3. Перегляньте статус PCIe лінку (LnkSta) на предмет зниження швидкості.
  4. Встановіть правильну bifurcation для мульти-NVMe адаптерів.
  5. Перемістіть NVMe в інший слот і перевірте знову.

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

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

Команда розгортала новий кластер Proxmox для внутрішнього CI. План збереження був «просто»: вісім SAS-дисків на вузол, дзеркала ZFS, готово. Закупівля привезла сервери з «SAS RAID контролером» замість замовленого HBA. Ніхто не панікував, бо в назві контролера теж було «SAS», а BIOS показував величезний логічний диск.

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

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

Виправлення було негарним: заміна RAID-контролера на справжній HBA, перебудова вузла та впровадження політики: ZFS отримує сирі диски, ідентифіковані WWN, а відповідність відсіків перевіряється по LED та серійним номерам перед фізичним втручанням. Припущення «SAS = HBA» було коренем, і воно коштувало їм вихідних.

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

Інша команда мала проблеми з продуктивністю під час resilver-ів ZFS. Хтось запропонував «оптимізувати кабелювання», використавши один експандерний бекплейн, щоб зменшити кількість портів HBA і зберегти порядок. Менше кабелів, менше точок відмов — правда?

Насправді експандер додав тонку поведінку: під час інтенсивного I/O кілька SATA SSD (використовувалися як спеціальні vdev) періодично відвалювалися на кілька секунд, потім поверталися. HBA і ядро логували скидання лінків, і ZFS позначав пристрої як faulted або degraded в залежності від таймінгу. Симптом виглядав як «ZFS нестабільний», бо падіння були транзієнтними.

Команда намагалася налаштувати таймаути і глибину черги, бо інженери люблять ручки, а експандер виглядав «enterprise». Тюнінг зменшив явні помилки, але не вирішив корінну проблему. Під реальним інцидентом — перезавантаження вузла плюс відновлення VM — пристрої знову флапали й пул відмовився імпортуватися без ручного втручання.

Вони відкочували «оптимізацію». Пряме підключення SSD, експандер залишили для масивних HDD, де затримка менш критична, і стандартизували моделі дисків за експандером. Продуктивність і сон покращилися. Іноді менше кабелів — це просто менше підказок, коли щось ламається.

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

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

Через рік вузол почав повідомляти періодичні помилки контрольних сум під час scrub-ів. Журнали вказували на хиткий лінк, а не на несправний диск, але топологія пулу включала 12 дисків і експандер. В старому світі це перетворилося б у «витягайте диски, поки помилки не зникнуть». Так створюють нові інциденти.

Натомість вони зіставили вражений WWN з відсіком. Помилки були завжди на дисках у відсіках 9–12. Це співпало з одним mini-SAS кабелем, що живив ту частину бекплейна. Вони поміняли кабель під коротке вікно обслуговування, прогнали scrub і помилки зникли.

Без драм. Без здогадок. Нудна практика інвентаризації перетворила потенційно складний інцидент у 20-хвилинний ремонт з ясною причиною. Надійність часто — це просто облік з переконаністю.

FAQ

1) Інсталятор Proxmox не показує диски. Це завжди проблема драйвера HBA?

Ні. Якщо lspci не показує контролер — це BIOS/PCIe/апарат. Якщо контролер показано, але немає дисків — можлива проблема з драйвером/прошивкою/кабелями.

2) Я бачу диски в BIOS, але не в Linux. Як це можливо?

BIOS може показувати RAID-логічні томи або підсумок контролера, не відкриваючи цілі для Linux. Або Linux не має потрібного модуля, або контролер не ініціалізується під час завантаження (перевірте journalctl -k).

3) Чи потрібен мені IT-режим для Proxmox?

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

4) Чому диски показуються як /dev/dm-0 замість /dev/sda?

Зазвичай це multipath або device-mapper стек (LVM, dm-crypt). Для локальних дисків, які не призначені для multipath, виправте конфігурацію multipath або вимкніть його.

5) Мої диски з’являються, але GUI Proxmox не показує їх як доступні. Це означає, що вони зламані?

Часто вони мають існуючі сигнатури (старі ZFS/LVM/RAID метадані) або вже є частиною імпортованого пулу. Перевірте lsblk, wipefs -n та zpool import перед будь-якими руйнівними діями.

6) Чи може поганий SAS-кабель дійсно спричинити зникнення лише одного диска?

Так. Mini-SAS несе кілька ліній; залежно від відображення бекплейна, проблема з лінією може ізолювати один відсік або підмножину. Шаблони допомагають.

7) NVMe не визначається: яка найпоширеніша помилка в BIOS?

Неправильні налаштування bifurcation при використанні мульти-NVMe адаптерів або спільне використання ліній, що вимикає слот, коли заповнений інший M.2/U.2.

8) Чи слід примусово встановлювати швидкість PCIe Gen, щоб виправити проблеми лінку?

Іноді примусовий нижчий Gen стабілізує ненадійні лінки (корисно для діагностики), але справжнє виправлення зазвичай — це посадка, riser, кабелі або вибір слота на платі.

9) Як вирішити, що замінити: диск чи кабель/бекплейн?

Якщо кілька дисків показують помилки на одному HBA-порту/сегменті бекплейна — підозрюйте кабель/бекплейн. Якщо один диск «пливе» за диском по відсіках — це диск.

10) Чи безпечно перескановувати SCSI-хости на продуктивному вузлі?

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

Висновок: практичні наступні кроки

Якщо Proxmox не бачить дисків — перестаньте гадати і проходьте ланцюг: перелічення PCIe → прив’язка драйвера → стабільність лінку → виявлення цілей → стабільні ID → споживання Proxmox/ZFS. Найшвидші перемоги зазвичай фізичні: посадка, розподіл ліній і кабелі. Найдорожчі відмови походять від неправильного режиму контролера і халатного іменування пристроїв.

  1. Запустіть швидкий план діагностики і класифікуйте домен відмов за 10 хвилин.
  2. Зберіть докази: lspci -k, lsblk та журнали ядра навколо часу виявлення.
  3. Уніфікуйте: HBA/IT-режим для ZFS, іменування by-id та карта відсіку→WWN.
  4. Виправляйте корінь, а не симптом: замініть підозрілі кабелі/riser-и, виправте bifurcation у BIOS, оновлюйте прошивку обдумано.
  5. Після відновлення виконайте один scrub/resilver і перегляньте журнали. Якщо ви не перевірили — ви не виправили, ви просто перестали бачити проблему.

MySQL чи PostgreSQL на VPS з 4 ГБ ОЗП: що налаштувати насамперед для вебсайтів

У вас є VPS з 4 ГБ ОЗП. Кілька сайтів. База даних. І раптом — пейдж, тікет або лист від клієнта: «Сайт повільний». Нічого не принижує так, як спостереження за коробкою за $10/місяць, яка намагається поводитися як корпоративна платформа, бо хтось увімкнув плагін, що «виконує лише один запит».

Це польовий гід для того, щоб зробити MySQL або PostgreSQL стабільними й достатньо швидкими для навантаження сайтів на маленькому VPS‑обладнанні. Не бенчмарк‑фантазія. Не скидання конфігів. Те, що ви налаштовуєте насамперед, що вимірюєте першим і що припиняєте робити, перш ніж це забере у вас вихідні.

Перше рішення: MySQL чи PostgreSQL для сайтів на 4 ГБ

На VPS з 4 ГБ «найкраща база» — та, яку ви вмієте тримати прогнозованою під тиском пам’яті і спалахоподібним трафіком. Ваш ворог — не теоретична пропускна здатність. Це шторм свопінгу, навали підключень і сплески затримки сховища, які перетворюють «достатньо швидко» на «чому чекає оформлення замовлення?»

Виберіть MySQL (InnoDB), коли:

  • Ваш стек уже натівний до MySQL (WordPress, Magento, багато PHP‑додатків) і ви не хочете бути тим, хто переписує все «для забави».
  • Вам потрібна відносно проста історія кешування: InnoDB buffer pool — це великий регулятор, і він поводиться як великий регулятор.
  • Потрібна реплікація, яку легко експлуатувати звичними інструментами, і ви готові до компромісів eventual consistency у деяких режимах.

Виберіть PostgreSQL, коли:

  • Вам важлива коректність запитів і багаті можливості SQL (справжні window‑функції, CTE, кращі обмеження і типи даних), і ви дійсно будете їх використовувати.
  • Хочете передбачувані плани виконання, хорошу спостережуваність і розумні значення за замовчуванням для сучасних шаблонів додатків.
  • Готові впровадити пулінг підключень (pgBouncer), бо модель процес‑на‑підключення PostgreSQL карає «просто відкрий більше підключень» на малих машинах.

Якщо це переважно CMS‑трафік з плагінами, якими ви не керуєте, я зазвичай консервативний: залишайтеся з MySQL, якщо додаток вже MySQL‑орієнтований. Якщо будуєте нову систему з командою, що пише SQL усвідомлено, PostgreSQL часто кращий довгостроковий вибір. Але на 4 ГБ короткостроковою перемогою буде оперативна простота, а не філософська чистота.

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

Цікавi факти та історичний контекст (що реально впливає на рішення)

  1. Рання домінація MySQL у вебі зумовлена поширенням LAMP і «достатньою» швидкістю для сайтів із великою кількістю читань. Саме тому багато веб‑додатків досі розраховують на діалектні особливості MySQL.
  2. InnoDB став дефолтом у MySQL 5.5 (ера близько 2010). Якщо ви все ще мислите в термінах MyISAM (блокування таблиць, відсутність відновлення після краху), ви носите в кишені викопний артефакт.
  3. MVCC‑модель PostgreSQL — одна з причин, чому він тримає консистентність при конкуренції, але вона створює постійну потребу у vacuum. Ігнорувати vacuum — не означає, що база буде кричати; вона просто поступово деградує.
  4. Postgres з часом став дружнішим до паралельності (паралельні запити, кращий планувальник). На маленькому VPS це менш критично, ніж на «важкому залізі», але це частина того, чому Postgres «відчувається сучасним» для аналітичних запитів.
  5. Query cache у MySQL був видалений у MySQL 8.0 через погану масштабованість під конкуренцією. Якщо хтось радить «увімкнути query_cache_size», ви знайшли мандрівника в часі.
  6. Postgres отримав кредит за стандарти й коректність, бо історично пріоритет віддавався фічам і цілісності над ранньою сирою швидкістю. Сьогодні він теж швидкий, але культурна ДНК видно в дефолтах і інструментах.
  7. Обидва рушії консервативні щодо надійності за замовчуванням (fsync, WAL/redo). Вимикання налаштувань цілісності робить бенчмарки героїчними, а постмортеми — кримінальними сценами.
  8. MariaDB відходить від MySQL у значних деталях. Поради з «тонкого налаштування MySQL» іноді погано мапляться на версії MariaDB і її сховища. Перевіряйте, що саме ви запускаєте.
  9. RDS і керовані сервіси формували фольклор налаштувань: люди копіюють хмарові дефолти на VPS, а потім дивуються, чому 4 ГБ коробка «тоне».

Базова архітектура для VPS з 4 ГБ (і чому це важливо)

На VPS з 4 ГБ у вас немає «зайвої пам’яті». У вас є бюджет. Витрачайте його на кеші, які зменшують I/O, і на резерв пам’яті, щоб уникнути свопінгу. Кеш сторінок ОС також важливий, бо і MySQL, і PostgreSQL врешті читають із файлової системи, і ядро не ваш ворог; воно — останній рубіж захисту.

Реалістичний бюджет пам’яті

  • OS + SSH + базові демони: 300–600MB
  • Вебсервер + PHP‑FPM: дуже варіюється. Від кількох сотень МБ до кількох ГБ залежно від кількості процесів і поведінки додатка.
  • База даних: те, що залишилося, але не все відразу. Якщо віддати БД усе, веб‑шар OOMить або почне свопити під час сплесків трафіку.

Для «сайтів на одному VPS» база даних не ізольована. Це один із небагатьох випадків, коли «налаштував і забув» — не лінь, а виживання.

Думка: Якщо ви запускаєте і веб, і БД на одному VPS з 4 ГБ, плануйте виділити приблизно 1.5–2.5GB для кеш‑рівня бази даних максимум, якщо тільки ви не виміряли споживання пам’яті PHP під навантаженням і воно дійсно мале. Мета — стабільна латентність, а не героїчний розмір buffer pool.

Жарт №1: VPS з 4 ГБ — як студія: технічно вміститься бігова доріжка, але ви ненавидітимете життя, і сусіди теж.

План швидкої діагностики: знайти вузьке місце за 10 хвилин

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

Перше: чи голодна коробка (CPU, RAM, swap)?

  • Перевірте load у порівнянні з кількістю CPU.
  • Перевірте активність swap і великі page fault‑и.
  • Перевірте історію OOM killer.

Друге: чи це затримка сховища (IOPS/fsync/WAL/redo)?

  • Високий iowait, повільні fsync, довгі commit‑и або завислі checkpoints.
  • Шукайте глибину черги і середні times await.

Третє: чи це тиск підключень?

  • Надто багато підключень до БД або потоків.
  • Штурм підключень від PHP‑воркерів.
  • Кількість потоків/процесів доходить до меж пам’яті.

Четверте: чи це блокування або довгі транзакції?

  • MySQL: metadata locks, InnoDB row locks, довгі транзакції.
  • Postgres: заблоковані запити, idle‑in‑transaction сесії, vacuum, заблокований старими snapshot‑ами.

П’яте: чи це «погані запити + відсутні індекси»?

  • Slow query log / pg_stat_statements показують головних порушників.
  • Шукайте повні сканування таблиць і «filesort»/тимчасові таблиці або sequential scans з великим числом рядків.

Ось і все. Не починайте з хаотичних змін регуляторів. Не копіюйте «high performance my.cnf» з 64GB сервера. Вимірюйте, а потім зробіть одну зміну, яку можете пояснити.

Цитата (парафраз ідеї): Ідея Джона Оллспо: продукція — це місце, де припущення вмирають, тому проєктуйте та експлуатуйте для навчання, а не для впевненості.

Практичні завдання: команди, виводи та подальші дії

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

Завдання 1: Підтвердити базовий тиск системи (CPU, RAM, swap)

cr0x@server:~$ uptime
 14:22:19 up 36 days,  3:18,  1 user,  load average: 5.84, 5.12, 3.90

Що це означає: На VPS з 2 vCPU середні навантаження вище ~2–3 протягом тривалого часу часто означають чергу runnable (CPU) або незнімні очікування I/O.

Рішення: Якщо load високий, негайно перевірте iowait і пам’ять/swap перед тим, як чіпати конфіги бази даних.

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:           3.8Gi       3.3Gi       120Mi        90Mi       420Mi       220Mi
Swap:          1.0Gi       860Mi       164Mi

Що це означає: Використання swap з низьким «available» — тривожний знак для затребуваних за латентністю веб‑навантажень.

Рішення: Якщо swap використовується в піку, зменшіть пам’ять бази даних і/або зменшіть кількість PHP‑FPM воркерів. Розгляньте ввімкнення невеликого swap як подушку від краху, а не як шар для продуктивності.

Завдання 2: Перевірити активний свопінг і iowait

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
 2  1 880000 120000  28000 380000   12   45   210   480  520  900 18  8 40 34  0
 3  0 880000 118000  28000 381000    0   60   160   510  600 1100 20  9 35 36  0
 1  2 880000 115000  28000 379000   20    0   400   200  480  850 15  7 38 40  0
 4  0 880000 119000  28000 382000    0    8    90   520  610 1200 22 10 32 36  0
 2  1 880000 117000  28000 381500    0   35   200   600  590 1050 19  9 34 38  0

Що це означає: Ненульові si/so вказують на свопінг. Високий wa — це I/O wait.

Рішення: Якщо свопінг активний, припиніть «тонке» налаштування запитів і вирішіть питання пам’яті/кількості підключень. Якщо iowait високий — зосередьтесь на затримці сховища, поведінці checkpoints/flush і зменшенні write amplification.

Завдання 3: Швидко ідентифікувати затримки диска і черги

cr0x@server:~$ iostat -xz 1 3
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          18.20    0.00    8.10   34.50    0.00   39.20

Device            r/s     w/s   rkB/s   wkB/s  rrqm/s  wrqm/s  %util  await  aqu-sz
vda              12.0    48.0   900.0  6200.0    0.2     3.1   92.0   21.5    1.8

Що це означає: %util близько 100% і await > 10–20ms на диску VPS зазвичай означають, що сховище — вузьке місце.

Рішення: Зменшити запис (батчування, налаштування autovacuum, InnoDB flush), перенести базу на краще сховище або розділити БД і веб. У деяких планах VPS єдиний «тюнінг», що має значення — платити за більший IOPS.

Завдання 4: Підтвердити, яку СУБД ви запускаєте і її версію

cr0x@server:~$ mysql --version
mysql  Ver 8.0.36-0ubuntu0.22.04.1 for Linux on x86_64 ((Ubuntu))

Що це означає: Великі версії змінюють дефолти і прибирають регулятори (наприклад query cache). Версія визначає, які поради застосовні.

Рішення: Не застосовуйте рецепти налаштувань, якщо вони не відповідають вашій основній версії і флейвору (MySQL vs MariaDB).

cr0x@server:~$ psql --version
psql (PostgreSQL) 16.1 (Ubuntu 16.1-1.pgdg22.04+1)

Що це означає: Новіші версії Postgres покращують vacuum, WAL і планувальник. Це змінює те, що «болить» на малих коробках.

Рішення: На старому Postgres доведеться більше вручну приглядати. На новому — зосередьтеся на пулінгу підключень і порогах autovacuum.

Завдання 5: Порахуйте підключення до DB (MySQL)

cr0x@server:~$ mysql -e "SHOW STATUS LIKE 'Threads_connected';"
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_connected | 185   |
+-------------------+-------+

Що це означає: 185 підключень на VPS з 4 ГБ у режимі PHP часто проблема, навіть до того як запити стануть повільними.

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

Завдання 6: Порахуйте підключення до DB (PostgreSQL)

cr0x@server:~$ sudo -u postgres psql -c "SELECT count(*) AS connections FROM pg_stat_activity;"
 connections
-------------
         142
(1 row)

Що це означає: 142 сесії Postgres = 142 бекенд‑процеси. На VPS з 4 ГБ це податок по пам’яті та контекстним перемиканням.

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

Завдання 7: Знайти довгі запити і блокувальники (PostgreSQL)

cr0x@server:~$ sudo -u postgres psql -c "SELECT pid, now()-query_start AS age, state, wait_event_type, wait_event, left(query,80) AS q FROM pg_stat_activity WHERE state <> 'idle' ORDER BY age DESC LIMIT 5;"
 pid  |   age    | state  | wait_event_type | wait_event |                                       q
------+----------+--------+-----------------+------------+--------------------------------------------------------------------------------
 9123 | 00:02:18 | active | Lock            | relation   | UPDATE orders SET status='paid' WHERE id=$1
 9051 | 00:01:44 | active | IO              | DataFileRead | SELECT * FROM products WHERE slug=$1
(2 rows)

Що це означає: Очікування на блокування вказує на контенцію; очікування на I/O — на повільне сховище або пропуски кеша.

Рішення: Якщо домінують lock‑waits — виправляйте обсяг транзакцій і індекси. Якщо IO‑waits — збільшуйте ефективний кеш (в міру розумного) і зменшуйте випадкові читання через індекси та форму запитів.

Завдання 8: Знайти очікування блокувань (MySQL)

cr0x@server:~$ mysql -e "SHOW FULL PROCESSLIST;"
Id	User	Host	db	Command	Time	State	Info
210	app	10.0.0.12:50344	shop	Query	75	Waiting for table metadata lock	ALTER TABLE orders ADD COLUMN foo INT
238	app	10.0.0.15:38822	shop	Query	12	Sending data	SELECT * FROM orders WHERE created_at > NOW() - INTERVAL 1 DAY

Що це означає: Metadata locks можуть заморозити запис і читання за схемних змін, залежно від операції й версії.

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

Завдання 9: Перевірити Innodb buffer pool hit rate і тиск на читання

cr0x@server:~$ mysql -e "SHOW STATUS LIKE 'Innodb_buffer_pool_read%';"
+---------------------------------------+---------+
| Variable_name                         | Value   |
+---------------------------------------+---------+
| Innodb_buffer_pool_read_requests      | 9823412 |
| Innodb_buffer_pool_reads              | 412390  |
+---------------------------------------+---------+

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

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

Завдання 10: Перевірити кеш Postgres і тимчасові файли

cr0x@server:~$ sudo -u postgres psql -c "SELECT datname, blks_hit, blks_read, temp_files, temp_bytes FROM pg_stat_database ORDER BY temp_bytes DESC LIMIT 5;"
  datname  | blks_hit | blks_read | temp_files |  temp_bytes
-----------+----------+-----------+------------+--------------
 appdb     |  9201123 |   612332  |      1832  | 2147483648
(1 row)

Що це означає: Багато temp_bytes вказує, що сорти/хеші вивалюються на диск, бо work_mem замалий для цих операцій — або запити роблять забагато роботи.

Рішення: Не підвищуйте work_mem глобально на маленькому VPS. Виправляйте запити й індекси спочатку; потім підвищуйте work_mem для конкретних ролей або сеансів для спеціфічних навантажень.

Завдання 11: Подивитися топ‑запити (Postgres, якщо pg_stat_statements увімкнено)

cr0x@server:~$ sudo -u postgres psql -c "SELECT calls, mean_exec_time, rows, left(query,80) AS q FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;"
 calls | mean_exec_time | rows |                                       q
-------+----------------+------+--------------------------------------------------------------------------------
 82021 |          12.45 |    1 | SELECT id FROM sessions WHERE token=$1
  1220 |         210.12 |  300 | SELECT * FROM orders WHERE user_id=$1 ORDER BY created_at DESC LIMIT 50
(2 rows)

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

Рішення: Індексуйте «гарячі» шляхи і зменшуйте багаточисельні запити. Якщо запит виконується 80k разів і займає 12ms — це витрата одного повного ядра процесора.

Завдання 12: Увімкнути і читати slow query log у MySQL швидко

cr0x@server:~$ mysql -e "SET GLOBAL slow_query_log=ON; SET GLOBAL long_query_time=0.5; SET GLOBAL log_queries_not_using_indexes=ON;"
...output omitted...

Що це означає: Ви вмикаєте збір доказів. Тримайте пороги розумними, щоб не DOS‑нути себе логами.

Рішення: Збирайте 15–60 хвилин під час піку, потім аналізуйте й виправляйте найгірших винуватців. Вимикайте log_queries_not_using_indexes, якщо воно надто шумить для вашого додатка.

cr0x@server:~$ sudo tail -n 5 /var/log/mysql/mysql-slow.log
# Query_time: 1.204  Lock_time: 0.000 Rows_sent: 50  Rows_examined: 84512
SELECT * FROM orders WHERE user_id=123 ORDER BY created_at DESC LIMIT 50;

Що це означає: Rows examined значно більше, ніж rows sent: класичний відсутній індекс або неправильний порядок індексів.

Рішення: Додайте/підлаштуйте складні індекси під патерн фільтрації + сортування (наприклад (user_id, created_at)), потім перевірте з EXPLAIN.

Завдання 13: Перевірити місце на файловій системі та інодинаміку

cr0x@server:~$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        80G   74G  2.1G  98% /

Що це означає: Майже повні диски руйнують продуктивність і можуть порушити записи в БД. Postgres може відмовлятися відчекпоінтитись; MySQL може впасти або перейти в режим тільки для читання залежно від обставин.

Рішення: Звільніть місце негайно. Потім налаштуйте ротацію логів і моніторинг із тривогою на 80–85%.

Завдання 14: Перевірити пам’ять процесів ядра і БД

cr0x@server:~$ ps -eo pid,comm,rss,pmem --sort=-rss | head
 2311 mysqld     1854320 46.2
 1822 php-fpm8.1  412000 10.2
 1825 php-fpm8.1  405000 10.1
  911 postgres    220000  5.4

Що це означає: RSS показує фактичну резидентну пам’ять. Кілька великих PHP‑воркерів плюс великий DB‑кеш можуть звести коробку в своп.

Рішення: Якщо БД + PHP вже споживають більшість RAM, припиніть збільшувати DB‑буфери. Зменшіть конкурентність і обмежте споживачів пам’яті.

Якщо ви обрали MySQL: що налаштувати насамперед на VPS з 4 ГБ

MySQL на малому VPS зазвичай працює добре, якщо ви не ставите його поводитися як бездонна яма для підключень і пам’яті. InnoDB — ваш дефолтний рушій; налаштовуйте під InnoDB, не за ностальгією.

1) Встановіть innodb_buffer_pool_size по‑дорослому

Мета: Кешувати «гарячі» дані/індекси, зменшити випадкові читання, не відбирати пам’ять у всіх інших.

  • Якщо БД на тій же коробці, що й веб: починайте з близько 1.0–1.5GB.
  • Якщо БД здебільшого сама: до 2.0–2.5GB може бути прийнятно.

Режим відмови: Перенасичення buffer pool не «використовує вільну пам’ять». Воно конкурує з кешем ОС і веб‑шаром. Потім ви свопите. А потім кожен запит стає бенчмарком сховища.

2) Встановіть max_connections нижче, ніж здається

Потоки MySQL споживають пам’ять. PHP‑додатки люблять відкривати підключення, ніби це безкоштовно. Ні, не безкоштовно.

  • Почніть з приблизно 100–200 залежно від додатка та латентності запитів.
  • Якщо бачите 300–800 підключень — це не «проблема продуктивності бази даних». Це проблема контролю конкуренції.

3) Зберігайте поведінку redo log і flush розумною

На малому VPS з невпевненим сховищем агресивні flush можуть спричиняти сплески. Але відключати надійність — це шлях до оновлення резюме.

  • innodb_flush_log_at_trx_commit=1 для реальної надійності (за замовчуванням).
  • Якщо зовсім немає вибору і ви готові втратити до 1 секунди транзакцій при краху: розгляньте =2. Задокументуйте і додайте в інцидент‑рукбуки. Не прикидайтеся, що це безкоштовно.

4) Вимикайте те, що не потрібно, але не сліпо

Performance Schema корисна; вона також має накладні витрати. На крихітному VPS можна зменшити інструментацію, а не видаляти її повністю.

  • Якщо постійно CPU‑зв’язане з низькою латентністю запитів, подумайте про обрізання Performance Schema споживачів.
  • Але зберігайте достатню видимість, щоб ловити регресії. Діагностика без метрик — це творче письмо.

5) Розумно встановлюйте ліміти для тимчасових таблиць

Веб‑додатки люблять ORDER BY і GROUP BY, часто з надто широкими наборами результатів.

  • tmp_table_size і max_heap_table_size можуть зменшити дискові тимчасові таблиці, але якщо встановити їх занадто високо — ви «вибухнете» пам’яттю під конкуренцією.

Ескіз початкової конфігурації MySQL (не догма)

Дух цього конфігу для змішаного веб+БД 4 ГБ VPS. Налаштовуйте згідно вимірювань вище.

cr0x@server:~$ sudo cat /etc/mysql/mysql.conf.d/99-vps-tuning.cnf
[mysqld]
innodb_buffer_pool_size = 1G
innodb_buffer_pool_instances = 1
max_connections = 150
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
slow_query_log = ON
long_query_time = 0.5

Що це означає: Менший buffer pool для збереження запасу пам’яті, обмежені підключення, direct I/O щоб зменшити подвійне кешування (залежить від вашої ФС і навантаження) і логування повільних запитів для збору доказів.

Рішення: Застосуйте, рестартуйте в тихий інтервал, потім повторно перевірте swap/iowait і slow‑логи. Якщо латентність покращилась і своп зник — вірний напрям.

Якщо ви обрали PostgreSQL: що налаштувати насамперед на VPS з 4 ГБ

Postgres відмінний для сайтів, але змусить вас звернути увагу на три речі відразу: кількість підключень, vacuum і WAL/checkpoints. Ігнор будь‑якої з них — і ви отримаєте «випадкові» гальмування, що насправді не випадкові.

1) Встановіть пулінг підключень (pgBouncer) раніше, ніж «потрібно»

На 4 ГБ бекенди Postgres не є диспо́забельними. Сплеск трафіку, що відкриває сотні підключень, може перетворитися на тиск пам’яті та контекстні перемикання.

Робіть: запустіть pgBouncer у transaction pooling для типової веб‑роботи.

Не робіть: піднімайте max_connections до 500 і називайте це масштабуванням.

2) Встановіть shared_buffers консервативно

Фольклор каже «25% RAM». На змішаному веб+БД VPS я б починав з:

  • 512MB — 1GB для shared_buffers.

Postgres також виграє від кеша ОС. Віддавати все shared_buffers може задушити ОС і інші процеси.

3) Встановіть work_mem низьким глобально; підвищуйте вибірково

work_mem — на сортування/хеш операцію, на запит, на бекенд. На малому VPS не варто хизуватися.

  • Почніть з 4–16MB глобально залежно від конкуренції.
  • Підвищуйте для конкретної ролі або сеансу, якщо маєте відомий важкий звітний запит.

4) Підтримуйте autovacuum здоровим

Autovacuum — не опціональне прибирання. Це спосіб Postgres уникати bloat і зберігати можливість index‑only scans.

  • Моніторте dead tuples і відставання vacuum.
  • Налаштовуйте autovacuum пороги для «гарячих» таблиць при потребі.

5) Зробіть checkpoints менш «піковими»

На повільному VPS‑сховищі spike‑и чекпоінтів виглядають як раптові стрибки латентності. Рівномірніші чекпоінти зменшують біль.

  • Збільшіть checkpoint_timeout (в межах розумного).
  • Встановіть checkpoint_completion_target високим, щоб розподілити записи.

Ескіз початкової конфігурації Postgres

cr0x@server:~$ sudo cat /etc/postgresql/16/main/conf.d/99-vps-tuning.conf
shared_buffers = 768MB
effective_cache_size = 2304MB
work_mem = 8MB
maintenance_work_mem = 128MB
checkpoint_completion_target = 0.9
checkpoint_timeout = 10min
wal_compression = on
log_min_duration_statement = 500ms

Що це означає: Консервативні shared_buffers, реалістичне хинтування кеша, помірний work_mem, згладжені чекпоінти і логування повільних запитів.

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

Підключення: мовчазний вбивця на малих машинах

Якщо ви запускаєте сайти, найпростіший спосіб зіпсувати базу даних — дозволити додатку самим вирішувати конкуренцію. PHP‑FPM воркери + «відкривай підключення в кожному запиті» — це гуркітний рій. На 4 ГБ виживають не через швидкість. Ви виживаєте через спокій.

Як виглядає «занадто багато підключень»

  • CPU бази даних високий, але він не виконує корисної роботи (контекстні перемикання, контенція mutex).
  • Споживання пам’яті зростає з трафіком до свопу.
  • Латентність зростає навіть для простих запитів.

Що робити натомість

  • Обмежити конкурентність додатка: менше PHP‑FPM дітей або налаштування менеджера процесів, щоб уникнути спалахів.
  • Використовувати пулінг: pgBouncer для Postgres; для MySQL розгляньте пулінг на рівні додатка або налаштування persistent connections розумно.
  • Fail fast: іноді нижчий max_connections — правильний вибір, бо він захищає коробку від повного thrash.

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

Зберігання та реалії файлової системи: IOPS, fsync і чому «швидкий SSD» бреше

На VPS‑платформах «SSD» може означати все: від поважного NVMe до спільного мережевого блоку, що має поганий день. Бази даних більше хвилює затримка, ніж пропускна здатність. Кілька мілісекунд додаткового fsync на комітті стають помітними на сайті.

Як записи б’ють по вас по‑різному в MySQL та PostgreSQL

  • MySQL/InnoDB: redo‑логування + doublewrite buffer (залежно від конфіг/версії) + скидання брудних сторінок. Пучкові flush можуть посилити затримки.
  • PostgreSQL: WAL‑записи + чекпоінти + background writer. Vacuum також створює I/O, а bloat збільшує майбутні I/O.

Краща практика для малого VPS: зменшіть write amplification перш за все

  • Виправте «балакучі» додатки (занадто багато дрібних транзакцій).
  • Пакетуйте записи, де це дозволяє правильність.
  • Уникайте оновлення «last_seen» на кожному запиті, якщо воно не потрібне.
  • Тримайте індекси компактними; кожен індекс — це податок на запис.

Підводні камені файлової системи

  • Не кладіть бази даних на ненадійні мережеві файлові системи, якщо платформа не гарантує семантику надійності.
  • Слідкуйте за умовою «диск повний»: Postgres і MySQL по‑різному поводяться при цьому, але жоден сценарій не є приємним.

Три корпоративні історії з передової

1) Інцидент через неправильне припущення: «Кеш покриє це»

Маленька команда вела кілька маркетингових сайтів і сервіс оформлення замовлення на одному VPS з 4 ГБ. Там були MySQL, Nginx і PHP‑FPM. Трафік був «переважно статичний», і це було правдою до запуску кампанії, коли сервіс оформлення почав отримувати спалахи авторизованих запитів.

Припускали, що page‑cache і кеш додатка перекриють читання, тож підняли innodb_buffer_pool_size майже до 3GB, щоб «розігнати базу». Це чудово виглядало у спокійні години. Потім стартувала кампанія.

PHP‑FPM розгорнув воркерів на обробку трафіку. Кожен воркер споживав більше пам’яті, ніж хтось пам’ятав. ОС почала свопити. Buffer pool бази був великий, тому ядру залишалося менше місця для всього іншого. Латентність не зростала поступово; вона обрушилася. Чек‑аут почав таймити, ретраї піднімали трафік, і шторм ретраїв перетворив ресурсну проблему на самохостний DoS.

Виправлення не було екзотичним. Зменшили buffer pool, щоб залишити запас, обмежили PHP‑FPM дітей, знизили MySQL max_connections, щоб система падала швидко, а не thrashилася, і поставили явну чергу перед чек‑аутом. Також навчилися різниці між «вільною пам’яттю» і «доступною пам’яттю під сплеск».

2) Оптимізація, що повернулася боком: «Просто підніміть work_mem, це нормально»

Внутрішній додаток працював на PostgreSQL. Користувачі скаржилися на повільні звіти, тому хтось значно підвищив work_mem, бо блог‑пост сказав, що це зменшить I/O тимчасових файлів. Воно і зменшилося. Для одного користувача. В одному сеансі.

Потім настала понеділкова ранкова сесія. Декілька користувачів одночасно запустили звіти. Ці звіти кожен робив кілька сортувань і hash‑join. Postgres правильно виділив work_mem під кожну операцію. Споживання пам’яті різко зросло. VPS не впав відразу; він ставав все повільнішим і повільнішим, поки не ввімкнувся своп. База виглядала «живою», але кожен запит чекав за штормом I/O від свопу.

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

3) Нудна, але правильна практика, що врятувала день: «Обмежувати підключення і логувати повільні запити»

Інша організація хостила кілька невеликих клієнтських сайтів на спільному VPS з 4 ГБ. Нічого особливого. З першого дня вони робили три нудних речі: обмежували підключення до БД, увімкнули slow query logging з розумним порогом і моніторили дискове місце з тривогою задовго до 90%.

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

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

Ось як виглядає надійність: контрольований відмова, збір доказів і достатній запас, щоб один невдалий деплой не перетворився на катастрофу.

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

1) Симптом: раптові 10–60с зависання по всьому сайту

Корінь: сплески затримки сховища під час чекпоінтів/flush або своп‑шторм.

Виправлення: підтвердіть за допомогою iostat і vmstat; зменшіть тиск пам’яті (менші DB‑кеші, менше воркерів додатка), згладьте чекпоінти (Postgres) і зменшіть write amplification (обидва).

2) Симптом: CPU бази даних високий, запити «не такі вже й повільні» поодинці

Корінь: занадто багато одночасних підключень; наклад від контенції домінує.

Виправлення: обмежте підключення; додайте пулінг (pgBouncer); зменшіть concurrenсy PHP‑FPM; кешуйте на рівні додатка або реверс‑проксі; вмикайте fail fast замість thrash.

3) Симптом: Postgres росте і росте; продуктивність поступово деградує

Корінь: відставання vacuum і bloat таблиць/індексів через недостатній autovacuum або довгі транзакції.

Виправлення: знайдіть idle‑in‑transaction сесії, налаштуйте autovacuum для гарячих таблиць, і припиніть утримувати транзакції відкритими між запитами.

4) Симптом: MySQL «Waiting for table metadata lock» у processlist

Корінь: зміна схеми або DDL заблокована довгими транзакціями; запити стоять у черзі за metadata locks.

Виправлення: плануйте DDL у вікнах обслуговування; тримайте транзакції короткими; використовуйте online schema change підходи за потреби.

5) Симптом: багато тимчасових файлів або «Using temporary; Using filesort» у MySQL

Корінь: відсутні індекси для ORDER BY/GROUP BY патернів; запити сортують великі набори.

Виправлення: додайте складні індекси, що відповідають filter+sort; зменшіть вибір колонок; робіть пагінацію; уникайте OFFSET пагінації для глибоких сторінок.

6) Симптом: часті помилки «too many connections»

Корінь: витоки підключень у додатку, відсутність пулінгу або стрибки у кількості веб‑воркерів.

Виправлення: пулінуйте підключення; встановіть розумні таймаути; обмежте конкуренцію додатка; встановіть DB max_connections на число, яке ви можете дозволитися.

7) Симптом: після «налаштування» продуктивність погіршилася

Корінь: глобальна настройка (як work_mem або занадто великий buffer pool) збільшила пам’ять на підключення і спричинила своп під конкуренцією.

Виправлення: відкотіть; застосовуйте налаштування на рівні користувача/запиту; вимірюйте пам’ять і конкуренцію явно.

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

Крок 0: Визначте, що означає «добре»

  • Виберіть SLO‑подібну метрику: наприклад, homepage p95 < 500ms, checkout p95 < 800ms.
  • Виберіть вікно вимірювання і зафіксуйте базу (CPU, RAM, swap, iowait, підключення, повільні запити).

Крок 1: Стабілізуйте хост

  • Переконайтеся, що на диску є принаймні 15–20% вільного місця.
  • Переконайтеся, що ви не свопите під звичайним піковим трафіком.
  • Встановіть консервативні ліміти сервісів (systemd limits за потреби), щоб уникнути неконтрольованих процесів.

Крок 2: Обмежте конкуренцію свідомо

  • Встановіть PHP‑FPM max children на кількість, яку ви можете дозволитися в RAM.
  • Встановіть DB max_connections, щоб захистити машину.
  • На Postgres: впровадьте pgBouncer і зменшіть бекенд‑підключення.

Крок 3: Встановіть перші пам’ятні регулятори

  • MySQL: встановіть innodb_buffer_pool_size так, щоб робочий набір поміщався без виснаження ОС.
  • Postgres: встановіть shared_buffers консервативно; тримайте work_mem низьким глобально.

Крок 4: Увімкніть збір доказів

  • MySQL: slow query log на 0.5–1s під час піку, потім аналізуйте й виправляйте.
  • Postgres: log_min_duration_statement і бажано pg_stat_statements.

Крок 5: Виправте топ‑3 патерни запитів

  • Додайте відсутні індекси, що зменшують сканування рядків.
  • Усуньте N+1 запити в додатку.
  • Не запускайте дорогі запити на кожен запит; передобчислюйте або кешуйте.

Крок 6: Перетестуйте і встановіть запобіжники

  • Повторіть завдання триажу під піком.
  • Додайте алерти на активність swap, використання диска, кількість підключень і частоту повільних запитів.
  • Задокументуйте «безпечні» налаштування і підґрунтя, щоб майбутнє‑ви їх не відкотив випадково.

FAQ

1) На VPS з 4 ГБ, що пріоритизувати: кеш БД чи кеш ОС?

Пріоритет — стабільність. Для single‑box веб+БД не душіть ОС. Помірний кеш БД плюс запас краще, ніж величезний кеш, що викликає своп під сплесками.

2) Чи PostgreSQL «повільніший» за MySQL для сайтів?

Не за законом. Для багатьох веб‑навантажень обидва достатньо швидкі при правильних індексах. Більший відмінник на 4 ГБ — управління підключеннями і патерни записів, а не сирий двигун.

3) Який перший параметр MySQL варто змінити?

innodb_buffer_pool_size, підібраний до реалій. Потім обмежте max_connections. Потім увімкніть slow query logging і виправляйте те, що воно покаже.

4) Який перший параметр PostgreSQL варто змінити?

Стратегія пулінгу підключень (pgBouncer) і max_connections. Потім консервативні shared_buffers і логування/pg_stat_statements, щоб ідентифікувати топ‑запити.

5) Чи можна просто збільшити swap, щоб вирішити проблеми пам’яті?

Можна збільшити swap, щоб уникнути раптових OOM‑крахів, але swap — не оперативна пам’ять для продуктивності. Якщо база або PHP‑воркери регулярно потрапляють у swap, латентність стане непередбачуваною.

6) Чи варто вимикати fsync для швидкості?

Ні для продуктивних сайтів, де важлива цілісність даних. Якщо ви вимикаєте надійність і хост падає — втрачаєте дані. Бенчмарки це люблять; клієнти — ні.

7) Як зрозуміти, що я I/O bound?

Високий iowait у vmstat, високий await і %util у iostat, а також сесії БД, що чекають на IO‑події (Postgres) — сильні сигнали.

8) Коли варто розділити веб і БД на окремі сервери?

Коли налаштування починають бути компромісом між пам’яттю веб‑шара і БД, або коли затримки сховища роблять записи невпевненими. Розділення дає ізоляцію і ясніше планування потужностей.

9) Чи достатньо дефолтів сьогодні?

Дефолти кращі, ніж раніше, але вони не підлаштовані під вашу «все на одній коробці 4 ГБ» ситуацію. Контроль підключень і бюджет пам’яті — все ще на вас.

10) Яка найбезпечніша «перемога в продуктивності», яку можна зробити без глибокої експертизи?

Увімкніть slow query logging (або pg_stat_statements), знайдіть топ‑3 споживача часу і додайте правильні індекси. Також обмежте підключення, щоб сервер залишався стабільним під навантаженням.

Наступні кроки, які не будуть вам соромно пізніше

На VPS з 4 ГБ ви не оптимізуєте базу даних. Ви управляєте конкуренцією між вебом, базою і сховищем, намагаючись зробити латентність нудною.

  1. Запустіть план швидкої діагностики під піком і запишіть, що насправді відбувається: swap, iowait, підключення, блокування, топ‑запити.
  2. Обмежте конкуренцію перш за все: PHP‑FPM воркери і підключення до БД. Додайте pgBouncer, якщо ви на Postgres.
  3. Встановіть перший пам’ятний регулятор (InnoDB buffer pool або Postgres shared_buffers) на консервативне значення, що залишає запас.
  4. Увімкніть збір доказів (slow query logs / pg_stat_statements) і виправте топ‑винуватців індексами й змінами запитів.
  5. Перепровірте диск і поведінку записів; згладьте чекпоінти, зменшіть тимчасові виливи і припиніть галасливі записи, що не потрібні.
  6. Прийміть рішення, чи справжнє рішення — архітектурне: перенести БД на окремий VPS, оновити рівень сховища або використовувати керовану базу. Іноді найдієвіший параметр тюнінгу — ваш рахунок.

Якщо зробите лише одне сьогодні: обмежте підключення і припиніть свопити. Все інше — гарнір.

MySQL vs ClickHouse: зупиніть, щоб аналітика не вбивала OLTP (План чистого розділення)

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

Це не проблема «поганого запиту». Це архітектурна проблема: OLTP і аналітика — різні тварини, і тримати їх в одному вольєрі має передбачувані наслідки. Вирішення — чіткий розділ: MySQL виконує транзакції, ClickHouse — аналітику, і ви припиняєте дозволяти цікавості DDoS-ити шлях доходу.

Фактична проблема: OLTP і аналітика конфліктують на рівні зберігання

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

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

Основний висновок: OLTP і аналітика змагаються за одні й ті ж обмежені ресурси — CPU-цикли, дискове I/O, page cache, buffer pool, блокування/затримки і фонове обслуговування (флаші, контрольні точки, злиття). Навіть якщо ви додаєте репліку для читання, ви все одно часто ділитеся тим самим фундаментальним болем: лаг реплікації, насичення I/O і непередбачувана продуктивність через скани.

Де ножа встромлює: конфлікт ресурсів у MySQL

  • Забруднення buffer pool: великий звітний запит читає холодний зріз історії, витісняє гарячі сторінки й раптово ваш основний робочий потік стає залежним від диска.
  • Фоновий тиск InnoDB: довгі скани + тимчасові таблиці + сорти можуть збільшити кількість «брудних» сторінок і навантаження на redo. Флеш-шторм — неприємний гість.
  • Блокування й MDL: деякі шаблони звітності викликають неприємні взаємодії (уявіть «ALTER TABLE у робочий час» зустрічає «SELECT …», що утримує MDL).
  • Реплікаційний лаг: важкі запити на репліці крадуть I/O і CPU у SQL-потоку, що застосовує зміни.

Куди пасує ClickHouse

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

Дисципліна проста: вважайте MySQL системою запису для транзакцій. Вважайте ClickHouse системою істини для аналітики — істини в сенсі «виведено з запису, відтворювано і запитувано в масштабі».

Перефразована ідея Вернера Фогельса: «Усе ламається; проектуйте на випадок відмов». Це стосується й даних: проєктуйте під відмови, як-от хвилі запитів, лаг і бекфіли.

MySQL vs ClickHouse: реальні відмінності, що важать в продакшні

Розташування зберігання: рядок проти стовпця

MySQL/InnoDB орієнтований на рядки. Чудово підбирати рядок за первинним ключем, оновлювати кілька колонок, підтримувати вторинні індекси і забезпечувати обмеження. Але сканувати мільярд рядків для обчислення агрегатів означає переносити через движок цілі рядки, торкатися сторінок, які вам не потрібні, і спалювати кеш.

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

Модель конкуренції: транзакційна проти аналітичної паралельності

MySQL добре обробляє багато коротких транзакцій одночасно — до меж вашої схеми, індексів і обладнання. ClickHouse теж справляється з великою кількістю читань, але магія в ефективному паралелізуванні великих читань і агрегацій. Якщо ви вкажете BI-інструменту ClickHouse і дозволите необмежену конкуренцію без лімітів запитів, він постарається підпалити ваш CPU. Ви можете і повинні обмежувати це.

Послідовність і коректність

MySQL — ACID (з відомими нюансами, але так, це ваш транзакційний анклав). ClickHouse зазвичай врешті-решт узгоджений для інжекції даних і орієнтований на додавання. Ви можете моделювати оновлення/видалення, але робите це на умовах ClickHouse (ReplacingMergeTree, CollapsingMergeTree, стовпці версій або асинхронні видалення). Це нормально: аналітика зазвичай хоче поточну істину і часову послідовність змін, а не транзакційні семантики на рівні рядка.

Індексація та шаблони запитів

Індекси MySQL — B-дерева, що підтримують точкові пошуки і діапазонні скани. ClickHouse використовує впорядкування первинного ключа й розріджені індекси, а також data skipping індекси (наприклад bloom-фільтри), де це допомагає. Найкращий запит для ClickHouse — той, що може пропустити великі шматки даних, бо ваше партиціювання й ORDER BY відповідають шаблонам доступу.

Операційна позиція

Операції MySQL крутяться навколо здоров’я реплікації, резервних копій, міграцій схем і стабільності запитів. Операції ClickHouse — навколо злиттів, використання диска, кількості частин, TTL і управління запитами. Іншими словами: ви міняєте одного дракона на іншого. Угода все одно варта того, бо ви припиняєте дозволяти аналітиці псувати оформлення замовлення.

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

Факти та історичний контекст (корисно, не тривіально)

  1. InnoDB став за замовчуванням у MySQL 5.5 (приблизно 2010 рік), закріпивши row-store OLTP-поведінку для більшості розгортань.
  2. ClickHouse починався в Yandex для забезпечення аналітики в масштабі; він виріс у світі, де швидкий скан великої кількості даних — головне завдання.
  3. Колонкові сховища набули популярності, бо CPU став швидшим за диски, і стиснення + векторизоване виконання дозволили витрачати CPU, щоб уникати I/O.
  4. «Забруднення» InnoDB buffer pool — класичний режим відмови, коли довгі скани знищують гарячі сторінки; движок не «ламався», він робив те, що його просили.
  5. Аналітика через реплікацію існує десятиліттями: люди передавали OLTP-зміни в сховища даних ще до того, як «data lake» потрапив в резюме.
  6. Кеш запитів MySQL видалили в MySQL 8.0, бо він створював контенцію і не масштабувався; кешування не безкоштовне, а глобальні блоки дорогі.
  7. Сімейство MergeTree у ClickHouse зберігає дані в частинах і зливає їх у фоновому режимі — добре для записів і стиснення, але створює операційні сигнали (кількість частин, backlog злиттів), які треба моніторити.
  8. «Зіркова схема» і вимірна модель передували сучасним інструментам; ClickHouse часто штовхає команди назад до денормалізованих, дружніх до запитів форм, бо джойни в масштабі мають реальну вартість.

План чистого розділення: підходи, що не плавлять продакшн

Принцип 1: MySQL — для обслуговування користувачів, не цікавості

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

Ви отримаєте заперечення. Це нормально. Хитрість — замінити «ні» на «ось безпечний шлях». Забезпечте безпечний шлях: доступ до ClickHouse, підготовлені набори даних і робочий процес, що не вимагає благати чергового дозволу на JOIN за рік замовлень.

Принцип 2: Оберіть стратегію переміщення даних відповідно до терпимості до відмов

Є три загальні способи підживлювати ClickHouse з MySQL. У кожного є свої гострі кути.

Варіант A: пакетний ETL (дамп і завантаження)

Ви витягуєте щогодини/щодня снапшоти (mysqldump, CSV-експорти, Spark‑джоби), завантажуєте в ClickHouse і приймаєте застарілість. Це найпростіше в операційному плані, але болісне, коли потрібні майже реальні метрики, а бекфіли важкі.

Варіант B: інжекція через реплікацію (CDC)

Фіксуєте зміни з binlog MySQL і стримуєте їх у ClickHouse. Це дає майже реальну аналітику, ізолюючи MySQL від навантаження запитів. Але це вводить здоров’я пайплайну як першокласну виробничу проблему: лаг, дрейф схем і перепроцесування стають вашим новим хобі.

Варіант C: подвійний запис (додаток пише в обидва)

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

План чистого розділення зазвичай означає CDC плюс кураторські моделі даних в ClickHouse. Пакетний ETL прийнятний, коли ви можете терпіти застарілість. Подвійний запис — пастка, якщо вам не подобається пояснювати невідповідності даних під час постмортемів інцидентів.

Принцип 3: Модель ClickHouse під ваші запитання, не під схему

Більшість OLTP‑схем нормалізовані. Аналітика хоче менше джойнів, стабільні ключі і таблиці подій. Ваше завдання — побудувати аналітичне представлення, яке легко запитувати і важко зловживати.

  • Віддавайте перевагу таблицям подій: orders_events, sessions, payments, shipments, support_tickets. Додавайте події. Виводьте факти.
  • Партиціюйте за часом: зазвичай по днях або місяцях. Це дає передбачуване відсікання і керований TTL.
  • ORDER BY за вимірами запитів: розміщуйте найпоширеніші ключі фільтрації/групування раніше в ORDER BY (після ключа часу, якщо ви завжди фільтруєте за часом).
  • Попередньо агрегуйте там, де це стабільно: materialized views можуть виробляти зведення, щоб панелі не сканували сирі дані повторно.

Принцип 4: Управління краще за героїзм

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

  • Розділіть користувачів і квоти: BI‑користувачі отримують таймаути і ліміт пам’яті. ETL має інший профіль.
  • Встановіть max threads і конкуренцію: уникайте «грому стад» паралельних запитів.
  • Використовуйте виділені «gold» набори даних: стабільні view або таблиці, від яких залежать панелі, версіоновані за потреби.
  • Визначте SLO: SLO на латентність MySQL святе. SLO на свіжість ClickHouse переговорний, але вимірюваний.

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

Це рухи, які ви фактично робите о 02:13. Кожне завдання містить команду, приклад виводу, що це означає, і рішення, яке потрібно ухвалити.

Завдання 1: Підтвердити, що MySQL страждає через аналітичні скани (top digests)

cr0x@server:~$ mysql -e "SELECT DIGEST_TEXT, COUNT_STAR, SUM_TIMER_WAIT/1e12 AS total_s FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 5\G"
*************************** 1. row ***************************
DIGEST_TEXT: SELECT customer_id, sum(total) FROM orders WHERE created_at BETWEEN ? AND ? GROUP BY customer_id
COUNT_STAR: 9421
total_s: 18873.214
*************************** 2. row ***************************
DIGEST_TEXT: SELECT * FROM orders WHERE created_at > ? ORDER BY created_at DESC LIMIT ?
COUNT_STAR: 110233
total_s: 8211.532

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

Рішення: Заблокуйте або перенаправте цей шаблон аналітичного запиту. Не перетворюйте MySQL на OLAP‑движок. Почніть з переміщення цієї панелі в ClickHouse або в таблицю-зведення.

Завдання 2: Перевірити поточну активність потоків MySQL (чи це натовп?)

cr0x@server:~$ mysql -e "SHOW PROCESSLIST;" | head
Id	User	Host	db	Command	Time	State	Info
31	app	10.0.2.14:51234	prod	Query	2	Sending data	SELECT customer_id, sum(total) FROM orders WHERE created_at BETWEEN '2025-12-01' AND '2025-12-30' GROUP BY customer_id
44	app	10.0.2.14:51239	prod	Query	2	Sending data	SELECT customer_id, sum(total) FROM orders WHERE created_at BETWEEN '2025-12-01' AND '2025-12-30' GROUP BY customer_id
57	app	10.0.2.14:51241	prod	Query	1	Sending data	SELECT customer_id, sum(total) FROM orders WHERE created_at BETWEEN '2025-12-01' AND '2025-12-30' GROUP BY customer_id

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

Рішення: Тротлінг на рівні додатка/BI і введення кешування або попередньої агрегації в ClickHouse. Також розгляньте ліміти з’єднань MySQL і керування ресурсами на користувача.

Завдання 3: Перевірити тиск на InnoDB buffer pool (гарячі сторінки витісняються)

cr0x@server:~$ mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
Variable_name	Value
Innodb_buffer_pool_read_requests	987654321
Innodb_buffer_pool_reads	12345678

Що це означає: Велика кількість фізичних читань (Innodb_buffer_pool_reads) відносно логічних читань вказує, що ваш робочий набір не тримається в пам’яті — часто через великі скани.

Рішення: Зупиніть скани (переносьте аналітику), і лише потім розгляньте збільшення buffer pool або налаштування навантаження. Обладнання не зможе замінити погану комбінацію навантажень назавжди.

Завдання 4: Виявити насичення дискового I/O на хості MySQL

cr0x@server:~$ iostat -xz 1 3
Linux 6.2.0 (mysql01) 	12/30/2025 	_x86_64_	(16 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          18.12    0.00    6.44   31.55    0.00   43.89

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   w_await aqu-sz  %util
nvme0n1         820.0  64200.0     0.0    0.0   12.4    78.3     410.0  18800.0    9.8   18.2   98.7

Що це означає: %util близько 100% і високий iowait означають, що диск — вузьке місце. Скани аналітики обожнюють такий результат.

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

Завдання 5: Визначити лаг реплікації MySQL (ваша «репліка для читання» не допомагає)

cr0x@server:~$ mysql -h mysql-replica01 -e "SHOW SLAVE STATUS\G" | egrep "Seconds_Behind_Master|Slave_SQL_Running|Slave_IO_Running"
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 487

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

Рішення: Не використовуйте репліку як аналітичний стік. Використовуйте CDC у ClickHouse або принаймні виділіть репліку під відмову з контрольованим доступом до запитів і гарантованими ресурсами.

Завдання 6: Показати фактичний план дорогого запиту (перестаньте гадати)

cr0x@server:~$ mysql -e "EXPLAIN SELECT customer_id, sum(total) FROM orders WHERE created_at BETWEEN '2025-12-01' AND '2025-12-30' GROUP BY customer_id\G"
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: orders
type: range
possible_keys: idx_created_at
key: idx_created_at
rows: 98234123
Extra: Using where; Using temporary; Using filesort

Що це означає: Навіть з індексом ви скануєте ~98M рядків і використовуєте temporary/filesort. Це не OLTP‑запит; це OLAP‑задача.

Рішення: Перенесіть його. Якщо деякі агрегати мають залишитись в MySQL, використовуйте summary tables, що оновлюються інкрементально, а не ad hoc GROUP BY по сирим фактам.

Завдання 7: Підтвердити базове здоров’я ClickHouse (злиття або диск — проблема?)

cr0x@server:~$ clickhouse-client -q "SELECT hostName(), uptime()"
ch01
345678

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

Рішення: Переходьте до глибшої перевірки: частини/злиття, навантаження запитів і диск.

Завдання 8: Перевірити активні запити ClickHouse і їхнє використання ресурсів

cr0x@server:~$ clickhouse-client -q "SELECT user, query_id, elapsed, read_rows, formatReadableSize(memory_usage) AS mem, left(query, 80) AS q FROM system.processes ORDER BY memory_usage DESC LIMIT 5 FORMAT TabSeparated"
bi_user	0f2a...	12.4	184001234	6.31 GiB	SELECT customer_id, sum(total) FROM orders_events WHERE event_date >= toDate('2025-12-01')
etl	9b10...	3.1	0	512.00 MiB	INSERT INTO orders_events FORMAT JSONEachRow

Що це означає: BI споживає пам’ять. Це нормально, якщо це бюджетовано. Це проблема, якщо це позбавляє ресурси злиттів або викликає OOM.

Рішення: Встановіть max_memory_usage на користувача, max_threads і, можливо, max_concurrent_queries. Тримайте ETL надійним.

Завдання 9: Перевірити backlog злиттів ClickHouse (частини ростуть як бур’яни)

cr0x@server:~$ clickhouse-client -q "SELECT database, table, sum(parts) AS parts, formatReadableSize(sum(bytes_on_disk)) AS disk FROM system.parts WHERE active GROUP BY database, table ORDER BY sum(parts) DESC LIMIT 10 FORMAT TabSeparated"
analytics	orders_events	1842	1.27 TiB
analytics	sessions	936	640.12 GiB

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

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

Завдання 10: Перевірити відсікання за партицією (якщо сканує все — ви неправильно змоделювали)

cr0x@server:~$ clickhouse-client -q "EXPLAIN indexes=1 SELECT customer_id, sum(total) FROM analytics.orders_events WHERE event_date BETWEEN toDate('2025-12-01') AND toDate('2025-12-30') GROUP BY customer_id"
Expression ((Projection + Before ORDER BY))
  Aggregating
    Filter (WHERE)
      ReadFromMergeTree (analytics.orders_events)
        Indexes:
          MinMax
            Keys: event_date
            Condition: (event_date in [2025-12-01, 2025-12-30])
            Parts: 30/365
            Granules: 8123/104220

Що це означає: Воно читає 30/365 частин завдяки фільтру за датою. Ось як виглядає «працює як задумано».

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

Завдання 11: Моніторити використання диска ClickHouse і передбачити проблеми з ємністю

cr0x@server:~$ df -h /var/lib/clickhouse
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme1n1    3.5T  3.1T  330G  91% /var/lib/clickhouse

Що це означає: 91% заповнення. Ви в одному бекфілі від поганого дня, а злиттям потрібен вільний простір.

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

Завдання 12: Перевірити лаг CDC на стороні споживача (чи аналітика застаріла?)

cr0x@server:~$ clickhouse-client -q "SELECT max(ingested_at) AS last_ingest, now() AS now, dateDiff('second', max(ingested_at), now()) AS lag_s FROM analytics.orders_events"
2025-12-30 19:03:12	2025-12-30 19:03:29	17

Що це означає: ~17 секунд лаг. Це здорово для «майже реальної» аналітики.

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

Завдання 13: Перевірити формат binlog MySQL для коректності CDC

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'binlog_format';"
Variable_name	Value
binlog_format	ROW

Що це означає: ROW-формат зазвичай потрібен CDC-інструментам для коректності. STATEMENT може бути неоднозначним для недетермінованих запитів.

Рішення: Якщо ви не на ROW, заплануйте вікно змін. Коректність CDC — не те, на що можна «сподіватися».

Завдання 14: Підтвердити, що MySQL має адекватний лог повільних запитів (щоб довести причинність)

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'slow_query_log%'; SHOW VARIABLES LIKE 'long_query_time';"
Variable_name	Value
slow_query_log	ON
slow_query_log_file	/var/log/mysql/mysql-slow.log
Variable_name	Value
long_query_time	0.500000

Що це означає: Ви будете фіксувати запити повільніші за 500 мс. Це агресивно, але корисно під час шумного періоду.

Рішення: Під час інцидентів тимчасово знизьте long_query_time і робіть вибірки. Після — встановіть стабільний поріг і використовуйте digest‑зведення.

Завдання 15: Перевірити ліміти користувачів ClickHouse (щоб запобігти «паралелізму» BI)

cr0x@server:~$ clickhouse-client -q "SHOW CREATE USER bi_user"
CREATE USER bi_user IDENTIFIED WITH sha256_password SETTINGS max_memory_usage = 4000000000, max_threads = 8, max_execution_time = 60, max_concurrent_queries = 5

Що це означає: BI відгороджений: 4GB пам’яті, 8 потоків, 60s виконання, 5 одночасних запитів. Це різниця між панеллю і стрес‑тестом.

Рішення: Якщо ви не можете встановити ліміти через «потреби бізнесу», ви не керуєте аналітикою — ви граєте в рулетку.

Покроковий план швидкої діагностики

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

Перше: чи MySQL перевантажений читаннями, записами, блокуваннями чи I/O?

  1. Top query digests (performance_schema digests або slow log): виявіть сімейства запитів, що їдять час.
  2. Стан потоків (SHOW PROCESSLIST): «Sending data» вказує на скан/агрегацію; «Locked» — на контенцію; «Waiting for table metadata lock» — на DDL‑колізію.
  3. Дискове I/O (iostat): якщо iowait високий і %util диска запеклий, зупиніть скани перш ніж щось інше налаштовувати.

Друге: чи «рішення» (репліка) насправді не погіршує ситуацію?

  1. Лаг реплікації (SHOW SLAVE STATUS): якщо лаг — хвилини, користувачі аналітики приймають рішення на застарілих даних і звинувачують вас.
  2. Контенція ресурсів на репліці: важкі запити можуть позбавляти SQL‑потік ресурсів і ще більше збільшувати лаг.

Третє: якщо ClickHouse існує, чи він здоровий і під управлінням?

  1. system.processes: виявіть запити BI‑втікачі й хелпери пам’яті.
  2. Частини й злиття (system.parts): занадто багато частин — проблема форми інжекції або backlog злиттів.
  3. Дисковий запас (df): злиттям і TTL потрібен простір; 90% заповнення — операційний борг з відсотками.

Четверте: чи скарга насправді про свіжість даних?

  1. Лаг CDC (max ingested_at): кількісно оцініть застарілість.
  2. Повідомте про запасний план: якщо свіжість деградує, деградуйте панелі — не оформлення замовлення.

Три корпоративні міні‑історії з фронту

Інцидент через хибне припущення: «Репліки для звітності»

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

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

Потім хтось припустив: «Якщо первинний впаде, ми зможемо переключитись на репліку». Саме коли лаг був максимальний, первинний мав unrelated проблему хоста і став нездоровим. Автоматика спробувала просунути «кращу» репліку — але «краща» була на 20 хвилин позаду.

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

Виправлення не було героїчним. Вони розділили обов’язки: виділена репліка для failover з жорсткою блокадою запитів, а аналітика перейшла в ClickHouse через CDC. Звітність стала швидкою, а failover — надійним, бо репліка більше не була мішенню для аналітики.

Оптимізація, що обернулася проти: «Додамо індекс»

Команда електронної торгівлі мала повільний звітний запит по orders: фільтр за часом і group-by. Хтось додав індекс на created_at і складений індекс на (created_at, customer_id). Запит став швидшим у ізоляції, тому вони випустили це і святкували.

Через два тижні латентність записів почала рости. Вставки в orders уповільнились, а фонова швидкість флашів підскочила. Нові індекси збільшили write amplification — кожна вставка тепер підтримувала більше B‑дерев. На піку трафіку вони платили індексну ціну за кожну транзакцію, щоб зробити кілька звітів дешевшими.

Потім BI додав нову панель, що запускала той самий запит кожну хвилину. Запит став швидшим, тож конкуренція зросла (люди люблять оновлювати, коли оновлення швидке). Система поміняла один повільний запит на багато середньошвидких і все одно опинилася I/O‑зв’язаною.

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

Сумна, але правильна практика, що врятувала день: квоти і поетапні бекфіли

B2B SaaS компанія використовувала ClickHouse для аналітики з жорсткими профілями користувачів. BI‑користувачі мали max_execution_time, max_memory_usage і ліміти конкуренції. ETL мав інші ліміти і працював у контрольованій черзі. Ніхто не любив ті обмеження. Усі від них виграли.

Одного дня аналітик спробував запустити широкий запит за два роки сирих подій без фільтру за датою. ClickHouse почав сканувати, досягнув ліміту часу виконання і вбив запит. Аналітик поскаржився. Черговий не отримав сигналу. Це хороший обмін.

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

Нудна практика не була якимось складним алгоритмом. Це було управління і операційна дисципліна: ліміти, черги і інкрементні бекфіли. Це їх врятувало, бо система поводилася передбачувано, коли люди поводилися непередбачувано.

Жарт №2: Єдина річ більш постійна за тимчасову панель — це інцидентний канал, який вона створює.

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

  • Симптом: p95 латентності MySQL зростає під час «годин звітності»
    Корінна причина: довгі скани і GROUP BY конкурують з OLTP за buffer pool і I/O
    Виправлення: Перенесіть звітність в ClickHouse; введіть політику; додайте кураторські rollup‑таблиці; заблокуйте доступ BI до MySQL.
  • Симптом: Лаг репліки зростає, коли аналітики запускають звіти
    Корінна причина: Репліка I/O і CPU насичені; SQL‑потік не встигає застосувати binlog швидко
    Виправлення: Заберіть аналітичний доступ з реплік для failover; використовуйте CDC у ClickHouse; обмежуйте конкуренцію запитів.
  • Симптом: Запити ClickHouse повільнішають з часом без зміни обсягу даних
    Корінна причина: Вибух кількості частин; злиття відстають через фрагментовані вставки або тиск на диск
    Виправлення: Батчуйте вставки; тонко налаштуйте параметри злиття; моніторте частини; забезпечте диск‑запас; розгляньте переразбиття.
  • Симптом: Панелі «швидкі інколи» і таймаутяться випадково в ClickHouse
    Корінна причина: Незв’язаність BI‑конкуренції; тиск пам’яті; «шумні сусіди»
    Виправлення: Встановіть ліміти на користувача (пам’ять, потоки, час виконання, одночасні запити); створіть попередньо агреговані таблиці; додайте маршрутизацію запитів.
  • Симптом: Дані аналітики мають дублікати або «неправильний останній стан»
    Корінна причина: CDC застосовано як append-only без дедуплікації/версіонування; оновлення/видалення не змоделювані правильно
    Виправлення: Використовуйте стовпці версій і ReplacingMergeTree де потрібно; зберігайте події і виводьте поточний стан через materialized views.
  • Симптом: Диск ClickHouse постійно росте до аварійного стану
    Корінна причина: Немає TTL; зберігання сирого вічно; важкі бекфіли; відсутні обмеження ємності
    Виправлення: Застосуйте TTL для холодних даних; даунсемплуйте; архівуйте; стискайте; встановіть квоти і процедури бекфілу.
  • Симптом: «Ми перейшли на ClickHouse, але MySQL все ще повільний»
    Корінна причина: CDC‑пайплайн все ще читає MySQL важко (повні екстракти, часті снапшоти) або додаток все ще запускає звіти на MySQL
    Виправлення: Використовуйте binlog‑базований CDC; перегляньте джерела запитів MySQL; закрийте акаунти для звітності; валідируйте за допомогою digest‑даних.
  • Симптом: Свіжість ClickHouse відстає в піки
    Корінна причина: Вузьке місце інжесту (пропускна здатність пайплайну), злиття або тиск на диск; іноді занадто багато маленьких вставок
    Виправлення: Батчуйте вставки; масштабируйте інжест; моніторьте лаг; тимчасово зменшіть конкуренцію BI; пріоритезуйте ресурси ETL.

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

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

  1. Оголосіть межу: продукційний MySQL — тільки OLTP. Запишіть це. Забезпечте через акаунти й мережеву політику.
  2. Інвентаризуйте аналітичні запити: використайте digest‑таблиці MySQL і зведення slow log для переліку топ‑20 сімейств запитів.
  3. Оберіть метод інжесту: CDC для майже реального часу; пакет для щоденних/годинних оновлень; уникайте подвійного запису.
  4. Опишіть аналітичні таблиці в ClickHouse: почніть з таблиць подій, партиціювання за часом і ORDER BY ключів, узгоджених з фільтрами.
  5. Побудуйте «gold» набори даних: materialized views або rollup‑таблиці для панелей; зберігайте сирі дані для глибокого аналізу.
  6. Встановіть управління з першого дня: профілі користувачів, квоти, max_execution_time, max_memory_usage, max_concurrent_queries.
  7. Вимірюйте свіжість: відслідковуйте лаг інжесту і публікуйте SLO зацікавленим сторонам. Люди терплять застарілість, коли вона явна.
  8. Перенесіть панелі: мігруйте найвпливовіші панелі першими (ті, що непрямо викликають виклики чергових).
  9. Заблокуйте старий шлях: приберіть BI‑креденшіали з MySQL; якщо потрібно — фаєрвол; запобігайте регресії.
  10. Бекфіл безпечно: інкрементально, вимірювано, з перевіркою дискового запасу; без фантазій «запустимо вночі».
  11. Навантажувальне тестування аналітики: симулюйте конкуренцію панелей. ClickHouse охоче прийме ваш оптимізм, а потім покарає його.
  12. Операціоналізуйте: алерти за кількістю частин ClickHouse, використанням диска, помилками запитів, лагом інжесту; а також за латентністю/IO MySQL.

Чеклист релізу: переміщення однієї панелі з MySQL в ClickHouse

  • Чи включає запит панелі фільтр за часом, що відповідає партиціюванню?
  • Чи є таблиця rollup/materialized view, щоб уникнути повторних сканів сирих подій?
  • Чи обмежено користувача ClickHouse (пам’ять, потоки, час виконання, конкуренція)?
  • Чи метрика лагу CDC видима для користувачів панелі?
  • Чи старий MySQL‑запит заблоковано або принаймні вилучено з додатка/BI?
  • Чи ви перевірили результати для відомого вікна часу (спот‑чек сум і кількостей)?

Операційний чеклист: щотижнева гігієна, що запобігає повільним аваріям

  • Перегляньте активні частини ClickHouse по таблицях; дослідіть швидкий ріст.
  • Перегляньте дисковий запас ClickHouse; тримайте достатньо вільного місця для злиттів і бекфілів.
  • Перегляньте топ‑BI‑запити за read_rows і використанням пам’яті; оптимізуйте або попередньо агрегуйте.
  • Перегляньте топ digests MySQL, щоб переконатися, що аналітика не підкралась назад.
  • Перевіряйте шляхи відновлення: бекапи MySQL, очікування відновлення метаданих і даних ClickHouse.

Питання й відповіді

1) Хіба не можна просто вертикально масштабувати MySQL і вирішити проблему?

Можна, і ви отримаєте тимчасове полегшення. Режим відмови повернеться, коли з’явиться наступна панель або когортний запит. Проблема — у невідповідності навантаження, а не лише у потужності.

2) Якщо у мене вже є репліки MySQL — чи слід вказати BI на них?

Тільки якщо ви комфортно ставитеся до лагу і не використовуєте ті репліки для failover. Навіть тоді обмежте конкуренцію і розглядайте це як тимчасовий міст, а не кінцевий стан.

3) Чи достатньо ClickHouse «реального часу» для операційних панелей?

Часто так, з CDC. Вимірюйте лаг інжесту явно і проектуйте панелі з урахуванням невеликої затримки. Якщо вам потрібна субсекундна транзакційна істина — це зона MySQL.

4) Як обробляти оновлення і видалення з MySQL у ClickHouse?

Краще моделювання подій (append changes). Якщо потрібен «поточний стан», використовуйте версійні рядки з движками на кшталт ReplacingMergeTree і проектуйте запити/materialized views відповідно.

5) Чи замінить ClickHouse мій data warehouse?

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

6) Який найшвидший виграш, якщо ми зараз у вогні?

Припиніть виконувати аналітику на MySQL негайно: вбийте найгірші запити, приберіть доступ BI і відправте панель в ClickHouse або кеш‑роллап. Потім зробіть це правильно.

7) Який найбільший операційний сюрприз ClickHouse для команд MySQL?

Злиття й частини. Люди з row‑store очікують «я вставив — і все». ClickHouse продовжує працювати в тлі, і цю роботу потрібно моніторити.

8) Як запобігти аналітикам від написання дорогих запитів у ClickHouse?

Використовуйте профілі користувачів з квотами і таймаутами, надавайте кураторські «gold» таблиці і навчіть людей, що відсутність фільтрів за часом — не «дослідження», а підпал.

9) Чи вирішують materialized views усі проблеми?

Ні. Вони чудові для стабільних rollup‑ів і поширених агрегатів. Але можуть додавати складність і витрати на зберігання. Використовуйте їх там, де вони обґрунтовано зменшують повторну роботу.

10) Що робити, якщо мої аналітичні запити вимагають складних джойнів через багато таблиць?

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

Висновок: практичні наступні кроки

Якщо ви зробите одну дію цього тижня — зробіть ось це: видаліть навантаження аналітики з MySQL. Не благаючи користувачів «бути обережними», а надаючи краще місце для досліджень.

  1. Заблокуйте MySQL: окремі акаунти, блокування BI‑мереж і забезпечення, що продукційний MySQL обслуговує користувачів у першу чергу.
  2. Встановіть управління ClickHouse: ліміти, квоти і кураторські набори даних перед тим, як запрошувати всю компанію.
  3. Перенесіть топ‑5 найгірших запитів: реплікуйте потрібні дані через CDC або пакетно, потім побудуйте rollup‑и, щоб панелі залишалися дешевими.
  4. Операціоналізуйте свіжість: публікуйте лаг інжесту і ставте його як продуктове вимогу. Краще чесно відставати на 60 секунд, ніж бути непомітно неправим.
  5. Практикуйте бекфіли: поетапні, вимірювані, зворотні. Ваш майбутній я оцінить стриманість сьогоднішнього.

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

SLI/CrossFire: чому мульти‑GPU були мрією — і чому вона померла

Якщо ви колись намагалися «просто додати ще одну GPU» і очікували, що графік піде вгору і вправо, ви вже зустріли головного антагоніста цієї історії:
реальний світ. Мульти‑GPU у споживчих іграх — NVIDIA SLI та AMD CrossFire — виглядали як чиста інженерна справедливість: паралелізм,
більше кремнію, більше кадрів, і все готово.

А потім ви відправили продукт у реліз. Часи кадрів перетворилися на пікетний паркан. Стек драйверів став політикою переговорів між рушієм гри, планувальником GPU, PCIe
та тим таймінгом монітора, який ви вважали відомим. Ваш дорогий другий диск часто ставав просто обігрівачем із резюме.

Обіцянка: масштабування шляхом додавання GPU

Мульти‑GPU, як його продавали геймерам, був операційною казкою: ваша гра завантажує GPU, отже ще один GPU означає майже вдвічі більше продуктивності.
Ось цей меседж. Це також перше хибне припущення. Системи не масштабуються тому, що маркетинговий слайд каже «2×»; системи масштабуються, коли найповільніша
частина конвеєра перестає бути вузьким місцем.

Сучасний ігровий кадр — це брудна лінія зборки: симуляція на CPU, відправка викликів малювання, рендеринг на GPU, пост‑обробка, композитинг, презентація
і таймінгова угода з вашим дисплеєм. SLI/CrossFire намагалися приховати складність мульти‑GPU за драйверами, профілями та містком. Це приховування і
стало причиною їхньої загибелі.

Мульти‑GPU‑мрія померла, бо воювала з фізикою (затримки й синхронізація), економікою програмного забезпечення (розробники не тестують рідкісні конфігурації) та
змінами платформ (DX12/Vulkan передали відповідальність від драйвера до рушія). І тому, що «середній FPS» виявився брехнею через замовчування: те,
що відчувають ваші очі — це консистентність фреймтаймів, а не середнє.

Як SLI/CrossFire працювали насправді

Драйвер‑кероване мульти‑GPU: профілі всюди

У класичну епоху SLI/CrossFire покладалися на евристики драйвера та профілі для кожної гри. Драйвер вирішував, як розділити рендеринг між GPU,
без явного знання цього рушієм гри. Це звучить зручно. Воно також стало операційним кошмаром: тепер у вас розподілена система, де один вузол
(гра) не знає, що він розподілений.

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

Основні режими: AFR, SFR і «будь ласка, так не робіть»

Alternate Frame Rendering (AFR) був робочою конячкою. GPU0 рендерить кадр N, GPU1 — кадр N+1, і так далі. На папері: фантастично.
На практиці: AFR — машина для затримок і проблем з кадруванням. Якщо кадр N займає 8 мс, а кадр N+1 — 22 мс, ваш «середній FPS» може виглядати прийнятно, тоді як
очі побачать слайдшоу з пропусками.

Split Frame Rendering (SFR) ділить один кадр на області. Це вимагає акуратного балансування навантаження: одна половина екрана може містити вибух,
шейдери волосся, обʼємну графіку і ваші жаління; інша — просто стіну. Вгадайте, який GPU закінчить першим і буде сидіти в холості.

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

Мости, PCIe і чому інтерконект ніколи не був героєм

Мости SLI (а в ранні епохи — мости CrossFire) забезпечували шлях з вищою пропускною здатністю та нижчою затримкою для певних операцій синхронізації й
обміну буферами, ніж один тільки PCIe. Але міст не зливав VRAM магічно. Кожен GPU усе одно мав власну памʼять. У AFR кожному GPU зазвичай потрібна була
власна копія тих самих текстур і геометрії. Тому «два 8 ГБ карти» не ставали «16 ГБ». Вони ставали «8 ГБ, двічі».

Коли розробники почали інтенсивніше використовувати тимчасові техніки — TAA, screen‑space reflections з буферами історії, тимчасові апскейлери — AFR став
все більш несумісним. Ви не можете легко відрендерити кадр N+1 на GPU1, якщо йому потрібна історія з кадру N, що живе на GPU0, без додавання синхронізації і
передачі даних, які знищать виграш у продуктивності.

Одна перефразована ідея, широко приписувана духу мислення про надійність систем (і часто кажуть інженери з орбіти Google SRE): надія — це не стратегія.
Вона ідеально підходить для мульти‑GPU. SLI/CrossFire просили вас сподіватися, що рендер‑конвеєр вашої гри співпаде з припущеннями драйвера.

Чому це провалилося: смерть від тисячі країв випадків

1) Кадрування вбило відчуття «швидко»

AFR може давати високий середній FPS, одночасно створюючи нерівномірні часи кадрів (мікростаттер). Люди помічають варіацію. Ваш оверлей для моніторингу може показувати
«120 FPS», а ваш мозок реєструє «нестабільно». Це була центральна проблема для користувацького досвіду: SLI/CrossFire могли вигравати в бенчмарках і програвати по відчуттях.

Кадрування — це не просто «трохи сіпання». Воно взаємодіє з VSync, VRR (G‑SYNC/FreeSync), глибиною черги рендеру і плануванням CPU. Якщо драйвер ставить у чергу кадри занадто агресивно, ви отримуєте затримку вводу. Якщо він ставить занадто мало — зʼявляються бульбашки й сіпання.

Жарт №1: Мульти‑GPU — як мати двох стажерів, що по черзі пишуть сторінки одного звіту — швидко, поки ви не помітите, що вони не сходяться по сюжету.

2) Дзеркалення VRAM: ви платили за памʼять, якою не могли скористатися

У споживчому мульти‑GPU майже завжди відбувалося дзеркалення активів у памʼяті кожного GPU. Це дозволяло масштабувати без розгляду памʼяті як узгодженої спільної купи,
але також означало, що великі текстури, складна геометрія і сучасні структури для прискорення трасування обмежувалися обʼємом VRAM однієї карти.

Коли ігри стали більшими за вимогами до VRAM, план «просто додати другу GPU» став тільки гіршим: вузьке місце перемістилося з обчислень до ємності памʼяті, і мульти‑GPU
не допомагав. Гірше того, друга GPU підвищувала споживання енергії, тепло й вимоги до обдуву корпусу, при цьому даючи той самий ліміт VRAM, що й одна карта.

3) CPU став координатором, і він теж не масштабувався

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

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

4) Модель профілів драйвера не вижила в ланцюгу постачання ПЗ

Драйвер‑кероване SLI/CrossFire вимагало від вендорів встигати за новими релізами ігор, патчами, оновленнями рушіїв і новими техніками рендерингу. Студії гри
випускали щотижневі оновлення. Постачальники GPU випускали драйвери повільнішим темпом і мали тестувати тисячі комбінацій.

Профіль мульти‑GPU, який працював у версії 1.0, може зламатися у 1.0.3 через те, що змінився порядок пост‑обробки або новий тимчасовий фільтр почав читати попередній буфер кадру.
Драйвер, що «оптимізує» без контексту, може стати тим, що пошкоджує кадр.

5) VRR (змінна частота оновлення) та мульти‑GPU зробили одне одному пакості

Змінна частота оновлення — одне з найкращих покращень якості життя в ПК‑іграх. Вона також ускладнює кадрування мульти‑GPU: дисплей адаптується до кадрувальної подачі,
тому якщо AFR створює сплески й паузи, VRR не «згладить» це; він відобразить нерівномірність чесно.

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

6) Явне мульти‑GPU зʼявилося, і індустрія не захотіла платити рахунок

DX12 і Vulkan зробили можливим explicit multi‑adapter: рушій може керувати кількома GPU без посередництва драйвера. Це технічно чистіше, ніж драйверна магія.
Але це також дорога інженерна робота, яка приносить користь лише крихітній частці клієнтів.

Студії віддавали пріоритет фічам, які доходили до всіх: кращі апскейлери, кращий анти‑аліасинг, кращі пайплайни контенту, краща паритетність з консолями.
Мульти‑GPU був тягарем підтримки з низьким ROI. Він помер так само, як багато корпоративних фіч: тихо, бо ніхто не фінансував ротацію on‑call.

7) Енергоспоживання, тепловий режим і обмеження корпусу: фізичний шар дав відсіч

Дві флагманські GPU вимагають серйозного запасу по живленню, гарного повітряного потоку і часто материнської плати, яка може надати достатньо PCIe‑лінів без тротлінгу.
Конфігурація «користувацький корпус + дві флагманські GPU» — це проєкт теплової інженерії. А більшість людей хотіли компʼютер, а не хобі, що палить пил.

8) Безпека і стабільність: стек драйверів став більшим радіусом ураження

Чим складніша логіка планування драйвера і синхронізації між GPU, тим більше режимів відмов: чорні екрани, TDR (timeout detection and recovery),
дивні артефакти, краші конкретних ігор. В операційних термінах: ви підвищили складність системи і знизили середній час до невинності.

Жарт №2: SLI обіцяв «двічі більше GPU», але іноді давав «двічі більше вирішення проблем», що не є фічею, яку хтось вимірює в бенчмарках.

Історичний контекст: факти, які люди забувають

  • Факт 1: Початкове імʼя «SLI» походить від Scan‑Line Interleave компанії 3dfx наприкінці 1990‑х; NVIDIA пізніше знову використала абревіатуру з іншим технічним підходом.
  • Факт 2: Раннє споживче мульти‑GPU часто сильно покладалося на AFR, бо це був найпростіший спосіб масштабування без переписування рушіїв.
  • Факт 3: Масштабування мульти‑GPU було відомо як непослідовне: деякі тайтли бачили майже лінійний приріст, інші — нуль, а деякі ставали повільнішими через накладні витрати CPU/драйвера.
  • Факт 4: «Мікростаттер» став масовою скаргою на початку 2010‑х, коли оглядачі почали вимірювати фрамтайми, а не тільки середній FPS.
  • Факт 5: AMD інвестувала в покращення кадрування у драйверах після широкої критики; це допомогло, але не змінило фундаментальних обмежень AFR.
  • Факт 6: Багато рушіїв дедалі більше використовували тимчасові буфери історії (TAA, тимчасові апскейлери, вектори руху), що природно незручно для AFR.
  • Факт 7: Пропускна здатність PCIe зростала покоління за поколінням, але затримки та накладні витрати на синхронізацію залишалися центральними проблемами для залежностей між кадрами.
  • Факт 8: Явне мульти‑GPU у DX12/Vulkan передало контроль у застосунок; більшість студій вирішили не впроваджувати його через вибух тестової матриці.
  • Факт 9: NVIDIA поступово обмежувала/змінювала підтримку SLI у пізніших поколіннях, фокусуючись на високому сегменті і конкретних випадках використання замість широкої підтримки ігор.

Що їх замінило (певною мірою): явне мульти‑GPU і сучасні альтернативи

Явне мульти‑GPU: краща архітектура, гірша економіка

Явне мульти‑GPU (DX12 multi‑adapter, Vulkan device groups) — це те, як би ви спроектували систему, якщо б були тверезі: рушій знає, які навантаження можуть виконуватися
на якій GPU, які дані потрібно ділити і коли синхронізувати. Це прибирає багато здогадок драйвера.

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

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

Сучасне «мульти‑GPU», яке працює: спеціалізація

Мульти‑GPU живе там, де навантаження природно паралельне і не вимагає жорсткої когерентності між кадрами:

  • Офлайн‑рендеринг / path tracing: можна розподіляти вибірки або тайли між GPU та зливати результати.
  • Обчислення / навчання ML: дата‑паралелізм у явних фреймворках, хоч і з власними болями синхронізації.
  • Пайплайни кодування відео: окремі GPU можуть обробляти окремі потоки або стадії.

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

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

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

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

  • Чи обидва пристрої присутні в PCIe?
  • Чи працюють вони на очікуваному PCIe поколінні/ширині?
  • Чи встановлений потрібний міст (якщо він потрібен)?
  • Чи правильно підключені силові конектори і чи стабільне живлення?

По‑друге: підтвердьте, що програмний шлях справді мульти‑GPU

  • Чи відомо, що гра підтримує SLI/CrossFire для вашого покоління GPU?
  • Чи присутній/увімкнений профіль драйвера?
  • Чи сумісний API‑шлях (DX11 vs DX12 vs Vulkan) з мульти‑GPU‑режимом вендора?

По‑третє: виміряйте фреймтайми і визначте обмежений ресурс

  • Завантаження GPU по карті (не лише «загальне»).
  • Насичення потоку рендерингу CPU.
  • Використання VRAM і поведінка пагінгу.
  • Кадрування (фреймтайм 99‑го перцентиля), а не лише середній FPS.

По‑четверте: прибирайте змінні, поки поведінка не стане зрозумілою

  • Тимчасово вимкніть VRR/VSync, щоб подивитися сире кадрування.
  • Протестуйте відому добру гру/бенчмарк з задокументованим масштабуванням.
  • Протестуйте кожну GPU окремо, щоб виключити марґінальну карту.

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

Це передбачає Linux‑робочі станції, які використовують для тестування/CI стендів, лабораторного відтворення або просто тому, що вам подобається біль в передбачуваний спосіб.
Суть не в тому, що Linux — це місце піку SLI у іграх; справа в тому, що Linux дає спостережуваність без пошуку у GUI.

Завдання 1: Перелічити GPU і підтвердити топологію PCIe

cr0x@server:~$ lspci -nn | egrep -i 'vga|3d|display'
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP102 [GeForce GTX 1080 Ti] [10de:1b06]
02:00.0 VGA compatible controller [0300]: NVIDIA Corporation GP102 [GeForce GTX 1080 Ti] [10de:1b06]

Що це означає: Дві GPU перелічені на шині PCIe. Якщо ви бачите лише одну — зупиніться: у вас апаратна/прошивкова проблема.

Рішення: Якщо одна GPU відсутня, переставте в слот, перевірте живлення, налаштування BIOS (Above 4G decoding, конфігурація слотів PCIe), потім повторіть тест.

Завдання 2: Перевірити ширину та покоління PCIe для кожної GPU

cr0x@server:~$ sudo lspci -s 01:00.0 -vv | egrep -i 'LnkCap|LnkSta'
LnkCap: Port #0, Speed 8GT/s, Width x16
LnkSta: Speed 8GT/s, Width x16

Що це означає: GPU узгоджує PCIe Gen3 x16 як очікується. Якщо ви бачите x8 або Gen1 — ви знайшли вузьке місце або фолбек.

Рішення: Якщо лінк понижений, перевірте підключення слотів, шаринг ліній на материнській платі (M.2 відбирає лінії), налаштування BIOS PCIe, райзери і цілісність сигналу.

Завдання 3: Підтвердити, що драйвер NVIDIA бачить обидві GPU і звітує завантаження

cr0x@server:~$ nvidia-smi -L
GPU 0: GeForce GTX 1080 Ti (UUID: GPU-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee)
GPU 1: GeForce GTX 1080 Ti (UUID: GPU-ffffffff-1111-2222-3333-444444444444)

Що це означає: Драйвер бачить обидва пристрої. Якщо одна відсутня тут, але присутня в lspci, ймовірно проблема з привʼязкою драйвера або невідповідністю прошивки.

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

Завдання 4: Дивитися завантаження та памʼять по кожній GPU під навантаженням

cr0x@server:~$ nvidia-smi dmon -s pucvmet
# gpu   pwr gtemp mtemp    sm   mem   enc   dec  mclk  pclk   pviol  rxpci  txpci
    0   210    78     -    92    55     0     0  5500  1582      0    120    110
    1    95    64     -    18    52     0     0  5500  1582      0     40     35

Що це означає: GPU0 робить реальну роботу; GPU1 здебільшого простає, але тримає схожу кількість VRAM (дзеркалення активів). Це класична поведінка «друга GPU не використовується».

Рішення: Якщо GPU1 залишається простою, перевірте, чи додаток підтримує мульти‑GPU; інакше припиніть витрачати час на виправлення неможливої фічі.

Завдання 5: Підтвердити деталі сесії Xorg/Wayland (щоб уникнути сюрпризів від композитора)

cr0x@server:~$ echo $XDG_SESSION_TYPE
wayland

Що це означає: Ви на Wayland. Деякі інструменти і деякі застарілі шляхи мульти‑GPU поводяться по‑різному під Wayland і під Xorg.

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

Завдання 6: Перевірити логи ядра на помилки PCIe і ресети GPU

cr0x@server:~$ sudo dmesg -T | egrep -i 'pcie|aer|nvrm|gpu|xid' | tail -n 12
[Mon Jan 13 10:19:22 2026] NVRM: Xid (PCI:0000:02:00): 79, GPU has fallen off the bus.
[Mon Jan 13 10:19:22 2026] pcieport 0000:00:03.1: AER: Corrected error received: 0000:02:00.0

Що це означає: «Fallen off the bus» часто вказує на нестабільність живлення/термічну проблему, поганий райзер, ненадійний слот або проблеми цілісності сигналу — мульти‑GPU робить це більш імовірним.

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

Завдання 7: Перевірити індикатори вузького місця CPU (завантаження, черга runnable, тротлінг)

cr0x@server:~$ uptime
 10:22:11 up 3 days,  6:41,  1 user,  load average: 14.82, 13.97, 12.10

Що це означає: Високе значення load average може вказувати на насичення CPU або накопичення runnable‑потоків. Ігри можуть бути CPU‑звʼязані на одному потоці рендеру, навіть якщо загальний CPU не «100%».

Рішення: Якщо навантаження високе, а завантаження GPU низьке, припиніть ганятися за перемикачами SLI. Знизьте налаштування, що нагружають CPU (відстань видимості, густота NPC), або прийміть, що ви CPU‑звʼязані.

Завдання 8: Інспектувати використання по ядрах, щоб виявити «зашитий» рендер‑потік

cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.5.0 (server)  01/13/2026  _x86_64_  (16 CPU)

10:22:18 AM  CPU   %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
10:22:19 AM  all   42.0  0.0  8.0   0.2    0.0  0.5    0.0    0.0    0.0   49.3
10:22:19 AM    3   98.5  0.0  1.0   0.0    0.0  0.0    0.0    0.0    0.0    0.5

Що це означає: Одне ядро (CPU3) завантажене на максимум. Це ваш вузький потік рендеру/гри. Дві GPU не допоможуть, якщо кадр не може бути поданий.

Рішення: Знизьте налаштування, що навантажують CPU, або перейдіть на платформу з кращою одноядерною продуктивністю. Мульти‑GPU не вирішить вузький ап‑пайп.

Завдання 9: Перевірити тиск памʼяті (пагінг може маскуватися як «затримки GPU»)

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            32Gi        30Gi       500Mi       1.2Gi       1.5Gi       1.0Gi
Swap:           16Gi        10Gi       6.0Gi

Що це означає: Ви активно свопите. Це знищить фреймтайми незалежно від кількості GPU, які ви накопичуєте.

Рішення: Виправте тиск памʼяті спочатку: закрийте фонові додатки, знизьте налаштування текстур, додайте RAM і повторіть тест. Вважайте використання swap червоним прапором для кадрування.

Завдання 10: Підтвердити частоту CPU і стан тротлінгу

cr0x@server:~$ lscpu | egrep -i 'model name|cpu mhz'
Model name:                           AMD Ryzen 9 5950X 16-Core Processor
CPU MHz:                               3599.998

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

Рішення: Якщо частоти падають під ігровим навантаженням, виправте охолодження або ліміти живлення. Мульти‑GPU підвищує температуру корпусу, що може тихо знижувати буст CPU.

Завдання 11: Перевірити прапори обмеження потужності / тротлінга на NVIDIA

cr0x@server:~$ nvidia-smi -q -d PERFORMANCE | egrep -i 'Power Limit|Clocks Throttle Reasons' -A3
    Power Limit                        : 250.00 W
    Clocks Throttle Reasons
        Idle                           : Not Active
        Applications Clocks Setting     : Not Active
        SW Power Cap                   : Active

Що це означає: GPU досягає програмного ліміту потужності. У мульти‑GPU системах блок живлення і VRM теплові умови можуть навʼязувати консервативні ліміти.

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

Завдання 12: Перевірити підказки про шаринг PCIe з NVLink/статус мосту (коли доступно)

cr0x@server:~$ nvidia-smi topo -m
        GPU0    GPU1    CPU Affinity
GPU0     X      PHB     0-7
GPU1    PHB      X      8-15

Що це означає: PHB вказує, що комунікація йде через PCIe host bridge; це не тісний peer‑to‑peer шлях. Це може зашкодити навантаженням, які потребують інтенсивного обміну між GPU.

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

Завдання 13: Підтвердити видимість пристроїв Vulkan (для експериментів з явним мульти‑GPU)

cr0x@server:~$ vulkaninfo --summary | egrep -i 'GPU id|deviceName' -A1
GPU id : 0 (GeForce GTX 1080 Ti)
deviceName     = GeForce GTX 1080 Ti
GPU id : 1 (GeForce GTX 1080 Ti)
deviceName     = GeForce GTX 1080 Ti

Що це означає: Vulkan бачить обидва пристрої. Це передумова для явних мульти‑GPU додатків, але не гарантія, що гра це підтримуватиме.

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

Завдання 14: Перевірити затримку сховища (так, це може виглядати як «затримки GPU»)

cr0x@server:~$ iostat -xz 1 3
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          38.12    0.00    6.21    8.93    0.00   46.74

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s w_await aqu-sz  %util
nvme0n1         210.0   38400.0     0.0   0.00   3.10   182.86    75.0   10240.0   9.80   2.10  78.00

Що це означає: Високий iowait і підвищений await можуть спричинити підвисання стримінгу. Мульти‑GPU не виправить затримки компіляції шейдерів чи латентності стрімінгу ресурсів.

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

Поширені помилки (симптом → корінь → виправлення)

1) «Друга GPU показує 0–10% завантаження»

Симптоми: Одна GPU гаряча, інша простає; FPS незмінний порівняно з однією GPU.

Корінь: Ігровий/API‑шлях не підтримує драйвер‑кероване мульти‑GPU, або профіль драйвера відсутній/вимкнений.

Виправлення: Перевірте підтримку тайтлу для вашого покоління GPU і режим API. Якщо гра на DX12/Vulkan і не реалізує явне мульти‑GPU, прийміть однокарткову конфігурацію.

2) «Вищий середній FPS, але відчуття гірше»

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

Корінь: Варіація фреймтаймів від AFR (мікростаттер), черги або непостійне навантаження на кадр.

Виправлення: Виміряйте фреймтайми і обмежте FPS для стабілізації кадрування, або вимкніть мульти‑GPU. Пріоритезуйте 1% low / 99‑й перцентиль фреймтаймів замість середніх значень.

3) «Текстури зʼявляються пізніше, потім фризи стають жахливими на 4K»

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

Корінь: Ліміт VRAM на GPU; дзеркалення означає, що ви не отримали додаткової ємності. Відбувається пагінг активів і зупинки.

Виправлення: Знизьте роздільність текстур, зменште налаштування RT, або перейдіть на одну GPU з більшим VRAM.

4) «Випадкові чорні екрани / GPU зникла»

Симптоми: Ресети драйвера, одна GPU випадає з шини, переривчасті проблеми зі стабільністю.

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

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

5) «Працює в одній версії драйвера, ламається в наступній»

Симптоми: Масштабування зникає або зʼявляються артефакти після оновлення драйвера.

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

Виправлення: Зафіксуйте версії драйверів для вашого випадку використання, документуйте робочі комбінації і не вважайте «новіший драйвер» автоматично кращим для мульти‑GPU.

6) «Дві GPU, але використання CPU виглядає низьким — все одно CPU‑звʼязано»

Симптоми: Завантаження GPU низьке, FPS зафіксований, загальний CPU < 50%.

Корінь: Один або два гарячі потоки (рендер, ігровий потік). Загальний CPU приховує насичення по ядрах.

Виправлення: Спостерігайте використання по ядрах. Знизьте CPU‑важкі налаштування; орієнтуйтеся на стабільні фреймтайми; подумайте про апгрейд платформи замість додавання GPU.

7) «PCIe x8/x4 несподівано, погане масштабування»

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

Корінь: Шерінг ліній з M.2/іншими пристроями, неправильний вибір слота або обмеження чипсету.

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

Три корпоративні міні‑історії з поля бою

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

Невелика студія мала «лабораторію продуктивності» з кількома висококласними тестовими стендами. Хтось побудував монстр‑машину: дві топові GPU, купа RGB і таблиця бенчмарків,
що тішила менеджмент. Студія використовувала її для погодження бюджетів продуктивності для нового контент‑навантаженого рівня.

Неправильне припущення було тонким: вони вважали, що масштабування репрезентативне. Їхній підписувальний стенд працював в AFR з профілем драйвера, який випадково добре
підходив для тієї конкретної збірки. Він давав чудовий середній FPS у лабораторії. На більшості клієнтських машин він не давав хороших фреймтаймів і точно не репрезентував
одиночну GPU‑базу, якою володіла більшість.

Настав релізний тиждень. Соцмережі заповнилися скаргами на «фризи на новому рівні». Всередині студії стенд виглядав «ок». Інженери почали шукати фантомні баги в анімації і фізиці,
бо графіки GPU не здавалися зашитими.

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

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

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

Команда візуалізації підприємства (подумайте: великі сцени CAD, рендеринги в реальному часі) спробувала «витиснути безкоштовну продуктивність», увімкнувши AFR у контрольованому середовищі.
Їхні сцени були важкими на тимчасове накопичення: анти‑аліасинг, денойзинг і купа логіки «використовувати попередній кадр». Хтось стверджував, що оскільки GPU ідентичні, результати мають бути погодженими.

Вони отримали вищу середню пропускну здатність зі статичною камерою. Чудове демо. Потім вони випустили бета‑версію внутрішнім стейкхолдерам. Як тільки камера рухнулася — стабільність зображення
погіршилася: привиди, мерехтіння і непостійні тимчасові фільтри. Гірше, інтерактивна затримка відчувалася гірше через збільшення глибини черг під AFR.

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

Вони врешті‑решт вимкнули AFR і інвестували в нудні покращення: culling на боці CPU, спрощення шейдерів і правила LOD для контенту. Кінцева система була швидшою на одній GPU,
ніж AFR‑збірка була на двох. Оптимізація провалилася, бо намагалася паралелізувати те, що було фундаментально серійним у часовій залежності.

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

Група валідації апаратного забезпечення в середній компанії підтримувала флот GPU‑тестових вузлів. Вони не гралися на них; запускали регресії рендерингу і обчислень і час від часу відтворювали баги клієнтів.
Ноди включали мульти‑GPU бокси, бо клієнти використовували їх для обчислень, а не для розваг.

Їхньою секретною зброєю не був хитрий планувальник. Це був changelog. Кожен нод мав закріплену версію драйвера, базову прошивку і просту матрицю «known‑good».
Оновлення виконувалися поетапно: спочатку канарка‑нод, потім невелика партія, потім решта. Без винятків. Нікому це не подобалося. Це здавалося повільним.

Одного тижня новий драйвер ввів інтермітуючі кориговані помилки PCIe на конкретній ревізії материнської плати при навантаженні обох GPU змішаного типу.
На робочій станції розробника це виглядало як випадкові краші додатків. У флоті канарочний нод почав виділяти AER‑логи протягом кількох годин.

Через нудну дисципліну вони корелювали таймлайн, відкотили канарку і заблокували розгортання. Ніякої флот‑широкої нестабільності, ніякого масового перевстановлення, ніякого панічного порятунку.
Вони подали тикет вендору з відтворюваними логами і чіткою рецептурою відтворення.

«Порятунок» не був героїчною відладкою. Це була операційна практика поетапних розгортань і фіксації версій. Мульти‑GPU системи підсилюють маргінальні проблеми; єдино розумна відповідь —
ставитися до змін як до виробничих, а не як до вихідних експериментів.

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

Покроково: вирішіть, чи варто взагалі зачіпати мульти‑GPU

  1. Визначте мету. Це вищий середній FPS, кращі 1% лови, чи конкретне обчислювальне/рендерне навантаження?
  2. Ідентифікуйте тип навантаження. Режим реального часу з тимчасовими ефектами? Припустіть «ні». Офлайн‑рендер/обчислення? Можливо «так».
  3. Перевірте реальність підтримки. Якщо додаток не реалізує явне мульти‑GPU і вендор більше не підтримує профілі драйверів — зупиніться тут.
  4. Заміряйте базу. Одна GPU, стабільний драйвер, фреймтайми, використання VRAM, по‑ядрове завантаження CPU.
  5. Додайте другу GPU. Підтвердьте ширину PCIe, живлення, теплові умови і топологію.
  6. Повторно виміряйте. Шукайте покращення в 99‑му перцентилі фреймтаймів і пропускній здатності, а не лише в середньому FPS.
  7. Прийміть рішення. Якщо виграш невеликий або кадрування стало гіршим — приберіть другу карту. Податок на складність реальний.

Покроково: стабілізувати мульти‑GPU бокс (коли потрібно його запускати)

  1. Спочатку працюйте на стокових частотах. Розгони, «стабільні» на одній GPU, можуть впасти в двокарткових теплових умовах.
  2. Підтвердьте бюджет потужності. Переконайтеся в запасі по PSU; уникайте ланцюгового зʼєднання кабелів PCIe для високого споживання.
  3. Зафіксуйте версії. Закріпіть драйвер/прошивку; виконуйте оновлення поетапно, як у продакшені.
  4. Інструментуйте. Логуйте dmesg, AER‑події, причини тротлінгу GPU, температури і завантаження.
  5. Встановіть очікування. Для ігор ви оптимізуєте для стабільності і кадрування, а не для скріншотів бенчмарків.

Питання та відповіді

1) Чи працювали SLI/CrossFire колись по-справжньому?

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

2) Чому VRAM не сумувався між GPU для ігор?

Тому що кожному GPU потрібен локальний доступ до текстур і геометрії на повній швидкості, і споживче мульти‑GPU зазвичай дзеркалить ресурси на кожну карту.
Без уніфікованої моделі памʼяті ви не можете трактувати два пулі VRAM як один без великих витрат на синхронізацію і передачі.

3) Що таке мікростаттер, з операційної точки зору?

Це варіація латентності. Ви доставляєте кадри в непостійних інтервалах — сплески й паузи — через що рух виглядає нерівним. Саме тому «середній FPS» — небезпечна і неповна метрика.

4) Чому DX12/Vulkan зробили мульти‑GPU рідшим замість більш поширеним?

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

5) Чи можуть дві різні GPU працювати разом для ігор зараз?

Не так, як раніше «драйвер сам за вас». Явний multi‑adapter теоретично може використати різнорідні GPU, але реальна підтримка рідкісна і зазвичай спеціалізована. Для типових ігор —
припускайте «ні».

6) А NVLink — чи виправляє це?

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

7) Якщо я вже володію двома GPU, що мені робити?

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

8) Яким метрикам варто довіряти при тестуванні мульти‑GPU?

Перцентилі фреймтаймів (наприклад, 99‑й), відчуття затримки вводу (важко виміряти, легко помітити), завантаження по GPU, запас VRAM і логи стабільності.
Середній FPS у цьому контексті — метрика для показухи.

9) Чи мульти‑GPU повністю вмерло?

Не повністю — просто в споживчих реальних іграх як стандартний шлях прискорення. Мульти‑GPU живе там, де навантаження можна чисто розділити: офлайн‑рендеринг, наукові обчислення,
ML і деякі професійні візуалізаційні пайплайни.

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

Якщо ви думаєте про мульти‑GPU для ігор у 2026 році, ось відверта порада: не робіть цього. Купіть найкращу одну GPU, яку можете собі дозволити, а потім оптимізуйте
фреймтайми, запас VRAM і стабільність стеку драйверів. Ви отримаєте систему, яка поводиться передбачувано — а це те, чого ви хочете, коли вам доводиться її налагоджувати.

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

Практичні наступні кроки:

  • Змініть мислення тестування з «середній FPS» на перцентилі фреймтаймів і відтворювані прогони.
  • Підтвердьте ширину PCIe, топологію і стабільність живлення перед налаштуванням драйверів.
  • Вирішіть уперед, чи ваш додаток використовує явне мульти‑GPU; якщо ні — припиніть витрачати час.
  • Тримайте одну відому‑добру базову версію драйвера і ставтесь до оновлень як до контрольованого розгортання.

Docker Desktop Networking Weirdness: LAN Access, Ports, and DNS Fixes That Actually Work

Ви запускаєте docker run -p 8080:80, заходите на localhost:8080 і все працює. Передаєте URL колезі в тій самій Wi‑Fi мережі, і… нічого.
Або контейнер може робити curl в інтернет, але не досягає NAS у локальній мережі. Або DNS підкидає монету щоразу, коли підключається VPN.

Мережі Docker Desktop не «зламані». Просто це не та модель мереж Linux‑хоста, яку ви звикли уявляти.
Це віртуальна машина, NAT, набір платформозалежних прокладок і кілька спеціальних імен, які існують здебільшого, щоб зберегти нам розум.

Ментальна модель: чому Docker Desktop відрізняється

На Linux Docker зазвичай підключає контейнери до мостової мережі на хості, використовує iptables/nftables для NAT вихідного трафіку
і додає правила DNAT для опублікованих портів. Ваш хост — це хост. Ядро, що запускає контейнери, — те ж саме ядро, яке запускає ваш шелл.

Docker Desktop на macOS і Windows за замовчуванням інший. Він запускає невелику Linux‑VM (або середовище Linux через WSL2),
і контейнери живуть поза межею віртуалізації. Ця межа пояснює, чому «host‑networking» поводиться дивно,
чому доступ з LAN несиметричний і чому публікація портів може здаватися орієнтованою лише на localhost.

Думайте шарами:

  • Ваша фізична ОС (macOS/Windows): має інтерфейс Wi‑Fi/Ethernet, клієнт VPN і брандмауер.
  • Docker VM / WSL2: має власний віртуальний NIC, таблицю маршрутизації, власні iptables і поведінку DNS.
  • Мережі контейнерів: мости всередині того Linux‑середовища; контейнери рідко торкаються фізичного LAN напряму.
  • Прокладка публікації портів: Docker Desktop пересилає порти від ОС хоста до VM і до контейнера.

Тому коли хтось каже «контейнер не може дістатися LAN», перше питання: «Який шар не може дістатися якого шару?»

Цікаві факти та коротка історія (те, що пояснює сьогоднішній біль)

  1. Початкова модель мереж Docker припускала Linux. Ранній Docker популяризував патерн «мост + NAT + iptables», бо Linux робив це просто й портативно.
  2. macOS не може запускати Linux‑контейнери нативно. Docker Desktop на macOS завжди покладається на Linux‑VM, оскільки контейнери потребують функцій ядра Linux (namespaces, cgroups).
  3. Windows проходив два етапи. Спочатку був Docker Desktop на базі Hyper‑V; потім WSL2 став дефолтним шляхом для кращої роботи з файловою системою та ресурсами, з іншими мережевими особливостями.
  4. host.docker.internal існує тому, що «хост» неоднозначний. Всередині контейнера «localhost» — це контейнер; Docker Desktop потрібне стабільне ім’я для «ОС‑хоста».
  5. Опубліковані порти — це не просто правила iptables на Desktop. На Linux це саме вони; на Desktop часто це реалізовано через проксі/форвардер у просторі користувача через межу VM.
  6. Клієнти VPN люблять переписувати ваш DNS і маршрути. Вони часто встановлюють новий DNS‑сервер, блокують split DNS або додають віртуальний інтерфейс з вищим пріоритетом, ніж Wi‑Fi.
  7. Корпоративна захист часто інжектує локальний проксі. Це може ламати DNS контейнерів, MITM TLS або непомітно перенаправляти трафік на інспекційну інфраструктуру.
  8. ICMP вам бреше у віртуальних мережах. «Не пінгується» не reliably означає «не підключається», особливо коли брандмауер блокує ICMP, але дозволяє TCP.

Жарт №1: мережі Docker Desktop схожі на оргштаб — завжди є ще один шар, ніж ви думаєте, і ніхто не несе за нього відповідальності.

Швидка діагностика (перевірте перше/друге/третє)

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

1) Це проблема публікації порту чи маршрутизації/DNS?

  • Якщо localhost:PORT працює на вашій машині, але клієнти з LAN не дотягуються — ймовірно, справа в брандмауері хоста/адресі прив’язки/фільтрації маршрутів VPN.
  • Якщо контейнери не можуть резолвити імена або дістатися будь‑якого зовнішнього хоста — починайте з DNS і вихідної маршрутизації всередині контейнера/VM.

2) Визначте, де «вмирає» пакет (ОС хоста → VM → контейнер)

  • З ОС хоста: чи можете ви дістатись цілі в LAN?
  • Зсередини контейнера: чи можете ви дістатись тієї самої LAN‑цілі по IP?
  • Зсередини контейнера: чи резолвиться імʼя?

3) Перевірте реальну адресу прив’язки/прослуховування і форвардер

  • Чи сервіс прослуховує на 0.0.0.0 всередині контейнера, або тільки на 127.0.0.1?
  • Чи Docker публікує порт на всіх інтерфейсах або тільки на localhost?
  • Чи брандмауер хоста блокує вхідні з LAN?

4) Рано перевіряйте VPN і поведінку перевизначення DNS

  • Якщо проблема з’являється/зникає разом із VPN — не вважайте це багом Docker. Це політики, маршрути, DNS або інспекція трафіку.

5) Лише тоді міняйте налаштування Docker Desktop

  • Зміна DNS‑серверів або діапазонів мереж може допомогти, але робіть це з доказами. Інакше створите нову загадку.

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

Ось перевірки, які я насправді виконую. Кожна включає: команду, приклад виводу, що це означає і яке рішення приймати далі.
Команди показані з універсального запрошення; адаптуйте імена інтерфейсів та IP під своє середовище.

Task 1: Confirm which Docker context you’re using

cr0x@server:~$ docker context ls
NAME                DESCRIPTION                               DOCKER ENDPOINT
default *           Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
desktop-linux       Docker Desktop                            unix:///Users/me/.docker/run/docker.sock

Значення: Якщо ви думаєте, що працюєте з Desktop, але насправді підключені до віддаленого демона (або навпаки), всі мережеві припущення будуть неправильні.
Рішення: Якщо контекст зі зірочкою не той, що ви очікували, переключіться: docker context use desktop-linux.

Task 2: Inspect a container’s IP and network attachment

cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Ports}}'
NAMES          PORTS
web            0.0.0.0:8080->80/tcp
db             5432/tcp
cr0x@server:~$ docker inspect -f '{{.Name}} {{range .NetworkSettings.Networks}}{{.IPAddress}} {{.Gateway}}{{end}}' web
/web 172.17.0.2 172.17.0.1

Значення: Контейнер живе на внутрішньому мосту (тут 172.17.0.0/16). Це не ваш LAN.
Рішення: Якщо ви намагаєтеся дістатися до 172.17.0.2 з іншого ноутбука в Wi‑Fi — припиніть. Публікуйте порт або використайте інший мережевий режим.

Task 3: Check what address your service is actually listening on

cr0x@server:~$ docker exec -it web sh -lc "ss -lntp | head -n 5"
State  Recv-Q Send-Q Local Address:Port  Peer Address:Port Process
LISTEN 0      4096   0.0.0.0:80         0.0.0.0:*     users:(("nginx",pid=1,fd=6))

Значення: Прослуховування на 0.0.0.0 — добре; сервіс приймає трафік із мережі контейнера.
Якщо бачите 127.0.0.1:80, публікація порту «працюватиме» дивними способами або взагалі не спрацює.
Рішення: Якщо привʼязка до localhost — виправте конфігурацію програми: привʼяжіть до 0.0.0.0.

Task 4: Verify published port bindings on the Docker side

cr0x@server:~$ docker port web
80/tcp -> 0.0.0.0:8080

Значення: Docker вважає, що опублікував порт на всіх інтерфейсах.
Рішення: Якщо показує 127.0.0.1:8080, клієнти з LAN його не побачать. Перезапустіть з -p 0.0.0.0:8080:80 (або виправте файл compose).

Task 5: Confirm the host OS is listening on the expected port

cr0x@server:~$ ss -lntp | grep ':8080'
LISTEN 0      4096      0.0.0.0:8080     0.0.0.0:*    users:(("com.docker.backend",pid=2314,fd=123))

Значення: На Desktop часто видно процес бекенду Docker, а не PID контейнера. Це нормально.
Рішення: Якщо нічого не слухає — публікація не застосувалася або порт зайнятий іншим процесом.

Task 6: Test from the host OS to confirm the forward path works

cr0x@server:~$ curl -sS -D- http://127.0.0.1:8080/ | head
HTTP/1.1 200 OK
Server: nginx/1.25.3
Date: Sat, 03 Jan 2026 09:12:52 GMT
Content-Type: text/html

Значення: Форвардинг порту з хоста в контейнер працює локально.
Рішення: Якщо клієнти з LAN не підключаються — фокусуйтеся на брандмауері/VPN/привʼязці до localhost, а не на додатку в контейнері.

Task 7: Test from a LAN peer (simulate with another namespace/host if you can)

cr0x@server:~$ nc -vz 192.168.1.50 8080
Connection to 192.168.1.50 8080 port [tcp/http-alt] succeeded!

Значення: Порт доступний з LAN.
Рішення: Якщо таймаут — ймовірно брандмауер/маршрути. Якщо «refused» — щось слухає, але не приймає на цьому інтерфейсі або форвардер привʼязаний неправильно.

Task 8: Check container DNS configuration

cr0x@server:~$ docker exec -it web sh -lc "cat /etc/resolv.conf"
nameserver 192.168.65.5
search localdomain
options ndots:0

Значення: Docker Desktop часто інжектує stub‑резолвер IP (приклад: 192.168.65.5) всередині мережі VM.
Рішення: Якщо цей nameserver недосяжний або поводиться неправильно (часто з VPN), перевизначте DNS на рівні демона/compose.

Task 9: Test DNS resolution inside the container (don’t guess)

cr0x@server:~$ docker exec -it web sh -lc "getent hosts example.com | head -n 2"
2606:2800:220:1:248:1893:25c8:1946 example.com
93.184.216.34 example.com

Значення: DNS працює достатньо, щоб резолвити AAAA і A.
Рішення: Якщо зависає або нічого не повертає — проблема з DNS‑шляхом. Наступний крок: спробуйте резолвити через конкретний сервер або перевизначте резолвери.

Task 10: Test direct IP connectivity to a LAN resource from inside the container

cr0x@server:~$ docker exec -it web sh -lc "nc -vz 192.168.1.10 445"
192.168.1.10 (192.168.1.10:445) open

Значення: Маршрутизація контейнер → VM → ОС хоста → LAN працює для тієї цілі.
Рішення: Якщо IP працює, а імʼя — ні, це DNS. Якщо ні — маршрутизація/VPN/політика.

Task 11: Check the container’s default route (basic but decisive)

cr0x@server:~$ docker exec -it web sh -lc "ip route"
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 scope link  src 172.17.0.2

Значення: Контейнер має маршрут через gateway мосту. Gateway вирішує, як дістатися до LAN/інтернету.
Рішення: Якщо дефолтний маршрут відсутній або неправильний — у вас кастомна мережа; поверніться та протестуйте стандартний bridge.

Task 12: Check whether you’re colliding with a corporate/VPN subnet

cr0x@server:~$ ip route | head -n 12
default via 192.168.1.1 dev wlan0
10.0.0.0/8 via 10.8.0.1 dev tun0
172.16.0.0/12 via 10.8.0.1 dev tun0
192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.50

Значення: Якщо Docker‑мережі використовують 172.16.0.0/12, а ваш VPN також маршрутизує цей діапазон, ви створили неоднозначну маршрутизацію.
Desktop особливо чутливий до перекриття, бо вже робить NAT.
Рішення: Змініть внутрішні підмережі Docker, щоб уникнути перекриття з корпоративними маршрутами.

Task 13: Inspect Docker networks and their subnets

cr0x@server:~$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
a1b2c3d4e5f6   bridge    bridge    local
f1e2d3c4b5a6   host      host      local
123456789abc   none      null      local
cr0x@server:~$ docker network inspect bridge --format '{{(index .IPAM.Config 0).Subnet}}'
172.17.0.0/16

Значення: Ви тепер знаєте, які підмережі споживає Docker.
Рішення: Якщо це перекривається з VPN або вашим LAN — змініть їх.

Task 14: Validate that the container can reach the host OS via Docker Desktop’s special name

cr0x@server:~$ docker exec -it web sh -lc "getent hosts host.docker.internal"
192.168.65.2    host.docker.internal

Значення: Спеціальна відповідність існує й вказує на хост‑ендпоінт, який надає Docker.
Рішення: Якщо це імʼя не резолвиться — у вас старе середовище, кастомний режим мережі або DNS всередині контейнера змінений. Як крайній варіант використовуйте явні IP.

Шаблони доступу з LAN: що працює, а що ні

Є три поширені запити:

  • LAN → ваш сервіс у контейнері (колега хоче потрапити на ваш dev‑сервер).
  • Контейнер → ресурс у LAN (контейнер має дістати NAS, принтер, внутрішнє API, Kerberos тощо).
  • Контейнер → ОС хоста (контейнер викликає сервіс, що працює на вашому ноутбуці).

Шаблон A: LAN → контейнер через опубліковані порти (єдиний здравий дефолт)

Публікуйте порти на ОС‑хоста, а не намагайтеся роздавати IP контейнера.
У Docker Desktop ви не можете вважати IP контейнера маршрутизованим у фізичний LAN. Вони живуть за NAT, всередині VM, іноді за ще одним NAT якщо ваша ОС теж щось мудрує.

Що робити:

  • Привʼяжіть до всіх інтерфейсів: -p 0.0.0.0:8080:80 або в Compose "8080:80" і переконайтеся, що не публікуєте випадково тільки на localhost.
  • Відкрийте порт у брандмауері хоста (і обмежте сферу доступу; не відкривайте dev‑базу даних для кафе Wi‑Fi).
  • Якщо ваш VPN забороняє вхідні з LAN підключення, прийміть реальність: тестуйте без VPN або використайте належне dev‑середовище поза локалкою.

Шаблон B: контейнер → ресурси LAN (маршрутизація працює, поки не зламається)

Контейнери, що дістаються до LAN, зазвичай працюють «з коробки», бо Docker Desktop виконує NAT вихідного трафіку через ОС‑хоста.
Потім ви підключаєте VPN і ОС змінює DNS і маршрути. Раптом контейнер не може резолвити або дістатися підмереж, які тепер «належать» VPN.

Коли це ламається, воно ламається в кілька повторюваних способів:

  • Перекриття підмереж: Docker обирає приватний діапазон, який маршрутується вашим VPN. Пакети зникають у тунелі.
  • Несумісність split DNS: хост резолвить внутрішні імена через корпоративний DNS, але контейнери застрягають на stub‑резолвері, що не форвардить потрібні домени.
  • Політика брандмауера: корпоративний endpoint блокує трафік з «невідомих» віртуальних інтерфейсів.

Шаблон C: контейнер → сервіси ОС‑хоста (використовуйте спеціальні імена)

Використовуйте host.docker.internal. Саме для цього воно й існує.
Це не найелегантніше, але стабільніше при DHCP‑змінах і менш крихке, ніж хардкодити 192.168.x.y.

Якщо ви на Linux (не Desktop), цього імені може не бути; на Desktop воно зазвичай доступне.

Порти: публікація, адреси прив’язки і чому колеги не бачать ваш dev‑сервер

Опубліковані порти — це валюта «зробіть мій контейнер доступним». Все інше — заборгованість.

Localhost — це не моральна цінність, а адреса прив’язки

Дві речі постійно плутають:

  • Де програма слухає всередині контейнера (127.0.0.1 проти 0.0.0.0).
  • Де Docker привʼязує опублікований порт на хості (127.0.0.1:PORT vs 0.0.0.0:PORT).

Якщо будь‑яка з цих двох речей «тільки localhost», клієнти з LAN втрачають доступ. І ви витратите час, звинувачуючи інший шар.

Порада для Compose: не привʼязуйте випадково до localhost

Compose підтримує явну привʼязку IP хоста. Це чудово, коли ви маєте таку мету, і жахливо, коли ні.

cr0x@server:~$ cat docker-compose.yml
services:
  web:
    image: nginx:alpine
    ports:
      - "127.0.0.1:8080:80"

Значення: Цей сервіс навмисно доступний тільки з ОС‑хоста.
Рішення: Якщо ви хочете доступ із LAN, змініть на "8080:80" або "0.0.0.0:8080:80", а потім налаштуйте брандмауер за потребою.

Коли опубліковані порти все ще недоступні з LAN

Якщо Docker показує 0.0.0.0:8080, але клієнти з LAN не підключаються:

  • Брандмауер хоста: Application Firewall на macOS, Windows Defender Firewall, сторонні endpoint‑інструменти.
  • Вибір інтерфейсу: порт може бути привʼязаний, але ОС блокує вхідні на Wi‑Fi і дозволяє на Ethernet (або навпаки).
  • Політика VPN: деякі клієнти примусово «блокують локальний LAN» щоб зменшити ризик латерального руху.
  • Питання NAT hairpin: деякі мережі не дозволяють достукатися до власного публічного IP зсередини; це зробить ваш маршрутизатор, а не Docker.

Жарт №2: Нічого так не покращує командну роботу, як сказати «в мене працює» і мати це на увазі як архітектурне твердження.

DNS‑виправлення: від «воно нестабільне» до «воно детерміністичне»

DNS — це місце, куди Docker Desktop‑дивність йде ставати фольклором. Проблема зазвичай не в тому, що «Docker не вміє DNS».
Проблема в тому, що тепер у вас щонайменше два резолвери (ОС‑хост і VM), іноді три (VPN), і вони не узгоджені щодо split‑horizon правил.

Режим помилки 1: контейнер резолвить публічні імена, але не внутрішні

Класичний корпоративний split DNS: git.corp резолвиться лише через внутрішні DNS‑сервери, доступні тільки по VPN.
Ваша ОС‑хост робить усе правильно. Контейнер використовує stub‑резолвер, який не форвардить потрібні домени.

Варіанти виправлення, від кращого до гіршого:

  1. Сконфігуруйте DNS у Docker Desktop, щоб використовувати ваші внутрішні резолвери, коли ви на VPN, і публічні резолвери коли не на VPN. Іноді це треба робити вручну, бо «авто» ненадійне.
  2. DNS на проєкт у Compose:
    • Встановіть dns: на IP тих резолверів, які можуть відповідати і на внутрішні, і на зовнішні імена (часто ті, що надає VPN).
  3. Захардкодити /etc/hosts всередині контейнерів. Це тактичний хак, не стратегія.

Task 15: Override DNS in Compose and verify inside container

cr0x@server:~$ cat docker-compose.yml
services:
  web:
    image: alpine:3.20
    command: ["sleep","infinity"]
    dns:
      - 10.8.0.53
      - 1.1.1.1
cr0x@server:~$ docker compose up -d
[+] Running 1/1
 ✔ Container web-1  Started
cr0x@server:~$ docker exec -it web-1 sh -lc "cat /etc/resolv.conf"
nameserver 10.8.0.53
nameserver 1.1.1.1

Значення: Контейнер тепер використовує вказані DNS‑сервери.
Рішення: Якщо внутрішні домени почали резолвитись — ви довели, що проблема в шляху DNS/split DNS, а не в додатку.

Режим помилки 2: DNS працює, але лише інколи (таймаути, повільні збірки, нестабільні встановлення пакетів)

Періодичні проблеми з DNS часто походять від:

  • DNS‑серверів VPN, які гублять UDP під навантаженням або вимагають TCP для великих відповідей.
  • Корпоративних агентів безпеки, які перехоплюють DNS і інколи таймаутять.
  • Проблем MTU/MSS на тунельних лінках (DNS по UDP фрагментується і тихо вмирає).

Task 16: Detect DNS timeouts vs NXDOMAIN inside container

cr0x@server:~$ docker exec -it web-1 sh -lc "time getent hosts pypi.org >/dev/null; echo $?"
real    0m0.042s
user    0m0.000s
sys     0m0.003s
0

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

Режим помилки 3: внутрішній сервіс працює по IP, але не по імені (і лише на VPN)

Це знову split DNS, але зі складнішим ефектом: інколи VPN штовхає DNS‑суфікс і search‑domain на хості,
але резолвер Docker Desktop не успадковує це коректно.

Task 17: Confirm search domains inside container

cr0x@server:~$ docker exec -it web-1 sh -lc "cat /etc/resolv.conf"
nameserver 10.8.0.53
search corp.example
options ndots:0

Значення: Search domain присутній.
Рішення: Якщо його немає — FQDN можуть працювати, а короткі імена — ні. Використовуйте FQDN або налаштуйте search domains на рівні контейнера.

VPN, split tunnels, і «допомога» корпоративних endpoint‑ів

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

Маршрутизація: коли VPN «краде» ваш RFC1918 простір

Багато корпоративних мереж маршрутизують великі приватні діапазони типу 10.0.0.0/8 або 172.16.0.0/12 через тунель.
Docker за замовчуванням часто використовує 172.17.0.0/16 для bridge і інші 172.x для користувацьких мереж.

На чистому Linux‑хості ви зазвичай можете керувати цим за допомогою налаштувань мосту і iptables. На Desktop це теж можливо, але треба трактувати як конфігурацію першого класу.

Task 18: Create a user-defined network on a “safe” subnet

cr0x@server:~$ docker network create --subnet 192.168.240.0/24 devnet
9f8c7b6a5d4e3c2b1a0f
cr0x@server:~$ docker run -d --name web2 --network devnet -p 8081:80 nginx:alpine
b1c2d3e4f5a6
cr0x@server:~$ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web2
192.168.240.2

Значення: Ви перемістили мережу контейнера поза загальновживаними корпоративними маршрутами.
Рішення: Якщо проблеми, повʼязані з VPN, покращилися — введіть політику підмереж для dev‑мереж.

Endpoint‑security: невидима середова коробка

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

Ви не зможете «SRE‑шити» політику. Але ви можете швидко зібрати докази і ескалувати з конкретними даними.

Task 19: Prove it’s local firewall/policy with a quick inbound test

cr0x@server:~$ python3 -m http.server 18080 --bind 0.0.0.0
Serving HTTP on 0.0.0.0 port 18080 (http://0.0.0.0:18080/) ...

Значення: Це не Docker. Це звичайний процес на хості.
Рішення: Якщо пір‑peer у LAN теж не дотягується — припиняйте дебажити Docker і дивіться у бік брандмауера/VPN налаштувань «block local network».

Windows + WSL2 специфіка (куди пакети «йдуть на пенсію»)

На сучасному Windows Docker Desktop часто запускає рушій всередині WSL2. WSL2 має власну віртуальну мережу (NAT за Windows).
Це означає, що у вас може бути: NAT контейнера за Linux, за WSL2 NAT, за правилами брандмауера Windows. NAT до самого дна.

Типові симптоми на Windows

  • Опублікований порт доступний з Windows localhost, але не з LAN. Зазвичай це правила Windows Defender Firewall або привʼязка лише до loopback.
  • Контейнери не можуть дістатися підмережі LAN, яку Windows бачить. Зазвичай маршрути VPN не передаються у WSL2 або політика блокує WSL‑інтерфейси.
  • DNS відрізняється між Windows і WSL2. WSL2 пише власний /etc/resolv.conf; інколи він вказує на Windows‑сторінний резолвер, який не бачить DNS VPN.

Task 20: Check WSL2’s resolv.conf and route table (from inside WSL)

cr0x@server:~$ cat /etc/resolv.conf
nameserver 172.29.96.1
cr0x@server:~$ ip route | head
default via 172.29.96.1 dev eth0
172.29.96.0/20 dev eth0 proto kernel scope link src 172.29.96.100

Значення: WSL2 використовує Windows‑сторінний віртуальний шлюз/резолвер.
Рішення: Якщо DNS ламається лише з VPN — подумайте про фіксацію DNS в WSL2 (статичний resolv.conf) і узгодження DNS Docker з резолверами VPN.

macOS специфіка (pf, vmnet і ілюзія localhost)

На macOS Docker Desktop запускає Linux‑VM і форвардить порти назад на macOS.
Ваші контейнери не є повноцінними резидентами фізичного LAN. Вони гості за дуже ввічливим консьєржем.

На що плутаються користувачі macOS

  • «Працює на localhost, але не з мого телефона.» Зазвичай брандмауер macOS або порт опубліковано лише на loopback.
  • DNS змінюється при перемиканні Wi‑Fi мереж. Резолвер хоста змінюється швидко; VM іноді відстає або кешує дивно.
  • Корпоративний VPN блокує локальний підмережевий доступ. Ваш телефон не дістанеться до ноутбука, коли VPN підключено — і це не обовʼязково вина Docker.

Task 21: Confirm the host OS has the right IP and interface for LAN testing

cr0x@server:~$ ip addr show | sed -n '1,25p'
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    inet 127.0.0.1/8 scope host lo
2: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 192.168.1.50/24 brd 192.168.1.255 scope global dynamic wlan0

Значення: Ваша LAN‑IP — 192.168.1.50.
Рішення: Це адреса, яку повинен використовувати LAN‑peer, щоб дістатися до вашого опублікованого порту. Якщо колеги використовують старий IP — вони тестують не ту машину.

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

1) Симптом: localhost:8080 працює, колега не дотягується до 192.168.x.y:8080

  • Корінь: Порт опубліковано тільки на 127.0.0.1, або брандмауер хоста блокує вхідні.
  • Виправлення: Публікуйте на всіх інтерфейсах (-p 0.0.0.0:8080:80), потім дозвольте вхідні для цього порту у брандмауері для відповідного мережевого профілю.

2) Симптом: контейнер дістається в інтернет, але не до 192.168.1.10 (LAN NAS)

  • Корінь: Політика VPN «block local LAN» або маршрути, що штовхають LAN‑підмережі у тунель.
  • Виправлення: Тестуйте з відключеним VPN; якщо це допомагає — попросіть виключення для split‑tunnel або запускайте навантаження в належному середовищі (віддалена dev VM, staging). Не намагайтеся обходити політику хак‑методами.

3) Симптом: контейнер дістається до LAN IP, але внутрішні імена не працюють

  • Корінь: Split DNS не поширюється у Docker Desktop; контейнери використовують stub‑резолвер, що не бачить внутрішніх зон.
  • Виправлення: Налаштуйте DNS на проєкт (dns: у Compose) з корпоративними DNS, доступними через VPN; перевіряйте через getent hosts.

4) Симптом: DNS флапає під час збірок (apt/npm/pip випадково падають)

  • Корінь: Ненадійний UDP DNS через VPN, MTU проблеми, перехоплення endpoint‑ом.
  • Виправлення: Віддавайте перевагу стабільним резолверам; використовуйте два резолвери (внутрішній + публічний) де дозволено; зменшіть ризик фрагментації, налаштувавши MTU на VPN‑рівні, якщо це під контролем.

5) Симптом: сервіс опублікований, але з LAN приходить «connection refused»

  • Корінь: Додаток слухає тільки localhost контейнера або опубліковано неправильний порт.
  • Виправлення: Перевірте ss -lntp всередині контейнера; виправте адресу привʼязки; перевірте docker port і мапінг портів контейнера.

6) Симптом: не вдається підключитися до host.docker.internal з контейнера

  • Корінь: DNS‑перенапис видалив спеціальне імʼя, або ви використовуєте режим мережі, де Desktop його не інʼєкцює.
  • Виправлення: Уникайте сліпого перевизначення DNS; якщо потрібно, забезпечте, щоб спеціальне імʼя резолвилось (або додайте явну хост‑запис через extra_hosts як останній варіант).

7) Симптом: все ламається лише в одній Wi‑Fi мережі

  • Корінь: Та мережа ізолює клієнтів (AP isolation) або блокує вхідні з’єднання між пристроями.
  • Виправлення: Використовуйте нормальну мережу (або провідну), або запускайте сервіс за зворотним тунелем; не припускайте, що «та сама Wi‑Fi» означає «взаємна досяжність».

Три корпоративні міні‑історії (реалістично, анонімізовано, болісно)

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

Команда продукту збудувала демо‑оточення на ноутбуках для onsite‑воркшопу. План був простий: запустити кілька сервісів у Docker Desktop, опублікувати порти
і дозволити учасникам підключатися через готельний Wi‑Fi. Усі не раз робили «-p 8080:8080».
Неправильне припущення: Docker Desktop поводиться як Linux‑хост в плоскій LAN.

Вранці половина учасників не могла підключитися. Сервіси були запущені. Локальний curl працював. Ведучі іноді могли дістатися одне одного.
Люди почали перезавантажуватися, ніби 1998 рік. Проблема не в Docker; це був готельний Wi‑Fi з клієнтською ізоляцією — пристрої могли виходити в інтернет, але не бачити одне одного.

Друге неправильне припущення з’явилося миттєво: «Давайте використовувати IP контейнера і уникнути порт‑мапінгу.»
Вони роздали 172.17.x.x адреси, які були видимі всередині Docker VM — але звісно не були досяжні з інших ноутбуків.
Це призвело до десяти хвилин впевненого нісеніття і однієї сильно шкодуючої діаграми на дошці.

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

Міні‑історія 2: Оптимізація, що відстріляла собі в ногу

Платформена команда хотіла швидше виконувати CI на машинах розробників. Вони помітили багато DNS‑запитів під час збірок і вирішили «оптимізувати», примусивши контейнери використовувати публічний DNS‑резолвер.
На кавовому‑шопі це виглядало чудово: швидші резолви, менше таймаутів, гарні графіки.

Потім інженер спробував зібрати проект під VPN. Внутрішні реєстри пакетів доступні тільки через корпоративний DNS і внутрішні маршрути.
Раптом збірки почали падати з «host not found», хоча ОС‑хост резолвив правильно. Робочим обходом стало «відключити VPN» — чудовий шлях створити наступний інцидент.

Ситуація ускладнилася тим, що деякі внутрішні імена резолвилися публічно в заглушкові IP (з міркувань безпеки), тому «DNS‑успіх» приводив до чорної діри.
Налагодження було жорстким: бачиш записи A, додаток таймаутить, і всі у випадковому порядку звинувачують TLS, проксі та Docker.

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

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

Сервіс з високими вимогами до безпеки використовував Docker Desktop для локального інтеграційного тестування. Потрібно було викликати внутрішнє API і одночасно приймати вебхуки від тестового стенду на іншій машині в офісі.
Команда мала звичку, яку я поважаю: перед змінами вони знімали «known‑good» базовий стан — маршрути, конфігурацію DNS, мапінги портів.

Одного понеділка все зламалося після оновлення ОС. Контейнери не могли резолвити внутрішні імена. Вебхуки перестали доходити.
Замість гадань вони порівняли поточний стан з базовим: опубліковані порти тепер були привʼязані лише до localhost, а stub‑DNS всередині контейнерів вказував на новий VM‑IP, який не форвардив split DNS.

Виправили привʼязку портів у Compose, потім зафіксували DNS контейнерів на внутрішні резолвери при роботі з VPN.
Оскільки у них був baseline, вони могли показати команді endpoint‑безпеки саме те, що змінилося і чому. Інцидент не перетворився на тижневу битву звинувачень.

Така практика — зафіксувати базовий стан і робити diff при збоях — нудна, але дієва.

Контрольні списки / покроковий план (спеціально нудно)

Checklist 1: Надійно відкрити сервіс Docker Desktop у LAN

  1. Переконайтеся, що додаток слухає на 0.0.0.0 всередині контейнера (ss -lntp).
  2. Опублікуйте порт на всіх інтерфейсах: -p 0.0.0.0:8080:80 (або Compose "8080:80").
  3. Підтвердіть, що Docker бачить мапінг: docker port CONTAINER.
  4. Переконайтеся, що ОС‑хоста слухає на цьому порту: ss -lntp | grep :8080.
  5. Тест локально: curl http://127.0.0.1:8080.
  6. Тест з LAN‑peer: nc -vz HOST_LAN_IP 8080.
  7. Якщо LAN‑тест провалюється — запустіть не‑Docker слухач (python3 -m http.server), щоб відокремити брандмауер/VPN від проблем Docker.

Checklist 2: Зробити контейнерам доступ до внутрішніх ресурсів LAN (NAS, внутрішні API)

  1. З ОС‑хоста перевірте, що ціль досяжна по IP.
  2. Зсередини контейнера протестуйте IP‑зʼєднання (nc -vz або curl).
  3. Якщо IP не працює лише при VPN — перевірте перекриття маршрутів (ip route) і політику VPN («block local LAN»).
  4. Якщо IP працює, але імʼя ні — перевірте /etc/resolv.conf і резольв через getent hosts.
  5. За потреби перевизначте DNS на проєкт через Compose dns:.
  6. Уникайте перекриття підмереж: перемістіть Docker‑мережі в діапазон, який не маршрутизує ваш VPN.

Checklist 3: Стабілізувати DNS для dev‑зборок (коли pip/npm/apt флапає)

  1. Виміряйте час резолву всередині контейнера з time getent hosts.
  2. Перевірте поточні резолвери в /etc/resolv.conf.
  3. Якщо на VPN — віддавайте перевагу резолверам, що надає VPN (і додайте публічний fallback, якщо дозволено).
  4. Не хардкодьте публічний DNS глобально для всіх проєктів; ви зламаєте split DNS робочі процеси.
  5. Перепроводьте тест всередині контейнера після змін; не довіряйте лише результатам ОС‑хоста.

FAQ

1) Чому я не можу просто використати IP контейнера з іншої машини в LAN?

Тому що в Docker Desktop цей IP знаходиться на внутрішньому мосту всередині Linux‑VM (або WSL2 середовища). Ваш LAN до нього не маршрутизований. Публікуйте порти замість цього.

2) Чому -p 8080:80 працює локально, але не з мого телефону?

Зазвичай або порт привʼязано тільки до localhost (явно або через Compose), або брандмауер хоста/VPN блокує вхідні з LAN.

3) У чому різниця між 127.0.0.1 і 0.0.0.0 в цьому контексті?

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

4) Чи вирішить проблему опція --network host у Docker Desktop?

Ні. На Docker Desktop «host network» не те саме, що Linux host networking, і часто не дає бажаного результату. Краще використовувати bridge + публікацію портів.

5) Чому DNS працює на хості, але не всередині контейнерів?

Контейнер може використовувати інший шлях резолвінгу (stub всередині VM), і він може не успадковувати split DNS настройки VPN. Перевірте cat /etc/resolv.conf і getent hosts всередині контейнера, потім перевизначте DNS на проєкт якщо потрібно.

6) Чи варто встановити DNS Docker Desktop на публічний резолвер, щоб «виправити все»?

Лише якщо вам ніколи не потрібен внутрішній DNS. Публічні резолвери можуть зламати корпоративні домени, внутрішні реєстри та split‑horizon сценарії. Використовуйте DNS на проєкт або умовну поведінку залежно від стану VPN.

7) Мій контейнер не може дістатися пристрою LAN лише коли підключений VPN. Це вина Docker?

Майже завжди — ні. Клієнти VPN можуть маршрутизувати приватні підмережі через тунель або блокувати локальний LAN. Доведіть це, протестувавши те саме з ОС‑хоста і відключивши VPN як контроль.

8) Який найнадійніший спосіб, щоб контейнер викликав сервіс на моєму ноуті?

Використовуйте host.docker.internal і тримайте це як стандарт між середовищами. Уникайте хардкодних IP, що гуляють з Wi‑Fi мережами.

9) Як дізнатися, чи проблема в брандмауері чи в мапінгу порту Docker?

Запустіть не‑Docker слухач на хості (наприклад python3 -m http.server). Якщо LAN не дістається і до нього — проблема не в Docker.

10) Добре правило для здорового глузду мереж Docker Desktop?

Думайте про Docker Desktop як про «контейнери за VM за вашою ОС». Публікуйте порти, уникайте перекриття підмереж і перевіряйте DNS всередині контейнера.

Висновок: що зробити сьогодні

Мережі Docker Desktop перестають бути дивними, коли ви припините очікувати від них поведінки Linux‑хоста. Це межа VM з шаром форвардингу.
Як тільки ви це приймете, більшість проблем зводиться до трьох груп: адреси привʼязки, політики брандмауера/VPN і дрейф DNS/резолверів.

Практичні наступні кроки:

  1. Виберіть один тестовий сервіс, опублікуйте його на 0.0.0.0 і перевірте доступність з LAN end‑to‑end за допомогою ss, curl і nc.
  2. Зніміть базовий стан, коли все працює: docker port, /etc/resolv.conf контейнера і таблицю маршрутів хоста.
  3. Якщо використовуєте VPN, не допускайте, щоб Docker‑мережі перекривалися з корпоративними маршрутами. Стандартизуйте «безпечний» діапазон для dev‑мереж.
  4. Налаштовуйте DNS на проєкт, коли важливі внутрішні імена. Глобальні «фікси» — шлях до міжкомандних зламів.

Парафраз вислову Вернера Вогельса (CTO Amazon): «Усе ламається; проєктуйте системи — і операції — так, щоб вони вбирали ці відмови.»
Мережі Docker Desktop не якісь особливі. Це просто відмови з додатковими шарами.

Ubuntu 24.04: «Failed to get D-Bus connection» — виправте зламані сесії й служби (випадок №48)

Ви запускаєте systemctl, і воно викидає: «Failed to get D-Bus connection». Раптом ваше «просте перезапускання» перетворюється на місце події: служби не спілкуються, входи виглядають привидами, і будь-яка автоматизація, що очікує чистої сесії, починає падати.

Ця помилка рідко означає «просто D-Bus». Зазвичай це зламаний контракт між systemd, вашою сесією/логіном та сокетами шини під /run. Рішення банальне — але тільки коли ви припините гадати й почнете доводити.

Що насправді означає ця помилка (і чого вона не означає)

Коли інструмент каже «Failed to get D-Bus connection», він скаржиться, що не може дістатися до сокета шини повідомлень, який очікує знайти. На Ubuntu 24.04 типовими ініціаторами є systemctl, loginctl, компоненти GNOME, підказки policykit, помічники snapd або будь-який процес, що очікує одне з наступного:

  • Системна шина за адресою /run/dbus/system_bus_socket (використовується для системних служб), або
  • Сесійна шина користувача (на користувача) зазвичай у /run/user/UID/bus, якою керує systemd --user та dbus-daemon або dbus-broker залежно від конфігурації.

Фраза вводить в оману, бо корінь проблеми часто не в тому, що «D-Bus впав». Шина може бути в порядку; неправильно налаштоване середовище, відсутня runtime-папка, ви можете бути в контейнері/просторі імен або використовувати sudo так, що змінні шини відрізані.

Дві правила, які тримають вас у розумі:

  1. Визначте, чи потрібна вам системна шина чи шина користувача. Якщо ви керуєте службами через systemctl (системна область), вам важливі PID 1, dbus і системний сокет. Якщо ви виконуєте дії робочого столу/сесії, важливі systemd --user, XDG_RUNTIME_DIR і перукористувацький сокет.
  2. Завжди перевіряйте сокет, а не свої відчуття. Більшість відмов підписки «D-Bus connection» фактично викликані відсутніми шляхами в /run, померлими сесіями користувача або зламаним диспетчером вхідних сесій.

Одна перефразована ідея від Gene Kim (автор з DevOps/надійності): Покращення приходить від зменшення роботи в процесі і роблення проблем видимими якомога раніше. Це працює й тут: зробіть відмову видимою, перевіривши шляхи шини та стан сесії перш ніж перезапускати випадкові демони.

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

Коли це трапляється в продакшені о 02:00, вам не потрібна теорія. Потрібен цикл тріажу, який сходиться.

Крок 1: Визначте, яка шина відмовляє

  • Якщо помилка з’являється під час виконання systemctl status foo від root — ймовірно це системна шина або зв’язок із PID 1.
  • Якщо помилка з’являється в десктопному додатку, налаштуваннях GNOME або systemctl --user — це шина сесії користувача (/run/user/UID/bus).
  • Якщо це трапляється лише через SSH або автоматизацію — підозрюйте змінні середовища і non-login shell-си.

Крок 2: Перевірте сокети і runtime-папки (найшвидший сигнал)

  • Чи існує і є сокетом /run/dbus/system_bus_socket?
  • Чи існує /run/user/UID і чи належить вона користувачу?
  • Чи існує і є сокетом /run/user/UID/bus?

Крок 3: Перевірте менеджер сесій і стан systemd

  • systemctl is-system-running покаже, чи PID 1 здоровий.
  • systemctl status dbus покаже, чи сервіс системної шини існує/запущений.
  • loginctl list-sessions покаже, чи бачить logind вашу сесію (критично для створення /run/user/UID).

Крок 4: Виправляйте правильний шар, а не найголосніший

  • Відсутня /run/user/UID? Виправляйте життєвий цикл logind/сесій.
  • Сокет існує, але доступ заборонено? Виправте права, політики SELinux/AppArmor або контекст користувача.
  • Працює локально, але не зі sudo? Налагодьте збереження оточення; не «перезапускайте dbus» із зла.

Цікавинки та контекст (допоможуть швидше дебажити)

  • D-Bus був створений на початку 2000-х, щоб замінити ad-hoc IPC механізми в Linux-десктопах; згодом він став стандартом і для системних служб.
  • systemd не створив D-Bus, але systemd зробив залежності D-Bus явнішими через порядок запуску юнітів, socket activation і користувацькі сервіси.
  • Runtime-папки користувачів у /run/user/UID зазвичай створює systemd-logind при старті сесії — і видаляє, коли остання сесія завершується.
  • Ubuntu постачав як dbus-daemon, так і альтернативи (наприклад dbus-broker); важливий контракт сокета, а не бренд реалізації.
  • XDG_RUNTIME_DIR — частина специфікації XDG; має бути персональною для користувача, безпечною та тимчасовою — не випадковою папкою в /tmp.
  • systemctl спілкується з systemd через D-Bus; якщо systemctl не дістається шини, воно не може нічого запитати в systemd навіть якщо systemd технічно живий.
  • SSH-сесії не завжди є «сесіями logind» залежно від PAM-конфігурації; якщо ні, ви можете втратити автоматичне створення runtime-папки і доступність шини користувача.
  • У контейнерах часто немає повної системної шини, бо PID 1 не systemd або /run ізольовано. Ця помилка там нормальна, якщо ви не підключили її навмисно.
  • PolicyKit (polkit) залежить від D-Bus для запитів авторизації; зламаний доступ до шини може виглядати як «вікна авторизації не з’являються» або «доступ заборонено» без UI.

Жарт №1: D-Bus як офісна пошта — коли вона паде, всі раптом дізнаються, скільки речей вони ніколи не розуміли, що на неї покладались.

Польовий посібник: ізолюйте, до якої «шини» неможливо підключитися

Існує кілька типових форм відмов:

  • Root на сервері: systemctl не працює. Зазвичай відсутній системний сокет, юніт dbus впав, або PID 1 у деградованому/напівмертвому стані.
  • Сесія десктопного користувача: не працюють налаштування GNOME, ламається gsettings, systemctl --user відмовляє. Зазвичай XDG_RUNTIME_DIR не встановлено, /run/user/UID відсутня, або systemd --user не працює.
  • Автоматизація через sudo: працює як ваш користувач, не працює як root, або навпаки. Зазвичай змінні середовища і контекст сесії неправильні.
  • Всередині контейнерів/CI: systemctl і busctl відмовляють за дизайном, бо немає systemd PID 1 або системної шини.

Ключове: шина — це Unix-сокетний файл. Якщо сокета немає, не допоможе “більше ретраїв”. Якщо він є, але ваш процес не може до нього дістатися — це права, простори імен або проблеми ідентичності. Якщо він є і доступний, але відповіді відсутні — це вже проблема демона.

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

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

Task 1: Confirm the exact failing command and context

cr0x@server:~$ whoami
cr0x
cr0x@server:~$ systemctl status ssh
Failed to get D-Bus connection: No such file or directory

Значення: Клієнт не може дістатися свого сокета шини. «No such file or directory» натякає на відсутній шлях сокета, а не на проблему з правами.

Рішення: Визначте, чи це відмова системної шини (root/система) або шини користувача (користувацька область). Далі: перевірте, чи ви root і який саме systemctl запущено.

Task 2: Check whether PID 1 is systemd (containers and chroots)

cr0x@server:~$ ps -p 1 -o pid,comm,args
  PID COMMAND         COMMAND
    1 systemd         /sbin/init

Значення: PID 1 — systemd; systemctl має працювати, якщо шлях системної шини присутній.

Рішення: Якщо PID 1 не systemd (поширено в контейнерах), «вирішення» — уникати systemctl або запустити нормальний init. Якщо systemd — продовжуйте діагностику.

Task 3: Verify the system bus socket exists

cr0x@server:~$ ls -l /run/dbus/system_bus_socket
srwxrwxrwx 1 root root 0 Dec 30 10:12 /run/dbus/system_bus_socket

Значення: Файл сокета системної шини існує і є сокетом (позначка s на початку прав). Світовий запис тут нормальний для кінцевої точки сокета; доступ все одно контролюється політикою D-Bus.

Рішення: Якщо відсутній: зосередьтеся на сервісі dbus та ранніх етапах завантаження. Якщо присутній: перевірте, чи dbus відповідає.

Task 4: Check dbus service health (system bus)

cr0x@server:~$ systemctl status dbus --no-pager
● dbus.service - D-Bus System Message Bus
     Loaded: loaded (/usr/lib/systemd/system/dbus.service; static)
     Active: active (running) since Mon 2025-12-30 10:12:01 UTC; 2min ago
TriggeredBy: ● dbus.socket
       Docs: man:dbus-daemon(1)
   Main PID: 842 (dbus-daemon)
      Tasks: 1 (limit: 18939)
     Memory: 3.8M
        CPU: 52ms

Значення: Системна шина запущена; проблема може бути у здатності systemctl підключитися до systemd (не в dbus), або у просторі імен/правах.

Рішення: Якщо dbus неактивний/упав — перезапустіть його й читайте логи. Якщо активний — перевірте сам systemd і приватний сокет systemd.

Task 5: Confirm systemd is responsive

cr0x@server:~$ systemctl is-system-running
running

Значення: PID 1 звітує про нормальний стан. Якщо ви все ще бачите «Failed to get D-Bus connection», можливо ви запускаєте systemctl в середовищі, яке не бачить /run або не має правильного mount namespace.

Рішення: Якщо вивід degraded або maintenance, йдіть у journal за системними помилками. Якщо running, але клієнти падають — підозрюйте namespace, chroot або файлові проблеми під /run.

Task 6: Inspect /run mount and free space (yes, really)

cr0x@server:~$ findmnt /run
TARGET SOURCE FSTYPE OPTIONS
/run   tmpfs  tmpfs  rw,nosuid,nodev,relatime,size=394680k,mode=755,inode64
cr0x@server:~$ df -h /run
Filesystem      Size  Used Avail Use% Mounted on
tmpfs           386M  2.1M  384M   1% /run

Значення: /run — tmpfs; має бути записуваною і мати простір/іноди. Якщо /run доступна лише для читання або заповнена — сокети не створяться і ви отримаєте помилки про відсутню шину.

Рішення: Якщо повна/ro: виправте це першочергово (часто через процес, що витрачає простір або неправильне розмірення tmpfs). Якщо в порядку — переходьте до перевірки сесій користувача, якщо помилка в області користувача.

Task 7: Determine if you’re dealing with the user bus

cr0x@server:~$ echo "$XDG_RUNTIME_DIR"
/run/user/1000
cr0x@server:~$ echo "$DBUS_SESSION_BUS_ADDRESS"
unix:path=/run/user/1000/bus

Значення: Змінні середовища вказують на перукористувацьку шину. Якщо одна з них пуста, ваша сесія неповна (поширено при sudo, cron або зламаному PAM).

Рішення: Якщо не встановлені: потрібно встановити правильний контекст сесії або явне налаштування користувацької шини (краще перше). Якщо встановлено: перевірте наявність сокета.

Task 8: Validate the user bus socket exists and has sane ownership

cr0x@server:~$ id -u
1000
cr0x@server:~$ ls -ld /run/user/1000
drwx------ 12 cr0x cr0x 320 Dec 30 10:12 /run/user/1000
cr0x@server:~$ ls -l /run/user/1000/bus
srw-rw-rw- 1 cr0x cr0x 0 Dec 30 10:12 /run/user/1000/bus

Значення: Runtime-папка існує, приватна (0700), і сокет шини існує. Добре. Якщо /run/user/1000 відсутня — сесія не була належним чином зареєстрована в logind.

Рішення: Якщо відсутня: переходьте до loginctl і налагодження PAM/logind. Якщо присутня, але з неправильним власником: виправте права і розберіться, чому вони зсунулися (часто через скрипт, що запускався від root).

Task 9: Prove the user systemd instance is alive

cr0x@server:~$ systemctl --user status --no-pager
● cr0x@server
    State: running
    Units: 221 loaded (incl. snap units)
     Jobs: 0 queued
   Failed: 0 units
    Since: Mon 2025-12-30 10:12:05 UTC; 2min ago
  

Значення: Ваш користувацький менеджер працює і доступний. Якщо ви отримуєте «Failed to connect to bus», контекст шини або середовище користувача зламані.

Рішення: Якщо це відмовляє, але сокет існує — можливо ваше середовище вводить в оману (неправильний XDG_RUNTIME_DIR) або ви в іншому просторі імен (поширено при sudo і деяких інструментах віддаленого доступу).

Task 10: Use loginctl to verify logind sees your session

cr0x@server:~$ loginctl list-sessions
SESSION  UID USER SEAT  TTY
     21 1000 cr0x seat0 tty2

1 sessions listed.
cr0x@server:~$ loginctl show-user cr0x -p RuntimePath -p State -p Linger
RuntimePath=/run/user/1000
State=active
Linger=no

Значення: logind має активну сесію для користувача і знає, де runtime-шлях. Якщо сесій немає, runtime-папка може не створюватися.

Рішення: Якщо сесія відсутня через SSH: перевірте PAM-конфігурацію і чи використовує ваш шлях входу systemd/logind. Якщо потрібні фонові сервіси користувача — розгляньте lingering (обережно).

Task 11: Diagnose “sudo broke my bus” (classic)

cr0x@server:~$ sudo -i
root@server:~# echo "$DBUS_SESSION_BUS_ADDRESS"

root@server:~# systemctl --user status
Failed to connect to bus: No medium found

Значення: У shell-а root немає контексту користувацької шини; systemctl --user під root — не ваша користувацька сесія. Ця помилка очікувана.

Рішення: Не «виправляйте» це експортом випадкових змінних у root. Використовуйте systemctl (системна область) як root, а systemctl --user як користувач у сесії. Якщо потрібно керувати юнітом користувача з root, використовуйте machinectl shell або runuser з правильними змінними середовища, або включіть lingering через loginctl enable-linger і керуйте systemctl --user під тим користувачем.

Task 12: Check journal for the first failure, not the last complaint

cr0x@server:~$ journalctl -b -u systemd-logind --no-pager | tail -n 20
Dec 30 10:11:58 server systemd-logind[701]: New session 21 of user cr0x.
Dec 30 10:11:58 server systemd-logind[701]: Watching system buttons on /dev/input/event3 (Power Button)
Dec 30 10:12:01 server systemd-logind[701]: Removed session 19.

Значення: logind створює сесії. Якщо ви бачите повторювані помилки про створення runtime-папок — це ваша головна підказка.

Рішення: Якщо logind показує помилки про runtime-папки або cgroups — виправляйте ці шари. Перезапуск dbus не виправить «не можу створити /run/user/UID».

Task 13: Confirm dbus packages and user-session support are installed

cr0x@server:~$ dpkg -l | egrep 'dbus|dbus-user-session|libpam-systemd' | awk '{print $1,$2,$3}'
ii dbus 1.14.10-4ubuntu4.1
ii dbus-user-session 1.14.10-4ubuntu4.1
ii libpam-systemd 255.4-1ubuntu8

Значення: Необхідні компоненти присутні. Відсутність dbus-user-session може призвести до відсутності поведінки сесійної шини в деяких конфігураціях (особливо мінімальні інсталяції).

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

Task 14: Check PAM session hooks for systemd/logind (SSH-focused)

cr0x@server:~$ grep -R "pam_systemd.so" -n /etc/pam.d/sshd /etc/pam.d/login
/etc/pam.d/sshd:15:session    required     pam_systemd.so
/etc/pam.d/login:14:session    required     pam_systemd.so

Значення: PAM налаштовано на реєстрацію сесій в systemd/logind для SSH і консолі. Якщо відсутнє — ви можете залишитися без runtime-папки і без шини користувача.

Рішення: Якщо відсутнє для шляху входу, яким ви користуєтесь — додайте його (обережно, під контролем змін) і протестуйте нову сесію. Якщо присутнє — зосередьтеся, чому logind досі не створює runtime-папки (часто пов’язано з lingering, проблемами cgroup або зламаним state systemd).

Task 15: Check if the user runtime dir is being removed unexpectedly

cr0x@server:~$ sudo ls -l /run/user
total 0
drwx------ 12 cr0x cr0x 320 Dec 30 10:12 1000
drwx------ 10 gdm  gdm  280 Dec 30 10:11 120

Значення: Runtime-папки існують для активних користувачів. Якщо ваша зникає при відключенні SSH — ймовірно у вас немає lingering і немає активної сесії.

Рішення: Для фонового запуску сервісів користувача — розгляньте loginctl enable-linger username. Для інтерактивної роботи — забезпечте реальну сесію і уникайте запуску сесійозалежних команд з не-сесійного контексту.

Task 16: Enable lingering (only if you truly need user services without a login)

cr0x@server:~$ sudo loginctl enable-linger cr0x
cr0x@server:~$ loginctl show-user cr0x -p Linger
Linger=yes

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

Рішення: Використовуйте це для безголових сервісів у користувацькій області (іноді CI-агенти, per-user podman тощо). Не вмикайте це скрізь «про всяк випадок». Інакше ви отримаєте зомбі-менеджери користувачів, що з’їдають пам’ять на спільних хостах.

Task 17: If systemctl fails as root, test D-Bus directly

cr0x@server:~$ busctl --system list | head
NAME                      PID PROCESS         USER CONNECTION UNIT SESSION DESCRIPTION
:1.0                      842 dbus-daemon     root :1.0       -    -       -
org.freedesktop.DBus      842 dbus-daemon     root :1.0       -    -       -
org.freedesktop.login1    701 systemd-logind  root :1.2       -    -       -

Значення: Системна шина відповідає. Якщо systemctl все ще дає помилку, можливо є зламаний endpoint systemd D-Bus або невідповідність у середовищі/просторі імен.

Рішення: Якщо busctl теж відмовляє — системна шина дійсно зламана. Якщо busctl працює — зосередьтесь на зв’язку systemd і клієнтському середовищі.

Task 18: Check the systemd private socket (systemd’s IPC endpoint)

cr0x@server:~$ ls -l /run/systemd/private
srw------- 1 root root 0 Dec 30 10:11 /run/systemd/private

Значення: Приватний сокет systemd існує; systemctl використовує його в деяких шляхах коду. Якщо відсутній — щось глибоко неправильно з PID 1 або /run.

Рішення: Якщо відсутній: розглядайте це як проблему systemd/файлової системи runtime; подумайте про контрольований перезавантаження після вилучення логів. Якщо присутній — повертайтеся до області (system vs user) і проблем з просторами імен.

Task 19: Spot chroot/namespace issues (common in recovery shells)

cr0x@server:~$ readlink /proc/$$/ns/mnt
mnt:[4026532585]
cr0x@server:~$ sudo readlink /proc/1/ns/mnt
mnt:[4026531840]

Значення: Ваш shell у іншому mount namespace, ніж PID 1. Ви можете не бачити справжній /run, де живуть сокети.

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

Task 20: Last resort, controlled restarts (in the right order)

cr0x@server:~$ sudo systemctl restart systemd-logind
cr0x@server:~$ sudo systemctl restart dbus
cr0x@server:~$ sudo systemctl daemon-reexec

Значення: Ці перезапуски можуть відновити завислий logind/dbus/systemd. daemon-reexec важка операція; вона переекзекує PID 1 без перезавантаження.

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

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

1) «systemctl працює локально як root, але падає по SSH»

Симптом: Через SSH systemctl повертає «Failed to get D-Bus connection», але на консолі працює.

Причина: Ви в обмеженому середовищі (forced command, chroot, toolbox), або ваша SSH-сесія не бачить /run хоста (різниця в mount namespace).

Виправлення: Підтвердіть PID 1 і mount namespace; переконайтеся, що ваш SSH-путь не chroot-ований і має доступ до /run. Використайте Task 2 і Task 19.

2) «systemctl –user падає після sudo -i»

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

Причина: Root не має контексту вашої користувацької шини. Крім того, у root-у свій користувацький менеджер, не ваш.

Виправлення: Запускайте systemctl --user як користувач у сесії. Якщо потрібно з root, використовуйте runuser -l username -c 'systemctl --user …' і переконайтесь, що сесія існує (або ввімкніть lingering).

3) «GNOME Settings не відкриваються; підказки polkit не з’являються»

Симптом: GUI-дії безшумно відмовляють або скаржаться на D-Bus.

Причина: Сесійна шина користувача зламана: відсутній XDG_RUNTIME_DIR, застаріла DBUS_SESSION_BUS_ADDRESS або відсутній /run/user/UID/bus.

Виправлення: Перевірте Task 7/8. Вийдіть і зайдіть знову, щоб відновити чисту сесію. Якщо проблема не зникає — перевірте logind і інтеграцію PAM.

4) «Cron job падає з помилками D-Bus»

Симптом: Скрипт, що використовує gsettings, notify-send або systemctl --user, падає в cron.

Причина: Cron працює без користувацької сесії і без XDG_RUNTIME_DIR.

Виправлення: Не запускайте десктопні/сесійні команди в cron, якщо не створили контекст сесії. Використовуйте системні сервіси або ввімкніть lingering і запускайте користувацький сервіс, що не залежить від GUI.

5) «/run/user/UID існує, але належить root»

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

Причина: Хтось запускав очистку як root і створив папки неправильно, або скрипт невірно писав у /run/user.

Виправлення: Вийдіть користувачу (завершіть сесії), видаліть неправильну runtime-папку і дайте logind її відтворити. Якщо потрібно виправити на живому хості, коректно змініть власника і перезапустіть користувацький менеджер обережно.

6) «системний сокет відсутній після завантаження»

Симптом: /run/dbus/system_bus_socket відсутній; systemctl широко відмовляє.

Причина: dbus.socket або dbus.service не стартували, або /run змонтовано некоректно.

Виправлення: Перевірте mount /run (Task 6), потім systemctl status dbus dbus.socket і ранні логи завантаження.

7) «Працює на хості, але падає в контейнері»

Симптом: systemctl і busctl відмовляють у контейнері або CI runner-і.

Причина: Немає systemd PID 1, немає системної шини або /run ізольовано.

Виправлення: Не використовуйте systemctl у такому контейнері. Запускайте сервіс в foreground або створіть спеціальний контейнер із підтримкою systemd і потрібними привілеями/маунтами.

Три корпоративні міні-історії з практики

Міні-історія №1: Інцидент через хибне припущення

У середній компанії інженера викликали через «хост деплою не перезапускає служби». Він зайшов по SSH, виконав sudo systemctl restart app і отримав «Failed to get D-Bus connection». Припущення було негайним і впевненим: «dbus впав; перезапущу його».

Він перезапустив dbus, потім logind, потім зробив daemon-reexec. Хост стало важче доступним, кілька інтерактивних сесій відвалилися. Додаток усе ще не перезапускався. Інцидент розрісся.

Насправді проблема була прозаїчною: інженер був не на хості, а в chroot для обслуговування дисків. Це середовище мало інший mount namespace і інший /run. Звісно /run/dbus/system_bus_socket там не було; сокет жив у namespace хоста.

Коли він вийшов із chroot і виконав ту саму команду на справжньому хості, systemctl одразу запрацював. «Аварія D-Bus» виявилась маячнею через контекст. Виправлення — додати чіткий банер для rescue-середовищ і навчити команду запускати Task 2 і Task 19 перед втручанням у демони.

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

Ще одна команда хотіла пришвидшити час логіну і зменшити кількість фон-процесів на робочих станціях розробників. Хтось вирішив «спростити» образ, прибравши пакети, що здавалися «десктопним мотлохом».

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

Вони видалили компоненти, що опосередковано гарантували стабільну користувацьку шину. Системна шина була, але інфраструктура на користувача була непослідовною залежно від способу входу. Деякі логіни створювали /run/user/UID правильно; інші — ні, бо PAM-hooks були неповними і пакети для сесій відсутні.

Оптимізація не «була неправильною», бо зекономила CPU. Вона була неправильною, бо прибрала каркас, що робить користувацьку шину передбачуваною. Відкат відновив пакети і стандартизував шляхи логіну. Час входу трохи зріс, але кількість інцидентів різко впала. Іноді «швидко» — це просто «крихке із гарним маркетингом».

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

У регульованому середовищі команда мала Ubuntu-сервери, які іноді потребували екстреної консолі. Була політика, що здавалася старомодною: кожна реакція на інцидент починається зі зйомки стану, включно з уривками journalctl -b і знімком шляхів сокетів у /run, перед будь-яким перезапуском.

Це здавалося бюрократією, поки на продакшн-хості не почалися помилки D-Bus після оновлення ядра. On-call дотримався політики. Вони зняли findmnt /run, перевірили вільне місце, підтвердили наявність /run/systemd/private і помітили, що /run/dbus/system_bus_socket відсутній. Також зняли ранні логи завантаження з попередженнями щодо tmpfs.

Завдяки наявності доказів вони не слідували паніці. З’ясувалося, що /run змонтовано лише для читання через тонку помилку initramfs/mount. Після виправлення і контрольованого перезавантаження сокет з’явився, systemctl відновився і інцидент закінчився чисто.

Нудна практика не лише виправила машину; вона зберегла наратив. У корпоративних середовищах наратив — половина відновлення: потрібно пояснити, що трапилось, без звинувачень у космічних променях.

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

Чек-лист A: Ви бачите «Failed to get D-Bus connection» при запуску systemctl (системна область)

  1. Підтвердіть, що ви на хості і PID 1 — systemd (Task 2).
  2. Перевірте mount /run та його місткість (Task 6).
  3. Переконайтесь, що існує /run/dbus/system_bus_socket (Task 3).
  4. Перевірте systemctl status dbus dbus.socket (Task 4).
  5. Перевірте приватний сокет systemd /run/systemd/private (Task 18).
  6. Перевірте відповіді шини через busctl --system list (Task 17).
  7. Зберіть логи: journalctl -b і релевантні юніти (Task 12).
  8. Якщо потрібно перезапустити — робіть це свідомо: logind → dbus → daemon-reexec (Task 20).

Чек-лист B: Ви бачите помилку при systemctl --user або в інструментах робочого столу (сесійна область)

  1. Перевірте XDG_RUNTIME_DIR і DBUS_SESSION_BUS_ADDRESS (Task 7).
  2. Переконайтесь, що /run/user/UID і /run/user/UID/bus існують і належать користувачу (Task 8).
  3. Перевірте systemctl --user status (Task 9).
  4. Використайте loginctl list-sessions і loginctl show-user (Task 10).
  5. Якщо це SSH/cron: вирішіть, чи потрібна вам реальна сесія або замість цього системна служба.
  6. Якщо потрібні фонові сервіси користувача — увімкніть lingering для цього користувача (Task 16), потім повторіть тест.
  7. Якщо runtime-папка постійно зникає — виправте життєвий цикл сесій і PAM (Task 14/15).

Чек-лист C: Ви в автоматизації/CI і все падає

  1. Підтвердіть, чи ви в контейнері і PID 1 не systemd (Task 2).
  2. Перестаньте намагатися використовувати systemctl у цьому середовищі. Запускайте сервіс напряму або переробіть задачу.
  3. Якщо вам дійсно потрібен systemd — запускайте середовище із підтримкою systemd навмисно, а не випадково.

Жарт №2: Перезапускати dbus без перевірки сокетів — як перезавантажувати принтер через нестачу паперу: катарсично, неефективно і дивно популярно.

FAQ

1) Чому systemctl взагалі використовує D-Bus?

systemctl — клієнт. Воно розмовляє з API менеджера systemd, зазвичай доступним через D-Bus і приватний сокет systemd. Немає шини — немає діалогу.

2) Я бачу dbus-daemon в процесах. Чому я все ще отримую помилку?

Бо наявність процесу — не те ж саме, що досяжність сокета в вашому просторі/контексті. Перевірте шляхи сокетів під /run і впевніться, що ви в mount namespace хоста (Task 3, 6, 19).

3) Що змінює «No such file or directory» проти «Permission denied»?

No such file зазвичай означає, що шлях сокета відсутній у вашому уявленні (відсутній mount /run, відсутня runtime-папка, питання namespace). Permission denied означає, що сокет існує, але контролі доступу вам перешкоджають (не той користувач, політика або confinement).

4) Чому це ламається лише через SSH?

Або ваша SSH-сесія не реєструється в logind (несправна PAM-конфігурація), або ви виконуєте команду в обгортці/chroot. Перевірте pam_systemd.so і чи створюється /run/user/UID для цієї сесії (Task 10, 14).

5) Чи безпечно увімкнути lingering?

Це безпечно, коли ви знаєте, навіщо воно потрібно: запускати користувацькі сервіси без активних логінів. Небезпечно як універсальний хід, бо ви будете тримати менеджери користувачів живими, що може приховувати баги виходу і витрачати ресурси. Вмикайте вибірково (Task 16).

6) Чи можна просто експортувати DBUS_SESSION_BUS_ADDRESS і рухатися далі?

Можна, але не варто. Експорт застарілих адрес створює «працює в моєму шеллі» привиди, що ламають пізніше. Краще встановити реальний контекст сесії і дати logind/systemd задати XDG_RUNTIME_DIR та адресу шини.

7) Який найшвидший спосіб відрізнити системну шину від сесійної?

Якщо ви використовуєте systemctl без --user, то це системна область. Якщо релевантний сокет — /run/dbus/system_bus_socket, це системна шина. Якщо /run/user/UID/bus — це шина сесії користувача.

8) Я на мінімальному сервері — чи потрібен мені dbus-user-session?

Якщо ви запускаєте сервіси в користувацькій області або очікуєте, що сесії користувачів матимуть повноцінну сесійну шину — так, часто потрібен. Якщо ви лише керуєте системними службами, іноді можна й обійтись. Симптомно: якщо бракує шини користувача — перевірте пакети (Task 13).

9) Чому systemctl --user падає під root, навіть коли користувач залогінений?

Бо середовище root — не середовище користувача, і root не «приєднаний» до тієї користувацької шини. Запускайте команду як користувач у сесії або використовуйте інструменти, що адресують той менеджер користувача.

10) Коли варто перезавантажитись замість дебагу?

Якщо PID 1 у нездоровому стані, /run пошкоджено/лише для читання, або systemd-сокети відсутні і ви не можете їх відновити чисто — контрольований перезапуск часто найнадійніший вихід. Обов’язково збережіть логи перед цим.

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

«Failed to get D-Bus connection» — не привід перезапускати випадкові служби. Це заклик перевірити контракт: /run змонтовано і записувано, потрібний сокет існує, ваша сесія реальна, і середовище вказує на правильну шину.

Зробіть наступне:

  1. Пройдiть швидкий план: сокети, runtime-папки, сесії logind. Не стрибайте одразу до перезапусків.
  2. Визначте, чи ваш робочий процес залежить від шини користувача. Якщо так — стандартизуйте шляхи входу (PAM + logind) і уникайте cron для сесійної роботи.
  3. Якщо це питання масштабу флоту — додайте легку перевірку здоров’я: перевіряйте наявність /run/dbus/system_bus_socket і /run/systemd/private, та аларміть при відсутності runtime-папок для активних сесій.
  4. Запишіть правило контексту: chroot/контейнери можуть і матимуть право на падіння systemctl. Ваші рукописи на випадок інцидентів мають це явно вказувати.

RAID — не резервна копія: фраза, яку люди усвідомлюють запізно

Дзвінок зазвичай надходить, коли панель керування зелена, а дані зникли. Масив «здоровий». База даних «працює».
І все ж фінансовий директор дивиться на пустий звіт, продуктова команда дивиться на порожнє сховище, а ви дивитесь на ту одну фразу,
яку хотіли б витатуювати в замовленні на купівлю: RAID — не резервна копія.

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

Що RAID насправді робить (і що він ніколи не обіцяв)

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

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

Доступність vs довговічність vs відновлюваність

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

  • Доступність: чи може система продовжувати працювати прямо зараз? Тут допомагає RAID.
  • Довговічність: чи залишаться біти правильними з часом? RAID іноді допомагає, іноді вводить в оману.
  • Відновлюваність: чи можете ви відновити відомо-правильний стан після інциденту? Це — резервні копії, знімки, реплікація та процеси.

RAID може продовжувати віддавати пошкоджені дані. RAID може точно віддзеркалити ваше випадкове видалення. RAID може з надзвичайним ентузіазмом
реплікувати блоки, зашифровані ransomware. RAID — вірний працівник. Вірний не означає розумний.

Що означає «резервна копія» в системі, яку можна захистити

Резервна копія — це окрема копія даних, яка:

  • Незалежна від первинної доменної зони відмови (інші диски, інший хост, бажано інший обліковий запис/повноваження).
  • Версійна, щоб ви могли повернутися до стану до події.
  • Відновлювана протягом прийнятного часу (RTO) та до контрольної точки часу (RPO), яку ви можете прийняти.
  • Протестована, бо «у нас є бекапи» — це не факт, поки ви не відновили з них.

Знімки та реплікація — чудові інструменти. Вони не стають автоматично резервними копіями. Вони перетворюються на резервні копії, коли є
незалежними, захищеними від тих самих адмін-помилок і ви можете відновити їх під тиском.

Жарт №1: RAID — це ремінь безпеки. Резервна копія — це швидка допомога. Якщо ви розраховуєте на ремінь безпеки, щоб зробити операцію, у вас буде довгий день.

Чому RAID не підходить як резервна копія: режими відмов, що мають значення

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

1) Видалення та перезапис миттєво реплікуються

Видаліть директорію. RAID віддзеркалить видалення. Перезапишіть таблицю. RAID розподілить цю нову правду через набір. Немає «відкоту», бо робота RAID —
робити копії консистентними, а не історичними.

2) Тиха корупція, bit rot і пастка «виглядає нормально»

Диски, контролери, кабелі та прошивки можуть повертати неправильні дані без помилки. Файлові системи з контрольними сумами (як ZFS, btrfs) можуть
виявляти корупцію і, з надмірністю, часто самовідновлюватися. Традиційний RAID під файловою системою без контрольних сум на рівні блоків
може охоче повертати пошкоджені блоки і вважати це успіхом.

Навіть з контрольними сумами «від кінця до кінця», ви все ще можете пошкодити дані на вищому рівні: неправильні записі додатку, баг у процесі компактування, напівзастосовані міграції.
RAID збережe корупцію бездоганно.

3) Ransomware не цікавиться вашою парністю

Ransomware шифрує те, до чого має доступ. Якщо він має доступ до змонтованої файлової системи, він може зашифрувати ваші дані на RAID1, RAID10, RAID6,
ZFS mirrors тощо. Надмірність не зупиняє шифрування. Вона просто робить шифрування доступним.

4) Відмови контролера та прошивки тягнуть за собою масив

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

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

5) Відновлення (rebuild) стресує систему і погіршується із збільшенням дисків

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

6) Людська помилка: найпоширеніший і найменш шанований режим відмови

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

7) Катастрофи на сайті та радіус ураження

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

Цікаві факти та трохи історії (корисні)

Кілька конкретних фактів допомагають засвоїти тему, бо вони показують, як RAID почали сприймати як чарівну паличку.
Ось дев’ять фактів — всі доречні, жоден не романтизований.

  1. RAID було названо й популяризовано в статті UC Berkeley 1987 року, яка представила «надмірні масиви недорогих дисків» як альтернативу великим дорогим дискам.
  2. Рання маркетингова кампанія RAID сильно наголошувала на «стійкості до відмов», і багато хто тихо переклав це як «захист даних», що не є тим самим контрактом.
  3. Рівні RAID ніколи не були єдиним офіційним стандартом. Вендори реалізовували «RAID5» з різною поведінкою та політиками кешу, а потім сперечалися про семантику в вашому вікні простою.
  4. Аппаратні RAID-контролери історично використовували пропрієтарні формати метаданих на диску, тому відмова контролера може перетворитися на археологію.
  5. Зростання багатотерабайтних дисків зробило відновлення RAID5 надзвичайно ризиковим, бо час відновлення зростав, а ймовірність зустріти непридатний сектор під час відновлення зростала.
  6. URE (unrecoverable read error) широко обговорювали в 2000-х роках як практичну причину віддавати перевагу подвійній парності для великих масивів, особливо під час великих відновлень.
  7. ZFS (вперше випущений у середині 2000-х) ввів контрольні суми від кінця до кінця в звичайну експлуатацію, і зробив термін «bit rot» придатним для керівництва, бо це нарешті можна було виявити.
  8. Знімки стали звичними в корпоративних сховищах у 1990-х роках, але часто зберігалися на тому ж масиві — швидкий відкат, але не відновлення після катастрофи.
  9. Ransomware змінив розмову про резервні копії з «стрічка проти диска» на «іммутабельність проти повноважень», бо зловмисники навчилися спочатку видаляти резервні копії.

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

Міні-історія 1: Інцидент, спричинений хибним припущенням

Середня SaaS-компанія запустила свій первинний кластер PostgreSQL на парі висококласних серверів з аппаратним RAID10. Презентація вендора звучала
заспокійливо: надмірні диски, кеш із батарейним живленням, гарячі резерви. Команда почула «немає втрати даних» і ментально віднесла бекапи до «приємних, але необов’язкових».

Одного дня розробник запустив скрипт очищення проти production. Він мав працювати зі staging-схемою; він потрапив у живу.
За секунди були видалені мільйони рядків. База продовжувала обслуговувати трафік, і графіки моніторингу виглядали нормально — запити навіть стали швидшими,
бо даних стало менше.

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

Їх не було. Було налаштовано нічне логічне дампування місяці тому, але воно писалося на той самий RAID-том, і скрипт очищення теж видалив каталог дампів.
Компанія відновлювалася з логів застосунку і сторонніх потоків подій. Відновили більшість, але не все, і витратили тижні на виправлення тонких пошкоджень посилань.

Хибне припущення було не в «RAID безпечний». Воно було в «доступність означає відновлюваність». Вони мали високий аптайм і низьку правдивість.

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

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

У нормальній роботі все виглядало чудово. Глибина черг була низькою. Латентність знизилась. Керівництво отримало слайд «ефективність зберігання» для квартального звіту.
Вони спокійно спали приблизно місяць.

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

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

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

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

Фінансова компанія керувала файловим сервісом для внутрішніх команд. Сховище було ZFS mirror: просте, консервативне, нецікаве.
Цікавою була їхня гігієна резервного копіювання: нічні знімки, реплікація поза сайтом в іншу адміністраторську домену та місячні тести відновлення.
Усі скаржилися на тести відновлення, бо «вони марнують час». Менеджер SRE зробив їх обов’язковими.

Ноутбук підрядника було скомпрометовано. Зловмисник отримав доступ до VPN, а потім привілейовані облікові дані для запису на мережеву шару.
Протягом ночі ransomware почав шифрувати користувацькі директорії. Оскільки шар був онлайн і записуваний, шифрування швидко поширилося.

ZFS зробив саме те, що від нього чекали: зберіг нові зашифровані блоки з цілісністю. RAID-дзеркала забезпечили, що шифрування стало довговічним.
Наступного ранку користувачі знайшли файли перейменованими та непридатними для читання. Дзеркало було «здоровим». Бізнес — ні.

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

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

Швидкий план діагностики: знайти вузьке місце та радіус ураження

Коли щось іде не так із сховищем, команди витрачають час на суперечки щодо того, чи це «диски», чи «мережа», чи «база даних».
Правильний підхід — встановити: (1) що змінилося, (2) що сповільнилося, (3) що небезпечно, і (4) чому ви ще довіряєте.

Перш за все: перестаньте робити гірше

  • Якщо підозрюєте корупцію або ransomware, заморозьте запис там, де можете: перемонтуйте в режим read-only, зупиніть сервіси, відкличте повноваження.
  • Якщо масив деградований і відновлюється, розгляньте варіант зменшення навантаження, щоб уникнути другої відмови під час відновлення.
  • Почніть журнал інциденту: виконані команди, часові мітки, зроблені зміни. Пам’ять — не доказ.

По-друге: визначте, чи це продуктивність, цілісність чи доступність

  • Продуктивність: висока латентність, таймаути, глибина черги, iowait. Дані можуть бути ще коректними.
  • Цілісність: помилки контрольних сум, корупція на рівні застосунку, несподівані зміни файлів. Продуктивність може виглядати нормальною.
  • Доступність: відсутні пристрої, масиви деградовані/відмовили, файлові системи не монтуються. Система кричить.

По-третє: швидко локалізуйте домен відмови

  1. Хост: логи ядра, помилки диска, стан контролера.
  2. Стек сховища: RAID/mdadm/ZFS, стан файлової системи, стан scrub.
  3. Шлях IO: multipath, HBA, SAS expander, NIC, комутатори якщо використовується мережеве сховище.
  4. Застосунок: плани запитів, конкуренція за блокування, шторм повторів.
  5. Положення щодо бекапів/відновлення: чи є у вас чиста точка відновлення і чи вона доступна?

По-четверте: визначте мету

В інциденті ви повинні обрати одну провідну мету:

  • Тримати сервіс працюючим (доступність): стабілізуватися, прийняти деградований режим.
  • Захистити дані (цілісність): заморозити записи, зробити форензічні копії, відновити з відомо-добрих копій.
  • Відновити сервіс (відновлюваність): переключитися на інший ресурс, відновити деінде, відновити з бекапів.

Ці цілі конфліктують. Вдавати, що цього немає, — як отримати працюючу систему, яка віддає неправильні дані.

Практичні завдання з командами: що виконати, що це означає, які рішення прийняти

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

Завдання 1: Переглянути блокові пристрої та членство в RAID

cr0x@server:~$ lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,MODEL,SERIAL
NAME    SIZE TYPE FSTYPE MOUNTPOINT MODEL            SERIAL
sda   3.6T disk       	        HGST_HUS726T4TAL  K8H1ABCD
├─sda1 512M part vfat   /boot/efi
└─sda2 3.6T part
sdb   3.6T disk       	        HGST_HUS726T4TAL  K8H1EFGH
└─sdb1 3.6T part
md0   3.6T raid1 ext4   /data

Що це означає: У вас є програмний RAID1-пристрій md0, змонтований в /data, побудований з розділів.

Рішення: Якщо ви думали, що маєте «резервні копії», то ні. Це лише надмірність. Підтвердіть, що резервні копії зберігаються окремо.

Завдання 2: Перевірити стан mdadm RAID та статус відновлення

cr0x@server:~$ cat /proc/mdstat
Personalities : [raid1]
md0 : active raid1 sdb1[1] sda2[0]
      3906886464 blocks super 1.2 [2/2] [UU]

unused devices: <none>

Що це означає: [UU] означає, що обидва члени в мережі. Під час відновлення ви б бачили [U_] і рядок прогресу.

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

Завдання 3: Отримати деталі mdadm, включно з лічильниками подій

cr0x@server:~$ sudo mdadm --detail /dev/md0
/dev/md0:
           Version : 1.2
     Creation Time : Mon Oct  2 11:22:09 2023
        Raid Level : raid1
        Array Size : 3906886464 (3726.02 GiB 4000.79 GB)
     Used Dev Size : 3906886464 (3726.02 GiB 4000.79 GB)
      Raid Devices : 2
     Total Devices : 2
       State : clean
Active Devices : 2
Working Devices : 2
Failed Devices : 0
 Spare Devices : 0
           Name : server:0
           UUID : 1a2b3c4d:5e6f:7890:abcd:ef0123456789
         Events : 12891

    Number   Major   Minor   RaidDevice State
       0       8        2        0      active sync   /dev/sda2
       1       8       17        1      active sync   /dev/sdb1

Що це означає: «clean» — добре, але це нічого не каже про те, чи файли коректні, послідовні або відновлювані.

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

Завдання 4: Переглянути логи ядра на предмет помилок IO та скидань

cr0x@server:~$ sudo journalctl -k -S "1 hour ago" | egrep -i "ata|sas|scsi|i/o error|reset|timeout" | tail -n 8
Jan 22 10:11:41 server kernel: ata3.00: exception Emask 0x0 SAct 0x0 SErr 0x0 action 0x6 frozen
Jan 22 10:11:41 server kernel: ata3.00: failed command: READ DMA EXT
Jan 22 10:11:41 server kernel: blk_update_request: I/O error, dev sdb, sector 9175040 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0
Jan 22 10:11:42 server kernel: ata3: hard resetting link
Jan 22 10:11:47 server kernel: ata3: link is slow to respond, please be patient
Jan 22 10:11:52 server kernel: ata3: SATA link up 6.0 Gbps (SStatus 133 SControl 300)

Що це означає: Скидання лінку та помилки IO — ранні сигнали попередження. Може бути диск, кабель, backplane або контролер.

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

Завдання 5: Запитати SMART-стан та ключові лічильники

cr0x@server:~$ sudo smartctl -a /dev/sdb | egrep -i "SMART overall|Reallocated_Sector_Ct|Current_Pending_Sector|Offline_Uncorrectable|Power_On_Hours"
SMART overall-health self-assessment test result: PASSED
  5 Reallocated_Sector_Ct   0x0033   100   100   010    Pre-fail  Always       -       12
197 Current_Pending_Sector  0x0012   100   100   000    Old_age   Always       -       2
198 Offline_Uncorrectable   0x0010   100   100   000    Old_age   Offline      -       2
  9 Power_On_Hours          0x0032   089   089   000    Old_age   Always       -       41231

Що це означає: «PASSED» — не гарантія. Очікувані/офлайн-непоправні сектори важливіші. Цей диск погіршується.

Рішення: Замініть проактивно. Якщо RAID5/6, ризик відновлення зростає; плануйте відновлення з меншим навантаженням та перевіреними бекапами.

Завдання 6: Для апаратного RAID перевірити стан контролера/віртуального диска (приклад storcli)

cr0x@server:~$ sudo storcli /c0/vall show
Controller = 0
Status = Success
Description = Show Virtual Drives

DG/VD TYPE  State Access Consist Cache Cac sCC     Size Name
0/0   RAID5 dgrd  RW     No      RWBD  -   OFF  10.913 TB data_vd0

Що це означає: Віртуальний диск dgrd (деградований). «Consist No» натякає на необхідність перевірки консистентності.

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

Завдання 7: Підтвердити політику кешу запису та стан батареї/суперкапачу

cr0x@server:~$ sudo storcli /c0 show battery
Controller = 0
Status = Success
Description = Battery Status

BatteryType = iBBU
Status = Failed
Replacement required = Yes

Що це означає: Якщо захист кешу не працює, контролери часто вимикають write-back кеш або ризикують втратити підтверджені записи при відключенні живлення.

Рішення: Очікуйте змін продуктивності та потенційного ризику цілісності даних при некоректній політиці. Замініть батарею/суперкапач і перегляньте режим кешу.

Завдання 8: Виміряти, чи ви зацікавлені CPU чи IO (iostat)

cr0x@server:~$ iostat -xz 1 3
Linux 6.1.0 (server) 	01/22/2026 	_x86_64_	(16 CPU)

avg-cpu:  %user %nice %system %iowait  %steal   %idle
          12.34  0.00    5.12   31.45    0.00   51.09

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   w_await aqu-sz  %util
md0              85.0   5420.0     0.0    0.0   18.20    63.76     40.0   3120.0   44.10   2.90   98.7

Що це означає: Високий %iowait і %util близько 100% вказують на вузьке місце вводу/виводу. Латентність запису висока.

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

Завдання 9: Знайти процеси, що створюють найбільше IO (iotop)

cr0x@server:~$ sudo iotop -oPa -n 5
Total DISK READ: 55.43 M/s | Total DISK WRITE: 12.10 M/s
  PID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN  IO>  COMMAND
18422 be/4   postgres  40.22 M/s   8.10 M/s  0.00 % 92.00 % postgres: checkpointer
27109 be/4   root      12.11 M/s   0.00 B/s  0.00 % 15.00 % rsync -aH --delete /data/ /mnt/backup/

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

Рішення: Переплануйте вікна бекапів/обслуговування або реалізуйте обмеження швидкості, щоб бекапи не спричиняли простої (або навпаки).

Завдання 10: Швидко перевірити помилки файлової системи (приклад ext4)

cr0x@server:~$ sudo dmesg | egrep -i "EXT4-fs error|I/O error|Buffer I/O error" | tail -n 6
[915230.112233] EXT4-fs error (device md0): ext4_find_entry:1531: inode #524301: comm nginx: reading directory lblock 0
[915230.112240] Buffer I/O error on device md0, logical block 12345678

Що це означає: Файлова система бачить помилки читання. RAID може маскувати деякі відмови, але не всі.

Рішення: Зупиніть сервіси, якщо можливо, зафіксуйте логи, заплануйте контрольований fsck (або відновлення), а не дозволяйте корупції поширюватися.

Завдання 11: Перевірити стан пулу ZFS та лічильники помилок

cr0x@server:~$ sudo zpool status -v
  pool: tank
 state: DEGRADED
status: One or more devices has experienced an unrecoverable error.
action: Replace the faulted device, or use 'zpool clear' to mark the device repaired.
  scan: scrub repaired 0B in 00:42:18 with 0 errors on Sun Jan 18 02:15:01 2026
config:

        NAME        STATE     READ WRITE CKSUM
        tank        DEGRADED     0     0     0
          mirror-0  DEGRADED     0     0     0
            sdc     FAULTED      0     0     8  too many errors
            sdd     ONLINE       0     0     0

errors: Permanent errors have been detected in the following files:

        tank/data/app.db

Що це означає: ZFS виявив помилки контрольних сум і може сказати, який файл уражено. Це різниця між «ми підозрюємо» і «ми знаємо».

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

Завдання 12: Перевірити знімки ZFS і не плутати їх з бекапами

cr0x@server:~$ sudo zfs list -t snapshot -o name,creation -s creation | tail -n 5
tank/data@hourly-2026-01-22-0600  Thu Jan 22 06:00 2026
tank/data@hourly-2026-01-22-0700  Thu Jan 22 07:00 2026
tank/data@hourly-2026-01-22-0800  Thu Jan 22 08:00 2026
tank/data@hourly-2026-01-22-0900  Thu Jan 22 09:00 2026
tank/data@hourly-2026-01-22-1000  Thu Jan 22 10:00 2026

Що це означає: Добре. Але якщо ці знімки живуть на тому ж пулі, вони не переживуть втрату пулу, компрометацію облікового запису або катастрофу сайту.

Рішення: Реплікуйте знімки до незалежної цілі з іншими обліковими даними і захистом від видалення.

Завдання 13: Підтвердити, що резервні копії існують і свіжі (приклад restic)

cr0x@server:~$ restic -r /mnt/backup/restic-repo snapshots --last
repository 9b2f1c12 opened (version 2, compression level auto)
ID        Time                 Host        Tags        Paths
a1b2c3d4  2026-01-22 09:00:14  server                  /data

Що це означає: У вас є знімок резервної копії за сьогодні. Це початок.

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

Завдання 14: Зробити тестове відновлення одного файлу, щоб довести відновлюваність

cr0x@server:~$ mkdir -p /tmp/restore-test && restic -r /mnt/backup/restic-repo restore latest --target /tmp/restore-test --include /data/important/report.csv
repository 9b2f1c12 opened (version 2, compression level auto)
restoring <Snapshot a1b2c3d4 of [/data] at 2026-01-22 09:00:14 by cr0x@server> to /tmp/restore-test
Summary: Restored 1 files/dirs (42.133 KiB) in 0:00

Що це означає: Це момент, коли «у нас є бекапи» стає фактом. Ви успішно відновили реальний файл.

Рішення: Зробіть це регулярним завданням з випадковим вибором файлів і документуванням результатів.

Завдання 15: Перевірити, чи ваша «ціль бекапу» справді незалежна

cr0x@server:~$ mount | egrep "/data|/mnt/backup"
 /dev/md0 on /data type ext4 (rw,relatime)
 server:/export/backup on /mnt/backup type nfs4 (rw,relatime,vers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2)

Що це означає: Резервні копії йдуть на NFS. Незалежність залежить від того, де тому NFS фізично знаходиться і хто може його видаляти.

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

Завдання 16: Підтвердити політику зберігання та іммутабельність на рівні файлової системи (chattr)

cr0x@server:~$ sudo lsattr -d /mnt/backup
-------------e---- /mnt/backup

Що це означає: Тут немає прапорів іммутабельності. Це може бути прийнятно, але тоді іммутабельність має забезпечуватися системою бекапів або ціллю зберігання.

Рішення: Якщо ransomware у вашій моделі загроз (а він у ній), реалізуйте блокування збереження/іммутабельність поза досяжністю основного адміністратора.

Завдання 17: Перевірити, чи ви на відстані одного опечатки від видалення бекапів (права)

cr0x@server:~$ namei -l /mnt/backup/restic-repo | tail -n 4
drwxr-xr-x root root /mnt
drwxr-xr-x root root /mnt/backup
drwxrwxrwx root root /mnt/backup/restic-repo

Що це означає: Репозиторій бекапів доступний для запису всім. Це не бекап; це громадський арт-проєкт.

Рішення: Заблокуйте права, відокремте облікові дані бекапу та розгляньте варіант append-only або іммутабельних цілей.

Завдання 18: Виявити відновлення або скраб, що тихо вбиває продуктивність

cr0x@server:~$ sudo zpool iostat -v 1 3
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
tank                        2.10T  1.40T    820    210  92.1M  18.2M
  mirror-0                  2.10T  1.40T    820    210  92.1M  18.2M
    sdc                         -      -    420    105  46.0M   9.1M
    sdd                         -      -    400    105  46.1M   9.1M
--------------------------  -----  -----  -----  -----  -----  -----

Що це означає: Тривалі високі читання можуть вказувати на scrub/resilver або зміну навантаження. Потрібно зіставити це зі станом пулу та cron-завданнями.

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

Жарт №2: Відновлення RAID — це еквівалент «швидкої зміни в продакшні». Воно ніколи не швидке і точно все міняє.

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

Цей розділ навмисно конкретний. Загальні поради не витримують інциденту; вони просто цитуються в постмортемі.

1) «Масив здоровий, але файли пошкоджені»

  • Симптоми: Помилки застосунку при читанні конкретних файлів; невідповідність контрольних сум на рівні застосунку; користувачі бачать спотворені медіа; RAID показує оптимальний стан.
  • Коренева причина: Тиха корупція на диску/контролері/кабелі або застосунок записав некоректні дані. Парність/дзеркалювання RAID зберегли це.
  • Виправлення: Використовуйте файлову систему з контрольними сумами (ZFS) або контрольні суми на рівні застосунку; запускайте scrubs; відновіть пошкоджені об’єкти з незалежних бекапів; замініть нестабільне обладнання.

2) «Ми не можемо відновити: другий диск відмовив під час відновлення»

  • Симптоми: Віртуальний диск RAID5 відмовив під час відновлення; з’являються URE; кілька дисків мають помилки носія.
  • Коренева причина: Однопарна захист плюс великі диски плюс велике навантаження читання під час відновлення; недостатній запас на латентні помилки секторів.
  • Виправлення: Віддавайте перевагу RAID6/RAIDZ2 або дзеркалам для великих масивів; тримайте гарячі резерви; запускайте patrol reads/scrubs; замінюйте диски проактивно; переконайтеся, що у вас є відновлювані бекапи перед відновленням.

3) «Бекапи є, але відновлення надто повільне для RTO»

  • Симптоми: Завдання бекапу повідомляє про успіх; відновлення займає дні; бізнес потребує годин.
  • Коренева причина: RTO ніколи не було інженерно спроектовано; пропускної здатності цілі бекапу замало; занадто багато даних, занадто мало пріоритизації; немає поетапного плану відновлення.
  • Виправлення: Визначте RTO/RPO по наборах даних; реалізуйте швидке локальне відновлення (знімки) плюс офсайтні бекапи; передзавантажуйте критичні набори; практикуйте часткові відновлення.

4) «Знімки нас врятували… поки пул не загинув»

  • Симптоми: Впевнене планування знімків; потім катастрофічна втрата пулу; знімки пішли разом з ним.
  • Коренева причина: Знімки зберігалися в тій самій домені відмов як первинні дані.
  • Виправлення: Реплікуйте знімки на іншу систему/обліковий запис; додайте іммутабельність; вважайте «той самий хост» як «той самий радіус ураження».

5) «Ransomware зашифрував production і бекапи»

  • Симптоми: Репозиторій бекапів видалено/зашифровано; збереження очищено; повноваження використано легітимно.
  • Коренева причина: Система бекапів доступна/записувана тим же набором повноважень, які були скомпрометовані в production; немає іммутабельності/air gap.
  • Виправлення: Розділіть облікові дані й увімкніть MFA; ролі тільки для запису; іммутабельний об’єктний лок або append-only цілі; офлайн-копія на випадок найгіршого; моніторинг подій видалення.

6) «Продуктивність впала після заміни диска»

  • Симптоми: Пікові значення латентності після заміни диска; системи тайм-аутять; більше нічого не змінилося.
  • Коренева причина: Rebuild/resilver наситив IO; контролер тротлить; деградований режим на парних масивах.
  • Виправлення: Плануйте вікна відновлення; тротлте відновлення; перемістіть навантаження; додайте шпинделів/SSD; тримайте додатковий запас; не відновлюйте в піковий час, якщо вам не до вподоби хаос.

7) «Контролер помер і ми не можемо імпортувати масив»

  • Симптоми: Диски видно, але метадані масиву не розпізнаються; інструмент вендора не бачить віртуальний диск.
  • Коренева причина: Метадані апаратного RAID прив’язані до сімейства контролерів/прошивки; збій кеш-модуля; плутанина з foreign config.
  • Виправлення: Стандартизуйте контролери і тримайте запасні; експортуйте конфіги контролера; надавайте перевагу програмно-визначуваному сховищу для портативності; найголовніше — майте бекапи, для відновлення яких наявність контролера не є обов’язковою.

Чеклісти / покроковий план: побудувати резервні копії, що переживуть реальність

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

Крок 1: Класифікуйте дані за наслідками для бізнесу

  • Tier 0: автентифікація/ідентичність, білінг, дані клієнтів, критична база даних.
  • Tier 1: внутрішні інструменти, аналітика, логи, потрібні для безпеки/форензики.
  • Tier 2: кеші, артефакти збірки, відтворювані набори даних.

Якщо все «критично», то нічого не критично. Визначте RPO і RTO для кожного рівня. Запишіть це доступно для фінансів.

Крок 2: Виберіть базове правило, а потім перевищте його

Класичне правило — 3-2-1: три копії даних, на двох різних типах носіїв, одна копія поза сайтом. Це початок, не нагорода.
Для ransomware «поза сайтом» також означає «не можна видалити тими ж повноваженнями».

Крок 3: Розділяйте домени відмов навмисно

  • Інше обладнання: не «інший каталог».
  • Інша адміністративна межа: окремі облікові записи/ролі; production не повинен мати права видаляти бекапи.
  • Інша географія: принаймні одна копія поза сайтом/стойкою/регіоном, який ви можете втратити.

Крок 4: Використовуйте знімки для швидкості, бекапи — для виживання

Локальні знімки — для швидкого «ой»: випадковість видалення, поганий деплой, швидкий відкат. Робіть їх частими з коротким періодом зберігання.
Бекапи — для випадків, коли машина, масив або обліковий запис зникли.

Крок 5: Шифруйте і автентифікуйте конвеєр бекапів

  • Шифруйте в стані спокою й у транзиті (і керуйте ключами як важливими — бо вони важливі).
  • Використовуйте виділені облікові дані бекапу з мінімальними правами.
  • Віддавайте перевагу шляхам «тільки запис» з production до бекапу, коли це можливо.

Крок 6: Зробіть політику зберігання, а не інтуїцію

  • Коротко: погодинні/щоденні для швидкого відкату.
  • Середньо: щотижневі/щомісячні для бізнесових/юридичних потреб.
  • Довго: щоквартальні/щорічні, якщо потрібно, зберігати дешево і іммутабельно.

Крок 7: Тестуйте відновлення серйозно

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

Крок 8: Моніторте правильні речі

  • Свіжість бекапів: час останнього успішного знімка по набору даних.
  • Цілісність бекапів: періодична валідація або тест-відновлення.
  • Події видалення: сповіщення про незвичні видалення бекапів.
  • Здоров’я сховища: SMART, стан RAID, помилки ZFS, результати scrub.

Крок 9: Проведіть tabletop-вправу для найгірших сценаріїв

Практикуйте:

  • Випадкове видалення (відновити директорію).
  • Ransomware (припустіть, що зловмисник має production-admin права).
  • Відмова контролера (припустіть, що первинний масив не відновлюваний).
  • Втрата сайту (припустіть, що вся стійка/регіон пішов).

Крок 10: Вирішіть, для чого RAID потрібен (і перестаньте просити від нього роль резервної копії)

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

Одна цитата, яку варто тримати над монітором

У вірі надія — не стратегія. — генерал Джим Маттіс (часто цитують в інженерних та операційних колах)

Якщо ви будуєте сховище на надії, ви не будуєте сховище. Ви створюєте майбутній інцидент-звіт із тривалим терміном підготовки.

Питання та відповіді

1) Якщо в мене RAID1, чи потрібні мені ще бекапи?

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

2) Чи є знімки резервною копією?

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

3) Чи достатньо RAID6, щоб пропустити бекапи?

Ні. RAID6 зменшує ймовірність втрати масиву через відмови дисків під час відновлення. Він нічого не робить для логічних відмов (видалення, перезапис),
шкідливого ПЗ або катастрофічних подій. Бекапи потрібні, бо відмова диска — не єдина загроза.

4) А як щодо хмарного зберігання з надмірністю — чи це рахується як бекап?

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

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

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

6) Як часто тестувати відновлення?

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

7) У чому різниця між реплікацією та бекапом?

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

8) Як захистити бекапи від ransomware?

Розділіть облікові дані і обмежте права на видалення. Використовуйте іммутабельність/політики зберігання на цілі бекапу. Збережіть принаймні одну копію офлайн або в окремій
адміністративній домені. Моніторте підозрілі видалення і забороняйте доступ до репозиторію бекапів з звичайних хостів.

9) Чи усуває ZFS потребу в бекапах?

ZFS підвищує цілісність за допомогою контрольних сум і самовилікування (за наявності надмірності), а знімки — відмінні для швидкого відкату.
Але ZFS не зупиняє вас від видалення даних, шифрування їх або втрати всього пулу. Вам все одно потрібні незалежні бекапи.

10) Які RPO/RTO обрати?

Обирайте, виходячи з болю бізнесу, а не з бажань команди зберігання. Для Tier 0 даних RPO в межах хвилин/годин і RTO в межах годин може бути необхідним.
Для нижчих рівнів — дні можуть бути прийнятними. Головне, щоб числа були інженерно обґрунтовані та протестовані, а не оголошені.

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

RAID — це інструмент, щоб залишатися в мережі при певних апаратних відмовах. Він не машина часу. Він не свідок у суді. Йому байдуже,
чи дані правильні; йому важливо, щоб біти були консистентні між дисками.

Якщо ви керуєте продакшном, зробіть цього тижня такі кроки:

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

А потім, і лише потім, насолоджуйтеся своїм RAID. Він корисний, коли ви ставитеся до нього чесно: як до надмірності, а не як до порятунку.

Повільний ZFS scrub: як відрізнити нормальну затримку від реальної проблеми

Ваш scrub «виконується» вже стільки часу, що колеги запитують, чи не заселено сховище привидами. Додатки відчуваються повільними, панелі показують море I/O, а ETA або відсутній, або бреше. Потрібно зрозуміти: це нормальний, нудний scrub, який виконує свою роботу, чи це симптом проблеми, яка може підкласти вам міну пізніше?

Ось виробничо-дружній спосіб відповісти на це запитання. Ми відділимо очікувану повільність (ту, що ви плануєте й терпите) від патологічної повільності (яку треба виправити, поки вона не перетвориться на пожежу в службі підтримки).

Що насправді робить scrub (і чому іноді «повільно» — це нормально)

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

Це тягне за собою дві речі, які дивують людей:

  • Scrub за своєю суттю читає багато даних (з періодичними записами під час виправлень). Ваш пул може «повільніти», тому що читання повільні, через конкуренцію з реальними навантаженнями або тому, що ZFS свідомо поводиться стримано.
  • Scrub працює на рівні блоків, а не файлів. Фрагментація, вибір recordsize і накладні витрати на метадані можуть важити більше, ніж сирі МБ/с диска.

Scrub також поводиться по-різному залежно від розкладки vdev. Дзеркала зазвичай скрабляться швидше і передбачуваніше, ніж RAIDZ, бо дзеркала можуть обслужити читання з будь-якого диска, а паритетні обчислення простіші. RAIDZ scrubs цілком нормальні, коли пул здоровий, але вони можуть розтягнутися, якщо у вас широкі vdev, слабкі диски або інтенсивний випадковий I/O від додатків.

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

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

Цікаві факти та трохи історії

  • ZFS популяризував наскрізну перевірку контрольних сум у серверному сховищі. Контрольні суми зберігаються окремо від даних, тому ZFS може виявити «брехливі диски», які повертають пошкоджені блоки без I/O помилок.
  • Scrub — це відповідь ZFS на «bit rot» — тиху, поступову корупцію, яку класичний RAID часто не помічає, поки не прочитають дані і не спрацює відновлення паритету.
  • Термін «scrub» походить з більш старих систем зберігання, які періодично сканували носій на помилки. ZFS зробив це рутинним і видимим для користувача.
  • RAIDZ створювався, щоб уникнути write hole у класичних RAID5/6, зберігаючи транзакційну послідовність метаданих і семантику copy-on-write.
  • ZFS народився в Sun Microsystems і пізніше поширився через OpenZFS. Сучасна поведінка залежить від версії OpenZFS, а не лише від бренду «ZFS».
  • Раніше scrubs були болючішими на системах без хорошого планувальника I/O або де обмеження scrub були примітивні. Сучасні стек Linux і FreeBSD дають більше важелів, але й більше способів зашкодити собі.
  • Метадані мають значення. Пули з мільйонами дрібних файлів можуть скрабитися повільніше, ніж пул з меншим числом великих файлів, навіть якщо «використаної місця» виглядає схоже.
  • SMR-диски зробили scrubs непередбачуванішими у реальних умовах. Коли диск виконує фонову шінґлову збірку сміття, «читання» можуть перетворитися на «читання плюс внутрішній перезапис», що ускладнює роботу.
  • Підприємницькі масиви робили патрульні читання десятиліттями, часто непомітно. ZFS просто дає вам правду відкрито — і виявляється, що правда може бути повільною.

Нормальна повільність scrub проти реальної проблеми: модель мислення

«Повільний scrub» неоднозначний. Потрібно визначити, який саме тип повільності ви бачите. Я ділю це на чотири категорії:

1) «Великий пул, фізика» — повільно

Якщо у вас сотні ТБ на обертових дисках, scrub, що триває дні, може бути нормальним. Його обмежує послідовна пропускна здатність, розкладка vdev і те, що scrub не завжди отримує ідеально послідовні доступи (розподілені блоки не обов’язково сусідні).

Ознаки нормальності:

  • Швидкість scrub стабільна протягом годин.
  • Затримки диска не вибухають.
  • Вплив на додатки передбачуваний і обмежений.
  • Немає помилок контрольних сум або помилок читання.

2) «Навмисне обмежено» — повільно

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

Ознаки обмеження:

  • CPU в основному в нормі.
  • IOPS не досягають максимуму, але прогрес scrub рухається повільно.
  • Затримка навантаження залишається в межах SLO.

3) «Конкуренція з навантаженням» — повільно

Якщо пул обслуговує завантажену базу даних, ферму VM або об’єктне навантаження, читання scrub конкурують із читаннями/записами додатків. Тепер швидкість scrub залежить від робочих годин бізнесу. Це не провина ZFS; це питання планування.

Ознаки конкуренції:

  • Швидкість scrub змінюється залежно від патернів трафіку.
  • Спайки затримок корелюють із піками додатків.
  • Вимкнення scrub робить користувачів задоволенішими.

4) «Щось не так» — повільно

Ця категорія — саме те, про що ви насправді питаєте. Повільність scrub стає симптомом: диск повторно читає, контролер видає помилки, лінк перейшов на 1.5Gbps, член vdev хворіє і тягне всіх, або ви побудували розкладку пулу, яка придатна для ємності, але погана для scrub.

Ознаки, що це справжня проблема:

  • Помилки читання, помилки контрольних сум або зростання «відновлених» байтів між scrub.
  • Один диск показує значно вищі затримки або нижчу пропускну здатність, ніж інші.
  • Швидкість scrub колапсує з часом (спочатку нормальна, потім повзе).
  • Логи ядра показують ресети, тайм-аути або проблеми з лінком.
  • SMART-параметри показують переназначені/очікувані сектори або UDMA CRC помилки.

Ключове: «повільно» — не діагноз. Ви полюєте на вузьке місце і потім питаєте, чи це очікуване, налаштоване або відмовне.

Швидка діагностика (перший/другий/третій)

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

Перший: Чи здоровий scrub?

  • Перевірте стан пулу на наявність помилок і фактичну швидкість scrub.
  • Пошукайте будь-якого члена vdev, що деградував, відмовив або має «забагато помилок».
  • Рішення: якщо є помилки, сприймайте це як інцидент надійності перш за все, а питання продуктивності — по-друге.

Другий: Чи тягне якийсь пристрій весь vdev?

  • Перевірте затримки по диску і час обслуговування I/O під час scrub.
  • Швидко перевірте SMART на наявність очікуваних секторів, помилок носія і CRC лінку.
  • Рішення: якщо один диск повільний або робить ретраї, замініть його або ізолюйте; scrub — це канарка.

Третій: Це конкуренція чи обмеження?

  • Корелюйте швидкість scrub з метриками навантаження (IOPS, затримка, глибина черги).
  • Перевірте налаштування ZFS і чи не обмежено scrub навмисно.
  • Рішення: якщо це обмеження, налаштуйте обережно; якщо конкуренція — перенесіть чи розділіть навантаження.

Лише після цих трьох кроків переходьте до «архітектурних питань», як ширина vdev, recordsize, спеціальні vdev або додавання кешу. Якщо scrub повільний через гнучкий SATA-кабель, жодне «тонке налаштування» не допоможе.

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

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

Завдання 1: Підтвердити стан scrub, швидкість і помилки

cr0x@server:~$ zpool status -v tank
  pool: tank
 state: ONLINE
  scan: scrub in progress since Mon Dec 23 01:00:02 2025
        12.3T scanned at 612M/s, 8.1T issued at 403M/s, 43.2T total
        0B repaired, 18.75% done, 2 days 09:14:33 to go
config:

        NAME                        STATE     READ WRITE CKSUM
        tank                        ONLINE       0     0     0
          raidz2-0                  ONLINE       0     0     0
            sda                     ONLINE       0     0     0
            sdb                     ONLINE       0     0     0
            sdc                     ONLINE       0     0     0
            sdd                     ONLINE       0     0     0
            sde                     ONLINE       0     0     0
            sdf                     ONLINE       0     0     0

errors: No known data errors

Що це означає: ZFS показує і «scanned», і «issued». Issued ближче до фактичної швидкості завершення фізичних I/O. Якщо issued набагато нижчий за scanned, ви можете бачити readahead, ефекти кешування або очікування на повільні пристрої.

Рішення: Якщо READ/WRITE/CKSUM рахунки ненульові, перестаньте сприймати це як «просто повільно». Розслідуйте несправні пристрої перед налаштуванням.

Завдання 2: Отримати однолінійний прогрес регулярно (добре для каналів інцидентів)

cr0x@server:~$ zpool status tank | sed -n '1,12p'
  pool: tank
 state: ONLINE
  scan: scrub in progress since Mon Dec 23 01:00:02 2025
        12.3T scanned at 612M/s, 8.1T issued at 403M/s, 43.2T total
        0B repaired, 18.75% done, 2 days 09:14:33 to go
config:

        NAME                        STATE     READ WRITE CKSUM
        tank                        ONLINE       0     0     0

Що це означає: Це мінімальний життєздатний фрагмент статусу. Якщо ETA постійно зростає година за годиною, ймовірно, ви маєте конкуренцію або ретраї читань.

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

Завдання 3: З’ясувати, з якою розкладкою vdev ви маєте справу

cr0x@server:~$ zpool status -P tank
  pool: tank
 state: ONLINE
config:

        NAME                                   STATE     READ WRITE CKSUM
        tank                                   ONLINE       0     0     0
          raidz2-0                             ONLINE       0     0     0
            /dev/disk/by-id/ata-ST12000...A1   ONLINE       0     0     0
            /dev/disk/by-id/ata-ST12000...B2   ONLINE       0     0     0
            /dev/disk/by-id/ata-ST12000...C3   ONLINE       0     0     0
            /dev/disk/by-id/ata-ST12000...D4   ONLINE       0     0     0
            /dev/disk/by-id/ata-ST12000...E5   ONLINE       0     0     0
            /dev/disk/by-id/ata-ST12000...F6   ONLINE       0     0     0

Що це означає: RAIDZ2 у одному широкому vdev. Швидкість scrub буде обмежена найповільнішим диском та накладними витратами паритету. Один невідповідний диск може загальмувати весь vdev.

Рішення: Якщо у вас дуже широкий RAIDZ vdev і scrubs болісні, вам може знадобитися архітектурна зміна пізніше (більше vdev, вужча ширина). Не намагайтеся «натюнити» фізику.

Завдання 4: Перевірити затримки по дисках і завантаження під час scrub (Linux)

cr0x@server:~$ iostat -x 2 3
Linux 6.6.12 (server)     12/25/2025  _x86_64_    (32 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           4.21    0.00    2.73    8.14    0.00   84.92

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s  w_await aqu-sz  %util
sda             112.0   28800.0     0.0   0.00   18.40   257.1      2.0     512.0   4.30   2.10  98.5
sdb             118.0   30208.0     0.0   0.00   17.92   256.0      2.0     512.0   4.10   2.12  97.9
sdc             110.0   28160.0     0.0   0.00   19.30   256.0      2.0     512.0   4.20   2.05  98.2
sdd              15.0    3840.0     0.0   0.00  220.10   256.0      1.0     256.0  10.00   3.90  99.1
sde             115.0   29440.0     0.0   0.00   18.10   256.0      2.0     512.0   4.00   2.08  98.0
sdf             114.0   29184.0     0.0   0.00   18.70   256.0      2.0     512.0   4.20   2.11  98.4

Що це означає: sdd має r_await близько 220ms, тоді як інші — близько 18ms. Це ваш анклав scrub. Пул рухатиметься в темпі найгіршого виконавця в RAIDZ vdev.

Рішення: Негайно перевірте sdd на помилки/логи/SMART. Якщо це кабель або контролер — виправте це перед заміною диска.

Завдання 5: Перевірити системні логи на ресети/тайм-аути (Linux)

cr0x@server:~$ sudo dmesg -T | egrep -i 'ata|scsi|reset|timeout|error' | tail -n 12
[Wed Dec 24 13:18:44 2025] ata7.00: exception Emask 0x0 SAct 0x0 SErr 0x0 action 0x6 frozen
[Wed Dec 24 13:18:44 2025] ata7.00: failed command: READ FPDMA QUEUED
[Wed Dec 24 13:18:44 2025] ata7: hard resetting link
[Wed Dec 24 13:18:45 2025] ata7: SATA link up 1.5 Gbps (SStatus 113 SControl 300)
[Wed Dec 24 13:18:46 2025] ata7.00: sd 6:0:0:0: [sdd] tag#17 FAILED Result: hostbyte=DID_OK driverbyte=DRIVER_OK cmd_age=14s
[Wed Dec 24 13:18:46 2025] blk_update_request: I/O error, dev sdd, sector 123456789 op 0x0:(READ) flags 0x0 phys_seg 8 prio class 0

Що це означає: Ресет лінку і повторне узгодження на 1.5Gbps — класична ознака проблем з кабелем/бекплейном/портом. Це також може бути вмираючий диск, але кабелі дешевші і соромно поширені.

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

Завдання 6: Швидка перевірка SMART для повільного пристрою

cr0x@server:~$ sudo smartctl -a /dev/sdd | egrep -i 'Reallocated_Sector_Ct|Current_Pending_Sector|Offline_Uncorrectable|UDMA_CRC_Error_Count|SMART overall|Power_On_Hours'
SMART overall-health self-assessment test result: PASSED
  9 Power_On_Hours          0x0032   086   086   000    Old_age   Always       -       31245
  5 Reallocated_Sector_Ct   0x0033   100   100   010    Pre-fail  Always       -       0
197 Current_Pending_Sector  0x0012   100   100   000    Old_age   Always       -       12
198 Offline_Uncorrectable   0x0010   100   100   000    Old_age   Offline      -       3
199 UDMA_CRC_Error_Count    0x003e   200   199   000    Old_age   Always       -       27

Що це означає: Очікувані сектори та offline uncorrectable означають, що диск має проблеми з читанням деяких областей. UDMA CRC помилки часто вказують на проблеми з кабелем/бекплейном. «PASSED» — не виправдання; це маркетинг.

Рішення: Якщо є pending/offline uncorrectable, плануйте заміну. Якщо CRC помилки зростають, виправте шлях (кабель/бекплейн/HBA) також.

Завдання 7: Виявити, чи пул виконує виправлення (і скільки)

cr0x@server:~$ zpool status -v tank | sed -n '1,25p'
  pool: tank
 state: ONLINE
  scan: scrub in progress since Mon Dec 23 01:00:02 2025
        14.8T scanned at 540M/s, 10.2T issued at 372M/s, 43.2T total
        256M repaired, 23.61% done, 2 days 05:01:12 to go
config:

        NAME        STATE     READ WRITE CKSUM
        tank        ONLINE       0     0     0
          raidz2-0  ONLINE       0     0     0

Що це означає: Ненульовий показник «repaired» під час scrub означає, що ZFS знайшов невідповідності контрольних сум і виправив їх. Це scrub, який виконує свою роботу, але також доказ корупції десь нижче (диск, кабель, контролер або пам’ять).

Рішення: Якщо виправлення повторюються між scrub’ами, розслідуйте корінь проблеми. Одноразове виправлення після відомої події може бути прийнятним; повторювані ремонти — ні.

Завдання 8: Шукати індикатори I/O і затримки на рівні ZFS (Linux)

cr0x@server:~$ sudo cat /proc/spl/kstat/zfs/arcstats | egrep '^(hits|misses|size|c_max|demand_data_misses|prefetch_data_misses) ' | head
hits                            2876543210
misses                          456789012
size                            17179869184
c_max                           34359738368
demand_data_misses              123456789
prefetch_data_misses            234567890

Що це означає: ARC-статистика показує, чи читаються дані з пам’яті чи йдуть на диск. Під час scrub ARC може бути менш корисним, бо scrub-читання не завжди дружні до кешу, але мала ARC з великим навантаженням може погіршити конкуренцію.

Рішення: Якщо ARC виснажена і вузол підмінює, вирішіть проблему пам’яті перш за все. Не звинувачуйте scrub у дефіциті RAM.

Завдання 9: Перевірити I/O пулу в реальному часі (Linux з zpool iostat)

cr0x@server:~$ zpool iostat -v tank 2 3
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
tank        28.1T  15.1T  3.10K    220   380M  12.1M
  raidz2-0  28.1T  15.1T  3.10K    220   380M  12.1M
    sda          -      -    520     35  63.5M  2.1M
    sdb          -      -    525     36  64.1M  2.1M
    sdc          -      -    518     35  63.2M  2.0M
    sdd          -      -    110     34  12.9M  2.0M
    sde          -      -    523     35  64.0M  2.1M
    sdf          -      -    524     35  64.3M  2.1M
----------  -----  -----  -----  -----  -----  -----

Що це означає: Знову ж таки, sdd демонструє низьку продуктивність порівняно з іншими. У RAIDZ це може зменшити ефективну пропускну здатність і збільшити час scrub.

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

Завдання 10: Підтвердити ashift і базові властивості пулу (базова продуктивність)

cr0x@server:~$ zdb -C tank | egrep 'ashift|vdev_tree' -n | head -n 8
64:        ashift: 12
120:    vdev_tree:
121:        type: 'root'
122:        id: 0

Що це означає: ashift: 12 означає 4K сектори. Якщо ви бачите ashift: 9 на сучасних 4K дисках, ви можете отримати ампліфікацію записів і дивну поведінку продуктивності. Це не завжди показує себе під час scrub (в основному читання), але може погіршити загальну продуктивність пулу і накладні витрати на resilver/scrub.

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

Завдання 11: Перевірити стиснення та recordsize набору даних (взаємодія з навантаженням)

cr0x@server:~$ zfs get -o name,property,value -s local compression,recordsize tank/vmstore
NAME         PROPERTY     VALUE
tank/vmstore compression  lz4
tank/vmstore recordsize   128K

Що це означає: Для образів VM recordsize часто ставлять меншим (наприклад, 16K) залежно від патернів I/O. Великий recordsize не завжди «помилка», але якщо ваше навантаження випадкове 4K, ви можете отримати більше читань на корисний байт під час scrub і загалом більше операцій.

Рішення: Не змінюйте recordsize легковажно на існуючих даних. Але якщо більші труднощі зі scrub корелюють з набором даних, відомим дрібним випадковим I/O, перегляньте дизайн набору даних для наступної ітерації.

Завдання 12: Перевірити наявність спеціальних vdev (для метаданих) та їх стан

cr0x@server:~$ zpool status tank | egrep -n 'special|log|cache|spares' -A3
15:    special
16:      nvme0n1p2             ONLINE       0     0     0

Що це означає: Якщо у вас є special vdev (часто NVMe), що зберігає метадані/дрібні блоки, його стан і затримка можуть домінувати у поведінці scrub для пулів з великою кількістю метаданих. Помираючий special vdev може зробити весь пул «повільним», навіть якщо HDD у порядку.

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

Завдання 13: Перевірити фактичний шлях пристрою і швидкість лінку (поширена прихована несправність)

cr0x@server:~$ sudo hdparm -I /dev/sdd | egrep -i 'Transport|speed|SATA Version' | head -n 5
Transport: Serial, ATA8-AST, SATA 3.1
SATA Version is:  SATA 3.1, 6.0 Gb/s (current: 1.5 Gb/s)

Що це означає: Диск підтримує 6.0Gb/s, але наразі працює на 1.5Gb/s. Це сильний індикатор проблеми лінку, а не «ZFS повільний».

Рішення: Виправте фізичний шлях. Після ремонту підтвердіть узгодження на 6.0Gb/s і знову запустіть iostat.

Завдання 14: Перевірити параметри, пов’язані з обмеженням scrub (Linux OpenZFS)

cr0x@server:~$ sudo systool -m zfs -a 2>/dev/null | egrep 'zfs_scrub_delay|zfs_top_maxinflight|zfs_vdev_scrub_max_active' | head -n 20
  Parameters:
    zfs_scrub_delay        = "4"
    zfs_top_maxinflight    = "32"
    zfs_vdev_scrub_max_active = "2"

Що це означає: Ці значення впливають на те, як агресивно scrub витісняє I/O. Більш агресивне не завжди краще; ви можете збільшити глибину черги і затримки для додатків, а іноді уповільнити сам scrub через трешинг.

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

Завдання 15: Підтвердити TRIM і autotrim поведінку (SSD пули)

cr0x@server:~$ zpool get autotrim tank
NAME  PROPERTY  VALUE     SOURCE
tank  autotrim  off       default

Що це означає: У SSD пулах autotrim може впливати на довготривалу продуктивність. Не безпосередньо на швидкість scrub, але змінює поведінку пулу під стійкими читаннями/записами і GC, що може робити scrubs «випадково жахливими».

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

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

cr0x@server:~$ sudo grep -R "zpool scrub" -n /etc/cron* /var/spool/cron 2>/dev/null | head
/etc/cron.monthly/zfs-scrub:4: zpool scrub tank

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

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

Три корпоративні міні-історії з передової scrub

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

Середня компанія експлуатувала ZFS-підтримуваний кластер віртуалізації. Нічого екзотичного: RAIDZ2, великі SATA-диски, по одному пулу на вузол. Scrubs були заплановані щомісяця і завжди займали «трохи часу». Люди це сприймали як норму.

Потім одного місяця ETA scrub почав зростати. Спочатку не драматично — лише на день. На виклику припустили, що це конкуренція навантаження: пакетні задачі наприкінці кварталу. Вони дали процесу йти далі. «Scrubs повільні; закінчиться.»

Через два дні латентність VM для користувачів підскочила, потім спала, потім знову підскочила. Zpool status все ще показував ONLINE, без очевидних помилок. Припущення трималося: «занадто завантажено». Тож ніхто не дивився на статистику по дисках. Це була помилка.

Коли хтось нарешті запустив iostat -x, один диск мав read await 300–800ms, тоді як інші — 15–25ms. SMART показав pending сектори. Диск не помирав швидко; він помирав коректно, тягнучи весь vdev через ретраї. Це найгірший вид, бо виглядає як «нормальна повільність» доти, доки раптом не перестає бути нормальною.

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

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

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

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

Насправді сталося банальне: I/O патерн scrub витіснив кеш навантаження і розгромив черги дисків. HDD опинилися майже на 100% util з високими часами обслуговування. Деякі читання додатків стали хвостовими затримками. Сам scrub і не закінчився загалом швидше — бо коли черги виросли, ефективна пропускна здатність впала і зросли ретраї.

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

Короткий жарт №2: Налаштування зберігання — як офісна політика: якщо тиснеш занадто сильно, усі гальмують, і врешті-решт це все одно твоя провина.

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

Команда фінтеху експлуатувала OpenZFS на Linux для навантаження, схожого на реєстр. Scrubs вважали формальною процедурою обслуговування: планували, моніторили і порівнювали з історичними базами. Ніяких геройств. Просто графіки й дисципліна.

Вони мали простий рукбук: після кожного scrub записувати тривалість, середню issued bandwidth і будь-які відновлені байти. Якщо «repaired» ненульовий — це тригер для глибшої перевірки: логи ядра, SMART long test і перегляд останніх змін у апаратурі.

Якось місяця scrub завершився з невеликою кількістю repaired — нічого страшного саме по собі. Але це було другий місяць підряд. Їхня логіка по базі даних це відфільтрувала. На виклику знайшли інтермітентні CRC помилки на одному шляху диска. Не досить, щоб диск відразу відмовив, але достатньо, щоб іноді перевертати біти під навантаженням. Саме такий дефект руйнує вам день через шість місяців.

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

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

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

ETA scrub зростає з часом

  • Симптом: ETA з «12 годин» до «2 днів» під час виконання scrub.
  • Причина: Пристрій повторно читає (проблеми носія) або лінк флапає; альтернативно — конкуренція навантаження піднялася.
  • Виправлення: Запустіть iostat -x і zpool iostat -v, щоб знайти повільний диск; перевірте dmesg і SMART. Якщо повільного диска немає, корелюйте з навантаженням і перенесіть scrub.

Scrub «повільний» лише в робочі години

  • Симптом: Scrub повзе 9–17 і прискорюється вночі.
  • Причина: Конкуренція з виробничим навантаженням; ZFS і/або планувальник ОС роблять правильно, віддаючи пріоритет foreground I/O.
  • Виправлення: Плануйте scrubs на періоди з малим трафіком; розгляньте обмеження замість агресії. Не збільшуйте конкуренцію бездумно.

Один диск має в 10 разів більший await, ніж інші

  • Симптом: В iostat -x один диск має високий r_await або незвичні патерни %util.
  • Причина: Вмираючий диск, поведінка SMR під навантаженням, поганий кабель/бекплейн, порт узгодився вниз.
  • Виправлення: Перевірте dmesg і SMART, підтвердіть швидкість лінку, поміняйте кабель/порт, замініть диск якщо є pending сектори або uncorrectables.

Scrub викликає тайм-аути додатків

  • Симптом: Спайки затримки, тайм-аути, черги ростуть; scrub ніби «DoS» систему.
  • Причина: Scrub I/O надто агресивний, погана ізоляція навантажень, замало vdev, HDD-пул обслуговує випадкове I/O без достатньої кількості шпинделів.
  • Виправлення: Зменшіть агресивність scrub; перенесіть у часи з меншим трафіком; додайте vdev або перемістіть навантаження на SSD/NVMe; розгляньте спеціальні vdev для метаданих. Перестаньте очікувати, що один широкий RAIDZ vdev поводиться як масив.

Scrub повторно повідомляє про відновлені байти

  • Симптом: Кожен scrub щось відновлює.
  • Причина: Хронічне джерело корупції: поганий диск, кабель, контролер або проблеми з пам’яттю (так, пам’ять).
  • Виправлення: Розслідуйте апаратний шлях від початку до кінця; запустіть SMART long tests; перевірте логи ECC якщо доступні; розгляньте контрольне тестування пам’яті. Відновлення даних — це подарунок: не ігноруйте його.

Scrub повільний на SSD пулі «без причини»

  • Симптом: NVMe/SSD пул scrubs повільніші, ніж очікується, іноді з періодичними падіннями.
  • Причина: Троттлінг температури, GC SSD, погана поведінка TRIM, проблеми PCIe лінку або вузьке місце special vdev.
  • Виправлення: Перевірте температури і швидкість PCIe лінку; перегляньте autotrim; підтвердіть прошивку; переконайтеся, що special vdev не перевантажений або не має помилок.

Scrub ніколи не встигає до наступного запланованого scrub

  • Симптом: Постійно виконується scrub; оператори перестають звертати увагу.
  • Причина: Перевищено розмір пулу для наявних носіїв, занадто часта каденція або scrub перезапускається автоматично.
  • Виправлення: Зменшіть каденцію; переконайтеся, що scrubs не перезапускаються даремно; розгляньте архітектурні зміни (більше vdev, швидші носії), якщо перевірки цілісності не завершуються в розумні строки.

Швидкість scrub значно нижча, ніж підказує математика дисків

  • Симптом: «У нас N дисків, кожен дає X MB/s, отже чому не N×X?»
  • Причина: Scrub читає виділені блоки, не обов’язково послідовні; метадані; паритет RAIDZ; фрагментація; і пул може бути близький до заповнення, що погіршує ситуацію.
  • Виправлення: Порівнюйте зі своїми історичними scrub-базами, а не з даташитами виробника. Якщо майже повний — звільніть простір. Якщо фрагментація значна — розгляньте планову перебудову через реплікацію на свіжий пул.

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

Покроково: Визначити, чи повільний scrub — «нормальний»

  1. Захопіть поточний стан. Запустіть zpool status -v. Збережіть у тікеті/чаті.
  2. Пошукайте помилки. Ненульові READ/WRITE/CKSUM або зміни «repaired» змінюють пріоритет.
  3. Виміряйте issued rate. Якщо issued стабільний і в межах історії — ймовірно нормально.
  4. Перевірте затримки по дисках. Використайте iostat -x (Linux) і знайдіть аутлайєри.
  5. Перевірте логи. Один рядок у dmesg про ресети може пояснити дні scrub-болю.
  6. Перевірте SMART. Pending сектори, uncorrectable і CRC помилки вирішують питання заміни апаратури.
  7. Корелюйте з навантаженням. Якщо scrub повільний лише під навантаженням — виправте планування і/або throttling.
  8. Лише потім тюнінг. І робіть по одній зміні з планом відкату.

Покроково: Якщо ви знайшли повільний диск під час scrub

  1. Підтвердіть, що він послідовно повільний: iostat -x 2 5 і zpool iostat -v 2 5.
  2. Перевірте, чи лінк узгодився вниз: hdparm -I для SATA або логи контролера для SAS.
  3. Перегляньте системні логи на ресети/тайм-аути: відфільтруйте dmesg -T.
  4. Перевірте SMART: pending/offline uncorrectable означають, що диск «живе на позиченому часі».
  5. Спочатку поміняйте дешеві речі (кабель/порт), якщо є ознаки проблеми лінку.
  6. Замініть диск, якщо присутні проблеми носія або помилки залишаються після виправлення шляху.
  7. Після заміни запустіть ще один scrub або принаймні цільову перевірку відповідно до ваших операційних стандартів.

Покроково: Якщо scrub здоровий, але порушує продуктивність

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

FAQ

1) Яка «нормальна» швидкість ZFS scrub?

Нормальна — це те, що робить ваш пул, коли він здоровий, слабко навантажений і без помилок. Використовуйте власні історичні тривалість scrub і issued bandwidth як базу. Специфікації послідовності від виробника диска — не обіцянка scrub.

2) Чому scanned відрізняється від issued в zpool status?

«Scanned» відображає логічний прогрес по блоках; «issued» — фактичні I/O, надіслані/завершені на vdev. Великі розбіжності можуть бути через кешування, readahead або очікування повільних пристроїв. Якщо issued низький і затримка висока — шукайте тягнучий диск.

3) Чи scrub читає вільний простір?

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

4) Чи варто зупиняти scrub, якщо він повільний?

Якщо scrub здоровий, але порушує SLO виробництва, пауза/зупинка може бути розумним рішенням — і потім перепланувати. Якщо ви бачите помилки або виправлення, зупинка лише відтерміновує інформацію, яка вам, ймовірно, потрібна. Вирішуйте апаратні проблеми.

5) Як часто слід виконувати scrub?

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

6) Scrub знайшов і виправив дані. Чи тепер я в безпеці?

Ви в кращому положенні, ніж були, але це не кінець. Виправлення означає, що десь нижче була корупція. Якщо ремонти повторюються, потрібен root cause analysis дисків, кабелів, контролерів і, можливо, пам’яті.

7) Чи RAIDZ природно повільніший під час scrub порівняно з дзеркалами?

Дзеркала часто швидші і передбачуваніші для читань, бо можуть балансувати навантаження і не виконують паритетну реконструкцію на читанні. RAIDZ може бути нормальним, коли здоровий, але широкі RAIDZ vdev більш чутливі до одного повільного диска і до випадкових I/O патернів.

8) Чи тюнінг може значно прискорити scrubs?

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

9) Чому scrub повільний на пулі, що здебільшого порожній?

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

10) У чому різниця між scrub і resilver, і чому це важливо для повільності?

Scrub перевіряє наявні дані і виправляє корупцію; resilver відтворює дані на заміненому/повернутому пристрої. Resilver зазвичай має інший пріоритет і патерни, і може бути більш записоємним. Якщо плутати їх, ви неправильно оціните очікування швидкості і терміновість.

Висновок: практичні наступні кроки

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

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

  1. Запустіть zpool status -v і вирішіть, чи це подія надійності (помилки/ремонти) або питання планування/продуктивності.
  2. Запустіть iostat -x і zpool iostat -v, щоб знайти повільний пристрій або підтвердити конкуренцію.
  3. Перевірте dmesg і SMART на очевидні проблеми апаратного шляху.
  4. Лише потім розглядайте тюнінг і зміну розкладу, і вимірюйте вплив по відношенню до вашої історичної бази.

Одна перефразована ідея від W. Edwards Deming підходить для операційної роботи: «Без даних ви просто людина з думкою». Повільність scrub — це ваш шанс зібрати дані, поки ви не зібрали інцидентів.

Debian 13: Служба не запускається після зміни конфігурації — виправте, читаючи потрібні рядки журналу (випадок №1)

Ви змінили конфігурацію. Ви вчинили відповідально. Ви навіть залишили коментар типу «тимчасово», який обов’язково залишиться там у 2027 році. Тепер служба не запускається, моніторинг присилає сповіщення, а systemctl status поводиться загадково.

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

Випадок №1: зміна конфігурації → служба не запускається (що насправді сталося)

Це найпоширеніший сценарій, який я бачу на Debian-системах: служба працює, хтось редагує конфігураційний файл, а потім перезапускає службу. Перезапуск не вдається. Людина на чергуванні відкриває systemctl status, бачить «failed with result ‘exit-code’» і починає гадати.

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

  • Ранніше, ніж рядок «Main process exited…»
  • Від допоміжного процесу (наприклад, ExecStartPre), який перевірив конфіг і вийшов
  • Або від самого демона, виведений один раз і похований під системними рядками systemd

Для випадку №1 уявіть типову службу з кроком перевірки конфігурації:

  • ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on;
  • ExecStart=/usr/sbin/nginx -g daemon on; master_process on;

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

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

Кілька фактів і історія, які пояснюють, чому журнали виглядають так

Розуміння того, чому Debian 13 поводиться саме так, робить вас швидшим під тиском. Ось конкретні факти, які важливі, коли служба відмовляється запускатися після зміни конфігурації:

  1. systemd став системою ініціалізації за замовчуванням у Debian з версії 8 (Jessie). Це стандартизувало керування службами та очікування від логів, але також змінило місця, куди люди дивляться за помилками.
  2. journald — це не текстовий файл. Журнали зберігаються в бінарному журналі й запитуються через journalctl. Ви все ще можете переслати у syslog, але канонічним джерелом є журнал.
  3. systemctl status — це підсумок, а не розслідування. Воно показує обрізаний шматок журналу і високорівневий стан юніта. Це покликано спрямувати вас до глибших запитів, а не замінити їх.
  4. systemd-юніти можуть мати кілька процесів перед запуском «справжнього» демона. ExecStartPre, генератори, обгортки та файли середовища можуть упасти ще до того, як PID вашої служби з’явиться.
  5. Коди виходу стандартизовані, але часто вводять в оману без контексту. «exit status 1» може означати «синтаксична помилка», «доступ заборонено» або «порт уже зайнятий». Потрібне повідомлення поряд із кодом.
  6. Багато демонів спроектовані так, щоб відмовлятись при некоректному конфігу швидко. Nginx, Postfix, HAProxy та інші навмисно відмовляються запускатись, якщо тести конфігурації не проходять — бо запуск з частковою чи некоректною конфігурацією гірший.
  7. Пакети Debian часто додають перевірки безпеки. Упакувальники часто включають передстартові валідації в юніти або скрипти-обгортки. Це гарна інженерія, але означає, що помилки можуть походити зі скриптів, про які ви не підозрювали.
  8. Порядок у журналах може вводити в оману. journald має часові позначки, але паралельний старт юнітів та кілька процесів можуть перемішувати повідомлення. «Останній рядок» не завжди є «причиною».
  9. Обмеження швидкості реальні. journald може лімітувати спамні служби; перша помилка записується, а наступні 500 можуть бути підсумовані. Якщо ви читаєте тільки підсумок, ви пропускаєте першу підказку.

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

Швидкий план діагностики (перші/другі/треті перевірки)

Це порядок, який переможе в продакшені. Він орієнтований на отримання кореневої причини за менше ніж п’ять хвилин, а не на створення ілюзії зайнятості.

Перше: підтвердьте, що systemd вважає за неуспіх (огляд юніта)

  • Отримайте стан юніта, код виходу і яка фаза впала (передстарт чи основний запуск).
  • Витягніть точну командну лінію, яку systemd виконав (включно з ExecStartPre).

Друге: витягніть правильний фрагмент журналу (за часом і юнітом)

  • Запитуйте логи для цього юніта, для останнього завантаження, з мінімальним шумом.
  • Потім розширюйте часовий інтервал за потреби; не розширюйте спочатку область юніта.
  • Шукайте перший змістовний рядок з помилкою, а не останній рядок «exited».

Третє: запустіть власну валідацію конфігурації вручну

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

Четверте: вирішіть — виправити, відкотити чи тимчасово обійти

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

Практичні завдання: команди, очікуваний вивід і рішення (12+)

Ці завдання написані так, як насправді працює SRE: запустив команду, прочитав вивід, ухвалив рішення. Ніяких мотиваційних промов. Кожне завдання включає, що означає вивід і що робити далі.

Завдання 1: Перевірте статус юніта (але читайте його правильно)

cr0x@server:~$ systemctl status nginx.service --no-pager
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: failed (Result: exit-code) since Mon 2025-12-30 10:14:03 UTC; 42s ago
   Duration: 2.103s
    Process: 21984 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=1/FAILURE)
        CPU: 29ms

Dec 30 10:14:03 server nginx[21984]: nginx: [emerg] unexpected "}" in /etc/nginx/sites-enabled/app.conf:57
Dec 30 10:14:03 server systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE
Dec 30 10:14:03 server systemd[1]: nginx.service: Failed with result 'exit-code'.
Dec 30 10:14:03 server systemd[1]: Failed to start nginx.service - A high performance web server and a reverse proxy server.

Що це означає: Невдача сталася в ExecStartPre, до запуску демона nginx. Це помилка тесту конфігурації, а не аварійний збій у runtime.

Рішення: Не ганяйтеся за портами, PID-файлами чи лімітами ядра. Виправте рядок у конфігу, на який посилається повідомлення (app.conf:57), і знову запустіть тест конфігурації.

Завдання 2: Показати лише журнал для цього юніта (остання спроба, чисто)

cr0x@server:~$ journalctl -u nginx.service -b --no-pager -n 60
Dec 30 10:14:03 server systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
Dec 30 10:14:03 server nginx[21984]: nginx: [emerg] unexpected "}" in /etc/nginx/sites-enabled/app.conf:57
Dec 30 10:14:03 server systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE
Dec 30 10:14:03 server systemd[1]: nginx.service: Failed with result 'exit-code'.
Dec 30 10:14:03 server systemd[1]: Failed to start nginx.service - A high performance web server and a reverse proxy server.

Що це означає: Журнал підтверджує точну помилку парсера. Додаткові вгадки не потрібні.

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

Завдання 3: Витягти логи «з моменту перезапуску», коли завантаження шумне

cr0x@server:~$ systemctl show -p ActiveEnterTimestampMonotonic nginx.service
ActiveEnterTimestampMonotonic=81234567890
cr0x@server:~$ journalctl -u nginx.service -b --no-pager --since "2 min ago"
Dec 30 10:14:03 server systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
Dec 30 10:14:03 server nginx[21984]: nginx: [emerg] unexpected "}" in /etc/nginx/sites-enabled/app.conf:57
Dec 30 10:14:03 server systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE
Dec 30 10:14:03 server systemd[1]: nginx.service: Failed with result 'exit-code'.

Що це означає: Ви звужуєте логи за часом, замість обробляти весь процес завантаження.

Рішення: Якщо помилки немає в цьому вікні, розширте до 10 хвилин; але не знімайте фільтр за юнітом поки що.

Завдання 4: Проінспектуйте юніт на предмет передстартових перевірок і файлів середовища

cr0x@server:~$ systemctl cat nginx.service
# /lib/systemd/system/nginx.service
[Unit]
Description=A high performance web server and a reverse proxy server
After=network-online.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on;
ExecStart=/usr/sbin/nginx -g daemon on; master_process on;
ExecReload=/usr/sbin/nginx -g daemon on; master_process on; -s reload
TimeoutStopSec=5
KillMode=mixed

[Install]
WantedBy=multi-user.target

Що це означає: Невдача не в довгоживучому демонові; вона в кроці валідації. Також тут немає EnvironmentFile=, отже прихованих змінних менше.

Рішення: Запустіть точну передстартову команду вручну, щоб відтворити проблему; якщо вона падає — виправляйте конфіг. Якщо проходить — причина в оточенні (права, AppArmor, шляхи include).

Завдання 5: Запустіть тест конфігурації демона вручну (та сама команда)

cr0x@server:~$ sudo /usr/sbin/nginx -t -q -g "daemon on; master_process on;"
nginx: [emerg] unexpected "}" in /etc/nginx/sites-enabled/app.conf:57

Що це означає: Це детерміністична помилка парсингу конфігурації. Жодної містики systemd.

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

Завдання 6: Знайдіть проблемний рядок і перевірте структуру include

cr0x@server:~$ nl -ba /etc/nginx/sites-enabled/app.conf | sed -n '45,70p'
    45  server {
    46      listen 443 ssl;
    47      server_name app.example.internal;
    48      include /etc/nginx/snippets/tls.conf;
    49
    50      location / {
    51          proxy_pass http://127.0.0.1:8080;
    52          proxy_set_header Host $host;
    53      }
    54
    55  }   # end server
    56
    57  }

Що це означає: На рядку 57 зайва закриваюча дужка.

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

Завдання 7: Перевірте ще раз, потім перезапустіть (не пропускайте крок валідації)

cr0x@server:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Що це означає: Тепер безпечно перезапускати.

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

cr0x@server:~$ sudo systemctl restart nginx.service
cr0x@server:~$ systemctl is-active nginx.service
active

Що це означає: Служба запущена.

Рішення: Перевірте, що вона обслуговує трафік (локальна перевірка здоров’я) і коректно закрийте інцидент.

Завдання 8: Коли статус нічого не каже, покажіть повні логи з фільтром за пріоритетом

cr0x@server:~$ journalctl -u nginx.service -b -p warning --no-pager
Dec 30 10:14:03 server nginx[21984]: nginx: [emerg] unexpected "}" in /etc/nginx/sites-enabled/app.conf:57

Що це означає: Ви відфільтрували до warning і вище, тож не читаєте «Started…» шум.

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

Завдання 9: Підтвердьте, які файли конфігурації змінювалися недавно (зловіть реального винуватця)

cr0x@server:~$ sudo find /etc/nginx -type f -printf '%TY-%Tm-%Td %TH:%TM %p\n' | sort | tail -n 8
2025-12-30 10:12 /etc/nginx/sites-enabled/app.conf
2025-12-29 18:41 /etc/nginx/nginx.conf
2025-12-10 09:03 /etc/nginx/snippets/tls.conf
2025-11-21 15:22 /etc/nginx/mime.types

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

Рішення: Якщо помилка посилається на файл include, перевірте mtime і для нього. «Я змінив лише один рядок» рідко буває повною правдою.

Завдання 10: Якщо це не синтаксис, перевірте permission denied (класика після «загартування»)

cr0x@server:~$ journalctl -u nginx.service -b --no-pager -n 30
Dec 30 10:20:11 server nginx[22310]: nginx: [emerg] open() "/etc/nginx/snippets/tls.conf" failed (13: Permission denied)
Dec 30 10:20:11 server systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE
cr0x@server:~$ namei -l /etc/nginx/snippets/tls.conf
f: /etc/nginx/snippets/tls.conf
drwxr-xr-x root root /
drwxr-xr-x root root etc
drwxr-xr-x root root nginx
drwx------ root root snippets
-rw------- root root tls.conf

Що це означає: Права каталогу заважають nginx (який працює як www-data після старту) або його передстартовій перевірці прочитати включення.

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

Завдання 11: Підтвердьте користувача виконання і sandboxing служби

cr0x@server:~$ systemctl show nginx.service -p User -p Group -p DynamicUser -p ProtectSystem -p ReadWritePaths
User=
Group=
DynamicUser=no
ProtectSystem=no
ReadWritePaths=

Що це означає: Цей юніт не використовує директиви sandboxing systemd. Якщо ви бачите ProtectSystem=strict або жорсткі ReadWritePaths, читання/запис конфігів може бути заблоковане.

Рішення: Якщо sandboxing увімкнено, налаштуйте його під потреби демона, а не вимикайте сліпо. Додайте явні ReadOnlyPaths/ReadWritePaths в оверрайді.

Завдання 12: Інтерпретуйте причини відмови з перспективи systemd (коди виходу і сигнали)

cr0x@server:~$ systemctl show nginx.service -p ExecMainStatus -p ExecMainCode -p Result
ExecMainStatus=1
ExecMainCode=exited
Result=exit-code

Що це означає: Процес завершився нормально з кодом 1. Не SIGKILL, не OOM, не таймаут.

Рішення: Зосередьтеся на конфігурації, параметрах і правах. Якщо бачите ExecMainCode=killed або Result=timeout, це інша гілка діагностики.

Завдання 13: Якщо служба флапає, зупиніть цикл перезапусків, поки читаєте журнали

cr0x@server:~$ sudo systemctl reset-failed nginx.service
cr0x@server:~$ sudo systemctl stop nginx.service
cr0x@server:~$ systemctl status nginx.service --no-pager
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: inactive (dead)

Що це означає: Ви не дозволяєте systemd безупинно перезапускати службу, поки налагоджуєте. Це також робить журнал читабельнішим.

Рішення: Робіть це, коли Restart=always створює шум і навантаження. Потім перезапустіть свідомо, коли матимете виправлення.

Завдання 14: Безпечно порівняйте зміни конфігурації з метаданими dpkg (перевірка пакування)

cr0x@server:~$ dpkg -S /etc/nginx/nginx.conf
nginx-common: /etc/nginx/nginx.conf
cr0x@server:~$ sudo ls -l /etc/nginx/nginx.conf*
-rw-r--r-- 1 root root 1492 Dec 29 18:41 /etc/nginx/nginx.conf
-rw-r--r-- 1 root root 1479 Nov 21 15:22 /etc/nginx/nginx.conf.dpkg-dist

Що це означає: Можливо, у вас є дистрибутивний новий файл за замовчуванням або невирішений мердж. Це може взаємодіяти з вашою зміною.

Рішення: Якщо служба почала падати після оновлення плюс редагування конфігу, перевірте .dpkg-dist/.dpkg-old і свідомо узгодьте.

Завдання 15: Коли журнали відсутні, підтвердіть збереження journald і обмеження швидкості

cr0x@server:~$ sudo grep -E '^(Storage|SystemMaxUse|RateLimitIntervalSec|RateLimitBurst)=' /etc/systemd/journald.conf | sed '/^#/d;/^$/d'
Storage=auto
RateLimitIntervalSec=30s
RateLimitBurst=1000
cr0x@server:~$ journalctl --disk-usage
Archived and active journals take up 384.0M in the file system.

Що це означає: Якщо Storage=volatile, ви втрачаєте журнали при перезавантаженні. Якщо обмеження швидкості низьке, ви можете пропустити повторювані помилки.

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

Жарт №2 (короткий, по темі): «Вчора працювало» — це не доказ; це свідчення свідка з жахливою пам’яттю.

Три корпоративні міні-історії (і чому вони повчають)

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

Команда мала флот Debian-хостів з веб- і API-службами. Одного дня вийшла рутинна зміна конфігу: оновлення TLS-шифрів, уніфікація в середовищах. Хтось перезапустив nginx на канарці. Він впав. Вони запустили nginx -t вручну; тест пройшов. Припущення виникло миттєво: «systemd зламався на цьому хості».

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

Виправлення було смішно простим: systemd-юніт використовував інший шлях конфігурації через файл середовища. Не навмисно — просто історично. Ручний nginx -t тестував /etc/nginx/nginx.conf; systemd тестував /etc/nginx/nginx-canary.conf. Файл канарки включав шлях, якого не було на цьому хості.

Урок не в тому, щоб «не використовувати environment files». Він у тому, щоб ніколи не припускати, що ваше ручне відтворення збігається з тим, що робить менеджер служб. Витягніть точні ExecStartPre/ExecStart з systemctl cat і запустіть цю команду. Якщо є файл середовища — виведіть його і припиніть гадати.

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

Платформна група вирішила «прискорити деплои», замінивши restart на reload, коли можливо. Reload дешевший: менше розривів з’єднань, менше транзієнтних помилок. Добре складений намір. Потім вони застосували це масово скриптом.

Одна служба, брокер повідомлень, приймала сигнал reload, але застосовувала конфіг частково. Для деяких налаштувань потрібен був повний рестарт, але reload повертав успіх. З часом з’явився дрейф конфігів: запущений конфіг не відповідав тому, що на диску, і люди перестали довіряти обом.

Зрештою зміна конфігурації додала параметр, який упав би при чистому старті. Reload нічого не зробив, сказав «OK», і система працювала зі старими налаштуваннями. Через дні відбулося звичайне перезавантаження хоста. Тепер службі довелось робити cold start, вона прочитала поганий конфіг і відмовилася запускатися. Відмова сталася під час maintenance window — там, де зустрічаються ваші майбутні помилки.

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

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

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

Люди скаржилися: «це гальмує». «Я можу протестувати в голові». Звично. Але прийшов день, коли старший інженер редагував конфіг вживу під час інциденту — бо служба погано поводилась і потрібна була швидка міра. Правка мала тонку помилку у лапках. Наступний перезапуск убив би службу повністю.

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

Урок: нудна практика — не репозиторій. Це автоматична валідація плюс передбачуваний шлях відкату. Ці дві речі не дають маленьким помилкам перерости в outage.

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

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

1) Симптом: systemctl status показує «failed (Result: exit-code)» без корисної помилки

Корінна причина: Ви бачите лише підсумок. Змістовний рядок розташований раніше або обрізаний.

Виправлення: Запитуйте журнал напряму і розширте фрагмент.

cr0x@server:~$ journalctl -u myservice.service -b --no-pager -n 200
...look for the first real error line...

2) Симптом: служба падає миттєво після перезапуску; логи згадують ExecStartPre

Корінна причина: Передстартова валідація не пройшла (синтаксис, відсутній include, недійсна директива).

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

cr0x@server:~$ systemctl cat myservice.service | sed -n '/ExecStartPre/p'
ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on;

3) Симптом: тест конфігу проходить вручну, але падає під systemd

Корінна причина: Інший шлях до конфігу, інший користувач, інше оточення або обмеження sandbox.

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

cr0x@server:~$ systemctl show myservice.service -p Environment -p EnvironmentFiles
Environment=
EnvironmentFiles=/etc/default/myservice (ignore_errors=no)

4) Симптом: «Permission denied» на includes, сертифікатах, сокетах, PID-файлах

Корінна причина: Зміни в жорсткості прав (chmod/chown), новий шлях з обмеженими правами або невідповідність користувача служби.

Виправлення: Простежте права шляху з namei -l; виправте біт виконання каталогів і читання файлів.

5) Симптом: «Address already in use» після зміни конфігурації

Корінна причина: Ви змінили порт/прив’язку; інша служба вже займає його; або старий екземпляр не зупинився коректно.

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

cr0x@server:~$ sudo ss -ltnp | grep ':443 '
LISTEN 0      511          0.0.0.0:443        0.0.0.0:*    users:(("haproxy",pid=1203,fd=7))

6) Симптом: юніт показує Result=timeout

Корінна причина: Демон зависає під час старту (чекає DNS, сховища, міграцій) або таймаут systemd занадто жорсткий для холодного старту.

Виправлення: Читайте журнали навколо зависання, потім змінюйте TimeoutStartSec лише якщо стартова робота легітимна і обмежена в часі.

7) Симптом: після зміни конфігу служба «запускається», але не працює

Корінна причина: Ви використали reload і припустили, що це застосувало все; або конфіг прийнятий, але семантично неправильний.

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

8) Симптом: журнал не має записів для юніта

Корінна причина: Служба логувала у файл (або stdout перенаправлено), journald — volatile, або юніт ніколи не виконався через проблему залежностей.

Виправлення: Перевірте systemctl list-dependencies і налаштування journald; інспектуйте файлові логи, якщо служба їх використовує.

Чек-листи / поетапний план (безпечні виправлення і відкат)

Крок за кроком: діагностика і виправлення без стресу системи

  1. Зупиніть цикл перезапусків, якщо є. Якщо юніт флапає, поставте паузу, щоб читати стабільні журнали.
  2. Прочитайте підсумок юніта. Визначте, чи впав ExecStartPre, чи помер основний процес.
  3. Запитуйте journald по юніту і boot. Не починайте з глобальних логів.
  4. Витягніть точні команди старту. Читайте systemctl cat і перевіряйте drop-in файли.
  5. Запустіть тест конфігу служби вручну. Ті самі аргументи, той самий шлях конфігу.
  6. Виправте найменшу річ, яка дозволяє стартувати. Уникайте рефакторингу під час відповіді на інцидент.
  7. Перезапустіть один раз, потім перевірте на рівні застосунку. «active (running)» — це не те саме, що «обслуговує».
  8. Запишіть корінну помилку. Вставте точний рядок помилки в нотатку інциденту. Майбутній ви подякує сьогоднішньому вам.

План відкату: коли ви не впевнені, що виправлення правильне

Якщо ви не можете довести виправлення швидко — відкатуйте. Не «ітеруйте в продакшні», поки пейджер голосно співає.

  1. Збережіть зламаний конфіг. Скопіюйте його з часовою міткою для подальшого аналізу.
  2. Відновіть останній відомо-робочий з репо чи бекапу.
  3. Проведіть валідацію конфігу. Завжди запускайте тестовий режим демона.
  4. Перезапустіть службу і перевірте.
  5. Лише після відновлення: налагодьте зламану зміну в контрольованому середовищі.
cr0x@server:~$ sudo cp -a /etc/nginx/sites-enabled/app.conf /root/app.conf.broken.$(date +%F-%H%M%S)
cr0x@server:~$ sudo cp -a /root/rollback/app.conf /etc/nginx/sites-enabled/app.conf
cr0x@server:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
cr0x@server:~$ sudo systemctl restart nginx.service
cr0x@server:~$ systemctl is-active nginx.service
active

Коли потрібно тримати часткову службу в робочому стані (контроль збитків)

Іноді ви не можете повністю виправити негайно, але можете зменшити вплив:

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

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

FAQ

1) Чому systemctl status недостатньо?

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

2) Яка найкраща команда journalctl для цієї ситуації?

Зазвичай:

cr0x@server:~$ journalctl -u myservice.service -b --no-pager -n 200

Якщо це шумить, додайте -p warning або обмежте час через --since.

3) Як зрозуміти, чи це проблема конфігурації чи runtime?

Шукайте, чи не впав ExecStartPre (конфіг/валідація) або чи основний процес спочатку стартував, а потім помер (runtime). systemctl status зазвичай каже, який процес впав.

4) Чому іноді запуск тесту конфігу вручну проходить, а під systemd — ні?

Інше оточення. systemd може використовувати файл середовища, інший робочий каталог, sandboxing або інший користувацький контекст. Завжди відтворюйте, використовуючи точну командну лінію з юніта.

5) Як побачити drop-in оверрайди, які можуть змінювати поведінку?

cr0x@server:~$ systemctl status myservice.service --no-pager
...look for "Drop-In:" lines...
cr0x@server:~$ systemctl cat myservice.service
...includes /etc/systemd/system/myservice.service.d/*.conf if present...

6) Коли варто використовувати reload замість restart?

Тільки коли служба документує, що reload застосовує зроблені зміни, і у вас є крок валідації. Якщо невпевнені — перезапустіть у безпечне вікно або після зливання трафіку.

7) Що робити, якщо для юніта взагалі немає журналів?

Тоді або юніт не виконався, journald не зберігає журнали, або логи йдуть в інше місце (наприклад, /var/log/*). Перевірте залежності і налаштування journald, огляньте файлові логи, якщо служба їх налаштовує.

8) Як швидко зрозуміти, чи це проблема з правами доступу?

Шукайте «Permission denied» у журналі, потім простежте шлях файлу через namei -l. Проблеми з правами часто виникають від відсутності бітів виконання каталогів або змін загартування, які забули про користувача служби.

9) Який найнадійніший спосіб запобігти цьому класу відмов?

Автоматизуйте валідацію конфігу (режим тесту демона) перед restart/reload, тримайте конфіги в контролі версій і зробіть відкат тривіальним. Мета — зловити зламаний рядок раніше, ніж його побачить systemd.

Висновок: подальші кроки, щоб уникнути повторних інцидентів

Служба Debian 13, що падає після зміни конфігурації, рідко є загадкою. Зазвичай це один точний рядок помилки, який ви не витягли чисто. Прочитайте юніт, щоб дізнатися, що виконувалося. Прочитайте журнал, звужений до юніта, щоб дізнатися, що впало. Потім вручну протестуйте конфіг, використовуючи ту саму команду, яку використовує systemd.

Практичні подальші кроки:

  • Додайте переддеплойну перевірку конфігу для кожної служби, яка її підтримує.
  • Навчіть команду ставитися до systemctl status як до підказки, а не як до діагнозу.
  • Зробіть відкат першокласною операцією (копіювати, відновити, валідувати, перезапустити).
  • Стандартизуйте короткий «швидкий план діагностики» і тримайте його поруч із ротацією пейджера.

Зробіть це — і наступного разу, коли служба відмовиться запускатися, ви витратите час на виправлення реальної проблеми, а не на сперечання з екраном підсумку.