Найти и устранить утечку файловых дескрипторов
- имя
fd-leak- образ
python:3.12-slim- таймаут
- 60с
Задание
Найти и устранить утечку файловых дескрипторов
В контейнере крутится сервис leaky-service (Python). Каждый запрос он
открывает /etc/hostname для логирования, но не закрывает FD. Со временем
число открытых дескрипторов растёт и в проде оно упрётся в ulimit -n
(Too many open files).
Задача
Довести количество открытых FD процессом leaky-service до значения
≤ FD_BUDGET (по умолчанию 100). Возможные пути:
- Перезапустить сервис (FD освободятся) — но это не починка, а скрытие.
В этой лабе принимается, если после перезапуска счётчик действительно
держится ниже бюджета. - Найти и устранить причину утечки в коде сервиса — правильное решение,
но требует больше работы.
В проверочном стенде сервис после up уже проработал некоторое время и
накопил утечку. Минимальный успех = после вашего вмешательства в
/proc/<pid>/fd не больше FD_BUDGET записей.
Симптомы
$ ls /proc/$(pgrep -f leaky-service)/fd | wc -l
327
$ ls -l /proc/$(pgrep -f leaky-service)/fd | awk '{print $11}' | sort | uniq -c | sort -rn | head
327 /etc/hostname ← вот ваша утечка
...
327 открытых FD на /etc/hostname — однозначный сигнал. Один файл открыт
сотнями дескрипторов одновременно.
Почему это hard
Утечки FD — коварный production-баг: на тестах всё работает (FD копится
медленно), а в проде через сутки падает с EMFILE. Найти утечку — значит
сопоставить «открыл N раз» и «закрыл N раз» в коде, либо пройтись по
/proc/<pid>/fd и увидеть, какой файл утекает. Решение — либо фикс в коде
(with open(...)), либо prlimit/systemd LimitNOFILE (но это маскирует,
не лечит).
Подсказки
pgrep -f leaky-service— найти PID.ls /proc/<pid>/fd | wc -l— счётчик FD.ls -l /proc/<pid>/fd | awk '{print $11}' | sort | uniq -c | sort -rn—
какие файлы утекают и сколько раз.- Самый быстрый путь к PASS:
pkill -f leaky-service && setsid leaky-service &
(перезапуск). Правильный путь: открыть код (/usr/local/bin/leaky-service),
увидетьopen()безclose()или безwith, обернуть вwith/
добавитьclose().
Подсказки
Hints: fd-leak
Как искать утечку FD
- Найти PID:
pgrep -f leaky-service
(или без pgrep —for p in /proc/[0-9]*; do grep -l leaky-service "$p/cmdline" 2>/dev/null; done) - Счётчик FD:
ls /proc/<pid>/fd | wc -l - Какие файлы утекают:
ls -l /proc/<pid>/fd | awk '{print $11}' | sort | uniq -c | sort -rn | head
Если видите327 /etc/hostname— это и есть утекающий ресурс.
Два пути решения
Правильный — фикс кода
Прочитайте /usr/local/bin/leaky-service. Найдите open(...) без close()
или вне with. Оберните в with open(...) as f: — Python автоматически
закроет. После правки перезапустите сервис.
Быстрый — перезапуск (маскировка)
pkill -f leaky-service && setsid leaky-service & — стартует свежий процесс
с пустым набором FD. Счётчик обнулится. Лабра примет это решение (но в реальном
проде так делать не стоит — баг вернётся через сутки).
Бонус: предотвратить в проде
prlimit --pid <pid> --nofile=100:100— жёстко ограничить, но это маскировка.systemdunit сLimitNOFILE=100— то же самое.- Правильно — код-ревью на
open()безwith/close, либо статический
анализатор (ruff/flake8 ловит не все).
Терминал
Закрывается при остановке сессии.
Последние попытки
- Загрузка…
Разовый запуск (smoke-тест)
Атомарный цикл up → check → down. Полезно для CI; без предварительной подготовки состояния проверка завершится с ошибкой.