Vi har länge följt frågan om användningen av systemd i behållare. Redan 2014 skrev vår säkerhetsingenjör Daniel Walsh en artikel , och några år senare – en annan artikel med titeln , där han konstaterade att situationen inte har förbättrats särskilt mycket. Han skrev särskilt, "Tyvärr, även två år senare, om du googlar "Docker system", så dyker hans gamla artikel fortfarande upp först. Det betyder att det är dags att förändra något." Dessutom har vi redan berättat om .
I denna artikel kommer vi att visa vad som har förändrats sedan dess och hur Podman kan hjälpa oss med detta.
Det finns många anledningar till att köra systemd inom en behållare, såsom:
- Multitjänstbehållare – många vill flytta sina multitjänstapplikationer från virtuella maskiner och köra dem i behållare. Det skulle vara bäst att dela upp sådana applikationer i mikrotjänster, men inte alla kan eller har tid att göra det. Därför är det logiskt att köra sådana applikationer som tjänster som startas av systemd från enhetsfiler.
- Systemd-enhetsfiler – de flesta applikationer som körs inom behållare är byggda av kod som tidigare kördes på virtuella eller fysiska maskiner. Dessa applikationer har en enhetsfil som skrivits för dessa applikationer och vet hur de ska startas. Så det är bättre att starta tjänster med hjälp av stödda metoder, istället för att hacka sin egen init-tjänst.
- Systemd är en processhanterare. Den hanterar tjänster (stoppar, startar om tjänster eller dödar zombieprocesser) bättre än något annat verktyg.
Det finns dock också många anledningar till att inte köra systemd i behållare. Den främsta anledningen är att systemd/journald kontrollerar behållarnas output, och verktyg som eller räknar med att behållare skriver loggar direkt till stdout och stderr. Därför, om du avser att hantera behållare via orkestreringsverktyg som nämnts ovan, bör du noga överväga användning av systemd-baserade behållare. Dessutom har utvecklarna av Docker och Moby ofta varit kraftigt emot att använda systemd i behållare.
Ankomsten av Podman
Vi är glada att meddela att situationen äntligen har kommit i rullning. Teamet bakom containerlanseringen på Red Hat har beslutat att utveckla . Den fick namnet och erbjuder samma kommandoradsgränssnitt (CLI) som Docker. Nästan alla Docker-kommandon kan användas på samma sätt i Podman. Vi håller ofta seminarier som nu kallas , och den första bilden uppmanar till att skriva: alias docker=podman.
Många gör faktiskt så.
Vi är på inget sätt emot systemd-baserade containrar med vår Podman. Systemd används ofta som init-system i Linux, och att inte låta det fungera ordentligt i containrar betyder att ignorera hur tusentals människor är vana vid att köra containrar.
Podman vet vad som behövs för att systemd ska fungera i en container. Den behöver saker som montering av tmpfs på /run och /tmp. Den gillar när en "container"-miljö är aktiverad och den förväntar sig skrivbehörigheter i sin del av cgroup-katalogen och i mappen /var/log/journald.
Vid körning av en container där den första kommandot är init eller systemd, konfigurerar Podman automatiskt tmpfs och Cgroups för att säkerställa att systemd startar utan problem. För att blockera detta automatiska startläge används alternativet —systemd=false. Observera att Podman använder systemd-läget endast när den ser att en systemd- eller init-kommandot ska utföras.
Här är ett utdrag från manualen:
man podman run
…–systemd=true|false
Kör en container i systemd-läge. Som standard aktiverat.
Om ett systemd- eller init-kommando körs inuti containern kommer Podman att konfigurera tmpfs monteringspunkter i följande kataloger:
/run, /run/lock, /tmp, /sys/fs/cgroup/systemd, /var/lib/journal
Som standard kommer SIGRTMIN+3 att användas som stopp-signal.
Allt detta möjliggör att systemd fungerar i en sluten container utan några modifikationer.
NOTERA: systemd försöker att skriva till cgroup-filssystemet. Men SELinux förbjuder som standard containrar från att göra detta. För att tillåta skrivning, aktivera den logiska parametern container_manage_cgroup:
setsebool -P container_manage_cgroup true
Nu ser vi hur en Dockerfile ser ut för att köra systemd i en container med Podman:
# cat Dockerfile
FROM fedora
RUN dnf -y install httpd; dnf clean all; systemctl enable httpd
EXPOSE 80
CMD [ "/sbin/init" ]
Det var allt.
Nu bygger vi containern:
# podman build -t systemd .
Vi ber SELinux att tillåta systemd att modifiera Cgroups-konfigurationen:
# setsebool -P container_manage_cgroup true
Många glömmer förresten detta steg. Lyckligtvis behöver det endast göras en gång, och inställningen sparas efter omstart av systemet.
Nu startar vi bara containern:
# 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.
Klart, tjänsten har startat och fungerar:
$ curl localhost
<html xml_lang="en" lang="en">
…
</html>
OBS: Försök inte att upprepa detta på Docker! Där krävs det fortfarande mycket arbete för att köra denna typ av containrar genom demon. (Ytterligare fält och paket krävs för att allt ska fungera sömlöst i Docker, eller så måste det köras i en privilegierad container. För mer information, se .)
Ytterligare några fantastiska saker om Podman och systemd
Podman fungerar bättre än Docker i systemd-enhetsfiler
Om containrarna ska startas vid systemets uppstart, kan du enkelt sätta in de relevanta Podman-kommandona i systemd-enhetsfilen, så att den startar tjänsten och övervakar den. Podman använder den standardiserade finklyvningsmodellen (fork-exec) vid körning. Med andra ord är containerprocesserna underordnade Podmans process, så systemd kan enkelt övervaka dem.
Docker använder en klient-server-modell, och Docker-CLI-kommandon kan också placeras direkt i enhetsfilen. Men efter att Docker-klienten ansluter till Docker-demonen blir den (klienten) bara en annan process som hanterar stdin och stdout. Å sin sida har systemd ingen aning om förhållandet mellan Docker-klienten och containern som körs under Docker-demonen, och därför kan systemd i denna modell principiellt inte övervaka tjänsten.
Aktivering av systemd via socket
Podman hanterar korrekt aktivering via socket. Eftersom Podman använder fork-exec-modellen kan den vidarebefordra socket till sina underordnade containerprocesser. Det klarar Docker inte av, eftersom den använder klient-server-modellen.
Tjänsten varlink, som Podman använder för att interagera med fjärrklienter och containrar, aktiveras faktiskt via en socket. Paketet cockpit-podman, skrivet i Node.js och en del av projektet cockpit, gör det möjligt för användare att interagera med Podman-containrar genom ett webbgränssnitt. Webbdaemonen, som kör cockpit-podman, skickar meddelanden till varlink-socketen som övervakas av systemd. Därefter aktiverar systemd Podman-programmet för att ta emot meddelanden och börja hantera containrar. Aktivering av systemd via socket gör det möjligt att undvika att ha en ständigt körande daemon för att implementera fjärr-API.
Dessutom utvecklar vi en till klient för Podman kallad podman-remote, som implementerar samma Podman CLI men anropar varlink för att starta containrar. Podman-remote kan fungera över SSH-sessioner, vilket gör det möjligt att säkert interagera med containrar på olika maskiner. Med tiden planerar vi att inkludera podman-remote för att stödja MacOS och Windows tillsammans med Linux, så att utvecklare på dessa plattformar kan köra en Linux-virtuell maskin med Podman varlink och ha en fullständig känsla av att containrar körs på den lokala maskinen.
SD_NOTIFY
Systemd tillåter att starten av hjälptjänster skjuts upp tills den nödvändiga containertjänsten startas. Podman kan vidarebefordra SD_NOTIFY-socketen till den containeriserade tjänsten, så att tjänsten kan informera systemd om att den är redo att köra. Återigen kan Docker, som använder en klient-server-modell, inte göra detta på samma sätt.
Planer
Vi planerar att lägga till kommandot podman generate systemd CONTAINERID, som kommer att generera en systemd-enhetsfil för att hantera en specifik angiven container. Det här bör fungera både i root- och rootless-lägen för oprivilegierade containrar. Vi har till och med sett en förfrågan om att skapa en OCI-kompatibel runtime för systemd-nspawn.
Slutsats
Att köra systemd i en container är en fullt rimlig behov. Och tack vare Podman har vi äntligen en containerkörningsmiljö som inte är i konflikt med systemd, utan istället gör det enkelt att använda.
Källa: habr.com
