Verwalten von Ressourcengruppen
In Milvus können Sie eine Ressourcengruppe verwenden, um bestimmte Abfrageknoten physisch von anderen zu isolieren. Dieser Leitfaden zeigt Ihnen, wie Sie benutzerdefinierte Ressourcengruppen erstellen und verwalten sowie Knoten zwischen ihnen übertragen können.
Was ist eine Ressourcengruppe?
Eine Ressourcengruppe kann mehrere oder alle Abfrageknoten in einem Milvus-Cluster enthalten. Sie entscheiden, wie Sie die Abfrageknoten zwischen den Ressourcengruppen zuweisen möchten, je nachdem, was für Sie am sinnvollsten ist. In einem Szenario mit mehreren Sammlungen können Sie zum Beispiel jeder Ressourcengruppe eine angemessene Anzahl von Abfrageknoten zuweisen und Sammlungen in verschiedene Ressourcengruppen laden, so dass die Operationen innerhalb jeder Sammlung physisch unabhängig von denen in anderen Sammlungen sind.
Beachten Sie, dass eine Milvus-Instanz eine Standard-Ressourcengruppe unterhält, um alle Abfrageknoten beim Start zu halten und sie __default_resource_group nennt.
Ab Version 2.4.1 bietet Milvus eine deklarative Ressourcengruppen-API, während die alte Ressourcengruppen-API veraltet ist. Die neue deklarative API ermöglicht es den Benutzern, Idempotenz zu erreichen, um sekundäre Entwicklung in Cloud-nativen Umgebungen einfacher zu machen.
Konzepte der Ressourcengruppe
Eine Ressourcengruppe wird durch eine Ressourcengruppenkonfiguration beschrieben:
{
"requests": { "nodeNum": 1 },
"limits": { "nodeNum": 1 },
"transfer_from": [{ "resource_group": "rg1" }],
"transfer_to": [{ "resource_group": "rg2" }]
}
- Das Attribut requests gibt die Bedingungen an, die eine Ressourcengruppe erfüllen muss.
- Das limits-Attribut legt die Höchstgrenzen für eine Ressourcengruppe fest.
- Die Attribute transfer_from und transfer_to beschreiben, von welchen Ressourcengruppen eine Ressourcengruppe vorzugsweise Ressourcen beziehen bzw. an welche Ressourcengruppen sie Ressourcen übertragen soll.
Sobald sich die Konfiguration einer Ressourcengruppe ändert, passt der Milvus die aktuellen Ressourcen des Abfrageknotens so weit wie möglich an die neue Konfiguration an, um sicherzustellen, dass alle Ressourcengruppen schließlich die folgende Bedingung erfüllen:
.requests.nodeNum < nodeNumOfResourceGroup < .limits.nodeNum.
Außer in den folgenden Fällen:
- Wenn die Anzahl der QueryNodes im Milvus-Cluster unzureichend ist, d. h.
NumOfQueryNode < sum(.requests.nodeNum)
, wird es immer Ressourcengruppen ohne genügend QueryNodes geben. - Wenn die Anzahl der QueryNodes im Milvus-Cluster zu hoch ist, d.h.
NumOfQueryNode > sum(.limits.nodeNum)
, werden die redundanten QueryNodes immer zuerst in der __default_resource_group platziert.
Wenn sich die Anzahl der QueryNodes im Cluster ändert, wird Milvus natürlich ständig versuchen, die endgültigen Bedingungen zu erfüllen. Daher können Sie zuerst die Konfigurationsänderungen der Ressourcengruppe anwenden und dann die Skalierung der QueryNodes durchführen.
Verwendung der deklarativen API zur Verwaltung der Ressourcengruppe
Alle Codebeispiele auf dieser Seite sind in PyMilvus 2.4.9. Aktualisieren Sie Ihre PyMilvus-Installation, bevor Sie sie ausführen.
Erstellen Sie eine Ressourcengruppe.
Um eine Ressourcengruppe zu erstellen, führen Sie das Folgende aus, nachdem Sie sich mit einer Milvus-Instanz verbunden haben. Das folgende Snippet geht davon aus, dass
default
der Alias Ihrer Milvus-Verbindung ist.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.")
Ressourcengruppen auflisten.
Sobald Sie eine Ressourcengruppe erstellt haben, können Sie sie in der Ressourcengruppenliste sehen.
Um die Liste der Ressourcengruppen in einer Milvus-Instanz zu sehen, gehen Sie wie folgt vor:
rgs = utility.list_resource_groups(using='default') print(f"Resource group list: {rgs}") # Resource group list: ['__default_resource_group', 'rg']
Beschreiben Sie eine Ressourcengruppe.
Sie können Milvus eine Ressourcengruppe wie folgt beschreiben lassen:
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
Übertragen Sie Knoten zwischen Ressourcengruppen.
Sie werden feststellen, dass die beschriebene Ressourcengruppe noch keine Abfrageknoten hat. Verschieben Sie einige Knoten von der Standard-Ressourcengruppe in die von Ihnen erstellte wie folgt: Angenommen, es gibt derzeit 1 QueryNodes in der __default_resource_group des Clusters, und wir wollen einen Knoten in die erstellte rg übertragen.
update_resource_groups
gewährleistet Atomarität für mehrere Konfigurationsänderungen, so dass keine Zwischenzustände für Milvus sichtbar sind.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.
Sammlungen und Partitionen in eine Ressourcengruppe laden.
Sobald Abfrageknoten in einer Ressourcengruppe vorhanden sind, können Sie Sammlungen in diese Ressourcengruppe laden. Der folgende Ausschnitt geht davon aus, dass eine Sammlung namens
demo
bereits existiert.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)
Sie können auch einfach eine Partition in eine Ressourcengruppe laden und ihre Replikate auf mehrere Ressourcengruppen verteilen lassen. Im Folgenden wird davon ausgegangen, dass eine Sammlung mit dem Namen
Books
bereits vorhanden ist und eine Partition mit dem NamenNovels
enthält.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)
Beachten Sie, dass
_resource_groups
ein optionaler Parameter ist, und wenn Sie ihn nicht angeben, lädt Milvus die Replikate auf die Abfrageknoten in der Standard-Ressourcengruppe.Damit Milus jedes Replikat einer Sammlung in eine separate Ressourcengruppe lädt, stellen Sie sicher, dass die Anzahl der Ressourcengruppen gleich der Anzahl der Replikate ist.
Übertragen Sie Replikate zwischen Ressourcengruppen.
Milvus verwendet Replikate, um einen Lastausgleich zwischen Segmenten zu erreichen, die über mehrere Abfrageknoten verteilt sind. Sie können bestimmte Replikate einer Sammlung wie folgt von einer Ressourcengruppe in eine andere verschieben:
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.
Verwerfen einer Ressourcengruppe.
Sie können eine Ressourcengruppe, die keinen Abfrageknoten enthält (
limits.node_num = 0
), jederzeit auflösen. In dieser Anleitung hat die Ressourcengrupperg
jetzt einen Abfrageknoten. Sie müssen zuerst die Konfigurationlimits.node_num
der Ressourcengruppe auf Null ändern.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}.")
Für weitere Details lesen Sie bitte die entsprechenden Beispiele in pymilvus
Eine gute Praxis zur Verwaltung der Clusterskalierung
Derzeit kann Milvus in Cloud-nativen Umgebungen nicht unabhängig ein- und ausskaliert werden. Durch die Verwendung der deklarativen Ressourcengruppen-API in Verbindung mit der Container-Orchestrierung kann Milvus jedoch auf einfache Weise eine Ressourcenisolierung und -verwaltung für QueryNodes erreichen. Hier ist eine gute Praxis für die Verwaltung von QueryNodes in einer Cloud-Umgebung:
Standardmäßig erstellt Milvus eine __default_resource_group. Diese Ressourcengruppe kann nicht gelöscht werden und dient auch als Standard-Laderessourcengruppe für alle Sammlungen, und redundante QueryNodes werden ihr immer zugewiesen. Daher können wir eine schwebende Ressourcengruppe erstellen, die unbenutzte QueryNode-Ressourcen aufnimmt und verhindert, dass QueryNode-Ressourcen von der __default_resource_group belegt werden.
Wenn wir außerdem die Einschränkung
sum(.requests.nodeNum) <= queryNodeNum
strikt durchsetzen, können wir die Zuweisung von QueryNodes im Cluster genau kontrollieren. Gehen wir davon aus, dass es derzeit nur einen QueryNode im Cluster gibt und initialisieren wir den Cluster. Hier ist ein Beispiel-Setup: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)
Unter Verwendung des obigen Beispielcodes erstellen wir eine Ressourcengruppe namens __pending_nodes, um weitere QueryNodes aufzunehmen. Außerdem erstellen wir zwei benutzerspezifische Ressourcengruppen mit den Namen rg1 und rg2. Außerdem stellen wir sicher, dass die andere Ressourcengruppe der Wiederherstellung fehlender oder redundanter QueryNodes aus __pending_nodes Priorität einräumt.
Skalierung des Clusters
Angenommen, wir haben die folgende Skalierungsfunktion:
def scale_to(node_num: int): # scale the querynode number in Milvus into node_num. pass
Wir können die API verwenden, um eine bestimmte Ressourcengruppe auf eine bestimmte Anzahl von Abfrageknoten zu skalieren, ohne andere Ressourcengruppen zu beeinträchtigen.
# 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.
Cluster skalieren in
In ähnlicher Weise können wir Regeln für die Skalierung festlegen, die die Auswahl von QueryNodes aus der Ressourcengruppe __pending_nodes priorisieren. Diese Informationen können über die
describe_resource_group
API abgerufen werden. Erreichen des Ziels der Skalierung in einer bestimmten Ressourcengruppe.# 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
Wie Ressourcengruppen mit mehreren Replikaten interagieren
- Die Replikate einer einzelnen Sammlung und Ressourcengruppen stehen in einer N-zu-N-Beziehung.
- Wenn mehrere Replikate einer einzelnen Sammlung in eine Ressourcengruppe geladen werden, werden die QueryNodes dieser Ressourcengruppe gleichmäßig auf die Replikate verteilt, um sicherzustellen, dass der Unterschied in der Anzahl der QueryNodes jedes Replikats 1 nicht überschreitet.
Wie geht es weiter?
Um eine mandantenfähige Milvus-Instanz einzurichten, lesen Sie das Folgende: