V nastavení s více kontejnery komunikují služby běžící v kontejnerech mezi sebou prostřednictvím společné sítě. Ve stejném nastavení některé kontejnery také interagují s vnějším světem.
Tato interní a externí komunikace je řešena pomocí vystavených a publikovaných portů v Dockeru.
V tomto tutoriálu budu diskutovat o práci s porty v Dockeru. Přejdu k rozdílu mezi odhalením a publikováním portů, proč se používají a jak je používat.
Odhalení vs publikování portu v Dockeru
Existují dva způsoby, jak zacházet s porty v Dockeru:odhalením portů a publikováním portů.
Odhalení portu jednoduše znamená dát ostatním vědět, na kterém portu bude kontejnerová aplikace naslouchat nebo přijímat připojení. Slouží ke komunikaci s jinými kontejnery, nikoli s vnějším světem.
Publikování portu je spíše jako mapování portů kontejneru s porty hostitele. Tímto způsobem je kontejner schopen komunikovat s externími systémy, skutečným světem, internetem.
Tato grafika vám pomůže pochopit.

Vidíte, jak je port 80 kontejneru SERVER mapován na port 80 hostitelského systému? Tímto způsobem je kontejner schopen komunikovat s vnějším světem pomocí veřejné IP adresy hostitelského systému.
Na druhou stranu k odhaleným portům nelze přistupovat přímo z místa mimo svět kontejnerů.
Pro zapamatování:
- Vystavené porty se používají pro interní komunikaci kontejnerů v rámci světa kontejnerů.
- Publikované porty se používají pro komunikaci se systémy mimo svět kontejnerů.


