Hubble: наблюдаемость сетевых потоков внутри Kubernetes

Published: 2026-02-08

Hubble от Cilium — это слой сетевой наблюдаемости, встроенный прямо в eBPF-датаплейн. Каждый пакет, проходящий через кластер — под-под, под-сервис, под-внешний адрес — фиксируется с указанием источника, назначения, вердикта и причины. Никаких sidecar-прокси, никакого сэмплирования, никаких лишних хопов.

Этот пост — полная настройка, использование CLI для типичных сценариев отладки, метрики Prometheus и операционные паттерны, которые мы используем при диагностике сетевых проблем в шестикластерной среде.


Почему Hubble, а не tcpdump или логи сетевых политик

Традиционные инструменты отладки сети Kubernetes имеют серьёзные ограничения:

  • tcpdump требует exec в под или на узел, и нужно заранее знать, на каком узле проходит трафик. При 6 кластерах и 200+ подах на узле — это поиск иголки в стоге сена.
  • Логи NetworkPolicy о дропах отсутствуют в vanilla Kubernetes — пакеты отбрасываются молча, без audit trail.
  • Наблюдаемость через service mesh (Istio, Linkerd) требует внедрения sidecar, что добавляет каждому поду задержку, потребление памяти и сложность.

Hubble записывает все события потоков на уровне ядра через eBPF. Агент работает на каждом узле как часть DaemonSet Cilium — никаких дополнительных подов, никаких sidecar, никаких накладных расходов сверх того, что и так добавляет Cilium. Через hubble-relay данные доступны в масштабе всего кластера.


Компоненты

hubble-agent — встроен в каждый pod с Cilium agent. Экспортирует события потоков по gRPC.

hubble-relay — агрегирует события со всех узлов, предоставляет единый gRPC-эндпоинт для видимости на уровне всего кластера.

hubble-ui — веб-интерфейс для просмотра потоков и построения карт зависимостей сервисов в реальном времени.

Включите все три в Helm values для Cilium:

yamlhubble:
  enabled: true
  metrics:
    enabled:
      - dns:query;ignoreAAAA
      - drop
      - tcp
      - flow
      - icmp
      - http
  serviceMonitor:
    enabled: true
    labels:
      release: kube-prom-stack
  relay:
    enabled: true
    prometheus:
      enabled: true
      serviceMonitor:
        enabled: true
        labels:
          release: kube-prom-stack
  ui:
    enabled: true

labels: release: kube-prom-stack в serviceMonitor — это то, как Prometheus обнаруживает endpoint метрик: kube-prometheus-stack деплоится с label-селектором release=kube-prom-stack.

Метрика dns:query;ignoreAAAA игнорирует AAAA-запросы (IPv6) в метриках DNS, что существенно снижает кардинальность для IPv4-кластеров. Иначе каждый DNS-запрос hostname генерирует две записи метрик.


Hubble CLI

Установите hubble локально и пробросьте порт relay:

bashkubectl port-forward -n kube-system svc/hubble-relay 4245:80 &

# Последние 100 потоков по всему кластеру
hubble observe --last 100

# Отброшенные потоки в namespace
hubble observe --namespace app --verdict DROPPED

# Потоки от конкретного пода
hubble observe --pod app/backend --protocol TCP

# Режим слежения (как tail -f)
hubble observe --follow --namespace default

Или запустите Hubble CLI прямо внутри пода Cilium на любом узле:

bashkubectl exec -n kube-system ds/cilium -- hubble observe --last 50

Это избавляет от настройки port-forward — полезно при реагировании на инцидент, когда нужны ответы быстро.

Фильтр --verdict DROPPED — самый быстрый способ диагностировать, почему трафик не доходит до пода. Частые причины:

  • NetworkPolicy блокирует поток — вердикт покажет POLICY_DENIED
  • Нет активного эндпоинта — вердикт покажет UNKNOWN_CONNECTION
  • Несовпадение порта — вердикт покажет PORT_UNROUTABLE

Формат вывода

По умолчанию вывод человекочитаемый. Для структурированной обработки:

bash# JSON-вывод (для передачи в jq)
hubble observe --last 100 --output json | jq '.flow | select(.verdict == "DROPPED")'

# Подсчёт дропов по source namespace
hubble observe --last 1000 --output json \
  | jq -r 'select(.flow.verdict == "DROPPED") | .flow.source.namespace' \
  | sort | uniq -c | sort -rn

Видимость DNS

Hubble перехватывает DNS-запросы на уровне пода:

bashhubble observe --type l7 --protocol DNS
hubble observe --type l7 --protocol DNS --pod app/backend

Вывод показывает, какой под запрашивал какой hostname и каким был ответ. Незаменимо при отладке недоступности внешнего сервиса — видно, возвращает ли DNS правильный IP, происходит ли запрос вообще, или возвращается NXDOMAIN.

Типичные DNS-проблемы, которые выявляет Hubble:

  • Приложение запрашивает неправильный hostname (жёстко заданный IP или неверная переменная окружения)
  • DNS-разрешение успешно, но IP недоступен (проблема NetworkPolicy, не связанная с DNS)
  • DNS-запросы завершаются по таймауту (перегруженный CoreDNS)

Для проверки производительности CoreDNS:

bash# DNS-запросы к kube-dns от конкретного пода
hubble observe --to-pod kube-system/coredns --protocol DNS --follow

Если от одного пода идёт много повторных запросов — CoreDNS может оказаться узким местом.


Отладка NetworkPolicy

Предположим, трафик от frontend к backend молча отбрасывается. Без Hubble пришлось бы добавлять отладочные логи в приложение и деплоить заново. С Hubble:

bashhubble observe \
  --namespace app \
  --from-pod app/frontend \
  --to-pod app/backend \
  --verdict DROPPED

Если вердикт POLICY_DENIED с reason: INGRESS_DENIED, вы знаете точное направление и какая политика блокирует. Дальше:

bashkubectl get ciliumnetworkpolicy -n app
kubectl get networkpolicy -n app

Найдите политику и скорректируйте селектор ingress. В классическом NetworkPolicy это обычно добавление недостающего селектора from. В CiliumNetworkPolicy можно также ограничивать по L7 — HTTP-методу, пути, заголовкам.

AUDIT-режим для безопасного ввода политик

Режим AUDIT позволяет тестировать ограничивающую политику до её применения:

yamlapiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: restrict-backend
  namespace: app
spec:
  endpointSelector:
    matchLabels:
      app: backend
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
  policyAuditMode: true   # не отбрасывает пакеты, только логирует

Установите policyAuditMode: true, задеплойте, наблюдайте AUDIT-вердикты в Hubble 24 часа, затем уберите флаг для применения. Ужесточение политик без простоя.

bash# Наблюдать audit-вердикты для backend
hubble observe --namespace app --to-pod app/backend --verdict AUDIT --follow

Карта сервисов в Hubble UI

UI рисует граф зависимостей в реальном времени. Каждый узел — это namespace или рабочая нагрузка. Рёбра окрашены по уровню дропов и подписаны частотой запросов.

В нашей настройке UI доступен через ApisixRoute с проверкой группы RBAC — только для внутренних пользователей:

yamlapiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
  name: hubble-ui
  namespace: ingress-apisix
spec:
  http:
    - name: hubble
      match:
        hosts:
          - hubble.infra.example.com
        paths:
          - "/*"
      backends:
        - serviceName: hubble-ui
          serviceNamespace: kube-system
          servicePort: 80

Карта сервисов — первое место, куда мы смотрим после нового деплоя, когда что-то «тормозит». Часто карта показывает новую downstream-зависимость без ограничения частоты запросов, которая теперь получает их шквал. Визуализация делает это сразу очевидным: толстые красные рёбра к одному сервису — этот сервис отбрасывает трафик.


Метрики потоков в Prometheus

С serviceMonitor.enabled: true hubble-relay экспортирует метрики Prometheus в существующую Grafana:

promql# Частота дропов по namespace
rate(hubble_drop_total[5m])

# Частота дропов по причине (POLICY_DENIED, NO_TUNNEL_KEY и т.д.)
sum by (reason) (rate(hubble_drop_total[5m]))

# Частота HTTP-запросов по destination
rate(hubble_http_requests_total[5m])

# Частота HTTP-ошибок (4xx, 5xx)
rate(hubble_http_requests_total{status=~"[45].."}[5m])

# Частота DNS-ошибок
rate(hubble_dns_error_total[5m])

# Частота DNS-запросов по подам (топ-10)
topk(10, rate(hubble_dns_queries_total[5m]))

В нашем кластерном дашборде Grafana есть отдельная строка «Network»: частота дропов по namespace, частота HTTP-ошибок по сервисам, частота DNS-запросов. Она находится между строками «Node» и «App» и часто выявляет первопричину ошибок на уровне приложения раньше, чем их замечает команда разработки.

Алерт на всплески дропов

Полезное правило PrometheusRule:

yaml- alert: NetworkDropSpike
  expr: sum(rate(hubble_drop_total[5m])) by (namespace) > 10
  for: 2m
  labels:
    severity: warning
  annotations:
    summary: "Высокая частота дропов в {{ $labels.namespace }}"
    description: "{{ $value | printf \"%.1f\" }} дропов/сек в {{ $labels.namespace }}"

Срабатывает, если в любом namespace больше 10 дропов в секунду на протяжении 2 минут. Частые причины: неправильно настроенный NetworkPolicy после деплоя или новый сервис, которому не открыт проход через политику.


Вердикты Cilium NetworkPolicy

Каждый поток Hubble имеет вердикт и до трёх вердиктов политик:

Вердикт Значение
FORWARDED пакет прошёл все политики
DROPPED заблокирован NetworkPolicy
ERROR проблема с эндпоинтом
AUDIT был бы заблокирован, политика в режиме аудита
REDIRECTED перенаправлен к прокси (например, L7-политика)

Хранение и производительность

Потоки Hubble хранятся в памяти в кольцевом буфере на каждом узле. Размер буфера по умолчанию — 4096 событий на узел. Для высоконагруженных кластеров увеличьте:

yamlhubble:
  eventQueueSize: 65536
  eventBufferCapacity: 65535

Потоки старше ёмкости кольцевого буфера теряются. Если нужно более длительное хранение, пересылайте потоки во внешнее хранилище через gRPC-экспорт Hubble или используйте метрики Prometheus (с их retention).

При очень высоком трафике (10k+ потоков в секунду) накладные расходы CPU на обработку событий на каждом узле могут стать заметными. Профилируйте с cilium monitor --type drop перед включением всех метрик одновременно — начинайте с drop и dns, добавляйте http и tcp только при необходимости.