SSD Nodes Learn Hosting plans →
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-10-20

Docker Compose networking: jak działają sieci i mosty

Zrozum działanie sieci w Docker Compose. Wyjaśniamy mechanizm DNS dla nazw usług, konfigurację bridge, tryb host oraz ryzyko pomijania reguł UFW przy publikacji portów.

Co Compose buduje przed uruchomieniem aplikacji

Sieciowanie w Docker Compose opiera się na jednej zasadzie: docker compose up tworzy prywatną sieć dla projektu, podłącza do niej każdą usługę i umożliwia tym usługom komunikację za pomocą nazw usług. Nie trzeba pisać ani jednej linii networks:, aby to uzyskać. Większość nieporozumień dotyczących sieciowania w Compose wynika z nieświadomości, że to ustawienie domyślne jest już aktywne.

Oto niewielki 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 go i sprawdź, co utworzył Docker:

docker compose up -d
docker network ls

Lista zawiera teraz sieć o nazwie shop_default. Compose nazywa ją <project>_default, a nazwa projektu domyślnie przyjmuje nazwę katalogu zapisaną małymi literami. Można ją nadpisać za pomocą docker compose -p myproject up -d lub poprzez najwyższego poziomu name: myproject w pliku. Jej sterownikiem jest bridge, który działa jako wirtualny przełącznik wewnątrz hosta. Każdy kontener otrzymuje adres w prywatnej podsieci, a ruch wychodzący jest tłumaczony na adres hosta.

docker compose down usuwa tę sieć. To powód, dla którego nieaktywny kontener ze starego projektu może utrzymywać sieć otwartą: Docker odmawia operacji z błędem error while removing network: network shop_default has active endpoints, a rozwiązaniem jest zatrzymanie lub usunięcie kontenera, który jest wciąż do niej podłączony.

Jeśli Compose jest dla Ciebie nowością, warto najpierw zapoznać się z układem pliku Compose i poleceniami cyklu życia, ponieważ dalsza część tekstu zakłada umiejętność uruchamiania i zatrzymywania projektu.

DNS po nazwie usługi to element, który początkujący często pomijają

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. Rozwiązuje on nazwy usług na aktualne adresy kontenerów. Dzięki temu web łączy się z bazą danych pod nazwą hosta db, na porcie 5432, bez żadnej konfiguracji.

docker compose exec web getent hosts db

To polecenie wyświetla linię w formacie 172.18.0.2 db. Jeśli wynik jest pusty, oznacza to, że obie usługi nie znajdują się w tej samej sieci.

Błędem, który popełnia niemal każdy, jest użycie localhost w konfiguracji aplikacji. Wewnątrz kontenera localhost odnosi się do tego konkretnego kontenera, a nie do hosta czy drugiej usługi. Klienci Postgres zgłaszają to w czytelny sposób:

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?

Ciąg połączenia powinien mieć postać postgresql://postgres:example@db:5432/postgres. Częścią określającą hosta jest nazwa usługi.

Dwa szczegóły, które oszczędzają czas w przyszłości. Nazwy są rozwiązywane na adresy aktualnie działających instancji, więc docker compose up -d --scale web=3 zwraca jedną nazwę z trzema adresami, a klient, który buforuje DNS na stałe, przypisze się do nieaktywnego kontenera. Z kolei starsza sieć bridge, używana przez zwykłe docker run bez --network, w ogóle nie rozwiązuje nazw kontenerów: kontenery w tej sieci odnajdują się tylko po adresie IP, a zewnętrzne nazwy hostów sprawdzają poprzez kopię pliku /etc/resolv.conf z hosta. Dlatego porady dotyczące linkowania kontenerów z 2016 roku nie przystają do obecnych rozwiązań.

Nie potrzeba ports:, aby połączyć dwie usługi

ports: publikuje port kontenera na hoście. Służy to do obsługi ruchu przychodzącego spoza Dockera. Nie ma to związku z komunikacją między usługami, która działa w pełnym zakresie portów wewnątrz sieci projektu.

Zatem ports: - "5432:5432", które wiele osób dodaje do swojej usługi bazy danych, nie przynosi korzyści, a wręcz szkodzi: wystawia Postgres na publicznym interfejsie serwera. Należy to usunąć. Jeśli usługa ma być dostępna z laptopa na potrzeby migracji, należy powiązać ją z interfejsem zwrotnym za pomocą "127.0.0.1:5432:5432" i połączyć się przez tunel SSH. Różnice między gniazdem nasłuchującym, opublikowanym portem a regułą zapory sieciowej zostały opisane w jak działają porty i usługi nasłuchujące w systemie Linux.

expose: w Compose służy wyłącznie celom dokumentacyjnym. Nie otwiera żadnych portów, ponieważ komunikacja między kontenerami w tej samej sieci nie jest blokowana.

Kiedy tryb host jest właściwy i jakie niesie koszty

