Learning Platform
Глоссарий Troubleshooting
Урок 11.05 · 22 мин
Продвинутый
NetworkPolicytroubleshootingkubectl describekube-dnsCKAD

Troubleshooting NetworkPolicy

NetworkPolicy ломается тихо. Никакой ошибки в API server, никакого warning в kubectl describe. Просто Pod не может достучаться или, наоборот, продолжает достукиваться. Дальше — алгоритм пошаговой диагностики, который работает на CKAD и в production. Не угадывать, не править YAML наугад, а проверять гипотезы по порядку.


Сетевая диагностика: ping, dig, ss, nc, traceroute

Чеклист из 6 шагов

Диагностика NetworkPolicy: порядок проверок
1. CNI поддерживает policy?kubectl get pods -n kube-system | grep -E 'calico|cilium|antrea|weave'. Если пусто — policy игнорируется в принципе, никакой YAML не поможет.
2. Policy targets правильный Pod?kubectl describe networkpolicy NAME. Смотрите PodSelector. Проверьте labels target Pod — kubectl get pods --show-labels. PodSelector matches labels?
3. policyTypes указан явно?Часто забывают [Egress] и пишут только egress блок. K8s по эвристике должен добавить, но лучше указывать явно. Если в policy [Ingress] и есть egress блок — он игнорируется.
4. Match peers корректный?namespaceSelector + podSelector: один peer (AND) или два peer (OR)? Перечитайте YAML, особенно дефисы. kubectl describe netpol показывает интерпретацию.
5. DNS работает?Application использует имена Service? Без egress на kube-dns:53 будет name resolution failure. Это #1 проблема при deny-all egress.
6. Обе стороны разрешают?Если source isolated по egress И target isolated по ingress — нужны обе policies. Одной недостаточно.

Идём по очереди. На каждом шаге проверка занимает 10-30 секунд — это быстрее, чем редактировать YAML вслепую.


Шаг 1: CNI installed and supports NetworkPolicy

# Какие CNI работают в кластере
kubectl get pods -n kube-system | grep -E 'calico|cilium|antrea|weave|kube-router'

# Расширенная проверка — что лежит в /etc/cni/net.d на node
kubectl debug node/<NODE-NAME> -it --image=busybox -- ls /host/etc/cni/net.d/

Если ни одного CNI с поддержкой NetworkPolicy — задача нерешаема в принципе. На CKAD в задаче явно сказано «cluster уже настроен с поддержкой NetworkPolicy», но в production проверить — first thing.


Шаг 2: podSelector matches target Pod

# Какие labels у Pod
kubectl get pod <POD-NAME> --show-labels

# Что policy targets
kubectl describe networkpolicy <NETPOL-NAME>

# Видим раздел PodSelector: app=postgres
# Видим в labels Pod: app=postgres,version=14
# Match — yes

Если labels отличаются хотя бы одним символом (App vs app, postgres vs postgresql) — selector ничего не matchит, policy для Pod-а не применяется, он остаётся non-isolated, и весь трафик идёт по default-allow.

WARNING

Это особенно коварно при «deny-all», который не работает: вы думаете, что Pod должен быть запрещён, но он просто не matchится с policy. Pod остаётся в default-allow и принимает любой трафик. Часто бывает после копипасты — забыли поменять label в template.


Шаг 3: policyTypes — критически важное поле

kubectl describe networkpolicy <NAME>

# В выводе ищите:
# Policy Types: Ingress, Egress

Если видите только Ingress, а у вас есть egress блок — он игнорируется. Edit policy и добавьте Egress.

kubectl edit networkpolicy <NAME>
# Или
kubectl get netpol <NAME> -o yaml > netpol.yaml
# Поправьте, kubectl apply -f netpol.yaml
DANGER

Самая частая CKAD-ошибка: «написал egress, но не сработало». В 80% случаев — забыли [Egress] в policyTypes. Без явного указания egress блок может молчаливо игнорироваться (поведение зависит от API server version, на 1.35 обычно поправляется эвристикой, но лучше не полагаться).


Шаг 4: AND vs OR в peers

kubectl describe networkpolicy показывает peers таким образом:

Allowing ingress traffic:
  To Port: 80/TCP
  From:
    NamespaceSelector: team=backend
    PodSelector: app=web

Без отступа между NamespaceSelector и PodSelector — это OR (два peers). С отступом или явно в одном блоке — AND.

Проще проверять напрямую YAML:

kubectl get netpol <NAME> -o yaml

И смотреть на расположение дефисов в from:

# OR
from:
- namespaceSelector: {...}
- podSelector: {...}

# AND
from:
- namespaceSelector: {...}
  podSelector: {...}

Тестовый запуск из source Pod:

kubectl exec -it <SOURCE-POD> -n <SOURCE-NS> -- nc -zv <TARGET-IP> <PORT>

Если timeout — policy не разрешает. Если ‎succeeded — разрешает.


Шаг 5: DNS, DNS и ещё раз DNS

