惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
人人都是产品经理
人人都是产品经理
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
量子位
腾讯CDC
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
云风的 BLOG
云风的 BLOG
Vercel News
Vercel News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LangChain Blog
aimingoo的专栏
aimingoo的专栏
The Hacker News
The Hacker News
T
The Exploit Database - CXSecurity.com
B
Blog
S
SegmentFault 最新的问题
P
Privacy & Cybersecurity Law Blog
T
Threatpost
博客园 - 聂微东
T
Tailwind CSS Blog
The Last Watchdog
The Last Watchdog
C
Check Point Blog
N
Netflix TechBlog - Medium
D
DataBreaches.Net
爱范儿
爱范儿
IT之家
IT之家
S
Secure Thoughts
M
MIT News - Artificial intelligence
NISL@THU
NISL@THU
C
Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
有赞技术团队
有赞技术团队
A
Arctic Wolf
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
Schneier on Security
Schneier on Security
Simon Willison's Weblog
Simon Willison's Weblog
G
GRAHAM CLULEY
雷峰网
雷峰网
Project Zero
Project Zero
博客园 - Franky
H
Heimdal Security Blog
A
About on SuperTechFans
Security Latest
Security Latest
Webroot Blog
Webroot Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hugging Face - Blog
Hugging Face - Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Слишком много открытых файлов: лимит Linux, который валит прод в 3 часа ночи
Ксения Мосеенкова · 2026-05-27 · via Все публикации подряд на Хабре

Средний

9 мин

12K

Ваш сервис спокойно живёт неделями: графики ровные, алерты молчат, релизы проходят без сюрпризов. А потом в три часа ночи под нагрузкой всё начинает разваливаться с ошибкой: too many open files

На Go это может выглядеть так:

http: Accept error: accept tcp 0.0.0.0:8080: accept4: too many open files; retrying in 5ms

На Java — так:

java.io.IOException: Too many open files
      at sun.nio.ch.FileDispatcherImpl.init0(Native Method)

На Python — так:

OSError: [Errno 24] Too many open files

Названия разные, причина одна: EMFILE. Ядро отказалось выдать процессу новый файловый дескриптор, потому что тот упёрся в один из лимитов. Дальше всё обычно идёт по знакомому сценарию: сервис падает, оркестратор его перезапускает, под нагрузкой он снова быстро доходит до того же состояния — и цикл повторяется.

Самое неприятное начинается в момент диагностики. ulimit -n показывает 1,048,576. fs.file-max выглядит почти бездонным. lsof | wc -l даёт какие-нибудь 5000 открытых файлов. Казалось бы, до лимита ещё далеко. Тогда почему сервис всё равно падает?

Проблема в том, что лимит здесь не один. Есть как минимум три разных ограничения, и срабатывает не всегда то, на которое вы смотрите первым. Результат зависит от того, где живёт процесс: на обычном Linux-хосте, в контейнере, в Kubernetes или под непривилегированным пользователем.

В этой статье разберём, что в Linux вообще считается «открытым файлом», какие лимиты реально участвуют в ошибке too many open files, как они наследуются в Kubernetes и где смотреть, чтобы не чинить не тот слой инфраструктуры.

Что на самом деле считается «открытым файлом»

Файловый дескриптор — это небольшое целое число (0, 1, 2, 3, ...), которое ядро возвращает процессу, когда тот что-либо открывает. И под «что-либо» здесь понимается далеко не только файл:

  • Обычный файл на диске

  • TCP- или UDP-сокет

  • Unix-сокет

  • Канал (pipe)

  • eventfd, signalfd, timerfd

  • Экземпляр epoll

  • Каталог, открытый через opendir

  • Дескриптор устройства (/dev/null, /dev/random и т. д.)

Типичный HTTP-сервис на Go обычно держит:

  • 1 файловый дескриптор для сокета прослушивания

  • 1 файловый дескриптор на каждое принятое соединение

  • 1 файловый дескриптор на каждое исходящее HTTP-соединение клиента (часто через пул соединений)

  • Несколько файловых дескрипторов для логов, stdin/stdout/stderr, экземпляров epoll

Сервис, который обслуживает 10 000 одновременных соединений, имеет ещё 1000 исходящих подключений и систему телеметрии, легко выходит на 12 000+ файловых дескрипторов. А сервис с утечкой ресурсов (например, забывающий закрывать простаивающие соединения) будет бесконечно наращивать их количество, пока не упрётся в лимит и не упадёт.

Три типа ограничений

Существует три разных ограничения на количество файловых дескрипторов. Срабатывает самое маленькое из них.

Ограничение №1: мягкий лимит на процесс (ulimit -n внутри процесса)

