milvus-logo
LFAI
Home
  • Guía del usuario

Gestionar grupos de recursos

En Milvus, puede utilizar un grupo de recursos para aislar físicamente ciertos nodos de consulta de otros. Esta guía le explica cómo crear y gestionar grupos de recursos personalizados, así como transferir nodos entre ellos.

Qué es un grupo de recursos

Un grupo de recursos puede contener varios o todos los nodos de consulta de un cluster Milvus. Usted decide cómo desea asignar los nodos de consulta entre los grupos de recursos basándose en lo que tenga más sentido para usted. Por ejemplo, en un escenario de múltiples colecciones, puede asignar un número apropiado de nodos de consulta a cada grupo de recursos y cargar colecciones en diferentes grupos de recursos, de modo que las operaciones dentro de cada colección sean físicamente independientes de las de otras colecciones.

Tenga en cuenta que una instancia de Milvus mantiene un grupo de recursos por defecto para alojar todos los nodos de consulta al inicio y lo denomina __default_resource_group.

A partir de la versión 2.4.1, Milvus proporciona una API declarativa de grupos de recursos, mientras que la antigua API de grupos de recursos ha quedado obsoleta. La nueva API declarativa permite a los usuarios lograr idempotencia, para hacer más fácil el desarrollo secundario en entornos nativos de nube.

Conceptos de grupo de recursos

Un grupo de recursos se describe mediante una configuración de grupo de recursos:

{
    "requests": { "nodeNum": 1 },
    "limits": { "nodeNum": 1 },
    "transfer_from": [{ "resource_group": "rg1" }],
    "transfer_to": [{ "resource_group": "rg2" }]
}
  • El atributo requests especifica las condiciones que debe cumplir un grupo de recursos.
  • El atributo limits especifica los límites máximos para un grupo de recursos.
  • Los atributos transfer_from y transfer_to describen de qué grupos de recursos debe adquirir preferentemente recursos un grupo de recursos y a qué grupos de recursos debe transferir recursos, respectivamente.

Una vez que cambia la configuración de un grupo de recursos, el Milvus ajustará los recursos actuales del Nodo de Consulta tanto como sea posible de acuerdo con la nueva configuración, asegurando que todos los grupos de recursos cumplan finalmente la siguiente condición:

.requests.nodeNum < nodeNumOfResourceGroup < .limits.nodeNum.

Excepto en los siguientes casos:

  • Cuando el número de QueryNodes en el cluster Milvus es insuficiente, es decir, NumOfQueryNode < sum(.requests.nodeNum), siempre habrá grupos de recursos sin suficientes QueryNodes.
  • Cuando el número de QueryNodes en el cluster Milvus es excesivo, es decir, NumOfQueryNode > sum(.limits.nodeNum), los QueryNodes redundantes siempre se colocarán primero en el grupo de recursos __default_resource_group.

Por supuesto, si el número de QueryNodes en el cluster cambia, Milvus intentará ajustarse continuamente para cumplir las condiciones finales. Por lo tanto, puede aplicar primero los cambios de configuración del grupo de recursos y luego realizar el escalado de QueryNode.

Utilizar la api declarativa para gestionar el grupo de recursos

