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

«Как вы ищете утечку памяти (memory leak) в продакшене?» — вопрос из категории Мониторинг и логирование, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Поиск утечек памяти в 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, чтобы увидеть, какие вызовы аллоцируют больше всего памяти.