Сетевую модель выбирают по месту, где должны взаимодействовать узлы. Для контейнеров на одном Docker-хосте подходит пользовательская сеть bridge. Для связи контейнеров на нескольких Docker-хостах применяют overlay, обычно в Docker Swarm. В Kubernetes сетевой слой настраивает CNI-плагин: Calico, Cilium, Flannel и другие решения, совместимые со спецификацией Container Network Interface.
Короткое правило такое: bridge изолирует контейнеры внутри одного хоста, overlay соединяет Docker-узлы через распределенную сеть, CNI подключает Pod к сети Kubernetes и задает маршрутизацию между Pod, узлами и Service. Проброс портов Docker через -p создает публикацию порта на хосте, но не превращает контейнер в узел физической сети.
В Kubernetes Docker-сеть не управляет сетью Pod. Kubelet обращается к CRI runtime, runtime вызывает CNI-плагин, а тот создает интерфейс в сетевом пространстве имен Pod, назначает адрес и добавляет маршруты. Поэтому диагностика начинается с определения среды: Docker Engine, Docker Swarm или Kubernetes.
Вопрос, на который отвечает диагноз
Сетевой диагноз должен показать точку разрыва: приложение, сетевое пространство имен, виртуальный интерфейс, маршрут, фильтр, DNS, NAT или удаленный узел. Сообщение connection refused означает одну ситуацию, а таймаут, ошибку разрешения имени и ответ 403 требуют разных проверок.
| Симптом | Первая гипотеза | Проверка |
|---|---|---|
connection refused | Порт доступен по маршруту, но процесс его не слушает либо активно отклоняет соединение | ss -lntp, проверка адреса bind и логов приложения |
| Таймаут | Нет маршрута, трафик блокирует firewall или узел недоступен | ip route, nc -vz, tcpdump |
| Ошибка DNS | Неверный resolver, имя не существует или запрос блокирует политика | cat /etc/resolv.conf, getent hosts |
| Работает внутри Pod, но не снаружи | Service или Ingress не направляет запрос на нужный Pod | kubectl get svc,endpointslice, проверка selector |
| Работает на одном узле, но не на другом | Проблема межузлового маршрута, MTU, VXLAN или firewall | маршруты, MTU, порты CNI или overlay и захват пакетов |
Для HTTP-сбоев полезно отделять сетевой уровень от приложения. Команда curl -v показывает разрешение имени, попытку TCP-соединения, TLS и HTTP-ответ. Алгоритм с curl -v, логами веб-сервера и трассировкой описан в руководстве по диагностике маршрутизации.
Что именно я ищу
Перед командой диагностики зафиксируйте источник, назначение, порт, протокол и ожидаемый результат. Запрос из контейнера api к db:5432 отличается от запроса пользователя к опубликованному порту хоста 8080.
Docker: базовый осмотр
docker network ls
docker network inspect app_net
docker inspect api
docker exec api ip addr
docker exec api ip route
docker exec api cat /etc/resolv.conf
docker exec api getent hosts db
У пользовательской сети bridge проверяйте подсеть, шлюз, список подключенных контейнеров и параметры IPv4. Команда docker inspect показывает адрес контейнера и опубликованные порты, но опубликованный порт нужно дополнительно проверить с той стороны, откуда приходит запрос.
Kubernetes: базовый осмотр
kubectl get nodes -o wide
kubectl get pods -A -o wide
kubectl get svc,endpointslice -A
kubectl get networkpolicy -A
kubectl describe pod api-0
kubectl exec -it api-0 -- ip addr
kubectl exec -it api-0 -- ip route
kubectl exec -it api-0 -- cat /etc/resolv.conf
В выводе kubectl get pods -o wide сравнивайте адрес Pod и узел, где он запущен. Если ошибка возникает между узлами, переходите на хост и проверяйте интерфейсы CNI, маршруты, таблицы firewall и логи сетевого плагина. Названия DaemonSet и labels у Calico, Cilium и Flannel различаются, поэтому сначала получите их командой kubectl -n kube-system get pods.
Проверка процесса
ss -lntp
curl -v http://127.0.0.1:8080
nc -vz db 5432
getent hosts db
Процесс, который слушает 127.0.0.1 внутри контейнера, принимает запросы только из этого сетевого пространства имен. Для связи с другими контейнерами приложение обычно должно слушать адрес 0.0.0.0 или конкретный адрес интерфейса контейнера.
Три опоры и три уточнения
Практическая диагностика держится на трех опорах: интерфейс, маршрут и фильтрация. Если одна опора пропущена, проверка часто заканчивается догадкой.
- Интерфейс. У источника и назначения должны существовать рабочие интерфейсы. В Docker это может быть пара veth и мост
docker0или пользовательский bridge. В Kubernetes Pod получает собственный интерфейс, связанный с интерфейсом на узле. - Маршрут. Ядро должно знать, через какой шлюз отправлять пакет. Команда
ip route get 10.20.0.15показывает выбранный маршрут для конкретного адреса. - Фильтрация. Firewall, NetworkPolicy, security group и правила NAT могут изменить или заблокировать трафик. Проверяйте направление, интерфейс и порт, а не только факт наличия правила.
Три уточнения сокращают область поиска:
- Какое сетевое пространство имен содержит процесс?
- Какой адрес и порт указаны в запросе?
- На каком участке меняется результат: внутри контейнера, на хосте, между узлами или на внешнем маршрутизаторе?
Проверка ping не заменяет проверку TCP. ICMP может быть запрещен, а нужный TCP-порт при этом работать. Обратная ситуация тоже встречается: ICMP проходит, но firewall блокирует порт приложения.
Структурное интервью: диагностика как ситуация
Сетевую проблему удобно разбирать как короткое интервью с системой. Каждый ответ должен подтверждаться командой или наблюдением. Ниже приведены четыре типовых сценария.
Контейнер обращается к контейнеру
Оба контейнера должны находиться в одной пользовательской сети либо иметь маршрутизируемую связь через подключенные сети. Имя контейнера или имя сервиса разрешается встроенным DNS Docker только в пользовательских сетях.
docker network create --driver bridge app_net
docker run -d --name db --network app_net postgres:16
docker run --rm --network app_net busybox:1.36 nslookup db
docker run --rm --network app_net busybox:1.36 nc -vz db 5432
В приложении указывайте db:5432, а не localhost:5432. Адрес localhost указывает на текущий контейнер.
Клиент обращается к опубликованному порту Docker
docker run -d --name web -p 127.0.0.1:8080:80 nginx:alpine
ss -lntp | grep 8080
curl -v http://127.0.0.1:8080
Публикация с адресом 127.0.0.1 принимает запросы только на loopback-интерфейсе хоста. Запись -p 8080:80 обычно публикует порт на всех адресах хоста, если это не ограничено настройками Docker или firewall. Внутри контейнера сервис должен слушать порт 80, а не опубликованный порт 8080.
Контейнеры находятся на разных Docker-узлах
Для этой задачи создают overlay-сеть на manager-узле Docker Swarm:
docker swarm init --advertise-addr 192.0.2.10
docker network create --driver overlay --attachable app_overlay
docker service create --name web --network app_overlay --publish published=8080,target=80 nginx:alpine
Между узлами должны проходить TCP-порт 2377 для управления Swarm, TCP и UDP 7946 для обмена информацией и UDP 4789 для VXLAN-трафика overlay. Конкретные адреса, интерфейсы и правила firewall зависят от конфигурации узлов. При закрытом UDP 4789 сервисы могут запускаться, но межузловой обмен останется недоступным.
Pod обращается к Service Kubernetes
kubectl create deployment web --image=nginx:alpine
kubectl expose deployment web --port=80 --target-port=80
kubectl get svc web
kubectl get endpointslice -l kubernetes.io/service-name=web
kubectl run net-debug --rm -it --restart=Never --image=busybox:1.36 -- sh
Внутри отладочного Pod проверьте wget -S -O - http://web. Если у Service нет EndpointSlice, ищите ошибку в selector или readinessProbe. Если EndpointSlice заполнен, но запрос не проходит, проверяйте kube-proxy или eBPF dataplane, NetworkPolicy, маршрут Pod и состояние CNI.
Для тестового Kubernetes-кластера с VDS, сетью и управляемыми ресурсами можно использовать облачную инфраструктуру Timeweb Cloud. Перед проверкой production-сценария зафиксируйте диапазоны Pod и Service, MTU, правила доступа и способ публикации сервисов.
Чем структурированное отличается от структурного
Структурированная проверка отвечает на вопрос «в каком порядке искать ошибку». Структурная модель отвечает на вопрос «какие компоненты образуют путь пакета». В Docker и Kubernetes эти понятия нельзя смешивать.
| Уровень | Docker | Kubernetes | Что проверять |
|---|---|---|---|
| Процесс | Процесс внутри контейнера | Процесс внутри Pod | bind-адрес, порт, состояние приложения |
| Network namespace | Изолированный стек контейнера | Общий сетевой namespace всех контейнеров одного Pod | адреса, интерфейсы, resolver |
| Виртуальный сегмент | bridge или overlay | сеть Pod, которую создает CNI | veth, bridge, VXLAN, маршруты |
| Сервисный адрес | опубликованный порт хоста или DNS-имя контейнера | ClusterIP, NodePort, LoadBalancer или Ingress | selector, EndpointSlice, NAT или dataplane |
| Политика | iptables, nftables и параметры Docker | NetworkPolicy, firewall узла, security group | разрешенные направления и порты |
Как устроен bridge Docker
Docker создает сетевой namespace контейнера, пару виртуальных Ethernet-интерфейсов и подключает один конец к bridge на хосте. Контейнер получает адрес из подсети bridge, а трафик наружу обычно проходит через NAT. Пользовательская сеть bridge получает встроенное DNS-разрешение имен. У стандартной сети bridge возможности изоляции и обнаружения сервисов ограничены, поэтому для приложений создавайте отдельные пользовательские сети.
Как устроен overlay Docker
Overlay добавляет логическую сеть поверх нескольких Docker-узлов. Сеть использует VXLAN для передачи кадров между узлами, а Docker поддерживает распределенное обнаружение участников и виртуальные адреса сервисов. MTU overlay меньше MTU физического интерфейса из-за служебных заголовков. При фрагментации или блокировке ICMP Path MTU Discovery крупные HTTP-ответы могут зависать при исправной проверке TCP.
Как CNI подключает Pod
CNI-плагин получает параметры Pod от container runtime и выполняет операции ADD, DEL и, если поддерживается, CHECK. Обычно он создает пару veth, переносит один конец в namespace Pod, назначает IP-адрес, добавляет маршрут и настраивает правила dataplane. Calico может использовать маршрутизацию и политики, Flannel часто строит overlay-сеть, Cilium применяет eBPF для маршрутизации, балансировки и контроля трафика. Реальные возможности зависят от конфигурации плагина.
Сравнение производительности Docker, Kubernetes и LXC с измерениями сетевой задержки, пропускной способности и влияния bridge или overlay приведено в практическом тесте контейнерных сред.
Пять лет и «нормальное Я»
Для аварийной проверки полезен пятиточечный baseline, то есть зафиксированное нормальное состояние. Формулировка «раньше сеть работала» слишком расплывчата. Запишите конкретные значения и сравнивайте с ними после изменений.
- Адрес. Диапазон Pod или контейнера, адрес шлюза и адрес узла.
- Маршрут. Маршрут к подсети назначения и выбранный интерфейс.
- DNS. Resolver, который используется процессом, и ожидаемый ответ для имени.
- Порт. Адрес bind, порт процесса, порт Service и опубликованный порт.
- Путь. Разрешенные firewall, NetworkPolicy, security group и MTU.
Зафиксировать baseline можно обычными командами:
ip addr
ip route
ss -lntp
cat /etc/resolv.conf
kubectl get pods -A -o wide
При изменении CNI, Docker, ядра Linux или правил firewall снимите эти значения повторно. Разница между двумя снимками часто показывает причину быстрее, чем просмотр всех логов.
В production полезно наблюдать сетевые ошибки вместе с CPU, памятью, диском и состоянием узлов. Пошаговый алгоритм поиска причины по метрикам Kubernetes собран в руководстве по диагностике Pod и узлов.
Опросник верит на слово, интервью просит пример
Описание «сеть не работает» не дает диагностической информации. Нужен воспроизводимый пример: кто отправляет запрос, куда, каким протоколом, с какого узла и какой ответ возвращается.
| Плохое описание | Рабочая формулировка | Следующая команда |
|---|---|---|
| Сервис недоступен | Pod api-7d не подключается к db.default.svc на TCP-порт 5432 | getent hosts db, nc -vz db 5432 |
| Docker не пробрасывает порт | Запрос с другого хоста на адрес узла и порт 8080 получает таймаут | ss -lntp, firewall, tcpdump -ni any port 8080 |
| В Kubernetes сломалась сеть | Pod на worker-2 не достигает Pod на worker-1, локальный обмен работает | адреса Pod, маршруты, MTU, CNI и firewall |
Минимальный набор доказательств для инцидента:
- точное время и часовой пояс;
- адрес источника и назначения;
- порт и протокол;
- команда, которая воспроизводит ошибку;
- вывод команды и текст ошибки;
- узлы, Pod или контейнеры, затронутые проблемой;
- последнее изменение: deploy, обновление CNI, firewall, DNS или ядра.
Проверяйте одну переменную за раз. Сначала выполните запрос внутри источника, затем на хосте, после этого с соседнего узла и только потом с внешнего клиента. Такой порядок показывает, где меняется результат.
Две шкалы - два способа знать
В сетевой проверке нужны две независимые шкалы: связность и разрешение. Связность показывает, может ли пакет пройти. Разрешение показывает, разрешено ли конкретному субъекту обращаться к конкретному порту.
| Проверка | Что подтверждает | Что не подтверждает |
|---|---|---|
ip route get | локальный выбор маршрута | что удаленный узел пропустит пакет |
ping | ответ ICMP при разрешенном ICMP | доступность TCP или UDP-приложения |
nc -vz host port | TCP-соединение с указанным портом | корректность протокола и авторизации |
curl -v | DNS, TCP, TLS и HTTP-обмен | доступность другого порта или другого пути |
| NetworkPolicy | правило разрешения или запрета при поддержке плагина | корректность маршрута и самого приложения |
В Kubernetes NetworkPolicy работает только при поддержке и включенной проверке политик в CNI-плагине. Наличие YAML-объекта в API не гарантирует фильтрацию, если сетевой плагин не применяет эти правила.
Для проверки конкретного порта используйте команду из того же сетевого контекста, где работает приложение:
kubectl exec -it net-debug -- nc -vz api 8080
kubectl exec -it net-debug -- wget -S -O - http://api:8080/health
kubectl exec -it net-debug -- nslookup api
Если TCP-соединение установлено, но HTTP-запрос получает ошибку, переходите к логам приложения и readinessProbe. Если DNS-имя не разрешается, проверяйте CoreDNS, search domains и NetworkPolicy для UDP или TCP-порта 53.
Домены глазами интервьюера
Слово «домен» в контейнерных сетях встречается в двух смыслах. Сетевой домен определяет границы связности, а DNS-домен определяет правила разрешения имен. Ошибка в одном из них может выглядеть одинаково: приложение сообщает, что удаленный сервис недоступен.
DNS в Docker
В пользовательской сети Docker контейнеры могут находить друг друга по имени контейнера или alias. Внутри контейнера проверьте файл /etc/resolv.conf и выполните getent hosts db. Если имя не разрешается, проверьте, подключены ли оба контейнера к одной сети:
docker network inspect app_net
docker inspect -f '{{json .NetworkSettings.Networks}}' api
docker inspect -f '{{json .NetworkSettings.Networks}}' db
Для связи между изолированными сетями подключайте контейнер к обеим сетям только при ясной необходимости. Такой контейнер становится точкой пересечения сегментов и требует отдельного контроля доступа.
DNS в Kubernetes
Service получает DNS-имя внутри кластера. В том же namespace обычно достаточно короткого имени api. Полное имя имеет вид api.namespace.svc.cluster.local. Если короткое имя работает только в одном namespace, проверьте поля search в /etc/resolv.conf.
Для Service проверьте три объекта:
- selector Service должен совпадать с labels Pod;
- EndpointSlice должен содержать адреса готовых Pod;
- порт Service и
targetPortдолжны соответствовать порту приложения.
kubectl get svc api -o yaml
kubectl get endpointslice -l kubernetes.io/service-name=api -o wide
kubectl get pods -l app=api --show-labels
kubectl describe svc api
Сетевой DNS не исправит ошибку selector. CoreDNS может успешно вернуть ClusterIP, а Service при этом не будет иметь ни одного рабочего backend.
Нарциссизм как способ, а не структура
Название контейнера, label или имя Service описывает объект, но не строит сетевой путь. Путь задают интерфейсы, адреса, маршруты, правила публикации и политики. Поэтому диагностика по одному имени объекта часто приводит к неверному выводу.
Вместо доверия к названиям составьте явную схему:
- источник: процесс, контейнер или Pod;
- исходный адрес и namespace;
- назначение: контейнер, Service, NodePort, Ingress или внешний адрес;
- порт и протокол;
- маршрут между узлами;
- правила NAT, firewall и NetworkPolicy;
- ожидаемый ответ.
Пример политики с запретом входящего трафика и разрешением доступа к API только от Pod с label app=frontend:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-ingress
namespace: default
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
После применения проверьте разрешенный и запрещенный сценарии из двух Pod с разными labels. Если политика должна ограничивать исходящие запросы, добавьте Egress и явно разрешите DNS, доступ к базам данных и нужным внешним адресам.
Аудит сетевой безопасности Docker и Kubernetes включает проверку NetworkPolicies, RBAC, образов, Docker Bench и Kube-bench. Практический чек-лист доступен в руководстве по аудиту контейнеров и Kubernetes-кластера.
Для быстрой работы используйте такую последовательность: определить среду, зафиксировать источник и назначение, проверить DNS, затем порт, маршрут, firewall и политику. В Docker начинайте с docker network inspect и docker inspect. В Kubernetes добавляйте проверку Service, EndpointSlice, CNI и сетевого пространства имен Pod. Такой порядок отделяет ошибку приложения от ошибки контейнерной сети и сокращает время поиска причины.