---
metadata:
  - name: generator
    content: Diplodoc Platform v5.50.6
alternate:
  - https://ytsaurus.tech/docs/en/admin-guide/bundle-controller.md
  - https://ytsaurus.tech/docs/ru/admin-guide/bundle-controller.md
---
> **Documentation Index:** Fetch the complete configuration index at https://ytsaurus.tech/docs/ru/llms.txt

# Управление бандлами динамических таблиц

В YTsaurus есть возможность гибкого управления бандлами динамических таблиц. Для каждого бандла можно задавать количество таблетных нод, распределение числа потоков по пулам потоков и памяти по категориям. В случае отказа ноды автоматика назначит бандлу новую ноду из запаса.

## Обзор

Система, управляющая бандлами, называется Bundle controller. Она управляет инстансами таблетных нод. Каждая нода привязана к некоторому бандлу или находится в запасе. Каждый бандл и каждый инстанс относится к определённой зоне — набору инстансов с общим запасом. В настоящее время поддерживается только одна зона `zone_default`.

Bundle controller выставляет на каждый бандл атрибут `node_tag_filter`, имеющий вид `zone_default/<bundle_name>`. Также это значение устанавливается в атрибут `user_tags` всех нод, выданных бандлу. Каждая нода может принадлежать не более чем одному бандлу. В случае отказа ноды Bundle controller автоматически выдаёт бандлу новую ноду.

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

Также Bundle controller управляет аккаунтами, в которых хранятся журналы и снапшоты таблет-целлов бандла.

## Ограничения

Модель Bundle controller предполагает полную изоляцию различных бандлов по отдельным инстансам таблетных нод. Это значит, что нод в кластере должно быть хотя бы столько, сколько в кластере бандлов. В частности, поскольку по умолчанию в кластере сконфигурировано два бандла (`default` и `sys`), **для работы Bundle controller необходимо иметь хотя бы две таблетных ноды**.

В настоящее время Bundle controller может работать только с кластерами, в которых все инстансы таблетных нод имеют одинаковое количество CPU и RAM ресурсов.

## Первоначальная настройка { #setup }

### Установка сервиса

Bundle controller является отдельным компонентом кластера, аналогичным мастеру, планировщику и т.д. Для запуска используется бинарный файл `ytserver-bundle-controller`.

Если вы используете k8s-operator, необходимо добавить секцию в спецификацию:

```
spec:
  bundleController:
    instanceCount: N
    loggers: *loggers
```

