Esecuzione di systemd in un contenitore

Seguiamo da molto tempo l'argomento dell'utilizzo di systemd nei contenitori. Nel 2014, il nostro ingegnere della sicurezza Daniel Walsh ha scritto un articolo Esecuzione di systemd all'interno di un contenitore Docker, e un paio d'anni dopo - un altro, che si chiamava Esecuzione di systemd in un contenitore non privilegiato, in cui affermava che la situazione non era migliorata molto. In particolare, scrive che “purtroppo, anche due anni dopo, se si cerca su Google “sistema Docker”, la prima cosa che salta fuori è il suo stesso vecchio articolo. Quindi è ora di cambiare qualcosa”. Inoltre, ne abbiamo già parlato conflitto tra Docker e gli sviluppatori di systemd.

Esecuzione di systemd in un contenitore

In questo articolo mostreremo cosa è cambiato nel tempo e come Podman può aiutarci in questa materia.

Esistono molti motivi per eseguire systemd all'interno di un contenitore, ad esempio:

  1. Contenitori multiservizi – molte persone desiderano estrarre le proprie applicazioni multiservizio dalle macchine virtuali ed eseguirle in contenitori. Sarebbe meglio, ovviamente, suddividere tali applicazioni in microservizi, ma non tutti sanno ancora come farlo o semplicemente non hanno il tempo. Pertanto, eseguire applicazioni come servizi lanciati da systemd da file unit ha perfettamente senso.
  2. File di unità Systemd – La maggior parte delle applicazioni eseguite all'interno dei contenitori sono realizzate a partire da codice precedentemente eseguito su macchine virtuali o fisiche. Queste applicazioni hanno un file unitario scritto per queste applicazioni e comprende come dovrebbero essere avviate. Quindi è ancora meglio avviare i servizi utilizzando i metodi supportati, piuttosto che hackerare il proprio servizio init.
  3. Systemd è un gestore di processi. Gestisce i servizi (chiude, riavvia i servizi o uccide i processi zombie) meglio di qualsiasi altro strumento.

Detto questo, ci sono molti motivi per non eseguire systemd nei contenitori. Il principale è che systemd/journald controlla l'output di contenitori e strumenti simili kubernetes o OpenShift aspettarsi che i contenitori scrivano il log direttamente su stdout e stderr. Pertanto, se intendi gestire i contenitori tramite strumenti di orchestrazione come quelli menzionati sopra, dovresti prendere seriamente in considerazione l'utilizzo di contenitori basati su Systemd. Inoltre, gli sviluppatori Docker e Moby si sono spesso fermamente opposti all’utilizzo di systemd nei contenitori.

L'avvento di Podman

Siamo felici di annunciare che la situazione è finalmente andata avanti. Il team responsabile della gestione dei container in Red Hat ha deciso di svilupparlo il tuo motore container. Ha un nome Podman e offre la stessa interfaccia a riga di comando (CLI) di Docker. E quasi tutti i comandi Docker possono essere utilizzati in Podman allo stesso modo. Conduciamo spesso seminari, che ora si chiamano Cambiare Docker in Podman, e la primissima diapositiva richiede di scrivere: alias docker=podman.

Molti lo fanno.

Io e il mio Podman non siamo in alcun modo contrari ai contenitori basati su Systemd. Dopotutto, Systemd è il sottosistema init Linux più comunemente utilizzato e non consentirgli di funzionare correttamente nei contenitori significa ignorare il modo in cui migliaia di persone sono abituate a eseguire contenitori.

Podman sa cosa fare per far funzionare correttamente systemd in un contenitore. Ha bisogno di cose come montare tmpfs su /run e /tmp. Le piace avere l'ambiente "containerizzato" abilitato e si aspetta i permessi di scrittura sulla sua parte della directory cgroup e sulla cartella /var/log/journald.

Quando avvii un contenitore in cui il primo comando è init o systemd, Podman configura automaticamente tmpfs e Cgroups per garantire che systemd venga avviato senza problemi. Per bloccare questa modalità di avvio automatico, utilizzare l'opzione --systemd=false. Tieni presente che Podman utilizza la modalità systemd solo quando rileva che è necessario eseguire un comando systemd o init.

Ecco un estratto dal manuale:

l'uomo podman corre
...

–systemd=vero|falso

Esecuzione di un contenitore in modalità systemd. Abilitato per impostazione predefinita.

Se esegui un comando systemd o init all'interno di un contenitore, Podman configurerà i punti di montaggio tmpfs nelle seguenti directory:

/run, /run/lock, /tmp, /sys/fs/cgroup/systemd, /var/lib/journal

Inoltre il segnale di arresto predefinito sarà SIGRTMIN+3.

Tutto ciò consente a systemd di funzionare in un contenitore chiuso senza alcuna modifica.

