Pourquoi Conmon se trouve-t-il dans un autre groupe de contrôle lorsque podman est démarré avec systemd?

11

Étant donné que podman est installé sur un système linux et une unité systemd nommée 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

Et le baz.service a commencé:

# systemctl daemon-reload
# systemctl start baz.service

Ensuite, lorsque je vérifie l'état de l'unité, je ne vois pas le processus shou sleepdans le groupe de contrôle /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
# ...

Je m'attendais à voir le shet les sleepenfants dans mon statut baz.service parce que j'ai entendu des gens de redhat dire que podman utilise un modèle traditionnel de fork-exec.

Si podman a fait fork et exec, alors mon processus shet ne seraient-ils pas des sleepenfants de podman et seraient-ils dans le même groupe de contrôle que le processus podman d'origine?

Je m'attendais à pouvoir utiliser systemd et podman pour pouvoir gérer mes conteneurs sans que les enfants partent vers un parent différent et s'échappent de mon unité baz.service ssystemd.

En regardant la sortie de psje peux voir cela shet sleepsont en fait des enfants d'un processus différent appelé conmon. Je ne sais pas d'où vient Conmon, ni comment il a été démarré, mais SystemD ne l'a pas capturé.

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

D'après la sortie, il est clair que mon unité baz.service ne gère pas la chaîne conmon -> sh -> sleep.

  • En quoi podman est-il différent du modèle de serveur client docker?
  • En quoi le conmon de podman est-il différent du containerd de docker?

Peut-être sont-ils tous deux des runtimes de conteneurs et le dockerddémon est ce dont les gens veulent se débarrasser.

Alors peut-être que docker est comme:

  • démon dockerd
  • docker cli
  • exécution du conteneur containerd

Et podman est comme:

  • podman cli
  • exécution du conteneur conmon

Donc peut-être que podman utilise un modèle traditionnel de fork fork, mais ce n'est pas le podman cli qui forge et exécute, c'est le processus conmon.

Je suis confus.

mbigras
la source
Il y a une discussion sur cette question sur la liste de diffusion podman
mbigras

Réponses:

8

L'idée derrière podmanest de s'éloigner de l'architecture centralisée avec le superviseur super puissant (par exemple dockerd), où le démon centralisé est un point de défaillance unique. Il y a même un hashtag à ce sujet - " #nobigfatdaemons ".

Comment éviter la gestion centralisée des conteneurs? Vous supprimez le démon principal unique (à nouveau dockerd) et démarrez les conteneurs indépendamment (à la fin de la journée, les conteneurs ne sont que des processus, vous n'avez donc pas besoin du démon pour les générer).

Cependant, vous avez toujours besoin du moyen de

  • collecter les journaux du conteneur - quelqu'un doit tenir stdoutet stderrdu conteneur;
  • collecter le code de sortie du conteneur - quelqu'un doit wait(2)utiliser le PID 1 du conteneur;

A cet effet, chaque conteneur podman est toujours supervisé par un petit démon, appelé conmon(depuis "container monitor"). La différence avec le démon Docker est que ce démon est aussi petit que possible (vérifiez la taille du code source ), et il est généré par conteneur. Si conmonun conteneur tombe en panne, le reste du système reste inchangé.

Ensuite, comment le conteneur est généré?

Étant donné que l'utilisateur peut vouloir exécuter le conteneur en arrière-plan, comme avec Docker, le podman runprocessus se déroule deux fois et n'exécute ensuite que 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

Le processus intermédiaire entre podman runet conmon(c'est-à-dire le parent direct de conmon- dans l'exemple ci-dessus c'est le PID 8484) se terminera et conmonsera réparé par init, devenant ainsi un démon autogéré. Après cela, conmonbifurque également le runtime (par exemple runc) et, enfin, le runtime exécute le point d'entrée du conteneur (par exemple /bin/sh).

Lorsque le conteneur est en cours d'exécution, podman runn'est plus requis et peut se fermer, mais dans votre cas, il reste en ligne, car vous ne lui avez pas demandé de se détacher du conteneur.

Ensuite, podmanutilise des groupes de contrôle pour limiter les conteneurs. Cela signifie qu'il crée de nouveaux groupes de contrôle pour de nouveaux conteneurs et y déplace les processus . Selon les règles des cgroups, le processus peut être membre d'un seul cgroup à la fois, et l'ajout du processus à certains cgroup le supprime des autres cgroup (où il était précédemment) dans la même hiérarchie. Ainsi, lorsque le conteneur est démarré, la disposition finale des cgroups ressemble à ceci: podman runreste dans les cgroups de baz.service, créé par systemd, le conmonprocessus est placé dans ses propres cgroups et les processus conteneurisés sont placés dans leurs propres 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

Remarque: le PID 13075 ci-dessus est en fait un sleep 1processus, né après la mort du PID 13043.

J'espère que cela t'aides.

Danila Kiver
la source
1
"crée de nouveaux groupes de contrôle pour de nouveaux conteneurs et y déplace les processus" Je ne comprends pas pourquoi podman fait ce travail au lieu de systemd. Pouvez-vous ajouter une explication sur la raison pour laquelle nous utilisons conmon pour contenir stdout et stderr au lieu de systemd? En apprenant sur systemd, j'ai pensé que le but de systemd était de gérer les processus et de faire des tâches comme capturer stdout / stderr, comprendre l'état de sortie, gérer le redémarrage.
mbigras
2
Podman gère les cgroups car il possède le conteneur et doit garantir que le conteneur fonctionnera sans tenir compte du système d'initialisation dont vous disposez. Systemd gère les cgroups pour les services car il possède des services (et les services ne sont pas censés gérer les cgroups par défaut, bien que systemd supporte certains types de délégation - voir systemd.io/CGROUP_DELEGATION ). Si vous voulez que podman réutilise les cgroups créés par systemd pour le service, il doit y avoir un support du côté podman, et je n'en vois pas actuellement (même si je peux me tromper).
Danila Kiver
1
Quant à stdout/ stderrstreams - à nouveau, podmanpossède le conteneur et capture les flux du processus conteneurisé. systemdest propriétaire du service et capture les flux du processus principal du service (dans votre cas, systemdcapture stdout/ stderrdu podman runprocessus). Cela fonctionne exactement comme il devrait fonctionner, car conmoncapture les flux du conteneur, s'y podman runattache conmon, systemdcapture les flux de podman run, donc, finalement, tous les journaux du conteneur sont capturés par systemd, et vous les voyez dans systemctl status baz.service.
Danila Kiver