A modern szoftverfejlesztésben a Docker az alkalmazások konténerizálásának sarokkövévé vált. Lehetővé teszi a fejlesztők számára, hogy az alkalmazásokat és függőségeiket izolált, hordozható egységekbe csomagolják, garantálva a konzisztens működést a különböző környezetekben. Ennek a rugalmas ökoszisztémának a középpontjában a Dockerfile
áll, amely egyfajta „receptkönyvként” szolgál a Docker image-ek elkészítéséhez. Ez a fájl tartalmazza azokat az utasításokat, amelyek alapján a Docker felépíti a konténerfuttatáshoz szükséges képfájlt.
A Dockerfile
számos utasítást kínál, de kettő közülük különösen fontos, és gyakran okoz félreértést a kezdő és néha a tapasztaltabb Docker felhasználók körében is: a CMD
és az ENTRYPOINT
. Bár mindkettő alapvetően a konténer indulásakor végrehajtódó parancsot vagy alkalmazást határozza meg, működésükben, viselkedésükben és felülírhatóságukban jelentős különbségek rejlenek. E különbségek megértése kulcsfontosságú a robusztus, hatékony és könnyen karbantartható Docker image-ek építéséhez.
Ebben a cikkben részletesen megvizsgáljuk a CMD
és az ENTRYPOINT
utasításokat, feltárva azok célját, szintaxisát, egymással való interakciójukat és a legjobb gyakorlatokat, amelyek segítenek eldönteni, mikor melyiket használd. Célunk, hogy egy átfogó útmutatót nyújtsunk, amely eloszlatja a félreértéseket, és képessé tesz arra, hogy tudatosabban tervezd meg a Docker image-eidet.
Mi is az a Docker Röviden?
Mielőtt mélyebbre ásnánk, érdemes röviden felidézni, miért is olyan népszerű a Docker. A Docker egy nyílt forráskódú platform, amely lehetővé teszi az alkalmazások és azok környezetének „konténerekbe” csomagolását. Egy konténer egy könnyűsúlyú, önálló, futtatható szoftvercsomag, amely mindent tartalmaz, ami egy alkalmazás futtatásához szükséges: kódot, futtatókörnyezetet, rendszertámogató eszközöket, könyvtárakat és beállításokat. A konténerek előnye a gyors indítás, a platformfüggetlenség és az izoláció, ami nagyban leegyszerűsíti a fejlesztési, tesztelési és üzemeltetési folyamatokat.
A Konténer Indításának Alappillérei: CMD
és ENTRYPOINT
Amikor egy Docker image-ből elindítunk egy konténert, a Dockernek tudnia kell, milyen parancsot vagy programot kell futtatnia a konténer fő folyamataként. Ezt a célt szolgálja a CMD
és az ENTRYPOINT
utasítás. Bár mindkettő a konténer indulásakor végrehajtódó parancsot definiálja, a fő különbség abban rejlik, hogyan kezelik az argumentumokat, hogyan írhatók felül, és milyen szerepet töltenek be az image tervezésében.
A CMD
Utasítás Részletesen
A CMD
utasítás az egyik leggyakrabban használt elem egy Dockerfile
-ban. Fő célja, hogy alapértelmezett parancsot vagy argumentumokat szolgáltasson egy futtatható képhez. Ez a parancs akkor hajtódik végre, ha a docker run
parancsnak nem adunk meg explicit parancsot vagy argumentumokat. Ha megadunk, akkor a CMD
utasításban szereplő parancs felülíródik.
Formátumok:
- Exec form (ajánlott):
CMD ["futtatható", "paraméter1", "paraméter2"]
Ez a preferált formátum, mivel a parancs közvetlenül hajtódik végre, shell nélkül. A parancs lesz a konténer PID 1-es processze, ami kulcsfontosságú a megfelelő jelkezelés (pl. SIGTERM a leállításkor) biztosításához. Például:CMD ["nginx", "-g", "daemon off;"]
- Shell form:
CMD parancs paraméter1 paraméter2
Ez a forma a parancsot egy shellben (/bin/sh -c
) futtatja. Bár kényelmes lehet a shell funkciók (pl. környezeti változók, pipeline-ok) használata miatt, problémákat okozhat a jelkezelésben, mivel a shell lesz a PID 1, és nem adja tovább a jeleket az alkalmazásnak. Például:CMD echo "Hello Docker!"
- ENTRYPOINT-hoz tartozó paraméterek:
CMD ["paraméter1", "paraméter2"]
Ebben az esetben aCMD
csupán argumentumokat szolgáltat azENTRYPOINT
utasításnak. Erről később részletesebben is szó lesz.
Felülírási viselkedés:
A CMD
utasítás rendkívül könnyen felülírható. Ha a docker run
parancsnak adunk meg egy parancsot, az felülírja a Dockerfile
-ban definiált CMD
-t. Például:
Dockerfile
:CMD ["echo", "Hello from Dockerfile!"]
docker run myimage
: Output:Hello from Dockerfile!
docker run myimage echo "Hello from command line!"
: Output:Hello from command line!
(aCMD
felülíródott)
Fontos megjegyezni, hogy egy Dockerfile
-ban csak az utolsó CMD
utasítás érvényesül. Ha több CMD
sort is megadunk, a korábbiak figyelmen kívül maradnak.
Mikor használd a CMD
-t?
- Alapértelmezett parancsokhoz: Amikor szeretnél egy alapértelmezett viselkedést biztosítani az image-hez, de meg akarod adni a felhasználóknak a rugalmasságot, hogy könnyen felülírják azt.
- Argumentumok szolgáltatásához
ENTRYPOINT
-nak: Gyakori minta, hogy aCMD
azENTRYPOINT
által futtatott fő program alapértelmezett paramétereit biztosítja.
Az ENTRYPOINT
Utasítás Részletesen
Az ENTRYPOINT
utasítás célja, hogy a konténer fő végrehajtható parancsát vagy alkalmazását definiálja. Ez a parancs mindig lefut, függetlenül attól, hogy a docker run
kapott-e parancsot. Az ENTRYPOINT
-nak megadott parancsokat a docker run
argumentumai és a CMD
utasításban szereplő paraméterek egészítik ki.
Formátumok:
- Exec form (erősen ajánlott):
ENTRYPOINT ["futtatható", "paraméter1", "paraméter2"]
Ez a preferált és leggyakrabban használt forma. A parancs közvetlenül fut, a konténer PID 1-es processze lesz, biztosítva a megfelelő jelkezelést. Például:ENTRYPOINT ["java", "-jar", "app.jar"]
- Shell form:
ENTRYPOINT parancs paraméter1 paraméter2
Ez a forma a parancsot egy shellben (/bin/sh -c
) futtatja. Ahogy aCMD
esetében, ez is problémákat okozhat a jelkezelésben. Általában kerülendő, kivéve, ha kifejezetten szükséged van a shell funkciókra, és tisztában vagy a mellékhatásokkal. Például:ENTRYPOINT echo "Starting..." && myapp
Felülírási viselkedés:
Az ENTRYPOINT
utasítás nehezebben felülírható, mint a CMD
. Csak explicit módon a --entrypoint
flaggel írható felül a docker run
parancsban. Például:
Dockerfile
:ENTRYPOINT ["/usr/bin/ping"]
docker run myimage google.com
: Output:PING google.com (172.217.16.174) 56(84) bytes of data.
(agoogle.com
argumentumként adódik át aping
-nek)docker run --entrypoint /bin/bash myimage
: Futtatja a/bin/bash
-t aping
helyett, és interaktív shellt nyit a konténerben.
Ahogy a CMD
esetében, itt is csak az utolsó ENTRYPOINT
utasítás érvényesül egy Dockerfile
-ban.
Interakció a CMD
-vel:
Ez az, ahol a két utasítás közötti szinergia a leginkább megnyilvánul. Ha az ENTRYPOINT
exec formában van megadva, akkor a CMD
(szintén exec formában megadva) az ENTRYPOINT
parancs argumentumaiként szolgál.
Dockerfile
:
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
docker run myimage
: A Docker anginx -g daemon off;
parancsot futtatja.docker run myimage -v
: A Docker anginx -v
parancsot futtatja. (A-v
felülírta aCMD
-t, de azENTRYPOINT
megmaradt.)
Mikor használd az ENTRYPOINT
-ot?
- Konténer fő programjának meghatározására: Amikor a konténer egy adott alkalmazás (pl. webszerver, adatbázis) futtatására készült, és mindig az az alkalmazás kell, hogy induljon el.
- Segédprogram-konténerek (Utility Containers): Olyan image-ek esetén, amelyek egyetlen, jól definiált eszközt (pl.
curl
,kubectl
) biztosítanak. AzENTRYPOINT
itt a segédprogramot definiálja, aCMD
pedig az alapértelmezett paramétereit (pl.--help
). - Wrapper scriptek: Amikor a konténer indítása előtt valamilyen inicializációs logikát (pl. konfigurációs fájlok generálása, adatbázis migráció) kell végrehajtani. Ekkor az
ENTRYPOINT
egy shell scriptre mutat, amely elvégzi a feladatokat, majd azexec "$@"
parancs segítségével elindítja a tényleges alkalmazást, átadva neki aCMD
-ben vagy adocker run
-ban megadott argumentumokat.
A Szimbiózis: CMD
és ENTRYPOINT
Együtt
A leghatékonyabb Docker image-ek gyakran mindkét utasítást kombinálják az exec formátumok használatával. Ez a megközelítés lehetővé teszi a konténer funkciójának világos és rugalmas meghatározását:
- Az
ENTRYPOINT
definiálja a konténer fő végrehajtható programját, azt az „alkalmazást”, amelyet a konténer képvisel. Ez általában egy olyan parancs, amelynek argumentumokat lehet átadni. - A
CMD
alapértelmezett argumentumokat biztosít azENTRYPOINT
számára. Ezek az argumentumok könnyen felülírhatók adocker run
parancsban, így a felhasználó testreszabhatja a konténer indítási viselkedését anélkül, hogy azENTRYPOINT
-ot kellene módosítania.
Példa egy Nginx konténerre:
FROM nginx:latest
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
docker run my-nginx-image
: Elindítja az Nginx-et anginx -g daemon off;
paranccsal.docker run my-nginx-image -v
: ACMD
felülíródik, és anginx -v
parancs fut le, kiírva az Nginx verzióját.
Ha az ENTRYPOINT
shell formában van megadva, akkor a CMD
-ben szereplő utasítás egyszerűen hozzáadódik az ENTRYPOINT
parancs végéhez, mint annak része. Ez ritkán a kívánt viselkedés, mivel nehezen kezelhető és félreértésekhez vezethet.
Főbb Különbségek Összefoglalása
Az alábbi táblázatban összefoglaljuk a legfontosabb különbségeket:
Jellemző | CMD |
ENTRYPOINT |
---|---|---|
Célja | Alapértelmezett parancs vagy argumentumok. | A konténer fő végrehajtható parancsa. |
Felülírás módja | Könnyen felülírható a docker run <image> <parancs> -szal. |
Nehezebben felülírható, explicit módon a docker run --entrypoint <parancs> <image> -szal. |
Interakció a docker run argumentumokkal |
Ha a docker run parancsot kap, a CMD felülíródik. |
Ha a docker run argumentumokat kap, azok az ENTRYPOINT parancs argumentumaiként adódnak át. |
Interakció a másik utasítással | Ha van ENTRYPOINT exec formában, akkor a CMD az ENTRYPOINT argumentumaiként szolgál. |
Az ENTRYPOINT mindig lefut, és a CMD adja az alapértelmezett argumentumait (ha ENTRYPOINT exec formában van). |
Használati eset | Alapértelmezett, könnyen módosítható viselkedés. | A konténer lényegi funkciója, futtatható program, segédprogramok. |
Mikor Melyiket Válaszd? A Best Practices
A helyes választás nagyban függ a Docker image céljától és a kívánt rugalmasságtól:
Használd az ENTRYPOINT
-ot, ha:
- A konténer egy adott alkalmazást (pl. webszerver, adatbázis, speciális CLI eszköz) futtat, és ez az alkalmazás a konténer fő funkciója. Például:
ENTRYPOINT ["nginx"]
vagyENTRYPOINT ["java", "-jar", "myapp.jar"]
. - Egy segédprogramot (utility container) szeretnél létrehozni. Például egy
curl
image:FROM alpine/git ENTRYPOINT ["git"] CMD ["--help"]
Ekkor
docker run my-git-image clone <repo>
parancsot futtathatsz, és agit clone <repo>
hajtódik végre. - Egy wrapper scriptet szeretnél futtatni a tényleges alkalmazás előtt. Ez a script végezhet konfigurációt, jogosultságbeállítást, vagy ellenőrzéseket, mielőtt az
exec "$@"
paranccsal átadja a vezérlést aCMD
-nek vagy adocker run
argumentumainak.FROM busybox COPY entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] CMD ["echo", "Default command!"]
Az
entrypoint.sh
tartalma:#!/bin/sh echo "Running custom entrypoint..." # Some setup logic here exec "$@"
Így a script lefut, majd a
CMD
vagy a `docker run` argumentumai végrehajtódnak.
Használd a CMD
-t, ha:
- Alapértelmezett argumentumokat szeretnél biztosítani az
ENTRYPOINT
-hoz. Ez a leggyakoribb és ajánlott használati mód.FROM debian ENTRYPOINT ["ping"] CMD ["google.com"]
Ekkor
docker run my-ping-image
aping google.com
parancsot futtatja. Adocker run my-ping-image bing.com
pedig aping bing.com
-ot. - Egy image-et hozol létre, amelynek nincs fix
ENTRYPOINT
-ja, és csak egy könnyen felülírható alapértelmezett parancsot szeretnél biztosítani. Ez ritkábban fordul elő, de hasznos lehet egyszerű, egyszeri futtatásra szánt konténerek esetén.FROM alpine CMD ["echo", "Hello from simple container!"]
docker run my-simple-image
kiírja az üdvözlést.docker run my-simple-image ls -l
pedig a konténer gyökérkönyvtárának tartalmát listázza ki.
Haladó Megfontolások: Shell vs. Exec Form – A Rinek a Felszín Alatt
A CMD
és az ENTRYPOINT
utasításoknál említettük a „shell form” és „exec form” megkülönböztetését. Ez nem csupán szintaktikai különbség, hanem mélyrehatóan befolyásolja a konténer folyamatkezelését és viselkedését.
Exec form (ajánlott):
- Szintaxis:
["parancs", "paraméter1", "paraméter2"]
- Működés: A Docker közvetlenül futtatja a megadott parancsot. Ez a parancs lesz a konténer PID 1-es processze.
- Előnyök:
- Megfelelő jelkezelés: A PID 1-es processz megkapja a Docker által küldött leállítási jeleket (pl. SIGTERM), így az alkalmazásnak lehetősége van a „graceful shutdown”-ra (tisztességes leállásra), mielőtt a Docker erővel leállítaná (SIGKILL). Ez kulcsfontosságú az adatvesztés elkerüléséhez és az alkalmazás stabilitásához.
- Nincs felesleges shell processz: Nincs extra shell overhead, ami minimálisan csökkentheti az erőforrás-felhasználást és a komplexitást.
- Hátrányok:
- Nincsenek shell funkciók: Nem használhatók shell-specifikus funkciók (pl. környezeti változók közvetlen helyettesítése, wildcard-ok, pipeline-ok, összetett redirekciók). Pl.
CMD ["echo", "$MY_VAR"]
nem helyettesíti a változót, hanem szó szerint kiírja a$MY_VAR
-t.
- Nincsenek shell funkciók: Nem használhatók shell-specifikus funkciók (pl. környezeti változók közvetlen helyettesítése, wildcard-ok, pipeline-ok, összetett redirekciók). Pl.
Shell form:
- Szintaxis:
parancs paraméter1 paraméter2
- Működés: A Docker a parancsot egy shellben futtatja (pl.
/bin/sh -c "parancs paraméter1 paraméter2"
). A shell lesz a konténer PID 1-es processze. - Előnyök:
- Shell funkciók: Használhatók shell-specifikus funkciók (pl.
CMD echo "Hello $USER"
).
- Shell funkciók: Használhatók shell-specifikus funkciók (pl.
- Hátrányok:
- Rossz jelkezelés: Mivel a shell a PID 1, és a shell indítja el az alkalmazást mint gyermekprocesszt, a Docker által küldött leállítási jeleket (SIGTERM) a shell kapja meg, és gyakran nem adja tovább az alkalmazásnak. Ez azt jelenti, hogy az alkalmazás nem tudja magát rendesen leállítani, és végül erőszakosan lesz leállítva (SIGKILL), ami adatvesztéshez vezethet.
- Extra processz: Egy felesleges shell processz fut, ami minimális erőforrást fogyaszt.
Az exec
trükk:
Ha szükséged van shell funkciókra (pl. környezeti változók beállítása egy scriptben), de mégis szeretnéd a megfelelő jelkezelést, használhatod az exec
parancsot egy shell scripten belül, amelyet az ENTRYPOINT
futtat:
FROM alpine
COPY my_script.sh /usr/local/bin/my_script.sh
RUN chmod +x /usr/local/bin/my_script.sh
ENTRYPOINT ["/usr/local/bin/my_script.sh"]
CMD ["my_app_default_arg"]
A my_script.sh
tartalma:
#!/bin/sh
# Shell magic here, e.g., variable substitution
echo "Setting up environment..."
export MY_VAR="some_value"
# Finally, execute the main application
# The 'exec' command replaces the current shell process with the application,
# making the application the new PID 1.
exec my_app --arg1 "$MY_VAR" "$@"
Ez a módszer ötvözi a shell kényelmét a megfelelő PID 1 viselkedéssel.
Gyakorlati Példák és Szcenáriók
1. Webszerver (Nginx)
FROM nginx:alpine
# Az ENTRYPOINT a fő alkalmazást definiálja, ami mindig az Nginx.
ENTRYPOINT ["nginx"]
# A CMD az Nginx alapértelmezett paramétereit adja meg.
# Ez felülírható, ha a felhasználó más paraméterekkel szeretné indítani az Nginx-et.
CMD ["-g", "daemon off;"]
Futtatás:
docker run my-nginx-image
: Elindítja az Nginx-et anginx -g daemon off;
paranccsal.docker run my-nginx-image -v
: ACMD
felülíródik, és anginx -v
fut le (kiírja az Nginx verzióját).
2. Adatbázis Kliens (Postgres psql)
FROM postgres:16-alpine
# Az ENTRYPOINT a psql klienst definiálja.
ENTRYPOINT ["psql"]
# A CMD az alapértelmezett segítő üzenetet adja.
CMD ["--help"]
Futtatás:
docker run my-psql-client
: Futtatja apsql --help
parancsot.docker run my-psql-client -h db-server -U admin
: Futtatja apsql -h db-server -U admin
parancsot, kapcsolódva az adatbázishoz.
3. Saját Fejlesztésű Java Alkalmazás
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/my-app.jar my-app.jar
# Az ENTRYPOINT definiálja a fő végrehajtható programot.
ENTRYPOINT ["java", "-jar", "my-app.jar"]
# A CMD az alapértelmezett argumentumokat adja meg az alkalmazásnak.
CMD ["--server.port=8080", "--spring.profiles.active=prod"]
Futtatás:
docker run my-java-app
: Futtatja ajava -jar my-app.jar --server.port=8080 --spring.profiles.active=prod
parancsot.docker run my-java-app --server.port=9000
: Futtatja ajava -jar my-app.jar --server.port=9000
parancsot.
Összefoglalás
A CMD
és az ENTRYPOINT
utasítások közötti különbségek megértése alapvető fontosságú a hatékony és robusztus Docker image-ek építéséhez. Bár mindkettő a konténer indítási parancsát határozza meg, különböző szerepet töltenek be:
- Az
CMD
alapértelmezett parancsot vagy argumentumokat szolgáltat, amelyek könnyen felülírhatók adocker run
parancsban. Ideális az image alapértelmezett viselkedésének meghatározására, amely szükség esetén módosítható. - Az
ENTRYPOINT
a konténer fő végrehajtható programját definiálja, amely mindig lefut. Argumentumokat vár, amelyeket aCMD
vagy adocker run
parancsban megadott paraméterek biztosítanak. Ez a választás akkor optimális, ha a konténer egy adott alkalmazás céljára épült, és ezt az alkalmazást kell mindig futtatnia.
A legtöbb esetben az ENTRYPOINT
exec formában történő használata, kiegészítve a CMD
-ben megadott alapértelmezett argumentumokkal, a legrugalmasabb és leginkább ajánlott minta. Ez biztosítja a konténer kiszámítható működését, a megfelelő jelkezelést és a könnyű testreszabhatóságot a felhasználók számára.
Ne feledd, a Docker konténerek lényege a konzisztencia és a hordozhatóság. A CMD
és ENTRYPOINT
utasítások tudatos használatával olyan image-eket hozhatsz létre, amelyek világosan kommunikálják a szándékukat, és megbízhatóan működnek a különböző környezetekben. Kísérletezz bátran, olvasd el a Docker dokumentációját, és hamarosan profin fogod kezelni ezeket az alapvető utasításokat!
Leave a Reply