...

Servidor de límites de descriptores de archivos: Optimizar los límites en el alojamiento

Muestro cómo el Límite de descriptores de archivos en el servidor limita las conexiones, archivos y sockets y, por tanto, determina el rendimiento. Tomo medidas claras para aumentar los límites, medir la demanda y evitar errores de EMFILE antes de que los servicios fallen bajo carga.

Puntos centrales

Para que pueda actuar con rapidez, he resumido lo más importante Palanca para optimizar los límites de la FD:

  • CausaCada socket, cada archivo, cada conexión a BD consume FDs.
  • SíntomasHTTP 500, mensajes EMFILE, E/S bloqueadas, caídas del servicio.
  • Medición: ulimit, /proc/limits, file-max y lsof aportan claridad.
  • Optimización: Aumentar los límites en limits.conf, systemd y sysctl específicamente.
  • SeguridadFlanquea los límites altos con limitación y supervisión de la tasa.

Qué son los descriptores de archivo y por qué cuentan los límites

Un descriptor de fichero es un simple número entero Identificador, que el núcleo utiliza para hacer referencia a archivos, sockets, tuberías o dispositivos abiertos por proceso. Cada proceso tiene un límite suave y un límite duro, y también hay un máximo global para todo el sistema que limita todos los procesos juntos y minimiza así el número de procesos que pueden ejecutarse. Escasez debe evitarse. Por defecto, a menudo sólo hay 1024 FDs disponibles por proceso, lo que rápidamente se convierte en un problema para sitios web de alto tráfico, pasarelas API o backends de chat, etc. Picos de carga se intensifica. Si un proceso alcanza su límite, las nuevas conexiones fallan, los trabajadores ya no pueden abrir archivos y los registros se llenan de EMFILE, lo que puede provocar la Tiempos de respuesta extendido. Se vuelve particularmente crítico con configuraciones que ocupan varios manejadores por petición: PHP-FPM, backends de caché, archivos de registro y proxies inversos acumulan FDs hasta que el Límites bloque.

Reconocer y medir los síntomas

A menudo, los primeros signos de un límite demasiado estrecho se observan cuando HTTP-500 sin una causa clara, respuestas lentas o reinicios esporádicos de servicios individuales. Entradas de registro típicas como „Demasiados archivos abiertos“ indican EMFILE y señalan la inmediata Necesidad de actuar. Primero compruebo los límites relacionados con el proceso y el consumo de corriente para diferenciar entre los cuellos de botella locales y los problemas de todo el sistema, y así determinar la Causa para ser más precisos. Esta guía compacta es adecuada para una introducción estructurada Guía de límites del servidor, si desea tener una visión rápida de los tornillos de ajuste y Pasos plan. A continuación, utilizo lsof para medir cuántos descriptores tiene realmente un proceso, porque los valores medidos superan a las suposiciones en cuanto los perfiles de carga cambiar.

# Límite suave y duro del shell actual
ulimit -n
ulimit -Hn

# Comprobar los límites de un proceso
cat /proc//limits | grep "abrir ficheros"

# Estado general del sistema
cat /proc/sys/fs/file-nr # abierto | libre | máximo
cat /proc/sys/fs/file-max # máximo global

# Estimación aproximada del consumo por proceso
lsof -p  | wc -l

Comprobación de límites e interpretación de ratios

Hago una distinción estricta entre los límites relacionados con el proceso y los globales, para poder identificar los cuellos de botella de forma selectiva. eliminar en lugar de simplemente moverlo. El límite duro establece el límite superior para los aumentos en las sesiones, mientras que fs.file-max y fs.nr_open definen el marco global y, por lo tanto, la Capacidad del host. Una regla práctica y probada es permitir al menos 65535 FDs por proceso, siempre que la RAM y la carga de trabajo lo permitan y se disponga de los recursos necesarios. Carga saber. Al mismo tiempo, me aseguro de que la suma de trabajadores activos, procesos hijo y conexiones de red en escenarios realistas de alta carga dentro del global Valores del marco restos. Una visión clara de estas cifras evita aumentos ciegos sin un plan y mantiene la integridad del sistema bajo control. Presión.

Comando/Ruta Función A qué prestar atención
ulimit -n / -Hn Límite suave/duro de la sesión actual El límite duro establece el límite superior para Elevación
/proc//limits Límites por proceso y archivos abiertos Crítico para demonios como nginx/php-fpm
/proc/sys/fs/file-max Máximo global de todos los FD Debe añadirse al importe del proceso y RAM ajuste
/proc/sys/fs/file-nr Abierto, gratuito, máximo en número Tendencia en las pruebas de carga y Picos consulte
lsof Muestra las asas abiertas Por trabajador/hilo consumido Medir los FD

