Si en nuestro sistema ejecutamos servidores web, de aplicaciones o BBDD, con frecuencia nos podremos encontrar con errores del tipo “Too many open files”. Este problema surje cuando el nº de descriptores de fichero de un proceso excede al límite definido para el usuario que lo ejecuta.
Veamos un ejemplo práctico con una instancia de Jboss. Podemos comprobar los descriptores de fichero abiertos por este proceso con lsof -p 3974
, siendo esto último el PID
:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME standalon 3974 jota cwd DIR 8,1 4096 401750 /opt/jboss/jboss-eap-6.4/bin standalon 3974 jota rtd DIR 8,1 4096 2 / standalon 3974 jota txt REG 8,1 960392 1048720 /usr/bin/bash standalon 3974 jota mem REG 8,1 106065056 1332081 /usr/lib/locale/locale-archive standalon 3974 jota mem REG 8,1 2112384 1048645 /usr/lib64/libc-2.17.so standalon 3974 jota mem REG 8,1 19520 1062239 /usr/lib64/libdl-2.17.so standalon 3974 jota mem REG 8,1 174520 1048719 /usr/lib64/libtinfo.so.5.9 standalon 3974 jota mem REG 8,1 164440 1048639 /usr/lib64/ld-2.17.so standalon 3974 jota mem REG 8,1 26254 1178413 /usr/lib64/gconv/gconv-modules.cache standalon 3974 jota 0w CHR 1,3 0t0 4674 /dev/null standalon 3974 jota 1w CHR 1,3 0t0 4674 /dev/null ...
Para comprobar los límites de un usuario utilizaremos ulimit -Sa
. En este caso el usuario que ejecuta la instancia de Jboss se llama jota, por lo que haciendo login con ese usuario ejecutamos ulimit -Sa
en terminal:
core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 15743 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 4096 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
Como vemos, el límite actual es de 1024 descriptores de fichero (open files) por proceso para este usuario. Conforme vayamos recibiendo mayores niveles de concurrencia en nuestra instancia nos encontraremos limitados por estos valores tan bajos.
Si quisiéramos aumentar este límite de open files a 4096 tendríamos que modificar el fichero /etc/security/limits.conf con root o usuario con permisos de root con sudo, introduciendo las líneas:
jota soft nofile 4096 jota hard nofile 4096
Con estas líneas indicamos que un proceso dado del usuario jota no podrá exceder nunca el límite (hard) de 4096 descriptores de ficheros abiertos de forma concurrente. El único usuario que puede exceder este límite sería root, pero nosotros somos un usuario humilde y no podremos pasarnos de 4096.
Una vez guardado el cambio en el fichero, salimos de nuestra consola SSH y volvemos a entrar. Con el nuevo login se cargará la librería /lib/security/pam_limits.so que se encargará de leer los nuevos límites definidos para el usuario que hace login. Deberían haberse ampliado a 4096 los límites del usuario jota. Comprobamos con ulimit -Sa
:
core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 15743 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 4096 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 4096 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
En efecto ya tenemos el límite de open files donde deseábamos, en 4096. ¿Qué ocurre con el proceso de la instancia de Jboss, cogerá este valor o tendrá el límite anterior al cambio (1024)? Podemos comprobarlo de dos formas:
- En el sistema de ficheros virtual /proc, haciendo
cat /proc/3974/limits
, siendo 3974 elPID
. Observamos los valores para Max open files:Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 4096 15743 processes Max open files 1024 1024 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 15743 15743 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us
- Con la utilidad prlimit observando los valores definidos en NOFILE. Lanzamos el comando
prlimit -p 3974
:RESOURCE DESCRIPTION SOFT HARD UNITS AS address space limit unlimited unlimited bytes CORE max core file size 0 unlimited blocks CPU CPU time unlimited unlimited seconds DATA max data size unlimited unlimited bytes FSIZE max file size unlimited unlimited blocks LOCKS max number of file locks held unlimited unlimited MEMLOCK max locked-in-memory address space 65536 65536 bytes MSGQUEUE max bytes in POSIX mqueues 819200 819200 bytes NICE max nice prio allowed to raise 0 0 NOFILE max number of open files 1024 1024 NPROC max number of processes 4096 15743 RSS max resident set size unlimited unlimited pages RTPRIO max real-time priority 0 0 RTTIME timeout for real-time tasks unlimited unlimited microsecs SIGPENDING max number of pending signals 15743 15743 STACK max stack size 8388608 unlimited bytes
Como vemos, aunque tras el login el usuario ya ha adquirido los nuevos límites definidos en limits.conf, los procesos que se encontraban en ejecución previamente al cambio siguen con los límites anteriores. Tendremos que reiniciar el servidor de aplicaciones Jboss en este caso para que herede los nuevos valores que hemos definido.
Tras el reinicio, hacemos un prlimit sobre el nuevo proceso, en este caso prlimit -p 4227
:
RESOURCE DESCRIPTION SOFT HARD UNITS AS address space limit unlimited unlimited bytes CORE max core file size 0 unlimited blocks CPU CPU time unlimited unlimited seconds DATA max data size unlimited unlimited bytes FSIZE max file size unlimited unlimited blocks LOCKS max number of file locks held unlimited unlimited MEMLOCK max locked-in-memory address space 65536 65536 bytes MSGQUEUE max bytes in POSIX mqueues 819200 819200 bytes NICE max nice prio allowed to raise 0 0 NOFILE max number of open files 4096 4096 NPROC max number of processes 4096 15743 RSS max resident set size unlimited unlimited pages RTPRIO max real-time priority 0 0 RTTIME timeout for real-time tasks unlimited unlimited microsecs SIGPENDING max number of pending signals 15743 15743 STACK max stack size 8388608 unlimited bytes
Vemos que en NOFILE tiene el valor 4096, justo lo que buscábamos. Por tanto, cuando modifiquemos los límites de usuario tendremos que reiniciar los procesos asociados que se estuvieran ejecutando anteriormente al cambio. Esto es así ya que un proceso al iniciarse hereda los límites definidos del usuario que lo ejecuta en ese momento dado. Lo que no es necesario para hacer efectivo el cambio es reiniciar el servidor.