Seguiamo da tempo il tema dell'utilizzo di systemd nei contenitori. Già nel 2014 il nostro ingegnere della sicurezza Daniel Walsh ha scritto un articolo , e un paio d'anni dopo – un altro, intitolato , in cui ha constatato che la situazione non era affatto migliorata. In particolare, scriveva che "sfortunatamente, anche due anni dopo, se cerchi 'Docker system', il primo risultato è ancora il suo vecchio articolo. Significa che è tempo di cambiare". Inoltre, abbiamo già parlato del .
In questo articolo mostreremo cosa è cambiato nel frattempo e come può aiutarci in questo Podman.
Ci sono molte ragioni per eseguire systemd all'interno di un contenitore, come ad esempio:
- contenitori multi-servizio – molti vogliono estrarre le loro applicazioni multiserver da macchine virtuali e farle funzionare in container. Sarebbe meglio, naturalmente, suddividere tali applicazioni in microservizi, ma non tutti lo sanno ancora fare o semplicemente non hanno tempo. Pertanto, eseguire tali applicazioni come servizi avviati da systemd tramite file di unità ha sicuramente senso.
- File di unità Systemd – la maggior parte delle applicazioni che funzionano all'interno dei container è composta da codice che precedentemente veniva eseguito su macchine virtuali o fisiche. Queste applicazioni dispongono di un file di unità, scritto appositamente per esse, che indica come avviarle. Pertanto, è comunque preferibile avviare i servizi utilizzando metodi supportati, piuttosto che forzare la propria init-service.
- Systemd è un gestore di processi. Gestisce i servizi (termina, riavvia i servizi o elimina processi zombie) meglio di qualsiasi altro strumento.
Tuttavia, ci sono anche molte ragioni per non eseguire systemd nei container. La principale è che systemd/journald controlla l'output dei container, e strumenti come o si aspettano che i container scrivano i log direttamente su stdout e stderr. Pertanto, se avete intenzione di gestire i container attraverso strumenti di orchestrazione come quelli menzionati sopra, dovete seriamente considerare l'uso di container basati su systemd. Inoltre, gli sviluppatori di Docker e Moby sono stati spesso fortemente contrari all'uso di systemd nei container.
L'arrivo di Podman
Siamo lieti di annunciare che la situazione ha finalmente fatto un passo avanti. Il team di Red Hat responsabile dell'esecuzione dei container ha deciso di sviluppare . Ha preso il nome di e offre la stessa interfaccia a linea di comando (CLI) di Docker. Praticamente tutti i comandi di Docker possono essere utilizzati allo stesso modo in Podman. Spesso organizziamo seminari che ora si chiamano , e la prima diapositiva invita a scrivere: alias docker=podman.
Molti lo fanno.
Con il nostro Podman non siamo assolutamente contrari ai container basati su systemd. Infatti, systemd è frequentemente utilizzato come sistema init di Linux, e non permettergli di funzionare correttamente nei container significa ignorare come migliaia di persone sono abituate a eseguire i container.
Podman sa come comportare affinché systemd funzioni correttamente all'interno di un contenitore. Ha bisogno di elementi come il montaggio di tmpfs su /run e /tmp. Apprezza che ci sia un ambiente "container" attivo e si aspetta i permessi di scrittura nella sua parte della directory cgroup e nella cartella /var/log/journald.
Al momento dell'avvio di un contenitore in cui il primo comando è init o systemd, Podman configura automaticamente tmpfs e Cgroups affinché l'avvio di systemd avvenga senza problemi. Per bloccare questo avvio automatico viene utilizzata l'opzione —systemd=false. Si prega di notare che Podman attiva la modalità systemd solo quando rileva l'intenzione di eseguire il comando systemd o init.
Ecco un estratto dal manuale:
man podman run
…–systemd=true|false
Avvio del contenitore in modalità systemd. Attivato di default.
Se all'interno del contenitore viene eseguito il comando systemd o init, Podman configura i punti di montaggio tmpfs nelle seguenti directory:
/run, /run/lock, /tmp, /sys/fs/cgroup/systemd, /var/lib/journal
Inoltre, SIGRTMIN+3 sarà utilizzato come segnale di arresto predefinito.
Tutto ciò consente a systemd di funzionare correttamente all'interno di un contenitore isolato senza alcuna modifica.
NOTA: systemd sta tentando scrivere nel file system cgroup. Tuttavia, SELinux per impostazione predefinita vieta ai container di farlo. Per consentire la scrittura, attiva l'opzione booleana container_manage_cgroup:
setsebool -P container_manage_cgroup true
Ora vediamo come appare il Dockerfile per eseguire systemd in un container utilizzando Podman:
# cat Dockerfile
FROM fedora
RUN dnf -y install httpd; dnf clean all; systemctl enable httpd
EXPOSE 80
CMD [ "/sbin/init" ]
Ecco fatto.
Ora costruiamo il container:
# podman build -t systemd .
Permettiamo a SELinux di consentire a systemd di modificare la configurazione dei Cgroups:
# setsebool -P container_manage_cgroup true
Molti, tra l'altro, dimenticano questo passaggio. Fortunatamente, è sufficiente farlo una sola volta e la configurazione verrà mantenuta dopo il riavvio del sistema.
Adesso avviamo semplicemente il 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.
Tutto, il servizio è in esecuzione e funziona:
$ curl localhost
<html xml_lang="en" lang="en">
…
</html>
NOTA: Non provare a ripetere questo su Docker! Sono ancora necessari alcuni passaggi complicati per eseguire questo tipo di container attraverso il demone. (Saranno necessari campi e pacchetti aggiuntivi affinché tutto funzioni senza problemi in Docker, oppure sarà necessario eseguire in un container privilegiato. Maggiori dettagli sono disponibili nell' .)
Altre due cose interessanti su Podman e systemd
Podman funziona meglio di Docker nei file unit di systemd
Se è necessario avviare i container all'avvio del sistema, è possibile semplicemente inserire i comandi appropriati di Podman nel file di unità systemd, che avvierà il servizio e lo monitorerà. Podman utilizza un modello di fork-exec standard durante l'esecuzione. In altre parole, i processi dei container sono figli del processo di Podman, quindi systemd può facilmente monitorarli.
Docker utilizza un modello client-server, e i comandi CLI di Docker possono essere inseriti direttamente nel file di unità. Tuttavia, una volta che il client Docker si connette al demone Docker, esso (il client) diventa solo un altro processo che gestisce stdin e stdout. A sua volta, systemd non ha alcuna conoscenza del legame tra il client Docker e il container che opera sotto il demone Docker, e quindi in questo modello systemd non può monitorare il servizio.
Attivazione di systemd tramite socket
Podman gestisce correttamente l'attivazione tramite socket. Poiché Podman utilizza il modello fork-exec, può passare il socket ai propri processi container figli. Docker non è in grado di farlo, poiché utilizza un modello client-server.
Il servizio varlink, utilizzato da Podman per consentire l'interazione tra client remoti e container, viene attivato tramite un socket. Il pacchetto cockpit-podman, scritto in Node.js e parte del progetto cockpit, consente di interagire con i container Podman attraverso un'interfaccia web. Il demone web su cui si basa cockpit-podman invia messaggi al socket varlink, che viene monitorato da systemd. A questo punto, systemd attiva il programma Podman per ricevere messaggi e avviare la gestione dei container. L'attivazione di systemd tramite socket permette di evitare un demone in esecuzione continua nell'implementazione delle API remote.
Inoltre, stiamo sviluppando un altro client per Podman chiamato podman-remote, che implementa lo stesso Podman CLI, ma utilizza varlink per avviare i container. Podman-remote può funzionare sopra sessioni SSH, permettendo un'interazione sicura con i container su diverse macchine. Nel tempo, intendiamo utilizzare podman-remote per supportare MacOS e Windows insieme a Linux, affinché gli sviluppatori su queste piattaforme possano eseguire una macchina virtuale Linux con Podman varlink attivo e avere la sensazione completa che i container siano in esecuzione sulla macchina locale.
SD_NOTIFY
Systemd consente di ritardare l'avvio dei servizi ausiliari fino a quando non viene avviato il servizio containerizzato di cui hanno bisogno. Podman può passare il socket SD_NOTIFY al servizio containerizzato, affinché questo servizio notifichi a systemd la propria prontezza a funzionare. Anche Docker, che utilizza un modello client-server, non è in grado di farlo.
In programma
Prevediamo di aggiungere il comando podman generate systemd CONTAINERID, che genererà un file di unità systemd per gestire un determinato container. Questo dovrebbe funzionare sia in modalità root che in modalità rootless per container non privilegiati. Abbiamo anche visto una richiesta per creare un ambiente di esecuzione compatibile con OCI per systemd-nspawn.
Conclusione
Eseguire systemd all'interno di un container è una necessità comprensibile. E grazie a Podman, abbiamo finalmente un ambiente di esecuzione per container che non è in conflitto con systemd, ma permette di utilizzarlo facilmente.
Fonte: habr.com
