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.