Rulăm systemd într-un container

Am urmărit de mult timp subiectul utilizării systemd în containere. În 2014, inginerul nostru de securitate, Daniel Walsh, a scris un articol Executarea systemd într-un container Docker, iar câțiva ani mai târziu – altul, intitulat Executarea systemd într-un container non-privilegiat, în care el a constatat că situația nu s-a îmbunătățit semnificativ. În special, el scria că „din păcate, și după doi ani, dacă cauți „Docker system”, primul lucru care apare este tot articolul său vechi. Asta înseamnă că este timpul să schimbăm ceva”. În plus, am mai discutat despre conflictul dintre dezvoltatorii Docker și systemd.

Rulăm systemd într-un container

În acest articol, vom arăta ce s-a schimbat în ultimele timpuri și cum ne poate ajuta Podman în această privință.

Există multe motive pentru a rula systemd în interiorul unui container, cum ar fi:

  1. Containere multi-servicii – mulți doresc să își extragă aplicațiile multi-servicii din mașinile virtuale și să le ruleze în containere. Ar fi mai bine, desigur, să împărțim astfel de aplicații în microservicii, dar nu toată lumea știe să facă asta sau pur și simplu nu au timp. De aceea, rularea acestor aplicații sub formă de servicii, lansate de systemd din fișiere unit, are sens.
  2. Fișiere unit systemd – majoritatea aplicațiilor care rulează în containere sunt construite din cod care a fost anterior executat pe mașini virtuale sau fizice. Aceste aplicații au un fișier unit, care a fost scris pentru aceste aplicații și știe cum să le lanseze. Deci, este mai bine să lansați serviciile folosind metodele suportate, nu încercând să ‚spargeți’ propria dvs. serviciu init.
  3. Systemd este un manager de procese. Acesta gestionează serviciile (oprește, repornește servicii sau elimină procesele zombie) mai bine decât orice alt instrument.

Totuși, există și multe motive pentru a nu rula systemd în containere. Principalul este că systemd/journald controlează ieșirea containerelor, iar instrumentele precum Kubernetes sau OpenShift se așteaptă ca containerele să scrie loguri direct în stdout și stderr. Prin urmare, dacă intenționați să gestionați containerele prin instrumente de orchestration de tipul celor menționate mai sus, trebuie să luați în serios în considerare utilizarea containerelor bazate pe systemd. În plus, dezvoltatorii Docker și Moby au fost adesea împotriva utilizării systemd în containere.

Apariția Podmanului

Suntem bucuroși să vă informăm că situația s-a mișcat în sfârșit de la punctul mort. Echipa responsabilă în Red Hat pentru lansarea containerelor a decis să dezvolte un motor de containere propriu. Acesta a primit numele Podman și oferă aceeași interfață de linie de comandă (CLI) ca Docker. Practic, toate comenzile Docker pot fi utilizate în mod similar în Podman. Organizăm frecvent seminarii care acum se numesc Schimbăm Docker cu Podman, iar primul diapozitiv îi îndeamnă pe toți să scrie: alias docker=podman.

Mulți fac acest lucru.

Noi, cu Podman-ul nostru, nu avem nimic împotriva containerelor bazate pe systemd. Deoarece Systemd este adesea utilizat ca sistem init în Linux, a nu-i permite să funcționeze corect în containe înseamnă a ignora modul în care mii de oameni sunt obișnuiți să lanseze containere.

Podman știe ce trebuie făcut pentru ca systemd să funcționeze corect în container. Are nevoie de lucruri cum ar fi montarea tmpfs pe /run și /tmp. Îi place când mediu "container" este activat și așteaptă drepturi de scriere în partea sa din directorul cgroup și în folderul /var/log/journald.

Când un container este lansat, în care prima comandă este init sau systemd, Podman configurează automat tmpfs și Cgroups astfel încât lansarea systemd să decurgă fără probleme. Pentru a bloca această activare automată, se folosește opțiunea —systemd=false. Rețineți că Podman folosește modul systemd doar când detectează că trebuie să execute o comandă systemd sau init.

Iată un extras din manual:

man podman run
…

–systemd=true|false

Lansarea containerului în modul systemd. Activată implicit.

Dacă în interiorul containerului se execută o comandă systemd sau init, Podman va configura punctele de montare tmpfs în următoarele directoare:

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

De asemenea, ca semnal de oprire, implicit va fi utilizat SIGRTMIN+3.

Toate acestea permit systemd să funcționeze într-un container izolat fără modificări.

NOTĂ: systemd încearcă să scrie în sistemul de fișiere cgroup. Totuși, SELinux interzice, în mod implicit, containerelor să facă acest lucru. Pentru a permite scrierea, activați parametrul logic container_manage_cgroup:

setsebool -P container_manage_cgroup true

Acum, să vedem cum arată Dockerfile pentru a lansa systemd în container utilizând Podman:

# cat Dockerfile

FROM fedora

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

EXPOSE 80

CMD [ "/sbin/init" ]

Asta e tot.

Acum construim containerul:

# podman build -t systemd .

Spunem SELinux să permită systemd să modifice configurația Cgroups:

# setsebool -P container_manage_cgroup true

Mulți, de altfel, uită acest pas. Din fericire, este suficient să-l efectuezi o singură dată, iar configurația se păstrează după repornirea sistemului.

Acum pur și simplu lansăm containerul:

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

Gata, serviciul a fost lansat și funcționează:

$ curl localhost

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

…

</html>

NOTĂ: Nu încercați să repetați asta pe Docker! Acolo sunt în continuare necesare manevre complicate pentru a lansa acest tip de containere prin daemon. (Vor fi necesare câmpuri și pachete suplimentare pentru a face totul să funcționeze fără probleme în Docker, sau va fi necesar să rulați într-un container cu privilegii. Detalii în pe care l-ați citit.)

Încă câteva lucruri interesante despre Podman și systemd

Podman funcționează mai bine decât Docker în fișierele unității systemd

Dacă trebuie să lansezi containere la pornirea sistemului, poți insera pur și simplu comenzile corespunzătoare Podman în fișierul unității systemd; acesta va lansa serviciul și îl va monitoriza. Podman folosește modelul standard de fork-exec. Cu alte cuvinte, procesele containerului sunt fiice ale procesului Podman, astfel încât systemd le poate monitoriza cu ușurință.

Docker folosește modelul client-server, iar comenzile CLI Docker pot fi plasate direct în fișierul unității. Totuși, după ce clientul Docker se conectează la daemonul Docker, acesta (clientul) devine pur și simplu un alt proces care procesează stdin și stdout. La rândul său, systemd nu are nicio idee despre legătura dintre clientul Docker și containerul care rulează sub daemonul Docker, astfel încât, în cadrul acestui model, systemd nu poate în principiu să monitorizeze serviciul.

Activarea systemd prin socket

Podman gestionează corect activarea prin socket. Deoarece Podman folosește modelul fork-exec, poate trece socketul către procesele containerului său fiice. Docker nu face acest lucru, deoarece folosește modelul client-server.

Serviciul varlink, pe care Podman îl folosește pentru a interacționa cu clienții de la distanță, este activat de fapt printr-un socket. Pachetul cockpit-podman, scris în Node.js și parte a proiectului cockpit, permite utilizatorilor să interacționeze cu containerele Podman prin intermediul unei interfețe web. Demonul web, pe care rulează cockpit-podman, trimite mesaje către socketul varlink, care este ascultat de systemd. Apoi, systemd activează programul Podman pentru a primi mesajele și a începe gestionarea containerelor. Activarea systemd prin socket permite evitarea unui demon care rulează constant în implementarea API-urilor la distanță.

De asemenea, dezvoltăm un alt client pentru Podman numit podman-remote, care implementează aceeași interfață de linie de comandă Podman, dar apelează varlink pentru a porni containerele. Podman-remote poate funcționa deasupra sesiunilor SSH, permițând interacțiunea securizată cu containere pe diferite mașini. În timp, plănuim să extindem podman-remote pentru a suporta MacOS și Windows, alături de Linux, astfel încât dezvoltatorii de pe aceste platforme să poată porni o mașină virtuală Linux cu varlink Podman în funcțiune și să aibă senzația completă că containerele rulează pe mașina locală.

SD_NOTIFY

Systemd permite amânarea lansării serviciilor auxiliare până când începe serviciul containerizat de care au nevoie. Podman poate redirecționa socketul SD_NOTIFY către serviciul containerizat, astfel încât acesta să notifice systemd că este pregătit să funcționeze. Din nou, Docker, care utilizează modelul client-server, nu poate face acest lucru.

În planuri

Planificăm să adăugăm comanda podman generate systemd CONTAINERID, care va genera un fișier unit systemd pentru a gestiona un anumit container specificat. Acest lucru ar trebui să funcționeze atât în mod root, cât și în mod rootless pentru containerele neprivilegiate. Am văzut chiar și o solicitare pentru crearea unui mediu de execuție compatibil OCI systemd-nspawn.

Concluzie

Pornirea systemd într-un container este o necesitate destul de clară. Și datorită Podman, avem în sfârșit un mediu de rulare a containerelor care nu se opune lui systemd, ci permite utilizarea acestuia cu ușurință.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster