Linux-процессы, память и системные метрики
Многие backend-программы запускаются на Linux: в Docker-контейнере, на сервере, в Kubernetes или в CI. Даже если вы пишете Go-код, полезно понимать, что происходит под ним: что такое процесс, PID, память, сигналы, файловые дескрипторы и /proc.
Это знание помогает диагностировать реальные проблемы: сервис не останавливается, память растет, порт занят, файлы не закрываются, контейнер убивают по memory limit.
Процесс
Процесс - это запущенная программа. У него есть:
- PID;
- память;
- открытые файлы;
- переменные окружения;
- текущая директория;
- потоки выполнения;
- права пользователя.
Посмотреть процессы:
ps aux
Интерактивно:
top
Или более удобный вариант, если установлен:
htop
PID нужен, чтобы обратиться к конкретному процессу: посмотреть его файлы, отправить сигнал, изучить потребление ресурсов.
/proc
/proc - виртуальная файловая система с информацией о процессах и системе. Это не обычные файлы на диске. Ядро Linux показывает через них текущее состояние.
cat /proc/meminfo cat /proc/cpuinfo ls /proc/1234
Для процесса:
cat /proc/<pid>/status cat /proc/<pid>/cmdline ls /proc/<pid>/fd
Если вы видите /proc/<pid>/fd, вы смотрите открытые файловые дескрипторы процесса: файлы, сокеты, pipes.
Память
Частые показатели:
- RSS - физическая память, которую процесс реально занимает сейчас;
- VSZ/VIRT - виртуальная память, зарезервированная процессом;
- heap - память для динамических данных;
- stack - память вызовов функций;
- swap - память, вытесненная на диск.
Команда:
free -h
Linux активно использует свободную память под disk cache. Это нормально: "used" не всегда значит проблема.
RSS и утечки
Если RSS процесса постоянно растет и не снижается, это повод проверить:
- не копятся ли данные в map или slice;
- закрываются ли response body;
- нет ли goroutine leak;
- не сохраняются ли большие буферы в глобальном состоянии;
- не растет ли очередь задач быстрее, чем consumer успевает обрабатывать.
Для Go полезны также pprof и runtime metrics, но базовое наблюдение через ps, top и контейнерные метрики уже помогает увидеть направление проблемы.
Сигналы
Сигналы позволяют завершать или управлять процессом:
kill -TERM <pid> kill -INT <pid> kill -KILL <pid>
SIGTERM просит завершиться корректно. SIGINT обычно приходит от Ctrl+C. SIGKILL убивает процесс сразу и не дает ему закрыть файлы или соединения.
В Go сигнал можно обработать через os/signal:
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() <-ctx.Done()
Это основа graceful shutdown: сервис получает сигнал, перестает принимать новые запросы и дает текущим операциям время завершиться.
Файловые дескрипторы
Открытые файлы, сокеты и pipes - это файловые дескрипторы:
ls /proc/<pid>/fd
Если программа не закрывает файлы или сетевые соединения, количество дескрипторов растет. В какой-то момент процесс может получить ошибку "too many open files".
В Go частая ошибка:
resp, err := http.Get(url) if err != nil { return err } // забыли resp.Body.Close()
Правильно:
defer resp.Body.Close()
Порты и соединения
Если backend не запускается, потому что порт занят, полезны команды:
lsof -i :8080 ss -lntp
Они помогают понять, какой процесс слушает порт.
Для сетевых сервисов смотрите не только CPU и память, но и количество соединений. Медленные клиенты или зависшие запросы могут держать соединения открытыми слишком долго.
Диагностика Go-программы
Когда сервис ведет себя странно, задайте базовые вопросы:
- Процесс вообще жив?
- Какой у него PID?
- Сколько памяти занимает RSS?
- Растет ли RSS со временем?
- Сколько файловых дескрипторов открыто?
- Слушает ли процесс нужный порт?
- Обрабатывает ли сервис
SIGTERM? - Есть ли в логах старт, остановка и критичные ошибки?
Эти вопросы часто быстрее приводят к причине, чем чтение кода с первой строки.
Контейнеры
В Docker и Kubernetes процесс все равно остается Linux-процессом. Просто он работает в изолированном окружении с лимитами.
Если контейнер убит из-за памяти, в Kubernetes можно увидеть OOMKilled. Это значит, что процесс превысил memory limit.
Для локальной диагностики:
docker stats docker compose logs api docker compose exec api sh
Чеклист
- Для долгих сервисов реализован graceful shutdown.
- Файлы и сетевые соединения закрываются.
- Память проверяется по RSS и
available. - Рост RSS во времени расследуется отдельно.
- Количество файловых дескрипторов не растет бесконечно.
SIGKILLиспользуется только как крайняя мера.- Логи показывают старт, остановку и критичные ошибки.
- Для занятого порта проверяется процесс-владелец.