SSD Nodes Learn Hosting plans →
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-07

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

Wyjaśnienie działania sieci w Docker Compose. Omówienie domyślnego mostu, komunikacji przez nazwy usług, trybu host oraz współdzielenia sieci między projektami z pominięciem UFW.

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 według nazwy usługi to element, który początkujący często pomijają

W każdej zdefiniowanej przez użytkownika sieci Docker uruchamia wbudowany serwer DNS, który każdy kontener widzi pod adresem 127.0.0.11. Rozwiązuje on nazwy usług na bieżące adresy kontenerów. Dzięki temu web łączy się z bazą danych pod nazwą hosta db, na porcie 5432, bez żadnej dodatkowej konfiguracji.

docker compose exec web getent hosts db

Polecenie to 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 niemal każdy popełnia przynajmniej raz, jest użycie localhost w konfiguracji aplikacji. Wewnątrz kontenera localhost odnosi się do tego konkretnego kontenera, a nie do hosta czy innej 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łączeniowy powinien mieć postać postgresql://postgres:example@db:5432/postgres. Część odpowiadająca za hosta to nazwa usługi.

Dwa szczegóły, które pozwalają zaoszczędzić 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. Ponadto starsza sieć bridge, używana przez zwykłe polecenie docker run bez parametru --network, nie obsługuje rozwiązywania nazw. To wyjaśnia, dlaczego porady dotyczące łączenia kontenerów z 2016 roku nie przystają do obecnych rozwiązań.

Nie ma potrzeby używania ports: do łączenia dwóch usług

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

Dlatego ports: - "5432:5432", dodawane przez wielu użytkowników do usługi bazy danych, jest bezużyteczne i szkodliwe: wystawia Postgres na publicznym interfejsie serwera. Należy je usunąć. Jeśli usługa ma być dostępna z poziomu laptopa na potrzeby migracji, należy powiązać ją z interfejsem loopback za pomocą "127.0.0.1:5432:5432" i uzyskać do niej dostęp przez tunel SSH. Różnice między gniazdem nasłuchującym, opublikowanym portem a regułą zapory sieciowej opisano 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 domyślnie blokowana.

Kiedy network_mode host jest właściwym wyborem i jakie niesie koszty

Tryb host usuwa własną przestrzeń nazw sieciowych kontenera i pozwala procesowi na bezpośrednie korzystanie z interfejsów hosta.

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

Istnieją uzasadnione powody, by stosować to rozwiązanie. Proces wymagający dostępu do ruchu 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, musi mieć dostęp do tych interfejsów. Ponadto pomijany jest etap translacji adresów, co ma znaczenie przy dużej liczbie pakietów.

Koszty są konkretne.

ports: przestaje działać. Docker ostrzega, że publikowane porty są ignorowane w trybie sieci hosta, 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 wchodzą w konflikt, a drugi z nich kończy działanie z błędem bind: address already in use.

Rozpoznawanie nazw po nazwie 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. Komunikacja z nimi odbywa się wyłącznie przez porty opublikowane na hoście, zazwyczaj pod adresem 127.0.0.1.

Izolacja przestaje istnieć. Proces, który wiąże 0.0.0.0 wewnątrz kontenera w trybie host, nasłuchuje na każdym interfejsie serwera, w tym na publicznym, dokładnie tak samo jak pakiet zainstalowany przez apt. Istnieje jedna zaleta: ruch ten podąża standardową ścieżką wejściową, więc reguły UFW mają do niego zastosowanie, co nie jest prawdą w przypadku publikowanych portów.

Tryb host jest funkcją Linux Docker Engine. Docker Desktop wspiera go dopiero od wersji 4.34 i tylko po jego włączeniu, z dodatkowymi ograniczeniami: kontenery nie mogą wiązać się z adresami IP hosta, a obsługiwane są tylko protokoły TCP i UDP. Jeśli połowa zespołu pracuje na serwerach z systemem Linux, a połowa na Docker Desktop, należy spodziewać się, ż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 on 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ć problem za pomocą czterech poleceń

Należy zacząć od sprawdzenia, w jakiej sieci faktycznie znajduje się dany kontener:

docker network inspect shop_default

Blok Containers zawiera listę wszystkich podłączonych kontenerów wraz z ich adresami. Usługa, której brakuje na tej liście, znajduje się w innej sieci, działa w trybie host lub nie jest uruchomiona.

Należy przetestować rozpoznawanie nazw z poziomu tymczasowego kontenera podłączonego do tej samej sieci. Pozwala to uniknąć konieczności instalowania 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 rozpoznawaniem nazw lub przynależnością do sieci. Jeśli nslookup kończy się powodzeniem, a nc kończy się niepowodzeniem, oznacza to, że usługa działa, ale nie nasłuchuje na danym porcie lub nasłuchuje na 127.0.0.1 wewnątrz własnego kontenera, zamiast na 0.0.0.0. Ten ostatni przypadek jest częsty w serwerach deweloperskich, a rozwiązanie leży w adresie powiązania (bind address) aplikacji, a nie w Docker.

Kolejny błąd, który wygląda na usterkę Docker, występuje, gdy kontenery mogą komunikować się ze sobą, ale nie mogą połączyć się z maszyną w sieci biurowej lub VPN. Prawdopodobnie podsieć Docker pokrywa się z tą siecią. Domyślnie Docker przydziela adresy od 172.17.0.0/16 w górę. Należy zmienić pulę w /etc/docker/daemon.json:

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

Następnie należy wykonać sudo systemctl restart docker i odtworzyć 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.