Аналітичне інтернет-видання

Никита Кузнецов говорит о скрытой инженерии, из-за которой качественный продукт может в любой момент оказаться недоступным - Завтра.UA.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

DNS – це як вивіска на дверях, яку ніхто не помічає, поки не виникне проблема з доступом. Тоді вся команда збирається в коридорі і починає з'ясовувати, хто ж відповідальний за помилку в налаштуваннях.

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

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

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

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

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

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

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

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

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

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

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

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

Інфраструктурні деталі стають серйозною перешкодою для бізнесу.

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

"Найбільш небезпечний обліковий запис — це не той, який зламали хакери, а той, що був створений 'на короткий термін' і залишився активним."

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

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

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

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

Те ж саме можна сказати і про людей.

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

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

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

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

Никита Кузнецов радить зосередитися, перш за все, на зв'язках між цими елементами.

"Всі створюють квадратики. Лише одиниці малюють шляхи. А зазвичай, саме шлях і падає."

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

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

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

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

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

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

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

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

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

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

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

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

Для Никиты Кузнецова важливою є суттєва відмінність між висловами "ми збираємо інформацію" і "ми осмислюємо систему".

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Головне, що потрібно зробити, це усунути залежність від пам'яті.

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

"Коли у випадку гострої нудьги відсутні власник, термін перевірки та сигнал для аварійного реагування, це вже не інфраструктура. Це просто надія."

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

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

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

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

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

Читайте також