Никита Кузнецов про невидиму інженерію, через яку хороший продукт одного дня може просто не відкритися

IT-інженер Никита Кузнецов — про невидиму інфраструктуру, від якої залежить стабільність цифрового продукту.

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

IT-інженер Никита Кузнецов вважає саме цю невидиму частину продукту однією з найбільш недооцінених зон сучасної розробки. Можна написати якісний код, провести тестування, налаштувати сервери й місяцями без проблем обслуговувати користувачів. А потім один старий DNS-запис, прострочений сертифікат або забутий технічний обліковий запис перекреслює все інше. Застосунок фізично продовжує працювати, сервери справні, база відповідає, але користувач уже не може потрапити всередину.

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

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

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

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

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

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

За словами Никиты Кузнецова, тут корисно відмовитися від звички вважати інфраструктуру другорядним технічним шаром:

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

У нової функції майже завжди є зрозумілий власник. Є завдання, виконавець, термін, тестування та реліз. З інфраструктурними залежностями все часто набагато менш визначено. Хто відповідає за домен? Хто контролює сертифікати? Хто знає про всі сервісні облікові записи? Хто перевіряє, чи залишилися права в підрядника, який завершив роботу пів року тому?

Поки все функціонує, відсутність відповіді на ці питання майже непомітна. Аварія миттєво робить їх головними.

Никита Кузнецов пояснює, чому DNS та інші зовнішні залежності здатні зробити недоступним справно працюючий сервіс.
Никита Кузнецов пояснює, чому DNS та інші зовнішні залежності здатні зробити недоступним справно працюючий сервіс.

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

І це цілком може бути правдою.

Застосунок не змінювався. Сервери працюють. База відповідає. Останній реліз був кілька днів тому. Усередині інфраструктури сервіс навіть може відкриватися за прямою адресою. Але звичайний користувач вводить звичне доменне ім’я й не отримує працюючого сайту.

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

Технічно DNS пов’язує зрозуміле людині доменне ім’я з інформацією, необхідною для знаходження потрібного ресурсу. У ланцюжку беруть участь рекурсивні резолвери, кешування та авторитетні DNS-сервери. Тому неправильний запис або невдале оновлення може поводитися не так очевидно, як звичайна помилка застосунку. Різні користувачі певний час можуть отримувати різні результати через кеші. Параметр TTL визначає, як довго DNS-запис може зберігатися в кеші: що він більший, то довше старе значення потенційно продовжує використовуватися після зміни.

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

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

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

Головна проблема тут навіть не в конкретній технології. Вона виникає тоді, коли в критичного об’єкта немає власника, історії змін і зрозумілого способу перевірки.

Сертифікати Никита Кузнецов наводить як особливо показовий приклад. На відміну від багатьох програмних помилок, строк дії сертифіката не є несподіванкою. Дата відома заздалегідь.

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

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

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

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

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

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

Інфраструктурна дрібниця перетворюється на проблему бізнесу.

Ще серйозніша ситуація з правами доступу. Тут наслідки можуть виходити далеко за межі звичайного простою.

«Найстрашніший обліковий запис — не хакерський. А той, який завели “на кілька днів” і забули вимкнути».

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

Реліз проходить. З’являються нові завдання. Через кілька тижнів про тимчасове рішення вже ніхто не згадує. Через рік технічний ключ усе ще існує.

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

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

Те саме стосується людей.

«Незамінна людина з повним доступом — це не гордість команди. Це діра, яку назвали компетенцією».

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

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

Никита Кузнецов — про зв'язки між сервісами, тайм-аути та повторні запити, які можуть перетворити локальний збій на системну аварію.
Никита Кузнецов — про зв’язки між сервісами, тайм-аути та повторні запити, які можуть перетворити локальний збій на системну аварію.

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

Никита Кузнецов пропонує дивитися насамперед на з’єднання між ними.

«Усі малюють квадратики. Майже ніхто не малює дороги. А падає зазвичай дорога».

Сенс тут у поведінці сервісів під час проблем у сусідів. Скільки один сервіс готовий чекати відповіді іншого? Що відбувається після тайм-ауту? Чи повторюється запит? Скільки разів? Через який проміжок? Чи можна взагалі безпечно повторювати цю операцію?

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

У результаті система сама збільшує тиск на компонент, який уже має проблеми.

Це реальний клас відмов, відомий як retry storm — шторм повторних запитів. Неконтрольовані повтори під високим навантаженням здатні додатково перевантажити мережу та сервіс і тим самим знизити доступність. Серед стандартних способів захисту — обмеження кількості повторів, exponential backoff, випадкова затримка jitter і circuit breaker, який тимчасово припиняє звернення до явно проблемної залежності.

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

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

Так невелика проблема одного сервісу або залишається локальною, або стає аварією всієї системи.

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

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

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

Звичайний лог повідомляє, що в певний час конкретний компонент записав конкретну подію. Але в розподіленій системі цього недостатньо. Один користувацький запит може пройти через API-шлюз, авторизацію, кілька внутрішніх сервісів і базу даних. Якщо кожен із них веде окремий журнал без спільного зв’язку, інженерам доводиться вручну збирати історію за часом і непрямими ознаками.

Сучасна observability тому будується не лише навколо логів. Зазвичай використовуються кілька типів телеметрії — traces, metrics і logs. Distributed trace дозволяє простежити шлях одного запиту через різні компоненти, а окремі spans показують конкретні етапи цього шляху. Це дає змогу побачити не просто повідомлення про помилку, а місце й момент, де користувацька операція почала ламатися.

Особливо корисна кореляція логів із trace ID та span ID: записи різних сервісів можна пов’язати з однією конкретною операцією. Без такого контексту журнали розподілених компонентів часто залишаються розрізненим набором подій.

Для Никиты Кузнецова це принципова різниця між «ми збираємо дані» та «ми розуміємо систему».

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

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

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

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

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

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

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

Причина, на думку Никиты Кузнецова, доволі прозаїчна: результат профілактичної інженерної роботи погано помітний.

Нова функція існує фізично — її можна відкрити й показати. Новий дизайн можна поставити в презентацію. Зростання швидкості можна виразити цифрою.

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

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

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

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

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

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

Найважливіше тут — прибрати залежність від пам’яті.

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

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

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

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

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

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

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

Більше від автора

Росія вчетверте за місяць атакувала Запоріжсталь у Запоріжжі

DFC затвердила інвестиції понад 8 млрд зокрема для України