Классический ulimit. У каждого процесса есть мягкий и жёсткий лимиты. Именно мягкий лимит проверяется при вызовах open(). Жёсткий лимит задаёт верхнюю границу, до которой процесс может повысить свой мягкий лимит.

# Изнутри процесса или shell

ulimit -n           # текущий мягкий лимит
ulimit -Hn          # жёсткий лимит
ulimit -n 65536     # увеличить мягкий лимит (не выше жёсткого

Для процесса внутри pod в Kubernetes именно этот лимит ядро проверяет при вызовах open().

Ограничение №2: общесистемный лимит (fs.file-max и fs.nr_open)

У ядра есть глобальное ограничение на общее количество открытых файлов во всей системе (fs.file-max) и ограничение на максимальное число файловых дескрипторов для одного процесса (fs.nr_open).

sysctl fs.file-max     # общесистемный максимум (обычно очень большой, например 9_223_372)
sysctl fs.nr_open      # максимальный потолок для одного процесса (обычно 1_048_576)

# Текущее использование по всей системе
cat /proc/sys/fs/file-nr

# Формат вывода: <allocated> <free> <max>
# Например: 4032 0 9223372

Обычно с такими проблемами сталкиваются только на сильно загруженных общих хостах. На рабочем узле Kubernetes, работающем с 50 подами, fs.file-max значительно превышает допустимый уровень, если только не происходит массивная утечка памяти.

Более важным является параметр fs.nr_open: он ограничивает максимальный уровень, до которого отдельный процесс может поднять свой собственный мягкий лимит. Вы не можете использовать ulimit -n 2_000_000, если fs.nr_open равен 1_048_576.

Ограничение №3: cgroup pids.max (связанный, но другой ресурс)

Формально это не лимит файловых дескрипторов, но его часто с ними путают: pids.max ограничивает количество процессов и потоков в cgroup.

Нагрузка с утечкой потоков — например, сервис на Go, который создаёт горутину на каждый запрос и никогда их не завершает — может упереться именно в этот лимит. В таком случае проблема будет выглядеть скорее как ошибка создания процесса (fork failure), а не как EMFILE.

cat /sys/fs/cgroup/pids.max     # максимум для этой cgroup
cat /sys/fs/cgroup/pids.current # текущее количество

Это не основная тема статьи, но о таком лимите полезно помнить при смежной отладке.

Как ограничения взаимодействуют в Kubernetes

Именно здесь всё становится запутанным. Когда запускается pod:

  1. kubelet просит контейнерный рантайм — например, containerd или CRI-O — запустить контейнер.

  2. По умолчанию контейнерный рантайм не задаёт процессу ulimit -n. Процесс наследует те значения, которые настроены у самого рантайма.

  3. Значения ulimit по умолчанию зависят от рантайма: containerd, например, не задаёт отдельного переопределения и использует настройки хоста; некоторые дистрибутивы Kubernetes меняют это через конфигурацию kubelet.

  4. Значения по умолчанию на хосте приходят из лимитов unit-файлов systemd (LimitNOFILE). В большинстве современных дистрибутивов Linux они установлены на уровне 1_048_576 или выше.

В итоге pod обычно стартует с ulimit -n где-то между 1024 (редкие старые конфигурации) и 1,048,576 (современные системы). И, как правило, вы не знаете точное значение, пока не проверите его вручную.

# Проверка изнутри pod
kubectl exec -it $POD -- sh -c 'ulimit -n; ulimit -Hn'

# Проверка фактического лимита у работающего процесса
PID=$(kubectl exec -it $POD -- pgrep -f myapp | head -1 | tr -d '\r')
kubectl exec -it $POD -- cat /proc/$PID/limits | grep "Max open files"

Третье значение берётся из /proc/PID/limits, и именно оно является единственным достоверным источником: это тот лимит, который ядро реально применяет к конкретному процессу.

Как правильно задать лимит в Kubernetes

Нельзя декларативно задать ulimit в спецификации pod так же, как CPU или память. Параметра resources.limits.openFiles не существует. На практике используют три подхода.

Подход №1: значения по умолчанию в container runtime (правильное решение)

Настройте контейнерный рантайм kubelet, чтобы он задавал адекватные лимиты файловых дескрипторов для всех контейнеров.

Для containerd:

# /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
 # ... другие параметры ...
 Rlimits = [
   { type = "RLIMIT_NOFILE", hard = 1048576, soft = 1048576 }
 ]
Для CRI-O:
# /etc/crio/crio.conf
[crio.runtime]
default_ulimits = [
   "nofile=1048576:1048576",
]

