Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як працює bridge networking в Docker?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Docker bridge networking** використовує Linux software bridge (дефолтний `docker0`), щоб з'єднати container на тому самому host. Кожен container отримує virtual ethernet pair: один кінець всередині container, інший на bridge. iptables NAT-правила форвардять опубліковані порти між bridge і host. ```bash docker network create mynet # створює bridge "br-<id>" docker run --network mynet --name web nginx # web має IP на bridge ip a show br-<id> # bridge-інтерфейс на host brctl show # які veth на якому bridge ``` **Головне:** container на тому самому bridge говорять вільно; з/на host або зовні треба `-p` (NAT). User-defined bridge додають DNS-by-name; дефолтний `docker0` ні.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Docker bridge networking** це дефолтний режим для спілкування container на одному host. Під капотом це Linux software bridge плюс пара віртуальних інтерфейсів на container, усе зв'язано iptables NAT-правилами. Знання цього стеку дає змогу дебажити «чому container не говорять?» за 30 секунд замість 30 хвилин. ## Теорія ### TL;DR - Docker створює Linux software bridge на host (дефолтне ім'я `docker0`; user-defined bridge отримують ім'я `br-<id>`). - Для кожного container Docker створює `veth`-пару: один кінець всередині network namespace container як `eth0`, інший приєднано до bridge. - Усі container на тому самому bridge на приватній підмережі (наприклад, `172.17.0.0/16`) і дістають один одного напряму на будь-якому порту. - Host дістає container через їхній bridge IP (але не по імені container з host). - iptables MASQUERADE-правила NAT'ять вихідний container-трафік на IP host. iptables DNAT-правила реалізують `-p`. - **User-defined bridge** додають embedded DNS по імені container. Дефолтний `docker0` цього не має. ### Швидкий приклад ```bash $ docker network create mynet $ docker run -d --name web --network mynet nginx # На host бачимо bridge $ ip link show | grep -E 'br-|docker0' 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ... 7: br-1234abcd: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ... # Бачимо який veth приєднано $ brctl show br-1234abcd # або: bridge link bridge name bridge id STP enabled interfaces br-1234abcd 8000.0242a3f9d2b8 no vethabcd123 # Всередині container $ docker exec web ip a show eth0 5: eth0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 inet 172.18.0.2/24 brd 172.18.0.255 scope global eth0 ``` Bridge `br-1234abcd` на host, кінець `vethabcd123`, відповідний `eth0@if6` всередині container, три в'юхи на той самий провід. ### Архітектура veth-пари ``` Host network namespace Container network namespace +--------------------+ +----------------------+ | | | | | docker0 / br-X |═══vethXXX═══eth0│ процес | | (Linux bridge) | | container | | | | | +--------------------+ +----------------------+ | v iptables MASQUERADE для вихідного v iptables DNAT для -p<host>:<container> +--------------------+ | host eth0 / wlan0 | ↔ зовнішня мережа +--------------------+ ``` `veth` це по суті віртуальний кабель. Один кінець живе у host (приєднано до bridge); інший у namespace container (перейменовано на `eth0`). Пакети, що входять у один кінець, виходять з іншого. ### Як тече трафік між container ``` Container A (172.18.0.2) Container B (172.18.0.3) | | | eth0 | eth0 | vethA | vethB +----------------+ +------------+ v v br-1234abcd (bridge) ``` A шле пакет на IP B. Bridge-логіка kernel форвардить з `vethA` на `vethB`. Без NAT, без host'а на L3 крім routing. На user-defined bridge A може ще дістати B по імені (`db`, `api`), Docker додає `127.0.0.11` (свій embedded DNS) у `/etc/resolv.conf` container, і цей DNS знає про усі container-імена на тому самому bridge. ### Як працює вихідний трафік Container хоче дістатися `https://api.github.com`. Пакет: 1. Виходить з `eth0` container (172.18.0.2 → 140.82.x.x). 2. Прибуває на bridge. 3. Маршрутизується на вихідний інтерфейс host. 4. iptables-правило `MASQUERADE` переписує source IP на IP host. 5. Виходить в інтернет. Зворотний трафік використовує conntrack, щоб знайти шлях назад до container. ### Як працює `-p` (port publishing) З `docker run -p 8080:80 nginx`: 1. iptables DNAT-правило: `host:8080 → 172.18.0.2:80`. 2. Зовнішній запит б'є у host:8080. 3. iptables переписує destination на container. 4. Bridge форвардить container'у. 5. nginx відповідає. Зворотний шлях. `docker-proxy` userspace-процес теж стартує як fallback для деяких IPv6 / loopback кейсів. Більшість трафіку йде iptables-шляхом. ### Дефолтний bridge vs user-defined | | Дефолтний `bridge` | User-defined bridge | |---|---|---| | Створюється | Docker при інсталі | `docker network create <name>` | | Ім'я | `bridge` | будь-яке за вибором | | Linux-інтерфейс | `docker0` | `br-<random-id>` | | DNS по імені container | **Ні** (тільки legacy `--link`) | **Так** (embedded resolver) | | Ізоляція між project | Жодної (кожен container тут за замовчуванням) | Кожна мережа ізольована | | Auto-cleanup | Постійний | `docker network rm`, коли не used | | Рекомендовано для | майже нічого нового | усього | Якщо container не задає `--network`, він приземляється на дефолтний `bridge`. Завжди задавай user-defined bridge для будь-якого нетривіального use. ### Типові помилки **Спроба дістати container по імені з host** ```bash $ curl http://web # host намагається резолвити container-ім'я curl: (6) Could not resolve host: web ``` Embedded DNS обслуговує лише container на тому самому bridge, не host. З host бери опублікований порт (`localhost:8080`) або bridge-IP container (`172.18.0.2`). **Два container на різних bridge, очікуючи, що говоритимуть** ```bash $ docker network create net-a && docker network create net-b $ docker run -d --name api --network net-a myapp $ docker run -d --name db --network net-b postgres:16 $ docker exec api ping db # падає ``` Bridge ізольовані. Або постав обидва на ту саму мережу, або `docker network connect net-b api`, щоб приєднати `api` до обох. **Забути, що container на `docker0` не мають DNS** Свіжа інсталяція + `docker run -d --name x ...` приземляє `x` на `docker0`. Зсередини `ping x` не працює. Сучасна порада: завжди створюй мережу спочатку. **iptables flush ламає Docker networking** Docker керує своїми iptables-правилами у chain `DOCKER`. Запуск `iptables -F` або деяких firewall-менеджерів (UFW з дефолтними правилами) може стерти правила Docker і зламати port publishing. Рестартуй Docker (`systemctl restart docker`), щоб їх перестворити. ### Inspecting і debugging ```bash # Які bridge керує Docker? $ docker network ls --filter driver=bridge # Що на конкретній мережі? $ docker network inspect mynet # IP container $ docker inspect web --format '{{.NetworkSettings.Networks.mynet.IPAddress}}' # Live трафік на bridge $ sudo tcpdump -i br-1234abcd -n # iptables-правила, що Docker налаштував $ sudo iptables -t nat -L DOCKER -n -v ``` ### Реальне застосування - **Compose:** авто-створює user-defined bridge на project (`<projectname>_default`). Service-to-service трафік відбувається тут, з DNS по service-імені. - **Single-host production:** явний `docker network create appnet`, усі container приєднані, лише entry point опубліковано через `-p`. - **Кілька ізольованих стеків на одному host:** один bridge на стек. Postgres на `appnet1` випадково не дістати з container на `appnet2`. - **Reverse-proxy патерни:** Traefik або nginx-proxy приєднується до кількох bridge, щоб маршрутизувати трафік між стеками. ### Питання для поглиблення **Q:** Чому мій container має IP 172.17.x.x, коли очікував 172.18.x.x? **A:** Він на дефолтному `bridge` (172.17.0.0/16), не на user-defined. Задай `--network <yourname>` при запуску. **Q:** Як знайти IP container з host? **A:** `docker inspect <name> --format '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}'`. Container може бути на кількох мережах; формат вище друкує усі IP. **Q:** Чому трафік з мого container повільний? **A:** Bridge networking має overhead vs `host`-режим (NAT, додатковий хоп через bridge, можливо docker-proxy). Для сирого throughput `--network host` найшвидше. Для типових web/API навантажень overhead незначний. **Q:** Чи можна кастомізувати bridge-підмережу? **A:** Так, при створенні: `docker network create --subnet 10.0.0.0/24 --gateway 10.0.0.1 mynet`. Корисно, коли дефолтний `172.17/16` колізиться з VPN або офісними підмережами. **Q:** (Senior) Як дебажити пакет, що прибув на host, але ніколи не дістає container? **A:** Трасуй iptables-шлях. `sudo iptables -t nat -L DOCKER -n -v --line-numbers`, щоб побачити DNAT-правила для опублікованого порту. Потім `sudo iptables -L FORWARD -n -v` для підтвердження, що forward-chain приймає container-bound трафік. Запусти `sudo tcpdump -i any -n port 80`, щоб побачити, де пакет зупиняється. Поширені винуватці: host-firewall (UFW, firewalld) додає deny-правила вище Docker, або проблема `docker-proxy` на IPv6. ## Приклади ### Трасування `-p` mapping end-to-end ```bash $ docker run -d --name web -p 8080:80 nginx # 1. iptables-правило, що робить DNAT $ sudo iptables -t nat -L DOCKER -n | grep 8080 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80 # 2. IP container $ docker inspect web --format '{{.NetworkSettings.IPAddress}}' 172.17.0.2 # 3. З host дістаємо напряму через bridge-IP (без -p для цього) $ curl http://172.17.0.2/ # 4. Або через опублікований mapping $ curl http://localhost:8080/ ``` Один пакет, два валідних шляхи. DNAT-правило це міст між `localhost:8080` і `172.17.0.2:80`. ### Двоконтейнерний застосунок на user-defined bridge ```bash $ docker network create appnet $ docker run -d --name db --network appnet \ -e POSTGRES_PASSWORD=devpass postgres:16 $ docker run -d --name api --network appnet \ -e DATABASE_URL=postgres://postgres:devpass@db:5432/app \ myapp $ docker exec api nslookup db Server: 127.0.0.11 Name: db Address 1: 172.18.0.2 db.appnet ``` `db` резолвиться по імені тільки тому, що обидва container на user-defined `appnet`. На дефолтному bridge той самий setup провалиться.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.