Todos los ejemplos de código de esta página están en PyMilvus 2.4.9. Actualiza tu instalación de PyMilvus antes de ejecutarlos.

  1. Crear un grupo de recursos.

    Para crear un grupo de recursos, ejecute lo siguiente después de conectarse a una instancia de Milvus. El siguiente fragmento asume que default es el alias de su conexión 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:
        utility.create_resource_group(name, config=utility.ResourceGroupConfig(
            requests={"node_num": node_num},
            limits={"node_num": node_num},
        ), using='default')
        print(f"Succeeded in creating resource group {name}.")
    except Exception:
        print("Failed to create the resource group.")
    
  2. Listar grupos de recursos.

    Una vez que haya creado un grupo de recursos, podrá verlo en la lista de grupos de recursos.

    Para ver la lista de grupos de recursos en una instancia Milvus, haga lo siguiente:

    rgs = utility.list_resource_groups(using='default')
    print(f"Resource group list: {rgs}")
    
    # Resource group list: ['__default_resource_group', 'rg']
    
  3. Describa un grupo de recursos.

    Puede hacer que Milvus describa un grupo de recursos de la siguiente manera:

    info = utility.describe_resource_group(name, using="default")
    print(f"Resource group description: {info}")
    
    # Resource group description: 
    #        <name:"rg">,           // string, rg name
    #        <capacity:1>,            // int, num_node which has been transfer to this rg
    #        <num_available_node:0>,  // int, available node_num, some node may shutdown
    #        <num_loaded_replica:{}>, // map[string]int, from collection_name to loaded replica of each collecion in this rg
    #        <num_outgoing_node:{}>,  // map[string]int, from collection_name to outgoging accessed node num by replica loaded in this rg 
    #        <num_incoming_node:{}>.  // map[string]int, from collection_name to incoming accessed node num by replica loaded in other rg
    
  4. Transferir nodos entre grupos de recursos.

    Puede observar que el grupo de recursos descrito aún no tiene ningún nodo de consulta. Mueva algunos nodos desde el grupo de recursos por defecto al que cree de la siguiente manera: Suponiendo que actualmente hay 1 QueryNodes en el __default_resource_group del cluster, y queremos transferir un nodo al rg creado.update_resource_groups asegura la atomicidad para múltiples cambios de configuración, por lo que ningún estado intermedio será visible para Milvus.

    source = '__default_resource_group'
    target = 'rg'
    expected_num_nodes_in_default = 0
    expected_num_nodes_in_rg = 1
    
    try:
        utility.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},
            )
        }, using="default")
        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.
    
  5. Cargar colecciones y particiones a un grupo de recursos.

    Una vez que hay nodos de consulta en un grupo de recursos, puede cargar colecciones a este grupo de recursos. El siguiente fragmento asume que ya existe una colección llamada demo.

    from pymilvus import Collection
    
    collection = Collection('demo')
    
    # Milvus loads the collection to the default resource group.
    collection.load(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']
    collection.load(replica_number=2, _resource_groups=resource_groups) 
    

    También puede cargar una partición en un grupo de recursos y distribuir sus réplicas entre varios grupos de recursos. Lo siguiente asume que una colección llamada Books ya existe y tiene una partición llamada Novels.

    collection = Collection("Books")
    
    # Use the load method of a collection to load one of its partition
    collection.load(["Novels"], replica_number=2, _resource_groups=resource_groups)
    
    # Or, you can use the load method of a partition directly
    partition = Partition(collection, "Novels")
    partition.load(replica_number=2, _resource_groups=resource_groups)
    

    Tenga en cuenta que _resource_groups es un parámetro opcional, y dejándolo sin especificar Milvus cargará las réplicas en los nodos de consulta en el grupo de recursos por defecto.

    Para que Milus cargue cada réplica de una colección en un grupo de recursos separado, asegúrese de que el número de grupos de recursos es igual al número de réplicas.

  6. Transferir réplicas entre grupos de recursos.

    Milvus utiliza réplicas para equilibrar la carga entre segmentos distribuidos en varios nodos de consulta. Puede mover ciertas réplicas de una colección de un grupo de recursos a otro de la siguiente manera:

    source = '__default_resource_group'
    target = 'rg'
    collection_name = 'c'
    num_replicas = 1
    
    try:
        utility.transfer_replica(source, target, collection_name, num_replicas, using="default")
        print(f"Succeeded in moving {num_node} 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.
    
  7. Eliminar un grupo de recursos.

    Puede abandonar un grupo de recursos que no contenga ningún nodo de consulta (limits.node_num = 0) en cualquier momento. En esta guía, el grupo de recursos rg tiene ahora un nodo de consulta. Primero debe cambiar la configuración limits.node_num del grupo de recursos a cero.

    try:
        utility.update_resource_groups({
            "rg": utility.ResourceGroupConfig(
                requests={"node_num": 0},
                limits={"node_num": 0},
            ),
        }, using="default")
        utility.drop_resource_group("rg", using="default")
        print(f"Succeeded in dropping {source}.")
    except Exception:
        print(f"Something went wrong while dropping {source}.")
    

Para obtener más información, consulte los ejemplos correspondientes en pymilvus.

Una buena práctica para gestionar el escalado del cluster

Actualmente, Milvus no puede escalar independientemente en entornos nativos de la nube. Sin embargo, mediante el uso de la API Declarative Resource Group junto con la orquestación de contenedores, Milvus puede lograr fácilmente el aislamiento y la gestión de recursos para QueryNodes. He aquí una buena práctica para la gestión de QueryNodes en un entorno de nube:

  1. Por defecto, Milvus crea un __default_resource_group. Este grupo de recursos no se puede eliminar y también sirve como grupo de recursos de carga por defecto para todas las colecciones y los QueryNodes redundantes siempre se asignan a él. Por lo tanto, podemos crear un grupo de recursos pendiente para retener los recursos QueryNode no utilizados, evitando que los recursos QueryNode sean ocupados por el __default_resource_group.

    Además, si aplicamos estrictamente la restricción sum(.requests.nodeNum) <= queryNodeNum, podemos controlar con precisión la asignación de QueryNodes en el cluster. Supongamos que actualmente sólo hay un QueryNode en el cluster e inicialicemos el cluster. He aquí un ejemplo de configuración:

    from pymilvus import utility
    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.
        utility.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.
        utility.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.
            )})
        utility.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}],
        ))
        utility.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)
    

    Usando el código de ejemplo anterior, creamos un grupo de recursos llamado __pending_nodes para alojar QueryNodes adicionales. También creamos dos grupos de recursos específicos de usuario denominados rg1 y rg2. Además, nos aseguramos de que el otro grupo de recursos prioriza la recuperación de QueryNodes faltantes o redundantes de __pending_nodes.

  2. Escalado del clúster

    Suponiendo que tenemos la siguiente función de escalado:

    
    def scale_to(node_num: int):
        # scale the querynode number in Milvus into node_num.
        pass
    

    Podemos utilizar la API para escalar un grupo de recursos específico a un número designado de QueryNodes sin afectar a ningún otro grupo de recursos.

    # scale rg1 into 3 nodes, rg2 into 1 nodes
    utility.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.
    
  3. Escalado en clúster

    De forma similar, podemos establecer reglas de escalado de entrada que prioricen la selección de QueryNodes del grupo de recursos __pending_nodes. Esta información puede obtenerse a través de la API describe_resource_group. Conseguir el objetivo de escalado en el grupo de recursos especificado.

    # scale rg1 from 3 nodes into 2 nodes
    utility.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
    

Cómo interactúan los grupos de recursos con múltiples réplicas

  • Las réplicas de una única colección y los grupos de recursos tienen una relación de N a N.
  • Cuando se cargan varias réplicas de una única colección en un grupo de recursos, los QueryNodes de ese grupo de recursos se distribuyen uniformemente entre las réplicas, garantizando que la diferencia en el número de QueryNodes que tiene cada réplica no sea superior a 1.

Lo que sigue

Para desplegar una instancia Milvus multi-tenant, lea lo siguiente:

Traducido porDeepL

Try Managed Milvus for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started
Feedback

¿Fue útil esta página?