Systemd indítása Ubuntu 20.04-es Docker konténerben

Borító kép powerpointtal szerkesztve

A Systemd egy olyan init rendszer és processzkezelő, ami rengeteg funkciót ellát és nem telepítjük Docker konténerben, ha van más mód. És általában van más mód. De előfordulhat, hogy mégis szükséges konténerben alkalmazni, mert egy olyan szoftvert kell telepíteni majd egy szerveren, amit jó lenne kipróbálni előtte. Ansible esetén gyakori megoldás. De az is lehet, hogy tényleg tegnapra kell valami és valamiért nem opció a hoszt rendszerre telepítés, viszont nincs idő jól megoldani. Ilyenkor marad a konténerben, csomagkezelővel telepítés, ami viszont gyakran Systemd-t igényel. Bemutatok egy megoldást Ubuntu 20.04-es konténerben és kitérek arra is, mi bajt okozhat a grafikus felületünknek a hibás konfiguráció.

A cikkben készített forráskód elérhető a GitHub-on: https://github.com/itsziget/tutorial-linux-signals/tree/step-05

Tartalomjegyzék

Bevezető

[Tartalom]

Már az elején kiemelném, hogy az itt bemutatott megoldás ugyan működik nálam és kijelöli az utat a hasonló igények megvalósítására, de minden hoszt rendszeren lehetnek eltérések, így eltérő hibák is. A Systemd valóban nem arra lett eredetileg tervezve, hogy konténerben működjön, de idővel megjelent némi Docker támogatás benne, amit be is fogok mutatni. Mindent azonban ez sem old meg. Bár igyekeztem általános megoldást nyújtani, az általam használt Ubuntu 18.04-es hoszt rendszertől eltérő hosztokon lehetnek más felcsatolandó mappák vagy paraméterek, ahogy egy Centos vagy Fedora image-ben is szükséges lehet más konfiguráció is.

Az első működő Dockerfile Systemd-hez

[Tartalom]

Mindenek előtt szükség lesz egy Dockerfile fájlra. Mivel szeretném külön kezelni az alap telepítést és a további kiegészítéseket, több Dockerfile fájlom lesz. A következő rövid Dockerfile.v1 névre fog hallgatni és érdemes egy üres mappát létrehozni a fájloknak, hogy ne legyen semmi felesleges a Docker build contextben.

FROM ubuntu:20.04

RUN apt-get update \
 && apt-get install -y --no-install-recommends \
    systemd

STOPSIGNAL SIGRTMIN+3

CMD ["/bin/systemd"]

Tehát az ubuntu:20.04 image-ből kiindulva elég a systemd csomagot telepíteni. Ezután viszont az alapértelmezett, leállításra szolgáló signalt is meg kell változtatni. A Systemd a nevű signalt hallgat, így ezt kellett definiálni. Végül pedig a CMD direktívával be is állítom a konténer első számú processzeként. A Dockerfile tehát nagyon egyszerű, így tudunk fókuszálni arra, ami a nagyobb problémát jelenti számunkra./p>

Az első Systemd konténer indítása

[Tartalom]

Először is kell egy image-et kreálni a definícióból:

docker build -t localhost/ubuntu-2004-systemd:v1 -f Dockerfile.v1 .

A konténer indítását pedig megkísérlem a következő paranccsal:

docker run -it --rm --name systemd localhost/ubuntu-2004-systemd:v1

Ennyire persze nem lesz egyszerű ennek az indítása, hiszen a systemd-nek egyedi igényei vannak. Az első hibaüzenet pedig valószínűleg valami ilyesmi lesz:

Failed to mount tmpfs at /run: Operation not permitted
Failed to mount tmpfs at /run/lock: Operation not permitted
[!!!!!!] Failed to mount API filesystems.
Freezing execution.

Itt az első két sorra kell felfigyelni. A Systemd tehát azt várja, hogy a /run és a /run/lock mappák tmpfs-en legyenek. Ha nincsenek, akkor a mount műveletre jogosultság hibát kap. A docker run parancsnak viszont átadható a --tmpfs paraméterrel, hogy mely mappákat szeretnénk tmpfs-ként felcsatolni. Előtte viszont egy új terminálban bejelentkezve le kellene törölni a konténert vagy a --rm paraméterrel indítás miatt elég akár csak leállítani is a docker kill systemd utasítással. Majd pedig jöhet az újabb konténer:

docker run -it --rm --name systemd \
  --tmpfs /run \
  --tmpfs /run/lock \
  localhost/ubuntu-2004-systemd:v1

Bár már az elején is lehet egy figyelmeztetés, arra később térek ki. Most inkább nézzük a nagyobb, pirossal jelzett hibát, ami után megint nem tud továbbmenni az indulási folyamat:

Failed to create /docker/074a179e247370c547fc291bdeac25161fb01f4ee90bbbda9a6ce5110d6b698f/init.scope control group: Read-only file system
Failed to allocate manager object: Read-only file system
[!!!!!!] Failed to allocate manager object.
Freezing execution.

Itt persze az első sorban levő útvonalban szereplő hash mindenkinél más lesz, de ami érdekesebb, az a "control group: Read-only file system". Ezen a ponton emlékezhetünk arra, hogy a Docker konténerek működésének egyik alapelve a "Control group", avagy röviden "cgroup". A control groupoknak a mappája a hoszt operációs rendszeren a /sys/fs/cgroup útvonalon van. Ezt pedig fel is csatolhatjuk. Persze előbb ismét futtatni kell új terminálban a docker kill systemd parancsot, mert másképp nem jutunk ki a konténerből.

docker run -it --rm --name systemd \
  --tmpfs /run \
  --tmpfs /run/lock \
  -v /sys/fs/cgroup:/sys/fs/cgroup \
  localhost/ubuntu-2004-systemd:v1

Erre, ha minden igaz, eltűntek a piros hibaüzenetek, de valami továbbra sem jó. Két sárga hibaüzenet is van, de ha már úgyis mindegyik "csak" sárga figyelmeztetés, akkor nézzük a legelsőt, ami eddig is volt, csak átugrottam:

Failed to set up the root directory for shared mount propagation: Operation not permitted

Most kétségbeesetten nyúlhatna a --privileged paraméterhez az, aki már dolgozott Dockerrel és találkozott már olyan programokkal, amik extra jogosultságokat igényelnek, mivel erre már nincs hirtelen ötlet és "talán ez megoldja, ha nem is szép". Valóban, szinte minden leírásban, oktatóanyagban megjelenik ez a paraméter. Van viszont egy probléma azon túl is, hogy ez túlzottan nagy jogosultságot adhat és valójában valószínűleg eleve emiatt a magas jogosultság miatt történhet meg, ami egy Desktop operációs rendszeren futtatott Systemd konténernél megtörténhet. Mivel száz százalékig én sem értem az okot, előfordulhat, hogy nem mindenki találkozik vele, de a grafikus felületről kijelentkeztethet a hoszt operációs rendszer. Ez azoknál nem gond, akik egy szerver gépen dolgoznak de lokálisan tesztelve akár a munkahelyen egy konténert, kellemetlen lehet egy ilyen eset. Pláne demo alatt, hogy "próbáljuk ki, mi baj lehet". A következő parancsot tehát vagy NE futtasd, ha az adott gépen grafikus felületen is be vagy jelentkezve, vagy ments el mindent, mielőtt kipróbálod. A szokásos konténerleállítás után tehát jöhet a veszélyes parancs:

docker run -it --rm --name systemd \
  --tmpfs /run \
  --tmpfs /run/lock \
  -v /sys/fs/cgroup:/sys/fs/cgroup \
  --privileged \
  localhost/ubuntu-2004-systemd:v1

Ha pedig egyszer ezt a parancsot lefuttatuk, a következő futtatásra már valószínűleg nem lesz gond. Ettől függetlenül a cikk folytatás a előtt érdemes újraindítani a gépet, hogy helyreálljon minden alapértelmezésre. Rá kell jönnünk viszont, hogy nem a privileged mód itt a megoldás, hanem egy olyan beállítás, ami nem volt mindig, de a Systemd is fejlődik, így már használható a container változó, aminek megadhatjuk a "docker" értéket. Ezzel jelezzük a systemd-nek, hogy igazodjon ehhez a környezethez.

docker run -it --rm --name systemd \
  --tmpfs /run \
  --tmpfs /run/lock \
  -v /sys/fs/cgroup:/sys/fs/cgroup \
  -e container=docker \
  localhost/ubuntu-2004-systemd:v1

Erre elindul a systemd és még egy login promptot is ad, valahogy így:

Ubuntu 20.04.3 LTS e9e0a9854e42 console

e9e0a9854e42 login:

kicsit feljebb görgetve viszont sajnos még lesz egy hibaüzenet:

Couldn't move remaining userspace processes, ignoring: Input/output error

Erre ugyan próbáltam megoldást keresni, de mivel nem sikerült annak ellenére, hogy a hibaüzenetet meg lehet találni az interneten sok helyen, feladtam. A systemd már így is működik és alkalmasnak tűnik arra a célra, amire valószínűleg használni szeretnénk. Hosszú távon viszont továbbra sem használnám, főleg nem éles környezetben.

