10 Notas sobre la selección de procesos en las métricas proc.mem y proc.num

Procesos modificando su línea de comando

Algunos programas utilizan la modificación de su línea de comandos como método para mostrar su actividad actual. Un usuario puede ver la actividad ejecutando los comandos ps y top. Ejemplos de dichos programas son PostgreSQL, Sendmail, Zabbix.

Veamos un ejemplo de Linux. Supongamos que queremos monitorear un número de procesos del agente Zabbix.

El comando ps muestra procesos de interés como

$ ps -fu zabbix
       UID PID PPID C TIEMPO TTY TIEMPO CMD
       ...
       zabbix 6318 1 0 12:01 ? 00:00:00 sbin/zabbix_agentd -c /home/zabbix/ZBXNEXT-1078/zabbix_agentd.conf
       zabbix 6319 6318 0 12:01 ? 00:00:01 sbin/zabbix_agentd: collector [idle 1 sec]
       zabbix 6320 6318 0 12:01 ? 00:00:00 sbin/zabbix_agentd: listener #1 [waiting for connection]
       zabbix 6321 6318 0 12:01 ? 00:00:00 sbin/zabbix_agentd: listener #2 [waiting for connection]
       zabbix 6322 6318 0 12:01 ? 00:00:00 sbin/zabbix_agentd: listener #3 [waiting for connection]
       zabbix 6323 6318 0 12:01 ? 00:00:00 sbin/zabbix_agentd: active checks #1 [idle 1 sec]
       ...

Seleccionar los procesos por nombre y usuario hace el trabajo:

$ zabbix_get -s localhost -k 'proc.num[zabbix_agentd,zabbix]'
       6

Ahora cambiemos el nombre del ejecutable zabbix_agentd a zabbix_agentd_30 y reiniciémoslo.

ps ahora muestra

$ ps -fu zabbix
       UID PID PPID C TIEMPO TTY TIEMPO CMD
       ...
       zabbix 6715 1 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30 -c /home/zabbix/ZBXNEXT-1078/zabbix_agentd.conf
       zabbix 6716 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: collector [idle 1 sec]
       zabbix 6717 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: listener #1 [waiting for connection]
       zabbix 6718 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: listener #2 [waiting for connection]
       zabbix 6719 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: listener #3 [waiting for connection]
       zabbix 6720 6715 0 12:53 ? 00:00:00 sbin/zabbix_agentd_30: active checks #1 [idle 1 sec]
       ...

Ahora, seleccionar los procesos por nombre y usuario produce un resultado incorrecto:

$ zabbix_get -s localhost -k 'proc.num[zabbix_agentd_30,zabbix]'
       1

¿Por qué un simple cambio de nombre del ejecutable a un nombre más largo conduce a un resultado bastante diferente?

El agente Zabbix comienza verificando el nombre del proceso. /proc/<pid>/estado Se abre el archivo y se comprueba la línea "Name". En nuestro caso las líneas de Name son:

$ grep Name /proc/{6715,6716,6717,6718,6719,6720}/status
       /proc/6715/status:Name:zabbix_agentd_3
       /proc/6716/status:Name:zabbix_agentd_3
       /proc/6717/status:Name:zabbix_agentd_3
       /proc/6718/status:Name:zabbix_agentd_3
       /proc/6719/status:Name:zabbix_agentd_3
       /proc/6720/status:Name:zabbix_agentd_3

El nombre del proceso en el archivo "status" se trunca a 15 caracteres.

Se puede ver un resultado similar con el comando ps:

$ ps -u zabbix
         PID TTY TIEMPO CMD
       ...
        6715? 00:00:00 zabbix_agentd_3
        6716? 00:00:01 zabbix_agentd_3
        6717? 00:00:00 zabbix_agentd_3
        6718? 00:00:00 zabbix_agentd_3
        6719? 00:00:00 zabbix_agentd_3
        6720? 00:00:00 zabbix_agentd_3
        ...

Obviamente, eso no es igual al valor de nuestro parámetro name zabbix_agentd_30 de proc.num[]. Al no haber podido coincidir con el nombre del proceso del archivo status que el agente Zabbix convierte en el archivo /proc/<pid>/cmdline.

La forma en que el agente ve el archivo "cmdline" se puede ilustrar ejecutando el comando

