Сетевые модели в Docker и Kubernetes: bridge, overlay, CNI и решение практических задач | AdminWiki

Сетевые модели в Docker и Kubernetes: bridge, overlay, CNI и решение практических задач

10 сентября 2026 12 мин. чтения

Сетевую модель выбирают по месту, где должны взаимодействовать узлы. Для контейнеров на одном 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 не направляет запрос на нужный Podkubectl 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 могут изменить или заблокировать трафик. Проверяйте направление, интерфейс и порт, а не только факт наличия правила.

Три уточнения сокращают область поиска:

  1. Какое сетевое пространство имен содержит процесс?
  2. Какой адрес и порт указаны в запросе?
  3. На каком участке меняется результат: внутри контейнера, на хосте, между узлами или на внешнем маршрутизаторе?

Проверка 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 эти понятия нельзя смешивать.

УровеньDockerKubernetesЧто проверять
ПроцессПроцесс внутри контейнераПроцесс внутри Podbind-адрес, порт, состояние приложения
Network namespaceИзолированный стек контейнераОбщий сетевой namespace всех контейнеров одного Podадреса, интерфейсы, resolver
Виртуальный сегментbridge или overlayсеть Pod, которую создает CNIveth, bridge, VXLAN, маршруты
Сервисный адресопубликованный порт хоста или DNS-имя контейнераClusterIP, NodePort, LoadBalancer или Ingressselector, EndpointSlice, NAT или dataplane
Политикаiptables, nftables и параметры DockerNetworkPolicy, 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, то есть зафиксированное нормальное состояние. Формулировка «раньше сеть работала» слишком расплывчата. Запишите конкретные значения и сравнивайте с ними после изменений.

  1. Адрес. Диапазон Pod или контейнера, адрес шлюза и адрес узла.
  2. Маршрут. Маршрут к подсети назначения и выбранный интерфейс.
  3. DNS. Resolver, который используется процессом, и ожидаемый ответ для имени.
  4. Порт. Адрес bind, порт процесса, порт Service и опубликованный порт.
  5. Путь. Разрешенные 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-порт 5432getent 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 portTCP-соединение с указанным портомкорректность протокола и авторизации
curl -vDNS, 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 проверьте три объекта:

  1. selector Service должен совпадать с labels Pod;
  2. EndpointSlice должен содержать адреса готовых Pod;
  3. порт 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. Такой порядок отделяет ошибку приложения от ошибки контейнерной сети и сокращает время поиска причины.

Поделиться:
Сохранить гайд? В закладки браузера