Деякі відмови не починаються з краху. Вони починаються з «Ми лише змінили мережу». І раптом ваші контейнери не можуть достукатися до бази даних, моніторинг сліпне, а команда безпеки виявляє, що додаток слухає на кожному інтерфейсі, ніби повернувся 2009 рік.
Docker дає три спокусливі важелі для поведінки на локальному L2/L3 — bridge, host і macvlan. Усі три можуть працювати. Усі три можуть зруйнувати вам вихідні, якщо обрати неправильно. Давайте оберемо той, що добре старіє в продакшні.
Рішення спочатку: що обрати за 60 секунд
Якщо ви керуєте продакшн-системами, за замовчуванням оберігайте нудне. Нудні мережі тривожать менше.
Оберіть bridge коли
- Потрібне публікування портів (
-p) і розумна ізоляція. - Хост має залишатися хостом, а не «суп з контейнерів з root зверху».
- На хості кілька сервісів і ви хочете, щоб вони співіснували без конфліктів портів.
- Потрібен шлях до більш складних архітектур пізніше (кілька мереж, внутрішні мережі, контролі політик).
Оберіть host коли
- Потрібна дуже низька затримка та накладні витрати і ви готові прийняти ризик поширення (фільтри пакетів, порти та простори імен спільні).
- На хості працює одна велика мережева служба, яка вже прив’язує потрібні порти.
- Є чітка стратегія файрволінгу й спостереження на хості.
Оберіть macvlan коли
- Потрібно, щоб контейнери виглядали як повноцінні L2-учасники з власною MAC/IP у вашій фізичній LAN.
- Інтеграція з системами, що очікують унікальні IP для кожного робочого навантаження (legacy ліцензування, ACL, upstream маршрутизатори, multicast, незграбний моніторинг, «апарати безпеки»).
- Ви готові керувати реаліями L2: ARP, CAM-таблицями, політикою портів комутаторів і дисципліною IPAM.
Мій упереджений дефолт: починайте з user-defined bridge мереж. Вдавайтеся до host тільки коли можете обґрунтувати ризики письмово. Використовуйте macvlan лише коли потрібно інтегруватися з LAN як із реальним хостом і ви перевірили, що комутатор вас за це не покарає.
Ментальна модель, яка запобігає дурним інцидентам
Docker-мережі — це не магія. Це простір імен мережі Linux плюс трохи клею: віртуальні Ethernet пари (veth), пристрій мосту, правила маршрутизації і правила NAT/iptables/nftables. «Драйвер», який ви обираєте, здебільшого вирішує куди пакети прямують і хто володіє портами.
Три питання, які вирішують усе
- Де «живуть» порти? Ви відображаєте порти контейнера на порти хоста (bridge), чи контейнер ділиться простором портів хоста (host), чи контейнер отримує власну IP-адресу (macvlan)?
- Хто виконує політику L3/L4? Файрвол хоста + Docker-правила, чи upstream ACL, чи обидва?
- Яка ваша домен відмов? Чи хочете ви, щоб «один контейнер збожеволів» означало «збожеволів хост»?
Ще: продуктивність рідко перша проблема. Відлагоджуваність та передбачуваність важливіші. Драйвер мережі, що швидший на 3% але на 30% складніший для розслідування, — це не оптимізація. Це майбутній інцидент із запрошенням у календарі.
Одна цитата, яка справдилася у більшій кількості постмортемів, ніж мені хотілося б рахувати: парафраз: «Надія — не стратегія.»
— Джин Кранц (парафраз)
Bridge мережі: значення за замовчуванням не випадкове
Bridge-режим — це «хочу, щоб контейнери мали свій маленький світ, але були доступні» від Docker. Контейнер отримує IP у приватному підмережі. Docker створює Linux-мост (наприклад docker0 або user-defined), потім підключає eth0 контейнера до нього через veth-пару. Вихідний трафік маршрутизується/маскується на інтерфейс хоста. Вхідний трафік зазвичай йде через опубліковані порти.
Чому bridge підходить для продакшну
- Публікація портів явна. Ви відкриваєте лише те, що маєте на увазі. Це важливо, коли образ контейнера змінюється і раптом прив’язує додаткові порти.
- Імена мають значення. User-defined bridge мережі дають вбудоване DNS для знаходження сервісів. Контейнери можуть звертатися за іменем без того, щоб ви клеїли IP у конфіги.
- Ізоляція реальна — відносно. Це не повна межа VM, але це значна лінія утримання від випадкових конфліктів портів і деяких класів неправильних конфігурацій.
- Налагодження передбачуване. Ви можете мислити потоками: container → veth → bridge → host → uplink; і правила NAT видимі.
Коли bridge підводить
- Невідповідність MTU. Оверлеї, VPN або jumbo-кадри можуть зламати PMTUD і пакети зникатимуть.
- Сюрпризи NAT. Зміна вихідної IP може ламати upstream ACL або плутати логи.
- Поведінка hairpin. Трафік контейнер → хост → контейнер через опублікований порт може відрізнятися від прямого container → container.
- Дрейф файрволу. Docker маніпулює iptables/nftables. Якщо ваша базова політика файрволу вважає, що вона повинна повністю контролювати правила, виникне turf-war.
Bridge-мережі — як надійний седан. Не гламурно, але заводиться взимку, запчастини дешеві, і всі знають, як його полагодити.
Host мережі: швидко, гостро та небезпечно за замовчуванням
--network host скасовує претензії: контейнер ділиться простором мережі хоста. Ніякого NAT. Немає IP контейнера. Немає публікації портів. Якщо процес прив’язується до 0.0.0.0:443, він прив’язує порт 443 хоста. Це і є суть.
Для чого host mode справді підходить
- Високошвидкісні навантаження, де наклад conntrack і NAT відчутний і болючий.
- Мережеві апарати (маршрутизатори, BGP-спікери, DHCP-сервери), де потрібна пряма робота з інтерфейсами.
- Прості хости з одним орендарем, де одне робоче навантаження володіє машиною.
Що host mode тихо ламає
- Конфлікти портів стають «випадковими» збоями. Розгорніть два сервіси, що хочуть 8125/udp, і ви дізнаєтесь про це під час виконання.
- Межі безпеки стираються. Контейнер може бачити інтерфейси хоста, іноді локальні сервіси хоста, і ваше припущення «це всередині Docker» помирає.
- Спостережуваність ускладнюється. Інструменти, що очікують IP контейнера, втрачають орієнтир; атрибуція трафіку може вимагати інструментів, що знають про cgroup.
Короткий жарт №1: Host мережа — як дати контейнеру ключі від квартири, бо він пообіцяв лише помити машину.
Управління host mode, яке робить його терпимим
- Використовуйте systemd або оркестратор, щоб запобігти двом контейнерам, що прив’язують один порт.
- Наполягайте на явній політиці файрволу хоста; не покладайтеся на «гарні» дефолти Docker.
- Документуйте власність портів на хості як контракт. Бо це ним і є.
- Віддавайте перевагу host mode тільки для робочих навантажень, які його виправдовують: захоплення пакетів, агенти метрик, edge-проксі або реальні мережеві демони.
Macvlan: «виглядає як реальний хост» (і його пастки)
Macvlan призначає кожному контейнеру власну MAC-адресу та IP у вашому фізичному сегменті мережі. Для решти LAN контейнер — рівноправний вузол. Ніякої публікації портів, ніякого NAT. Просто «ще одна машина». Це привабливо, бо усуває категорію незручностей: upstream системи можуть говорити з контейнерами напряму без хитромудрих маніпуляцій з портами.
Коли macvlan — правильна відповідь
- Legacy ACL та IP-бейзовані allowlist-и, коли ви не можете або не хочете переписувати політику навколо NAT.
- Контейнери-«пристрої», яким потрібна власна IP-ідентичність для маршрутизації, моніторингу або сегментації мережі.
- Програмне забезпечення, що залежить від multicast/broadcast, де семантика NAT/bridge болісна (з застереженнями; не все стає простішим).
Великий підводний камінь macvlan: трафік від хоста до контейнера
За замовчуванням з macvlan хост не може звертатися до своїх macvlan-«дiтей» на тому ж фізичному інтерфейсі. Це дивує людей щоразу. Пакети не «hairpin’яться» так, як ви очікуєте.
Зазвичай виправлення — створити macvlan суб-інтерфейс на хості (shim-інтерфейс) у тій же macvlan-мережі й маршрутизувати через нього. Це не складно, але ще один компонент, який треба зберегти через перезавантаження та керування конфігурацією.
Де macvlan підведе пізніше
- Політика безпеки портів комутаторів і обмеження CAM-таблиці. Деякі комутатори не люблять, коли один фізичний порт починає емiтувати десятки MAC-адрес. Ви дізнаєтесь про це о 2:00 ночі під час інциденту, якщо не спитаєте заздалегідь.
- ARP-шторм та хвильова зміна таблиць сусідів. Контейнери з’являються і зникають; ARP-кеші не завжди встигають за цим.
- IPAM стає вашою проблемою. Docker IPAM може виділяти з діапазону, але він не буде домовлятися з вашим DHCP-сервером або таблицею мережевого відділу, якщо ви цього не організуєте.
- Трудніше тестувати «на одній машині». Не можна просто запускати все на одному ноутбуці і очікувати, що LAN поведе себе однаково, особливо коли до справи входить Wi‑Fi.
Короткий жарт №2: Macvlan чудовий — поки ваш комутатор не побачить тридцять нових MAC-адрес і не вирішить попрактикувати уважність, відкидаючи трафік.
Цікавинки та трохи історії (корисне, не для вікторини)
- Linux network namespaces (основа мереж контейнерів) з’явилися в ядрі наприкінці 2000-х, спочатку щоб ізолювати стек мережі для процесів без повної віртуалізації.
- Ранні мережеві рішення Docker сильно покладалися на
iptablesNAT-правила; роками «Docker зламав мій файрвол» був обрядом посвяти, бо він автоматично вставляв свої ланцюжки. - User-defined bridge мережі додали вбудований DNS для сервіс-дискавері, що стало великим кроком уперед порівняно з застарілим механізмом
--link, який запікав крихкі записи хостів у контейнери. - Macvlan як функція ядра передувала масовому використанню Docker; це Linux-драйвер, який дозволяє кільком віртуальним MAC-адресам ділитися одним фізичним інтерфейсом, історично використовувався для сегментації мереж і лабораторій.
- Conntrack (відстеження з’єднань) — прихований податок у середовищах з великою кількістю NAT; високі швидкості з’єднань можуть виснажити таблиці conntrack і виглядати як випадкова втрата пакетів.
- Проблеми з MTU погіршилися з поширенням оверлеїв/ VPN; «працює на одному хості, ламається між сайтами» часто пов’язане з Path MTU та поведінкою фрагментації.
- Host networking фактично відмовляєтесь від однієї з практичних ізоляцій контейнерів: розділення простору портів. Ось чому багато оркестраторів ставлять це серед привілейованих виборів.
- Bridge vs macvlan часто — це дебати про те, де живе ідентичність: у хості (bridge/NAT) або в LAN (macvlan). Жодне з рішень не є безкоштовним.
Практичні завдання: команди, виводи та рішення
Це ті завдання, які я справді виконую, коли хтось каже: «Мережа поводиться дивно». Кожне має команду, приклад виводу, що це значить і яке рішення можна прийняти далі.
Завдання 1: Перелік Docker-мереж і помітні речі
cr0x@server:~$ docker network ls
NETWORK ID NAME DRIVER SCOPE
a1b2c3d4e5f6 bridge bridge local
f6e5d4c3b2a1 host host local
112233445566 none null local
77aa88bb99cc app-net bridge local
Що це значить: У вас є дефолтний bridge, плюс user-defined bridge (app-net). Це добре: user-defined bridge мережі дають кращу DNS-поведінку та розділення.
Рішення: Якщо ваші сервіси все ще використовують дефолтний bridge і застарілі патерни, перемістіть їх у user-defined мережу, якщо немає причини інакше.
Завдання 2: Інспект мережі і перевірка підмережі, шлюзу та опцій
cr0x@server:~$ docker network inspect app-net
[
{
"Name": "app-net",
"Driver": "bridge",
"IPAM": {
"Config": [
{
"Subnet": "172.22.0.0/16",
"Gateway": "172.22.0.1"
}
]
},
"Options": {
"com.docker.network.bridge.name": "br-77aa88bb99cc"
}
}
]
Що це значить: Цей bridge використовує виділений Linux-міст та визначену підмережу. Передбачувано.
Рішення: Якщо ця підмережа перекривається з вашим корпоративним VPN або діапазонами дата-центру, змініть її зараз. Перекриття викликає «працює тільки з деяких ноутбуків» інциденти.
Завдання 3: Перевірте, якою мережею фактично користується контейнер
cr0x@server:~$ docker inspect -f '{{json .NetworkSettings.Networks}}' api-1
{"app-net":{"IPAddress":"172.22.0.10","Gateway":"172.22.0.1","MacAddress":"02:42:ac:16:00:0a"}}
Що це значить: Контейнер в мережі app-net з внутрішньою IP.
Рішення: Якщо додаток очікує бути доступним з LAN без пробросу портів, bridge недостатньо; розгляньте macvlan або коректне ingress/proxy рішення.
Завдання 4: Підтвердьте опубліковані порти і де вони прив’язані
cr0x@server:~$ docker ps --format 'table {{.Names}}\t{{.Ports}}'
NAMES PORTS
api-1 0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp
db-1 5432/tcp
Що це значить: api-1 експонований на порту хоста 8080. db-1 не опублікований; він тільки внутрішній (добре).
Рішення: Якщо бачите прив’язки 0.0.0.0, яких не очікували, закрийте їх (-p 127.0.0.1:... або файрвол), поки хтось інший цього не знайшов.
Завдання 5: Перевірте NAT-правила Docker (iptables) і чи вони існують
cr0x@server:~$ sudo iptables -t nat -S | sed -n '1,40p'
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N DOCKER
-N DOCKER_OUTPUT
-N DOCKER_POSTROUTING
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER_OUTPUT
-A POSTROUTING -s 172.22.0.0/16 ! -o br-77aa88bb99cc -j MASQUERADE
Що це значить: Docker керує NAT для bridge-підмережі. Правило MASQUERADE — ваш шлях для вихідного трафіку.
Рішення: Якщо у вас система тільки на nftables, перевірте сумісність Docker з вашим стеком файрволу. Змішане використання інструментів викликає плутанину «правила є, але не застосовуються».
Завдання 6: Перевірте навантаження conntrack (болі NAT видно тут)
cr0x@server:~$ sudo conntrack -S
cpu=0 found=120384 invalid=42 ignore=0 insert=120410 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=0
Що це значить: Invalid пакети низькі; падінь нема. Conntrack поки не горить.
Рішення: Якщо бачите зростання drops/insert_failed під навантаженням, підвищення лімітів conntrack або зменшення churn у NAT/з’єднаннях стає терміновим. Host mode іноді «вирішує» це, уникаючи NAT, але це компроміс.
Завдання 7: Перевірте маршрут і MTU зсередини контейнера
cr0x@server:~$ docker exec api-1 ip route
default via 172.22.0.1 dev eth0
172.22.0.0/16 dev eth0 proto kernel scope link src 172.22.0.10
cr0x@server:~$ docker exec api-1 ip link show eth0
2: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:ac:16:00:0a brd ff:ff:ff:ff:ff:ff link-netnsid 0
Що це значить: Дефолтний маршрут — шлюз мосту; MTU — 1500.
Рішення: Якщо підстеля має MTU 1450 через VPN/оверлей, встановіть MTU мережі Docker або MTU хоста, щоб контейнер не відправляв пакети, які будуть поглинені.
Завдання 8: Подивіться veth на боці хоста і членство в мосту
cr0x@server:~$ ip link show br-77aa88bb99cc
7: br-77aa88bb99cc: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:11:22:33:44 brd ff:ff:ff:ff:ff:ff
cr0x@server:~$ bridge link | grep br-77aa88bb99cc | head
21: veth6d2b1a2@if20: <BROADCAST,MULTICAST,UP,LOWER_UP> master br-77aa88bb99cc state forwarding priority 32 cost 2
Що це значить: Пара veth контейнера підключена до мосту і знаходиться в стані пересилання.
Рішення: Якщо інтерфейсу нема або він не пересилає, ймовірно проблема з ядром/мостом або контейнер у поганому стані простору імен мережі. Перезапуск Docker може «полагодити», але спершу зафіксуйте докази.
Завдання 9: Тест DNS всередині user-defined bridge мережі
cr0x@server:~$ docker exec api-1 getent hosts db-1
172.22.0.11 db-1
Що це значить: Вбудований DNS Docker працює; резольв контейнер→контейнер нормальний.
Рішення: Якщо тут DNS падає, не звинувачуйте корпоративний DNS спочатку. Перевірте, що контейнери в одній user-defined мережі; дефолтний bridge поводиться інакше.
Завдання 10: Підтвердьте поведінку host networking перевіряючи IP контейнера (його не буде)
cr0x@server:~$ docker run --rm --network host alpine ip addr show eth0 | sed -n '1,12p'
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 0c:de:ad:be:ef:01 brd ff:ff:ff:ff:ff:ff
inet 10.20.30.40/24 brd 10.20.30.255 scope global eth0
valid_lft forever preferred_lft forever
Що це значить: Ви бачите адресацію інтерфейсу хоста зсередини контейнера. Це і є «host network».
Рішення: Якщо вам потрібні окремі правила файрволу для контейнера або різні IP-ідентичності, host mode — не той інструмент.
Завдання 11: Створіть macvlan-мережу з контрольованим діапазоном IP
cr0x@server:~$ docker network create -d macvlan \
--subnet=10.50.10.0/24 --gateway=10.50.10.1 \
--ip-range=10.50.10.128/25 \
-o parent=eno1 macvlan-net
8f9e0d1c2b3a4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e
Що це значить: Контейнери можуть отримувати адреси в 10.50.10.128/25, тоді як решта підмережі залишається зарезервованою.
Рішення: Якщо ви не можете зарезервувати чистий діапазон і задокументувати його, не використовуйте macvlan. Конфлікти IP — повільні катастрофи.
Завдання 12: Запустіть контейнер на macvlan і підтвердіть, що він має LAN IP
cr0x@server:~$ docker run -d --name web-mv --network macvlan-net --ip 10.50.10.140 nginx:alpine
c2b1a0f9e8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b1
cr0x@server:~$ docker exec web-mv ip addr show eth0 | sed -n '1,10p'
2: eth0@if33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:0a:32:0a:8c brd ff:ff:ff:ff:ff:ff
inet 10.50.10.140/24 brd 10.50.10.255 scope global eth0
Що це значить: Контейнер тепер — повноцінна кінцева точка LAN.
Рішення: Перевірте політику комутатора (port security, обмеження MAC). Якщо пакети падають після кількох контейнерів, це не «Docker нестабільний». Це ваше L2, що застосовує обмеження.
Завдання 13: Підтвердіть класичне обмеження macvlan: хост не може дістатися контейнера (за замовчуванням)
cr0x@server:~$ ping -c 2 10.50.10.140
PING 10.50.10.140 (10.50.10.140) 56(84) bytes of data.
--- 10.50.10.140 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1017ms
Що це значить: Трафік з хоста до macvlan-дитини не працює. Це очікувана поведінка в багатьох налаштуваннях.
Рішення: Якщо сервіси на хості (агенти резервного копіювання, локальні монітори, сайдкари) повинні спілкуватися з цими контейнерами, створіть macvlan shim на хості.
Завдання 14: Додайте macvlan shim-інтерфейс на хості, щоб дістатися до macvlan-контейнерів
cr0x@server:~$ sudo ip link add macvlan-shim link eno1 type macvlan mode bridge
cr0x@server:~$ sudo ip addr add 10.50.10.2/24 dev macvlan-shim
cr0x@server:~$ sudo ip link set macvlan-shim up
cr0x@server:~$ ping -c 2 10.50.10.140
PING 10.50.10.140 (10.50.10.140) 56(84) bytes of data.
64 bytes from 10.50.10.140: icmp_seq=1 ttl=64 time=0.412 ms
64 bytes from 10.50.10.140: icmp_seq=2 ttl=64 time=0.398 ms
--- 10.50.10.140 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
Що це значить: Хост тепер може дістатися до macvlan-мережі через shim.
Рішення: Зробіть це персистентним (systemd-networkd/NetworkManager), інакше воно зникне після перезавантаження і ви знову натрапите на це обмеження в інциденті.
Завдання 15: Перевірте ARP/таблицю сусідів на хості
cr0x@server:~$ ip neigh show dev eno1 | head
10.50.10.1 lladdr 00:11:22:33:44:55 REACHABLE
10.50.10.140 lladdr 02:42:0a:32:0a:8c REACHABLE
10.50.10.141 lladdr 02:42:0a:32:0a:8d STALE
Що це значить: Хост вчиться сусідів. Якщо бачите багато FAILED або постійний churn, macvlan може створювати навантаження на L2/L3.
Рішення: Якщо churn сусідів корелює з втратою пакетів, зменшіть churn контейнерів, налаштуйте пороги GC або перерахуйте macvlan для цього середовища.
Завдання 16: Підтвердьте, який процес володіє портом (host mode і «таємні слухачі»)
cr0x@server:~$ sudo ss -lntp | grep ':8080'
LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("nginx",pid=21457,fd=6))
Що це значить: Щось (тут, nginx) володіє портом 8080 у стеку мереж хоста.
Рішення: Якщо ви очікували публікацію порту Docker, але бачите прямого слухача, можливо ви в host network mode або сервіс запущено на хості випадково. Виправте модель розгортання перед тим, як шукати фантомні проблеми з файрволом.
Завдання 17: Швидко простежте шлях пакета за допомогою tcpdump (bridge vs host vs macvlan)
cr0x@server:~$ sudo tcpdump -ni br-77aa88bb99cc port 5432 -c 5
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on br-77aa88bb99cc, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:01:10.112233 IP 172.22.0.10.49822 > 172.22.0.11.5432: Flags [S], seq 123456789, win 64240, options [mss 1460,sackOK,TS val 1 ecr 0,nop,wscale 7], length 0
Що це значить: Ви бачите трафік контейнер→контейнер на мосту. Це підтверджує, що проблема не «пакети ніколи не покидали контейнер».
Рішення: Якщо трафік видно на мосту, але не на uplink, сфокусуйтеся на маршрутизації/NAT правилах. Якщо на uplink видно, але не доходить до призначення — це upstream.
Швидкий план діагностики
Це порядок дій, що економить час. Не порядок, який робить з вас мережевого чарівника.
Перше: визначте режим мережі і бажану доступність
- Запустіть
docker inspectдля контейнера і підтвердіть, чи це bridge/host/macvlan. - Уточніть: відмова стосується контейнер → інтернет, контейнер → контейнер, LAN → контейнер або хост → контейнер?
Підказка-вузьке місце: Більшість «помилок мережі Docker» — це насправді «ваша ментальна модель хибна». Виправте модель, потім конфіг.
Друге: перевірте базові L3 речі (всередині контейнера і на хості)
- У контейнері:
ip addr,ip route, DNS черезgetent hosts. - На хості: стан мосту, наявність veth, маршрут до підмережі, таблиця сусідів (macvlan).
Підказка-вузьке місце: Відсутній дефолтний маршрут або поганий DNS викликає 80% скарг «не можу дістатися X».
Третє: валідуйте політику і трансляцію (iptables/nftables, прив’язки портів, conntrack)
- Bridge: вхідні проблеми — перевірте опубліковані порти і NAT-ланцюжки iptables.
- Host: перевірте, який процес володіє портом, і файрвол хоста.
- Macvlan: перевірте ARP/статус сусідів і обмеження/політику комутатора.
- Під навантаженням: перевірте статистику conntrack і журнали ядра.
Підказка-вузьке місце: Якщо трафік працює коротко, а потім ламається під навантаженням — припускайте тиск на conntrack або upstream L2 enforcement, перш ніж звинувачувати Docker.
Четверте: зніміть пакети на потрібному інтерфейсі
- Bridge:
tcpdumpна інтерфейсі контейнера (всередині) і на мосту (br-*). - Host:
tcpdumpна інтерфейсі хоста і використовуйте інструменти для атрибуції процесів. - Macvlan:
tcpdumpна батьківському інтерфейсі і на macvlan shim, якщо він є.
Підказка-вузьке місце: Якщо пакети покидають контейнер, але не потрапляють на міст/ uplink — проблема у wiring простору імен. Якщо потрапляють на uplink — upstream.
Типові помилки: симптом → корінна причина → виправлення
1) «Контейнер не може дістатися інтернету, але DNS резолюється»
Симптом: getent hosts example.com працює; curl зависає або таймаутиться.
Корінна причина: Відсутній/некоректний дефолтний маршрут, зламане правило MASQUERADE, або файрвол хоста блокує форвардинг.
Виправлення: Перевірте ip route всередині контейнера; на хості підтвердіть iptables -t nat MASQUERADE і що IP forwarding увімкнений. Переконайтеся, що файрвол хоста дозволяє форвардинг з bridge-підмережі.
2) «Сервіс запущений, але ззовні до нього ніхто не підключається» (bridge)
Симптом: Контейнер слухає на 0.0.0.0:8080 всередині, але клієнти LAN не підключаються.
Корінна причина: Порт не опубліковано, або опубліковано лише на localhost, або файрвол хоста блокує порт.
Виправлення: Перевірте docker ps на наявність 0.0.0.0:hostport->containerport. Якщо відсутній, додайте -p. Потім перевірте файрвол хоста для дозволу входу.
3) «Два контейнери постійно флапають, інколи один не стартує» (host)
Симптом: Петлі рестарту або періодичні помилки bind; логи говорять «address already in use».
Корінна причина: Host mode ділить простір портів; обидва сервіси хочуть один порт.
Виправлення: Припиніть використовувати host networking для обох, або перебудуйте порти. У host mode розглядайте виділення портів як глобальний ресурс на хості.
4) «Macvlan контейнери доступні з LAN, але хост їх не бачить»
Симптом: З іншої машини в підмережі підключитись можна; з Docker-хоста — ні.
Корінна причина: Поведінка macvlan за замовчуванням забороняє шлях host→child на тому ж parent.
Виправлення: Додайте macvlan shim-інтерфейс на хості з IP у тій же підмережі і маршрутизовуйте через нього (або розгляньте ipvlan L3, якщо підходить для вашого середовища).
5) «Все працювало, поки ми не додали більше контейнерів; потім почалися випадкові таймаути» (macvlan)
Симптом: Нові контейнери доступні інколи; ARP нестабільний; журнали комутатора сигналять.
Корінна причина: Політика port security комутатора або ліміти MAC, тиск на CAM-таблицю або обмеження ARP.
Виправлення: Координуйтеся з мережею: підвищте ліміти MAC на порту, відключіть жорстку політику там, де це доречно, або уникайте macvlan на цьому сегменті.
6) «Деякі запити зависають, особливо великі відповіді» (будь-який режим)
Симптом: Малі ping-и працюють; великі передачі зависають; TLS-руки іноді ламаються.
Корінна причина: Невідповідність MTU і зломаний Path MTU Discovery.
Виправлення: Виміряйте ефективний MTU, встановіть MTU мережі Docker або узгодьте MTU інтерфейсів, і перевірте за допомогою пінгів з DF-бітом.
7) «Container-to-container працює за IP, але не за іменем» (bridge)
Симптом: ping 172.22.0.11 працює; ping db-1 падає.
Корінна причина: Контейнери не в одній user-defined bridge мережі, або ви використовуєте дефолтний bridge без правильної DNS-поведінки.
Виправлення: Помістіть обидва контейнери в одну user-defined bridge мережу і використовуйте імена контейнерів (або явні псевдоніми) там.
Контрольні списки / поетапний план
Крок за кроком: безпечний вибір драйвера
- Напишіть матрицю досяжності. Хто з ким має спілкуватися (LAN → контейнер, контейнер → LAN, хост → контейнер, контейнер → інтернет).
- Визначте вимоги ідентичності. Чи вимагають upstream системи IP для кожного робочого навантаження? Якщо так — macvlan може бути потрібен; якщо ні — віддавайте перевагу bridge.
- Визначте модель експозиції. Бажаєте явне публікування портів (bridge) чи спільні порти хоста (host)? За замовчуванням — явне.
- Перевірте модель володіння файрволом. Якщо файрвол хоста централізовано керується і динамічні зміни Docker небажані, плануйте інтеграцію ретельно (bridge) або уникайте Docker-узагальнених NAT-патернів.
- Перевірте політику комутатора якщо macvlan у грі (ліміти MAC, port security, ARP inspection). Зробіть це перед розгортанням.
- Оберіть найпростіший варіант, що задовольняє вимоги. Потім задокументуйте його, щоб наступна людина не «оптимізувала» випадково.
Контрольний список для bridge в продакшн
- Використовуйте user-defined bridge мережі, а не дефолтний
bridge, для реальних додатків. - Обирайте підмережі, які не перекриваються з VPN/діапазонами дата-центрів.
- Публікуйте тільки необхідні порти; прив’язуйте до конкретних IP хоста, коли це можливо.
- Визначте, як ви керуватимете iptables/nftables (і тестуйте після оновлень ОС).
- Проведіть валідацію MTU end-to-end і встановіть його свідомо.
Контрольний список для host в продакшн
- Залишайте host mode для робочих навантажень, які це виправдовують (швидкість пакетів, мережеві демони, агенти хоста).
- Підтримуйте мапу власності портів на кожному хості (або автоматизуйте це).
- Загартовуйте правила файрволу хоста; не покладайтеся на дефолти контейнера.
- Перевірте можливість прив’язати трафік до контейнера/ cgroup у системі спостереження.
Контрольний список для macvlan в продакшн
- Резервуйте задокументований діапазон IP; уникайте змішування з DHCP, якщо ви точно не знаєте, що робите.
- Підтвердіть, що політика комутатора дозволяє кілька MAC на порті і не буде флапити.
- Сплануйте доступ хост→контейнер (shim-інтерфейс) якщо потрібно.
- Моніторьте ARP/поведінку таблиці сусідів і ліміти швидкості.
- Вирішіть, хто керує DNS: вам знадобляться прямі/зворотні записи, якщо корпоративні інструменти їх очікують.
Три корпоративні міні-історії (щоб ви їх не повторили)
Міні-історія 1: інцидент через неправильне припущення (macvlan і доступність з хоста)
Середня компанія контейнеризувала legacy сервіс звітності, який мав бути доступним для набору upstream batch-завдань. Вони обрали macvlan, бо кожне upstream-завдання мало хардкодований allowlist адрес, і переписати політику було політично складно.
У staging все виглядало добре. З інших машин у підмережі вони могли дістатися IP контейнерів напряму. Зміни пішли в продакшн у п’ятницю після обіду, бо звісно. І відразу на хості почали падати локальні health check-и. Оркестратор позначив інстанси як нездорові і перезапускав їх. Сервіс почав флапати, upstream-завдання частіше падали, що викликало ще більше повторних запусків і шуму.
Неправильне припущення: «Якщо LAN дістає — і хост дістане». У випадку macvlan шлях хост→дитина — відомий виняток у багатьох налаштуваннях. Їхні health check-и запускалися в просторі імен хоста і не могли потрапити на macvlan IP. Сервіс був робочим; просто хост його не бачив.
Виправлення було буденним: створити macvlan shim-інтерфейс на кожному хості, дати йому IP у macvlan-підмережі і оновити health check-и, щоб використовувати цей шлях. Також вони задокументували обмеження, щоб ніхто не «спростив» це згодом.
Міні-історія 2: оптимізація, що відплатилася (host networking щоб уникнути NAT)
Інша організація мала high-throughput pipeline збору метрик. Хтось помітив, що лічильники conntrack піднімаються під навантаженням і вирішив, що bridge NAT «їсть CPU». Пропозиція була проста: перевести ingest-контейнери в --network host, щоб пакети обминали NAT і conntrack.
У вузькому бенчмарку це спрацювало. CPU впала, латентність покращилася, і всі відчули, що знайшли чит-код. Зміна поступово розкатили по флоту.
Потім почалися дивності: на підмножині хостів після деплоїв з’явилися періодичні збої ingest. Не на всіх хостах, не завжди. Корінна причина — конфлікти портів між ingest-сервісом і відладним сайдкаром, який теж прив’язував UDP-порт. У bridge режимі вони могли співіснувати через різні простори імен. У host режимі другий сервіс просто не міг прив’язатися. Іноді порядковість деплойментів це маскувала; іноді вибухала.
Вони відкотили host networking для ingest-шару і замість цього налаштували conntrack і зменшили churn з’єднань батчуванням. Фактична проблема була не в «NAT повільний», а в «ми створили занадто багато короткотривалих потоків». Host mode вилікував симптом і додав новий клас відмов, який важче аналізувати.
Після цього вони ввели правило: будь-яка зміна, що збільшує blast radius (наприклад host networking), потребує письмової моделі загроз і плану відкату. Така політика була менш ефектною, ніж графік бенчмарку, але працювала.
Міні-історія 3: нудна, але правильна практика, що врятувала ситуацію (user-defined bridge + явне публікування)
Команда у фінансовому сервісі запускала кілька клієнтських додатків на спільних хостах. Вони стандартизувалися на user-defined bridge мережах для кожного стеку додатків і публікували порти явним чином, прив’язуючи до конкретних інтерфейсів хоста. Це не було модно. Це було також стійко.
Одного вечора оновлення образу від постачальника додало новий debug-listener всередині контейнера. Ніщо зловмисне; просто один із тих «упс, лишив включеним» дефолтів. Сам сервіс працював нормально.
Оскільки команда використовувала явну публікацію портів, новий внутрішній порт залишився внутрішнім. Він не з’явився на хості і не став доступним з LAN. Моніторинг не зазвучав, безпека не панікувала, і інцидент не став заголовком.
Їхній постмортем був коротким: «Нам не довелося перейматись, бо ми не експонували». Це та нудна перемога, яку помічаєш лише порівнюючи з альтернативним сценарієм.
FAQ
1) Чи bridge мережі завжди повільніші за host?
Ні. Bridge додає наклад (veth, lookup на мосту, часто NAT/conntrack), але для багатьох навантажень це не вузьке місце. Вимірюйте перш ніж «лікувати». Якщо ви не завантажуєте CPU на softirq/conntrack — bridge зазвичай підходить.
2) Чому краще віддавати перевагу user-defined bridge над дефолтним bridge?
User-defined bridge мережі дають кращу поведінку DNS/дискавері сервісів, чіткіше розділення і передбачуваніший multi-network сценарій. Дефолтний bridge — дружній до legacy, не до продакшну.
3) Коли --network host — хороша ідея?
Коли навантаження дійсно потребує прямих семантик мережі хоста: великі швидкості пакетів, мережеві демони або агенти хоста. Також коли хост фактично однопрофільний. Інакше host mode збільшує blast radius і складність відлагодження.
4) Чому хост не може дістатися macvlan контейнерів за замовчуванням?
Тому що macvlan ізолює трафік між parent інтерфейсом і macvlan-ендапойнтами так, що стек хоста не може напряму говорити з його «дітьми» на тому ж інтерфейсі. Загальне обхідне рішення — macvlan shim-інтерфейс на хості.
5) Чи можна запускати macvlan на Wi‑Fi?
Інколи, але часто це болісно. Багато драйверів Wi‑Fi і точки доступу не обробляють кілька source MAC на станцію так, як вам потрібно. Якщо мусите спробувати — робіть це в лабораторії і готуйтеся до розчарування.
6) Чи варто використовувати macvlan лише щоб уникнути конфліктів портів?
Зазвичай ні. Конфлікти портів краще вирішувати bridge-мережами з публікацією портів або ставити reverse proxy/ingress попереду. Macvlan міняє конфлікти портів на складність IPAM і L2.
7) Як зупинити Docker від маніпуляцій з моїм файрволом?
Ви можете обмежити поведінку Docker, але не уникнути необхідності правил, якщо потрібні NAT/опубліковані порти. Практичний підхід — вирішити, чи файрвол хоста «Docker-aware» і тестувати порядок правил після оновлень. Якщо середовище забороняє динамічні зміни файрволу, переробіть архітектуру навколо маршрутизованої мережі або upstream load balancer.
8) Який найнадійніший спосіб виставити сервіси з bridge режиму?
Публікуйте лише потрібні порти, прив’язуйте до конкретного IP хоста, коли доречно (наприклад лише до внутрішнього інтерфейсу), і застосовуйте правила файрволу хоста. Ставтеся до опублікованих портів як до зовнішнього API-поверху.
9) Як вирішити логування і видимість клієнтських IP при bridge NAT?
Ви можете втратити оригінальний IP клієнта, якщо трафік проксований/NAT-ований залежно від шляху. Використовуйте реверс-проксі, що передає X-Forwarded-For або proxy protocol, де це можливо, або уникайте NAT на цьому переході (macvlan/host або маршрутизована архітектура), якщо справжній IP клієнта обов’язковий.
Наступні кроки, які можна зробити цього тижня
- Інвентаризуйте хости: перерахуйт
е контейнери і зафіксуйте, які з них використовують host networking і чому. «Бо так працювало» — не причина. - Міґруйте один стек на user-defined bridge, якщо ви ще використовуєте дефолтний
bridge. Перевірте іменування та дисципліну публікації портів. - Визначте план неперекривних підмереж для Docker bridge у середовищах (dev/stage/prod). Перекриття — повільна течія, що стає повінню.
- Вирішіть політику macvlan: або «дозволено з перевіркою комутатора і резервуванням IP» або «не дозволено». Невизначеність — як macvlan потрапляє в продакшн без дорослих у кімнаті.
- Напишіть односторінковий руkbук використовуючи Швидкий план діагностики вище і додайте точні команди, які ваш on-call може виконати без роздумів.
Якщо хочете правило, що витримає стрес: використовуйте bridge, доки не зможете назвати конкретне обмеження bridge, яке вам заважає. Тоді обирайте host або macvlan як свідоме виняток, а не як трендовий вибір.