Hálózatkezelés a konténerekben¶
A konténerben futó alkalmazások többsége hálózati kapcsolatot igényel, tipikus példák az olyan webszerverek vagy adatbáziskezelők, amelyek szolgáltatásait más számítógépek veszik igénybe. A Docker hálózatkezelése többféle lehetőséget kínál a konténerek közötti kommunikációhoz és a hálózatok kezeléséhez. A megfelelő változat kiválasztása és beállítása kulcsfontosságú a Docker alkalmazások hatékony működéséhez. Ennek érdekében ebben a fejezetben megismerjük:
A Docker hálózati működésének koncepcióját.
A Docker által alkalmazott hálózati interfészeket.
A Docker hálózatok beállításainak módszereit.
A névszerverek beállításának módját.
A konténerek által kezelt külső és belső portok beállítását.
Docker konténerek hálózati statisztikáinak lekérdezését.
Szükséges eszközök: |
Windows, MacOS vagy Linux operációs rendszerű számítógép, telepített Docker szoftverrel. |
Feldolgozási idő: |
kb. 2 óra, gyakorlati feladatok megoldására további 2 óra. |
A Docker bridge¶
A konténerek számára hálózati kapcsolatainak működtetését végző leggyakoribb megoldás egy ún. bridge-en alapul. Ez az OSI modell hálózati rétegében működik és két (vagy több) hálózati eszközt köt össze. A Docker a telepítésekor docker0 néven automatikusan létrehoz egy ilyen interfészt – ez biztosítja az összeköttetést a Docker belső hálózata és a gép fizikai hálózati interfésze közt. A konténerek ehhez a belső hálózathoz kapcsolódnak, és ezen keresztül egymással is tudnak kommunikálni.
Bridge-elt hálózat a Dockerben¶
A Docker alapértelmezett belső hálózata a 172.17.0.0/16, ami egy teljes „B” osztály, azaz 65.534 gép címezhető meg benne. A bridge a belső hálózatban levő konténerek átjárójaként is funkcionál, a címe 172.17.0.1. A bridge-n keresztül a belső gépek is elérik egymást.
A host gépen az interfészek adatai az alábbi parancsokkal kérdezhetők le (az ens160 a példában szereplő gép fizikai interfésze):
root@columbo.uni-eszterhazy.hu:~# ip address ls ens160
2: ens160: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:0c:29:39:30:16 brd ff:ff:ff:ff:ff:ff
inet 193.225.33.196/24 brd 193.225.33.255 scope global ens160
valid_lft forever preferred_lft forever
inet6 fe80::20c:29ff:fe39:3016/64 scope link
valid_lft forever preferred_lft forever
root@columbo.uni-eszterhazy.hu:~# ip address ls docker0
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:0b:e1:78:f5 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
A belső hálózat interfészeinek neveit, IP címét és alhálózati maszkját, alapértelmezett átjáróját, valamint a köztük levő hálózati kapcsolat működőképességét két konténer létrehozásával demonstrálhatjuk. Ehhez most egy másik Linux disztribúciót, a CentOS-t használjuk (valójában azért, mert az Ubuntu alap változata nem tartalmazza az ehhez szükséges parancsokat és nem akarunk most ezzel bíbelődni). A CentOS-ből a hálózati paraméterek ellenőrzése után most nem az exit, hanem a Ctrl-p-q kombinációval lépünk ki, így a konténer a háttérben továbbra is működő állapotban marad.
Az első konténer neve legyen centos-1! Ebben az IP címet a hostname -I, az alapértelmezett átjárót az ip r paranccsal lehet lekérdezni.
root@columbo.uni-eszterhazy.hu:~# docker run -it --name centos-1 centos:latest /bin/bash
[root@347a5e549af4 /]# hostname -I
172.17.0.2
[root@347a5e549af4 /]# ip r
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2
[root@347a5e549af4 /]# [CTRL]-[p]-[q]
Hozzunk létre egy ugyanilyen konténert centos-2 névvel, majd kérjük le az IP címét, alapértelmezett átjáróját és pingeljük meg a centos-1 konténert! Látható, hogy a mindkét konténer IP címe a 172.17.0.0/24 -es hálózatban van, a 172.17.0.1-et használják átjáróként, és pingelni is tudják egymást. (A ping parancs folyamatos futását a Ctrl-c-vel lehet megszakítani.)
root@columbo.uni-eszterhazy.hu:~#
root@columbo.uni-eszterhazy.hu:~# docker run -it --name centos-2 centos:latest /bin/bash
[root@b6fa536617cb /]# hostname -I
172.17.0.3
[root@b6fa536617cb /]# ip r
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.3
[root@b6fa536617cb /]# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.096 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.100 ms
...
[root@b6fa536617cb /]# [CTRL]-[p]-[q]
Amennyiben a fizikai gép adott bridge-éhez kapcsolódó konténerek hálózati címeit szeretnénk lekérdezni, használjuk a docker network inspect parancsot a kérdéses bridge nevével. Az egyes konténerek felsorolása a Containers szekcióban történik, amelyben a név mellett leolvashatók a MAC- és IP címek is.
root@columbo.uni-eszterhazy.hu:~# docker network inspect bridge
"Containers": {
"347a5e549af43e0f199501f44f050a3cb26c462bb58d0ea6b37c5fea6143086c": {
"Name": "centos-1",
"EndpointID": "c3e8a624be109df75edcedfd228a95b1ede919f12682de8a228e229e7b1be883",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"b6fa536617cbf72f26831b7f0ceb0a1e1a1f3b97bfdbb4acd202537b2e0b89ae": {
"Name": "centos-2",
"EndpointID": "47e131142a548ec17d1d404e8b86225dc9ca706ef0420497ad868e544bed809a",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
},
További hálózatok¶
A Docker több hálózat működését is támogatja, ehhez viszont újabb bridge-eket kell létrehozni. Értelemszerűen az egyes bridge-ek eltérő IP hálózatokat definiálnak, melyek ugyan elérik a közös átjárójukat, de más bridge-ekhez kapcsolódó konténereket nem. Új bridge-ek létrehozása kiváló módszere az egyes konténerek elszeparálásának.
Egy új bridge létrehozása a docker network create paranccsal történik. További paraméterek megadása is szükséges, a -d bridge hatására egy új bridge jön létre, amely a --subnet után CIDR formátumban megadott hálózatot kezeli. A parancs utolsó paramétere a létrehozandó bridge belső, egyedi neve.
root@columbo.uni-eszterhazy.hu:~# docker network create -d bridge \
--subnet 192.168.1.0/24 secretbridge
Egy konténer létrehozásakor megadható, hogy azt melyik bridge-hez kapcsoljuk. Ennek kipróbálásához hozzunk létre egy harmadik CentOS-t úgy, hogy a hálózatát a most létrehozott secretbridge szolgálja ki! A konténer elindítása után kérdezzük le az IP címét (ami, ha jól dolgoztunk, a 192.168.1.0/24 hálózatból kerül ki) és próbáljuk meg pingelni az átjárót (192.168.1.1), a másik bridge-et (172.17.0.1), a Google névszerverét (8.8.8.8), végül a 172.17.0.2-es konténert!
root@columbo.uni-eszterhazy.hu:~# docker run -dit --name centos-3 --network secretbridge centos:latest /bin/bash
[root@f7265ac449e9 /]# hostname -I
192.168.1.2
[root@ee93a1e0e2a3 /]# ping 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.264 ms
[root@f7265ac449e9 /]# ping 172.17.0.1
PING 172.17.0.1 (172.17.0.1) 56(84) bytes of data.
64 bytes from 172.17.0.1: icmp_seq=1 ttl=64 time=0.127 ms
[root@ee93a1e0e2a3 /]# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=116 time=6.07 ms
[root@ee93a1e0e2a3 /]# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
(végtelen várakozás)
^C
--- 172.17.0.2 ping statistics ---
58 packets transmitted, 0 received, 100% packet loss, time 58376ms
Ellenőrizzük, hogy a centos-3 valóban a secretbridge hálózatban működik-e! Használjuk ehhez a már ismert docker network inspect parancsot!
root@columbo.uni-eszterhazy.hu:~# docker network inspect secretbridge | grep -i name
"Name": "secretbridge",
"Name": "centos-3",
A hálózatok kezelésével kapcsolatban meg kell említeni még néhány, a gyakorlatban is jól használható parancsot. A docker network ls felsorolja az összes, a rendszerben működő hálózati interfészt:
root@columbo.uni-eszterhazy.hu:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
22a5ee65d187 bridge bridge local
18487edbe6c7 host host local
22d5e840354f none null local
82ac7a55a546 secretbridge bridge local
A már nem használt interfészek a docker network prune paranccsal törölhetők. Az alábbi példában először töröljük a centos-3 konténert, így a secretbridge interfészhez már nem kapcsolódik egyetlen konténer sem. Ez után töröljük az összes, már nem használt bridge-et is.
root@columbo.uni-eszterhazy.hu:~# docker rm centos-3
centos-3
root@columbo.uni-eszterhazy.hu:~# docker network prune
WARNING! This will remove all custom networks not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Networks:
secretbridge
Névszerver a konténerben¶
A hálózati paraméterek megadásakor nem különösebben foglalkoztunk a névszerver kérdésével, mert a Docker alapjában véve a rendszerben beállított DNS szervert használja. A Docker átkonfigurálásával lehetőség van más DNS szerver használatára, a gyakorlatban azonban erre ritkán van szükség. Ha mégis, akkor az egyedi DNS szervert inkább az adott konténer számára érdemes beállítani a --dns paraméterrel. Az alábbi példában a nagy adatbiztonságot és sebességet ígérő Cloudflare DNS szerverét állítjuk be az nginx konténer számára, mely az 1.1.1.1 címen érhető el:
root@columbo.uni-eszterhazy.hu:~# docker run --name www.example.com \
--dns 1.1.1.1 nginx
Szolgáltatások elosztása¶
Képzeljük el, hogy egy fizikai szerveren olyan konténereket szeretnénk működtetni, amelyek különböző weblapokat működtetnek úgy, hogy minden egyes konténernek egy másik website tartalmát kell szolgáltatnia. Egy lehetséges megoldás az lehetne, hogy minden konténerhez egy-egy publikus IP cím rendelünk, de hát ez eléggé drága megoldás lenne (egy ezrest havonta már szinte minden ISP elkér egy címért). A gyakorlatban ezért a kérdést a port számokkal oldjuk meg.
A port szám a TCP csomagokban szereplő érték, amely a cél számítógép egy szolgáltatását azonosítja. A port számok és szolgáltatások ajánlott összerendelése Linuxon és Mac-en a /etc/services, Windows-on a C:windowssystem32driversetcservices fájl alapján történik. Ez azonban csak egy ajánlás, valójában bármely alkalmazás bármelyik port számmal jelölt adatcsomagokat fogadhatja, ha azt a rendszerben más program még nem használja (értelemszerűen két alkalmazás nem használhatja ugyanazt a port számot). A fogadásra kész portokat az operációs rendszerek és a konténerek is regisztrálják, és az adott port számú csomagokat az azt fogadó alkalmazásnak küldik. (Gyakori hasonlat egy lakótömb esete, ahol az IP címnek a blokkház címe, a lakás számának pedig a port szám felel meg, így azonos címre érkező leveleket a lakás számával lehet a címzetthez juttatni.) A kliens és szerver közti kommunikáció során tehát az adott szolgáltatáshoz tartozó port számra is szükség van: egy ssh kapcsolatot rendszerint a 22-esen, egy levél küldést gyakran az 587-es porton végeznek. A webszerverek alapvetően a 80-as porton kommunikálnak, de titkosított adatforgalom esetén, a https protokoll alkalmazásakor ez legtöbbször az alapértelmezett 443-as portra kerül át.
Az alábbi ábra három webhely működését mutatja be három különálló konténer alkalmazásával. Mindegyik konténerben egy webszerver működik, s mindannyian az alapértelmezett a 80-as porton várják a bejövő kéréseket. Egy ilyen felépítésben a szerver 193.225.33.196 című külső interfészére érkező, 80-as portra irányuló kérésről nem lehet eldönteni, hogy azt melyik konténer felé kellene irányítani. Ebben a megoldásban ezért újabb portokat kell létrehoznunk, és azokat egy táblázat alapján a megfelelő konténer felé kell irányítanunk.
Az első konténerhez az 5000-es port számot rendeljük, az ide érkező kéréseket a
172.17.0.2konténer 80-as portjára kell továbbítani.A második konténerhez az 5001-es port számra érkező kérések tartoznak, a kéréseket a
172.17.0.3konténer 80-as portjára küldjük.Az 5002-es port forgalmát pedig a
172.17.0.4ip című konténer 80-as portjára irányítjuk.
Hálózati forgalom elosztása a port számok alapján¶
A forgalom ilyen elosztását a gazda operációs rendszer és a Docker hálózatkezelésért felelős része együttesen végzi. A port számok összerendelését a konténer létrehozásakor a -p után megadott port:port párral kell megadni úgy, hogy elsőként a külsőt (ahol a külvilág éréseit fogadjuk), majd a belsőt (amelyen a konténer belsejében működő alkalmazás „hallgatózik”) írjuk le. A -p 5000:80 hatására tehát a külvilágból az 5000-es portra érkező kéréseket a konténer 80-as portra kell irányítani, ahol ennek forgalmát az abban működő webszerver kezeli.
Lássuk ezt egy gyakorlati példán! Hozzunk létre egy konténerben futó alap webszervert! Az egyik népszerű változat neve Apache, amelyet httpd-nek is neveznek. Készítsünk egy konténert, amely egy ilyen webszervert tartalmaz, és ellenőrizzük, hogy a kezdőoldala megjelenik-e a böngészőben! A konténernek a bejövő kéréseket az 5000-es porton kell fogadnia! A megoldáshoz a konténer indítását az eddig tanult módon végezzük, de ki kell egészítenünk azt a -p 5000:80 beállítással.
macbook:~$ docker run --name webszerver --rm -p 5000:80 httpd
Unable to find image 'httpd:latest' locally
latest: Pulling from library/httpd
...
172.17.0.1 - - [28/Jan/2024:20:58:58 +0000] "GET / HTTP/1.1" 200 45
A böngésző címsorában most nem elég a célállomás nevét vagy IP címét megadni, mert az alapértelmezett 80-as port helyett most mást kell használni. A port szám megadása a címet követően, egy kettősponttal elválasztva történik így: http://127.0.0.1:5000 vagy http://localhost:5001. (Azoknál a webcímeknél, amelybe nem írtunk port számot, a böngészők automatikusan a 80-ast fogják használni.) Fáradozásunk eredményeképp megjelenik az Apache alap weboldala.
Az Apache webszerver az 5000-es porton érhető el¶
Ha egy már létező konténer esetén utólag szeretnénk lekérdezni a port szám összerendeléseket, ismét a docker inspect parancsot kell használni. A használt port számok ennek kimenetében, a Ports szekcióban olvashatók le.
macbook:~$ docker inspect webszerver
...
"Ports": {
"80/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "5000"
}
]
},
...
A port számok használata nem reális egy olyan szerver esetén, amely konténerben futó, de publikusan elérhető website-okat szolgál ki, mert a felhasználók nem tudnák vagy akarnák memorizálni a címekben szereplő port számot. Ugyan mit gondolnánk arról, ha az egyetem informatika tanszékének weblapja a http://www.uni-eszterhazy.hu:4216 címen lenne elérhető, és a port szám megadása nélkül pedig egy másik website jelenne meg? Egy ISP esetében pedig még rosszabb lenne a helyzet, a port szám nélkül a látogató egy másik web tartalmát kapná meg, egy olyanét, amelynek a keresetthez semmi köze sem lenne.
A port számokon alapuló megoldás kiváló pl. REST API-k készítésére, háttérszolgáltatásokhoz vagy pl. „eldugott” website-ok működtetésére, de publikus webszolgáltatáshoz ezt a típusú szeparációt nem szokás alkalmazni. Egy több munkát igénylő és a problémát áthidaló megoldást a A HAProxy c. fejezetben ismerünk majd meg.
Összefoglalás¶
A Docker beépített hálózatkezelése elengedhetetlen a hálózati kommunikációt végző konténerek felépítéséhez. Ez nem csak a konténerek közötti kommunikációban és a szolgáltatások elosztásában nyújt hasznos megoldásokat, hanem biztosítja a konténerek internetes kommunikációját és az első elszeparált hálózat védelmét is. A Docker alapértelmezett megoldása egy bridge-re épülő hálózat, amely a konténerek belső hálózatát és a fizikai hálózatot kapcsolja össze a docker0 interfésszel, ami alapértelmezés szerint a 172.17.0.0/16 IP tartományt használja. A konténerek szeparációjára új belső hálózatok is létrehozhatók, melyek eltérő IP tartományban működnek. A Docker lehetőséget biztosít portok hozzárendelésére is, így a konténerek szolgáltatásai külső hálózatokból is elérhetők, de a webszolgáltatások számára a végső megoldást a HAProxy adja majd.
Kérdések, feladatok¶
Miért van szükség hálózati kapcsolatra egy konténerekben futó alkalmazás esetén?
Mi a Docker alapértelmezett belső hálózatának címe?
Mit jelent a bridge a hálózatkezelésben?
Milyen parancsok használhatók egy host gép fizikai interfészéhez tartozó hálózati paraméterek, és melyek a Docker bridge interfészének lekérdezésére?
Hogyan lehet lekérdezni egy futó konténer IP címét és alapértelmezett átjáróját?
Hogyan hozható létre egy új bridge Dockerben, és miért lehet erre szükség?
Hogyan rendelhetők portok a Docker konténerekhez, és miért van erre szükség?
Hogyan pingelhető meg egy konténerből a host gép és hogyan pingelhetők más konténerek a bridge hálózatán belül?
Hogyan állítható be egyéni DNS szerver egy konténer számára?
Milyen parancsokkal lehet listázni, lekérdezni és törölni a Docker hálózatait?
Megbízást kapsz egy egyszerű weboldal elkészítésére, mellyel szemben az egyetlen követelmény az, hogy a tulajdonosa az abban levő tartalmakat webes felületen, önállóan szerkeszthesse. Az oldal címe majd
www.company.hulesz. A választásod egy Grav nevű tartalomkezelőre esik, amely bár letölthető a https://getgrav.org oldalról, mégis konténerben szeretnéd azt elhelyezni. Helyezd üzembe a saját gépeden a Grav konténerét és jelenítsd meg a kezdőoldalt a http://localhost:5002 url-en! (Ha megtetszik, mielőtt nekiállnál vele kialakítani az oldalakat, előtte feltétlenül olvasd el a Kötetek kezelése c. fejezetet!)
A Grav kezdőoldala¶