![docker.png](http://faragocsaba.wdfiles.com/local--files/docker/docker.png)
(A kép forrása: https://developers.redhat.com/blog/2014/05/15/practical-introduction-to-docker-containers)
Table of Contents
|
Bevezető
Áttekintés
A Docker egy virtualizációs eszköz.
Aki ismeri a VirtualBoxot: funkciója lényegében hasonló, ám van egy lényeges eltérés: míg VirtualBoxban feltelepítünk egy teljes operációs rendszert, és azon belül telepítünk programokat, addig Dockerben egyes programokat telepítünk, amit a rendszer "körülvesz" egy egyszerűsített Linux operációs rendszerrel. Ha például egy rendszer 5 komponensből áll, akkor VirtualBoxban általában ugyanabban a virtuális operációs rendszeren belül telepítjük mind az ötöt míg Dockerben 5 különböző virtuális operációs rendszer fog futni.
Fogalmak
- Képfájl (image): egy olyan program, amit a Docker el tud indítani. Használhatunk mások által készített image-et, és mi magunk is létrehozhatunk.
- Konténer (container): ha elindítunk egy képfájlt, akkor létrejönnek konténerek. Úgy is felfoghatjuk, hogy a konténer a képfájlt feltelepített példánya, ami lehet futó vagy leállított. Egy futó konténer lényegében egy olyan operációs rendszerbe csomagolt program, ami nem a gazda operációs rendszer fájlrendszeréhez és portjaihoz kapcsolódik, hanem egy saját fájlrendszerhez és portokhoz. A fájlrendszerbe bemásolhatunk a gazdáról dolgokat, vagy felcsatolhatunk könyvtárakat, a portokat pedig leképezhetjük a gazda portjaira.
- Docker Hub: egy olyan weboldal, ahol már elkészített képfájlok vannak. Itt mi magunk is meg tudjuk osztani a mi képfáljainkat.
A konténer leállítható és újraindítható, így létezik nem futó konténer is. Ha egy olyan képfájlt szeretnénk indítani, amihez nem tartozik konténer, akkor létrejön egy, egyébként az a konténer indul.
Telepítés
A Docker telepítése nem egyszerű, nem felhasználóbarát és igen erőforrás igényes. Ráadásul az idők folyamán változik (nem feltétlenül az előnyére). Itt a Windows telepítésről lesz szó.
- BIOS szinten engedélyezni kell a virtualizációt (ez általában alapértelmezésben engedélyezve van).
- El kell döntenünk a backend típusát:
- Hyper-V: operációs rendszer szinten engedélyezni kell a Hyper-V-t (ez alapértelmezésben nincs engedélyezve): Control Panel (Vezérlőpult) → Programs and Features (Programok és Szolgáltatások) → Turn Windows features on and off (Windows-szolgáltatások be- és kikapcsolása) → itt a Hyper-V-t kapcsoljuk be (mindet) → OK, majd újra kell indítani a számítógépet. Ezzel egyébként lehetetlenné tesszük a VirtualBox indulását, így ha azt szeretnénk használni, akkor ki kell kapcsolni a Hyper-V-t, és ismét újra kell indítani a számítógépet.
- WSL 2: ha nincs engedélyezve a Hyper-V, akkor telepítés során felkínálja ennek a telepítését.
- Töltsük le a Docker Desktop on Windows-t: https://docs.docker.com/docker-for-windows/install/.
- Telepítsük fel. Készüljünk fel arra, hogy a telepítés során újra kell indítani a számítógépet.
- Indítsuk el.
Ha minden rendben befejeződött, akkor a jobb alsó sarokban megjelenik egy konténerhajó ikon, valamint parancssorban ki lehet adni a docker parancsot. Próbáljuk ki:
docker version
A telepítés során nem tudjuk kiválasztani, hogy hova telepítse, és azt sem tudjuk megadni, hogy hol legyenek az image-ek. Tehát jó sok helyre lesz szükségünk a C meghajtón. Ezen kívül a WSL 2 le fogja foglalni egy gyengébb számítógép szinte teljes erőforrását; ilyen esetben tényleg csak kipróbálni tudjuk, majd érdemes törölni.
A Docker által érintett könyvtárak (Csaba helyett az aktuális felhasználónév):
- c:\Program Files\Docker\
- c:\Users\Csaba\.docker\
- c:\Users\Csaba\AppData\Local\Docker\
- c:\Users\Csaba\AppData\Roaming\Docker\
- c:\Users\Csaba\AppData\Roaming\Docker Desktop\
- c:\Users\Public\Documents\Hyper-V\
- c:\ProgramData\Docker\
- c:\ProgramData\DockerDesktop\
Program indítása Dockerben
Adjuk ki a következő parancsot!
docker run hello-world
Nem túl kedves módon egy hibaüzenettel indít, majd letölt valamit:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:df5f5184104426b65967e016ff2ac0bfcd44ad7899ca3bbcf8e44e4461491a9e
Status: Downloaded newer image for hello-world:latest
A tulajdonképpeni program eredménye a következő:
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
A következő történt:
- Megmondtuk a Dockernek, hogy indítsa el a hello-world alkalmazást.
- Mivel nem találta lokálisan, automatikusan letöltötte.
- Ezt követően elindította.
- Az eredményt kiírta, és leállt.
Lássunk egy másik, kissé bonyolultabb példát!
docker run -d -p 80:80 docker/getting-started
Majd nyissuk meg böngészőből a http://localhost URL-t.
Itt az alábbi történt:
- A fentihez hasonlóan nem találta, letöltötte, majd elindította.
- A -d paranncsal azt mondtuk meg neki, hogy háttérben fusson (az angol detached (lecsatolt) rövidítéseként).
- A -p 80:80 azt jelenti, hogy a virtuális operációs rendszer 80-as portja (a : utáni szám) legyen elérhető a gazda számítógépen, szintén a 80-as porton (a : előtti szám). Ez a HTTP port.
Így böngészőből meg tudjuk nyitni, és egy 10 lépéses bevezető tananyagot kaptunk eredményül. Kb. ugyanez érhető el egyébként a https://docs.docker.com/get-started/ oldalon, és részben ez alapján készítettem el ezt a leírást is.
Grafikus felület
A Dockernek van egy grafikus felülete, amit úgy érhetünk el, hogy a jobb alsó sarokban található (esetenként elrejtett; ez esetben a felfele mutató nyílvégre kell kattintani) konténerhajó ikonra kell kattintani. Ez a felület az idők folyamán változott (nem feltétlenül előnyére). Az írás pillanatában a két legfontosabb fül a Containers / Apps, valamint az Images. Lényegében itt lehet az ezekkel kapcsolatos alapműveleteket végrehajtani: pl. elindítani, leállítani, törölni.
Parancssor
A fejlesztők általában nem hagyatkozhatnak a grafikus felületre, egyrészt mert az nem tud mindent, amit a parancssor tud, másrészt a gyakorlatban sok esetben csak egy terminál áll rendelkezésre, grafikus felület nem. A docker parancs kiadásával egy hosszú listát kapunk a lehetőségekről:
Usage: docker [OPTIONS] COMMAND
A self-sufficient runtime for containers
Options:
--config string Location of client config files (default
"C:\\Users\\Csaba\\.docker")
-c, --context string Name of the context to use to connect to the
daemon (overrides DOCKER_HOST env var and
default context set with "docker context use")
-D, --debug Enable debug mode
-H, --host list Daemon socket(s) to connect to
-l, --log-level string Set the logging level
("debug"|"info"|"warn"|"error"|"fatal")
(default "info")
--tls Use TLS; implied by --tlsverify
--tlscacert string Trust certs signed only by this CA (default
"C:\\Users\\Csaba\\.docker\\ca.pem")
--tlscert string Path to TLS certificate file (default
"C:\\Users\\Csaba\\.docker\\cert.pem")
--tlskey string Path to TLS key file (default
"C:\\Users\\Csaba\\.docker\\key.pem")
--tlsverify Use TLS and verify the remote
-v, --version Print version information and quit
Management Commands:
builder Manage builds
buildx* Build with BuildKit (Docker Inc., v0.5.1-docker)
compose* Docker Compose (Docker Inc., v2.0.0-beta.6)
config Manage Docker configs
container Manage containers
context Manage contexts
image Manage images
manifest Manage Docker image manifests and manifest lists
network Manage networks
node Manage Swarm nodes
plugin Manage plugins
scan* Docker Scan (Docker Inc., v0.8.0)
secret Manage Docker secrets
service Manage services
stack Manage Docker stacks
swarm Manage Swarm
system Manage Docker
trust Manage trust on Docker images
volume Manage volumes
Commands:
attach Attach local standard input, output, and error streams to a running container
build Build an image from a Dockerfile
commit Create a new image from a container's changes
cp Copy files/folders between a container and the local filesystem
create Create a new container
diff Inspect changes to files or directories on a container's filesystem
events Get real time events from the server
exec Run a command in a running container
export Export a container's filesystem as a tar archive
history Show the history of an image
images List images
import Import the contents from a tarball to create a filesystem image
info Display system-wide information
inspect Return low-level information on Docker objects
kill Kill one or more running containers
load Load an image from a tar archive or STDIN
login Log in to a Docker registry
logout Log out from a Docker registry
logs Fetch the logs of a container
pause Pause all processes within one or more containers
port List port mappings or a specific mapping for the container
ps List containers
pull Pull an image or a repository from a registry
push Push an image or a repository to a registry
rename Rename a container
restart Restart one or more containers
rm Remove one or more containers
rmi Remove one or more images
run Run a command in a new container
save Save one or more images to a tar archive (streamed to STDOUT by default)
search Search the Docker Hub for images
start Start one or more stopped containers
stats Display a live stream of container(s) resource usage statistics
stop Stop one or more running containers
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
top Display the running processes of a container
unpause Unpause all processes within one or more containers
update Update configuration of one or more containers
version Show the Docker version information
wait Block until one or more containers stop, then print their exit codes
Run 'docker COMMAND --help' for more information on a command.
To get more help with docker, check out our guides at https://docs.docker.com/go/guides/
Ezeknek csak egy részét nézzük meg az alábbiakban.
Komponensek
Képfájl
Angolul image. Úgy is elképzelhetjük, mint egy telepíthető program, amit már letöltöttünk. Lee tudunk tölteni képfájlokat, ill. mi magunk is tudunk készíteni; ez utóbbit ld. később. Képfájlt letölteni a docker pull parancs segítségével tudunk, pl.:
docker pull ubuntu
A képfájlokat kezelni a docker image parancs segítségével tudjuk. Pl. kilistázni a már letöltött képfájlokat:
docker image ls
Ennek eredménye:
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 1318b700e415 5 days ago 72.8MB
docker/getting-started latest 083d7564d904 7 weeks ago 28MB
hello-world latest d1165f221234 4 months ago 13.3kB
A fentiekben letöltöttük a hello-world és a docker/getting-started képfájlokat (a docker run letölti, ha nem áll rendelkezésre), az ubuntu képfájlt pedig most töltöttük le.
A docker image ls parancsnak van egy másik változata is:
docker images
Törölni a docker image rm parancs segítségével tudunk:
docker image rm hello-world
Jelen esetben hibaüzenetet kapunk:
Error response from daemon: conflict: unable to remove repository reference "hello-world" (must force) - container c37fa40e2690 is using its referenced image d1165f221234
Ez azt jelenti, hogy van egy futtatható konténer, ami erre hivatkozik, emiatt nem engedi letörölni. A konténerekről később lesz szó; most töröljük le a megadott azonosítóval:
docker container rm c37fa40e2690
Ezzel ekvivalens a következő rövidítés:
docker rm c37fa40e2690
Ha többször elindítottuk, akkor mindegyik konténert ki kell törölni egyesével, végül sikeresen kiadhatjuk a képfájl törlése parancsot:
docker image rm hello-world
Eredmény:
Untagged: hello-world:latest
Untagged: hello-world@sha256:df5f5184104426b65967e016ff2ac0bfcd44ad7899ca3bbcf8e44e4461491a9e
Deleted: sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726
Deleted: sha256:f22b99068db93900abe17f7f5e09ec775c2826ecfe9db961fea68293744144bd
Ellenőrzés, hogy sikerült-e:
docker images
Eredmény:
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 1318b700e415 5 days ago 72.8MB
docker/getting-started latest 083d7564d904 7 weeks ago 28MB
Futtatás
Amint azt láttuk, a docker run paranccsal tudunk képfájlt indítani. Ez egészen pontosan a következőt hajtja végre:
- Létrehoz egy konténert az adott program számára.
- Elindítja a programot.
A programtelepítés példában ez olyan, mintha a letöltött programot (képfájl) egyszerre feltelepítenénk, és el is indítanánk. Ebben az analógiában a feltelepített program "visszahivatkozik" a telepítőre, így a telepítőt addig nem lehet letörölni, amíg van feltelepített példány.
Emlékeztetőül a fenti két indítás:
docker run hello-world
Ez a legegyszerűbb, nincs paraméter. A másik:
docker run -d -p 80:80 docker/getting-started
Most próbáljuk meg elindítani a fent letöltött Ubuntu-t:
docker run ubuntu
Egyből visszakaptuk a parancssort, nem írt ki semmit. A futó programok kilistázása:
docker ps
Eredménye:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e447a3508082 docker/getting-started "/docker-entrypoint.…" 32 minutes ago Up 32 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp gifted_newton
A getting-started fut, de az ubuntu nem. Ráadásul bizonyos információk el vannak rejtve. A —no-trunc paraméterrel tudjuk megjeleníteni:
docker ps --no-trunc
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e447a350808259f644e4d36abe9e390baf3242cd0bc301c7f08269a28ab4f456 docker/getting-started "/docker-entrypoint.sh nginx -g 'daemon off;'" 33 minutes ago Up 33 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp gifted_newton
Visszatérve az Ubuntu indítására, használjuk az alábbi parancsot:
docker run -i -t --rm ubuntu bash
Ekkor már elindul, kapunk egy parancssori terminált, melyben szokásos Linux parancsokat adhatunk ki:
root@a93581264a9a:/# ls -al
total 56
drwxr-xr-x 1 root root 4096 Aug 1 10:42 .
drwxr-xr-x 1 root root 4096 Aug 1 10:42 ..
-rwxr-xr-x 1 root root 0 Aug 1 10:42 .dockerenv
lrwxrwxrwx 1 root root 7 Jul 23 17:35 bin -> usr/bin
drwxr-xr-x 2 root root 4096 Apr 15 2020 boot
drwxr-xr-x 5 root root 360 Aug 1 10:42 dev
drwxr-xr-x 1 root root 4096 Aug 1 10:42 etc
drwxr-xr-x 2 root root 4096 Apr 15 2020 home
lrwxrwxrwx 1 root root 7 Jul 23 17:35 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Jul 23 17:35 lib32 -> usr/lib32
lrwxrwxrwx 1 root root 9 Jul 23 17:35 lib64 -> usr/lib64
lrwxrwxrwx 1 root root 10 Jul 23 17:35 libx32 -> usr/libx32
drwxr-xr-x 2 root root 4096 Jul 23 17:35 media
drwxr-xr-x 2 root root 4096 Jul 23 17:35 mnt
drwxr-xr-x 2 root root 4096 Jul 23 17:35 opt
dr-xr-xr-x 167 root root 0 Aug 1 10:42 proc
drwx------ 2 root root 4096 Jul 23 17:38 root
drwxr-xr-x 5 root root 4096 Jul 23 17:38 run
lrwxrwxrwx 1 root root 8 Jul 23 17:35 sbin -> usr/sbin
drwxr-xr-x 2 root root 4096 Jul 23 17:35 srv
dr-xr-xr-x 11 root root 0 Aug 1 10:42 sys
drwxrwxrwt 2 root root 4096 Jul 23 17:38 tmp
drwxr-xr-x 13 root root 4096 Jul 23 17:35 usr
drwxr-xr-x 11 root root 4096 Jul 23 17:38 var
root@a93581264a9a:/# exit
exit
Figyeljük meg, hogy paraméterként meg kellett adnunk az, hogy mi fusson: ez a bash.
A következő parancs kiírja az indítás kilométer hosszú paraméterlistáját:
docker run --help
Most csak néhányat nézünk meg, ill. ismétlünk át:
- -i: az interaktív módot jelenti.
- -t: terminál létrehozása. Az egy kötőjeles paraméterek összevonhatóak, ráadásul a -i és a -t "kéz a kézben jár", így a tipikus használata pl. ez: docker run -it ubuntu bash.
- -p: belső port továbbítása a gazda gép felé, gazdaport:belsőport formában.
- -d: futtatás a háttrében.
- —rm: futás után törölje le a konténert. A programtelepítős példában ez azt jelenti, hogy telepítsük fel, indítsuk el, és amikor leállítjuk, akkor automatikusan töröljük is le. A telepítő (a képfájl) tehát megmarad, de a feltelepített (a konténer) nem. Alapértelmezésben megmarad a konténer, amit el tudunk indítani (ld. később), az újabb docker run viszont újabb konténert hoz létre. Ez olyan, mintha minden egyes induláskor a program feltelepülne újabb és újabb könyvtárba.
Konténerek
Konténerek a docker container parancs segítségével tudunk kezelni. Pl. a következő parancs kilistázza az éppen futó konténereket:
docker container ls
Ez ekvivalens az alábbival:
docker ps
A -a paraméter az összes konténert kiírja, tehát a nem futókat is:
docker container ls -a
Eredmény:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3a459a3d26f3 ubuntu "bash" About a minute ago Exited (0) About a minute ago elegant_ganguly
e447a3508082 docker/getting-started "/docker-entrypoint.…" 55 minutes ago Up 55 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp gifted_newton
Láthatjuk, hogy ott van a hibásan indított Ubuntu; ezt töröljük le a docker container rm paranccsal:
docker container rm 3a459a3d26f3
Elegendő egyértelmű megadni egy egyértelmű előtagot; pl. a következő parancs is működne:
docker container rm 3a
Hogy igazán megértsük, mit jelent a konténer, hajtsuk végre az alábbi parancsot:
docker run -it --name my_ubuntu ubuntu bash
(Itt megismerünk egy újabb parancsot: a —name segítségével nevet adhatunk; egyébként generált neveket kapunk, pl. a fenti elegant_ganguly vagy gifted_newton.) Hozzunk létre egy fájlt, majd lépjünk ki:
root@0dc7755587d0:/# echo Hello world! > mytext.txt
root@0dc7755587d0:/# ls -al mytext.txt
-rw-r--r-- 1 root root 13 Aug 1 11:02 mytext.txt
root@0dc7755587d0:/# cat mytext.txt
Hello world!
root@0dc7755587d0:/# exit
exit
Listázzuk ki a konténereket:
docker container ls -a
Eredmény:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0dc7755587d0 ubuntu "bash" 4 minutes ago Exited (0) 3 minutes ago my_ubuntu
e447a3508082 docker/getting-started "/docker-entrypoint.…" About an hour ago Up About an hour 0.0.0.0:80->80/tcp, :::80->80/tcp gifted_newton
Az ubuntu tehát befejeződött. Most indítsuk újra:
docker run -it ubuntu bash
Próbáljuk meg kiírni a mytext.txt tartamát:
root@809d788edc08:/# cat mytext.txt
cat: mytext.txt: No such file or directory
root@809d788edc08:/# exit
exit
Nem sikerült, mert nincs ilyen. De mit történt?
docker container ls -a
Az eredmény:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
809d788edc08 ubuntu "bash" About a minute ago Exited (1) 45 seconds ago upbeat_napier
0dc7755587d0 ubuntu "bash" 8 minutes ago Exited (0) 8 minutes ago my_ubuntu
e447a3508082 docker/getting-started "/docker-entrypoint.…" About an hour ago Up About an hour 0.0.0.0:80->80/tcp, :::80->80/tcp gifted_newton
Létrejött egy másik ubuntu példány! Indítsuk el az elsőt:
docker container start -i my_ubuntu
Láthatjuk, hogy konténer azonosító mellett név alapján is hivatkozhatunk rá. Most már ki tudjuk listázni a mytext.txt tartalmát:
root@0dc7755587d0:/# cat mytext.txt
Hello world!
root@0dc7755587d0:/# exit
exit
Végül: a docker container stop paranccsal tudjuk leállítani a konténert:
docker container stop e447a3508082
Ennek egyszerűbb alternatívája a docker stop
docker stop e447a3508082
Tárhely
Konténer létrehozásakor szükség lehet tárhelyre, ami automatikusan létrejön, pl. a MySQL adatbázis esetén (ld. később). Mi magunk is létre tudunk hozni tárhelyet, a docker volume paranccsal. Volume létrehozása:
docker volume create my_volume
Kilistázása:
docker volume ls
Használata:
docker run -v my_volume:/test -it ubuntu bash
A létrehozott volume-ra a /test könyvtár hivatkozik. Példaként hozzunk létre egy fájlt benne, majd lépjünk ki. Indítsuk el még egyszer a fenti parancsot. Ekkor újabb Ubuntu példány jön létre, de a /test könyvtárban ott lesz a másikban létrehozott fájl.
Volume törlése:
docker volume rm my_volume
De előtte le kell törölni azokat a konténereket, amelyek erre hivatkoznak.
Hálózat
Ha több programot szeretnénk indítani, amelyek egymással kommunikálnak, akkor szükség lehet külön hálózat létrehozására. Erre később látunk majd példát, amikor külön hálózatba kerül egy webalkalmazás és a hozzá tartozó adatbázis. Itt a hálózatok kezelését nézzük meg. A hálózatokat a docker network paranccsal tudjuk kezelni. Pl. kilistázása:
docker network ls
Hálózat létrehozása:
docker network create my_network
Hálózat törlése:
docker network rm my_network
Népszerű képfájlok
NGINX
Ez a legnépszerűbb Docker képfájl. Ez egy webszerver, így önálló használata ritka, viszont számos más alkalmazás alapját képezi. Önálló használatára példa: hozzunk létre egy könyvtárat, pl. d:\Docker\ngnix, és oda másoljunk be egy fájlt index.html néven, valamilyen egyszerű tartalommal. Indítsuk el a következőt:
docker run -v d:\Docker\ngnix:/usr/share/nginx/html -p 12345:80 -d nginx
Majd egy böngészőből nyissuk meg a http://localhost:12345/ oldalt.
Ubuntu
Volt már szó arról, hogy a Docker programokat "körülöleli" egy leegyszerűsített Linux operációs rendszer. Valójában magát az operációs rendszert is letölthetjük:
docker pull ubuntu
Indítása:
docker run -t -i ubuntu /bin/bash
Ezzel egyből el is indítottuk a shell-t. Az operációs rendszer viszont annyira le van egyszerűsítve, hogy még egy vi sincs benne. (Nem igaz tehát az a mondás, hogy vi minden Unix rendszeren van!) Viszont hogy lássuk, ez már egy "igazi" Linux, fel tudjuk telepíteni az alábbi két paranccsal:
apt-get update
apt-get install vim
vim
Ne feledjük, kilépés a vi-ból: q: (Enter), bonyolultabb esetekben Esc :q! Enter.
Ha most kilépünk a rendszerből exit, akkor docker run helyett érdemes a már egyszer elindított konténert újraindítani. Nézzük meg a konténer azonosítót a következő paranccsal:
docker ps -a
Majd indítsuk el magát a konténert (példaként a nálam generált konténer azonosítóval):
docker container start -i 6502292e765c
Busybox
Ahogy arról már volt szó, Dockerben tipikusan mindegyik program külön konténert kap, amit egy Linux operációs rendszer "vesz körül". Ha sok programot szeretnénk futtatni, akkor nem mindegy, hogy mekkora az az operációs rendszer. A legfrissebb Ubuntu mérete az írás pillanatában 72,8 MB. Ez operációs rendszer mércével mérve nem nagy, de ha fel kell szorozni egy viszonylag nagy számmal, akkor már nagyra nőhet. Emiatt van jelentősége a Busybox Linux konténernek, ami egy floppy lemezre is ráférne: mindössze 1,24 MB a mérete. Indítása:
docker run -it --rm busybox sh
Ez tehát egy shell, azon belül is a legegyszerűbb sh, hogy ezzel is spóroljanak a méreten, és amint kilépünk, az operációs rendszer leáll.
MySQL
Letöltése:
docker pull mysql
Indítása:
docker run -e MYSQL_ROOT_PASSWORD=farago -d mysql
Kapcsolódás a frissen indított szerverhez:
docker exec -it ace3f46a10e4 mysql -p
(A ace3f46a10e4 a konténer azonosító, amit a docker ps paranccsal tudunk lekérdezni.)
Itt meg kell adnunk a fenti jelszót, majd SQL parancsokat tudunk kiadni, pl.:
show databases;
connect mysql;
show tables;
select * from time_zone_name where name like 'Europe%';
Ha leállítjuk, majd ugyanazt a konténert elindítjuk (tehát nem docker start, hanem docker run [contaniner_id]), akkor a korábbi változtatások megmaradnak.
PostgreSQL
Letöltés:
docker pull postgres
Indítás:
docker run -e POSTGRES_PASSWORD=farago -d postgres
Kapcsolódás:
docker exec -it 9df5bae94ac5 psql -U postgres
Itt a 9df5bae94ac5 a konténer azonosító, amit a docker ps paranccsal tudunk lekérdezni.
Jenkins
Letöltése:
docker pull jenkins/jenkins
Indítása:
docker run -p 8080:8080 -p 50000:50000 jenkins/jenkins
Kipróbáláshoz ez is megfelelő. Igazi üzemeltetéshez viszont érdemes elolvasni a https://www.jenkins.io/doc/book/installing/docker/ oldalon a tudnivalókat.
Első induláskor a következő jelenik meg a naplóban:
*************************************************************
*************************************************************
*************************************************************
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:
50b5629175bd41c58ce4a4aa975474f4
This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
*************************************************************
*************************************************************
*************************************************************
Ha nem jegyeztük fel, akkor a következőképpen tudjuk kiolvasni:
>docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
69a309cfbc78 jenkins/jenkins "/sbin/tini -- /usr/…" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 0.0.0.0:50000->50000/tcp, :::50000->50000/tcp xenodochial_euclid
Majd megadva a konténer azonosítót:
docker exec 69a309cfbc78 cat /var/jenkins_home/secrets/initialAdminPassword
Nyissuk meg a http://localhost:8080/ oldalt. Ott kérni fogja a fenti jelszót, az másoljuk be. Majd folytassuk a beállításokat a webes felületen úgy, mintha lokálisan telepítettük volna.
Git
A Dockerből futtatott programok nem feltétlenül kell, hogy szolgáltatásként fussanak a háttérben, "egyszer használatos" parancsokat is kiadhatunk. Pl. git repository-t klónozhatunk az alábbi paranccsal:
docker run -ti --rm -v ${HOME}:/root -v ${pwd}:/git alpine/git clone https://github.com/faragocsaba/teszt.git
Ez Windows alatt csak PowerShell-ben működik. Normál parancssorból meg kell adni a könyvtárakat, pl.:
docker run -ti --rm -v C:\Users\Csaba:/root -v D:\Docker:/git alpine/git clone https://github.com/faragocsaba/teszt.git
Képfájl készítése
A készítés lépései
Mi magunk is tudunk Docker képfájlt készíteni. A módszert példákon keresztül nézzük meg, a végén pedig összefoglaljuk a tanultakat. Első példaként hozzunk létre egy fájlt Dockerfile néven, pl. az alábbi tartalommal:
FROM nginx
COPY content /usr/share/nginx/html
A Dockerfile mellé hozzunk létre egy content könyvtárat, bele egy index.html fájlt, valamilyen minimális tartalommal. Majd adjuk ki az alábbi parancsot:
docker build -t my_webapp .
Ha sikeres volt a művelet, a docker images parancs kilistázza az imént létrejött képfájlt is. Indítása:
docker run -p 80:80 -d my_webapp
A http://localhost/ oldalt megnyitva az index.html tartalmát kell, hogy lássuk. (Megjegyzés: noha a getting-started futtatását leállítottam, a böngésző "megjegyezte", hogy át kell irányítani a localhost 80-at, így hibára futottam. A problémát nem tudtam megoldani; egy másik böngészőből rendesen működött.)
A Docker képfájlt létrehozásának a lépései tehát:
- Hozzunk létre egy leíró fájlt Dockerfile néven, és adjuk meg benne a szükséges lépéseket.
- Adjuk ki a docker build parancsot.
- Indítsuk hagyományos módon.
Ubuntu példa
Arról már volt szó, hogy az Ubuntu Docker képfájl egy nagyon lecsupaszított disztribúciót tartalmaz, ami még vi szövegszerkesztőt sem tartalmaz. Készítsünk egy olyan képfájlt, ami az alap Ubuntu disztribúción felül ezt is tartalmazza.
Dockerfile
FROM ubuntu
RUN apt-get update
RUN apt-get install -y vim
Képfájl létrehozása:
docker build -t ubuntu_with_vi .
Indítása:
docker run -it ubuntu_with_vi bash
Parancssorból el tudjuk indítani a vi-t!
NodeJS példa
Győződjünk meg arról, hogy nem fut semmi a 8080-as porton (vagy módosítsuk a lenti példát ennek megfelelően). Készítsünk el egy egyszerű webszervert my_node_webserver.js néven:
const http = require('http'); http.createServer(function (req, res) { res.write('Hello from Node.js!'); res.end(); }).listen(8080);
Próbáljuk is ki:
node my_node_webserver.js
Nyissuk meg a böngészőből a http://localhost:8080/ oldalt. A fenti szöveget kell látnunk. Állítsuk le a webszervert.
Készítsünk most ebből Docker képfájlt! A Dockerfile tartalma:
FROM node:12-alpine
WORKDIR /app
COPY my_node_webserver.js .
CMD ["node", "my_node_webserver.js"]
A képfájlt elkészítése:
docker build -t my_node_webapp .
Indítása:
docker run -p 8080:8080 -d my_node_webapp
A http://localhost:8080/ oldalon a fenti szöveget kell látnunk.
Python példa
Hozzunk létre egy webszervert Pythonban (my_python_webserver.py):
from flask import Flask server = Flask("my_python_webserver") @server.route("/") def hello(): return "Hello from Python!" server.run(host='0.0.0.0')
Ehhez szükségünk van a flask könyvtárra; telepítsük fel:
pip install flask
Indítsuk el:
python my_python_webserver.py
A kiírtak alapján a http://192.168.1.104:5000/ oldalon érhető el, de egyébként a http://localhost:5000/ oldalon is megjelenik a szöveg. Állítsuk le, hogy felszabaduljon az 5000-es port. (Győződjünk meg arról, hogy valóban felszabadult.)
Készítsünk ebből Docker képfájlt!
Dockerfile
FROM python:3.9-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY my_python_webserver.py .
CMD ["python", "my_python_webserver.py"]
Hozzunk létre egy fájlt requirements.txt néven, az alábbi tartalommal:
Flask==1.1.1
A program gyökerében tehát 3 fájl van: my_python_webserver.py, Dockerfile és requirements.txt.
Készítsük el a képfájlt:
docker build -t my_python_webapp .
Indítsuk el:
docker run -p 5000:5000 -d my_python_webapp
Ha minden rendben történt, akkor a http://localhost:5000/ oldalon megjelenik a szöveg.
Java példa
A Java webszervert Spring Boot segítségével készítjük el. Az eredmény egy futtatható jar fájl, ami minden szükségeset tartalmaz. A Docker bemenete valójában csak ez a jar fájl, és "nem tudja", hogy mögötte Spring Boot van. Így ugyanezzel a módszerrel akármilyen futtatható jar fájlból készíthetünk Docker képfájlt.
A művelethez szükség lesz egy legalább 11-es Java-ra, valamint Maven-re (https://maven.apache.org/). Győződjünk meg arról, hogy a 8080-as porton nem fut semmi.
Először készítsük el a programot! Ez már bonyolultabb lesz, mint a Node.js vagy a Python. Hozzuk létre az alábbi fájlszerkezetet:
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.0</version> <relativePath/> </parent> <groupId>hu.faragocsaba</groupId> <artifactId>my_java_webserver</artifactId> <version>1.0</version> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
src/main/java/hu/faragocsaba/MyApplication.java
package hu.faragocsaba; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }
src/main/java/hu/faragocsaba/MyController.java
package hu.faragocsaba; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MyController { private Logger logger = LoggerFactory.getLogger(MyController.class); @GetMapping("/") public String index() { logger.info("GET /"); return "Hello from Spring Boot!"; } }
Fordítás:
mvn clean package
Az eredmény: target/my_java_webserver-1.0.jar. Indítsuk el:
cd target
java -jar my_java_webserver-1.0.jar
Próbáljuk ki, hogy működik-e; nyissuk meg a http://localhost:8080/ oldalt. Ha minden rendben ment, a böngészőben láthatjuk a szöveget. Győződjünk meg arról, hogy a naplóban megjelenik a GET bejegyzés. Majd állítsuk le a programot (Ctrol + C).
Hozzuk létre a program gyökerében (ahol a pom.xml található) a Dockerfile-t az alábbi tartalommal:
FROM openjdk:11-slim
WORKDIR /app
COPY ./target/my_java_webserver-1.0.jar my_java_webserver.jar
CMD "java" "-jar" "my_java_webserver.jar"
A képfájl létrehozása:
docker build -t my_java_webapp .
Indítása:
docker run -p 8080:8080 -d my_java_webapp
Kipróbálás: a http://localhost:8080/ oldal megnyitásával.
Lejjebb arról is szó lesz, hogy hogyan tudunk belépni a virtuális gépre, ill hogy hogyan tudjuk kívülről megnézni a naplófájlt.
Paraméterek
A Dockerfile legfontosabb parancsai tehát az alábbiak:
- FROM: ezzel adjuk meg azt, hogy mi legyen az alap. Láthattunk példát webszerverre, Linux operációs rendszerre, Node.js-re, Pythonra, Java-ra.
- WORKDIR: ezzel adjuk meg az alapértelmezett könyvtárat a képfájlban.
- COPY: fájlokat tudunk másolni a lokális gépről a képfájlba.
- RUN: magában a képfájlban lefutó parancsok. A képfájl már ezek eredményét tartalmazza.
- CMD: ez az a parancs, ami indításkor lefut.
Műveletek
Shell indítása
Már futó alkalmazáshoz a docker exec paranccsal tudunk csatlakozni, pl.:
docker exec -it c8796e5adce4 bash
A c8796e5adce4 helyére a mi konténerünk azonosítóját kell írni, ill. fontos, hogy legyen bash. Elképzelhető, hogy csak sh van, bash nincs.
Naplófájl megjelenítése
A naplófájlt az alábbi paranccsal tudjuk megjeleníteni:
docker logs -f c8796e5adce4
A c8796e5adce4 helyére a mi konténerünk azonosítóját kell írni.
Fájl másolása konténerbe
A docker cp parancs segítségével tudunk fjlt másolni konténerbe ill. onnan ki, pl.:
docker cp testfile.txt 54d6bcffccd5:/testfile.txt
ill.
docker cp 54d6bcffccd5:/testfile.txt testfile.txt
A 54d6bcffccd5 heéyre értelemszerűen a mi konténer azonosítónkat kell írni, ill. a testfile.txt-nek léteznie kell.
Tag-elés és programok megosztása
A Docker Hub-on mi magunk is meg tudunk osztani komponenseket. Ennek lépései:
- Regisztráljunk a https://hub.docker.com/ oldalon. Ez ingyenes.
- Tag-eljük meg a megosztani kívánt képfájlt. Prefixként a saját felhasználónevet használjuk, pl.:
docker tag ubuntu_with_vi fcsaba77/ubuntu_with_vi
- Lépjünk be parancssorból a Docker Hub-ra (értelemszerűen a saját felhasználónevet megadva):
docker login -u fcsaba77
- Adjuk ki a docker push parancsot:
docker push fcsaba77/ubuntu_with_vi
Ha nincs másik számítógépünk, ki tudjuk próbálni ezen az oldalon: https://labs.play-with-docker.com/.
Összetett alkalmazások
Egy Spring Boot példa
Készítsünk egy Spring Boot webalkalmazást, amivel adatokat mentünk az adatbázisba, valamint onnan ki is olvasunk. Az egyszerűség érdekében a további műveleteket mellőzzük. Konkrétabban, a következő GET HTTP hívások hatására bekerülnek az adatbázisba értékek:
- http://localhost:8080/add?name=apple
- http://localhost:8080/add?name=banana
- http://localhost:8080/add?name=orange
A következővel pedig ki tudjuk listázni a beírt értékeket: http://localhost:8080/.
Az alkalmazás forráskódja:
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.0</version> <relativePath/> </parent> <groupId>hu.faragocsaba</groupId> <artifactId>fruits</artifactId> <version>1.0</version> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
src/main/java/hu/faragocsaba/FruitApp.java
package hu.faragocsaba; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class FruitApp { public static void main(String[] args) { SpringApplication.run(FruitApp.class, args); } }
src/main/java/hu/faragocsaba/Fruit.java
package hu.faragocsaba; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Fruit { @Id @GeneratedValue private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
src/main/java/hu/faragocsaba/FruitRepository.java
package hu.faragocsaba; import org.springframework.data.jpa.repository.JpaRepository; public interface FruitRepository extends JpaRepository<Fruit, Long> {}
src/main/java/hu/faragocsaba/FruitController.java
package hu.faragocsaba; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class FruitController { private Logger logger = LoggerFactory.getLogger(FruitController.class); @Autowired private FruitRepository fruitRepository; @GetMapping public List<Fruit> getAllFruits() { logger.info("Get all fruits"); return fruitRepository.findAll(); } @GetMapping("/add") public Fruit addFruit(@RequestParam String name) { logger.info("Add fruit: " + name); Fruit fruit = new Fruit(); fruit.setName(name); return fruitRepository.save(fruit); } }
src/main/resources/application.properties
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:postgresql://localhost:5432/fruits
spring.datasource.username=postgres
spring.datasource.password=farago
A program letölthető innen: fruits.zip.
Fordítsuk le a szokásos módon:
mvn clean package
Indítás előtt győződjünk meg arról, hogy fusson egy PostgreSQL adatbázis. A beállításhoz segítséget kaphatunk a Adatbázisok oldalon. Esetünkben a psotgres felhasználó jelszava farago, és van egy fruits adatbázis (CREATE DATABASE my_db;).
Indítsuk el a backend programot:
cd target
java -jar fruits-1.0.jar
Böngészőből próbáljuk ki (http://localhost:8080/add?name=apple, http://localhost:8080/add?name=banana, http://localhost:8080/add?name=orange, http://localhost:8080/), ill. érdemes az adatbázist is ellenőrizni, pl. a psql parancssori alkalmazással. (Csatlakozás az adatbázishoz: \c fruits, majd select * from fruit;).
Ha működik, álltsuk le az alkalmazást és az adatbázist is. Ez utóbbi leállítása: Szolgáltatások (Services) → itt keressük meg a P betűnél, majd állítsuk le.
Csatlakozás Dockerben futó adatbázishoz
Győződjünk meg arról még egyszer, hogy szabad az 5432 és a 8080 port is. Indítsuk el a PostgreSQL adatbázist Dockerből:
docker run -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=farago -e POSTGRES_DB=fruit -d postgres
A szokásos eszközökkel (parancssori psql vagy DBeaver) meggyőződhetünk arról, hogy valóban elindult. Elvben nem lenne szabad, hogy másképp működjön, mint a saját gépre közvetlenül telepített, így a java -jar fruits-1.0.jar hatására el kell, hogy induljon. Valóban elindul, bár sok esetben nehezen értelmezhető kivételeket dob. Minden esetben arra jutottam, hogy én gépeltem el valamit, szóval másodszor, harmadszor is ellenőrizzük, hogy betűről betűre jól írtuk-e.
A backend és az adatbázis is Dockerben fut
A már bemutatott Java alkalmazás mintájára készítsük el ennek is a Docker képfájlját.
Dockerfile
FROM openjdk:11-slim
WORKDIR /app
COPY ./target/fruits-1.0.jar fruits.jar
CMD "java" "-jar" "fruits.jar"
Képfájl generálása:
docker build -t fruits .
Győződjünk meg, hogy létrejött a docker images parancs kiadásával.
A két komponens együttes indítása már trükkösebb; hosszú időbe telt, mire sikerült működésre bírni. Először is létre kell hozni egy hálózatot:
docker network create fruit
Indítás előtt győződjünk meg, hogy nem fut egy másik példány sem; szükség esetén állítsuk le (docker ps, majd docker stop). Indítsuk el a PostgreSQL-t ezzel a hálózattal:
docker run --name fruit_db -p 5432:5432 --network fruit -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=farago -e POSTGRES_DB=fruits -d postgres
Majd a webalkalmazést:
docker run -p 8080:8080 --network fruit -e SPRING_DATASOURCE_URL=jdbc:postgresql://fruit_db:5432/fruits fruits
Figyeljük meg, hogy a spring.datasource.url-t felüldefiniáltuk a SPRING_DATASOURCE_URL környezeti változóval. A konvenció tehát:
- nagybetűssé kell alakítani,
- a pontokat aláhúzásokká kell alakítani.
Ha minden rendben történt, akkor a fenti tesztet így is végre tudjuk hajtani.
Állítsuk le a webalkalmazást és az adatbázist is (docker ps, docker stop).
Docker Compose
Ez már így is nehézkes, és képzeljük el, hogy több komponenst szeretnénk indítani, sok egyéb beállítással; azt nehéz lenne parancssorból megfelelően kezelni. Erre találták ki a Docker Compose fájlt. A fentivel többé-kevésbé ekvivalens megoldás az alábbi:
docker-compose.yml
version: "3.9"
services:
fruit_database:
image: "postgres"
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=farago
- POSTGRES_DB=fruits
ports:
- "5432:5432"
fruit_web:
image: "fruits"
ports:
- "8080:8080"
depends_on:
- fruit_database
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://fruit_database:5432/fruits
Hálózatot nem kell külön létrehozni, azt automatikusan létrehozza a Docker. Indítása:
docker compose up
Vagy háttérben:
docker compose up -d
A háttérben indított rendszer leállítása:
docker compose stop
További beállítások
A Docker Compose igazi erejét a rendszer némi átalakításával illusztráljuk. Tegyük fel, hogy az application.properties fájlba az adott fejlesztő számítógépének az adatai kerültek. Az egyes üzemeltetői gépeken ez eltér. Tegyük fel, hogy a PostgreSQL adatbázis azonosítója és jelszava eltér. Ez esetben nem kell módosítani az application.properties fájlt, sőt, másikat sem kell feltétlenül létrehozni, hanem a már elkészített Docker képfájlt használhatjuk, mindössze két környezeti változót kell beállítani:
version: "3.9"
services:
fruit_database:
image: "postgres"
environment:
- POSTGRES_USER=fruituser
- POSTGRES_PASSWORD=fruitpassword
- POSTGRES_DB=fruits
ports:
- "5432:5432"
fruit_web:
image: "fruits"
ports:
- "8080:8080"
depends_on:
- fruit_database
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://fruit_database:5432/fruits
- SPRING_DATASOURCE_USERNAME=fruituser
- SPRING_DATASOURCE_PASSWORD=fruitpassword
A konvenció ismét: nagybetűsítés + pont aláhúzásra cserélése.
Többlépcsős építkezés
Legalábbis erre próbáltam lefordítani az angol multi-stage builds kifejezést. Arról van szó, hogy egy Dockerfile több lépést is tartalmazhat.
Egy tipikus használati eset az, hogy első lépésben lefordítjuk a programot, a másodikban pedig futtatjuk. Ebben az esetben a felhasználó akkor is tud Docker képfájlt készíteni, ha nincs a számítógépén se Java, se Maven. Ennek meg van az az előnye is, hogy a fordítás mindig tiszta olyan értelemben, hogy frissen töltődik le minden függőség, és frissen fordul le minden egy úgymond szűz rendszeren, azaz nem fordulhat elő az, hogy a fejlesztői gépen valami beállítás máshol megismételhetetlen eredményre vezet.
Módosítsuk a programot úgy, hogy H2 adatbázist használjon! A pom.xml-bőltöröljük a PostgreSQL függőséget, és írjuk be a helyére ezt:
<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>
Az src/main/resource/application.properties tartalma az alábbi legyen:
spring.datasource.name=fruits
spring.datasource.generate-unique-name=false
spring.datasource.username=sa
spring.datasource.url=jdbc:h2:mem:fruits
spring.jpa.hibernate.ddl-auto=create
spring.jpa.defer-datasource-initialization=true
spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.h2.console.settings.web-allow-others=true
Itt a spring.h2.console.settings.web-allow-others=true amiatt kell, mert enélkül nem lenne elérhető kívülről a H2 webes konzol.
A Dockerfile:
# Build stage
FROM maven:3.8.2-jdk-11-slim AS build
COPY src /app/source/src
COPY pom.xml /app/source
RUN mvn -f /app/source/pom.xml clean package
# Package stage
FROM openjdk:11-jre-slim
WORKDIR /app
COPY --from=build /app/source/target/fruits-1.0.jar /app/fruits.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "fruits.jar"]
Lássuk az újdonságokat!
- Megjegyzéseket a # segítségével tudunk beszúrni.
- FROM maven:3.8.2-jdk-11-slim AS build: ez maga a Maven. Vegyük észre a végén az AS build-et ezzel a névvel hivatkozunk a második fázisban.
- COPY —from=build: itt azt adjuk meg, hogy nem a saját gépünkről szeretnénk másolni, hanem az első fázis eredményéből.
- EXPOSE 8080: ez nem csinál semmit. Valójában ezzel üzen a Dockerfile készítője a felhasználójának, hogy futtatáskor mely portokat kell kiajánlani.
- ENTRYPOINT ["java", "-jar", "fruits.jar"]: nagyjából ugyanazt jelenti, mint a CMD; a különbség az, hogy a CMD felülírható, az ENTRYPOINT nem.
A fordítás és a futtatás ugyanúgy történik, mint idáig:
docker build -t fruits .
ill.
docker run -p 80:8080 fruits
Fordításkor az alábbi történik:
- Létrejön egy Docker képfájl a maven:3.8.2-jdk-11-slim alapján.
- Bemásolódnak a szükséges forrásfájlok.
- A mvn -f /app/source/pom.xml clean package lefordítja. Ez azt is jelenti, hogy letölti az internetről a jar fájlokat. Ez minden esetben bekövetkezik, nem menti el a korábbiakat. (Emiatt nem érdemes mobilnetről futtatni.)
- Az eredmény belekerül a végleges képfájlba. A végleges képfájl alapja a openjdk:11-jre-slim, ami nem tartalmazza sem a Mavent, sem a forrásokat. (Figyeljük meg, hogy itt a {{-jre} képfájlt használtuk alapként.})
A futtatásnál is változtattunk egy picit a dolgon: a 80-as porton tettük elérhetővé, így nem kell beírni a :8080-at. Próbáljuk ki:
- http://localhost/add?name=apple: gyümölcs hozzáadása.
- http://localhost/add?name=banana: gyümölcs hozzáadása.
- http://localhost/add?name=orange: gyümölcs hozzáadása.
- http://localhost: listázás.
- http://localhost/h2-console: itt a JDBC URL-hez írjuk be ezt: jdbc:h2:mem:fruits, majd Connect. Kiadhatunk SQL parancsokat, pl. SELECT * FROM FRUIT.
A program letölthető innen: fruits-multistage.zip.