Модуль 3. Сеть и публикация сервисов BI
В BI/DWH нам нужно:
- Соединить поды между собой (CNI).
- Стабильно адресовать сервисы (Service/ClusterIP/Headless).
- Публиковать веб-интерфейсы наружу (Ingress/Gateway + TLS/WAF).
- Разделять «внешние» и «внутренние» витрины, ограничивать доступ (IP-списки, IdP/SSO, mTLS).
- Обеспечивать наблюдаемость, стабильность, производительность (балансировка, таймауты, загрузки файлов, WebSocket).
CNI: Calico, Flannel, Cilium — что выбирать и почему
Flannel
- Простой оверлей (VXLAN/host-gw), быстрый старт, минимум функций.
- Нет нативных L3-политик, для сложной сегментации/безопасности не хватает.
Calico
- L3-сеть, NetworkPolicy (и расширенные), BGP-пиринг (можно без оверлея), поддержка VXLAN/IPIP.
- Хорошо подходит для on-prem: можно интегрировать с сетевым ядром (BGP), делать fine-grained политики между namespace/подами.
- Компромисс «функции ↔ сложность» — оптимальный дефолт для большинства BI/DWH.
Cilium (eBPF)
- Датаплейн на eBPF, может заменить kube-proxy; L3/L4/L7-политики, observability, высокопроизводительная балансировка.
- Плюсы: меньше оверхеда, лучше телеметрия, продвинутые функции (HTTP-aware политики), есть собственный сервис-меш без сайдкаров.
- Минусы: чуть выше порог входа, внимательная совместимость ядра/версий.
Рекомендация:
- Стартуем с Calico для предсказуемой политики и BGP/overlay на ваш выбор.
- Если цели — высокая производительность/наблюдаемость L7 и отказ от kube-proxy — Cilium.
Базовые примитивы публикации: Service и балансировка
Service типы:
- ClusterIP — только внутри кластера (дефолт).
- NodePort — публикует порт на всех нодах; просто, но мало управляемо.
- LoadBalancer — отдаёт виртуальный IP от внешнего балансировщика (в облаках встроено, on-prem — MetalLB).
- Headless (clusterIP: None) — резолв имен в конкретные Pod’ы (важно для StatefulSet’ов).
On-prem:
- Если нет внешнего LB — ставим MetalLB (L2 или BGP) и получаем LoadBalancer-IP для Ingress-контроллера/сервисов.
- Для сохранения исходного IP используем externalTrafficPolicy: Local (важно для IP-allowlist/WAF правил).
Сессии и липкость:
- Часть BI требует сессионной липкости. На L7 решается Ingress-контроллером (cookie-sticky), на L4 — балансировщиком. Проверяйте требования конкретного BI.
Ingress-контроллеры и Gateway API
Ingress-контроллеры:
- NGINX Ingress — де-факто стандарт on-prem, много аннотаций, ModSecurity/WAF, WebSocket/gRPC, богатые опции таймаутов/лимитов.
- HAProxy Ingress — высокая производительность, расширенные балансировщики L4/L7.
- Traefik — простой, удобный, встроенный дашборд, OIDC middleware.
- Envoy/Contour/Gloo — мощные L7-возможности, близки к Gateway API.
Gateway API — «следующее поколение» поверх Ingress: разделение ролей (GatewayClass/Gateway/HTTPRoute), удобнее для мульти-тенантности и «внутренний/внешний» контур. В новых внедрениях стоит проектировать сразу под Gateway API (или выбирать контроллер с поддержкой обоих интерфейсов).
TLS: сертификаты, алгоритмы, ротация
cert-manager автоматизирует выдачу/продление:
- ClusterIssuer/Issuer: ACME (Let’s Encrypt), внутренний CA, самоподписанный для теста.
- Для on-prem часто используется внутренний CA (корпоративный), чтобы браузеры внутри доверяли.
- Обязательно: modern ciphers, OCSP stapling, HSTS (внешние витрины).
TLS-терминация:
- Обычно — на Ingress (L7).
- Если BI сам терминирует TLS (и вам нужна сквозная криптография) — используйте TLS-passthrough (ограничения по L7-фичам) или TLS-перешифровку до бэкенда.
L7-маршрутизация и «тонкая настройка» Ingress
Правила: host-based (superset.company.ru), path-based (/bi/, /api/), header-based (по группам/версиям).
WebSocket/gRPC: включаем соответствующие флаги/аннотации.
Размеры и таймауты: proxy-body-size, proxy-read-timeout, proxy-send-timeout — подстраиваем под экспорт/импорт файлов.
Перезапись путей: rewrite-target при публикации приложений не из корня.
Ответы/заголовки безопасности: CSP/X-Frame-Options/X-Content-Type-Options — в шаблоне контроллера или аннотациями.
WAF: ModSecurity + OWASP CRS
Для NGINX Ingress можно включить WAF:
- nginx.ingress.kubernetes.io/modsecurity-enable: "true"
- nginx.ingress.kubernetes.io/modsecurity-snippet: с профилем OWASP CRS.
- Настраиваем anomaly scoring, исключаем ложные срабатывания на эндпоинтах импорта/SQL-параметров BI, задаем лимиты на тело запроса, медленные запросы/slowloris.
Советы: сначала — «детект-режим» + логирование, соберите ложноположительные, только потом включайте блокировку.
Внутренние vs внешние витрины BI
Два инстанса Ingress-контроллера (или два Gateway) — ingressClass: external/internal.
- Внешний — вынесен через LoadBalancer/MetalLB, доступ из Интернета/VPN, строгие WAF/SSO, лимиты, HSTS.
- Внутренний — только из корпоративной сети/VPN; можно разрешить шире размеры запросов/экспортов.
Сегментация:
- Отдельные namespace/сетевые политики; default-deny + адресное разрешение к БД/объектному хранилищу (S3).
- IP-allowlist на внешнем Ingress: nginx.ingress.kubernetes.io/whitelist-source-range.
SSO/аутентификация на краю:
- OIDC/SAML (oauth2-proxy, dex, встроенные middleware контроллеров).
- Можно пускать только пользователей из определенных групп (claims-based RBAC).
Файлы и экспорты:
- Для тяжелых отчетов лучше выдавать pre-signed S3 URLs (нет нагрузки на Ingress/BI-под).
- Страничные/stream-ответы вместо «все в память».
Ограничение доступа и mTLS внутри
Слоевое ограничение:
- Ingress: IP-списки, SSO, WAF.
- Сеть: NetworkPolicy (Calico/Cilium) — default-deny ingress/egress, только нужные CIDR/сервисы.
- Приложение: RBAC в BI, row-level security, каталоги.
mTLS внутри кластера (простой путь): сервис-меш
- Linkerd или Cilium service mesh: включаем в namespace аннотацией → автоматически шифруется трафик Pod↔Pod, получаем сертификаты/ротацию без боли.
- Плюсы: минимум ручной криптографии; Минусы: сайдкары (кроме Cilium), надо понимать протоколы/порты.
mTLS между Ingress и бэкендом (ручной путь):
- У бэкенда — серверный сертификат; Ingress выступает TLS-клиентом с сертификатом (nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" и proxy-ssl-secret с клиентским ключом).
- Сложнее в сопровождении, но иногда требуется регуляторикой.
Наблюдаемость и алертинг сети
- Метрики Ingress: qps, p50/p95/p99 latency, 4xx/5xx, размер/время тела запроса, открытые соединения.
- Логи доступа в централизованный лог (Loki/EFK) с полями: user, group, endpoint, размер, тайминг, upstream status.
- Трассировки (OpenTelemetry/Zipkin/Tempo) — полезны для «медленных дашбордов».
- Алерты: рост 5xx, истекающие TLS-сертификаты, рост 499 (клиент закрыл), превышения лимитов тела запроса.
Риски и как их снижать
-
Случайная публикация внутрянки наружу.
Митигировать: два класса Ingress, отдельные контроллеры/LoadBalancer-пулы, CI-проверки аннотаций/хостов, review-правила. -
Слабый TLS (старые шифры), просроченные сертификаты.
Митигировать: cert-manager с алертами, современный сipher-suite, HSTS, OCSP stapling. -
Вырезанные NetworkPolicy.
Митигировать: default-deny и «политики по умолчанию» в каждом namespace, шаблоны для команд. -
Неправильные таймауты/лимиты тела запроса → обрывы экспорта/импорта.
Митигировать: установить обоснованные proxy-* параметры, для тяжёлых файлов — pre-signed S3. -
Ложноположительные WAF.
Митигировать: фазовый запуск: detect → tune → block, исключения на конкретные URI/параметры. -
Сессионная липкость не настроена.
Митигировать: включить sticky-cookie в Ingress или уйти на stateless-сессию (если поддерживается).
Практика: Ingress для Superset/Metabase + базовая mTLS
Предполагаем: кластер уже есть; установлен NGINX Ingress Controller и cert-manager. Для on-prem без облачного LB — MetalLB. Если ничего из этого нет — сначала поднимите их (по вашим стандартам).
Шаг 1. Namespace и общий TLS
kubectl create ns bi
# Кластерный self-signed (для лаборатории)
cat <<'EOF' | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned
spec:
selfSigned: {}
EOF
cat <<'EOF' | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: bi-tls
namespace: bi
spec:
secretName: bi-tls
dnsNames:
- metabase.local
- superset.local
issuerRef:
name: selfsigned
kind: ClusterIssuer
EOF
Шаг 2. Metabase (простой деплой)
apiVersion: apps/v1
kind: Deployment
metadata:
name: metabase
namespace: bi
spec:
replicas: 2
selector: { matchLabels: { app: metabase } }
template:
metadata: { labels: { app: metabase } }
spec:
containers:
- name: metabase
image: metabase/metabase:latest
ports: [{ containerPort: 3000 }]
env:
- name: MB_DB_TYPE
value: h2 # для prod: postgres; вынести в отдельную БД (см. Модуль 2)
readinessProbe: { httpGet: { path: /api/health, port: 3000 }, initialDelaySeconds: 20 }
---
apiVersion: v1
kind: Service
metadata:
name: metabase
namespace: bi
spec:
selector: { app: metabase }
ports: [{ port: 80, targetPort: 3000 }]
type: ClusterIP
Шаг 3. Superset (демо-режим на SQLite для лаборатории)
В проде используйте PostgreSQL для метаданных и официальный Helm-чарт. Ниже — упрощённый «запустилось и показалось».
apiVersion: apps/v1
kind: Deployment
metadata:
name: superset
namespace: bi
spec:
replicas: 1
selector: { matchLabels: { app: superset } }
template:
metadata: { labels: { app: superset } }
spec:
containers:
- name: superset
image: apache/superset:3.1.0
ports: [{ containerPort: 8088 }]
env:
- { name: SUPERSET_SECRET_KEY, valueFrom: { secretKeyRef: { name: superset-secret, key: secret } } }
- { name: _PIP_ADDITIONAL_REQUIREMENTS, value: "" }
command: ["/bin/sh","-c"]
args:
- |
superset db upgrade && \
superset fab create-admin --username admin --firstname A --lastname B --email admin@example.com --password admin && \
superset init && \
gunicorn -w 2 -k gevent --timeout 300 "superset.app:create_app()" -b 0.0.0.0:8088
readinessProbe: { httpGet: { path: /health, port: 8088 }, initialDelaySeconds: 40, periodSeconds: 10 }
---
apiVersion: v1
kind: Secret
metadata: { name: superset-secret, namespace: bi }
type: Opaque
stringData:
secret: "superset-very-secret"
---
apiVersion: v1
kind: Service
metadata:
name: superset
namespace: bi
spec:
selector: { app: superset }
ports: [{ port: 80, targetPort: 8088 }]
type: ClusterIP
Шаг 4. Ingress (TLS, ограничения, базовые лимиты)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: bi-ingress
namespace: bi
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
# пример IP-allowlist для внешней витрины (комментируйте/раскомментируйте по надобности)
# nginx.ingress.kubernetes.io/whitelist-source-range: "192.0.2.0/24,198.51.100.0/24"
# включение ModSecurity (WAF) в режиме обнаружения:
# nginx.ingress.kubernetes.io/modsecurity-enable: "true"
# nginx.ingress.kubernetes.io/modsecurity-snippet: |
# SecRuleEngine DetectionOnly
# Include /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf
spec:
tls:
- hosts: ["metabase.local","superset.local"]
secretName: bi-tls
rules:
- host: metabase.local
http:
paths:
- path: /
pathType: Prefix
backend: { service: { name: metabase, port: { number: 80 } } }
- host: superset.local
http:
paths:
- path: /
pathType: Prefix
backend: { service: { name: superset, port: { number: 80 } } }
Пропишите в корпоративном DNS (или в /etc/hosts лабораторной машины) metabase.local и superset.local на адрес LoadBalancer/Ingress-контроллера.
Шаг 5. Базовая mTLS для внутренней коммуникации (простой путь — сервис-меш)
В лаборатории проще всего включить Linkerd (даёт сквозную mTLS Pod↔Pod без вашей криптографии).
- Установите CLI и сам Linkerd (в тестовом контуре):
linkerd check --pre linkerd install | kubectl apply -f - linkerd check
- Включите авто-инъекцию в ns bi и перезапустите pod’ы:
kubectl annotate ns bi linkerd.io/inject=enabled kubectl rollout restart deploy -n bi
- Проверьте, что трафик между сервисами в ns bi — в mTLS:
linkerd -n bi edges deploy # или linkerd viz tap deploy/metabase
Для прод-контура вместо Linkerd можно использовать Cilium service mesh или другой меш; цель лаборатории — показать идею mTLS без возни с сертификатами.
(Опционально) Шаг 6. OIDC-SSO на краю через oauth2-proxy
- Поднимите oauth2-proxy (в связке с вашим IdP — Keycloak/AD FS).
- Добавьте к Ingress аннотации:
nginx.ingress.kubernetes.io/auth-url: "https://auth.company.ru/oauth2/auth" nginx.ingress.kubernetes.io/auth-signin: "https://auth.company.ru/oauth2/start?rd=$scheme://$host$request_uri"
- На уровне IdP ограничьте группы/claims для доступа.
Чек-лист перед продом (сеть и публикация)
- CNI выбран осознанно (Calico или Cilium), NetworkPolicy включены, базовый default-deny внедрён.
- Два контура публикации: внешний/внутренний (разные ingressClass/LoadBalancer/DNS/Firewall).
- TLS: cert-manager, современные шифры, HSTS (внешне), алерты на expiry.
- Ingress-лимиты и таймауты соответствуют профилю BI (экспорты/импорты/WS).
- Sticky-сессии включены, если нужны продукту.
- WAF в режиме detect→tune→block, заведены исключения.
- mTLS внутри кластера включён (меш) для чувствительных сервисов.
- Логи/метрики/трейсы собраны, алерты на 5xx/latency/сертификаты настроены.
- Внешний доступ ограничен (IP-allowlist, SSO), секреты/ключи в External Secrets/Vault.
Короткие ответы на частые вопросы
- Нужен ли всегда сервис-меш? Нет. Но для mTLS и сквозной наблюдаемости — это самый «безболезненный» способ.
- Можно ли обойтись без WAF? На внутреннем контуре — иногда. На внешнем — лучше включать хотя бы в detect-режиме.
- Ingress или Gateway API? Если строите «надолго» — смотрите в сторону Gateway API. Сегодня Ingress NGINX — рабочий дефолт.




