Kör systemd i en container

Vi har följt ämnet att använda systemd i containrar under lång tid. Redan 2014 skrev vår säkerhetsingenjör Daniel Walsh en artikel Kör systemd i en Docker Container, och ett par år senare - en annan, som hette Kör systemd i en icke-privilegierad behållare, där han konstaterade att situationen inte hade förbättrats mycket. I synnerhet skrev han att "tyvärr, även två år senare, om du googlar "Docker system", är det första som kommer upp hans samma gamla artikel. Så det är dags att ändra något." Dessutom har vi redan pratat om konflikt mellan Docker och systemd utvecklare.

Kör systemd i en container

I den här artikeln kommer vi att visa vad som har förändrats över tiden och hur Podman kan hjälpa oss i denna fråga.

Det finns många anledningar till att köra systemd inuti en behållare, till exempel:

  1. Multiservicecontainrar – många människor vill dra ut sina multitjänstapplikationer från virtuella maskiner och köra dem i containrar. Det skulle naturligtvis vara bättre att dela upp sådana applikationer i mikrotjänster, men inte alla vet hur man gör detta än eller har helt enkelt inte tid. Därför är det helt logiskt att köra sådana applikationer som tjänster som lanseras av systemd från enhetsfiler.
  2. Systemd Unit Files – De flesta applikationer som körs i containrar är byggda från kod som tidigare kördes på virtuella eller fysiska maskiner. Dessa applikationer har en enhetsfil som är skriven för dessa applikationer och förstår hur de ska startas. Så det är fortfarande bättre att starta tjänster med stödda metoder, snarare än att hacka din egen init-tjänst.
  3. Systemd är processledare. Den hanterar tjänster (stänger av, startar om tjänster eller dödar zombieprocesser) bättre än något annat verktyg.

Som sagt, det finns många anledningar till att inte köra systemd i containrar. Det viktigaste är att systemd/journald styr utmatningen av containrar och verktyg som Kubernetes eller öppen växling förvänta sig att behållare skriver logg direkt till stdout och stderr. Därför, om du ska hantera behållare genom orkestreringsverktyg som de som nämns ovan, bör du allvarligt överväga att använda systembaserade behållare. Dessutom har Docker- och Moby-utvecklare ofta varit starkt motståndare till att använda systemd i containrar.

Podmans ankomst

Vi är glada att kunna rapportera att situationen äntligen har gått framåt. Teamet som ansvarar för att köra containrar på Red Hat bestämde sig för att utveckla din egen containermotor. Han fick ett namn poddman och erbjuder samma kommandoradsgränssnitt (CLI) som Docker. Och nästan alla Docker-kommandon kan användas i Podman på samma sätt. Vi genomför ofta seminarier, som nu kallas Ändra Docker till Podman, och den allra första bilden kräver att du skriver: alias docker=podman.

Många människor gör just det.

Min Podman och jag är inte på något sätt emot systembaserade behållare. När allt kommer omkring är Systemd det vanligaste Linux init-undersystemet, och att inte tillåta det att fungera korrekt i behållare innebär att ignorera hur tusentals människor är vana vid att köra behållare.

Podman vet vad man ska göra för att få systemd att fungera korrekt i en container. Den behöver saker som att montera tmpfs på /run och /tmp. Hon gillar att ha den "containeriserade" miljön aktiverad och förväntar sig skrivbehörigheter till sin del av cgroup-katalogen och till mappen /var/log/journald.

När du startar en container där det 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änd alternativet --systemd=false. Observera att Podman endast använder systemd-läge när den ser att den behöver köra ett systemd- eller init-kommando.

Här är ett utdrag ur manualen:

man podman springa
.

–systemd=true|false

Kör en container i systemd-läge. Aktiverad som standard.

Om du kör ett systemd- eller init-kommando inuti en container kommer Podman att konfigurera tmpfs-monteringspunkter i följande kataloger:

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

Standard stoppsignal kommer också att vara SIGRTMIN+3.

Allt detta gör att systemd kan köras i en sluten behållare utan några ändringar.

OBS: systemd försöker skriva till cgroup-filsystemet. SELinux förhindrar dock behållare från att göra detta som standard. För att aktivera skrivning, aktivera den booleska parametern container_manage_cgroup:

setsebool -P container_manage_cgroup sant

