Перейти к основному содержимому

Создание балансировщика из Kubernetes

В managed Kubernetes TatNet достаточно создать Service с type: LoadBalancer. Встроенный cloud-controller-manager (CCM) создаст балансировщик TatNet в проекте кластера и запишет его публичный IP в status.loadBalancer.ingress. Отдельно устанавливать CCM или передавать API-ключ в манифест не нужно.

Один Service создаёт один балансировщик. Каждый порт Service становится слушателем TCP, который отправляет трафик на NodePort узлов кластера, а Kubernetes направляет его в поды по selector. Поддерживается TCP; UDP и SCTP не поддерживаются. Для HTTPS можно передавать TCP-трафик на порт приложения или ingress-контроллера, который сам завершает TLS.

Подготовка​

Нужны работающий кластер TatNet с готовыми рабочими узлами, его kubeconfig, kubectl и curl. Скачайте kubeconfig из панели и укажите путь к нему:

export KUBECONFIG="$HOME/Downloads/tatnet-kubeconfig.yaml"
kubectl config current-context
kubectl get nodes

Убедитесь, что выбрали нужный кластер. Пример ниже создаёт тестовые поды и платный балансировщик; после проверки удалите их. Узлы балансировщика оплачиваются отдельно от узлов Kubernetes.

1. Создайте приложение и Service​

Сохраните следующий манифест в файл lb-demo.yaml. Namespace lb-demo должен быть свободен: он используется только для этого примера.

apiVersion: v1
kind: Namespace
metadata:
name: lb-demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: lb-demo
spec:
replicas: 2
selector:
matchLabels:
app: lb-demo-web
template:
metadata:
labels:
app: lb-demo-web
spec:
containers:
- name: nginx
image: nginx:stable-alpine
ports:
- name: http
containerPort: 80
readinessProbe:
httpGet:
path: /
port: http
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 500m
memory: 128Mi
---
apiVersion: v1
kind: Service
metadata:
name: web
namespace: lb-demo
annotations:
tatnet/lb-node-count: "2"
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
app: lb-demo-web
ports:
- name: http
protocol: TCP
port: 80
targetPort: http
kubectl apply -f lb-demo.yaml
kubectl -n lb-demo rollout status deployment/web --timeout=300s

port: 80 — порт публичного балансировщика, targetPort: http — именованный порт контейнера. Kubernetes сам выделяет nodePort: не отключайте allocateLoadBalancerNodePorts и не задавайте loadBalancerClass, чтобы Service обрабатывал встроенный CCM. В примере externalTrafficPolicy: Local сохраняет IP клиента до пода. Балансировщик проверяет NodePort и направляет новые соединения только на узлы с локальными готовыми endpoint. Обычный TCP Service с CCM 0.6.0 использует нативный L4-путь без PROXY protocol.

2. Дождитесь внешнего IP​

kubectl -n lb-demo wait service/web \
--for=jsonpath='{.status.loadBalancer.ingress[0].ip}' --timeout=600s
kubectl -n lb-demo get service web

Пока балансировщик создаётся, EXTERNAL-IP имеет значение <pending>. CCM публикует адрес после перехода балансировщика в состояние active или degraded. Наличие IP ещё не подтверждает готовность приложения: её проверим HTTP-запросом. Балансировщик также появится в панели проекта с привязкой к кластеру.

3. Проверьте HTTP (смоук)​

Запускайте проверку с компьютера, имеющего доступ к публичному IP:

