SSD Nodes Learn 8GB RAM — $66/rok
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-01

Docker Compose: sieci, DNS i tryb hosta

Wyjaśniono sieć bridge projektu, DNS po nazwie usługi, tryb hosta, współdzielenie sieci między projektami oraz port publikowany mimo reguł UFW.

Co Compose tworzy przed uruchomieniem aplikacji

Sieć Docker Compose działa według jednej zasady: docker compose up tworzy prywatną sieć dla projektu, dołącza do niej każdą usługę i umożliwia usługom komunikowanie się za pomocą nazw usług. Nie trzeba w tym celu dodawać ani jednej linii networks:. Większość nieporozumień dotyczących sieci Compose wynika z braku wiedzy, że sieć domyślna jest tworzona automatycznie.

Oto mały plik. Zapisz go jako compose.yaml w katalogu o nazwie shop.

services:
  web:
    image: nginx:1.27
    ports:
      - "8080:80"
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example

Uruchom projekt i sprawdź, co utworzył Docker:

docker compose up -d
docker network ls

Lista zawiera teraz sieć o nazwie shop_default. Compose nadaje jej nazwę <project>_default, a nazwa projektu jest domyślnie zgodna z nazwą katalogu zapisaną małymi literami. Można ją zmienić za pomocą docker compose -p myproject up -d albo za pomocą elementu najwyższego poziomu name: myproject w pliku. Sterownik tej sieci to bridge, czyli wirtualny przełącznik działający wewnątrz hosta. Każdy kontener otrzymuje adres w prywatnej podsieci, a ruch wychodzący jest po drodze tłumaczony na adres hosta.

docker compose down ponownie usuwa tę sieć. Z tego powodu nieaktualny kontener ze starego projektu może utrzymywać sieć w stanie aktywnym: Docker zwraca błąd error while removing network: network shop_default has active endpoints, a rozwiązaniem jest zatrzymanie lub usunięcie kontenera, który jest nadal do niej podłączony.

Jeśli Compose jest dla Ciebie nowym narzędziem, najpierw warto przeczytać Układ pliku Compose i polecenia zarządzające cyklem życia, ponieważ dalsza część zakłada umiejętność uruchamiania i zatrzymywania projektu.

Początkujący pomijają obsługę DNS według nazwy usługi

W każdej sieci zdefiniowanej przez użytkownika Docker uruchamia wbudowany serwer DNS, który każdy kontener widzi pod adresem 127.0.0.11. Serwer ten rozwiązuje nazwy usług na aktualne adresy kontenerów. Dlatego web łączy się z bazą danych pod nazwą hosta db na porcie 5432, bez żadnej konfiguracji.

docker compose exec web getent hosts db

Polecenie wyświetla wiersz podobny do 172.18.0.2 db. Jeśli nie wyświetla nic, obie usługi nie znajdują się w tej samej sieci.

Błędem, który prawie każdy popełnia raz, jest użycie localhost w konfiguracji aplikacji. W kontenerze localhost oznacza ten kontener, a nie hosta ani inną usługę. Klienci Postgres zgłaszają to jasno:

could not connect to server: Connection refused
	Is the server running on host "localhost" (127.0.0.1) and accepting
	TCP connections on port 5432?

Łańcuch połączenia powinien mieć postać postgresql://postgres:example@db:5432/postgres. Część hosta stanowi nazwa usługi.

Dwa szczegóły pozwalają później zaoszczędzić czas. Nazwy są rozwiązywane na adresy aktualnie uruchomionych kontenerów, dlatego docker compose up -d --scale web=3 zwraca jedną nazwę z trzema adresami. Klient, który bezterminowo buforuje DNS, pozostanie przypisany do nieaktywnego kontenera. Ponadto starsza sieć bridge, używana przez zwykłe docker run bez --network, w ogóle nie zapewnia rozwiązywania nazw. Z tego powodu zalecenia dotyczące linków kontenerów z 2016 nie odpowiadają obserwowanemu działaniu.

Nie potrzebujesz ports: do łączenia dwóch usług

ports: publikuje port kontenera na hoście. Służy do obsługi ruchu przychodzącego spoza Docker. Nie ma związku z ruchem między usługami, który już działa w całym zakresie portów w sieci projektu.

Dlatego ports: - "5432:5432", które wiele osób dodaje do usługi bazy danych, jest bezużyteczne i powoduje realne zagrożenie: udostępnia Postgres na publicznym interfejsie serwera. Należy je usunąć. Jeśli potrzebny jest dostęp z laptopa na czas migracji, należy powiązać port z interfejsem loopback za pomocą "127.0.0.1:5432:5432" i uzyskać dostęp przez tunel SSH. Różnicę między gniazdem nasłuchującym, opublikowanym portem i regułą zapory opisano w działaniu portów i usług nasłuchujących w systemie Linux.

expose: służy w Compose wyłącznie do celów dokumentacyjnych. Nie otwiera żadnego portu, ponieważ między kontenerami w tej samej sieci nic nie zostało zamknięte.

Kiedy tryb network_mode host jest właściwy i jakie są jego koszty

Tryb host usuwa własną przestrzeń nazw sieci kontenera i pozwala procesowi korzystać bezpośrednio z interfejsów hosta.

services:
  probe:
    image: alpine:3.20
    network_mode: host
    command: sleep infinity

Istnieją uzasadnione powody, aby go używać. Proces, który musi odbierać ruch rozgłoszeniowy lub multicastowy w sieci lokalnej, na przykład w celu wykrywania urządzeń przez serwer multimediów lub centralę automatyki domowej, nie może odbierać go zza mostu, ponieważ most nie przekazuje tego ruchu do kontenera. Agent monitorujący, który odczytuje liczniki interfejsów hosta, potrzebuje interfejsów hosta. Pomijany jest również etap translacji adresów, co ma znaczenie przy dużej liczbie pakietów.

Koszty są konkretne.

ports: przestaje działać. Docker ostrzega, że mapowane porty są pomijane podczas korzystania z trybu sieci hosta, a kontener wiąże porty wskazane przez swój proces. Dwa kontenery w trybie host próbujące używać portu 8080 powodują konflikt, a drugi kończy działanie z błędem bind: address already in use.

Rozpoznawanie nazw usług przestaje działać w obu kierunkach. Kontener nie znajduje się w sieci projektu, więc nie może rozpoznać nazwy db, a pozostałe usługi nie mogą rozpoznać jego nazwy. Może połączyć się z nimi wyłącznie przez porty opublikowane na hoście, zazwyczaj pod adresem 127.0.0.1.

Izolacja przestaje obowiązywać. Proces, który wiąże adres 0.0.0.0 wewnątrz kontenera w trybie host, nasłuchuje na każdym interfejsie serwera, w tym na interfejsie publicznym, dokładnie tak jak pakiet zainstalowany za pomocą apt. Ma to również jedną zaletę: taki ruch przechodzi standardową ścieżką wejściową, dlatego dotyczą go reguły UFW. Nie dotyczy to portów opublikowanych.

Tryb host jest funkcją Docker Engine w systemie Linux. Docker Desktop obsługuje go dopiero od wersji 4.34 i tylko po jego włączeniu. Dodatkowo kontenery nie mogą wiązać adresów IP hosta, a obsługiwane są wyłącznie protokoły TCP i UDP. Jeżeli część zespołu korzysta z serwerów Linux, a część z Docker Desktop, należy oczekiwać różnego działania tego samego pliku.

Tryb host należy stosować, gdy wymagany jest dostęp do interfejsów hosta. Nie należy stosować go do rozwiązywania problemów z połączeniami, ponieważ zwykle zastępuje jeden problem trudniejszym.

Łączenie dwóch projektów Compose za pomocą zewnętrznej sieci

Sieć utworzona przez jeden projekt nie jest widoczna dla drugiego. Dlatego reverse proxy w proxy/compose.yaml nie może połączyć się z aplikacją w app/compose.yaml, nawet jeśli działają na tym samym serwerze. Rozwiązaniem jest sieć, której nie posiada żaden z projektów.

Należy utworzyć ją jednorazowo ręcznie:

docker network create edge

