Модуль 8 / 18
Мережі Docker: bridge, DNS, публікація портів та IPv6
Зміст
1. Цілі та модель мережевого шляху
Після модуля ви зможете пояснити шлях пакета між network namespace, veth, bridge, firewall/NAT та host interface; використовувати вбудований DNS; безпечно публікувати порти й діагностувати IPv4/IPv6 за рівнями.
- network driver визначає модель зв’язності
- user-defined bridge створює ізольований L2/L3 segment одного Engine
- вбудований DNS розпізнає container names та aliases
- EXPOSE документує порт, publish створює host mapping
- bind address визначає поверхню доступу
- IPv6 address не скасовує firewall і routing requirements
2. Теорія: namespace, veth та bridge
Контейнер отримує власний network namespace з interfaces, routes та resolver configuration. Для bridge driver один кінець veth перебуває в namespace контейнера, інший підключений до Linux bridge хоста. Docker керує IPAM, forwarding, isolation та masquerading.
Default bridge є спільною legacy-мережею й не надає сучасного автоматичного name resolution між контейнерами. User-defined bridge дає scoped DNS, налаштовуваний subnet/MTU/options і можливість connect/disconnect без перестворення контейнера.
3. Теорія: вбудований DNS та service discovery
У user-defined network контейнери звертаються до embedded DNS Docker, зазвичай через 127.0.0.11. Він розпізнає container name та network-scoped aliases в адресу endpoint цієї мережі, а зовнішні запити пересилає налаштованим upstream resolvers.
IP контейнера — runtime detail. Клієнт має використовувати стабільне ім’я/alias та container port. Один container у кількох мережах може мати різні адреси й aliases. DNS resolution не доводить доступність listener: після DNS окремо перевіряють route, TCP та application protocol.
4. Теорія: EXPOSE, publish та bind address
EXPOSE записує metadata image і нічого не відкриває. --publish HOST_IP:HOST_PORT:CONTAINER_PORT створює mapping через firewall/NAT або routing mode. Якщо HOST_IP пропущено, порт зазвичай прив’язується до всіх адрес хоста й може стати доступним ззовні.
Для локального administrative endpoint використовуйте 127.0.0.1 або ::1 та перевіряйте реальний listener/firewall. Publish не потрібний для спілкування контейнерів однієї мережі: вони звертаються до container port напряму. -P публікує всі exposed ports на випадкові host ports і рідко підходить production.
5. Теорія: forwarding, NAT та host firewall
На Linux Docker створює firewall rules для bridge isolation, masquerading та published ports. Engine 29 підтримує iptables backend і експериментальний nftables backend. Не можна вручну змінювати Docker-owned chains: вони є внутрішньою реалізацією та перестворюються.
Вимкнення iptables/ip6tables у daemon майже завжди ламає network behavior. UFW може бути обійдений DNAT до його звичайних INPUT/OUTPUT rules, тому доступ контролюють з урахуванням Docker forwarding path. Для iptables backend призначений DOCKER-USER; у nftables backend цієї chain немає й потрібні окремі base chains із коректним priority.
6. Теорія: IPv6 та dual stack
User-defined bridge можна створити з --ipv6. Сучасний Engine може автоматично виділити ULA prefix, якщо IPv6 pool не заданий. Для production зазвичай планують власний ULA/GUA subnet, routing, neighbor discovery, DNS AAAA та firewall симетрично IPv4.
IPv6 не використовує NAT як обов’язкову модель. Gateway modes nat, routed, nat-unprotected та isolated змінюють direct routing і filtering. Routed mode потребує маршруту ззовні до container subnet; публікація й bind semantics відрізняються, тому обраний mode документують і тестують окремо.
7. Методичні вказівки
- Створюйте окрему мережу для кожної application boundary
- Використовуйте DNS alias, а не зафіксований container IP
- Публікуйте лише потрібні ports та явний host address
- Не застосовуйте host network як обхід DNS або firewall
- Перевіряйте listener усередині контейнера до host NAT
- Не змінюйте Docker-owned firewall rules вручну
- Тестуйте IPv4 та IPv6 незалежно
- Збирайте network inspect до видалення failed container
8. Лабораторна робота: DNS, publish, isolation та IPv6
Зафіксуйте host/network baseline, потім створіть user-defined bridge, web endpoint і client.
docker info --format '{{json .Plugins.Network}}' | jq
docker network ls
docker network inspect bridge
sysctl net.ipv4.ip_forward net.ipv6.conf.all.forwarding
ip -brief link
ip routedocker network create --driver bridge docker-m8-app
docker run --detach --name docker-m8-web --network docker-m8-app --network-alias api nginx:1.29-alpine
docker run --detach --name docker-m8-client --network docker-m8-app alpine:3.21 sleep 600
docker exec docker-m8-client cat /etc/resolv.conf
docker exec docker-m8-client nslookup docker-m8-web
docker exec docker-m8-client nslookup api
docker exec docker-m8-client wget -qO- http://api/
docker network inspect docker-m8-appДоведіть, що EXPOSE образу не публікує порт, потім створіть окремий mapping лише на loopback.
docker port docker-m8-web
curl --fail --max-time 2 http://127.0.0.1:18088/ || true
docker run --detach --name docker-m8-published --network docker-m8-app --publish 127.0.0.1:18088:80 nginx:1.29-alpine
docker port docker-m8-published
curl --fail --silent --show-error http://127.0.0.1:18088/
docker inspect docker-m8-published --format '{{json .NetworkSettings.Ports}}' | jqПеревірте isolation двох bridge networks та динамічне підключення endpoint.
docker network create docker-m8-backend
docker run --detach --name docker-m8-isolated --network docker-m8-backend alpine:3.21 sleep 600
docker exec docker-m8-isolated nslookup docker-m8-web || true
docker network connect docker-m8-app docker-m8-isolated
docker exec docker-m8-isolated wget -qO- http://docker-m8-web/
docker network disconnect docker-m8-app docker-m8-isolated
docker inspect docker-m8-isolated --format '{{json .NetworkSettings.Networks}}' | jqСтворіть тимчасову IPv6-enabled мережу з автоматично призначеним ULA та перевірте ICMPv6 за DNS-ім’ям.
docker network create --ipv6 docker-m8-v6
docker run --detach --name docker-m8-v6-server --network docker-m8-v6 alpine:3.21 sleep 600
docker run --rm --network docker-m8-v6 alpine:3.21 ping -6 -c 3 docker-m8-v6-server
docker network inspect docker-m8-v6 --format '{{json .IPAM.Config}}' | jq
docker inspect docker-m8-v6-server --format '{{json .NetworkSettings.Networks}}' | jqКритерій: aliases розпізнаються; unpublished endpoint недоступний через host port; loopback mapping відповідає; окрема мережа ізольована; IPv6 endpoint отримує адресу й відповідає.
9. Діагностика: DNS працює, з’єднання немає
Ідіть шляхом packet послідовно: обраний context → endpoint membership → DNS → route → listener → firewall/NAT → protocol. Заміна network mode до локалізації причини знищує evidence.
docker container ls --all --filter name=docker-m8
docker network ls
docker network inspect docker-m8-app
docker exec docker-m8-client ip address
docker exec docker-m8-client ip route
docker exec docker-m8-client nslookup api
docker exec docker-m8-client wget -S -O- http://api/ 2>&1 | head -40
ss -lntp | grep 18088 || true
docker logs docker-m8-published- inspect Networks — чи підключений endpoint і який alias
- resolv.conf/nslookup — чи працює DNS
- ip route — чи існує gateway/path
- listener має слухати потрібний address і container port
- docker port/inspect Ports — чи існує host mapping
- ss — чи слухається host port
- logs — чи дійшов request до застосунку
- MTU та asymmetric routing перевіряють після базової зв’язності
10. Контроль, cleanup і шпаргалка
- Чому user-defined bridge кращий за default bridge?
- Що розпізнає 127.0.0.11?
- Чим EXPOSE відрізняється від --publish?
- Чому контейнери однієї мережі використовують container port?
- Як omission HOST_IP змінює exposure?
- Чому UFW може не обмежити published port очікувано?
- Що потрібне routed IPv6 network?
- У якому порядку діагностувати connection refused?
docker container rm --force docker-m8-web docker-m8-client docker-m8-published docker-m8-isolated docker-m8-v6-server 2>/dev/null || true
docker network rm docker-m8-app docker-m8-backend docker-m8-v6
docker network lsМодуль засвоєно, якщо ви проєктуєте network boundaries, не залежите від container IP і доводите кожну ділянку шляху DNS → route → listener → firewall → application.