# Внутри Pod тестируем name resolution
kubectl exec -it <POD> -- nslookup postgres.production.svc.cluster.local
# или
kubectl exec -it <POD> -- getent hosts postgres

Если nslookup тоже timeout-ит — это DNS, а не target Service. Проверяем allow-dns policy:

kubectl get netpol -A | grep dns
kubectl describe netpol allow-dns-egress

Должен быть egress на UDP/53 (минимум) в Pods с label k8s-app=kube-dns в kube-system.

Если нет — добавьте policy из урока 03.


Шаг 6: Обе стороны разрешают?

Если у source policy запрещает egress, и/или у target policy запрещает ingress — обе должны разрешить конкретный flow.

# Какие policies применяются к Pod
kubectl describe pod <POD> | grep -A5 "Labels"
kubectl get netpol -n <NS> -o json | jq '.items[] | select(.spec.podSelector.matchLabels // {} | to_entries | all(.value == "ALLOWED-LABEL-VALUE"))'

# Проще — все netpol в ns:
kubectl get netpol -n <NS>
# И смотрим describe на каждый, у которых podSelector сматчится с labels Pod

Стратегия: если есть default-deny-all в namespace, нужны как минимум три policy для каждого flow:

  1. ingress на target (от source);
  2. egress на source (к target);
  3. egress на source (к kube-dns).

Часто люди забывают вторую или третью, и удивляются «policy есть, но не работает».


kubectl describe networkpolicy: что читать

Name:         api-allowlist
Namespace:    production
Created on:   2026-05-13 10:00:00 +0000 UTC
Labels:       <none>
Annotations:  <none>
Spec:
  PodSelector:     app=api
  Allowing ingress traffic:
    To Port: 8080/TCP
    From:
      PodSelector: app=web
  Allowing egress traffic:
    To Port: 5432/TCP
    To:
      PodSelector: app=postgres
  Policy Types: Ingress, Egress

Что проверять в порядке:

  1. PodSelector — к каким Pods применяется (Шаг 2).
  2. Policy Types — Ingress, Egress или оба (Шаг 3).
  3. Allowing ingress / egress traffic — список разрешённых rules. Внимательно на отступы From: вложенные блоки = AND, новый блок без отступа = OR.

Если какого-то rule нет в kubectl describe — значит API server его не разобрал, проверяйте YAML.


Типичные ошибки CKAD

Топ 5 ошибок при работе с NetworkPolicy
1. Забыли policyTypesНаписали egress блок без [Egress] в policyTypes. Блок может тихо игнорироваться. Всегда указывайте явно.
2. Забыли DNSПрименили deny-all-egress. Pods не резолвят Service names. App падает с name resolution failure. Решение: всегда после deny-all сразу allow-dns.
3. AND vs ORНаписали список из двух peers (OR), хотели одну строгую AND-комбинацию. Получили слишком permissive policy. Или наоборот — собрали AND, когда нужен был OR.
4. PodSelector не matchesОпечатка в label, label на Pod не выставлен, Pod в другом namespace. Policy висит, но к Pod не применяется. kubectl describe netpol + kubectl get pod --show-labels.
5. CNI не поддерживаетУстановлен Flannel без Calico, или default bridge. Policy создаётся, но ничего не делает. Это диагностируется в первую очередь — kubectl get pods -n kube-system.

Канонический CKAD-сценарий: «разрешить app к одному Service + DNS»

Задача: в namespace app есть Pod с label app=consumer. Создать NetworkPolicy так, чтобы:

  1. Consumer мог достучаться только к Pod с label app=api в namespace backend на :8080.
  2. Consumer мог разрешать DNS.
  3. Никакого другого egress.

Решение — одна policy с двумя egress rules:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: consumer-egress
  namespace: app
spec:
  podSelector:
    matchLabels:
      app: consumer
  policyTypes:
  - Egress
  egress:
  # Разрешить к api Service в backend
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: backend
      podSelector:
        matchLabels:
          app: api
    ports:
    - port: 8080
      protocol: TCP
  # Разрешить DNS
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - port: 53
      protocol: UDP

Что критично:

  • policyTypes: [Egress] — явно, обязательно.
  • namespaceSelector + podSelector в одном peer (AND): только Pods api в backend, а не «или api в любом ns, или Pods в backend».
  • DNS — UDP/53. Без него consumer не resolveт api.backend.svc.

Шаги проверки после apply:

kubectl exec -it consumer-pod -n app -- nslookup api.backend.svc.cluster.local
# должно работать

kubectl exec -it consumer-pod -n app -- nc -zv api.backend 8080
# должно работать

kubectl exec -it consumer-pod -n app -- nc -zv google.com 443
# должно timeout-ить — это deny по умолчанию

Инструменты сверх kubectl

  • calicoctl — для Calico-кластеров. calicoctl get globalnetworkpolicy, calicoctl get hostendpoint, плюс Calico-specific объекты.
  • cilium connectivity test — Cilium-агент в namespace cilium-test поднимает тестовые Pods и автоматически проверяет matrix всех flow. Очень удобно для smoke-test policy после изменения.
  • kubectl np-viewer pluginkubectl np-viewer pod <POD> показывает effective ingress/egress правила для конкретного Pod в человекочитаемом виде.
  • netshoot image (nicolaka/netshoot) — заранее установлены tcpdump, dig, mtr, nmap, curl. Запускаем как ephemeral Pod и debug.
kubectl run debug --rm -it --image=nicolaka/netshoot --restart=Never \
  -n production -l app=consumer -- bash
# Теперь Pod в namespace production с label app=consumer — applies те же policies, что и реальный consumer

Важный приём: запускайте debug Pod с теми же labels, что target Pod. Тогда policy будет действовать на него, и тесты дают честный ответ. Без labels debug Pod останется non-isolated и будет ходить куда угодно.


Killer момент: kubectl exec — это не Pod-network трафик

Когда вы делаете:

kubectl exec -it pod -- nc -zv target 80

Цепочка:

  1. ваш CLI → API server (HTTPS, через kubeconfig auth);
  2. API server → kubelet на узле Pod (HTTPS, через bearer token);
  3. kubelet → CRI socket → container runtime;
  4. runtime открывает exec session в namespace контейнера;
  5. внутри session запускается nc.

Сам канал «CLI → Pod» не идёт через сеть кластера. Policy его не видит. Если вы пишете NetworkPolicy «запретить весь ingress на Pod» — exec продолжит работать, потому что это control-plane operation.

Что policy видит: только nc → target. То есть исходящий трафик от Pod к target внутри сети — это и есть Pod egress, policy на него действует.

TIP

Если хотите запретить exec — это RBAC, не NetworkPolicy. pods/exec subresource в Role/RoleBinding. NetworkPolicy фильтрует dataplane трафик между Pods, не control-plane операции через API server.

Это часто путает CKAD-кандидатов: «применили deny-all, но я могу зайти в Pod» — да, потому что exec идёт через API server, не через Pod-сеть. Тестируйте Pod-Pod трафик через nc/wget изнутри Pod, а не через kubectl exec к target.


Минимальная шпаргалка команд

# Все policies в namespace
kubectl get netpol -n <NS>

# Подробно
kubectl describe netpol <NAME> -n <NS>

# Labels Pod
kubectl get pod <NAME> --show-labels

# Тестовый Pod с labels
kubectl run test --rm -it --image=nicolaka/netshoot --restart=Never \
  -l app=consumer -- bash

# Тесты внутри Pod
nslookup api.backend.svc.cluster.local
nc -zv <IP> <PORT>
wget -O- --timeout=3 http://api:8080/healthz
dig +short kube-dns.kube-system.svc

# Labels Pods kube-dns
kubectl get pods -n kube-system -l k8s-app=kube-dns

# Удалить все policies в ns (для быстрого reset на CKAD)
kubectl delete netpol --all -n <NS>

Проверка знанийKnowledge check
kubectl exec в Pod показывает, что nc -zv target 80 работает. Но реальное приложение в Pod не может подключиться. Что проверить?
ОтветAnswer
Скорее всего — DNS. nc -zv с IP-адресом работает, потому что не нужен DNS. Приложение использует имя Service, делает getaddrinfo, DNS-запрос блокируется egress policy. Тест: kubectl exec -- nslookup api или getent hosts api. Если name resolution failed — добавить allow-dns egress.
Проверка знанийKnowledge check
Применили deny-all ingress на Pod. Но kubectl exec в Pod продолжает работать. Это баг policy?
ОтветAnswer
Нет. kubectl exec идёт через API server → kubelet → CRI, не через Pod network. NetworkPolicy фильтрует dataplane (Pod-Pod трафик), не control-plane операции через API server. Ограничение exec — это RBAC (pods/exec subresource), не NetworkPolicy.
Проверка знанийKnowledge check
Policy создана с правильным podSelector, но Pods не блокируются. Самая частая причина?
ОтветAnswer
Labels Pod не matchятся с podSelector. Проверяем kubectl get pod --show-labels и kubectl describe netpol PodSelector. Часто — опечатка в label (App vs app), забыли выставить label при создании Pod, Pod в другом namespace. Selector mismatch — policy не применяется, Pod остаётся non-isolated.
Проверка знанийKnowledge check
CKAD: разрешить Pod consumer (ns app) доступ только к api (ns backend) :8080 и DNS. Один или несколько NetworkPolicy?
ОтветAnswer
Достаточно одной policy на consumer с policyTypes [Egress] и двумя rules: первый — to namespaceSelector backend + podSelector api на 8080/TCP (AND в одном peer), второй — to namespaceSelector kube-system + podSelector k8s-app=kube-dns на 53/UDP. Всё, что не matchится — DROP по deny semantics.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. kubectl exec -it app-pod -- nc -zv 10.0.5.10 5432 — успех. Но реальное приложение в Pod выдаёт connection failed. Что проверить?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5