Az image készítésnek több lehetséges módja van. Először nézzük meg a “kézműves” docker image készítést, aztán pedig megnézzük, hogy hogyan lehet Dockerfile segítségével automatizáltan megtenni ugyanazt.

Első körben indítsunk el egy konténert, ami a hivatalos ubuntu imaget fogja használni.

docker run --name imagemaker -ti ubuntu

Ez elindít egy imagemaker nevű konténert a -ti kapcsolóval, hogy tudjunk gépelni. Figyeld meg, hogy nem használtuk a --rm kapcsolót. (Hogy mi ez arról korábban már írtam) Ezt azért tettük, mert a futás után szükségünk lesz majd a leállított konténerre, hisz abból fogjuk elkészíteni az imaget.

Amennyiben kiadod a curl parancsot a “command not found” üzenetet fogod kapni, hisz az nincs telepítve.

A következő két utasítás fogja a curl alkalmazást telepíteni. Amennyiben nem mozogsz otthonosan a Linux világában ne ijedj meg, nem fogunk most elmélyülni benne. Add ki a következő két parancsot:

apt-get update
apt-get install -y curl

Az első parancs frissíti a csomaginformációkat, amik nincsenek benne az imageben, hogy annak mérete minél kisebb legyen. Az, hogy ez miért is jó, majd később lesz szó. A második parancs a tényleges telepítés. Az -y kapcsoló fontos, mivel ez a parancs automatán fut és nem interaktívan, ezért a kérdésekre nem fog tudni válaszolni ezzel a kapcsolóval viszont minden kérdésre yes, vagyis igen választ ad majd. Még egyszer megjegyezném: most csak azt kell megérteni, hogy ez a két parancs telepíti a curl parancsot.

Ki is próbálhatod. Amennyiben futtatod a curl parancsot, már nem a hibaüzenettel fogsz szembesülni, hanem a curl használatáról kapsz információkat. Lépjünk ki a konténerből az exit paranccsal. Itt igazából nem a konténerből léptünk ki, hanem csak a bash shell-ből, de mivel csak ez futott a konténerbe, így a konténer futása is befejeződött.

Amennyiben kiadjuk a docker ps -a parancsot, akkor láthatjuk, hogy a imagemaker nevű konténer létezik.

`docker ps -a` parancs kimenete

Ezután adjuk ki a következő parancsot, hogy elkészüljön az image:

docker commit imagemaker local/curl

A commit parancsnak először a konténer nevét vagy azonosítóját, majd az image nevét kell megadni. Azért, hogy ne akadjon össze a hivatalos image és az általam gyártottak én a tanarurkerem/ előtagot szoktam használni általánosan. Azonban akkor, ha tudom, hogy csak azon az egy gépen használom, amit készítettem, akkor a local/ előtagot. A docker image ls local/curl parancs segítségével láthatjuk, hogy létre is jött az image.

`docker image ls local/url` parancs kimenete

Indítsunk is el egy konténert a frissen készített image segítségével:

 docker run --rm -ti local/curl

Teszteljük, adjuk ki a curl parancsot. Amennyiben jól dolgoztunk a curl parancs használatáról kapunk némi információt.

`curl` parancs kimenete

Vizsgáljuk meg egy picit jobban ezt az imaget, hisz ideje beszélni a layerekről, mely az egyik erőssége a Docker által megalkotott eszközkészletnek. Adjuk ki a docker image history local/curl parancsot.

`docker image history local/curl` parancs kimenete

A látott kép értelmezéséhez érdemes tudni azt, hogy a Docker egy úgynevezett naplózó fájlrendszert használ az imagek és konténerek fájlrendszeréhez. Amikor elindítunk egy konténert, akkor az alapjául szolgáló image fájlrendszerét kapja meg, de a futás során az image fájlrendszere változatlan, a változásokat egy külön rétegbe vagyis layerbe menti el a rendszer. Szemben a virtuális gépekkel, ha elindítunk száz konténert egy két gigabájtos image alapján, akkor az a két gigabájt csak egyszer lesz lefoglalva a háttértárakon. Akkor, amikor letöltésre kerül az adott image és függőségei. A száz konténer csak annyi helyet fog foglalni, amennyi adatot módosítanak. Száz virtuális gépnél ilyenkor kétszáz gigányi tárhely kerül lefoglalásra, vagyis százszor annyi.

A fenti commit parancs nem csinál mást, mint az imagemaker konténer layerét elmenti local/curl néven, valamint azt az információt, hogy az adott konéner az ubuntu image használatával lett elindítva. A Docker nem tárolja el a teljes fájlrendszert újra, hanem csak a különbségeket. Amennyiben megnézzük a history parancs kimenetét, láthatjuk, hogy az utolsó réteg, amit mi készítettünk az 46MB. A docker image ls local/curl és a docker image ls ubuntu parancsok segítségével láthatjuk, hogy a kiinduló ubuntu image 77.8 MB és az általunk készített local/curl image pedig 124MB (78+46) méretű.

Ez az a pont, ahol a commit parancsot el is felejthetjük. Nem hinném, hogy szükségünk lesz rá többet. Imaget ugyanis nem így fogunk készíteni, hanem Dockerfile és a build parancs segítségével. A fenti részt azért tartottam fontosnak bemutatni, mert a mögöttes működési mechanizmus megértéséhez feltétlenül szükséges volt.

Készítsünk egy tetszőleges könyvtárat, majd álljunk bele és hozzunk létre ott egy Dockerfile nevű fájlt a következő tartalommal:

FROM ubuntu
RUN apt-get update
RUN apt-get install -y curl

Majd készítsük el az image-t a következő paranccsal:

docker build -t local/curl .