Titta nu på hur Dockerfilen ser ut för att köra systemd i en behållare med Podman:

# cat Dockerfile

FROM fedora

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

EXPOSE 80

CMD [ "/sbin/init" ]

Det är allt.

Nu sätter vi ihop behållaren:

# podman build -t systemd .

Vi säger åt SELinux att tillåta systemd att ändra Cgroups-konfigurationen:

# setsebool -P container_manage_cgroup true

Förresten, många människor glömmer detta steg. Lyckligtvis behöver detta bara göras en gång och inställningen sparas efter omstart av systemet.

Nu startar vi bara behållaren:

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

Det är allt, tjänsten är igång:

$ curl localhost

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

…

</html>

OBS: Prova inte detta på Docker! Där behöver du fortfarande dansa med en tamburin för att lansera den här typen av behållare genom demonen. (Ytterligare fält och paket kommer att krävas för att få allt att fungera sömlöst i Docker, eller så måste det köras i en privilegierad behållare. För detaljer, se artikeln.)

Ett par mer coola saker om Podman och systemd

Podman fungerar bättre än Docker i systemd-enhetsfiler

Om behållare behöver startas när systemet startar, kan du helt enkelt infoga lämpliga Podman-kommandon i systemd-enhetsfilen, som startar tjänsten och övervakar den. Podman använder standardmodellen fork-exec. Behållarprocesser är med andra ord barn till Podman-processen, så systemd kan enkelt övervaka dem.

Docker använder en klient-servermodell, och Docker CLI-kommandon kan också placeras direkt i en enhetsfil. Men när Docker-klienten ansluter till Docker-demonen, blir den (klienten) bara ytterligare en process som bearbetar stdin och stdout. Systemd har i sin tur ingen aning om kopplingen mellan Docker-klienten och behållaren som körs under kontroll av Docker-demonen, och därför, inom denna modell, kan systemd i princip inte övervaka tjänsten.

Aktiverar systemd via uttag

Podman hanterar aktivering via uttag korrekt. Eftersom Podman använder gaffel-exec-modellen kan den vidarebefordra socket till dess underordnade containerprocesser. Docker kan inte göra detta eftersom den använder en klient-servermodell.

Varlink-tjänsten som Podman använder för att kommunicera med fjärrklienter till containrar aktiveras faktiskt via en socket. Cockpit-podman-paketet, skrivet i Node.js och en del av cockpitprojektet, tillåter människor att interagera med Podman-behållare via ett webbgränssnitt. Webdemonen som kör cockpit-podman skickar meddelanden till en varlink-socket som systemd lyssnar på. Systemd aktiverar sedan Podman-programmet för att ta emot meddelanden och börja hantera containrar. Aktivering av systemd över en socket eliminerar behovet av en ständigt körande demon vid implementering av fjärr-API:er.

Dessutom utvecklar vi en annan Podman-klient som heter podman-remote, som implementerar samma Podman CLI men anropar varlink för att köra behållare. Podman-remote kan köras ovanpå SSH-sessioner, vilket gör att du säkert kan interagera med behållare på olika maskiner. Med tiden planerar vi att aktivera podman-remote för att stödja MacOS och Windows tillsammans med Linux, så att utvecklare på dessa plattformar kan köra en virtuell Linux-maskin med Podman varlink igång och ha den fulla upplevelsen av att behållare körs på den lokala maskinen.

SD_NOTIFY

Systemd låter dig skjuta upp lanseringen av extratjänster tills den containerservice de kräver startar. Podman kan vidarebefordra SD_NOTIFY-uttaget till den containeriserade tjänsten så att tjänsten meddelar systemd att den är redo att användas. Och återigen, Docker, som använder en klient-server-modell, kan inte göra detta.

I planerna

Vi planerar att lägga till kommandot podman generera systemd CONTAINERID, som kommer att generera en systemd enhetsfil för att hantera en specifik behållare. Detta bör fungera i både rot- och rotlöst läge för oprivilegierade behållare. Vi har till och med sett en begäran om en OCI-kompatibel systemd-nspawn runtime.

Slutsats

Att köra systemd i en container är ett förståeligt behov. Och tack vare Podman har vi äntligen en containerkörning som inte står i konflikt med systemd, men som gör den enkel att använda.

Källa: will.com

Lägg en kommentar