Топология сети
Путь пакета от хоста до приложения и обратно.
Что создаётся при запуске container'а
ХОСТ
┌───────────────────────────────────────────────┐
│ │
│ eth0 (внешняя сеть) │
│ │ │
│ │ правила трансляции адресов │
│ │ 8080/tcp ──► 172.18.0.2:8000 │
│ │ │
│ ┌─┴──────────────┐ │
│ │ docker0 или │ мост │
│ │ br-xxxx │ 172.18.0.1 │
│ └─┬──────────┬───┘ │
│ │ │ │
│ veth-a veth-b ← пары интерфейсов │
└─────┼──────────┼─────────────────────────────-┘
│ │
┌─────┴────┐ ┌───┴──────┐
│ eth0 │ │ eth0 │ ← внутри своих
│172.18.0.2│ │172.18.0.3│ network namespace
│ │ │ │
│ api │ │ db │
│ lo: │ │ lo: │ ← у каждого СВОЙ 127.0.0.1
│ 127.0.0.1│ │ 127.0.0.1│
└──────────┘ └──────────┘
Как читать
veth — пара интерфейсов, работающая как отрезок кабеля: что вошло с одного конца, вышло с другого. Один конец находится в namespace container'а и называется там eth0, другой подключён к мосту на хосте.
Мост соединяет всё, что к нему подключено. Container'ы одной сети видят друг друга напрямую.
Что здесь неочевидно
У каждого container'а свой 127.0.0.1. Это отдельный интерфейс lo внутри своего namespace. Приложение, слушающее петлю, недоступно ни с хоста, ни от соседа — только для процессов того же container'а.
api пытается: postgresql://localhost:5432
└─► свой lo, где ничего нет
должно быть: postgresql://db:5432
└─► 172.18.0.3 через мост
Публикация порта — правило трансляции, а не «открытие». Оно направляет трафик с порта хоста на адрес container'а. Если приложение слушает петлю, правило ведёт в пустоту:
curl localhost:8080
│
▼ правило: 8080 ──► 172.18.0.2:8000
│
▼ на 172.18.0.2:8000 никто не слушает
отказ (приложение на 127.0.0.1:8000)
EXPOSE не создаёт ни одного правила. Это запись в метаданных образа.
Разрешение имён
сеть по умолчанию bridge пользовательская сеть
───────────────────────── ─────────────────────
только адреса встроенный DNS 127.0.0.11
getent hosts db → пусто getent hosts db → 172.18.0.3
Compose создаёт пользовательскую сеть автоматически — поэтому симптом «имя не разрешается» встречается только при ручном docker run.
Изоляция сетей
хост
│ публикация 8080
▼
┌─────────┐
│frontend │
└────┬────┘
│
┌────┴────┐
│ api │ состоит в обеих сетях
└────┬────┘
│
┌────┴──────────────────────┐
│ backend (internal: true) │ нет выхода наружу
│ ┌────┐ ┌────┐ ┌─────┐ │
│ │ db │ │cache│ │worker│ │
│ └────┘ └────┘ └─────┘ │
└───────────────────────────┘
internal: true — не то же, что «не публиковать порты»:
| Что | Направление |
|---|---|
| Не публиковать порт | Снаружи внутрь |
internal: true | Изнутри наружу |
Второе защищает от того, что скомпрометированная зависимость обратится в интернет.
Режимы сети
bridge (умолчание) свой namespace, адрес в мосте, NAT
host namespace ХОСТА: нет NAT, нет изоляции
none только lo
container:имя тот же namespace, что у другого container'а
Последний — основной приём отладки:
docker run --rm -it --network container:api nicolaka/netshoot
ss -tlnp # видит сокеты api
От симптома к месту на схеме
| Симптом | Где искать |
|---|---|
| Порт опубликован, ответа нет | Слушает ли приложение и на каком адресе |
| Изнутри отвечает, снаружи нет | lo вместо eth0 |
| Имя не разрешается | Сеть по умолчанию вместо пользовательской |
| Имя разрешается, соединения нет | Сосед не слушает или не готов |
| Внешние имена не разрешаются | internal: true или DNS хоста |
Работает только при --network host | Приложение слушает петлю |
Подробнее
Урок 8.1. Основы сети
Урок 8.2. Bridge-сети
Урок 8.3. Публикация портов
Networking cheat sheet
Навигация
Раздел 08. Networking
Диаграмма стека Compose
Главное оглавление