• Informazioni su Milvus
  • Inizia
  • Concetti
  • Guida per l'utente
  • Importazione dei dati
  • Strumenti di intelligenza artificiale
  • Guida all'amministrazione
  • Strumenti
  • Integrazioni
  • Tutorial
  • Domande frequenti
  • API Reference

Gestione dei gruppi di risorse

In Milvus, è possibile utilizzare un gruppo di risorse per isolare fisicamente determinati nodi di query dagli altri. Questa guida illustra come creare e gestire gruppi di risorse personalizzati, nonché come trasferire i nodi da un gruppo all'altro.

Che cos’è un gruppo di risorse

Un gruppo di risorse può contenere alcuni o tutti i nodi di query presenti in un cluster Milvus. Spetta a te decidere come allocare i nodi di query tra i gruppi di risorse in base a ciò che ritieni più opportuno. Ad esempio, in uno scenario con più collezioni, puoi allocare un numero appropriato di nodi di query a ciascun gruppo di risorse e caricare le collezioni in gruppi di risorse diversi, in modo che le operazioni all’interno di ciascuna collezione siano fisicamente indipendenti da quelle delle altre collezioni.

Si noti che un'istanza di Milvus mantiene un gruppo di risorse predefinito per contenere tutti i nodi di query all'avvio e lo chiama __default_resource_group.

A partire dalla versione 2.4.1, Milvus fornisce un’API dichiarativa per i gruppi di risorse, mentre la vecchia API è stata deprecata. La nuova API dichiarativa consente agli utenti di ottenere l’idempotenza, facilitando lo sviluppo secondario in ambienti cloud-native.

Concetti relativi ai gruppi di risorse

Un gruppo di risorse è descritto da una configurazione del gruppo di risorse:

{
    "requests": { "nodeNum": 1 },
    "limits": { "nodeNum": 1 },
    "transfer_from": [{ "resource_group": "rg1" }],
    "transfer_to": [{ "resource_group": "rg2" }]
}
  • L’attributo `requests ` specifica le condizioni che un gruppo di risorse deve soddisfare.
  • L’attributo ` limits ` specifica i limiti massimi per un gruppo di risorse.
  • Gli attributi `transfer_from ` e `transfer_to` descrivono, rispettivamente, da quali gruppi di risorse un gruppo di risorse dovrebbe preferibilmente acquisire risorse e a quali gruppi di risorse dovrebbe trasferire risorse.

Una volta modificata la configurazione di un gruppo di risorse, Milvus adeguerà il più possibile le risorse attuali del nodo di query in base alla nuova configurazione, assicurando che tutti i gruppi di risorse soddisfino alla fine la seguente condizione:

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

Tranne nei seguenti casi:

  • Quando il numero di QueryNode nel cluster Milvus è insufficiente, ovvero NumOfQueryNode < sum(.requests.nodeNum), ci saranno sempre gruppi di risorse senza un numero sufficiente di QueryNode.
  • Quando il numero di QueryNode nel cluster Milvus è eccessivo, ovvero NumOfQueryNode > sum(.limits.nodeNum), i QueryNode ridondanti verranno sempre collocati per primi nel __default_resource_group.

Naturalmente, se il numero di QueryNode nel cluster cambia, Milvus tenterà continuamente di adattarsi per soddisfare le condizioni finali. Pertanto, è possibile applicare prima le modifiche alla configurazione dei gruppi di risorse e poi eseguire il ridimensionamento dei QueryNode.

Utilizzare l’API dichiarativa per gestire i gruppi di risorse

