¿Por qué está Conmon en un cgroup diferente cuando podman se inicia con systemd?

11

Dado podman se instala en un sistema Linux y una unidad systemd llamada baz.service:

# /etc/systemd/system/baz.service
[Service]
ExecStart=/usr/bin/podman run --rm --tty --name baz alpine sh -c 'while true; do date; sleep 1; done'
ExecStop=/usr/bin/podman stop baz

Y comenzó el servicio baz.ser:

# systemctl daemon-reload
# systemctl start baz.service

Luego, cuando puedo comprobar el estado de la unidad no veo el sho sleepproceso en el cgroup /system.slice/baz.service

# systemctl status baz
● baz.service
   Loaded: loaded (/etc/systemd/system/baz.service; static; vendor preset: enabl
   Active: active (running) since Sat 2019-08-10 05:50:18 UTC; 14s ago
 Main PID: 16910 (podman)
    Tasks: 9
   Memory: 7.3M
      CPU: 68ms
   CGroup: /system.slice/baz.service
           └─16910 /usr/bin/podman run --rm --tty --name baz alpine sh -c while
# ...

Yo estaba esperando a ver el she sleephijos en mi estado baz.service porque he oído a la gente de decir RedHat podman utiliza un modelo tradicional tenedor-ejecutivo.

Si podman hizo fork y exec, ¿entonces mi shy sleepproceso no serían hijos de podman y estarían en el mismo cgroup que el proceso original de podman?

Esperaba poder usar systemd y podman para poder administrar mis contenedores sin que los niños se vayan a un padre diferente y escapen de mi unidad baz.service ssystemd.

Mirando la salida de pspuedo ver eso shy en sleeprealidad son hijos de un proceso diferente llamado conmon. No estoy seguro de dónde vino Conmon, o cómo se inició, pero systemd no lo capturó.

# ps -Heo user,pid,ppid,comm
# ...
root     17254     1   podman
root     17331     1   conmon
root     17345 17331     sh
root     17380 17345       sleep

De la salida está claro que mi unidad baz.service no está administrando la cadena de sueño conmon -> sh ->.

  • ¿En qué se diferencia Podman del modelo de servidor del cliente de Docker?
  • ¿En qué se diferencia el conmon de Podman de los contenedores de Docker?

Tal vez ambos son tiempos de ejecución de contenedores y el dockerddemonio es de lo que la gente quiere deshacerse.

Entonces quizás Docker es como:

  • dockerd daemon
  • docker cli
  • contenedor contenedor tiempo de ejecución

Y podman es como:

  • podman cli
  • tiempo de ejecución del contenedor conmon

Entonces, tal vez podman use un modelo tradicional de ejecución de horquilla, pero no es el cli de podman el que se bifurca y el ejecutivo, es el proceso de conmon.

Me siento confundido.

mbigras
fuente
Hay una discusión sobre esta pregunta en la lista de correo de podman
mbigras

Respuestas:

8

Toda la idea detrás podmanes alejarse de la arquitectura centralizada con el supervisor superpotente (por ejemplo dockerd), donde el demonio centralizado es un punto único de falla. Incluso hay un hashtag sobre esto: " #nobigfatdaemons ".

¿Cómo evitar la gestión centralizada de contenedores? Eliminas el demonio principal único (de nuevo dockerd) y comienzas los contenedores de forma independiente (al final del día, los contenedores son solo procesos, por lo que no necesitas el demonio para generarlos).

Sin embargo, aún necesita el camino para

  • recolectar los registros del contenedor: alguien tiene que sostener stdouty stderrdel contenedor;
  • recoger el código de salida del contenedor: alguien tiene que hacerlo wait(2)en el PID 1 del contenedor;

Para este propósito, cada contenedor podman todavía es supervisado por un pequeño demonio, llamado conmon(desde "monitor de contenedor"). La diferencia con el demonio Docker es que este demonio es lo más pequeño posible (verifique el tamaño del código fuente ) y se genera por contenedor. Si conmonpor un contenedor se bloquea, el resto del sistema no se ve afectado.

A continuación, ¿cómo se genera el contenedor?

Teniendo en cuenta que el usuario puede querer ejecutar el contenedor en segundo plano, como con Docker, el podman runproceso se bifurca dos veces y solo luego se ejecuta conmon:

$ strace -fe trace=fork,vfork,clone,execve -qq podman run alpine
execve("/usr/bin/podman", ["podman", "run", "alpine"], 0x7ffeceb01518 /* 30 vars */) = 0
...
[pid  8480] clone(child_stack=0x7fac6bffeef0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tid=[8484], tls=0x7fac6bfff700, child_tidptr=0x7fac6bfff9d0) = 8484
...
[pid  8484] clone(child_stack=NULL, flags=CLONE_VM|CLONE_VFORK|SIGCHLD <unfinished ...>
[pid  8491] execve("/usr/bin/conmon", ... <unfinished ...>
[pid  8484] <... clone resumed>)        = 8491

El proceso intermedio entre podman runy conmon(es decir, el padre directo de conmon- en el ejemplo anterior es PID 8484) saldrá y conmonserá reparentado por init, convirtiéndose así en demonio autogestionado. Después de esto, conmontambién se bifurca el tiempo de ejecución (por ejemplo runc) y, finalmente, el tiempo de ejecución ejecuta el punto de entrada del contenedor (por ejemplo /bin/sh).

Cuando el contenedor se está ejecutando, podman runya no es necesario y puede salir, pero en su caso permanece en línea, porque no le pidió que se separara del contenedor.

A continuación, podmanutiliza cgroups para limitar los contenedores. Esto significa que crea nuevos cgroups para nuevos contenedores y mueve los procesos allí . Según las reglas de cgroups, el proceso puede ser miembro de un solo cgroup a la vez, y al agregar el proceso a algún cgroup lo elimina de otro cgroup (donde estaba anteriormente) dentro de la misma jerarquía. Entonces, cuando se inicia el contenedor, el diseño final de cgroups se ve así: podman runpermanece en cgroups del baz.service, creado por systemd, el conmonproceso se coloca en sus propios cgroups, y los procesos en contenedores se colocan en sus propios cgroups:

$ ps axf
<...>
 1660 ?        Ssl    0:01 /usr/bin/podman run --rm --tty --name baz alpine sh -c while true; do date; sleep 1; done
 1741 ?        Ssl    0:00 /usr/bin/conmon -s -c 2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6 <...>
 1753 pts/0    Ss+    0:02  \_ sh -c while true; do date; sleep 1; done
13043 pts/0    S+     0:00      \_ sleep 1
<...>

$ cd /sys/fs/cgroup/memory/machine.slice
$ ls -d1 libpod*
libpod-2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6.scope
libpod-conmon-2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6.scope

$ cat libpod-2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6.scope/cgroup.procs 
1753
13075

$ cat libpod-conmon-2f56e37a0c5ca6f4282cc4c0f4c8e5c899e697303f15c5dc38b2f31d56967ed6.scope/cgroup.procs 
1741

Nota: PID 13075 anterior es en realidad un sleep 1proceso, generado después de la muerte de PID 13043.

Espero que esto ayude.

Danila Kiver
fuente
1
"crea nuevos cgroups para nuevos contenedores y mueve los procesos allí" No entiendo por qué Podman está haciendo ese trabajo en lugar de systemd. ¿Puedes agregar una explicación sobre por qué usamos conmon para contener stdout y stderr en lugar de systemd? Al aprender acerca de systemd, pensé que el propósito de systemd es administrar procesos y realizar tareas como capturar stdout / stderr, averiguar el estado de salida, manejar el reinicio.
mbigras
2
Podman administra cgroups porque posee el contenedor y tiene que garantizar que el contenedor funcionará sin tener en cuenta qué sistema de inicio tiene. Systemd administra cgroups para servicios porque posee servicios (y se supone que los servicios no administran cgroups de manera predeterminada, aunque systemd admite algunos tipos de delegación; consulte systemd.io/CGROUP_DELEGATION ). Si desea que podman reutilice los cgroups creados por systemd para el servicio, debe haber un soporte del lado de podman, y actualmente no veo ninguno (aunque puedo equivocarme).
Danila Kiver
1
En cuanto a stdout/ stderrstreams, nuevamente, podmanposee el contenedor y captura los streams del proceso en contenedor. systemdposee el servicio y captura las secuencias del proceso principal del servicio (en su caso, systemdrealmente captura stdout/ stderrdel podman runproceso). Esto funciona exactamente como debería funcionar, porque conmoncaptura los flujos del contenedor, se podman runconecta a ellos conmon, systemdcaptura los flujos de podman run, por lo que, finalmente, todos los registros del contenedor son capturados systemdy usted los ve systemctl status baz.service.
Danila Kiver