Как вы ищете утечку памяти (memory leak) в продакшене?

Ответ

Поиск утечек памяти в production — это многоуровневый процесс, от наблюдения до глубокого анализа. Я выстраиваю его так:

1. Обнаружение (Что не так?):

  • Прометеус + Grafana: Настраиваю алерты на ключевые метрики контейнеров и нод:
    • container_memory_working_set_bytes — рост без возврата к базовому уровню после нагрузки.
    • container_memory_rss — устойчивый рост Resident Set Size.
    • node_memory_MemAvailable_bytes — общее снижение доступной памяти на ноде.
  • Логи Kubernetes: Ищу OOMKilled события для подов (kubectl get events --field-selector=reason=OOMKilled).

2. Диагностика (Где проблема?):

  • Внутри контейнера: Использую kubectl exec для запуска профилировщиков:
    • Для JVM-приложений: jcmd <pid> GC.heap_dump /tmp/heap.hprof (далее анализ в Eclipse MAT или JVisualVM).
    • Для Go: pprof через встроенный HTTP-эндпоинт или отправляю сигнал SIGUSR1 для дампа.
    • Для Node.js: --inspect флаг и Chrome DevTools или модуль heapdump.
  • На уровне ноды: Использую классические Linux-инструменты:
    # Находим процесс-потребитель
    kubectl debug node/<node-name> -it --image=nicolaka/netshoot -- htop
    # Или смотрим детали по конкретному PID из контейнера
    cat /proc/<pid>/status | grep VmRSS
    cat /proc/<pid>/smaps

3. Проактивное предотвращение:

  • Лимиты и запросы в Kubernetes: Всегда задаю resources.limits.memory. Это не предотвращает утечку, но ограничивает ущерб и вызывает OOMKilled, что является четким сигналом.
  • Тестирование под нагрузкой: Перед релизом запускаю нагрузочное тестирование (например, с помощью k6 или Locust) и наблюдаю за графиками памяти в течение длительного времени, чтобы выявить постепенный рост.
  • Инструменты статического анализа: Интегрирую в CI/CD (например, SpotBugs для Java) для выявления потенциальных антипаттернов, ведущих к утечкам.

Пример для Python-приложения в K8s: При подозрении на утечку в Python-сервисе я бы добавил в его Dockerfile профилировщик (py-spy), собрал новый образ и в debug-поде выполнил: kubectl exec <pod> -- py-spy top --pid 1, чтобы увидеть, какие вызовы аллоцируют больше всего памяти.

Ответ 18+ 🔞

А, ну это ж классика, ёпта! Поиск утечек памяти в продакшене — это как искать иголку в стоге сена, который ещё и горит, и тебя в него пихают. Но если по делу, то я обычно действую по чёткому плану, а то сам от себя охуеешь, когда приложение память хавает как не в себя.

1. Первый звоночек (Что за херня творится?):

  • Прометеус с Графаной: Настраиваю алерты, чтобы они орали, как резаные, на ключевые метрики. Смотрю в основном на две вещи:
    • container_memory_working_set_bytes — если после нагрузки память не отвалилась обратно, а так и торчит, это уже подозрение ёбаное чувствую.
    • container_memory_rss — если резидентная память ползёт вверх стабильно, как будто её кто-то накачивает.
    • Ну и общую память на ноде (node_memory_MemAvailable_bytes) мониторю, чтобы не накрылся медным тазом весь сервер.
  • Логи кубера: Просто и эффективно. Бегу смотреть, кого система прибила за жадность: kubectl get events --field-selector=reason=OOMKilled. Если вижу такие события — всё, волнение ёбать, начинается охота.

2. Глубокий анализ (Ну-ка, сука, покажись!): Тут уже лезем внутрь, как хирурги-садисты.

  • В контейнер: Через kubectl exec запускаем профилировщики. Для каждого языка свой подход, ядрёна вошь!
    • JVM (Java и прочее): Команда jcmd <pid> GC.heap_dump /tmp/heap.hprof сделает дамп кучи. Потом этот файл тащишь себе и смотришь в Eclipse MAT, кто там объекты держит, как приклеенные. Удивление пиздец иногда бывает.
    • Go: Тут обычно pprof через HTTP-ендпоинт, или можно сигнальчик SIGUSR1 отправить — он дамп сделает. Красиво и удобно.
    • Node.js: Флаг --inspect и вперёд, в Chrome DevTools, либо модуль heapdump использовать.
  • На самой ноде (Linux-магия): Иногда надо снаружи глянуть. Запускаю отладочный под с утилитами (kubectl debug node/<node-name>) и там уже htop или:
    # Смотрим на конкретный PID из контейнера
    cat /proc/<pid>/status | grep VmRSS
    cat /proc/<pid>/smaps

    Это покажет, сколько реально памяти жрёт процесс и в каких регионах. Бывает, что одна хитрая жопа из кэша не выгружается.

3. Чтобы не охуеть потом (Проактивность): Лучше предотвратить, чем потом, бля, в аврале искать.

  • Лимиты в Kubernetes: Это святое. Всегда прописываю resources.limits.memory. Саму утечку это не починит, но хотя бы ограничит ущерб. Пода убьёт, но нода цела будет. Чёткий сигнал, что пора ковыряться.
  • Нагрузочное тестирование: Перед выкаткой гоняю сервис под нагрузкой (k6, Locust) и долго-долго смотрю на график памяти. Нужно поймать этот медленный, но верный рост — вот где доверия ёбать ноль к коду.
  • Статический анализ в CI/CD: Вешаю в пайплайн штуки типа SpotBugs для Java. Пусть ищут потенциальные косяки в коде, которые к утечкам ведут. Лучше пусть ругается на этапе сборки, чем в проде.

Конкретный пример для Python: Допустим, питоновский сервис в кубере начал пухнуть. Что делаю? В Dockerfile добавляю py-spy, пересобираю образ. Потом в дебаг-поде выполняю: kubectl exec <pod> -- py-spy top --pid 1 И смотрю, какие вызовы функций больше всего памяти аллоцируют. Иногда сразу видно, что какой-то цикл или кэш сходит с ума. Главное — не паниковать, а методично ковырять.