Temos acompanhado o tópico do uso do systemd em contêineres há muito tempo. Em 2014, nosso engenheiro de segurança Daniel Walsh escreveu um artigo chamado
Running systemd dentro de um Docker Container e, alguns anos depois, outro artigo chamado
Running systemd in a container não privilegiado , no qual afirmou que a situação não era muito melhorado. Em particular, ele escreveu que “infelizmente, e dois anos depois, se você pesquisar no sistema Docker, a primeira coisa que aparece é o mesmo artigo antigo dele. Então é hora de mudar alguma coisa. ” Além disso, já conversamos sobre o
conflito entre os desenvolvedores do Docker e o systemd .

Neste artigo, mostraremos o que mudou nos últimos tempos e como o Podman pode nos ajudar nesse assunto.
Existem muitos motivos para executar o systemd dentro de um contêiner, como:
- Contêineres multisserviços - muitas pessoas desejam obter seus aplicativos multisserviços de máquinas virtuais e executá-los em contêineres. Seria melhor, é claro, dividir esses aplicativos em microsserviços, mas nem todos podem fazê-lo ainda ou simplesmente não há tempo. Portanto, iniciar esses aplicativos na forma de serviços lançados pelo systemd a partir de arquivos de unidades faz todo o sentido.
- Arquivos de unidade Systemd - a maioria dos aplicativos em execução dentro de contêineres é compilada a partir de código executado anteriormente em máquinas virtuais ou físicas. Esses aplicativos possuem um arquivo de unidade que foi gravado para esses aplicativos e compreende como executá-los. Portanto, é melhor iniciar os serviços usando os métodos suportados, em vez de invadir seu próprio serviço init.
- Systemd é um gerenciador de processos. Ele gerencia serviços (encerra, reinicia serviços ou rastreia processos zumbis) melhor do que qualquer outra ferramenta.
Existem muitos motivos para não executar o systemd em contêineres. O principal é que systemd / journald controla a saída de contêineres, e ferramentas como
Kubernetes ou
OpenShift esperam que contêineres gravem o log diretamente no stdout e stderr. Portanto, se você pretende gerenciar contêineres por meio de ferramentas de orquestração como as mencionadas acima, precisará considerar seriamente o uso de contêineres com base no systemd. Além disso, os desenvolvedores do Docker e Moby costumavam se opor fortemente ao uso do systemd em contêineres.
A vinda de Podman
Temos o prazer de anunciar que a situação finalmente saiu do papel. A equipe responsável pelo lançamento de contêineres na Red Hat decidiu desenvolver
seu próprio mecanismo de contêiner . Ele recebeu o nome de
Podman e oferece a mesma interface de linha de comando (CLI) do Docker. E quase todos os comandos do Docker podem ser usados da mesma maneira no Podman. Freqüentemente realizamos seminários, que agora são chamados de
Change Docker para Podman , e o primeiro slide incentiva você a se registrar: alias docker = podman.
Muitos fazem isso.
Meu Podman e eu não somos de forma alguma contra contêineres baseados em systemd. Afinal, o Systemd é mais frequentemente usado como o subsistema init do Linux, e não deixá-lo funcionar normalmente em contêineres significa ignorar a maneira como milhares de pessoas estão acostumadas a executar contêineres.
Podman sabe o que fazer para que o systemd funcione corretamente no contêiner. Ela precisa de coisas como montar tmpfs em / run e / tmp. Ela gosta quando o ambiente “contêiner” está ativado e aguarda permissões de gravação para sua parte do diretório cgroup e para a pasta / var / log / journald.
Ao iniciar um contêiner no qual init ou systemd é o primeiro comando, o Podman configura automaticamente tmpfs e Cgroups para que o systemd inicie sem problemas. Para bloquear esse modo de inicialização automática, use a opção --systemd = false. Observe que o Podman usa o modo systemd somente quando considerar necessário executar o comando systemd ou init.
Aqui está um trecho do manual:
homem podman correr
...
–Systemd = true | false
Executando o contêiner no modo systemd. Ativado por padrão.
Se um comando systemd ou init for executado dentro do contêiner, o Podman configurará os pontos de montagem tmpfs nos seguintes diretórios:
/ run, / run / lock, / tmp, / sys / fs / cgroup / systemd, / var / lib / journal
Além disso, o SIGRTMIN + 3 será usado como um sinal de parada por padrão.
Tudo isso permite que o systemd trabalhe em um contêiner fechado sem nenhuma modificação.
NOTA: systemd está tentando gravar no sistema de arquivos cgroup. No entanto, o SELinux, por padrão, impede que os contêineres façam isso. Para ativar a gravação, ative o parâmetro em lote container_manage_cgroup:
setsebool -P container_manage_cgroup true
Agora veja como é o Dockerfile para executar o systemd no contêiner ao usar o Podman:
# cat Dockerfile FROM fedora RUN dnf -y install httpd; dnf clean all; systemctl enable httpd EXPOSE 80 CMD [ "/sbin/init" ]
Isso é tudo.
Agora colete o contêiner:
# podman build -t systemd .
Dizemos ao SELinux para permitir que o systemd modifique a configuração do Cgroups:
# setsebool -P container_manage_cgroup true
Muitos, a propósito, esquecem esta etapa. Felizmente, basta fazer isso apenas uma vez e a configuração é salva após a reinicialização do sistema.
Agora basta executar o contêiner:
# podman run -ti -p 80:80 systemd systemd 239 running in system mode. (+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN2 -IDN +PCRE2 default-hierarchy=hybrid) Detected virtualization container-other. Detected architecture x86-64. Welcome to Fedora 29 (Container Image)! Set hostname to <1b51b684bc99>. Failed to install release agent, ignoring: Read-only file system File /usr/lib/systemd/system/systemd-journald.service:26 configures an IP firewall (IPAddressDeny=any), but the local system does not support BPF/cgroup based firewalling. Proceeding WITHOUT firewalling in effect! (This warning is only shown for the first loaded unit using IP firewalling.) [ OK ] Listening on initctl Compatibility Named Pipe. [ OK ] Listening on Journal Socket (/dev/log). [ OK ] Started Forward Password Requests to Wall Directory Watch. [ OK ] Started Dispatch Password Requests to Console Directory Watch. [ OK ] Reached target Slices. … [ OK ] Started The Apache HTTP Server.
Tudo, o serviço começou e funciona:
$ curl localhost <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> … </html>
NOTA: Não tente repetir isso no Docker! Lá, ainda são necessárias danças com um pandeiro para lançar esses recipientes através de um demônio. (Campos e pacotes adicionais serão necessários para que isso funcione perfeitamente no Docker ou precisará ser executado em um contêiner privilegiado. Consulte o
artigo para
obter detalhes .)
Mais algumas coisas legais sobre Podman e systemd
O Podman funciona melhor que o docker em arquivos de unidades systemd
Se os contêineres precisarem ser iniciados na inicialização do sistema, você poderá simplesmente inserir os comandos Podman apropriados no arquivo de unidade systemd, que iniciará o serviço e o monitorará. Podman usa o modelo fork-exec padrão. Em outras palavras, os processos do contêiner são afiliados ao processo do Podman, para que o systemd possa monitorá-los facilmente.
O Docker usa o modelo cliente-servidor, e os comandos da CLI do Docker também podem ser colocados diretamente no arquivo da unidade. No entanto, depois que o cliente do Docker se conecta ao daemon do Docker, ele (o cliente) se torna apenas outro processo que processa stdin e stdout. Por sua vez, o systemd não tem idéia sobre a conexão entre o cliente do Docker e o contêiner que está executando o daemon do Docker e, portanto, nesse modelo, o systemd não pode monitorar fundamentalmente o serviço.
Ativação do Systemd via soquete
Podman realiza corretamente a ativação através de um soquete. Como o Podman usa o modelo fork-exec, ele pode encaminhar um soquete para seus processos de contêiner filho. O Docker não sabe como, porque usa um modelo cliente-servidor.
O serviço varlink que o Podman usa para interagir com clientes remotos com contêineres é realmente ativado pelo soquete. O pacote cockpit-podman, escrito em Node.js e parte do projeto do cockpit, permite que as pessoas interajam com os contêineres do Podman por meio de uma interface da web. O daemon da web em que o cockpit-podman está executando envia mensagens para o soquete varlink em que o systemd está ouvindo. Depois disso, o systemd ativa o programa Podman para receber mensagens e começar a gerenciar contêineres. A ativação do systemd por meio de um soquete permite que você fique sem um daemon em constante funcionamento ao implementar APIs remotas.
Além disso, estamos desenvolvendo outro cliente para o Podman, chamado podman-remote, que implementa a mesma CLI do Podman, mas chama o varlink para lançar contêineres. O Podman-remote pode funcionar em cima de sessões SSH, o que permite que você interaja com segurança com contêineres em máquinas diferentes. Com o tempo, planejamos usar o podman-remote para oferecer suporte ao MacOS e Windows junto ao Linux, para que os desenvolvedores nessas plataformas possam executar a máquina virtual Linux com o Podman varlink em execução e ter a sensação total de que os contêineres estão em execução na máquina local.
SD_NOTIFY
O Systemd permite adiar o lançamento de serviços auxiliares até que o serviço em contêiner necessário seja iniciado. Podman pode encaminhar o soquete SD_NOTIFY para o serviço em contêiner, para que o serviço notifique o sistema sobre sua disponibilidade para o trabalho. E novamente, o Docker, usando o modelo cliente-servidor, não sabe como.
Nos planos
Planejamos adicionar o comando podman generate systemd CONTAINERID, que gerará o arquivo de unidade systemd para gerenciar um contêiner específico. Isso deve funcionar nos modos raiz e sem raiz para contêineres sem privilégios. Até vimos uma solicitação para criar um tempo de execução systemd-nspawn compatível com OCI.
Conclusão
A execução do systemd em um contêiner é uma necessidade compreensível. E, graças ao Podman, finalmente temos um ambiente de lançador de contêiner que não é hostil ao systemd, mas facilita o uso.