$ for i in 6715 6716 6717 6718 6719 6720; do cat /proc/$i/cmdline | awk '{gsub(/\x0/,"<NUL>"); print};'; done
       sbin/zabbix_agentd_30<NUL>-c<NUL>/home/zabbix/ZBXNEXT-1078/zabbix_agentd.conf<NUL>
       sbin/zabbix_agentd_30: collector [idle 1 sec]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
       sbin/zabbix_agentd_30: listener #1 [waiting for connection]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
       sbin/zabbix_agentd_30: listener #2 [waiting for connection]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
       sbin/zabbix_agentd_30: listener #3 [waiting for connection]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...
       sbin/zabbix_agentd_30: active checks #1 [idle 1 sec]<NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL><NUL>...

Los archivos /proc/<pid>/cmdline en nuestro caso contienen bytes nulos invisibles y no imprimibles, utilizados para terminar cadenas en lenguaje C. Los bytes nulos se muestran como "<NUL>" en este ejemplo.

El agente de Zabbix comprueba "cmdline" para el proceso principal y toma zabbix_agentd_30, que coincide con el valor de nuestro parámetro name zabbix_agentd_30. Entonces, el proceso principal se cuenta para la métrica proc.num[zabbix_agentd_30,zabbix].

Al comprobar el siguiente proceso, el agente toma zabbix_agentd_30: collector [idle 1 sec] del archivo cmdline y no cumple con nuestro parámetro name zabbix_agentd_30. Entonces, sólo el proceso principal que no modifica su línea de comando se cuenta. Los otros procesos del agente modifican su línea de comando y son ignorados.

Este ejemplo muestra que el parámetro name no se puede utilizar en proc.mem[] y proc.num[] para seleccionar procesos en este caso.

El uso del parámetro cmdline con una expresión regular adecuada produce un resultado correcto:

$ zabbix_get -s localhost -k 'proc.num[,zabbix,,zabbix_agentd_30[:]]'
       6

Tenga cuidado al utilizar las métricas proc.mem[] y proc.num[] para monitorear programas que modifican sus líneas de comando.

Antes de poner los parámetros name y cmdline en las métricas proc.mem[] y proc.num[], es posible que desee verificar los parámetros utilizando la métrica proc.num[] y el comando ps.

Hilos del kernel de Linux

Los subprocesos no se pueden seleccionar con el parámetro cmdline en las métricas proc.mem[] y proc.num[]

Tomemos como ejemplo uno de los subprocesos del kernel:

$ ps -ef| grep kthreadd
       root  2  0. 0 09:33.  ?  00:00:00 [kthreadd]

Se puede seleccionar con el parámetro name del proceso:

$ zabbix_get -s localhost -k 'proc.num[kthreadd,root]'1
       1

Pero la selección por el parámetro cmdline del proceso no funciona:

$ zabbix_get -s localhost -k 'proc.num[,root,,kthreadd]'
       0

La razón es que el agente Zabbix toma la expresión regular especificada en el parámetro cmdline y lo aplica al contenido del proceso /proc/<pid>/cmdline. Para los subprocesos del kernel, sus archivos /proc/<pid>/cmdline están vacíos. Entonces, el parámetro cmdline nunca coincide.

Recuento de subprocesos en las métricas proc.mem[] y proc.num[]

Los subprocesos del kernel de Linux se cuentan por la métrica proc.num[] pero no se informa la memoria en la métrica proc.mem[]. Por ejemplo:

$ ps-ef | grep kthreadd
       root 2 0 0 09:51 ? 00:00:00 [kthreadd]
       
       $ zabbix_get -s localhost -k 'proc.num[kthreadd]'
       1
       
       $ zabbix_get -s localhost -k 'proc.mem[kthreadd]'
       ZBX_NOTSUPPORTED: No se puede obtener la cantidad de memoria "VmSize".

Pero, ¿qué sucede si hay un proceso de usuario con el mismo nombre que un hilo del núcleo? Entonces podría verse así:

$ ps-ef | grep kthreadd
       root 2 0 0 09:51 ? 00:00:00 [kthreadd]
       zabbix 9611 6133 0 17:58 pts/1 00:00:00 ./kthreadd
       
       $ zabbix_get -s localhost -k 'proc.num[kthreadd]'
       2
       
       $ zabbix_get -s localhost -k 'proc.mem[kthreadd]'
       4157440

proc.num[] contó tanto el hilo del núcleo como el proceso del usuario. proc.mem[] informa memoria solo para el proceso del usuario y cuenta la memoria del hilo del kernel como si fuera 0. Esto es diferente del caso de arriba cuando se informó ZBX_NOTSUPPORTED.

Tenga cuidado al utilizar las métricas proc.mem[] y proc.num[] si el nombre del programa coincide con uno de los hilos.

Antes de poner parámetros en las métricas proc.mem[] y proc.num[], es posible que desee verificar los parámetros utilizando la métrica proc.num[] y el comando ps.