После перезапуска все новые контейнеры будут наследовать эти лимиты. Это самый чистый и правильный вариант, потому что каждое приложение получает адекватные настройки по умолчанию без отдельных конфигураций для каждого pod.

Подход №2: securityContext.sysctls (ограниченно применим)

Kubernetes позволяет задавать некоторые параметры sysctl для отдельных pod через securityContext.sysctls. Параметр fs.file-max изолирован по пространствам имён (namespaced) и может быть настроен, но это общесистемный лимит, а не ограничение на отдельный процесс. Он не задаёт ulimit для процесса.

spec:
  securityContext:
    sysctls:
      - name: fs.file-max
        value: "1048576"

В ряде случаев это полезно, но типичную проблему EMFILE не решает, потому что она обычно связана с мягким лимитом конкретного процесса.

Подход №3: явная установка ulimit в entrypoint

Можно заставить контейнер поднимать собственный ulimit при запуске — до выполнения основного бинарника:

ENTRYPOINT ["/bin/sh", "-c", "ulimit -n 1048576 && exec /usr/local/bin/myapp"]

Но здесь есть важный нюанс: процесс может поднять свой мягкий лимит только до значения жёсткого лимита. Если жёсткий лимит равен 1024, команда ulimit -n 1048576 завершится ошибкой.

Чтобы увеличить жёсткий лимит, контейнеру нужна capability CAP_SYS_RESOURCE, которой обычно нет у защищённых контейнеров без дополнительных привилегий.

Такой подход подходит как временный костыль, когда вы не можете изменить конфигурацию рантайма. Но как долгосрочное решение он считается неправильным.

Диагностика EMFILE в продакшене

Когда процесс начинает падать с ошибкой «too many open files»:

Шаг 1: проверьте лимит и текущее количество файловых дескрипторов

PID=$(pgrep -f myapp | head -1)

# Какой установлен лимит?
cat /proc/$PID/limits | grep "Max open files"

# Сколько файловых дескрипторов реально используется?
ls /proc/$PID/fd | wc -l

Если текущее количество приближается к лимиту или уже достигло его, значит у вас либо утечка ресурсов, либо приложению действительно не хватает ёмкости.

Шаг 2: определите, что именно открыто

# Группировка файловых дескрипторов по типу
ls -la /proc/$PID/fd | awk '{print $NF}' | grep -oE 'socket|^/.*' | sort | uniq -c | sort -rn | head

Это покажет, что именно утекает: файловые дескрипторы файлов, сокеты или и то и другое одновременно.

Шаг 3: если проблема в сокетах — копайте глубже

# Показать детали сокетов (состояние, удалённые адреса)
ss -tnp | grep "pid=$PID"

# Сокеты в состоянии CLOSE_WAIT часто указывают на утечку
# (удалённая сторона закрыла соединение, а вы — нет)
ss -tnp state close-wait | wc -l

Состояние CLOSE_WAIT — один из самых известных признаков утечки HTTP-клиента. Удалённый сервер уже закрыл соединение, но ваш клиент так и не дочитал EOF, поэтому сокет остаётся в CLOSE_WAIT до тех пор, пока процесс не будет завершён.

Шаг 4: если проблема в файловых дескрипторах файлов — найдите, что именно открыто

ls -la /proc/$PID/fd | awk '{print $NF}' | grep '^/' | sort | uniq -c | sort -rn | head

Повторяющиеся записи с одним и тем же путём означают, что в коде есть участок, который открывает файл, но не закрывает его.

Типичные причины EMFILE на уровне кода

Большинство ошибок EMFILE связаны не с инфраструктурой, а с самим приложением.

  1. HTTP-клиент без пула соединений

Каждый запрос создаёт новое соединение; под нагрузкой соединения накапливаются быстрее, чем закрываются.

// ПЛОХО: новый клиент на каждый запрос
http.Get(url)   // использует клиент по умолчанию, но создаёт новые соединения

// ЛУЧШЕ: общий клиент с нормально настроенным Transport
var client = &http.Client{
   Transport: &http.Transport{
       MaxIdleConns:        100,
       MaxIdleConnsPerHost: 10,
       IdleConnTimeout:     90 * time.Second,
   },
}

2. Подключения к базе данных без пула соединений

Та же проблема встречается и с SQL-драйверами. Большинство современных драйверов используют пул соединений по умолчанию, но только если вы переиспользуете экземпляр *sql.DB (потому что именно он и является пулом).

3. Файлы открываются в горячем цикле (hot loop) без defer close() (Go), with (Python) или try-with-resources (Java)#

Если функция открывает файл и при ошибке выходит раньше времени, не закрывая его, вы получаете утечку одного файлового дескриптора на каждую такую ошибку.