Następnie należy zadeklarować ją jako zewnętrzną w każdym projekcie. Po stronie proxy:

services:
  proxy:
    image: traefik:v3.1
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - edge

networks:
  edge:
    name: edge
    external: true

Po stronie aplikacji:

services:
  app:
    image: nginx:1.27
    networks:
      - edge
      - internal
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: example
    networks:
      - internal

networks:
  edge:
    name: edge
    external: true
  internal:

external: true informuje Compose, aby dołączył do istniejącej sieci zamiast ją tworzyć, a także pozostawił ją na miejscu podczas docker compose down. Oddzielny klucz name: ma większe znaczenie, niż może się wydawać: bez niego Compose szuka sieci o nazwie dokładnie edge, natomiast dzięki niemu można nadać sieci jedną nazwę w pliku, a inną na hoście.

Jeśli sieć nie istnieje, Compose odmawia uruchomienia i zgłasza, że sieć została zadeklarowana jako zewnętrzna, ale nie można jej znaleźć. Należy najpierw ją utworzyć.

Należy zwrócić uwagę na sposób użycia internal w pliku aplikacji. Baza danych znajduje się wyłącznie w sieci lokalnej tego projektu, więc proxy nie może się z nią połączyć; może to zrobić tylko app. Dodanie internal: true w sekcji sieci dodatkowo całkowicie usuwa trasę z tej sieci do świata zewnętrznego. Jest to dobre ustawienie domyślne dla bazy danych, ale przed jego zastosowaniem należy znać jeden skutek: kontener w sieci wewnętrznej nie może niczego pobierać, dlatego entrypoint wykonujący podczas uruchamiania apt-get update lub pip install zawiesi się, a następnie zakończy działanie po przekroczeniu limitu czasu.

Kompletny przykład konfiguracji z regułami routingu i certyfikatami opisano w sekcji uruchamianie wielu aplikacji za jednym wystąpieniem Traefik.

Opublikowane porty omijają UFW

Jest to część obsługi sieci przez Compose, która prowadzi do incydentu bezpieczeństwa. Publikowany jest port, sprawdzana jest aktywność UFW i blokowanie wszystkiego poza SSH, a usługa nadal jest dostępna z Internetu.

sudo ufw status
curl http://203.0.113.10:8080

UFW informuje, że port jest zablokowany. curl mimo to zwraca stronę. Nie oznacza to awarii. Docker bezpośrednio dodaje własne reguły translacji adresów i przekazywania do iptables. Ruch kierowany do opublikowanego portu kontenera jest przekazywany do kontenera, a nie dostarczany do hosta. W związku z tym nie przechodzi przez łańcuch zarządzany przez UFW dla ruchu przeznaczonego lokalnie. Reguły Dockera są również sprawdzane przed regułami UFW.

Najprostsza poprawka polega na publikowaniu portu tylko tam, gdzie jest to potrzebne:

    ports:
      - "127.0.0.1:8080:80"

Powoduje to powiązanie strony hosta z loopbackiem. Port jest wtedy dostępny z samego serwera i przez tunel SSH, ale z żadnego innego miejsca. Publiczny punkt wejścia należy umieścić za reverse proxy, które celowo publikuje porty 80 i 443. Pełne wyjaśnienie, w tym opis łańcucha DOCKER-USER używanego w sytuacjach, gdy opublikowany port musi być filtrowany, znajduje się w artykule dlaczego Docker publikuje porty bezpośrednio poza UFW i jak to naprawić.

Jak debugować to za pomocą czterech poleceń

Najpierw sprawdź, do której sieci faktycznie należy każdy kontener:

docker network inspect shop_default

Blok Containers zawiera listę wszystkich podłączonych kontenerów wraz z ich adresami. Usługi nieobecnej na tej liście należy szukać w innej sieci. Kontener może też działać w trybie hosta albo być zatrzymany.

Przetestuj rozwiązywanie nazw z tymczasowego kontenera podłączonego do tej samej sieci. Dzięki temu nie trzeba instalować żadnych narzędzi we własnych obrazach:

docker run --rm --network shop_default busybox nslookup db
docker run --rm --network shop_default busybox nc -zv db 5432

Niepowodzenie nslookup wskazuje na problem z rozwiązywaniem nazw lub członkostwem w sieci. Powodzenie nslookup przy niepowodzeniu nc oznacza, że usługa działa, ale nie nasłuchuje na tym porcie albo nasłuchuje na 127.0.0.1 we własnym kontenerze zamiast na 0.0.0.0. Drugi przypadek często występuje w serwerach programistycznych. Należy wtedy zmienić adres nasłuchiwania w aplikacji, a nie konfigurację Docker.

Istnieje jeszcze jeden problem, który może wyglądać jak błąd Docker. Jeśli kontenery komunikują się ze sobą, ale nie mogą połączyć się z komputerem w sieci firmowej lub VPN, podsieć Docker prawdopodobnie nakłada się na tę sieć. Docker domyślnie przydziela podsieci od 172.17.0.0/16 wzwyż. Zmień pulę w /etc/docker/daemon.json:

{
  "default-address-pools": [
    { "base": "10.200.0.0/16", "size": 24 }
  ]
}

Następnie uruchom sudo systemctl restart docker i utwórz ponownie sieci, których dotyczy problem. Istniejąca sieć zachowuje podsieć przypisaną podczas jej tworzenia.

FAQ

Dlaczego kontenery nie mogą komunikować się ze sobą za pomocą nazwy usługi?

Nie znajdują się w tej samej sieci. Compose automatycznie umieszcza każdą usługę w sieci <project>_default, ale po dodaniu do usługi listy networks: lista ta staje się kompletnym zestawem sieci tej usługi, a sieć domyślna nie jest już dodawana. Uruchom docker network inspect <network> i sprawdź, czy oba kontenery występują w sekcji Containers. Sprawdź również, czy żadna z usług nie używa network_mode: host, ponieważ kontener działający w trybie hosta nie jest podłączony do żadnej sieci Docker i nie może rozpoznawać nazw usług.

Czy należy publikować porty, aby jedna usługa mogła łączyć się z inną?

Nie. W sieci Compose każdy port każdego kontenera jest dostępny dla pozostałych kontenerów w tej sieci. ports: służy wyłącznie do udostępniania kontenera dla ruchu spoza Docker, a expose: pełni funkcję dokumentacyjną. Publikowanie portu bazy danych jest częstym i kosztownym nawykiem, ponieważ udostępnia bazę danych w publicznym interfejsie serwera.

Jaka jest różnica między siecią bridge a siecią host?

Sieć bridge zapewnia kontenerowi własną przestrzeń nazw sieci i adres na przełączniku wirtualnym, automatyczne rozpoznawanie nazw między kontenerami oraz translację wychodzącego ruchu. Sieć host udostępnia kontenerowi bezpośrednio stos sieciowy hosta: nie ma oddzielnego adresu, rozpoznawania nazwy usługi, publikowania portów ani izolacji od pozostałych procesów nasłuchujących na hoście. Bridge jest ustawieniem domyślnym i właściwym wyborem, chyba że proces wymaga dostępu do interfejsów hosta.

Jak połączyć kontenery z dwóch różnych plików Compose?

Utwórz współdzieloną sieć za pomocą docker network create edge, a następnie zadeklaruj ją w obu plikach za pomocą external: true i podłącz do niej usługi, które muszą się komunikować. Compose nie utworzy ani nie usunie tej sieci. Po pominięciu etapu tworzenia Compose odmawia uruchomienia i zgłasza, że zadeklarowana sieć zewnętrzna nie została znaleziona.

Dlaczego kontener jest dostępny z Internetu, gdy UFW blokuje port?

Ponieważ opublikowany port jest obsługiwany przez reguły przekazywania dodawane przez Docker do iptables. Reguły te są dopasowywane przed regułami UFW, a przekazywany ruch i tak nie przechodzi przez łańcuch filtrowany przez UFW. Powiąż stronę hosta z interfejsem loopback za pomocą "127.0.0.1:8080:80" i umieść wszystkie publicznie dostępne usługi za reverse proxy na portach 80 i 443.