Tryb host usuwa własną przestrzeń nazw sieciowych 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, by z tego korzystać. Proces, który musi widzieć ruch broadcast lub multicast w sieci lokalnej, na przykład wykrywanie urządzeń dla serwera multimediów lub hubu automatyki domowej, nie zobaczy go zza mostka, ponieważ mostek nie przekazuje takiego ruchu do kontenera. Agent monitorujący, który odczytuje liczniki interfejsów hosta, potrzebuje dostępu do tych interfejsów. Pomija się również przeskok translacji adresów, co ma znaczenie przy dużej liczbie pakietów.

Koszty są konkretne.

ports: przestaje działać. Docker ostrzega, że opublikowane porty są ignorowane w trybie sieci host, a kontener wiąże się z tym, z czym wiąże się jego proces. Dwa kontenery w trybie host próbujące użyć portu 8080 wejdą w konflikt, a drugi z nich zakończy działanie z błędem bind: address already in use.

Rozwiązywanie nazw według nazwy usługi przestaje działać w obie strony. Kontener nie znajduje się w sieci projektu, więc nie może rozwiązać db, a inne usługi nie mogą rozwiązać jego nazwy. Nadal może się z nimi łączyć przez porty opublikowane na hoście, zazwyczaj pod adresem 127.0.0.1. W systemie Linux może również łączyć się bezpośrednio z kontenerem typu bridge pod jego adresem IP na dowolnym nasłuchującym porcie, ponieważ host ma otwarty dostęp do kontenerów w swoich sieciach bridge, a kontener w trybie host współdzieli stos sieciowy hosta. Te adresy zmieniają się po odtworzeniu kontenerów, więc port opublikowany na loopback jest stabilną ścieżką.

Izolacja znika. Proces, który wiąże się z 0.0.0.0 wewnątrz kontenera w trybie host, nasłuchuje na każdym interfejsie serwera, w tym na publicznym, dokładnie tak, jak pakiet zainstalowany przez apt. Ma to jedną zaletę: ten ruch podąża standardową ścieżką wejściową, więc reguły UFW mają do niego zastosowanie, co nie dotyczy portów opublikowanych.

Tryb host jest funkcją Docker Engine dla systemu Linux. Docker Desktop wspiera go dopiero od wersji 4.34 i tylko po jego włączeniu, z dodatkowymi ograniczeniami: kontenery nie mogą wiązać adresów IP hosta, a obsługiwane są tylko protokoły TCP i UDP. Jeśli połowa zespołu pracuje na serwerach Linux, a połowa na Docker Desktop, należy oczekiwać, że ten sam plik będzie zachowywał się inaczej.

Sięgaj po tryb host, gdy potrzebujesz interfejsów hosta. Nie używaj go do naprawy problemów z połączeniem, ponieważ zazwyczaj zastępuje jeden problem innym, trudniejszym.

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

Sieć utworzona przez jeden projekt nie jest widoczna dla innego. Dlatego reverse proxy w proxy/compose.yaml nie widzi aplikacji w app/compose.yaml, nawet jeśli znajdują się na tym samym serwerze. Rozwiązaniem jest sieć, która nie należy do żadnego z projektów.

Należy ją utworzyć ręcznie jeden raz:

docker network create edge

Następnie należy zadeklarować ją jako zewnętrzną w każdym projekcie. Strona 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

Strona 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 podłączył się do istniejącej sieci zamiast tworzyć nową i pozostawił ją w spokoju podczas docker compose down. Osobny klucz name: jest ważniejszy, niż się wydaje: bez niego Compose szuka sieci o nazwie dokładnie edge, a dzięki niemu można nadać sieci jedną nazwę w pliku, a inną w systemie hosta.

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

Zwróć uwagę, co plik aplikacji robi z internal. Baza danych znajduje się tylko w tej lokalnej sieci projektu, więc proxy nie może uzyskać do niej dostępu, a jedynie app. Dodanie internal: true w sekcji sieci idzie o krok dalej i całkowicie usuwa trasę do świata zewnętrznego. Jest to dobre ustawienie domyślne dla bazy danych, przy czym warto znać jeden koszt przed jego zastosowaniem: kontener w sieci wewnętrznej nie może niczego pobrać, więc punkt wejścia (entrypoint), który uruchamia apt-get update lub pip install podczas startu, zawiesi się, a następnie zakończy błędem przekroczenia czasu oczekiwania (timeout).

Pełną, gotową konfigurację z regułami routingu i certyfikatami można znaleźć w uruchamianie kilku aplikacji za jedną instancją Traefik.

Opublikowane porty omijają UFW

To element sieci w Docker Compose, który często prowadzi do incydentów bezpieczeństwa. Publikujesz port, sprawdzasz, czy UFW jest aktywny i blokuje wszystko poza SSH, a usługa nadal jest dostępna z Internetu.

sudo ufw status
curl http://203.0.113.10:8080

UFW twierdzi, że port jest zablokowany. Mimo to curl zwraca stronę. Nic nie jest uszkodzone. Docker zapisuje własne reguły translacji adresów i przekierowań bezpośrednio w iptables. Ruch skierowany na opublikowany port kontenera jest przekazywany do kontenera, zamiast trafiać do hosta, więc nigdy nie przechodzi przez łańcuch zarządzany przez UFW dla ruchu lokalnego. Reguły Dockera są również sprawdzane przed regułami UFW.

