/ HOWTO

Entender y ajustar el uso de recursos en un Contenedor.


Algunos trucos para entender los recursos que usa un contenedor y ajustar el consumo de RAM y CPU.


Breve introducción a Docker y el uso de los recursos (CPU, RED, Memoria y Disco).

En un sistema operativo corriendo en un equipo real, todos los recursos disponibles (físicos) están asignados a ese sistema operativo. Todos los servidores reales que están funcionando tienen un S.O. corriendo en real, y sobre ese S.O. hay varias tecnologías para utilizar.

La opción mas común es (o era) la de tener un entorno de virtualización (Xen, VMware, VirtualBox y otros) instalado donde sobre esa capa corran equipos virtualizados.

En esta opción la monitorización se puede realizar en el equipo físico, en el S.O. físico, en las herramientas que nos provée la soluciónd e virtualización y en el último punto en el sistema operativo virtualizado.

Son varias capas funcionando en conjunto, todas acaban consumiendo recursos de un equipo físico disponible (CPU, Disco, Red y Memoria), aquí no hay nada nuevo bajo el sol.

Al aparecer Docker y justamente por su “nuevo” concepto, pasamos a tener procesos corriendo sobre un sistema operativo (real o virtualizado) que consumirán los recursos del S.O. que tenga debajo. No es muy distinto a las otras opciones pero un poco mas complicado de monitorizar.

Usando Docker, cada contenedor necesitará los mismos recursos para resolver las tareas que tenga que hacer, se activarán y desactivarán procesos internos, y también aparecerán procesos en nuestro equipo real.

Anteriormente existían conceptos de “jail” en Unix, donde un proceso estaba encapsulado y contenido en el sistema operativo.


Veamos ejemplos, abrimos una sesión de Bash en un contenedor (ce7aea05b590)

# docker exec -it ce7aea05b590 /bin/bash

Dentro del contenedor vemos los procesos corriendo.

# ps -A
  PID TTY          TIME CMD
    1 pts/0    00:00:00 bash
   38 pts/1    00:00:00 bash
   50 pts/1    00:00:00 ps

Ahora en otra sesión de nuestro equipo real, abrimos otra sesión contra el mismo contenedor. Veremos que aparece ahora el proceso 51.

# docker exec -it ce7aea05b590 /bin/bash
root@ce7aea05b590:/# ps -A
  PID TTY          TIME CMD
    1 pts/0    00:00:00 bash
   38 pts/1    00:00:00 bash
   51 pts/2    00:00:00 bash
   61 pts/2    00:00:00 ps
root@ce7aea05b590:/#

En otra sesión podemos dejar corriendo el comando “stats” y veremos los procesos.

# docker stats
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
ce7aea05b590        ubussh1             0.00%               3.332MiB / 7.685GiB   0.04%               2.96kB / 0B         0B / 0B             3

¿ Qué nos muestra nuestro S.O. real ? Podemos ver que en mis sesiones terminal, tengo corriendo 3 sesiones contra docker (al ser “docker” es el cliente)

┬─xfce4-terminal─┬─bash───su───bash───docker───9*[{docker}]
│                ├─bash───su───bash───docker───5*[{docker}]
│                ├─bash───su───bash───docker───8*[{docker}]

También en mi equipo real, podemos ver corriendo el demonio de Docker (dockerd)

├─dockerd─┬─docker-containe─┬─3*[docker-containe─┬─bash]
│         │                 │                    └─8*[{docker-containe}]]
│         │                 └─13*[{docker-containe}]
│         └─17*[{dockerd}]

Podemos ver las 3 sesiones de Bash que aunque estén corriendo en un contenedor, aparecen como procesos en mi equipo real.

Uno de los beneficios de “Docker”, procesos en lugar de servidores completos.

28499 pts/0    00:00:00 bash
30111 pts/0    00:00:00 bash
30119 pts/0    00:00:00 bash

Tres sesiones bash corriendo, se verán en cualquiera de ellas como

# top
top - 11:46:13 up  4:12,  0 users,  load average: 0.24, 0.48, 0.60
Tasks:   4 total,   1 running,   3 sleeping,   0 stopped,   0 zombie
%Cpu(s):  3.1 us,  0.6 sy,  0.0 ni, 96.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  8058104 total,   505852 free,  5294448 used,  2257804 buff/cache
KiB Swap:  4096120 total,  4096120 free,        0 used.  1694028 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
    1 root      20   0   18484   3400   2928 S   0.0  0.0   0:00.05 bash
   38 root      20   0   18492   3544   2976 S   0.0  0.0   0:00.02 bash
   51 root      20   0   18492   3456   2988 S   0.0  0.0   0:00.02 bash
   63 root      20   0   41192   3336   2864 R   0.0  0.0   0:00.00 top

Podemos obtener mas datos de uno de los contenedores corriendo.

# docker inspect ce7aea05b590
"Status": "running",
"Pid": 28499,
"Hostname": "ce7aea05b590",
"ExposedPorts": { "22/tcp" }

Como ocurre en Linux, también podremos obtener valores de pseudo-ficheros.

# cat /sys/fs/cgroup/cpuacct/docker/
ce7aea05b590455bf6108fe464c01a0d41f247545f90db4330b960b0c3410fcc/ cpuacct.usage_sys
cgroup.clone_children                                             cpuacct.usage_user
cgroup.procs                                                      cpu.cfs_period_us
cpuacct.stat                                                      cpu.cfs_quota_us
cpuacct.usage                                                     cpu.shares
cpuacct.usage_all                                                 cpu.stat
cpuacct.usage_percpu                                              notify_on_release
cpuacct.usage_percpu_sys                                          tasks
cpuacct.usage_percpu_user
# cat /sys/fs/cgroup/memory/docker/
ce7aea05b590455bf6108fe464c01a0d41f247545f90db4330b960b0c3410fcc/ memory.max_usage_in_bytes
cgroup.clone_children                                             memory.memsw.failcnt
cgroup.event_control                                              memory.memsw.limit_in_bytes
cgroup.procs                                                      memory.memsw.max_usage_in_bytes
memory.failcnt                                                    memory.memsw.usage_in_bytes
memory.force_empty                                                memory.move_charge_at_immigrate
memory.kmem.failcnt                                               memory.numa_stat
memory.kmem.limit_in_bytes                                        memory.oom_control
memory.kmem.max_usage_in_bytes                                    memory.pressure_level
memory.kmem.slabinfo                                              memory.soft_limit_in_bytes
memory.kmem.tcp.failcnt                                           memory.stat
memory.kmem.tcp.limit_in_bytes                                    memory.swappiness
memory.kmem.tcp.max_usage_in_bytes                                memory.usage_in_bytes
memory.kmem.tcp.usage_in_bytes                                    memory.use_hierarchy
memory.kmem.usage_in_bytes                                        notify_on_release
memory.limit_in_bytes                                             tasks
# cat /sys/fs/cgroup/pids/docker/
ce7aea05b590455bf6108fe464c01a0d41f247545f90db4330b960b0c3410fcc/ pids.current
cgroup.clone_children                                             pids.events
cgroup.procs                                                      pids.max
notify_on_release                                                 tasks
# cat /sys/fs/cgroup/devices/docker/
ce7aea05b590455bf6108fe464c01a0d41f247545f90db4330b960b0c3410fcc/ devices.deny
cgroup.clone_children                                             devices.list
cgroup.procs                                                      notify_on_release
devices.allow                                                     tasks

Veremos que para cada grupo de recursos, existe una carpeta con el Hash de un contenedor corriendo

En esa carpeta están los contadores de ese contenedor. Como ejemplo, los PIDs corriendo.

# cat /sys/fs/cgroup/devices/docker/ce7aea05b590455bf6108fe464c01a0d41f247545f90db4330b960b0c3410fcc/tasks
2734
2841
3960
28499

El tiempo usado por usuario y S.O desde el arranque.

# cat /sys/fs/cgroup/cpuacct/docker/cpuacct.stat
user 1692
system 651

Segundos usados en los CPUs desde el arranque.

# cat /sys/fs/cgroup/cpuacct/docker/ce7aea05b590455bf6108fe464c01a0d41f247545f90db4330b960b0c3410fcc/cpuacct.usage
732910231

Estadísticas sobre la memoria

# cat /sys/fs/cgroup/memory/docker/ce7aea05b590455bf6108fe464c01a0d41f247545f90db4330b960b0c3410fcc/memory.stat
cache 4096
rss 2256896
rss_huge 0
mapped_file 0
dirty 0
writeback 0
swap 0
pgpgin 4324
pgpgout 3772
pgfault 7829
pgmajfault 0
inactive_anon 1179648
active_anon 1077248
inactive_file 4096
active_file 0
unevictable 0
hierarchical_memory_limit 9223372036854771712
hierarchical_memsw_limit 9223372036854771712
total_cache 4096
total_rss 2256896
total_rss_huge 0
total_mapped_file 0
total_dirty 0
total_writeback 0
total_swap 0
total_pgpgin 4324
total_pgpgout 3772
total_pgfault 7829
total_pgmajfault 0
total_inactive_anon 1179648
total_active_anon 1077248
total_inactive_file 4096
total_active_file 0
total_unevictable 0

¿ Cómo modificamos los límites de uso de recursos en un contenedor ?

# docker container update ce7aea05b590 --memory=16M --memory-swap=-1 --cpuset-cpus=2

Luego vemos como quedaron los valores máximos.

# docker container inspect ce7aea05b590

Podemos usar “grep” para ver solo alguno de los valores.

# docker container inspect ce7aea05b590 | grep emory
# docker container inspect ce7aea05b590 | grep pusetC
# docker container inspect ce7aea05b590 | grep Swap
...
"MemorySwap": -1,
"CpusetCpus": "2",
"Memory": 16777216,
...

Existen unos pocos valores mas para ajustar,

--memory-swappiness.
--memory-swap.
--oom-kill-disable.

Y otros relacionados con realtime.


Subscríbete y recibirás los últimos artículos semanalmente en tu email.