Executando o systemd em um contêiner

Há muito tempo que acompanhamos o tópico do uso do systemd em contêineres. Em 2014, nosso engenheiro de segurança Daniel Walsh escreveu um artigo Executando o systemd em um contêiner Docker, e alguns anos depois - outro, que foi chamado Executando o systemd em um contêiner sem privilégios, no qual afirmou que a situação não melhorou muito. Em particular, ele escreveu que “infelizmente, mesmo dois anos depois, se você pesquisar “sistema Docker” no Google, a primeira coisa que aparece é o mesmo velho artigo. Então é hora de mudar alguma coisa.” Além disso, já falamos sobre conflito entre desenvolvedores Docker e systemd.

Executando o systemd em um contêiner

Neste artigo mostraremos o que mudou ao longo do tempo e como o Podman pode nos ajudar nessa questão.

Existem muitos motivos para executar o systemd dentro de um contêiner, como:

  1. Contêineres multisserviços – muitas pessoas desejam extrair seus aplicativos multisserviços de máquinas virtuais e executá-los em contêineres. Seria melhor, claro, dividir tais aplicações em microsserviços, mas nem todo mundo sabe como fazer isso ainda ou simplesmente não tem tempo. Portanto, executar aplicativos como serviços iniciados pelo systemd a partir de arquivos unitários faz todo o sentido.
  2. Arquivos de unidade Systemd – A maioria dos aplicativos executados em contêineres são criados a partir de código executado anteriormente em máquinas virtuais ou físicas. Esses aplicativos possuem um arquivo de unidade que foi escrito para esses aplicativos e entende como eles devem ser iniciados. Portanto, ainda é melhor iniciar serviços usando métodos suportados, em vez de hackear seu próprio serviço init.
  3. Systemd é um gerenciador de processos. Ele gerencia serviços (desliga, reinicia serviços ou elimina processos zumbis) melhor do que qualquer outra ferramenta.

Dito isto, há muitos motivos para não executar o systemd em contêineres. A principal delas é que o systemd/journald controla a saída de contêineres e ferramentas como Kubernetes ou OpenShift espere que os contêineres gravem log diretamente em stdout e stderr. Portanto, se você pretende gerenciar contêineres por meio de ferramentas de orquestração como as mencionadas acima, considere seriamente o uso de contêineres baseados em systemd. Além disso, os desenvolvedores do Docker e do Moby frequentemente se opõem fortemente ao uso do systemd em contêineres.

A vinda de Podman

Temos o prazer de informar que a situação finalmente avançou. A equipe responsável pela execução de containers na Red Hat decidiu desenvolver seu próprio mecanismo de contêiner. Ele ganhou um nome Podman e oferece a mesma interface de linha de comando (CLI) do Docker. E quase todos os comandos do Docker podem ser usados ​​no Podman da mesma maneira. Frequentemente realizamos seminários, que agora são chamados Mudando Docker para Podman, e o primeiro slide exige a escrita: alias docker=podman.

Muitos o fazem.

Meu Podman e eu não somos contra contêineres baseados em systemd. Afinal, Systemd é o subsistema init do Linux mais comumente usado, e não permitir que ele funcione corretamente em contêineres significa ignorar como milhares de pessoas estão acostumadas a executar contêineres.

Podman sabe o que fazer para que o systemd funcione corretamente em um contêiner. Ele precisa de coisas como montar tmpfs em/run e/tmp. Ela gosta de ter o ambiente "containerizado" habilitado e espera permissões de gravação em sua parte do diretório cgroup e na pasta /var/log/journald.

Quando você inicia um contêiner no qual o primeiro comando é init ou systemd, o Podman configura automaticamente tmpfs e Cgroups para garantir que o systemd seja iniciado sem problemas. Para bloquear este modo de inicialização automática, use a opção --systemd=false. Observe que o Podman só usa o modo systemd quando percebe que precisa executar um comando systemd ou init.

Aqui está um trecho do manual:

homem podman corre
...

–systemd = verdadeiro | falso

Executando um contêiner no modo systemd. Habilitado por padrão.

Se você executar um comando systemd ou init dentro de um contêiner, o Podman configurará 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 seja executado em um contêiner fechado sem nenhuma modificação.

