• À propos de Milvus
  • Commencer
  • Concepts
  • Guide de l'utilisateur
  • Importation de données
  • Outils d'IA
  • Guide d'administration
  • Outils
  • Intégrations
  • Tutoriels
  • Foire aux questions
  • API Reference

Gérer les groupes de ressources

Dans Milvus, vous pouvez utiliser un groupe de ressources pour isoler physiquement certains nœuds de requête des autres. Ce guide vous explique comment créer et gérer des groupes de ressources personnalisés, ainsi que comment transférer des nœuds d'un groupe à l'autre.

Qu'est-ce qu'un groupe de ressources ?

Un groupe de ressources peut contenir plusieurs nœuds de requête, voire tous les nœuds de requête d’un cluster Milvus. C’est vous qui décidez comment répartir les nœuds de requête entre les groupes de ressources, en fonction de ce qui vous semble le plus judicieux. Par exemple, dans un scénario à collections multiples, vous pouvez attribuer un nombre approprié de nœuds de requête à chaque groupe de ressources et charger les collections dans des groupes de ressources différents, de sorte que les opérations au sein de chaque collection soient physiquement indépendantes de celles des autres collections.

Notez qu’une instance Milvus gère un groupe de ressources par défaut contenant tous les nœuds de requête au démarrage et le nomme __default_resource_group.

À partir de la version 2.4.1, Milvus fournit une API déclarative pour les groupes de ressources, tandis que l’ancienne API a été dépréciée. La nouvelle API déclarative permet aux utilisateurs d’obtenir l’idempotence et de réaliser plus facilement des développements secondaires dans des environnements cloud natifs.

Concepts relatifs aux groupes de ressources

Un groupe de ressources est décrit par une configuration de groupe de ressources :

{
    "requests": { "nodeNum": 1 },
    "limits": { "nodeNum": 1 },
    "transfer_from": [{ "resource_group": "rg1" }],
    "transfer_to": [{ "resource_group": "rg2" }]
}
  • L’attribut ` requests ` spécifie les conditions qu’un groupe de ressources doit remplir.
  • L'attribut ` limits ` spécifie les limites maximales d'un groupe de ressources.
  • Les attributs `transfer_from ` et `transfer_to ` décrivent respectivement à partir de quels groupes de ressources un groupe de ressources doit de préférence acquérir des ressources et vers quels groupes de ressources il doit transférer des ressources.

Dès que la configuration d’un groupe de ressources change, Milvus ajuste autant que possible les ressources actuelles des nœuds de requête en fonction de la nouvelle configuration, en veillant à ce que tous les groupes de ressources finissent par satisfaire la condition suivante :

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

Sauf dans les cas suivants :

  • Lorsque le nombre de nœuds de requête (QueryNodes) dans le cluster Milvus est insuffisant, c’est-à-dire lorsque NumOfQueryNode < sum(.requests.nodeNum), il y aura toujours des groupes de ressources ne disposant pas d’un nombre suffisant de nœuds de requête.
  • Lorsque le nombre de nœuds de requête dans le cluster Milvus est excessif, c’est-à-dire NumOfQueryNode > sum(.limits.nodeNum), les nœuds de requête redondants seront toujours placés en priorité dans le groupe de ressources __default_resource_group.

Bien entendu, si le nombre de QueryNodes dans le cluster change, Milvus tentera en permanence de s’adapter pour répondre aux conditions finales. Par conséquent, vous pouvez d’abord appliquer les modifications de configuration des groupes de ressources, puis procéder à la mise à l’échelle des QueryNodes.

Utilisation de l’API déclarative pour gérer les groupes de ressources

Tous les exemples de code présentés sur cette page sont écrits en PyMilvus 3.0.1. Mettez à jour votre installation de PyMilvus avant de les exécuter.

  1. Créer un groupe de ressources.

    Pour créer un groupe de ressources, exécutez la commande suivante après vous être connecté à une instance Milvus. L'extrait de code suivant part du principe que default est l'alias de votre connexion 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. Répertorier les groupes de ressources.

    Une fois que vous avez créé un groupe de ressources, vous pouvez le voir dans la liste des groupes de ressources.

    Pour afficher la liste des groupes de ressources d’une instance Milvus, procédez comme suit :

    rgs = milvus_client.list_resource_groups()
    print(f"Resource group list: {rgs}")
    
    # Resource group list: ['__default_resource_group', 'rg']
    
  3. Décrire un groupe de ressources.

    Vous pouvez demander à Milvus de décrire un groupe de ressources donné comme suit :

    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. Transférer des nœuds entre des groupes de ressources.

    Vous remarquerez peut-être que le groupe de ressources décrit ne comporte encore aucun nœud de requête. Déplacez quelques nœuds du groupe de ressources par défaut vers celui que vous venez de créer, comme suit : En supposant qu’il y ait actuellement 1 QueryNode dans le groupe de ressources __default_resource_group du cluster, et que nous souhaitions transférer un nœud vers le groupe de ressources créé. La commande `update_resource_groups ` garantit l’atomicité des modifications de configuration multiples ; ainsi, aucun état intermédiaire ne sera visible par 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.
    
  5. Chargement de collections et de partitions dans un groupe de ressources.

    Dès qu’un groupe de ressources contient des nœuds de requête, vous pouvez y charger des collections. L’extrait de code suivant part du principe qu’une collection nommée demo existe déjà.

    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) 
    

    De plus, vous pouvez simplement charger une partition dans un groupe de ressources et répartir ses répliques entre plusieurs groupes de ressources. L’exemple suivant part du principe qu’une collection nommée Books existe déjà et qu’elle comporte une partition nommée 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)
    

    Notez que _resource_groups est un paramètre facultatif ; si vous ne le spécifiez pas, Milvus chargera les répliques sur les nœuds de requête du groupe de ressources par défaut.

    Pour que Milvus charge chaque réplique d’une collection dans un groupe de ressources distinct, assurez-vous que le nombre de groupes de ressources est égal au nombre de répliques.

  6. Transférer des répliques entre des groupes de ressources.

    Milvus utilise des répliques pour assurer l’équilibrage de charge entre les segments répartis sur plusieurs nœuds de requête. Vous pouvez déplacer certaines répliques d’une collection d’un groupe de ressources vers un autre comme suit :

    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. Supprimer un groupe de ressources.

    Vous pouvez à tout moment supprimer un groupe de ressources ne contenant aucun nœud de requête (limits.node_num = 0). Dans ce guide, le groupe de ressources rg contient désormais un nœud de requête. Vous devez d’abord modifier la configuration limits.node_num du groupe de ressources pour la mettre à zéro.

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

Pour plus de détails, veuillez vous reporter aux exemples pertinents dans pymilvus

Une bonne pratique pour gérer la mise à l’échelle du cluster

Actuellement, Milvus ne peut pas s’adapter de manière autonome (augmentation ou réduction) dans les environnements cloud natifs. Cependant, en utilisant l’API déclarative des groupes de ressources en conjonction avec l’orchestration de conteneurs, Milvus peut facilement assurer l’isolation et la gestion des ressources pour les QueryNodes. Voici une bonne pratique pour gérer les QueryNodes dans un environnement cloud :

  1. Par défaut, Milvus crée un groupe de ressources __default_resource_group. Ce groupe de ressources ne peut pas être supprimé et sert également de groupe de ressources de chargement par défaut pour toutes les collections ; les QueryNodes redondants y sont toujours affectés. Par conséquent, nous pouvons créer un groupe de ressources « en attente » pour accueillir les ressources QueryNode inutilisées, empêchant ainsi le groupe de ressources __default_resource_group de les occuper.

    De plus, en appliquant strictement la contrainte sum(.requests.nodeNum) <= queryNodeNum, nous pouvons contrôler avec précision l’affectation des QueryNodes au sein du cluster. Supposons qu’il n’y ait actuellement qu’un seul QueryNode dans le cluster et initialisons le cluster. Voici un exemple de configuration :

    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)
    

    À l’aide du code d’exemple ci-dessus, nous créons un groupe de ressources nommé __pending_nodes destiné à contenir des QueryNodes supplémentaires. Nous créons également deux groupes de ressources spécifiques à l’utilisateur, nommés rg1 et rg2. De plus, nous nous assurons que l’autre groupe de ressources donne la priorité à la récupération des QueryNodes manquants ou redondants à partir de __pending_nodes.

  2. Extension horizontale du cluster

    Supposons que nous disposions de la fonction d’évolutivité suivante :

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

    Nous pouvons utiliser l’API pour faire évoluer un groupe de ressources spécifique vers un nombre défini de QueryNodes sans affecter les autres groupes de ressources.

    # 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. Réduction de la capacité du cluster

    De la même manière, nous pouvons définir des règles de réduction de capacité qui donnent la priorité à la sélection de nœuds de requête issus du groupe de ressources __pending_nodes. Ces informations peuvent être obtenues via l’API describe_resource_group. Atteindre l’objectif de réduction de capacité d’un groupe de ressources spécifié.

    # 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
    

Comment les groupes de ressources interagissent avec plusieurs répliques

  • Les répliques d’une même collection et les groupes de ressources entretiennent une relation N-à-N.
  • Lorsque plusieurs répliques d’une même collection sont chargées dans un même groupe de ressources, les nœuds de requête (QueryNodes) de ce groupe sont répartis de manière uniforme entre les répliques, garantissant ainsi que la différence entre le nombre de nœuds de requête de chaque réplique ne dépasse pas 1.

Prochaines étapes

Pour déployer une instance Milvus multi-locataires, consultez les documents suivants :