Як пріоритети pod'ів у Kubernetes стали причиною простою у Grafana Labs

Прим. перев.: Представляємо вашій увазі технічні подробиці про причини недавнього простою в роботі хмарного сервісу, який обслуговує творець Grafana. Це класичний приклад того, як нова і, здавалося б, винятково корисна можливість, покликана покращити якість інфраструктури… може зашкодити, якщо не передбачити численних нюансів її застосування у реаліях production. Чудово, коли з'являються такі матеріали, що дають змогу навчатися не лише на своїх помилках. Подробиці — у перекладі цього тексту від віце-президента з Grafana Labs.

Як пріоритети pod'ів у Kubernetes стали причиною простою у Grafana Labs

У п'ятницю, 19 липня, сервіс Hosted Prometheus у Grafana Cloud перестав функціонувати приблизно на 30 хвилин. Приношу вибачення всім клієнтам, які постраждали від збою. Наше завдання – надавати потрібні інструменти для моніторингу, і ми розуміємо, що їхня недоступність ускладнює ваше життя. Ми дуже серйозно ставимося до цього інциденту. У цій статті пояснюється, що сталося, як ми на це відреагували і що робимо для того, щоб подібне більше не повторювалося.

Передісторія

Сервіс Grafana Cloud Hosted Prometheus заснований на Кора — проект CNCF щодо створення горизонтально масштабованого, високодоступного, мультитенантного (multi-tenant) сервісу Prometheus. Архітектура Cortex складається з набору окремих мікросервісів, кожен із яких виконує свою функцію: реплікацію, зберігання, запити тощо. Cortex активно розробляється, у нього постійно з'являються нові можливості та підвищується продуктивність. Ми регулярно деплоїмо нові релізи Cortex в кластери, щоб клієнти могли скористатися цими можливостями - благо Cortex вміє оновлюватися без простоїв.

Для безпростих оновлень сервіс Ingester Cortex вимагає додаткову репліку Ingester під час процесу оновлення. (Прим. перев.: Ingester - Базовий компонент Cortex'а. Його завдання - збирати постійний потік sample'ів, групувати їх у chunk'і Prometheus і зберігати в базі даних на кшталт DynamoDB, BigTable або Cassandra.) Це дозволяє старим Ingester'ам пересилати поточні дані новим Ingester'ам. Варто зазначити, що Ingester'и вимогливі до ресурсів. Для роботи необхідно мати по 4 ядра і 15 Гб пам'яті на pod, тобто. 25% процесорної потужності та пам'яті базової машини у разі наших кластерів Kubernetes. Загалом у нас зазвичай набагато більше невикористаних ресурсів у кластері, ніж 4 ядра та 15 Гб пам'яті, тому ми можемо легко запускати ці додаткові Ingester'и під час оновлень.

Однак часто буває так, що під час нормальної роботи на жодній з машин немає цих 25% незатребуваних ресурсів. Та ми й не прагнемо: CPU та пам'ять завжди стануть у нагоді для інших процесів. Для вирішення цієї проблеми ми вирішили скористатися Kubernetes Pod Priorities. Ідея в тому, щоб привласнювати Ingester'ам вищий пріоритет, ніж іншим (stateless) мікросервісам. Коли нам потрібно запустити додатковий (N+1) Ingester, ми тимчасово витісняємо інші менші pod'и. Ці pod'и переносяться у вільні ресурси на інших машинах, залишаючи досить велику «дірку» для запуску додаткового Ingester'а.

У четвер, 18 липня, ми розгорнули чотири нові рівні пріоритетів у своїх кластерах: критичний, високий, середній и низький. Вони тестувалися на внутрішньому кластері без клієнтського трафіку приблизно на тиждень. За замовчуванням pod'и без заданого пріоритету отримували середній пріоритет, для Ingester'ів був встановлений клас з високим пріоритетом. Критичний був зарезервований для моніторингу (Prometheus, Alertmanager, node-exporter, kube-state-metrics тощо). Наш конфіг - відкритий, і подивитися PR можна тут.

аварія

У п'ятницю, 19 липня, один із інженерів запустив новий виділений кластер Cortex для великого клієнта. Конфіг для цього кластера не включав нові пріоритети pod'ів, тому всім новим pod'ам присвоювався пріоритет за умовчанням. середній.

У кластері Kubernetes не вистачило ресурсів для нового кластера Cortex, а існуючий production-кластер Cortex не був оновлений (Ingester'и залишилися без високого пріоритету). Оскільки Ingester'и нового кластера за замовчуванням мали середній пріоритет, а існуючі в production pod'и працювали взагалі без пріоритету, Ingester'и нового кластера витіснили Ingester з існуючого production-кластера Cortex.

ReplicaSet для витісненого Ingester'а в production-кластері виявив витіснений pod і створив новий для підтримки заданої кількості копій. Новому pod'у за умовчанням було присвоєно середній Пріоритет, і черговий «старий» Ingester у production втратив ресурси. Результатом став лавиноподібний процес, який привів до витіснення всіх pod'ів з Ingester для production-кластерів Cortex'а.

Ingester'и зберігають стан (stateful) та зберігають дані за попередні 12 годин. Це дозволяє нам ефективніше стискати їх перед записом у довгострокове сховище. Для цього Cortex проводить шардинг даних серій, використовуючи розподілену хеш-таблицю (Distributed Hash Table, DHT), і реплікує кожну серію на три Ingester'а за допомогою кворумної узгодженості в стилі Dynamo. Cortex не пише дані в Ingester'и, які відключаються. Таким чином, коли велика кількість Ingester'ів залишають DHT, Cortex не може забезпечити достатню реплікацію записів, і вони «падають».

Виявлення та усунення

Нові повідомлення Prometheus на основі «бюджету помилок» (error-budget-based - Подробиці з'являться в майбутній статті) стали бити на сполох через 4 хвилини з моменту початку відключення. Протягом наступних приблизно п'яти хвилин ми провели діагностику і наростили кластер Kubernetes, що лежить нижче, для розміщення як нового, так і існуючих production-кластерів.

Ще через п'ять хвилин старі Ingester'и успішно записали свої дані, а нові запустилися, і кластери Cortex знову стали доступними.

Ще 10 хвилин пішло на діагностику та виправлення out-of-memory (OOM) помилок від зворотних проксі-серверів автентифікації, розташованих перед Cortex. ООМ-помилки були викликані десятикратним зростанням QPS (як ми вважаємо, через надмірно агресивні запити з серверів Prometheus клієнта).

Наслідки

Загальна тривалість простою становила 26 хвилин. Дані були втрачені. Ingester'и успішно завантажили всі in-memory-дані в довготривале сховище. Під час відключення Prometheus-сервери клієнтів зберігали у буфері віддалені (дистанційний) записи за допомогою нового API remote_write на основі WAL (за авторством Callum Styan з Grafana Labs) і повторили невдалі записи після збою.

Як пріоритети pod'ів у Kubernetes стали причиною простою у Grafana Labs
Операції запису production-кластера

Висновки

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

Озираючись назад, слід визнати, що нам не слід задавати за замовчуванням середній пріоритет, поки всі Ingester'и у production не отримали високий пріоритет. Крім того, слід заздалегідь подбати про них високому пріоритет. Тепер все виправлено. Сподіваємося, що наш досвід допоможе іншим організаціям, які розглядають можливість використання пріоритетів pod'ів у Kubernetes.

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

Нарешті, ми автоматизуємо зміну розміру у зворотного проксі-сервера аутентифікації для запобігання ООМ при перевантаженні, свідками чого ми стали, і проаналізуємо параметри Prometheus за умовчанням, пов'язані з відкатом і масштабуванням, для попередження таких проблем.

Пережитий збій мав і деякі позитивні наслідки: отримавши у розпорядження необхідні ресурси, Cortex автоматично поновився без додаткового втручання. Ми також отримали цінний досвід роботи з Графана Локі — нашою новою системою агрегації ліг, — яка допомогла переконатися, що всі Ingester'и належним чином повелися під час та після збою.

PS від перекладача

Читайте також у нашому блозі:

Джерело: habr.com

Додати коментар або відгук