NOTA: o systemd tenta gravar no sistema de arquivos cgroup. No entanto, o SELinux impede que os contêineres façam isso por padrão. Para habilitar a gravação, habilite o parâmetro booleano container_manage_cgroup:

setsebool -P container_manage_cgroup verdadeiro

Agora veja a aparência do Dockerfile para executar o systemd em um contêiner 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 montamos 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

Aliás, muita gente se esquece dessa etapa. Felizmente, isso só precisa ser feito uma vez e a configuração é salva após a reinicialização do sistema.

Agora é só iniciar o container:

# 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.

É isso, o serviço está instalado e funcionando:

$ curl localhost

<html  xml_lang="en" lang="en">

…

</html>

NOTA: Não tente isso no Docker! Lá você ainda precisa dançar com um pandeiro para lançar esse tipo de contêiner através do daemon. (Campos e pacotes adicionais serão necessários para que tudo isso funcione perfeitamente no Docker, ou precisará ser executado em um contêiner privilegiado. Para obter detalhes, consulte статье.)

Mais algumas coisas legais sobre Podman e systemd

Podman funciona melhor que Docker em arquivos de unidade systemd

Se os contêineres precisarem ser iniciados quando o sistema inicializar, você pode simplesmente inserir os comandos Podman apropriados no arquivo da unidade systemd, que iniciará o serviço e o monitorará. Podman usa o modelo fork-exec padrão. Em outras palavras, os processos contêineres são filhos do processo Podman, portanto o systemd pode monitorá-los facilmente.

O Docker usa um modelo cliente-servidor e os comandos CLI do Docker também podem ser colocados diretamente em um arquivo de unidade. No entanto, uma vez que o cliente Docker se conecta ao daemon Docker, ele (o cliente) se torna apenas mais um processo que manipula stdin e stdout. Por sua vez, o systemd não tem ideia da conexão entre o cliente Docker e o contêiner que roda sob o controle do daemon Docker e, portanto, dentro desse modelo, o systemd fundamentalmente não pode monitorar o serviço.

Ativando o systemd via soquete

Podman lida corretamente com a ativação via soquete. Como o Podman usa o modelo fork-exec, ele pode encaminhar o soquete para seus processos de contêiner filho. O Docker não pode fazer isso porque usa um modelo cliente-servidor.

O serviço varlink que Podman usa para se comunicar com clientes remotos em contêineres é, na verdade, ativado por meio de um soquete. O pacote cockpit-podman, escrito em Node.js e parte do projeto cockpit, permite que as pessoas interajam com os contêineres Podman por meio de uma interface web. O daemon da web executando cockpit-podman envia mensagens para um soquete varlink que o systemd escuta. O Systemd então ativa o programa Podman para receber mensagens e começar a gerenciar contêineres. A ativação do systemd por meio de um soquete elimina a necessidade de um daemon em execução constante ao implementar APIs remotas.

Além disso, estamos desenvolvendo outro cliente Podman chamado podman-remote, que implementa a mesma CLI do Podman, mas chama varlink para executar contêineres. O Podman-remote pode ser executado em sessões SSH, permitindo que você interaja com segurança com contêineres em diferentes máquinas. Com o tempo, planejamos permitir que o podman-remote suporte MacOS e Windows junto com Linux, para que os desenvolvedores nessas plataformas possam executar uma máquina virtual Linux com o Podman varlink em execução e ter a experiência completa de que os contêineres estão sendo executados 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 systemd que está pronto para operar. E, novamente, o Docker, que usa um modelo cliente-servidor, não pode fazer isso.

Nos planos

Planejamos adicionar o comando podman generate systemd CONTAINERID, que irá gerar um arquivo de unidade systemd para gerenciar um contêiner específico especificado. Isso deve funcionar nos modos root e sem root para contêineres sem privilégios. Vimos até uma solicitação de um tempo de execução systemd-nspawn compatível com OCI.

Conclusão

Executar o systemd em um contêiner é uma necessidade compreensível. E graças ao Podman, finalmente temos um tempo de execução de contêiner que não entra em conflito com o systemd, mas que o torna fácil de usar.

Fonte: habr.com

Adicionar um comentário