LB_IP=$(kubectl -n lb-demo get service web \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
test -n "$LB_IP"
curl --noproxy '*' --fail --show-error --silent \
--connect-timeout 5 --max-time 15 \
--retry 12 --retry-delay 5 --retry-all-errors \
"http://${LB_IP}/" -o lb-demo-response.html
grep -F 'Welcome to nginx!' lb-demo-response.html

Ожидаемый результат — успешный HTTP-ответ и строка Welcome to nginx!. Эта проверка проходит весь путь: публичный IP → балансировщик TatNet → NodePort → Service → готовый под nginx. Для команды повтора нужен curl 7.71.0 или новее.

Внутренний балансировщик​

Для второго ingress-контроллера с приватным адресом создайте отдельный Service в его namespace. В отличие от обычного ClusterIP, это управляемый балансировщик TatNet с нативным L4-путём и health checks OVN.

apiVersion: v1
kind: Service
metadata:
name: ingress-internal
namespace: ingress-internal
annotations:
tatnet/lb-scheme: "internal"
tatnet/lb-node-count: "2"
spec:
type: LoadBalancer
externalTrafficPolicy: Local
selector:
app: haproxy-internal
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
- name: https
port: 443
targetPort: 443
protocol: TCP

Namespace и поды HAProxy должны уже существовать; замените selector и targetPort на значения своего Helm-чарта. Если чарт позволяет задавать аннотации Service через values, передайте tatnet/lb-scheme: internal там, чтобы Helm сохранял настройку при обновлениях.

В EXTERNAL-IP Kubernetes покажет приватный адрес из сети узлов кластера: название столбца не означает доступность из интернета. Публичный IP не выделяется и не оплачивается. Адрес доступен из сети этого кластера; в режиме VPC балансировщик получает адрес из своей VPC. Правила доступа других сетей этой аннотацией не открываются.

Для публичного ingress создайте другой Service с tatnet/lb-scheme: "internet-facing". Без аннотации используется публичный режим, чтобы сохранять поведение существующих Service.

Поддерживается и аннотация AWS service.beta.kubernetes.io/aws-load-balancer-scheme с теми же значениями — это удобно для переноса Helm values. Если заданы обе аннотации, значения должны совпадать. Неверное или конфликтующее значение останавливает создание: оно не заменяется публичным режимом.

Режим задаётся при создании. Чтобы поменять его, пересоздайте Service; приватный IP при этом может измениться. Для внутреннего HTTPS используйте TLS в ingress-контроллере, загруженный сертификат либо DNS-01. Проверка HTTP-01 через интернет для внутреннего балансировщика недоступна.

Проверку выполняйте из своего кластера, например из существующего пода с curl:

kubectl -n ingress-internal wait service/ingress-internal \
--for=jsonpath='{.status.loadBalancer.ingress[0].ip}' --timeout=600s
INTERNAL_IP=$(kubectl -n ingress-internal get service ingress-internal \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
# Замените namespace, client-pod и имя внутреннего сайта на свои.
kubectl -n app exec client-pod -- curl --fail --show-error \
--connect-timeout 5 --max-time 15 \
-H 'Host: internal.example.com' "http://${INTERNAL_IP}/"

Убедитесь отдельно, что внутренний сайт недоступен из интернета. Учтите, что Cilium может направлять обращения подов к IP LoadBalancer прямо в Service: для проверки именно пути через OVN нужен клиент в той же приватной сети вне Kubernetes.

Метрики L4​

В панели доступны активные соединения, новые соединения в секунду и входящий / исходящий трафик в байтах в секунду. Учёт ведётся на сетевом пути балансировщика, включает IP/TCP-заголовки и исключает служебные проверки доступности backend-узлов. HTTP-запросы и коды ответов доступны у балансировщиков, которые обрабатывают HTTP.

Если данные с одного из сетевых узлов отсутствуют или устарели, на графике появляется пропуск: неполная сумма не показывается как реальная нагрузка. История начинается с момента включения сбора метрик.

При первом включении учёта байтов существующие соединения продолжают работать. Пока они не завершатся, график трафика может показывать состояние прогрева; метрики соединений уже доступны. Новые балансировщики получают полный учёт с самого начала.

Аннотации Service​

Все значения аннотаций задавайте строками. Они применяются ко всем портам данного Service.

АннотацияЗначениеДействие
tatnet/lb-scheme"internal" или "internet-facing"Приватный или публичный адрес. Режим неизменяемый; неверное значение блокирует создание.
tatnet/lb-node-count"1", "2", "3"Число узлов балансировщика. По умолчанию CCM использует 2; аннотация позволяет изменить число и после создания.
tatnet/lb-preserve-client-ip"true" или "false"Нативное сохранение адреса клиента; по умолчанию включено для TCP Service без PROXY. Для HTTP health checks и stickiness задайте "false".
tatnet/lb-health-check-pathНапример, "/healthz"HTTP-проверка на NodePort вместо TCP-проверки в режиме прокси. Приложение должно отвечать по этому пути.
tatnet/lb-proxy-protocol"none", "v1" или "v2"Передаёт PROXY protocol бэкенду. Включайте только если принимающий сервер настроен его читать; обычный nginx из примера этого не делает.
tatnet/lb-stickiness"source_ip"Закрепляет соединения одного исходного IP за целью балансировщика. Это узел Kubernetes; привязка к конкретному поду этим не гарантируется.

Путь проверки должен начинаться с / и содержать только символы, разрешённые шаблоном ^/[A-Za-z0-9._~/=&?-]*$. Неверное значение tatnet/lb-scheme блокирует создание балансировщика. Неверные и противоречивые настройки сохранения IP и PROXY protocol блокируют применение. Для остальных известных аннотаций CCM показывает событие InvalidAnnotation и использует значение по умолчанию. Неизвестные имена аннотаций не настраивают балансировщик. Используйте префикс tatnet/; прежний tatnet.ru/ больше не поддерживается. Нативный режим доступен с CCM 0.6.0. Подробнее — реальный IP клиента.

Например, изменить число узлов:

kubectl -n lb-demo annotate service web tatnet/lb-node-count="3" --overwrite

Если проверка не проходит​

kubectl -n lb-demo describe service web
kubectl -n lb-demo get events --sort-by=.metadata.creationTimestamp
kubectl -n lb-demo get pods -l app=lb-demo-web -o wide
kubectl -n lb-demo get endpointslices -l kubernetes.io/service-name=web
  • EXTERNAL-IP остаётся <pending>. Проверьте события Service, готовность рабочих узлов и состояние балансировщика в панели. Во время создания CCM повторяет попытки; ошибки API или отсутствие подходящих узлов требуют диагностики.
  • IP есть, но HTTP не отвечает. Проверьте selector, targetPort, готовность подов и адреса в EndpointSlice. При ImagePullBackOff сначала устраните проблему скачивания образа. PROXY protocol должен быть выключен для этого примера.
  • InvalidAnnotation. Исправьте значение аннотации согласно таблице.
  • ConfigRejected при порте 80. Порт 80 допустим, но если к этому балансировщику вручную прикреплён сертификат HTTP-01, порт резервируется для ACME. Отсоедините этот сертификат, используйте DNS-01 либо измените порт Service. Одно ожидание повторной попытки конфликт не устранит.

4. Удалите тестовые ресурсы​

Сначала удалите Service и дождитесь завершения: его finalizer позволяет CCM удалить связанный балансировщик. Затем удалите namespace с приложением.

kubectl -n lb-demo delete service web --wait=true --timeout=600s
kubectl delete namespace lb-demo --wait=true --timeout=300s

Удаление балансировщика в платформе асинхронное: после исчезновения Service он может некоторое время оставаться в состоянии deleting. В панели дождитесь, чтобы связанный балансировщик исчез. Если Service завис в удалении, проверьте события и доступность CCM; не снимайте finalizer вручную, иначе платный балансировщик может остаться без владельца в Kubernetes. Удалять сам балансировщик через панель для этого примера не нужно: пока Service существует, CCM может создать его заново с новым IP.