Управление группами ресурсов
В Milvus можно использовать группы ресурсов для физической изоляции определенных узлов запросов от остальных. В этом руководстве описано, как создавать и управлять настраиваемыми группами ресурсов, а также переносить узлы между ними.
Что такое группа ресурсов
Группа ресурсов может содержать несколько или все узлы запросов в кластере Milvus. Вы сами решаете, как распределить узлы запросов между группами ресурсов, исходя из того, что наиболее целесообразно для вас. Например, в сценарии с несколькими коллекциями вы можете распределить соответствующее количество узлов запросов по каждой группе ресурсов и загрузить коллекции в разные группы ресурсов, чтобы операции внутри каждой коллекции были физически независимы от операций в других коллекциях.
Обратите внимание, что экземпляр Milvus при запуске поддерживает группу ресурсов по умолчанию, в которой находятся все узлы запросов, и называет её __default_resource_group.
Начиная с версии 2.4.1, Milvus предоставляет декларативный API групп ресурсов, а старый API групп ресурсов признан устаревшим. Новый декларативный API позволяет пользователям обеспечить идемпотентность и упростить вторичную разработку в облачных средах.
Понятия, связанные с группами ресурсов
Группа ресурсов описывается конфигурацией группы ресурсов:
{
"requests": { "nodeNum": 1 },
"limits": { "nodeNum": 1 },
"transfer_from": [{ "resource_group": "rg1" }],
"transfer_to": [{ "resource_group": "rg2" }]
}
- Атрибут requests определяет условия, которым должна соответствовать группа ресурсов.
- Атрибут ` limits ` задает максимальные ограничения для группы ресурсов.
- Атрибуты ` transfer_from ` и `transfer_to` описывают, из каких групп ресурсов данная группа ресурсов должна в первую очередь получать ресурсы и в какие группы ресурсов она должна передавать ресурсы, соответственно.
Как только конфигурация группы ресурсов изменяется, Milvus максимально корректирует текущие ресурсы узла запросов в соответствии с новой конфигурацией, обеспечивая, чтобы все группы ресурсов в конечном итоге соответствовали следующему условию:
.requests.nodeNum < nodeNumOfResourceGroup < .limits.nodeNum.
За исключением следующих случаев:
- Когда количество узлов запросов (QueryNodes) в кластере Milvus недостаточно, т. е.
NumOfQueryNode < sum(.requests.nodeNum), всегда будут существовать группы ресурсов, не имеющие достаточного количества узлов запросов. - Когда количество узлов запросов (QueryNodes) в кластере Milvus превышает необходимое, т. е.
NumOfQueryNode > sum(.limits.nodeNum), избыточные узлы запросов всегда будут в первую очередь размещаться в группе ресурсов __default_resource_group.
Конечно, если количество QueryNodes в кластере изменится, Milvus будет постоянно пытаться адаптироваться, чтобы соответствовать конечным условиям. Поэтому вы можете сначала применить изменения конфигурации групп ресурсов, а затем выполнить масштабирование QueryNodes.
Используйте декларативный API для управления группой ресурсов
Все примеры кода на этой странице приведены для PyMilvus версии 2.6.17. Перед их запуском обновите свою установку PyMilvus.
Создание группы ресурсов.
Чтобы создать группу ресурсов, выполните следующее после подключения к экземпляру Milvus. В приведенном ниже фрагменте кода предполагается, что
default— это псевдоним вашего подключения к Milvus.import pymilvus # A resource group name should be a string of 1 to 255 characters, starting with a letter or an underscore (_) and containing only numbers, letters, and underscores (_). name = "rg" node_num = 0 # create a resource group that exactly hold no query node. try: milvus_client.create_resource_group(name, config=ResourceGroupConfig( requests={"node_num": node_num}, limits={"node_num": node_num}, )) print(f"Succeeded in creating resource group {name}.") except Exception: print("Failed to create the resource group.")Вывод списка групп ресурсов.
После создания группы ресурсов вы сможете увидеть её в списке групп ресурсов.
Чтобы просмотреть список групп ресурсов в экземпляре Milvus, выполните следующие действия:
rgs = milvus_client.list_resource_groups() print(f"Resource group list: {rgs}") # Resource group list: ['__default_resource_group', 'rg']Описать группу ресурсов.
Вы можете заставить Milvus описать нужную группу ресурсов следующим образом:
info = milvus_client.describe_resource_group(name) print(f"Resource group description: {info}") # Resource group description: # ResourceGroupInfo: # <name:rg1>, // resource group name # <capacity:0>, // resource group capacity # <num_available_node:1>, // resource group node num # <num_loaded_replica:{}>, // collection loaded replica num in resource group # <num_outgoing_node:{}>, // node num which still in use by replica in other resource group # <num_incoming_node:{}>, // node num which is in use by replica but belong to other resource group # <config:{}>, // resource group config # <nodes:[]> // node detail infoПеремещение узлов между группами ресурсов.
Вы можете заметить, что в описанной группе ресурсов пока нет ни одного узла запросов. Переместите несколько узлов из группы ресурсов по умолчанию в созданную вами группу следующим образом: Предположим, что в группе ресурсов __default_resource_group кластера в настоящее время находится 1 узел запросов (QueryNode), и мы хотим перенести один узел в созданную группу ресурсов (rg). Команда `
update_resource_groups` обеспечивает атомарность при выполнении нескольких изменений конфигурации, поэтому промежуточные состояния будут невидимы для Milvus.source = '__default_resource_group' target = 'rg' expected_num_nodes_in_default = 0 expected_num_nodes_in_rg = 1 try: milvus_client.update_resource_groups({ source: ResourceGroupConfig( requests={"node_num": expected_num_nodes_in_default}, limits={"node_num": expected_num_nodes_in_default}, ), target: ResourceGroupConfig( requests={"node_num": expected_num_nodes_in_rg}, limits={"node_num": expected_num_nodes_in_rg}, ) }) print(f"Succeeded in move 1 node(s) from {source} to {target}.") except Exception: print("Something went wrong while moving nodes.") # After a while, succeeded in moving 1 node(s) from __default_resource_group to rg.Загрузите коллекции и партиции в группу ресурсов.
Как только в группе ресурсов появятся узлы запросов, вы сможете загрузить коллекции в эту группу ресурсов. В следующем фрагменте кода предполагается, что коллекция с именем
demoуже существует.from pymilvus import Collection collection_name = "demo" # Milvus loads the collection to the default resource group. milvus_client.load_collection(collection_name, replica_number=2) # Or, you can ask Milvus load the collection to the desired resource group. # make sure that query nodes num should be greater or equal to replica_number resource_groups = ['rg'] milvus_client.load_collection(replica_number=2, _resource_groups=resource_groups)Кроме того, можно просто загрузить раздел в группу ресурсов, при этом его реплики будут распределены между несколькими группами ресурсов. В приведенном ниже примере предполагается, что коллекция с именем
Booksуже существует и в ней есть раздел с именемNovels.collection = "Books" partition = "Novels" # Use the load method of a collection to load one of its partition milvus_client.load_partitions(collection, [partition], replica_number=2, _resource_groups=resource_groups)Обратите внимание, что
_resource_groupsявляется необязательным параметром, и если его не указать, Milvus загрузит реплики на узлы запросов в группе ресурсов по умолчанию.Чтобы Milvus загружал каждую реплику коллекции в отдельную группу ресурсов, убедитесь, что количество групп ресурсов равно количеству реплик.
Перенос реплик между группами ресурсов.
Milvus использует реплики для обеспечения балансировки нагрузки между сегментами, распределенными по нескольким узлам запросов. Переместить определенные реплики коллекции из одной группы ресурсов в другую можно следующим образом:
source = '__default_resource_group' target = 'rg' collection_name = 'c' num_replicas = 1 try: milvus_client.transfer_replica(source, target, collection_name, num_replicas) print(f"Succeeded in moving {num_replicas} replica(s) of {collection_name} from {source} to {target}.") except Exception: print("Something went wrong while moving replicas.") # Succeeded in moving 1 replica(s) of c from __default_resource_group to rg.Удаление группы ресурсов.
Вы можете в любой момент удалить группу ресурсов, не содержащую ни одного узла запросов (
limits.node_num = 0). В данном руководстве группа ресурсовrgв настоящее время содержит один узел запросов. Сначала необходимо изменить конфигурациюlimits.node_numгруппы ресурсов, установив значение равным нулю.resource_group = "rg try: milvus_client.update_resource_groups({ resource_group: ResourceGroupConfig( requests={"node_num": 0}, limits={"node_num": 0}, ), }) milvus_client.drop_resource_group(resource_group) print(f"Succeeded in dropping {resource_group}.") except Exception: print(f"Something went wrong while dropping {resource_group}.")
Более подробную информацию см. в соответствующих примерах в pymilvus
Рекомендуемая практика управления масштабированием кластера
В настоящее время Milvus не может самостоятельно масштабироваться в облачных средах. Однако, используя API декларативных групп ресурсов в сочетании с оркестрацией контейнеров, Milvus может легко обеспечить изоляцию ресурсов и управление узлами запросов (QueryNodes). Ниже приведена рекомендуемая практика управления узлами запросов в облачной среде:
По умолчанию Milvus создает группу ресурсов __default_resource_group. Эту группу ресурсов нельзя удалить, она также служит группой ресурсов по умолчанию для загрузки всех коллекций, и ей всегда назначаются избыточные узлы QueryNode. Таким образом, мы можем создать группу ресурсов в режиме ожидания для хранения неиспользуемых ресурсов QueryNode, предотвращая их захват группой __default_resource_group.
Кроме того, если строго соблюдать ограничение
sum(.requests.nodeNum) <= queryNodeNum, можно точно контролировать распределение узлов QueryNode в кластере. Предположим, что в данный момент в кластере находится только один узел QueryNode, и инициализируем кластер. Вот пример настройки:from pymilvus.client.types import ResourceGroupConfig _PENDING_NODES_RESOURCE_GROUP="__pending_nodes" def init_cluster(node_num: int): print(f"Init cluster with {node_num} nodes, all nodes will be put in default resource group") # create a pending resource group, which can used to hold the pending nodes that do not hold any data. milvus_client.create_resource_group(name=_PENDING_NODES_RESOURCE_GROUP, config=ResourceGroupConfig( requests={"node_num": 0}, # this resource group can hold 0 nodes, no data will be load on it. limits={"node_num": 10000}, # this resource group can hold at most 10000 nodes )) # update default resource group, which can used to hold the nodes that all initial node in it. milvus_client.update_resource_groups({ "__default_resource_group": ResourceGroupConfig( requests={"node_num": node_num}, limits={"node_num": node_num}, transfer_from=[{"resource_group": _PENDING_NODES_RESOURCE_GROUP}], # recover missing node from pending resource group at high priority. transfer_to=[{"resource_group": _PENDING_NODES_RESOURCE_GROUP}], # recover redundant node to pending resource group at low priority. )}) milvus_client.create_resource_group(name="rg1", config=ResourceGroupConfig( requests={"node_num": 0}, limits={"node_num": 0}, transfer_from=[{"resource_group": _PENDING_NODES_RESOURCE_GROUP}], transfer_to=[{"resource_group": _PENDING_NODES_RESOURCE_GROUP}], )) milvus_client.create_resource_group(name="rg2", config=ResourceGroupConfig( requests={"node_num": 0}, limits={"node_num": 0}, transfer_from=[{"resource_group": _PENDING_NODES_RESOURCE_GROUP}], transfer_to=[{"resource_group": _PENDING_NODES_RESOURCE_GROUP}], )) init_cluster(1)Используя приведённый выше пример кода, мы создаём группу ресурсов с именем __pending_nodes для хранения дополнительных узлов QueryNode. Мы также создаём две пользовательские группы ресурсов с именами rg1 и rg2. Кроме того, мы обеспечиваем, чтобы другая группа ресурсов отдавала приоритет восстановлению отсутствующих или избыточных узлов QueryNode из группы __pending_nodes.
Горизонтальное масштабирование кластера
Предположим, что у нас есть следующая функция масштабирования:
def scale_to(node_num: int): # scale the querynode number in Milvus into node_num. passМы можем использовать API для масштабирования конкретной группы ресурсов до заданного количества узлов QueryNode без влияния на другие группы ресурсов.
# scale rg1 into 3 nodes, rg2 into 1 nodes milvus_client.update_resource_groups({ "rg1": ResourceGroupConfig( requests={"node_num": 3}, limits={"node_num": 3}, transfer_from=[{"resource_group": _PENDING_NODES_RESOURCE_GROUP}], transfer_to=[{"resource_group": _PENDING_NODES_RESOURCE_GROUP}], ), "rg2": ResourceGroupConfig( requests={"node_num": 1}, limits={"node_num": 1}, transfer_from=[{"resource_group": _PENDING_NODES_RESOURCE_GROUP}], transfer_to=[{"resource_group": _PENDING_NODES_RESOURCE_GROUP}], ), }) scale_to(5) # rg1 has 3 nodes, rg2 has 1 node, __default_resource_group has 1 node.Уменьшение масштаба кластера
Аналогичным образом мы можем установить правила уменьшения масштаба, которые отдают приоритет выбору узлов QueryNodes из группы ресурсов __pending_nodes. Эту информацию можно получить с помощью API
describe_resource_group. Достижение цели уменьшения масштаба указанной группы ресурсов.# scale rg1 from 3 nodes into 2 nodes milvus_client.update_resource_groups({ "rg1": ResourceGroupConfig( requests={"node_num": 2}, limits={"node_num": 2}, transfer_from=[{"resource_group": _PENDING_NODES_RESOURCE_GROUP}], transfer_to=[{"resource_group": _PENDING_NODES_RESOURCE_GROUP}], ), }) # rg1 has 2 nodes, rg2 has 1 node, __default_resource_group has 1 node, __pending_nodes has 1 node. scale_to(4) # scale the node in __pending_nodes
Как группы ресурсов взаимодействуют с несколькими репликами
- Между репликами одной коллекции и группами ресурсов существует связь типа «N к N».
- Когда несколько реплик одной коллекции загружаются в одну группу ресурсов, узлы QueryNodes этой группы равномерно распределяются между репликами, что гарантирует, что разница в количестве узлов QueryNodes у каждой реплики не превышает 1.
Что дальше
Чтобы развернуть многопользовательский экземпляр Milvus, ознакомьтесь со следующими материалами: