Estamos acompanhando há muito tempo a utilização do systemd em contêineres. Já em 2014, nosso engenheiro de segurança Daniel Walsh escreveu um artigo , e alguns anos depois – outro, intitulado , no qual ele constatou que a situação não melhorou muito. Em particular, ele escreveu que "infelizmente, dois anos depois, se você pesquisar por 'Docker system', o primeiro resultado ainda é o seu antigo artigo. Portanto, está na hora de algo mudar". Além disso, já falamos sobre .
Neste artigo, vamos mostrar o que mudou ao longo do tempo e como o Podman pode nos ajudar com isso.
Existem muitas razões para executar o systemd dentro de um contêiner, como:
- Contêineres multi-serviço – muitos desejam tirar suas aplicações multi-serviço das máquinas virtuais e executá-las em contêineres. Seria melhor, é claro, dividir essas aplicações em microsserviços, mas nem todos ainda têm esse conhecimento ou simplesmente não têm tempo. Portanto, executar essas aplicações como serviços, iniciados pelo systemd a partir de arquivos de unidade, faz bastante sentido.
- Arquivos de unidade do Systemd – a maioria das aplicações que operam dentro dos contêineres é composta por código que anteriormente rodava em máquinas virtuais ou físicas. Essas aplicações têm um arquivo de unidade, que foi escrito para elas e entende como precisam ser iniciadas. Portanto, é preferível iniciar serviços utilizando métodos suportados, em vez de tentar burlar seu próprio serviço init.
- Systemd é um gerenciador de processos. Ele gerencia serviços (encerra, reinicia serviços ou elimina processos zumbis) melhor do que qualquer outra ferramenta.
Por outro lado, há também muitas razões para não executar o systemd em contêineres. A principal é que o systemd/journald controla a saída dos contêineres, enquanto ferramentas como ou esperam que os contêineres escrevam logs diretamente no stdout e stderr. Portanto, se você planeja gerenciar contêineres por meio de ferramentas de orquestração como as mencionadas, deve considerar cuidadosamente o uso de contêineres baseados no systemd. Além disso, os desenvolvedores do Docker e Moby muitas vezes se opuseram veementemente ao uso do systemd em contêineres.
A Chegada do Podman
Temos o prazer de informar que a situação finalmente avançou. A equipe responsável na Red Hat pelo gerenciamento de containers decidiu desenvolver . O nome dado é e oferece a mesma interface de linha de comando (CLI) que o Docker. Quase todos os comandos do Docker podem ser usados da mesma forma no Podman. Frequentemente realizamos seminários que agora se chamam , e o primeiro slide nos exorta a escrever: alias docker=podman.
Muitos assim o fazem.
Nós, com nosso Podman, não nos opomos a containers baseados em systemd. De fato, o Systemd é frequentemente utilizado como sistema init no Linux, e impedir seu funcionamento adequado em containers significa ignorar como milhares de pessoas costumam iniciar containers.
O Podman sabe o que fazer para que o systemd funcione corretamente no container. Ele precisa de coisas como montagem tmpfs em /run e /tmp. Ele aprecia um ambiente “containerizado” e espera permissões de escrita em sua parte do diretório cgroup e na pasta /var/log/journald.
Ao iniciar um container com init ou systemd como o primeiro comando, o Podman configura automaticamente tmpfs e Cgroups para garantir que a inicialização do systemd ocorra sem problemas. Para desativar esse modo automático, usa-se a opção —systemd=false. Observe que o Podman utiliza o modo systemd apenas quando detecta que é necessário executar um comando systemd ou init.
Aqui está um trecho do manual:
man podman run
…–systemd=true|false
Executa o container no modo systemd. Ativado por padrão.
Se um comando systemd ou init for executado dentro do container, 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 sinal de parada padrão será SIGRTMIN+3.
Tudo isso permite que o systemd funcione em um container isolado sem quaisquer modificações.
NOTA: o systemd tenta escrever no sistema de arquivos cgroup. No entanto, o SELinux por padrão proíbe que os containers façam isso. Para permitir a gravação, ative a variável booleana container_manage_cgroup:
setsebool -P container_manage_cgroup true
Agora veja como fica um Dockerfile para iniciar o systemd em um container usando 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 vamos construir o container:
# podman build -t systemd .
Dizemos ao SELinux para permitir que o systemd modifique a configuração dos Cgroups:
# setsebool -P container_manage_cgroup true
Aliás, muitos esquecem dessa etapa. Felizmente, isso precisa ser feito apenas uma vez e a configuração é mantida após a reinicialização do sistema.
Agora, basta iniciar 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.
Pronto, o serviço foi iniciado e está em funcionamento:
$ curl localhost
<html xml_lang="en" lang="en">
…
</html>
NOTA: Não tente repetir isso no Docker! Ainda são necessárias manobras complicadas para iniciar esse tipo de contêiner por meio do daemon. (Serão necessárias configurações adicionais e pacotes para que tudo funcione perfeitamente no Docker, ou será necessário executar em um contêiner privilegiado. Para mais detalhes, consulte .)
Mais algumas coisas legais sobre Podman e systemd
Podman funciona melhor que Docker em arquivos de unidade do systemd
Se os contêineres precisam ser iniciados na inicialização do sistema, você pode simplesmente inserir os comandos correspondentes do Podman no arquivo de unidade do systemd, que iniciará o serviço e o monitorará. O Podman utiliza o modelo padrão de fork-exec. Em outras palavras, os processos dos contêineres são filhos do processo do Podman, portanto, o systemd pode monitorá-los facilmente.
O Docker usa um modelo cliente-servidor, e os comandos da CLI do Docker também podem ser diretamente colocados no arquivo de unidade. No entanto, após o cliente do Docker se conectar ao daemon do Docker, ele (o cliente) se torna apenas mais um processo lidando com stdin e stdout. Por sua vez, o systemd não tem conhecimento da conexão entre o cliente do Docker e o contêiner que está sendo executado sob o daemon do Docker, e, portanto, dentro desse modelo, o systemd não pode monitorar o serviço.
Ativação do systemd via socket
O Podman trata corretamente a ativação via socket. Como o Podman utiliza o modelo fork-exec, ele pode passar o socket para seus processos filhos do contêiner. O Docker não consegue fazer isso, pois utiliza o modelo cliente-servidor.
O serviço varlink, que o Podman utiliza para interagir com clientes remotos nos contêineres, na verdade é ativado por meio de um socket. O pacote cockpit-podman, escrito em Node.js e parte do projeto cockpit, permite que as pessoas interajam com os contêineres Podman através de uma interface web. O daemon web, que roda o cockpit-podman, envia mensagens ao socket varlink, que é escutado pelo systemd. Após isso, o systemd ativa o programa Podman para receber as mensagens e começar a gerenciar os contêineres. A ativação do systemd através do socket permite evitar um daemon em execução constante durante a implementação de APIs remotas.
Além disso, estamos desenvolvendo outro cliente para o Podman chamado podman-remote, que implementa o mesmo Podman CLI, mas chama o varlink para iniciar contêineres. O Podman-remote pode funcionar sobre sessões SSH, permitindo uma interação segura com contêineres em várias máquinas. Com o tempo, planejamos utilizar o podman-remote para dar suporte a MacOS e Windows, além do Linux, para que os desenvolvedores nessas plataformas possam iniciar uma máquina virtual Linux com o Podman varlink funcionando e ter a completa impressão de que os contêineres estão sendo executados em sua máquina local.
SD_NOTIFY
O systemd permite adiar o início de serviços auxiliares até que o serviço contêinerizado necessário seja iniciado. O Podman pode passar o socket SD_NOTIFY para o serviço contêinerizado, permitindo que esse serviço notifique o systemd sobre sua prontidão. E novamente, o Docker, que utiliza um modelo cliente-servidor, não consegue fazer isso.
Planos
Planejamos adicionar o comando podman generate systemd CONTAINERID, que irá gerar um arquivo de unidade systemd para gerenciar um contêiner específico definido. Isso deve funcionar tanto nos modos root quanto rootless para contêineres não privilegiados. Nós até vimos um pedido para a criação de um ambiente de execução compatível com OCI usando systemd-nspawn.
Conclusão
Executar o systemd em um contêiner é uma necessidade bastante compreensível. E graças ao Podman, finalmente temos um ambiente de execução de contêineres que não é hostil ao systemd, mas que permite usá-lo facilmente.
Fonte: habr.com