Если вы управляете кластером самостоятельно, необходимо запустить `ytserver-bundle-controller` с указанием конфига. Пример построения конфига можно посмотреть в [репозитории k8s-operator](https://github.com/ytsaurus/ytsaurus-k8s-operator/blob/b880afc047a0793f3f9e7134e2a5a0aff4f81fff/pkg/ytconfig/bundle_controller.go#L43).

Для корректной работы достаточно одного инстанса Bundle controller, однако в целях отказоустойчивости рекомендуется выделять больше одного инстанса.

### Конфигурация кластера

После запуска компонента необходимо провести настройку кластера: определённым образом разметить бандлы и таблетные ноды, чтобы Bundle controller подхватил управление.

Для упрощения настройки типичного сценария подготовлен скрипт [bundle_controller_tools.py](https://github.com/ytsaurus/ytsaurus/tree/main/yt/yt/scripts/dynamic_tables/bundle_controller_tools). Запускать скрипт можно двумя способами.
- Предварительно скомпилировать бинарный файл через `ya make`.
- Напрямую запускать `__main__.py` интерпретатором Python. Для этого должны в системе должен быть установлен pip-пакет `ytsaurus-client`.
Для работы скрипта необходимо сохранить токен в `~/.yt/token` или воспользоваться альтернативными способами конфигурации токена, описанными в [документации](https://ytsaurus.tech/docs/ru/api/python/userdoc.md#configuration_token) по Python API. Адрес кластера указывается в переменной `YT_PROXY` или через аргумент `--proxy`.

Рекомендуется запускать скрипт с командой `init --init-all`. При запуске необходимо указать флаги `--cpu` и `--memory`, соответствующие параметрам инстансов таблетных нод: количество ядер CPU и байт RAM. Если в кластере есть инстансы разных размеров, рекомендуется выставлять параметры по минимальному инстансу.

В дальнейших разделах описаны шаги по настройке, выполняемые скриптом.

### Системные директории

**При запуске `bundle_controller_tools.py init` системные директории создаются автоматически. Для пропуска этого шага можно использовать флаг `--no-init-system-directories`.**

Для начала эксплуатации Bundle controller необходимо создать следующие директории:

- `//sys/bundle_controller/coordinator`
- `//sys/bundle_controller/controller/zones/zone_default`
- `//sys/bundle_controller/controller/bundles_state`

Также необходимо создать аккаунт с названием `bundle_system_quotas`.

### Зона zone\_default

**Для автоматической настройки зоны необходимо запустить `bundle_controller_tools.py init` с флагом `--init-default-zone`.**

Инстансы каждого типа привязаны к некоторому бандлу или находятся в запасе. Каждый бандл и инстанс относится к определённой зоне. В конфиге зоны необходимо указать размер инстансов (CPU и RAM), имеющихся в кластере, а также настройки по умолчанию для пулов потоков и категорий памяти нод.

{% note warning "Внимание" %}

В настоящее время Bundle controller может работать только с кластерами, в которых все инстансы одного размера. Это значит, что поле `resource_guarantee` в конфиге зоны, конфигах всех бандлов и аннотациях всех таблетных нод должно принимать одно и то же значение.

Если в кластере есть инстансы разных размеров, рекомендуется выставлять `resource_guarantee` по минимальному инстансу.

{% endnote %}

Конфиг зоны находится по пути `//sys/bundle_controller/controller/zones/zone_default`. Каждое поле конфига является атрибутом указанной директории.

#### Таблетные ноды

Конфигурация таблетных нод указывается в атрибуте `tablet_node_sizes`. Это словарь, состоящий из одного поля `regular` (тип инстанса). Он имеет следующий вид:

```
{
    "default_config" = {
        "cpu_limits" = {
            "lookup_thread_pool_size" = 8;
            "write_thread_pool_size" = 10;
            "query_thread_pool_size" = 8;
        };
        "memory_limits" = {
            "tablet_dynamic" = 1000000;
            "versioned_chunk_meta" = 1000000;
            "uncompressed_block_cache" = 200000;
            "tablet_static" = 0;
            "compressed_block_cache" = 1000000;
            "reserved" = 5000000;
            "lookup_row_cache" = 0;
        };
    };
    "resource_guarantee" = {
        "net_bytes" = 0;
        "vcpu" = 6000;
        "memory" = 1073741824;
    };
}
```

Здесь `default_config` — это распределение пулов потоков и категорий памяти по умолчанию для всех бандлов, а `resource_guarantee` — размер инстанса таблетной ноды. Поле `memory` соответствует размеру RAM в байтах, поле `vcpu` соответствует числу ядер CPU контейнера, умноженному на 1000. Поле `net_bytes` в настоящий момент не используется.

Подробнее о распределении пулов потоков и категорий памяти см. раздел [Создание бандла](#create_bundle).

#### RPC proxy

{% note info "Примечание" %}

В текущей версии управление RPC proxy поддержано не полностью.

{% endnote %}

Конфигурация RPC proxy указывается в атрибуте `rpc_proxy_sizes`. Это словарь, состоящий из одного поля `regular` (тип инстанса). Он имеет следующий вид:

```
 {
   "resource_guarantee" = {
        "net_bytes" = 0;
        "vcpu" = 6000;
        "memory" = 1073741824;
    };
}
```

Значения полей имеют тот же смысл, что и в случае таблетных нод.

### Аннотации инстансов

**Для автоматической настройки инстансов необходимо запустить `bundle_controller_tools.py init` с флагом `--init-tablet-nodes`.**

Все таблетные ноды, находящиеся под управлением Bundle controller, должны быть размечены определёнными атрибутами. Атрибуты подробно описаны в разделе [Добавление нод в кластер](#new_instance).

### Настройка бандлов

**Для автоматической настройки бандлов необходимо запустить `bundle_controller_tools.py init` с флагом `--init-bundle <bundle-name>`, `--init-all-bundles` или `--init-all`.**

Чтобы Bundle controller начал управлять существующими бандлами, необходимо установить на них атрибуты, указанные в разделе [Создание бандла](#create_bundle). Если на существующих бандлах был установлен атрибут `node_tag_filter`, его необходимо предварительно установить в значение `""` (пустая строка).

Скрипт `bundle_controller_tools.py` вычисляет необходимое число нод для каждого существующего бандла по текущему количеству таблет-целлов. В случае, если необходимо выдать больше или меньше нод, используйте аргумент `--bundle-node-count`, принимающий на вход yson-словарь в формате `{<bundle_name>=<node_count>}`.

#### Аккаунты под журналы и снапшоты

**Для автоматической настройки бандлов необходимо запустить `bundle_controller_tools.py init` с флагом `--init-bundle-system-quotas` или `--init-all`.**

Bundle controller может управлять аккаунтами, в которых хранятся журналы и снапшоты таблет-целлов. Аккаунты управляются полями `changelog_account` и `snapshot_account` в опциях бандла в атрибуте `//sys/tablet_cell_bundles/<bundle_name>/@options`. Аккаунты, находящиеся под управлением Bundle controller, имеют название `<bundle_name>_bundle_system_quotas` и являются дочерними для корневого аккаунта `bundle_system_quotas`. Bundle controller автоматически регулирует квоты родительского и дочерних аккаунтов в случае изменения числа таблет-целлов.

В статическом конфиге Bundle controller в секции `bundle_controller` есть [поля](https://github.com/ytsaurus/ytsaurus/blob/main/yt/yt/server/cell_balancer/config.h#L78), регулирующие объём ресурсов, необходимый для одного таблет-целла.

| Название                       | Значение                                                       | Значение по умолчанию |
| ------------------------------ | -------------------------------------------------------------- | --------------------- |
| `node_count_per_cell`          | число нод Кипариса на один таблет-целл                         | 25                    |
| `chunk_count_per_cell`         | число чанков на один таблет-целл                               | 100                   |
| `journal_disk_space_per_cell`  | объём дискового пространства для журналов на один таблет-целл  | 100 GB                |
| `snapshot_disk_space_per_cell` | объём дискового пространства для снапшотов на один таблет-целл | 15 GB                 |
| `min_node_count`               | минимальное число нод Кипариса, выдаваемое бандлу              | 1000                  |
| `min_chunk_count`              | минимальное число чанков, выдаваемое бандлу                    | 1000                  |

## Создание бандла { #create_bundle }

**Для создания бандла можно использовать скрипт `bundle_controller_tools.py create-bundle <bundle_name>`.**

Чтобы бандл оказался под управлением Bundle controller, на нём должны быть установлены атрибуты `enable_bundle_controller = %true` и `zone = "zone_default"`. Также должен быть установлен атрибут `bundle_controller_target_config`, имеющий следующий вид:

```
{
    "cpu_limits" = {
        "lookup_thread_pool_size" = 2;
        "query_thread_pool_size" = 2;
        "write_thread_pool_size" = 2;
    };
    "memory_limits" = {
        "tablet_dynamic" = 1000000;
		"tablet_static" = 0;
		"compressed_block_cache" = 1000000;
		"uncompressed_block_cache" = 200000;
		"versioned_chunk_meta" = 1000000;
		"lookup_row_cache" = 0;
		"reserved" = 5000000;
    };
    "rpc_proxy_count" = 0;
    "rpc_proxy_resource_guarantee" = {
        "memory" = 1073741824;
        "net_bytes" = 0;
        "vcpu" = 6000;
    };
    "tablet_node_count" = 1;
    "tablet_node_resource_guarantee" = {
        "memory" = 1073741824;
        "net_bytes" = 0;
        "vcpu" = 6000;
    };
}
```

Поля имеют следующее значение:

- `tablet_node_count`: число таблетных нод, выданных бандлу.
- `tablet_node_resource_guarantee`: размер инстанса таблетных нод, выданных бандлу. Должен совпадать со значением в конфиге зоны.
- `rpc_proxy_count`: число RPC proxy, выданных бандлу. *В настоящий момент не поддерживается.*
- `rpc_proxy_resource_guarantee`: размер инстанса RPC proxy, выданных бандлу. Должен совпадать со значением в конфиге зоны. *В настоящий момент не поддерживается.*
- `memory_limits`: распределение категорий памяти таблетных нод бандла. Суммарное значение должно быть не больше, чем `memory` в `resource_guarantee`.
  - `tablet_dynamic`: буфер памяти под dynamic stores.
  - `tablet_static`: память под in-memory таблицы (с `in_memory_mode` отличным от `none`).
  - `compressed_block_cache`, `uncompressed_block_cache`: кеши для блоков с данными, используемые при чтении.
  - `versioned_chunk_meta`: кеш метаинформации о чанках.
  - `lookup_row_cache`: память под построчный кеш.
  - `reserved`: память, зарезервированная под системные нужды и неучтённые категории.
- `cpu_limits`: распределение потоков таблетных нод бандла по пулам потоков. В отличие от памяти, здесь возможен оверкоммит, и число потоков может превосходить `vcpu/1000` в `resource_guarantee`.
  - `lookup_thread_pool_size`: число потоков под lookup-запросы.
  - `query_thread_pool_size`: число потоков под select-запросы.
  - `write_thread_pool_size`: число таблет-целлов на ноду.

Также необходимо установить поля `cpu` и `memory` в атрибуте `resource_limits` бандла. Суммарные `vcpu` и `memory` инстансов бандла не могут превосходить значений `cpu*1000` и `memory` из `resource_limits`. Подробнее об этом написано в разделе [Квотирование ресурсов](#resource_limits).

## Добавление нод в кластер { #new_instance }

После добавления инстансов в кластер необходимо разметить их атрибутом `bundle_controller_annotations`. Для этого можно использовать скрипт `bundle_controller_tools.py` с флагом `--init-tablet-nodes`.

Атрибут `bundle_controller_annotations` имеет следующий вид:

```
{
    "allocated" = %true;
    "allocated_for_bundle" = "spare";
    "resources" = {
        "vcpu" = 6000;
        "memory" = 1073741824;
        "type" = "regular";
    };
}
```

Здесь поле `allocated` означает, что инстанс находится под управлением Bundle controller, поле `allocated_for_bundle = "spare"` означает, что инстанс находится в запасе и не относится ни к какому бандлу, а поле `resources` содержит ресурсы инстанса и должно совпадать с полем `resource_guarantee` в соответствующей секции настройки зоны.

## Вывод нод из эксплуатации { #maintenance }

Для того чтобы вывести ноду из эксплуатации, необходимо выставить атрибут `//sys/cluster_nodes/<node_address>/@cms_maintenance_requests` в значение `{maintenance={}}`. После этого Bundle controller плавно перенесёт все таблет-целлы с этой ноды на другую ноду, выданную из запаса. После того как на выводимой ноде не останется таблет-целлов, её можно безопасно отключать. Наличие таблет-целлов отображается в атрибуте `tablet_slots`.

В случае аварийного отказа ноды Bundle controller выдаст бандлу новую ноду из запаса, после чего на ней восстановятся таблет-целлы.

## Квотирование ресурсов { #resource_limits }

Ресурсы бандла расположены в атрибуте `//sys/tablet_cell_bundles/<bundle_name>/@resource_limits`. Атрибут содержит следующие поля:

- `tablet_count`: ограничение на число таблетов таблиц бандла.
- `tablet_static_memory`: ограничение на суммарный размер in-memory таблиц бандла. Устанавливается автоматически.
- `cpu`: ограничение на суммарное значение `cpu` нод бандла.
- `memory`: ограничение на суммарное значение `memory` нод бандла.

Поля `tablet_count` и `tablet_static_memory` влияют на таблицы, создаваемые в бандле. Поля `cpu` и `memory` влияют на число таблетных нод, которое Bundle controller может выдать бандлу.

Поле `tablet_static_memory` автоматически устанавливается Bundle controller. Его значение вычисляется как `node_count * memory`, где `node_count` — это число нод, выданных бандлу, а `memory` — количество памяти на инстансе таблетной ноды, определённое в конфиге зоны.

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

Для упрощения настройки ограничений `cpu` и `memory` в скрипте `bundle_controller_tools.py` есть команда `set-bundle-resource-limits`, которая принимает ограничение на число нод и устанавливает лимиты в соответствии с конфигом зоны.

## Управление бандлом

Для управления бандлом используется кнопка `Edit bundle` на странице бандла в пользовательском интерфейсе. Интерфейс позволяет указать число нод, разбивку памяти по категориям и число потоков в пулах потоков для lookup- и select-запросов.

Для управления бандлом необходимо право `manage` на бандл.

## Учения бандла { #drills }

Для имитации отключения бандла на отдельном кластере (например, при учениях по проверке отказоустойчивости) у бандла есть режим учений (drills mode). Режим включается атрибутом `enable_drills_mode` в `bundle_controller_target_config` того кластера, который нужно «выключить».

Включение режима учений:

```bash
yt --proxy <cluster> set //sys/tablet_cell_bundles/<bundle_name>/@bundle_controller_target_config/enable_drills_mode %true
```

Выключение режима учений:

```bash
yt --proxy <cluster> set //sys/tablet_cell_bundles/<bundle_name>/@bundle_controller_target_config/enable_drills_mode %false
```

Флаг устанавливается на бандле на том кластере, который вы хотите прикрыть. Например, чтобы провести учения для бандла на `seneca-sas`, выставьте `enable_drills_mode = %true` в этом бандле на `seneca-sas`.

### Как это работает { #drills-how }

При включении режима учений Bundle controller:

- снимает с нод бандла `node_tag_filter`, что вытесняет таблет-целлы с нод, и переводит бандл на отдельный drills-фильтр вида `<zone>/<bundle_name>_drills_mode_on`;
- отключает проверки исправности таблет-целлов (`mute_tablet_cells_check` и `mute_tablet_cell_snapshots_check`), чтобы не поднимать алерты во время учений.

При выключении (`enable_drills_mode = %false`) эти действия откатываются в обратном порядке: возвращается обычный `node_tag_filter`, затем, по истечении grace-периода, вновь включаются проверки исправности.

Пока режим учений активен, на бандле горит алерт `bundle_has_drills_mode_enabled`.

### Ограничения и меры предосторожности { #drills-caveats }

Использование режима учений может быть опасно при работе с асинхронными репликами. Если во время учений реплика внезапно исчезает, транзакции асинхронной репликации могут «залипать» и мешать таблетам отмонтироваться — в таком состоянии таблеты надолго уходят в `freezing`.

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

В качестве workaround во время учений рекомендуется отключать балансировку таблетов. Отсутствие балансировки в течение нескольких часов, как правило, не влияет на работоспособность бандла.

## Запрет дата-центров для бандла { #forbidden-data-centers }

Чтобы запретить размещение бандла в отдельных дата-центрах (например, для имитации отказа ДЦ при учениях или для эвакуации бандла из ДЦ с сетевыми проблемами), в `bundle_controller_target_config` есть атрибут `forbidden_data_centers` — список названий ДЦ, в которых ноды и RPC proxy бандла размещаться не должны.

Установка списка (один или несколько ДЦ):

```bash
yt --proxy <cluster> set //sys/tablet_cell_bundles/<bundle_name>/@bundle_controller_target_config/forbidden_data_centers '["sas"]'
```

Снятие запрета (возврат к штатному размещению по всем ДЦ):

```bash
yt --proxy <cluster> set //sys/tablet_cell_bundles/<bundle_name>/@bundle_controller_target_config/forbidden_data_centers '[]'
```

### Как это работает { #forbidden-dc-how }

Запрещённые ДЦ помещаются в конец предпочтительного порядка ДЦ бандла. Пока у бандла хватает разрешённых ДЦ для размещения, Bundle controller:

- перестаёт назначать бандлу новые таблетные ноды и RPC proxy в запрещённых ДЦ;
- освобождает уже выданные в запрещённых ДЦ ноды и proxy, перенося их в разрешённые ДЦ.

Пока список `forbidden_data_centers` непуст, на бандле горит алерт `bundle_has_forbidden_dc` — он информационный и лишь подтверждает, что конфиг применён.

## Отключение Bundle controller

Для перевода Bundle controller в read-only режим необходимо установить атрибут `//sys/@disable_bundle_controller` в значение `%true`. Bundle controller перестанет выполнять какие-либо действия с кластером, однако состояние бандлов по-прежнему можно будет наблюдать в пользовательском интерфейсе.

## Решение проблем { #faq }

#### Bundle controller не работает

- Проверьте атрибут `//sys/@disable_bundle_controller`.
- Проверьте наличие блокировок на узле `//sys/bundle_controller/coordinator/lock/@locks`. Отсутствие блокировок означает, что Bundle controller не может зарегистрироваться на кластере. Часто причины указаны в error-логе контейнера.

#### В интерфейсе бандла наблюдается состояние `Failed`

В интерфейсе бандла есть отображаются два статуса: `Health` — это состояние таблет-целлов, `State` — состояние Bundle controller. В случае, если `State` находится в состоянии `Failed`, по нажатию на состояние отображается возникшая ошибка. В некоторых случаях это состояние может быть вызвано нарушениями инвариантов Bundle controller (например, в случае несовпадения `resource_guarantee` в настройках зоны, нод и бандлов). Рекомендуются следующие шаги:

- отключить Bundle controller путём установки атрибута `//sys/@disable_bundle_controller` в значение `%true`;
- запустить команду `bundle_controller_tools.py drop-allocations <bundle_name>`. Эта команда удалит некорректные запросы на выдачу нод;
- убедиться, что конфигурация корректна (например, путём запуска `bundle_controller_tools.py init --init-all --cpu <cpu_guarantee> --memory <memory_guarantee>`).
