• Acerca de Milvus
  • Empezar
  • Conceptos
  • Guía del usuario
  • Importación de datos
  • Herramientas de IA
  • Guía de administración
  • Herramientas
  • Integraciones
  • Tutoriales
  • Preguntas frecuentes
  • API Reference

Gestionar grupos de recursos

En Milvus, puedes utilizar un grupo de recursos para aislar físicamente determinados nodos de consulta del resto. Esta guía te explica cómo crear y gestionar grupos de recursos personalizados, así como cómo 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 clúster de Milvus. Tú decides cómo quieres distribuir los nodos de consulta entre los grupos de recursos en función de lo que te resulte más adecuado. Por ejemplo, en un escenario con varias colecciones, puedes asignar un número adecuado de nodos de consulta a cada grupo de recursos y cargar las 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.

Ten en cuenta que una instancia de Milvus mantiene un grupo de recursos predeterminado para albergar todos los nodos de consulta al inicio y lo denomina __default_resource_group.

A partir de la versión 2.4.1, Milvus ofrece una API declarativa para grupos de recursos, mientras que la antigua API de grupos de recursos ha quedado obsoleta. La nueva API declarativa permite a los usuarios lograr la idempotencia y facilitar el desarrollo secundario en entornos nativos de la nube.

Conceptos de los grupos 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, respectivamente, de qué grupos de recursos debe adquirir preferentemente recursos un grupo de recursos y a qué grupos de recursos debe transferir recursos.

Una vez que cambia la configuración de un grupo de recursos, Milvus ajustará los recursos actuales del nodo de consulta en la medida de lo posible de acuerdo con la nueva configuración, asegurándose de que todos los grupos de recursos cumplan finalmente la siguiente condición:

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

Salvo en los siguientes casos:

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

Por supuesto, si el número de QueryNodes del clúster cambia, Milvus intentará continuamente ajustarse para cumplir las condiciones finales. Por lo tanto, puede aplicar primero los cambios en la configuración de los grupos de recursos y, a continuación, realizar el escalado de los QueryNodes.

Utilizar la API declarativa para gestionar el grupo de recursos

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

  1. Crea un grupo de recursos.

    Para crear un grupo de recursos, ejecuta lo siguiente después de conectarte a una instancia de Milvus. El siguiente fragmento de código da por hecho que default es el alias de tu conexión a 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.")
    
  2. Mostrar la lista de grupos de recursos.

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

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

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

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

    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
    
  4. Trasladar nodos entre grupos de recursos.

    Es posible que observes que el grupo de recursos descrito aún no tiene ningún nodo de consulta. Mueve algunos nodos del grupo de recursos predeterminado al que has creado de la siguiente manera: Supongamos que actualmente hay 1 QueryNode en el __default_resource_group del clúster y que queremos transferir un nodo al grupo de recursos creado. update_resource_groups garantiza la atomicidad de los cambios de configuración múltiples, por lo que Milvus no verá ningún estado intermedio.

    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.
    
  5. Carga colecciones y particiones en un grupo de recursos.

    Una vez que haya nodos de consulta en un grupo de recursos, podrás cargar colecciones en dicho grupo. El siguiente fragmento de código da por hecho que ya existe una colección llamada 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) 
    

    Además, puede limitarse a cargar una partición en un grupo de recursos y hacer que sus réplicas se distribuyan entre varios grupos de recursos. Lo siguiente da por hecho que ya existe una colección llamada Books y que tiene una partición llamada 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)
    

    Ten en cuenta que _resource_groups es un parámetro opcional; si no se especifica, Milvus cargará las réplicas en los nodos de consulta del grupo de recursos predeterminado.

    Para que Milvus cargue cada réplica de una colección en un grupo de recursos independiente, asegúrate de que el número de grupos de recursos sea igual al número de réplicas.

  6. Transferir réplicas entre grupos de recursos.

    Milvus utiliza réplicas para lograr el equilibrio de carga entre los segmentos distribuidos en varios nodos de consulta. Puedes mover determinadas 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:
        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.
    
  7. Eliminar un grupo de recursos.

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

    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}.")
    

Para obtener más detalles, consulta los ejemplos pertinentes en pymilvus

Una buena práctica para gestionar el escalado del clúster

Actualmente, Milvus no puede escalar de forma independiente (aumentar o reducir) en entornos nativos de la nube. Sin embargo, al utilizar la API de grupos de recursos declarativos junto con la orquestación de contenedores, Milvus puede lograr fácilmente el aislamiento y la gestión de recursos para los QueryNodes. A continuación se presenta una buena práctica para gestionar los 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 predeterminado para todas las colecciones; además, siempre se le asignan QueryNodes redundantes. Por lo tanto, podemos crear un grupo de recursos «pendiente» para albergar los recursos de QueryNode que no se estén utilizando, evitando así que el grupo de recursos __default_resource_group ocupe dichos recursos.

    Además, si aplicamos estrictamente la restricción sum(.requests.nodeNum) <= queryNodeNum, podemos controlar con precisión la asignación de QueryNodes en el clúster. Supongamos que actualmente solo hay un QueryNode en el clúster e inicialicemos el clúster. A continuación se muestra un ejemplo de configuración:

    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)
    

    Utilizando el código de ejemplo anterior, creamos un grupo de recursos denominado __pending_nodes para albergar QueryNodes adicionales. También creamos dos grupos de recursos específicos para el usuario denominados rg1 y rg2. Además, nos aseguramos de que el otro grupo de recursos dé prioridad a la recuperación de QueryNodes que falten o sean redundantes a partir de __pending_nodes.

  2. Escalabilidad horizontal del clúster

    Supongamos que disponemos de 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 determinado de QueryNodes sin afectar a ningún otro grupo de recursos.

    # 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.
    
  3. Reducción de la escala del clúster

    Del mismo modo, podemos establecer reglas de reducción de escala que den prioridad a la selección de QueryNodes del grupo de recursos __pending_nodes. Esta información se puede obtener a través de la API « describe_resource_group ». De este modo, se logra el objetivo de reducir la escala del grupo de recursos especificado.

    # 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
    

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

  • Las réplicas de una misma colección y los grupos de recursos mantienen una relación N a N.
  • Cuando se cargan varias réplicas de una misma colección en un único grupo de recursos, los QueryNodes de ese grupo de recursos se distribuyen de manera uniforme entre las réplicas, lo que garantiza que la diferencia en el número de QueryNodes que tiene cada réplica no supere 1.

Próximos pasos

Para implementar una instancia multitenant de Milvus, consulta lo siguiente: