Як ми досягли нуля помилок 502 під час розгортань
Кожного разу, коли ми розгортали бекенд Mythex, кілька запитів падало з 502 Bad Gateway. Небагато, але 502 під час розгортання означає, що чиєсь клацання нічого не зробило або збірка, за якою хтось стежив, на мить зупинилася. Ми простежили це до двох окремих проблем: вотчдога із самовідновленням, який воював із нашими розгортаннями, і проміжку між моментом, коли сервер перестає слухати, і моментом, коли балансувальник перестає надсилати йому трафік. Після виправлень у трьох поспіль виробничих розгортаннях кількість зменшилася з 10 до 6, а потім до 0.
Налаштування
Наш API та оркестратор працюють як контейнери на невеликій групі серверів за балансувальником навантаження AWS Application Load Balancer (ALB). Розгортання оновлює один сервер за раз: воно зупиняє старі контейнери, запускає нові і переходить далі. Вотчдог на кожному сервері перевіряє, що контейнери працюють, і перезапускає будь-який, що не працює. Він існує для того, щоб процес, що впав о третій ночі, повертався без того, щоб хтось прокидався.
Обидва механізми розумні самі по собі. Разом вони не працювали.
Проблема 1: вотчдог змагався з розгортанням
Вотчдог працював раз на хвилину і «лікував» після однієї невдалої перевірки здоров'я. А розгортання навмисно зупиняє сервіси на до 150 секунд, і під час цього перевірка здоров'я має не проходити. Тож вотчдог спрацьовував під час кожного розгортання.
Здебільшого він програвав змагання без наслідків. Одного разу виграв: переписав compose-файл (опис того, які контейнери мають працювати) посеред розгортання і залишив сервер без жодного контейнера шлюзу, а завдання розгортання повідомило про успіх.
Виправлення:
- Одне блокування. Розгортання тепер бере те саме файлове блокування, що й скрипт початкового налаштування сервера, тож вотчдог і розгортання ніколи не виконуються одночасно.
- Чотири попередження, а не одне. Вотчдогу потрібні чотири поспіль невдалі перевірки, перш ніж він діятиме. Сервіс, що дренується, більше не виглядає мертвим.
- Одне визначення. Compose-файл тепер має рівно одне джерело, опубліковане на серверах, замість копій, які могли розійтися.
- Перевіряйте, а не довіряйте. Завдання розгортання більше не проходить лише тому, що команда повернула код успіху. Воно перевіряє API, оркестратор, шлюз і воркер публікації на кожному сервері, перш ніж може завершитися успішно.
Проблема 2: сервер пішов раніше за трафік
Друга проблема була тихішою. Коли розгортання дренує сервер, сервер починає відповідати на свою кінцеву точку здоров'я кодом 503, щоб балансувальник вивів його з ротації. Наш сервер робив це 5 секунд, а потім вимикався.
Але ALB перевіряв здоров'я лише зрідка і потребував кількох невдалих перевірок поспіль, перш ніж перестати направляти трафік на ціль, до приблизно 60 секунд від початку до кінця. Тож протягом близько 55 секунд балансувальник продовжував надсилати справжні запити серверу, який уже перестав слухати. Це були наші 502.
Виправлення полягало в тому, щоб узгодити два графіки:
- Цільова група тепер перевіряє кожні 5 секунд і позначає ціль несправною після 2 невдач, тобто близько 10 секунд на виявлення.
- Дренування продовжує обслуговувати справжній трафік ще 15 секунд після того, як починає провалювати перевірки здоров'я, тож завжди є перекриття.
- Ці налаштування живуть у скрипті, що надає інфраструктуру, а не в консолі, яку хтось колись клікнув.
Щоб числа трималися одне за одного
Такі тайм-аути залежать одне від одного. Дренування має тривати довше, ніж балансувальнику потрібно, щоб помітити проблему; дренування разом із вимкненням має завершитися до примусового знищення контейнера. Легко, щоб хтось через півроку змінив одне число і тихо повернув баг.
Тому ми записали ці залежності як тести: дренування має бути довшим за інтервал перевірки здоров'я, помножений на поріг невдач; час до повторного визнання здоров'я має лишатися коротким; увесь дренаж має вміщуватися в пільговий період зупинки контейнера. Змініть одне число без інших, і CI впаде. Ми також провели мутаційне тестування самих тестів: змінювали кожне число на хибне значення й перевіряли, що тест це ловить.
Вимірювати розгортання легко помилитися
Наш перший вимір казав, що чисте розгортання зламане приблизно на 3%. Це було неправдою. Ми рахували кожну відповідь, відмінну від 200, а 503 з кінцевої точки здоров'я під час дренування не є помилкою. Це дренування робить свою роботу, повідомляючи балансувальнику не обирати цей сервер, поки він ще обслуговує справжні запити.
Те, що відчуває користувач, це 502: запит ні до чого не дійшов. Рахуючи лише їх, ми отримали справжню картину: 10, потім 6, потім 0 у трьох виробничих розгортаннях.
Що ми з цього взяли
- Самовідновлення має знати про заплановані простої. Вотчдог, який не відрізняє розгортання від збою, рано чи пізно буде воювати з розгортанням.
- Дренування це рукостискання. Сервер має обслуговувати, доки балансувальник не перестане надсилати трафік, і цей час задається на балансувальнику, а не на сервері.
- Кодуйте інваріанти в тестах. Операційні числа з часом розходяться. Тести, що пов'язують їх одне з одним, цього не роблять.
- Вирішіть, що вимірюєте, до вимірювання. «Помилки» були неправильною метрикою; «запити, що ні до чого не дійшли» були правильною.
Якщо ви публікуєте застосунок на Mythex, нічого з цього вам не видно, і в цьому суть. Пов'язане: як ваш опублікований застосунок залишається працездатним під час повторної публікації.