Ajuste temporal y permanente de los límites de la FD

Para pruebas rápidas, utilizo ulimit para fijar un Límite suave, antes de definir reglas permanentes y reiniciar los servicios. A continuación, escribo las entradas apropiadas en /etc/security/limits.conf, añado anulaciones de systemd y verifico el cambio con un objetivo Prueba de carga. Importante: El usuario del servicio debe ser correcto, de lo contrario el aumento no tendrá ningún efecto y el Problema reaparece bajo carga. También ajusto el límite global para que muchos procesos de trabajadores no superen juntos el límite del sistema. acumular let. Sólo cuando las partes del proceso y del sistema encajan, la configuración puede soportar escenarios reales de alta carga y evitar EMFILE.

# Temporal (hasta cierre de sesión/reinicio)
ulimit -n 65535

# En todo el sistema (hasta el reinicio o permanentemente a través de sysctl.conf)
sudo sysctl -w fs.file-max=2097152

# Permanente (ejemplo para usuarios del servidor web)
echo -e "www-data soft nofile 65535\nww-data hard nofile 65535\n* soft nofile 65535\n* hard nofile 65535" | sudo tee -a /etc/security/limits.conf

# systemd services (e.g. nginx)
sudo mkdir -p /etc/systemd/system/nginx.service.d
cat <<'EOF' | sudo tee /etc/systemd/system/nginx.service.d/limits.conf
[Servicio]
LimitNOFILE=65536
EOF
sudo systemctl daemon-reload && sudo systemctl restart nginx

Configurar correctamente los parámetros del núcleo

Valido fs.file-max y fs.nr_open juntos para que el kernel tenga suficiente Tampón para los picos de carga. Si sólo aumenta el límite por proceso, de lo contrario alcanzará el límite global y desplazará el cuello de botella a nivel del sistema. Tiene sentido mantener una diferencia entre el pico de carga típico y los valores globales, de modo que haya reservas para ventanas de mantenimiento o ráfagas de tráfico y Picos de riesgo puede amortiguarse. Encontrará información detallada sobre el ajuste de todo el sistema en el artículo sobre Ajuste del núcleo, que me gusta utilizar como herramienta para personalizaciones en profundidad del sistema operativo. Lista de control uso. Después de hacer los cambios, vuelvo a cargar los parámetros, compruebo de nuevo file-nr y verifico que todos los servicios se han reiniciado con los nuevos límites y el Valores hacerse cargo.

# Parámetros permanentes del kernel
sudo bash -c 'cat >> /etc/sysctl.d/99-ulimits.conf <<EOF
fs.file-max = 2097152
fs.nr_open = 2097152
EOF
sudo sysctl --sistema

Control #
cat /proc/sys/fs/file-max
cat /proc/sys/fs/nr_open || sysctl fs.nr_open

Planificación y arquitectura de la capacidad

Una planificación realista de la capacidad empieza por medir Perfiles por petición: ¿cuántos FD necesitan juntos el servidor web, la capa de aplicaciones, la base de datos y la caché? A partir de estas cifras, deduzco el número total de handles abiertos simultáneamente por host y planifico buffers para Picos on. Calcula de forma conservadora: la rotación de registros, los sockets adicionales, los archivos temporales y los trabajos de copia de seguridad aumentan la demanda y consumen recursos. Reservas. Presto atención al hecho de que el escalado horizontal con balanceadores de carga reduce la carga FD por nodo, lo que reduce la tolerancia a fallos y las ventanas de cambio. Simplificado. Sólo con valores límite claros por nivel puede establecer límites específicos y asignar capacidad de forma sensata entre los servicios. dividir.

Puesta a punto del servidor web y la base de datos

Para los servidores web, me atengo a la regla Hilos*4 menor que el límite de FD, de modo que haya reservas para conexiones ascendentes, archivos temporales y registros. Para nginx y Apache, incluyo keep-alive, acceso abierto y registros de errores así como sockets upstream en el cálculo y así me aseguro Tampón. Bases de datos como MariaDB o PostgreSQL abren muchos sockets contra aplicaciones, replicación y monitorización; sus límites deben ajustarse al pool de conexiones y a los picos de tráfico. ajuste. Las cachés (Redis, Memcached) reducen la carga de la BD, pero no reducen necesariamente el número de FD si muchos clientes realizan peticiones y conexiones en paralelo. mantenga. Por lo tanto, estoy planeando límites coordinados a lo largo de la cadena: frontend, upstream, DB, caché y colas de mensajes, de modo que en ninguna parte se supere el primer límite duro. Barrera alcanza.