4. Горутиины или потоки удерживают сокеты бесконечно долго

Пул воркеров, который бесконтрольно растёт под нагрузкой, создаёт отдельный воркер на каждое соединение; каждый воркер удерживает своё соединение открытым; количество файловых дескрипторов постепенно растёт.

5. Долгоживущие процессы с внутренними кэшами

«Кэш открытых файлов» без механизма вытеснения продолжает накапливать файловые дескрипторы бесконечно.

Во всех этих случаях исправление должно происходить на уровне приложения. Повышение ulimit только маскирует утечку, но не устраняет её.

Когда действительно нужно повышать лимит

Иногда лимит и правда оказывается слишком низким для нормальной рабочей нагрузки:

  • Реверс прокси вроде NGINX, Envoy или HAProxy, обслуживающий десятки тысяч одновременных соединений

  • WebSocket-сервер с постоянными соединениями

  • Сервер базы данных с большим количеством одновременных клиентских подключений

  • Пограничный CDN-узел

Для таких сценариев имеет смысл выставить значение по умолчанию в рантайме на уровне 1,048,576 или выше. При этом важно убедиться, что внутренние ограничения самого приложения (NGINX worker_connections, уровень параллелизма в Envoy и т. д.) настроены соответствующим образом.

Краткая памятка: чек-лист по EMFILE

1. Найдите процесс:

PID=$(pgrep -f myapp | head -1)

2. Проверьте лимит процесса:

cat /proc/$PID/limits | grep "Max open files"

3. Проверьте текущее использование:

ls /proc/$PID/fd | wc -l

4. Если количество файловых дескрипторов растёт и приближается к лимиту:

— Определите типы:

ls -la /proc/$PID/fd | awk '{print $NF}' | sort | uniq -c

 — Для сокетов:

ss -tnp | grep "pid=$PID"

— Ищите CLOSE_WAIT (удалённая сторона закрыла соединение, а вы — нет)

5. Сначала исправляйте приложение, потом инфраструктуру:

— Добавьте пул соединений
— Исправьте шаблоны close()/defer/with
— Ограничьте размер пулов воркеров

6. Если лимит действительно нужно повысить:

— Настройте default ulimits в containerd/CRI-O
— Перезапустите kubelet (существующие pod нужно перезапустить, чтобы они получили новые лимиты)
— Проверьте значения через /proc/PID/limits

7. Настройте мониторинг:

— алерт при (open_fds / max_fds) > 0.8
— алерт при бесконтрольном росте количества CLOSE_WAIT

Что мониторить

Метрики Prometheus из process_exporter или встроенные метрики языка дают всё необходимое:

# Соотношение использования файловых дескрипторов для процесса
process_open_fds / process_max_fds > 0.8

# Для pod через cAdvisor / kubelet
container_file_descriptors / container_ulimits_soft{ulimit="open_files"} > 0.8

# Детектор CLOSE_WAIT
# (требуется exporter состояний TCP)
node_netstat_Tcp_CloseWait > 1000

Панель мониторинга с количеством используемых файловых дескрипторов по каждому сервису и алертами на уровне 80% позволяет обнаружить утечки за несколько дней до того, как они уронят продакшен.

Правильная ментальная модель

ulimit -n — это мягкое ограничение ресурса на уровне процесса. Его задаёт тот компонент, который запускает процесс: unit-файл systemd, конфигурация containerd, сессия shell и так далее. В Kubernetes цепочка выглядит так: kubelet -> runtime -> container.

EMFILE в продакшене почти всегда сводится к одной из двух причин:

  • утечка на уровне приложения (лимит нормальный, но приложение держит файловые дескрипторы, которые должно было закрыть);

  • или нагрузка, которой действительно не хватает стандартного лимита рантайма (в этом случае нужно исправлять конфигурацию рантайма, а не городить костыли на уровне отдельных pod).

Когда проблема возникает, последовательность диагностики всегда одна и та же: проверить лимит, посчитать количество открытых файловых дескрипторов, определить их типы, найти источник утечки.

Самое сложное — понимать, куда смотреть. Но вы теперь понимаете.

В ошибках вроде too many open files быстро выясняется, что одного ulimit -n мало. Разобраться в смежной практике по Linux можно на бесплатных уроках с возможностью задать вопросы экспертам:

  • 2 июня в 20:00. «Введение в Docker: контейнеризация приложений в Linux». Записаться

  • 18 июня в 20:00. «Основы Bash: пишем простые скрипты для автоматизации в Linux». Записаться

Тем, кто хотел бы подтянуть базу, рекомендую обратить внимание на мини-курс по основам Linux (сейчас всего за 10 рублей)