Как разделить трафик

По умолчанию все прокси в кластере имеют роль default. Это создаёт проблему «шумных соседей»: один пользователь, запустивший тяжёлую операцию чтения, может перегрузить прокси и замедлить работу всех остальных, включая запросы веб-интерфейса.

Решение — разбить прокси на группы с помощью ролей. Это позволит разделить нагрузку между лёгкими и тяжёлыми запросами, а также изолировать ресурсы разных проектов и команд.

В этой статье показано, как:

Разделение лёгкого и тяжёлого трафика (для HTTP)

На практике HTTP-прокси обычно делят на две функциональные группы:

  • Control-прокси (обычно с ролью control) — обслуживают «лёгкие» запросы: просмотр веб-интерфейса, листинг директорий, создание/удаление таблиц, работа с метаинформацией.
  • Data-прокси (обычно с ролью default) — обслуживают «тяжёлые» запросы: чтение и запись таблиц или файлов.

Для отказоустойчивости рекомендуется, чтобы каждой роли соответствовало несколько реплик прокси.

Задать роли можно в спецификации Ytsaurus:

spec:
  httpProxies:
    # Тяжёлая группа (по умолчанию принимает Data-трафик)
    - role: default
      instanceCount: 5
      serviceType: NodePort
    # Контрольная группа (UI и метаданные)
    - role: control
      instanceCount: 2
      serviceType: LoadBalancer # Удобно для Ingress

Примечание

Имя роли становится частью имени K8s-пода (например, hp-control-0). Поэтому используйте только DNS-совместимые имена (строчные буквы, цифры и дефисы). Символ подчеркивания (_) использовать нельзя.

Как это работает:

  • Оператор создаёт две независимые группы подов (StatefulSet) с лейблами control и default.
  • Оператор автоматически создаёт Kubernetes-сервис (Service) для каждой группы. В селекторе сервиса http-proxies-control прописан фильтр: «обслуживать только поды с меткой control».
  • Вы настраиваете Ingress/DNS на сервис группы control. Клиенты будут автоматически получать адреса Data-прокси через механизм Discovery и отправлять тяжёлые запросы напрямую на них.

Примечание

При создании Service или Ingress вручную (без участия оператора) убедитесь, что в Label Selector сервиса прописан правильный фильтр по метке роли. Проверить лейблы на запущенных подах можно командой kubectl get pods --show-labels. Без этого фильтра балансировщик будет отправлять лёгкие запросы на все прокси подряд, включая Data-прокси.

Особенности HTTP доступа и Ingress

Для HTTP-прокси часто используют Ingress-контроллеры для терминации SSL и маршрутизации. Если Ingress используется только для Control-прокси, а Data-прокси выставлены через NodePort (как описано выше), схема работает эффективно.

Если же весь трафик требуется направить через Ingress (включая Data), придётся решить несколько проблем:

  • Двойная балансировка и потеря эффективности.
  • Сложные правила Ingress для каждого пода data-прокси отдельно (для прямой адресуемости).
  • Настройка липких сессий для корректной работы с транзакциями.

Проблема липких сессий

При использовании Ingress для data-прокси важно учитывать особенности работы с транзакциями. Транзакции в YTsaurus создаются на конкретном прокси, и все последующие запросы в рамках одной транзакции должны попадать на тот же самый прокси. Вызов через Ingress-контроллер случайным образом может привести к ошибкам вида "Transaction not found" или "Invalid transaction state". Чтобы избежать этого, настройте липкие сессии в вашем Ingress-контроллере.

Настройка липких сессий зависит от используемого Ingress-контроллера:

  • NGINX Ingress: используйте аннотацию nginx.ingress.kubernetes.io/affinity: "cookie"
  • Traefik: настройте stickiness в конфигурации сервиса
  • HAProxy Ingress: используйте haproxy.org/cookie-persistence
Пример для NGINX Ingress с липкими сессиями

Этот Ingress настраивает NGINX, чтобы он помечал клиента специальной cookie и всегда отправлял его на один и тот же под прокси.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: yt-data-proxies
  annotations:
    # Включаем липкие сессии: NGINX запомнит, на какой под отправить клиента
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-name: "yt-proxy-id"
    nginx.ingress.kubernetes.io/session-cookie-max-age: "14400"
spec:
  rules:
  - host: yt-data.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: http-proxies-default
            port:
              number: 80

Про TLS

Важно

При выставлении Data-прокси напрямую (через NodePort) трафик не проходит через Ingress, а значит, передается без TLS-шифрования.

Для высоконагруженных инсталляций, если нет жёстких требований к шифрованию, оптимальной остаётся раздельная схема:

  • используйте Ingress только для Control-трафика (для удобного доступа к UI, лёгким API-запросам и HTTPS);
  • Data-прокси публикуйте напрямую через NodePort или LoadBalancer с настроенным механизмом Discovery (трафик пойдёт открыто, но максимально производительно).

Если требования безопасности диктуют обязательное использование шифрования для внешних клиентов, есть два пути:

  • направлять весь трафик через Ingress (с настроенными липкими сессиями), принимая во внимание возможное снижение производительности,
  • либо настраивать балансировщики (Network Load Balancer) или внутренний TLS в самом кластере.

Изоляция проектов (для RPC)

Для RPC-прокси чаще применяется деление по проектам. Например, можно создать выделенную группу прокси с ролью project-a и выдать доступ к ней только конкретному пользователю.

spec:
  rpcProxies:
    # Поды для общих нужд
    - role: default
      instanceCount: 2
      serviceType: NodePort
    # Выделенные поды под проект Project A
    - role: project-a
      instanceCount: 2
      serviceType: NodePort

Чтобы клиент начал работать с выделенной группой прокси, в коде приложения (или в настройках CLI) нужно явно указать proxy_role. Если этого не сделать, клиент по умолчанию пойдёт в роль default.

Пример для Python
# Пример клиента Python, жестко привязанного к группе
client = yt.YtClient(proxy="yt.cluster", config={"proxy_role": "project-a"})
Пример для CLI
# Можно передать через переменную окружения перед запуском скриптов
export YT_CONFIG_PATCHES='{proxy_role="project-a"}'
yt list //home

Липкие сессии (sticky sessions) — механизм балансировки, при котором Ingress-контроллер привязывает клиента к конкретному серверу (поду) на время всей сессии. При первом обращении балансировщик выдаёт клиенту специальную cookie-метку, благодаря которой все последующие запросы гарантированно попадают на тот же самый под.