# Ejemplo: nginx systemd limit y worker
LimitNOFILE=65536 # systemd
worker_processes auto;
worker_connections 4096; # 4096 * Worker <= 65536

# Ejemplo: PostgreSQL
max_connections = 1000 # Necesidad de FD ~ 1-2 por conexión + archivos/registros

Mantenga la eficacia de WordPress y PHP

Las instancias de WordPress con muchos plugins abren más archivos, más conexiones de red y más Registros. Reduzco el número de includes innecesarios con OPCache, reduzco la carga de las bases de datos con Redis Object Cache y externalizo los activos estáticos a través de una CDN para minimizar los accesos a archivos y Conexiones para reducir la carga. Al mismo tiempo, aumento específicamente los límites para php-fpm y el servidor web para que los picos durante cronjobs, crawlers o checkouts de la tienda no sean un problema. interrupciones generar. Un manejo limpio de los registros de errores y rotaciones evita que los escritores de registros se encuentren con nada y no abran nuevos archivos mayo. Así combino la reducción del consumo y el aumento del límite para que la pila siga siendo asequible bajo carga y Rendimiento retenciones.

Contenedores y entornos en nube

En Docker y Kubernetes, los procesos suelen heredar los límites de FD del Nodos, por lo que primero compruebo los parámetros del host y luego las definiciones del servicio. Principios análogos se aplican a systemd-nspawn o containerd, pero la implementación se hace en archivos unitarios, PodSpecs o configuraciones de demonio con Anula. Documento los límites como código (IaC) y los mantengo coherentes mediante playbooks para que los nuevos nodos tengan límites idénticos. Límites traer consigo. Con Kubernetes, también compruebo SecurityContexts y establezco las capacidades necesarias para que el lado del sistema Límites surtan efecto. Al final, la medición en el clúster sigue siendo importante, ya que la programación, el autoescalado y las actualizaciones rodantes cambian la distribución de las manijas abiertas y ponen a prueba su Tampón.

# Ejemplo: systemd en hosts contenedores.
cat <<'EOF' | sudo tee /etc/systemd/system/myapp.service.d/limits.conf
[Service]
LimitNOFILE=65536
EOF

# Kubernetes: podSpec (la imagen del contenedor debe respetar ulimit)
# Nota: la configuración de rlimit debe establecerse de forma diferente en función del runtime/OS

Seguridad, limitación de velocidad y control

Los límites altos proporcionan un respiro, pero aumentan la superficie de ataque para Inundaciones, Por lo tanto, limito las solicitudes en el borde con limitación de velocidad y establezco límites de conexión en el servidor web. Un cortafuegos de aplicaciones web y unos tiempos de espera razonables impiden que las conexiones inactivas bloqueen permanentemente los FD. vincular. Para las pruebas recurrentes, utilizo perfiles de carga reproducibles y uso Prometheus, Netdata o Nagios para supervisar sistemáticamente las métricas relativas a archivos abiertos y Enchufes. En función de la carga de trabajo, corrijo los valores límite gradualmente en lugar de aumentarlos bruscamente para que los efectos sigan siendo mensurables y Desmontaje es fácil. Si desea profundizar en los valores límite en el lado de la conexión, este artículo compacto sobre Límites de conexión, que utilizo en los límites de la red como Guía sirve.

Resolución de problemas con EMFILE: procedimiento estructurado

Empiezo con una mirada al Revista y en los registros de servicio para acotar el tiempo y la frecuencia de los errores. A continuación utilizo lsof para comprobar el consumo máximo por proceso e identificar patrones como fugas, aumento del registro o inusuales Zócalo-tipos. A continuación, comparo los límites establecidos con los picos reales y los aumento temporalmente al principio para poder validar la causa y el efecto en una prueba controlada y, a partir de ahí, determinar Ajustes permanentes derivar. Si se detecta una fuga, aplico un parche o reduzco el componente, porque los límites más altos sólo ocultan los síntomas y posponen el problema. Problema. Por último, documento la corrección, establezco alarmas y planifico una nueva prueba de carga para que la solución sea válida y pueda volver a utilizarse. Confíe en crea.

Evaluar de forma realista el coste en recursos de unos límites elevados de FD.

Cada archivo o socket abierto cuesta memoria del kernel. Por lo tanto, planifique la Huella RAM Dependiendo de la versión del kernel y de la arquitectura, se necesitan de unos cientos de bytes a unos pocos kilobytes por FD (incluyendo estructuras VFS/socket). Con cientos de miles de FDs, esto se acumula. Dimensiono file-max global de tal forma que, en el peor de los casos, quede suficiente memoria caché de páginas y memoria de trabajo libre para las aplicaciones. Una simple comprobación se lleva a cabo mediante vmstat, free y la tendencia de FDs abiertos mediante file-nr durante un Pruebas de carga máxima. El objetivo es una configuración que no se incline hacia el intercambio en picos de carga ni desencadene una actividad excesiva de reclamación u OOM.

Trampas de distribución y ruta de inicio (PAM, systemd, Cron)

La aplicación de límites depende del Ruta de inicio de. Los inicios de sesión basados en PAM (ssh, su, login) leen /etc/security/limits.conf, mientras que los servicios systemd utilizan principalmente sus parámetros de unidad (LimitNOFILE) y no PAM obligatorio. Cron/at pueden tener sus propios contextos. Por lo tanto, valido por servicio:

  • ¿Cómo se inicia el proceso? (systemctl status, ps -ef)
  • ¿Qué límites ve realmente? (cat /proc//limits)
  • ¿Funciona PAM? (Compruebe los módulos PAM en /etc/pam.d/*)
  • ¿Existen valores por defecto para todo el sistema? (systemd: DefaultLimitNOFILE en system.conf)

De este modo, evito que las aplicaciones reciban diferentes límites de FD en función de la ruta de inicio y que inconsistente reaccionar.

Dimensionamiento práctico con ejemplos de cálculo

Cuento con la Trabajadores y perfiles de conexión desde atrás hasta la capacidad FD requerida:

  • nginx con 8 trabajadores con 4000 conexiones cada uno: ~32000 conexiones. Por regla general, nginx reserva 1 FD por conexión activa; además, upstream (keep-alive) y logs añaden ~10-20% de buffer. Resultado: ~38000 FD sólo para nginx.
  • php-fpm con 150 niños, normalmente 20-40 FDs por niño (incluye, sockets, logs): conservadoramente 6000 FDs.
  • Clientes Redis/DB: 200 conexiones paralelas, 1-2 FDs cada una: ~400 FDs.

Total por host: ~44k FDs. Fijé LimitNOFILE para nginx a 65536, php-fpm análogo, y plano global fs.file-max para que quepan todos los servicios más la reserva (x1.5-x2). Para varias instancias muy utilizadas por host, escale globalmente a 1-2 millones de FDs si la RAM y las rutas de E/S son suficientes. entrega.

Diagnóstico más profundo: detección de fugas y puntos calientes

Si los FD suben continuamente, utilizo herramientas específicas para encontrar la causa:

# Manejadores abiertos agrupados por tipo
lsof -p  | awk '{print $5}'' | sort | uniq -c | sort -nr

# Sólo sockets de un proceso
lsof -Pan -p  -i

# Qué ficheros están creciendo (logs, temp)
lsof +L1 # Archivos borrados pero aún abiertos
ls -l /proc//fd

# Vista Syscall: ¿quién está abriendo constantemente?
strace -f -p  -e trace=open,openat,close,socket,accept,accept4 -s 0

Especialmente traicioneras son Registros borrados, que siguen abiertos: Ocupan espacio y FDs, pero ya no aparecen en el sistema de archivos. El reinicio o una reapertura explícita (por ejemplo, con nginx a través de USR1) resuelve este problema limpiamente. Los watchers/exporters mal configurados también pueden abrir constantemente nuevos sockets - los límites de velocidad y agrupación.

Delimitar claramente Inotify, epoll y EMFILE

No todos los límites de recursos se denominan límites de FD. En entornos de desarrollo y CI, las compilaciones suelen fallar con ENOSPC en relación con Inotify (límites de vigilante). Lo compruebo y lo establezco como complemento:

# Límites de Inotify (para todo el usuario e instancias)
sysctl fs.inotify.max_user_watches
sysctl fs.inotify.max_user_instances

# Ejemplo de aumento
sudo sysctl -w fs.inotify.max_user_watches=524288
sudo sysctl -w fs.inotify.max_user_instances=1024

Aunque epoll funciona internamente con los FD, el verdadero cuello de botella con la masiva De larga duración-las conexiones superan a menudo el propio límite de FD. Por lo tanto, correlaciono los datos del bucle epoll/event (por ejemplo, las manijas activas) con el consumo de file-nr y relacionado con el proceso.

Especialidades de lenguaje y tiempo de ejecución (Java, Node.js, Go, Python)

Los tiempos de ejecución manejan los FD de forma diferente:

  • Java/NettyMuchos canales NOK por proceso, los marcos de registro mantienen abiertos los anexadores de archivos. Establezco límites generosos y roto los registros con una estrategia de reapertura en lugar de cierre/reemplazo.
  • Nodo.jsEMFILE se produce rápidamente con cargas de trabajo pesadas del sistema de archivos (por ejemplo, watcher, build pipelines). Regulo las operaciones fs paralelas, aumente los límites y establezca estrategias de backoff/retry.
  • Vaya aUn alto paralelismo a través de goroutines puede abrir muchos sockets. Limito los tiempos de espera de las cabeceras de marcación y respuesta y compruebo si las conexiones se cierran correctamente (IdleConnTimeout).
  • Python/uWSGI/GunicornioLos modelos worker/thread consumen FDs para logs, sockets y ficheros temporales; armonizo el número de workers, thread pools y sin archivo-límites.

Todos ellos tienen algo en común: Sin una rotación de troncos coordinada y Gestión de las conexiones Los FD suben gradualmente.

Contenedores en concreto: configuración de Docker y Kubernetes

Para asegurarme de que los contenedores realmente ven los límites deseados, los establezco de forma coherente a lo largo de la cadena:

  • Ejecución Dockerinicie con -ulimit nofile=65535:65535 o configure por defecto el demonio (por ejemplo, límites por defecto).
  • ImágenesLos scripts de inicio no deben restablecer un ulimit restrictivo.
  • KubernetesDependiendo del tiempo de ejecución (containerd, cri-o), los ajustes de rlimit tienen un efecto diferente. Pruebo en el pod a través de cat /proc/self/limits y ajusto los valores predeterminados de node/runtime si los específicos del pod no son suficientes.

Especialmente en entornos multi-tenant, aseguro la Importe total contra fs.file-max y aislar los efectos de vecinos ruidosos usando conjuntos de nodos separados o presupuestos de pods para que los despliegues individuales no consuman los FDs reservados por el host.

Aclarar las métricas de supervisión y las alarmas

Además de file-nr y file-max, también controlo los FD por proceso y las líneas de tendencia:

  • En todo el sistemaAsignado vs. máximo, tasa de cambio, relación pico/máximo.
  • Por procesoFDs por trabajador/hilo, procesos Top-N, anomalías nocturnas (batch/jobs).
  • CualitativoÍndices de error HTTP, longitudes de cola, errores de aceptación/manipulación sincronizados con la tendencia FD.

Establezco alertas multinivelAviso a 70-80%, Crítico a partir de 90% del límite configurado, más Detección de fugas mediante tendencias alcistas de 7 días. Esto me permite reaccionar a tiempo antes de que las barreras duras surtan efecto.

Libro de ruta para emergencias

Cuando el EMFILE ataca de forma aguda, actúo por pasos claros:

  1. Identificar los principales consumidores (lsof, /proc//fd, entradas de diario).
  2. Aumente temporalmente el límite suave (ulimit en sesión o LimitNOFILE override) y reinicie el servicio.
  3. Si el motivo son los registros: Detenga la rotación, active la reapertura, reduzca el nivel de registro.
  4. Ráfagas de tráfico en la periferia acelerador (Aumentar o endurecer los límites de tarifa/conexión, según la situación).
  5. Corrección de causas (fugas, paralelismo demasiado agresivo, falta de tiempos de espera) y límites permanentes. apriete.

El seguimiento es importante: documentación, pruebas de carga repetidas y alarmas de afilado para que la misma cadena se reconozca en una fase temprana.

Brevemente resumido

Con límites de FD aumentados, parámetros de kernel establecidos limpiamente y una arquitectura probada, puedo ofrecer servicios a mis clientes. Espacio de maniobra bajo carga elevada. Primero mido, luego establezco los valores límite apropiados, verifico con pruebas de carga y aseguro el resultado con limitación de velocidad, monitorización y clear Reglas.

Artículos de actualidad