Настройка внешнего доступа к YTsaurus в Kubernetes
По умолчанию кластер YTsaurus, развёрнутый в Kubernetes, изолирован от внешней сети. Для публикации сервисов, как правило, используются механизмы LoadBalancer или Ingress. Они хорошо подходят для отдельных веб-сервисов, предоставляя единую точку входа для клиентов, но не обеспечивают масштабирование большого сетевого потока.
Из данного руководства вы узнаете, как:
-
Решить проблему сетевой изоляции — настроить кластер так, чтобы внешние клиенты могли напрямую обращаться к узлам кластера для эффективной записи и чтения больших объёмов данных.
-
Разделить нагрузку между клиентами — изолировать ресурсы разных проектов, а также разделить трафик на «лёгкий» (метаданные, UI) и «тяжёлый» (чтение и запись таблиц).
-
Настроить доступ для SPYT — настроить TCP-проксирование для прямого соединения внешнего драйвера Spark с воркерами внутри кластера.
Обзор прокси
Пользователи взаимодействуют с сервером YTsaurus не напрямую, а через специальные прокси. Это компоненты YTsaurus, которые выступают единой точкой входа и скрывают взаимодействие между компонентами и внутреннюю топологию кластера — например, адреса мастеров и data-нод.
С точки зрения Kubernetes, прокси обычно разворачиваются как StatefulSet, состоящий из нескольких подов. Их количество и выделяемые ресурсы (CPU, RAM) задаются в спецификации оператора YTsaurus, а при запуске кластера каждый под прокси автоматически регистрируется в Кипарисе (в системных директориях //sys/http_proxies и //sys/rpc_proxies).
Прокси в YTsaurus бывают двух типов:
- HTTP-прокси — реализуют HTTP API YTsaurus. Активно используются в SDK, для работы веб-интерфейса и CLI.
- RPC-прокси — реализуют более быстрый бинарный протокол (YT RPC). В первую очередь необходимы там, где требуется низкая латентность запросов (например, при интенсивной потоковой работе с динамическими таблицами). Для всех остальных сценариев рекомендуется использовать HTTP-прокси.
Понятие роли
Прокси можно разбивать на функциональные группы с помощью ролей. Как правило, выделяют две основные группы:
- Контрольные (control-прокси) — обрабатывают «лёгкие» запросы (навигация в UI, работа с метаданными Кипариса). Для них обычно назначается роль
control. - Тяжёлые (data-прокси) — обрабатывают «тяжёлые» запросы (потоковое чтение и запись больших таблиц). В Kubernetes-инсталляциях они чаще всего работают под ролью
default.
Такое разделение позволяет гибко управлять ресурсами кластера: активное чтение огромной таблицы через data-прокси не затормозит работу веб-интерфейса и не помешает другим пользователям просматривать дерево Кипариса.
Технически роль — это строковая метка (атрибут @role в Кипарисе), которая присваивается инстансу прокси при запуске. По умолчанию все прокси в кластере запускаются с ролью default. Назначить роль можно в спецификации Ytsaurus.
Механизм Discovery
При инициализации клиента (SDK) разработчик указывает общий адрес кластера (например, yt.example.com). Обычно за этим адресом стоит балансировщик (Ingress или LoadBalancer), распределяющий запросы между доступными прокси-серверами.
Однако прогонять гигабайты трафика чтения/записи через один центральный балансировщик неэффективно. Чтобы масштабировать сетевой поток, SDK автоматически отправляют тяжёлые запросы на data-прокси напрямую, минуя центральную точку входа. Разработчику не нужно указывать в коде десятки адресов — SDK узнает про них самостоятельно через встроенный механизм Discovery. Он работает следующим образом:
- Перед выполнением тяжёлого запроса SDK отправляет служебный HTTP-запрос
GET /api/v4/discover_proxiesна общий балансировщик. - В ответ сервер возвращает список адресов (FQDN) активных data-прокси.
- SDK выбирает один адрес из списка и отправляет тяжёлый запрос напрямую к этому поду.
Ниже приведена схема работы механизма Discovery при вызове write_table в HTTP-прокси:
Пояснения к схеме
- Клиентская библиотека (SDK) выполняет служебный HTTP-запрос
discover_proxiesна основной публичный адрес кластера (балансировщикyt.example.com). - Балансировщик принимает запрос и перенаправляет его внутрь кластера на один из доступных контрольных прокси-серверов (Control Proxy).
- Контрольный прокси формирует список FQDN активных data-прокси (например,
hp-0.svc.local,hp-1.svc.local) и возвращает его балансировщику. - Балансировщик возвращает этот список клиенту.
- SDK выбирает из списка один конкретный адрес (в нашем примере —
hp-0) и отправляет запрос на запись данных (write_table) напрямую к этому поду, минуя центральный балансировщик. - Между клиентом и data-прокси устанавливается прямое соединение, по которому передаётся поток данных.
Роли прокси в Discovery
При запросе discover_proxies клиент может дополнительно передать требуемую роль. При этом действует логика:
- Если роль указана явно (например,
role=heavy), балансировщик вернёт адреса только выделенных под неё прокси. - Если роль не указать, будут запрашиваться прокси с ролью
default.
Discovery в разных протоколах
- В HTTP используется «ленивый» подход. Запрос к
discover_proxiesвыполняется перед началом чтения или записи файла. - В RPC используется «жадный» подход. Клиент вызывает
discover_proxiesсразу при старте, получает список адресов RPC-прокси и устанавливает с ними постоянные TCP-соединения.
Примечание
В более старых версиях API (< v4) точки входа в сервис Discovery различались.
Отличия в Discovery в API v3 и API v4
В версиях API ниже v4:
- Для получения списка всех HTTP-прокси клиенты обращались к эндпоинту
/v3/entry. - Для получения RPC-прокси — к
/v3/discover_proxies.
Начиная с версии v4, оба типа клиентов используют единый универсальный эндпоинт /api/v4/discover_proxies (с параметром type=rpc для RPC-клиентов).
Почему возникает проблема доступов
В стандартной конфигурации Kubernetes адреса подов являются внутренними (например, hp-0.http-proxies.default.svc.cluster.local).
Когда внешний SDK вызывает discover_proxies, кластер возвращает ему список внутренних FQDN. SDK, находясь за периметром кластера, не может разрешить эти DNS-имена в IP-адреса. В результате лёгкие команды через балансировщик работают успешно, а попытка записать данные завершается различными сетевыми ошибками — от ошибок разрешения DNS-имён до невозможности подключиться (Temporary failure in name resolution, Connection refused, Connection timed out).
Пример: как распознать проблему
Рассмотрим сценарий: кластер YTsaurus развёрнут в Kubernetes, и требуется проверить доступ с локальной машины.
Для быстрого доступа к API контрольных прокси порт открыт через kubectl port-forward:
$ kubectl port-forward service/http-proxies-control-lb 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Выполним лёгкую операцию — создать таблицу.
$ export YT_PROXY=127.0.0.1:8080
$ yt create table //home/my-table
30-56c4-10191-712a11b3
Команда сработала: таблица создана. Механизм Discovery не задействовался, запрос ушёл напрямую на адрес, указанный в переменной YT_PROXY.
Теперь попробуем записать данные в эту таблицу (write-table):
$ echo '{ "id": 0, "text": "Hello" }' | yt write-table //home/my-table --format json
WARNING HTTP PUT request http://hp-0.http-proxies.default.svc.cluster.local/api/v4/write_table failed with error NewConnectionError...
Failed to establish a new connection: [Errno -3] Temporary failure in name resolution
Что произошло:
При выполнении write-table SDK запросил список data-прокси. Кластер вернул внутренний адрес пода: hp-0.http-proxies.default.svc.cluster.local. SDK попытался соединиться с этим FQDN напрямую, но с локальной машины это имя не резолвится.
В качестве временного решения для отладки можно отключить Discovery на клиенте. Сделать это можно через переменные окружения, тогда весь трафик пойдёт через port-forward:
# Через патч конфига:
export YT_CONFIG_PATCHES='{proxy={enable_proxy_discovery=%false}}'
# Или через более короткий и популярный алиас для CLI:
export YT_USE_HOSTS=0
echo '{ "id": 0, "text": "Hello" }' | yt write-table //home/my-table --format json
Если после этого запись прошла успешно — значит, проблема именно в маршрутизации Discovery.
См. также
- Как решить проблему сетевой изоляции
- Как разделить нагрузку между клиентами
- Как настроить доступ для SPYT
- FAQ
LoadBalancer и Ingress пропускают весь трафик через одну точку входа, но при больших объёмах данных это становится узким местом.
Чтобы масштабировать нагрузку, клиенты должны подключаться к узлам кластера напрямую. В Kubernetes IP-адреса подов по умолчанию внутренние — внешние клиенты не смогут к ним обратиться напрямую без дополнительной настройки.