Odhalení portů v Dockeru
Za prvé, odhalení portu není nezbytně nutné. Proč? Protože většina obrazů dockeru, které používáte ve svém nastavení, již má ve své konfiguraci odhalený výchozí port.
Kontejnerovaná frontendová aplikace může například komunikovat s databází MariaDB pouhým zadáním IP kontejneru a libovolného portu, na kterém MariaDB přijímá připojení (výchozí 3306).
Odhalení pomáhá v situaci, kdy se nepoužívají výchozí hodnoty, jako v tomto případě, kdyby MariaDB nepřijímala připojení na portu 3306, měl by být odhalen alternativní port.
Port můžete odhalit dvěma způsoby:
- Pomocí
EXPOSE
Instrukce pro dockerfile. - Pomocí
--expose
pomocí ukotvitelného rozhraní CLI neboexpose
klíč v docker-compose.
Je čas ponořit se hlouběji do obou.
Metoda 1:Odhalení portů prostřednictvím Dockerfile
Do svého Dockerfile můžete přidat jednoduchou instrukci, abyste dali ostatním vědět, na kterém portu bude vaše aplikace přijímat připojení.
O tomto návodu musíte vědět následující:-
EXPOSE
není přidat další vrstvy do výsledného obrazu ukotvitelného panelu. Pouze přidává metadata.EXPOSE
je způsob dokumentace port vaší aplikace. Jediný efekt, který to má, je z hlediska čitelnosti nebo porozumění aplikaci.
Jak funguje expozice, můžete vidět na jednoduchém obrázku kontejneru, který jsem vytvořil právě pro tento účel. Tento obrázek nic nedělá .
Vytáhněte obrázek.
docker pull debdutdeb/expose-demo:v1
Tento obrázek odhaluje celkem čtyři porty, vypište obrázek pomocí následujícího příkazu.
docker image ls --filter=reference=debdutdeb/expose-demo:v1
Podívejte se na SIZE
ve sloupci bude 0 bajtů.
➟ docker image ls --filter=reference=debdutdeb/expose-demo:v1
REPOSITORY TAG IMAGE ID CREATED SIZE
debdutdeb/expose-demo v1 ad3d8ffa9bfe N/A 0B
Důvod je jednoduchý, na tomto obrázku nejsou žádné vrstvy, všechny přidané pokyny k expozici byly metadata, žádné skutečné vrstvy.
Počet dostupných vrstev můžete také získat pomocí následujícího příkazu:-
docker image inspect -f '{{len .RootFS.Layers}}' debdutdeb/expose-demo:v1
Měli byste vidět výstup jako tento:-
➟ docker image inspect -f '{{len .RootFS.Layers}}' debdutdeb/expose-demo:v1
0
Jak jsem již řekl, exponované porty jsou přidány jako metadata obrázku, abychom věděli, které porty aplikace používá.
Jak vidíte tyto informace, aniž byste se podívali na Dockerfile? Musíte si obrázek prohlédnout. Chcete-li být konkrétnější, podívejte se na příkaz níže, podobný příkaz můžete použít na libovolném obrázku a zobrazit seznam vystavených portů.
Pro demonstraci používám svůj ukázkový obrázek.
docker image inspect -f \
'{{range $exposed, $_ := .Config.ExposedPorts}}{{printf "%s\n" $exposed}}{{end}}' \
debdutdeb/expose-demo:v1
Ukázkový výstup:
➟ docker image inspect -f '{{range $exposed, $_ := .Config.ExposedPorts}}{{printf "%s\n" $exposed}}{{end}}' debdutdeb/expose-demo:v1
443/tcp
80/tcp
8080/tcp
9090/tcp
Tento obrázek, který odhaluje několik portů, můžete také porovnat s debdutdeb/noexpose-demo:v1
obrázek, který neodhaluje žádné porty. Spusťte stejnou sadu příkazů také na tomto obrázku a všimněte si rozdílu.
Metoda 2:Zpřístupnění portů prostřednictvím rozhraní CLI nebo docker-compose
Někdy se vývojáři aplikací vyhýbají zahrnutí dalšího EXPOSE
instrukce v jejich Dockerfile.
V takovém případě, abyste se ujistili, že ostatní kontejnery (prostřednictvím docker API) mohou snadno detekovat používaný port, můžete po sestavení vystavit více portů jako součást procesu nasazení.
Buď zvolte imperativní metodu, tj. CLI, nebo deklarativní metodu, tj. vytváření souborů.
Metoda CLI
V této metodě při vytváření kontejneru vše, co musíte udělat, je použít --expose
možnost (kolikrát, kolikrát je potřeba) s číslem portu a volitelně protokol s /
. Zde je jeden příklad:-
docker container run \
--expose 80 \
--expose 90 \
--expose 70/udp \
-d --name port-expose busybox:latest sleep 1d
Používám busybox
obrázek, který ve výchozím nastavení neodhaluje žádné porty.
Metoda vytvoření souboru
Pokud používáte nový soubor, můžete přidat pole expose
v definici služby. Předchozí nasazení můžete převést na nový soubor takto:-
version: "3.7"
services:
PortExpose:
image: busybox
command: sleep 1d
container_name: port-expose
expose:
- 80
- 90
- 70/udp
Jakmile budete mít kontejner spuštěný, stejně jako předtím jej můžete zkontrolovat, abyste věděli, které porty jsou vystaveny. Příkaz vypadá podobně.
docker container inspect -f \
'{{range $exposed, $_ := .NetworkSettings.Ports}}
{{printf "%s\n" $exposed}}{{end}}' \
port-expose
Ukázkový výstup:-
➟ docker container inspect -f '{{range $exposed, $_ := .NetworkSettings.Ports}}{{printf "%s\n" $exposed}}{{end}}' port-expose
70/udp
80/tcp
90/tcp
Publikování portu v Dockeru
Publikování portů je synonymem pro předávání portů, kdy jsou požadavky z příchozího připojení na veřejném portu předávány na port kontejneru.
Podobně jsou odpovědi odeslané z kontejneru přes jeho port odesílány klientovi přesměrováním provozu na zadaný port v prostoru portů hostitele.
Existují dva způsoby publikování portu, jeden přes CLI a druhý pomocí nového souboru. Obě metody mají také jednu dlouhou syntaxi a jednu krátkou syntaxi.
Metoda 1:Publikování portů pomocí příkazu Docker
Tyto dvě syntaxe jsou následující:
-p [optional_host_ip]:[host_port]:[container_port]/[optional_protocol]
--publish target=[container_port],published=[optional_host_ip]:[host_port],protocol=[optional_protocol]
Pro volitelnou IP hostitele můžete použít jakoukoli IP adresu spojenou s kteroukoli síťovou kartou. Pokud je IP vynechána, docker spojí port se všemi dostupnými IP adresami.
Nejvíce využijete ten první. Druhý je čitelnější. Podívejme se na příklad pomocí kontejneru nginx. Spusťte následující příkaz:-
docker container run --rm --name nginx \
--publish target=80,published=127.0.0.1:8081,protocol=tcp \
-d nginx
Pomocí tohoto příkazu jednoduše připojím port 80 kontejneru k portu 8081 mého hostitele na localhost. Pokud nyní zamíříte na http://localhost:8081, uvidíte spuštěný nginx.
Předchozí příkaz lze snadno převést do kratšího tvaru takto
docker container run --rm --name nginx \
-p 80:127.0.0.1:8081/tcp -d nginx
Přestože je kratší, hůře se čte.
Můžete také použít -p
nebo --publish
vícekrát pro publikování více portů.
Metoda 2:Publikování portu prostřednictvím nového souboru
Chcete-li publikovat port pomocí nového souboru, budete potřebovat pole s názvem ports
v definici služby. Toto pole může být seznam řetězců, který vypadá podobně jako krátká syntaxe CLI, nebo můžete použít seznam objektů, který je podobný dlouhé syntaxi.
Pokud bych převedl předchozí nasazení nginx pomocí složeného souboru s polem řetězců pro sekci portů, vypadalo by to následovně:-
version: "3.7"
services:
Nginx:
image: nginx
container_name: nginx
ports:
- 80:127.0.0.1:8081/tcp
Dovolte mi také ukázat, jak používat syntaxi pole objektů.
version: "3.7"
services:
Nginx:
image: nginx
container_name: nginx
ports:
- target: 80
published: 127.0.0.1:8081
protocol: tcp
Chcete-li zobrazit seznam všech publikovaných portů, můžete si kontejner prohlédnout takto-
docker container inspect -f '{{range $container, $host := .NetworkSettings.Ports}}{{printf "%s -> %s\n" $container $host}}{{end}}' nginx
Po spuštění uvidíte následující výstup:-
➟ docker container inspect -f '{{range $container, $host := .NetworkSettings.Ports}}{{printf "%s -> %s\n" $container $host}}{{end}}' nginx
80/tcp -> [{127.0.0.1 8081}]
Existuje další, jednodušší způsob, jak vypsat seznam publikovaných portů, pomocí docker container port
příkaz.
docker container port nginx
Příklad výstupu:-
➟ docker container port nginx
80/tcp -> 127.0.0.1:8081
Kdy vystavit port a kdy jej publikovat?
To je férová otázka. Vystavování a publikování nemají být konkurenty. Každá slouží jinému účelu. Pokud jste vývojářem obrázku, odkryjete porty, aby si uživatel mohl být lépe jistý, kde se pokusit o připojení. Na druhou stranu, pokud používáte obrázek a potřebujete jej zpřístupnit vnějšímu světu, budete publikovat potřebné porty.
Doufám, že vám byl tento článek užitečný. Pokud máte nějaké dotazy, dejte mi vědět v komentářích níže.