Felmerülhet a gondolat, hogy ha már van egy ilyen változónk a Systemd-hez, hogy "container", akkor talán egyből ezzel kellett volna kezdeni. Ennyire viszont nem old meg mindent. Nyerünk viszont annyit vele, hogy a amúgy is megjelenő hibaüzenetek a konténer leállását eredményezik, így visszakapjuk a hoszt promptot. Ki is lehet próbálni:

docker run -it --rm --name systemd -e container=docker localhost/ubuntu-2004-systemd:v1

Systemd login prompt letiltása konténerben

[Tartalom]

Már örülünk, hogy van egy működő konténer, viszont valójában teljesen felesleges a legtöbb esetben, hogy konténerben is legyen egy console, amin be lehet jelentkezni. Ott lesz nekünk a docker exec, ráadásul valószínűleg nincs is jelszavas felhasználó, amivel beléphetnénk. Létrehozok tehát egy Dockerfile.v2 nevű fájlt, aminek a tartalma az alábbi, egyetlen sor újdonsággal:

FROM ubuntu:20.04

RUN apt-get update \
 && apt-get install -y --no-install-recommends \
    systemd

CMD ["/bin/systemd"]

RUN unlink /lib/systemd/system/getty.target

A fent látható "getty.target" fájl felel a promptért. A fájl törlésével pedig le is tilthatjuk. Ezután pedig az alábbi build és run utasításokkal el is indítható az új fájlból a konténer:

docker build -t localhost/ubuntu-2004-systemd:v2 -f Dockerfile.v2 .
docker run -it --rm --name systemd  \
  --tmpfs /run \
  --tmpfs /run/lock \
  -v /sys/fs/cgroup:/sys/fs/cgroup \
  -e container=docker \
  localhost/ubuntu-2004-systemd:v2

Apache HTTPD szerver telepítése Systemd alá

[Tartalom]

Az előző konténer törlése után jöhet még egy újabb lépés. Mindössze két utasítás hozzáadásával már az "apache2" csomag telepítésével az aktuális disztribúcióban támogatott verziójú Apache HTTPD szerver is telepíthető. Ehhez ismét új fájlt használok Dockerfile.v3 néven.

FROM ubuntu:20.04

RUN apt-get update \
 && apt-get install -y --no-install-recommends \
    systemd

STOPSIGNAL SIGRTMIN+3

CMD ["/bin/systemd"]

RUN unlink /lib/systemd/system/getty.target

ARG DEBIAN_FRONTEND=noninteractive

RUN apt-get update \
 && apt-get install -y --no-install-recommends \
    apache2

Itt már talán csak az ARG igényel érdemel magyarázatot. Az apache2 csomag telepítésekor interaktívan időzónát is kellene választanunk. Mivel ezt egy build processzben nem akarom, a DEBIAN_FRONTEND változó "noninteractive"-ra álításával ezt letilthatom. A változóra viszont csak itt, a build időben van szükségem, ezért a build argumentumként alapértelmezett értékként megadása praktikus megoldás lehet. Végül az alábbi build és run utasítással már a webszervert is tartalmazó Systemd konténerünk is elkészül:

docker build -t localhost/ubuntu-2004-systemd:v3 -f Dockerfile.v3 .
docker run -it --rm --name systemd \
  --tmpfs /run \
  --tmpfs /run/lock\
  -v /sys/fs/cgroup:/sys/fs/cgroup \
  -e container=docker \
  -p 8080:80 \
  localhost/ubuntu-2004-systemd:v3

Látható, hogy a 8080-as portot beirányítottam a docker konténerbe, hogy elérjem a webszervert a "localhost" domainen. Jöjjön tehát egy gyors teszt, hogy valóban működik is-e a szerver. A következő parancsot tehát azon a gépen futtatom, amelyiken a Docker daemon is van.

curl localhost:8080

Erre egy HTML forráskódnak kell megjelenni, ami az Apache HTTPD alapértelmezett főoldalának a forrása.

Végszó

[Tartalom]

Lehet tehát Systemd-t használni Docker konténerben, de nem javasolt. Ahogy a bevezetőben említettem, minden rendszeren lehetnek eltérések és mély ismeretek szükségesek Linuxról és Systemd-ről egyaránt ahhoz, hogy igazán magabiztosan futassunk ilyen konténereket, így tesztelésre azt mondom, bátran használd, de élesben semmiképp, vagy legalább ne hosszú távon. Persze elképzelhető, hogy idővel még ennél is egyszerűbb lesz a Systemd konténerizációja.

Kategóriák: 
Megosztás/Mentés