Networking cheat sheet
Управление сетями, публикация портов, диагностика.
127.0.0.1внутри container'а — петлевой интерфейс самого container'а, а не хоста. Половина сетевых затруднений сводится к этому (урок 8.3).
Содержание
- Управление сетями
- Публикация портов
- Разрешение имён
- Диагностика изнутри
- Диагностика снаружи
- От симптома к проверке
Управление сетями
| Задача | Команда |
|---|---|
| Список | docker network ls |
| Создать пользовательскую | docker network create app-net |
| Создать без выхода наружу | docker network create --internal secure-net |
| Задать подсеть | docker network create --subnet 172.30.0.0/16 net |
| Подробности и участники | docker network inspect app-net |
| Подключить работающий container | docker network connect app-net имя |
| Отключить | docker network disconnect app-net имя |
| Удалить неиспользуемые | docker network prune |
Режимы сети
| Режим | Что даёт | Чего лишает |
|---|---|---|
bridge (умолчание) | Свой namespace, адрес в мосте | — |
| Пользовательская сеть | То же плюс разрешение имён | — |
--network host | Сеть хоста напрямую, нет NAT | Сетевую изоляцию целиком |
--network none | Только петля | Любую связь |
--network container:имя | Тот же namespace, что у другого | — |
Последний режим — основной приём отладки: подключить container с утилитами к сети исследуемого.
Публикация портов
docker run -p 8080:8000 образ # доступен из сети
docker run -p 127.0.0.1:8080:8000 образ # только с хоста
docker run -p 8000 образ # случайный порт хоста
docker run -P образ # все EXPOSE на случайные порты
docker port имя # какие порты опубликованы
Синтаксис: [адрес_хоста:]порт_хоста:порт_container'а.
| Что | Значение |
|---|---|
EXPOSE в Dockerfile | Документация; ничего не публикует |
expose: в Compose | То же; доступ только внутри сетей проекта |
ports: в Compose | Настоящая публикация |
Три условия, чтобы запрос дошёл: порт опубликован, приложение слушает этот порт, приложение слушает 0.0.0.0, а не петлю. Нарушение любого даёт один и тот же симптом.
Разрешение имён
| Где | Работает ли по именам |
|---|---|
Сеть по умолчанию bridge | Нет |
| Пользовательская сеть | Да, по имени container'а |
| Compose | Да, по имени сервиса |
--network host | Разрешение хоста |
docker run --network app-net --name db образ
docker exec api getent hosts db
Обращение к хосту изнутри
| Способ | Заметка |
|---|---|
host.docker.internal | Docker Desktop; на Linux требует --add-host |
--add-host=host.docker.internal:host-gateway | Явно на Linux |
| Адрес шлюза сети | docker network inspect bridge --format '{{(index .IPAM.Config 0).Gateway}}' |
Диагностика изнутри
Образы slim и alpine не содержат curl, ss, netstat, dig. Python есть — им и пользуемся.
Слушает ли порт:
docker exec имя python -c "
import socket
for addr in ('127.0.0.1', '0.0.0.0'):
s = socket.socket(); s.settimeout(1)
print(addr, 'открыт' if s.connect_ex((addr, 8000)) == 0 else 'закрыт')"
Разрешается ли имя:
docker exec имя getent hosts db
docker exec имя python -c "import socket; print(socket.gethostbyname('db'))"
Доступен ли сосед:
docker exec имя python -c "
import socket
s = socket.create_connection(('db', 5432), 2); s.close(); print('доступен')"
HTTP-запрос без curl:
docker exec имя python -c "
import urllib.request
r = urllib.request.urlopen('http://127.0.0.1:8000/healthz')
print(r.status, r.read()[:200])"
Что видит container о сети:
docker exec имя cat /etc/resolv.conf # серверы имён
docker exec имя cat /etc/hosts # статические записи
docker exec имя cat /proc/net/tcp # соединения; порты в шестнадцатеричном виде
Разбор /proc/net/tcp:
docker exec имя python -c "
for line in open('/proc/net/tcp').read().splitlines()[1:]:
f = line.split()
addr, port = f[1].split(':')
state = f[3]
if state == '0A': # LISTEN
ip = '.'.join(str(int(addr[i:i+2], 16)) for i in (6, 4, 2, 0))
print(f'{ip}:{int(port, 16)}')"
Диагностика снаружи
Полный набор утилит без изменения образа:
docker run --rm -it --network container:имя nicolaka/netshoot
Второй container подключается к тому же network namespace: ss, tcpdump, dig работают так, как если бы были внутри (урок 13.5).
То же средствами хоста:
PID=$(docker inspect имя --format '{{.State.Pid}}')
sudo nsenter -n -t "$PID" ss -tlnp
Прочее:
docker port имя # опубликованные порты
docker inspect имя --format '{{json .NetworkSettings.Networks}}'
docker network inspect app-net --format '{{json .Containers}}'
docker network inspect app-net --format '{{.Internal}}'
От симптома к проверке
| Симптом | Первая проверка | Вероятная причина |
|---|---|---|
| Порт опубликован, ответа нет | Слушает ли изнутри | Слушает 127.0.0.1 или другой порт |
| Изнутри отвечает, снаружи нет | Адрес привязки | 127.0.0.1 вместо 0.0.0.0 |
| Имя не разрешается | docker inspect сетей | Сеть по умолчанию bridge |
| Имя разрешается, соединения нет | Строка подключения | localhost вместо имени сервиса |
| Внешние имена не разрешаются | docker network inspect --format '{{.Internal}}' | internal: true или DNS хоста |
Работает при --network host, иначе нет | Адрес привязки | Приложение слушает петлю |
| Соединение к базе отклонено при старте | depends_on | Ждёт запуска, а не готовности |
| Порт занят на хосте | docker ps --format '{{.Names}} {{.Ports}}' | Другой container уже опубликовал |
--scale не работает | container_name в конфигурации | Имя фиксировано |
Разбор самого частого случая
curl localhost:8080 не отвечает при -p 8080:8000:
# 1. Опубликован ли порт на самом деле
docker port имя
# 2. Слушает ли приложение внутри — и на каком адресе
docker exec имя python -c "
import socket
for addr in ('127.0.0.1', '0.0.0.0'):
s = socket.socket(); s.settimeout(1)
print(addr, s.connect_ex((addr, 8000)))"
# 3. Запустилось ли приложение вообще
docker logs --tail 20 имя
| Результат шага 2 | Вывод |
|---|---|
127.0.0.1 = 0, 0.0.0.0 ≠ 0 | Слушает петлю — исправить адрес привязки |
| оба ≠ 0 | Приложение не слушает этот порт |
| оба = 0, снаружи молчит | Проблема в публикации или в правилах хоста |
Навигация
Вернуться к справочникам
Docker CLI cheat sheet
Debugging checklist
Раздел 08. Networking
Главное оглавление