Почему conmon входит в другую группу, когда podman запускается с systemd?

11

Данный podman установлен в системе Linux и системном модуле с именем 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

И начался баз.сервис:

# systemctl daemon-reload
# systemctl start baz.service

Затем, когда я проверяю состояние модуля, я не вижу shили sleepпроцесс в 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
# ...

Я ожидал увидеть shи sleepдетей в моем статусе baz.service, потому что слышал, как люди из redhat говорят, что podman использует традиционную модель fork-exec.

Если podman сделал fork и exec, то не будут ли my shи sleepprocess дочерними по отношению к podman и были бы в той же группе, что и исходный процесс podman?

Я ожидал, что смогу использовать systemd и podman, чтобы иметь возможность управлять моими контейнерами без перехода детей к другому родителю и выхода из моего модуля baz.service ssystemd.

Глядя на выходе psя вижу , что shи sleepна самом деле дети другого процесса , который называется conmon. Я не уверен, откуда появился conmon или как он был запущен, но systemd не захватил его.

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

Из вывода ясно, что мой модуль baz.service не управляет цепочкой conmon -> sh -> sleep.

  • Чем podman отличается от модели сервера-докера?
  • Чем отличается приман подмана от контейнера докера?

Возможно, они оба являются средами выполнения контейнеров, и dockerdдемон - это то, от чего люди хотят избавиться.

Так что, возможно, докер это как:

  • Dockerd Daemon
  • докер кли
  • контейнерная среда выполнения

И Подман это как:

  • подман кли
  • время выполнения контейнера conmon

Так что, возможно, podman использует традиционную модель fork exec, но это не podman cli, который разветвляется и exec, это процесс conmon.

Я чувствую себя смущенным.

mbigras
источник
Об этом вопросе есть обсуждение в списке рассылки podman
mbigras

Ответы:

8

Основная идея podmanзаключается в том, чтобы уйти от централизованной архитектуры с помощью сверхмощного наблюдателя (например dockerd), где централизованный демон является единственной точкой отказа. Об этом даже есть хэштег - " #nobigfatdaemons ".

Как избежать централизованного управления контейнерами? Вы удаляете одного главного демона (снова dockerd) и запускаете контейнеры независимо (в конце дня контейнеры - это просто процессы, поэтому вам не нужен демон для их порождения).

Тем не менее, вам все еще нужен способ

  • собирать логи контейнера - кто-то должен держать stdoutи stderrконтейнер;
  • собрать код выхода контейнера - кто-то должен wait(2)указать PID контейнера 1;

Для этой цели каждый контейнер podman по-прежнему контролируется небольшим демоном, называемым conmon(из «контейнера-монитора»). Разница с демоном Docker заключается в том, что этот демон настолько мал, насколько это возможно (проверьте размер исходного кода ), и он создается для каждого контейнера. Если conmonдля одного контейнера происходит сбой, остальная часть системы остается неизменной.

Далее, как контейнер порождается?

Учитывая, что пользователь может захотеть запустить контейнер в фоновом режиме, как в Docker, podman runпроцесс дважды разветвляется и только потом выполняет 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

Промежуточный процесс между podman runи conmon(т. Е. Прямым родителем conmon- в приведенном выше примере это PID 8484) завершится и conmonбудет переименован init, таким образом становясь демоном самоуправления. После этого conmonтакже отключается среда выполнения (например runc) и, наконец, среда выполнения выполняет точку входа контейнера (например /bin/sh).

Когда контейнер работает, podman runон больше не требуется и может выйти, но в вашем случае он остается в сети, потому что вы не просили его отсоединиться от контейнера.

Далее, podmanиспользует cgroups для ограничения контейнеров. Это означает, что он создает новые cgroups для новых контейнеров и перемещает туда процессы . По правилам cgroups, процесс может быть членом только одной cgroup одновременно, и добавление процесса в некоторую cgroup удаляет его из другой cgroup (где это было ранее) в пределах той же иерархии. Таким образом, при запуске контейнера, окончательное расположение контрольных групп выглядят следующим образом : podman runостатки на контрольных группы из baz.service, созданных systemd, то conmonпроцесс помещается в своих контрольных группах, а также контейнерные процессы помещаются в их собственных контрольных группах:

$ 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

Примечание: PID 13075 выше на самом деле sleep 1процесс, порожденный после смерти PID 13043.

Надеюсь это поможет.

Данила Кивер
источник
1
«создает новые cgroups для новых контейнеров и перемещает туда процессы». Я не понимаю, почему podman выполняет эту работу вместо systemd. Можете ли вы объяснить, почему мы используем conmon для хранения stdout и stderr вместо systemd? Из изучения systemd я подумал, что целью systemd является управление процессами и выполнение таких задач, как захват stdout / stderr, определение состояния выхода, обработка перезапуска.
Мбиграс
2
Podman управляет cgroups, потому что он владеет контейнером и должен гарантировать, что контейнер будет работать независимо от того, какая у вас система инициализации. Systemd управляет cgroups для сервисов, потому что он владеет сервисами (и сервисы не должны управлять cgroups по умолчанию, хотя systemd поддерживает некоторые виды делегирования - см. Systemd.io/CGROUP_DELEGATION ). Если вы хотите, чтобы podman повторно использовал cgroups, созданные systemd для службы, должна быть поддержка со стороны podman, и в настоящее время я ее не вижу (хотя я могу ошибаться).
Данила
1
Что касается stdout/ stderrstreams - опять же, podmanвладеет контейнером и захватывает потоки контейнерного процесса. systemdвладеет сервисом и захватывают потоки основного процесса сервиса (в вашем случае, на systemdсамом деле захватывает stdout/ stderrо podman runпроцессе). Это работает именно так, как и должно работать, потому что conmonзахватывает потоки контейнера, podman runприсоединяется к ним conmon, systemdзахватывает потоки podman run, поэтому, наконец, все журналы из контейнера захватываются systemdи вы видите их в systemctl status baz.service.
Данила