NOTA: systemd tenta di scrivere sul filesystem cgroup. Tuttavia, SELinux impedisce ai contenitori di farlo per impostazione predefinita. Per abilitare la scrittura, abilitare il parametro booleano container_manage_cgroup:

setsebool -P container_manage_cgroup vero

Ora guarda come appare il Dockerfile per l'esecuzione di systemd in un contenitore utilizzando Podman:

# cat Dockerfile

FROM fedora

RUN dnf -y install httpd; dnf clean all; systemctl enable httpd

EXPOSE 80

CMD [ "/sbin/init" ]

Questo è tutto.

Ora assembliamo il contenitore:

# podman build -t systemd .

Diciamo a SELinux di consentire a systemd di modificare la configurazione di Cgroups:

# setsebool -P container_manage_cgroup true

A proposito, molte persone dimenticano questo passaggio. Fortunatamente, è necessario farlo solo una volta e l'impostazione viene salvata dopo aver riavviato il sistema.

Ora non ci resta che avviare il contenitore:

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

Questo è tutto, il servizio è attivo e funzionante:

$ curl localhost

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

…

</html>

NOTA: non provarlo su Docker! Lì devi ancora ballare con un tamburello per lanciare questo tipo di contenitori attraverso il demone. (Saranno necessari campi e pacchetti aggiuntivi per far sì che tutto funzioni perfettamente in Docker, oppure dovrà essere eseguito in un contenitore privilegiato. Per i dettagli, vedere Articolo.)

Un altro paio di cose interessanti su Podman e systemd

Podman funziona meglio di Docker nei file di unità systemd

Se è necessario avviare i contenitori all'avvio del sistema, è sufficiente inserire i comandi Podman appropriati nel file unit systemd, che avvierà il servizio e lo monitorerà. Podman utilizza il modello fork-exec standard. In altre parole, i processi contenitore sono figli del processo Podman, quindi systemd può monitorarli facilmente.

Docker utilizza un modello client-server e i comandi CLI di Docker possono anche essere inseriti direttamente in un file unitario. Tuttavia, una volta che il client Docker si connette al demone Docker, esso (il client) diventa semplicemente un altro processo che elabora stdin e stdout. A sua volta, systemd non ha idea della connessione tra il client Docker e il contenitore che viene eseguito sotto il controllo del demone Docker e quindi, all'interno di questo modello, systemd fondamentalmente non può monitorare il servizio.

Attivazione systemd tramite socket

Podman gestisce correttamente l'attivazione tramite socket. Poiché Podman utilizza il modello fork-exec, può inoltrare il socket ai processi contenitori figli. Docker non può farlo perché utilizza un modello client-server.

Il servizio varlink utilizzato da Podman per comunicare con i client remoti ai contenitori viene effettivamente attivato tramite un socket. Il pacchetto Cockpit-Podman, scritto in Node.js e parte del progetto Cockpit, consente alle persone di interagire con i contenitori Podman attraverso un'interfaccia web. Il demone web che esegue cockpit-podman invia messaggi a un socket varlink su cui systemd è in ascolto. Systemd attiva quindi il programma Podman per ricevere messaggi e iniziare a gestire i contenitori. L'attivazione di systemd su un socket elimina la necessità di un demone costantemente in esecuzione durante l'implementazione delle API remote.

Inoltre, stiamo sviluppando un altro client Podman chiamato podman-remote, che implementa la stessa CLI Podman ma chiama varlink per eseguire i contenitori. Podman-remote può essere eseguito su sessioni SSH, consentendoti di interagire in modo sicuro con contenitori su macchine diverse. Nel corso del tempo, prevediamo di abilitare podman-remote per supportare MacOS e Windows insieme a Linux, in modo che gli sviluppatori su tali piattaforme possano eseguire una macchina virtuale Linux con Podman varlink in esecuzione e avere l'esperienza completa che i contenitori sono in esecuzione sulla macchina locale.

SD_NOTIFICA

Systemd consente di posticipare il lancio dei servizi ausiliari fino all'avvio del servizio containerizzato richiesto. Podman può inoltrare il socket SD_NOTIFY al servizio containerizzato in modo che il servizio notifichi a systemd che è pronto per funzionare. E ancora una volta Docker, che utilizza un modello client-server, non può farlo.

i piani

Prevediamo di aggiungere il comando podman generate systemd CONTAINERID, che genererà un file di unità systemd per gestire un contenitore specifico specificato. Questo dovrebbe funzionare sia in modalità root che rootless per contenitori senza privilegi. Abbiamo anche visto una richiesta per un runtime systemd-nspawn compatibile con OCI.

conclusione

L'esecuzione di systemd in un contenitore è un'esigenza comprensibile. E grazie a Podman, finalmente abbiamo un runtime del contenitore che non entra in conflitto con systemd, ma lo rende facile da usare.

Fonte: habr.com

Aggiungi un commento