Tutti gli esempi di codice presenti in questa pagina sono in PyMilvus 3.0.2. Aggiornare l’installazione di PyMilvus prima di eseguirli.

  1. Creare un gruppo di risorse.

    Per creare un gruppo di risorse, eseguire il comando seguente dopo essersi connessi a un'istanza di Milvus. Il frammento di codice seguente presuppone che default sia l'alias della connessione 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. Elencare i gruppi di risorse.

    Una volta creato un gruppo di risorse, è possibile visualizzarlo nell'elenco dei gruppi di risorse.

    Per visualizzare l’elenco dei gruppi di risorse in un’istanza di Milvus, procedere come segue:

    rgs = milvus_client.list_resource_groups()
    print(f"Resource group list: {rgs}")
    
    # Resource group list: ['__default_resource_group', 'rg']
    
  3. Descrivere un gruppo di risorse.

    È possibile chiedere a Milvus di descrivere un gruppo di risorse specifico come segue:

    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. Spostare i nodi tra i gruppi di risorse.

    Si noterà che il gruppo di risorse descritto non ha ancora alcun nodo di query. Spostare alcuni nodi dal gruppo di risorse predefinito a quello appena creato come segue: Supponendo che attualmente ci sia 1 QueryNode nel gruppo di risorse __default_resource_group del cluster e che si voglia trasferire un nodo nel gruppo di risorse creato.update_resource_groups garantisce l'atomicità per più modifiche di configurazione, quindi Milvus non vedrà alcuno stato 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. Caricare le collezioni e le partizioni in un gruppo di risorse.

    Una volta che in un gruppo di risorse sono presenti dei nodi di query, è possibile caricare le collezioni in tale gruppo di risorse. Il frammento di codice seguente presuppone che esista già una collezione denominata 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) 
    

    Inoltre, è possibile caricare semplicemente una partizione in un gruppo di risorse e distribuire le sue repliche tra diversi gruppi di risorse. Quanto segue presuppone che esista già una collezione denominata Books e che questa abbia una partizione denominata 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)
    

    Si noti che _resource_groups è un parametro facoltativo; se non viene specificato, Milvus caricherà le repliche sui nodi di query nel gruppo di risorse predefinito.

    Per fare in modo che Milvus carichi ciascuna replica di una raccolta in un gruppo di risorse separato, assicurarsi che il numero di gruppi di risorse sia uguale al numero di repliche.

  6. Trasferimento delle repliche tra gruppi di risorse.

    Milvus utilizza le repliche per ottenere il bilanciamento del carico tra i segmenti distribuiti su diversi nodi di query. È possibile spostare determinate repliche di una collezione da un gruppo di risorse a un altro come segue:

    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. Eliminare un gruppo di risorse.

    È possibile eliminare in qualsiasi momento un gruppo di risorse che non contiene alcun nodo di query (limits.node_num = 0). In questa guida, il gruppo di risorse rg ora contiene un nodo di query. È necessario prima modificare la configurazione limits.node_num del gruppo di risorse impostando il valore su zero.

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

Per ulteriori dettagli, fare riferimento agli esempi pertinenti in pymilvus

Una buona pratica per gestire il ridimensionamento del cluster

Attualmente, Milvus non è in grado di scalare autonomamente in su o in giù in ambienti cloud-native. Tuttavia, utilizzando l’API Declarative Resource Group in combinazione con l’orchestrazione dei container, Milvus può facilmente ottenere l’isolamento e la gestione delle risorse per i QueryNodes. Ecco una buona pratica per la gestione dei QueryNodes in un ambiente cloud:

  1. Per impostazione predefinita, Milvus crea un __default_resource_group. Questo gruppo di risorse non può essere eliminato e funge anche da gruppo di risorse di caricamento predefinito per tutte le collezioni; inoltre, i QueryNode ridondanti vengono sempre assegnati ad esso. Pertanto, è possibile creare un gruppo di risorse in sospeso per contenere le risorse dei QueryNode inattive, impedendo che tali risorse vengano occupate dal __default_resource_group.

    Inoltre, se applichiamo rigorosamente il vincolo sum(.requests.nodeNum) <= queryNodeNum, possiamo controllare con precisione l’assegnazione dei QueryNode nel cluster. Supponiamo che al momento ci sia un solo QueryNode nel cluster e inizializziamo il cluster. Ecco un esempio di configurazione:

    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)
    

    Utilizzando il codice di esempio sopra riportato, creiamo un gruppo di risorse denominato __pending_nodes per contenere ulteriori QueryNode. Creiamo inoltre due gruppi di risorse specifici per l’utente denominati rg1 e rg2. Inoltre, ci assicuriamo che l’altro gruppo di risorse dia priorità al recupero dei QueryNode mancanti o ridondanti da __pending_nodes.

  2. Scalabilità orizzontale del cluster

    Supponendo di disporre della seguente funzione di scalabilità:

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

    Possiamo utilizzare l’API per scalare un gruppo di risorse specifico a un numero prestabilito di QueryNodes senza influire su nessun altro gruppo di risorse.

    # 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. Ridimensionamento del cluster

    Analogamente, possiamo stabilire regole di riduzione delle dimensioni che diano priorità alla selezione dei QueryNodes dal gruppo di risorse __pending_nodes. Queste informazioni possono essere ottenute tramite l’API describe_resource_group. Raggiungere l’obiettivo di ridurre le dimensioni di un gruppo di risorse specificato.

    # 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
    

Come i gruppi di risorse interagiscono con più repliche

  • Le repliche di una singola collezione e i gruppi di risorse hanno una relazione N-a-N.
  • Quando più repliche di una singola collezione vengono caricate in un unico gruppo di risorse, i QueryNode di quel gruppo di risorse vengono distribuiti uniformemente tra le repliche, garantendo che la differenza nel numero di QueryNode di ciascuna replica non superi 1.

Prossimi passi

Per distribuire un'istanza multi-tenant di Milvus, consultare quanto segue: