Проблема
Утро, все как обычно. Разработчики пишут: «Слушай, что-то с Sentry. Вроде работает, но issues не приходят». Проверяю статус, инстанс жив, но все верно: логов нет, issues не создаются.
Первый рефлекс, перезапустить. Делаю docker compose down && docker compose up. Через минуту инстанс поднимается, issues идут, разработчики довольны. День-два все нормально.
Потом повторяется. Issues нет. Нужен еще один рестарт.
Через несколько дней такого становится понятно: это не случайное падение. Происходит что-то системное. Перезапускать каждый день не решение.
Первая попытка: базовые метрики
Смотрю на дашборд мониторинга. На сервере только базовые метрики: диск, память, CPU, сеть. Все выглядит нормально, ничего не выделяется. Пользы никакой.
Добавляю детализацию: метрики диска и Redis
Понимаю, что нужна более гранулярная картина. Добавляю:
-
Disk I/O в разрезе устройств (dm-0, dm-1, dm-2, sda, sr0)
-
Load Average
-
Redis: RAM, evicted keys, expired keys, connected clients
Включаю и жду следующего утра.
Первая реальная зацепка
Следующим утром примерно в то же время вижу это на графиках.
Память Redis выстреливает до лимита. Потом начинают вытесняться ключи. Это уже настоящая зацепка: если события обрабатываются нормально, память должна освобождаться. Если она забивается и ключи вытесняются, значит, события где-то застревают.
Они копятся в Redis, потому что их никто не обрабатывает.
Добавляю метрики Kafka
Копаюсь в логах и метриках и нахожу ответ. Snuba-консьюмеры лежат. Они не разбирают события из Kafka. Это все объясняет: события не обрабатываются, не удаляются из Redis, Redis забивается, ключи вытесняются.
Перезапускаю Snuba-консьюмеров, они поднимаются, события начинают разгребаться, все приходит в норму. Разработчики снова видят issues.
Но я понимаю, что завтра это повторится. Что-то стабильно убивает эти консьюмеры. Нужно понять что.
Теперь мне нужен контроль над консьюмерами. Добавляю более детальные метрики по Kafka:
-
Stuck consumers
-
Consumer group members
-
Consumer lag
Заодно вешаю алерты на consumer lag, чтобы ловить проблему сразу.
Иду по следу: Disk I/O
Жду, когда проблема проявится снова. В этот раз смотрю внимательно.
При следующем срабатывании присматриваюсь к графику disk I/O, который добавил раньше. И вот оно, странно что не заметил раньше
Каждое утро примерно в одно и то же время disk I/O упирается в потолок почти на 30 минут подряд. Disk I/O почти 100%, диск полностью загружен.
Это не случайность. Происходит ровно в одно и то же время каждый день.
Тут все складывается. Kafka работает с диском. Когда диск полностью загружен, Kafka не может нормально читать и писать. Консьюмеры начинают ловить таймауты, соединения рвутся, они отваливаются.
Но почему диск так загружен? Активных процессов, которые пишут большие объемы, нет. Ни джоб, ни бэкап-сервисов. Проверяю и ничего подозрительного.
Спрашиваю у админов, что происходит с диском по утрам.
«А, ну да, по утрам делается полный бэкап или снепшот сервера. Занимает диск где-то на полчаса».
Полная картина
Теперь видна вся цепочка событий:
-
Утро: стартует бэкап/снепшот — диск полностью забивается
-
Kafka не может нормально работать — диск занят, I/O заблокирован
-
Snuba-консьюмеры ловят таймауты — соединения рвутся, они отваливаются
-
События остаются в Redis — они удаляются только после успешной обработки
-
Redis начинает забиваться необработанными событиями
-
Redis вытесняет старые ключи, чтобы освободить место
-
Через 30 минут диск освобождается — консьюмеры снова работают, события разгребаются
-
Но часть консьюмеров так и осталась отвалившейся после таймаута, поэтому обработка идёт медленнее
-
Redis продолжает расти — медленнее, чем раньше, но растет. Пока я не перезапущу Snuba-консьюмеров
Бэкап — неизбежная часть продакшен-инфраструктуры. Просто выключить его нельзя, данные критичны. Нужно адаптировать систему так, чтобы она переживала окно бэкапа.
Решение
Было сделано несколько слоев.
Первое: relay на всех сервисах, где еще не успели Relay — это буфер. Если Redis недоступен или перегружен, события копятся локально на relay, а потом отправляются, когда Redis оживет. Защита от потери данных:
https://github.com/getsentry/relay
Второе: увеличил лимит памяти Redis. Вместо того чтобы вытеснять ключи при заполнении памяти, просто дать памяти больше пространства:
...redis: ... command: redis-server --maxmemory 10gb --maxmemory-policy allkeys-lru ...
Политику вытеснения ключей я менять не стал, чтобы в худшем случае Redis не встал совсем. Лучше потерять несколько ключей, чем заблокировать весь сервис.
Третье: healthcheck и авторестарт для Snuba-консьюмеров. Если консьюмер падает, пусть перезапускается сам. Быстро:
snuba-consumer: image: getsentry/snuba:latest healthcheck: test: ["CMD", "curl", "-f", "http://localhost:1218/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s restart: on-failure:5 environment: SNUBA_SETTINGS: docker KAFKA_BROKERS: kafka:9092 REDIS_HOST: redis ...
Результат
После этих изменений всё стабилизировалось. Redis не переполняется благодаря relay и увеличенному лимиту памяти. Snuba-консьюмеры, если и падают, поднимаются сами. Разработчики видят issues так, как и должны.
ссылка на оригинал статьи https://habr.com/ru/articles/1069426/