Por que o conmon está em um cgroup diferente quando o podman é iniciado com o systemd?


11

Dado que o podman está instalado em um sistema Linux e em uma unidade systemd chamada 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

E o baz.service começou:

# systemctl daemon-reload
# systemctl start baz.service

Então, quando verifico o status da unidade, não vejo o processo shnem sleepno 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
# ...

Eu esperava ver as crianças she sleepno meu status baz.service porque ouvi pessoas de redhat dizer que podman usa um modelo tradicional de fork-exec.

Se o podman fez fork e exec, meu processo she não sleepseriam filhos do podman e estariam no mesmo cgroup do processo original do podman?

Eu esperava poder usar o systemd e o podman para gerenciar meus contêineres sem que os filhos fossem para um pai diferente e escapassem da minha unidade baz.service ssystemd.

Olhando para a saída de ps, posso ver isso she sleepna verdade são filhos de um processo diferente chamado conmon. Não sei de onde veio o conmon ou como ele foi iniciado, mas o systemd não o capturou.

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

A partir da saída, fica claro que minha unidade baz.service não está gerenciando a cadeia de sono conmon -> sh ->.

  • Qual a diferença entre o podman e o modelo do servidor cliente docker?
  • Como o conmon de podman é diferente do container de docker?

Talvez eles sejam ambos tempos de execução de contêiner e o dockerddaemon é o que as pessoas querem se livrar.

Talvez o docker seja como:

  • daemon dockerd
  • docker cli
  • tempo de execução do contêiner

E podman é como:

  • podman cli
  • tempo de execução do contêiner conmon

Então, talvez o podman use um modelo tradicional de exec fork, mas não seja o podman cli que é bifurcado e exec, é o processo de conmon.

Eu me sinto confuso.


Há uma discussão sobre esta questão na lista de discussão podman: lists.podman.io/archives/list/podman@lists.podman.io/thread/...
mbigras

Respostas:


8

A idéia por trás disso podmané afastar-se da arquitetura centralizada com o superintendente super poderoso (por exemplo dockerd), onde o daemon centralizado é um ponto único de falha. Existe até uma hashtag sobre isso - " #nobigfatdaemons ".

Como evitar o gerenciamento centralizado de contêineres? Você remove o daemon principal único (novamente dockerd) e inicia os contêineres de forma independente (no final do dia, os contêineres são apenas processos, portanto você não precisa do daemon para gerá-los).

No entanto, você ainda precisa do caminho para

  • coletar os logs do contêiner - alguém tem que segurar stdoute stderrdo contêiner;
  • coletar o código de saída do contêiner - alguém precisa fazer wait(2)o PID 1 do contêiner;

Para esse propósito, cada contêiner do podman ainda é supervisionado por um pequeno daemon, chamado conmon(de "monitor de contêiner"). A diferença com o daemon do Docker é que ele é o menor possível (verifique o tamanho do código-fonte ) e é gerado por contêiner. Se conmonum contêiner travar, o restante do sistema não será afetado.

Em seguida, como o contêiner é gerado?

Considerando que o usuário pode querer executar o contêiner em segundo plano, como no Docker, o podman runprocesso bifurca-se duas vezes e só então é executado 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

O processo intermediário entre podman rune conmon(ou seja, o pai direto de conmon- no exemplo acima, é PID 8484) será encerrado e conmonserá reparado init, tornando-se assim um daemon autogerenciado. Depois disso, conmontambém obtém o tempo de execução (por exemplo runc) e, finalmente, o tempo de execução executa o ponto de entrada do contêiner (por exemplo /bin/sh).

Quando o contêiner está em execução, podman runnão é mais necessário e pode sair, mas, no seu caso, permanece on-line, porque você não solicitou a desanexação do contêiner.

Em seguida, podmanutiliza cgroups para limitar os contêineres. Isso significa que ele cria novos cgroups para novos contêineres e move os processos para lá . Pelas regras dos cgroups, o processo pode ser membro de apenas um cgroup de cada vez, e adicionar o processo a algum cgroup o remove de outro cgroup (onde estava anteriormente) dentro da mesma hierarquia. Portanto, quando o contêiner é iniciado, o layout final dos cgroups é semelhante ao seguinte: podman runpermanece em cgroups do baz.service, criado por systemd, o conmonprocesso é colocado em seus próprios cgroups e os processos em contêineres são colocados em seus próprios 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: O PID 13075 acima é realmente um sleep 1processo, gerado após a morte do PID 13043.

Espero que isto ajude.


11
"cria novos cgroups para novos contêineres e move os processos para lá" Eu não entendo por que o podman está fazendo esse trabalho em vez do systemd. Você pode adicionar uma explicação sobre por que usamos o conmon para manter stdout e stderr em vez de systemd? Ao aprender sobre o systemd, pensei que o objetivo do systemd é gerenciar processos e executar tarefas como capture stdout / stderr, descobrir o status da saída e lidar com a reinicialização.
mbigras

2
O Podman gerencia os cgroups porque ele é o proprietário do contêiner e tem que garantir que o contêiner funcione independentemente do sistema de inicialização que você possui. O Systemd gerencia cgroups para serviços porque possui serviços (por padrão, os serviços não devem gerenciar cgroups, embora o systemd suporte alguns tipos de delegação - consulte systemd.io/CGROUP_DELEGATION ). Se você quiser que o podman reutilize os cgroups criados pelo systemd para o serviço, deve haver um suporte do lado do podman, e atualmente não vejo um (embora eu possa errar).
Danila Kiver

11
Quanto a stdout/ stderrfluxos - novamente, podmanpossui o contêiner e captura os fluxos do processo em contêiner. systemdpossui o serviço e captura os fluxos do processo principal do serviço (no seu caso, systemdrealmente captura stdout/ stderrdo podman runprocesso). Isso funciona exatamente como deveria funcionar, porque conmoncaptura os fluxos do contêiner, podman runanexa a conmon, systemdcaptura os fluxos de podman runmodo que, finalmente, todos os logs do contêiner são capturados systemde você os vê systemctl status baz.service.
Danila Kiver
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.