Amennyiben minden jól ment ez a parancs elkészíti a local/curl imaget, amit ugyanúgy tudunk használni mint a korábbit. Próbáljuk ki és indítsuk el és teszteljük a curl parancsot:

docker run --rm -ti local/curl

Így persze csak azt fogjuk tudni tesztelni, hogy ugyanúgy működik még az adott image, de nem lehetünk benne biztosak, hogy tényleg egy másik imageről van-e szó. Adjuk ki a docker image history local/curl parancsot. Ekkor valami ilyesmit kell kapnunk:

`docker image history local/curl` parancs kimenete

Amint látjuk a history parancs már egészen más kimenetet ad mint eddig. Nem csak egy plusz réteget látunk ott, hanem kettőt. Ebből le is vonhatjuk a következtetést, hogy ez bizony már egy másik image, hiába néz ki ugyanúgy.

Most vizsgáljuk meg picit a Dockerfilet. Ne próbáljuk meg hasonlítani semmihez ez egy teljesen egyedi formátum saját szabályokkal. Minden sor egy utasítás és az adott utasítás paraméterei.

[UTASÍTÁS] [paraméterek]

Üres sorokkal tagolhatjuk a fájlt, az nem fogja megzavarni a működést. Amennyiben egy sor végén visszaperjel (\) található, úgy a következő sor hozzáfűzésre kerül. Így több sorba is törhetjük a hosszú parancsokat, amennyiben ez szükséges. # jellel megjegyzéseket tehetünk a fájlba. A Dockerfile parancsok kis-nagybetűre nem érzékenyek, de konvenció szerint mindig csupa nagybetűvel írjuk azokat. Mindig egy FROM paranccsal kezdünk, amely megmondja, hogy mi lesz a kiinduló, base image. Jelen esetben is, mint ahogyan a parancssori példában az ubuntu imageből indulunk ki. A RUN parancs a paraméterül kapott parancsokat lefuttatja és az eredményből készít egy újabb layert. Ezért van itt két layer, míg korábban csak egy volt. A fenti két parancsot összevonhatjuk és egy sorba tehetjük. Ehhez a && jel kell, mely egy shell operátor, és a két utasítást egymás után futtatja le. Próbáljuk ki! Módosítsuk a Dockerfile-t, majd futtassuk újra a buildet:

FROM ubuntu
RUN apt-get update && apt-get install -y curl

A history most már csak egy plusz réteget mutat.

`docker image history local/curl` parancs kimenete

Természetesen több sorba is törhetjük a parancsot a \ segítségével. Módosítsuk a Dockerfile-t majd építsük újra:

FROM ubuntu
RUN apt-get update && \
    apt-get install -y curl

Amennyiben nem húzzuk beljebb a második apt-get parancsot, úgy image azonosítóra megegyező imaget kapunk, hisz a Docker először “kiegyenesíti” a sort és utána képez belőle egy azonosítót. A behúzás miatt a “kiegyenesített” sor tartalmaz plusz szóközöket és ezért más azonosítót fog kapni, de tartalmilag ugyanaz az image lesz. Ezt a méretén is láthatjuk.

Végezetül beszélnünk kell az image méretéről. Minél kisebb egy image annál rövidebb idő alatt lehet azt szerverek között mozgatni a hálózaton és annál kevesebb helyet foglal a hoston. Ezért törekedni kell a minél kisebb imagek létrehozására. Könnyen belátható, hogy a nagyobb image több kódot tartalmaz. A több kóddal növekszik a sérülékenységek valószínűsége. Ez is amellett szól, hogy az imagek méretét ne engedjük nagyra nőni, ezért se mindegy, hogy mi is van abban az imageben. Épp ezért nincsen benne a kiinduló imageben az apt csomagok metainformációi, ezért kellett futtatnunk az apt-get update parancsot. Ez azt is jelenti, hogy az általunk készített image tartalmazza ezeket. Módosítsuk hát a Dockerfilet és adjuk hozzá a szükséges rm parancsot:

FROM ubuntu
RUN apt-get update && \
    apt-get install -y curl && \
    rm -rf /var/lib/apt/lists/*

Építsük meg az imaget, majd nézzük meg, hogy mi lett az eredmény:

`docker image history local/curl` parancs kimenete. Egy plusz réteget látunk rajta.

Láthatólag a curl telepítésének lépése lement 46 MByteról 7 MBytera. Ki lehet próbálni, hogy a három parancsot három külön RUN parancsba tesszük és azt fogjuk tapasztalni, hogy az image mérete nem csökkent.

`docker image history local/curl` parancs kimenete. Három plusz réteget látunk rajta

A képet megvizsgálva azt is láthatjuk, hogy az apt-get update parancs 40MByteos méretnövekedését tudjuk megspórolni, ha a három parancsot egy RUN utasításba tesszük. Figyeljük meg, hogy a törlés (rm -rf /var/lib/apt/lists/*) egy igen fontos eleme a méretcsökkentésnek. Pusztán azzal, ha egy RUN utasításba írjuk a parancsokat nem lesz kisebb az image mérete.

Ne feledd! Amennyiben szeretnél fejlődni keress meg nyugodtan. Csoportos és személyes Docker oktatással és tanácsadással tudlak téged vagy csapatodat, cégedet támogatni.

A Docker gyorstalpaló cikksorozat részei

  1. Bevezetés - Docker gyorstalpaló
  2. Telepítés - Docker gyorstalpaló
  3. CLI avagy a parancssori értelmező - Docker gyorstalpaló
  4. Konténer futtatása - Docker gyorstalpaló
  5. Compose - Docker gyorstalpaló
  6. Stack futtatása - Docker gyorstalpaló
  7. Image készítése, Dockerfile - Docker gyorstalpaló

Borító just-pics képe a Pixabay-en.