Krótkie rozwiązanie polega na publikowaniu portów tylko tam, gdzie jest to konieczne:

    ports:
      - "127.0.0.1:8080:80"

To wiąże stronę hosta z interfejsem loopback, dzięki czemu port jest dostępny z poziomu samego serwera oraz przez tunel SSH, ale nie z żadnego innego miejsca. Umieść publiczny punkt wejścia za reverse proxy, które celowo publikuje porty 80 i 443. Pełne wyjaśnienie, w tym łańcuch DOCKER-USER dla przypadków, w których konieczne jest filtrowanie opublikowanego portu, znajduje się w dlaczego Docker publikuje porty z pominięciem UFW i jak to naprawić.

Jak debugować sieć w czterech poleceniach

Zacznij od sprawdzenia, w jakiej sieci faktycznie znajduje się każdy kontener:

docker network inspect shop_default

Blok Containers wymienia każdy podłączony kontener wraz z jego adresem. Usługa niewidoczna na tej liście znajduje się w innej sieci, w trybie host lub nie jest uruchomiona.

Przetestuj rozwiązywanie nazw z tymczasowego kontenera podłączonego do tej samej sieci, aby nie potrzebować żadnych narzędzi wewnątrz własnych obrazów:

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 przynależnością do sieci. Jeśli nslookup kończy się sukcesem, a nc nie, oznacza to, że nazwa i sieć są poprawne, a problem leży w samym połączeniu. Jeśli nc zgłasza Connection refused, najczęstszą przyczyną jest to, że usługa nie nasłuchuje na tym porcie lub nasłuchuje na 127.0.0.1 wewnątrz własnego kontenera zamiast na 0.0.0.0. To ostatnie jest częste w serwerach deweloperskich, a rozwiązanie leży w adresie wiązania aplikacji, a nie w Dockerze. Jeśli nc zawiesza się i przekracza limit czasu, szukaj czegoś, co odrzuca ruch po drodze, na przykład reguły firewalla lub kontenera, który jest wstrzymany lub nie odpowiada.

Jeszcze jedna awaria, która wygląda jak błąd Dockera. Jeśli kontenery mogą komunikować się ze sobą, ale nie mogą połączyć się z maszyną w sieci biurowej lub VPN, podsieć Dockera prawdopodobnie nakłada się na tę sieć. Domyślnie Docker przydziela adresy od 172.17.0.0/16 w górę. Zmień pulę w /etc/docker/daemon.json:

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

Następnie wykonaj sudo systemctl restart docker i odtwórz dotknięte sieci, ponieważ istniejąca sieć zachowuje podsieć, z którą została utworzona.

FAQ

Dlaczego moje kontenery nie mogą komunikować się ze sobą za pomocą nazw usług?

Nie znajdują się w tej samej sieci. Compose automatycznie umieszcza każdą usługę w <project>_default, ale w momencie dodania listy networks: do usługi, lista ta staje się kompletnym zestawem sieci dla danego kontenera i domyślne ustawienie przestaje obowiązywać. Uruchom docker network inspect <network> i sprawdź, czy oba kontenery pojawiają się w bloku Containers. Sprawdź również, czy żadna z usług nie korzysta z network_mode: host, ponieważ kontener w trybie host nie znajduje się w żadnej sieci Docker i nie może rozwiązywać nazw usług.

Czy muszę publikować porty, aby jedna usługa mogła połączyć się z drugą?

Nie. W sieci Compose każdy port każdego kontenera jest dostępny dla pozostałych kontenerów w tej samej sieci. ports: służy wyłącznie do wystawiania kontenera na ruch spoza środowiska Docker, a expose: pełni funkcję dokumentacyjną. Publikowanie portu bazy danych to powszechny i ryzykowny nawyk, ponieważ umieszcza bazę danych na publicznym interfejsie serwera.

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

Tryb bridge nadaje kontenerowi własną przestrzeń nazw sieciową i adres na wirtualnym przełączniku, zapewniając automatyczną rozdzielczość nazw między kontenerami oraz translację ruchu wychodzącego. Tryb host udostępnia kontenerowi bezpośrednio stos sieciowy hosta: brak osobnego adresu, brak rozdzielczości po nazwie usługi, brak publikowania portów i brak izolacji od innych usług 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 przypisz do niej usługi, które mają się komunikować. Compose nie będzie jej tworzyć ani usuwać. Jeśli pominiesz krok tworzenia, Compose odmówi startu i zgłosi błąd, że sieć jest zadeklarowana jako zewnętrzna, ale nie została znaleziona.

Dlaczego mój kontener jest dostępny z Internetu, mimo że UFW blokuje port?

Ponieważ opublikowany port jest obsługiwany przez reguły przekierowania, które Docker dodaje do iptables. Reguły te są sprawdzane przed regułami UFW, a przekierowany ruch i tak nie przechodzi przez łańcuchy filtrowane przez UFW. Powiąż stronę hosta z interfejsem loopback za pomocą "127.0.0.1:8080:80" i umieść wszystkie publiczne usługi za reverse proxy na portach 80 oraz 443.

Czy ten artykuł był przydatny?