• Sobre a Milvus
  • Começar
  • Conceitos
  • Guia do Utilizador
  • Importação de dados
  • Ferramentas de IA
  • Guia de Administração
  • Ferramentas
  • Integrações
  • Tutoriais
  • Perguntas frequentes
  • API Reference

Gerir grupos de recursos

No Milvus, pode utilizar um grupo de recursos para isolar fisicamente determinados nós de consulta dos restantes. Este guia explica-lhe como criar e gerir grupos de recursos personalizados, bem como transferir nós entre eles.

O que é um grupo de recursos

Um grupo de recursos pode conter vários ou todos os nós de consulta de um cluster do Milvus. É o utilizador que decide como deseja distribuir os nós de consulta pelos grupos de recursos, com base no que fizer mais sentido para si. Por exemplo, num cenário com várias coleções, pode atribuir um número adequado de nós de consulta a cada grupo de recursos e carregar as coleções em grupos de recursos diferentes, de modo a que as operações dentro de cada coleção sejam fisicamente independentes das de outras coleções.

Note que uma instância do Milvus mantém um grupo de recursos predefinido para conter todos os nós de consulta no arranque e denomina-o __default_resource_group.

A partir da versão 2.4.1, o Milvus disponibiliza uma API declarativa para grupos de recursos, tendo a antiga API de grupos de recursos sido descontinuada. A nova API declarativa permite aos utilizadores alcançar a idempotência, facilitando o desenvolvimento secundário em ambientes nativos da nuvem.

Conceitos de grupo de recursos

Um grupo de recursos é descrito por uma configuração de grupo de recursos:

{
    "requests": { "nodeNum": 1 },
    "limits": { "nodeNum": 1 },
    "transfer_from": [{ "resource_group": "rg1" }],
    "transfer_to": [{ "resource_group": "rg2" }]
}
  • O atributo requests especifica as condições que um grupo de recursos deve cumprir.
  • O atributo «limits» especifica os limites máximos para um grupo de recursos.
  • Os atributos «transfer_from» e «transfer_to» descrevem, respetivamente, de que grupos de recursos um grupo de recursos deve, preferencialmente, adquirir recursos e para que grupos de recursos deve transferir recursos.

Assim que a configuração de um grupo de recursos for alterada, o Milvus ajustará os recursos atuais do Nodo de Consulta tanto quanto possível de acordo com a nova configuração, garantindo que todos os grupos de recursos acabem por cumprir a seguinte condição:

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

Exceto nos seguintes casos:

  • Quando o número de QueryNodes no cluster Milvus for insuficiente, ou seja, NumOfQueryNode < sum(.requests.nodeNum), haverá sempre grupos de recursos sem QueryNodes suficientes.
  • Quando o número de QueryNodes no cluster do Milvus for excessivo, ou seja, NumOfQueryNode > sum(.limits.nodeNum), os QueryNodes redundantes serão sempre colocados primeiro no __default_resource_group.

É claro que, se o número de QueryNodes no cluster se alterar, o Milvus tentará continuamente ajustar-se para cumprir as condições finais. Por conseguinte, pode primeiro aplicar as alterações à configuração do grupo de recursos e, em seguida, efetuar o dimensionamento dos QueryNodes.

Utilizar a API declarativa para gerir o grupo de recursos

Todos os exemplos de código nesta página estão em PyMilvus 3.0.1. Atualize a sua instalação do PyMilvus antes de os executar.

  1. Crie um grupo de recursos.

    Para criar um grupo de recursos, execute o seguinte depois de se ligar a uma instância do Milvus. O trecho de código a seguir pressupõe que default é o alias da sua ligação ao 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. Listar grupos de recursos.

    Depois de criar um grupo de recursos, poderá vê-lo na lista de grupos de recursos.

    Para visualizar a lista de grupos de recursos numa instância do Milvus, proceda da seguinte forma:

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

    Pode solicitar ao Milvus que descreva um grupo de recursos específico da seguinte forma:

    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. Transferir nós entre grupos de recursos.

    Poderá reparar que o grupo de recursos descrito ainda não possui nenhum nó de consulta. Mova alguns nós do grupo de recursos predefinido para o que criou, da seguinte forma: Partindo do princípio de que existem atualmente 1 QueryNodes no __default_resource_group do cluster e que pretendemos transferir um nó para o rg criado. O comando `update_resource_groups ` garante a atomicidade para múltiplas alterações de configuração, pelo que nenhum estado intermédio será visível para o 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. Carregue coleções e partições para um grupo de recursos.

    Assim que houver nós de consulta num grupo de recursos, pode carregar coleções nesse grupo de recursos. O trecho de código seguinte pressupõe que já existe uma coleção chamada 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) 
    

    Além disso, pode simplesmente carregar uma partição num grupo de recursos e fazer com que as suas réplicas sejam distribuídas por vários grupos de recursos. O código a seguir pressupõe que já existe uma coleção chamada Books e que esta possui uma partição chamada 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)
    

    Note que _resource_groups é um parâmetro opcional e, se não for especificado, o Milvus carregará as réplicas nos nós de consulta do grupo de recursos predefinido.

    Para que o Milvus carregue cada réplica de uma coleção num grupo de recursos separado, certifique-se de que o número de grupos de recursos é igual ao número de réplicas.

  6. Transferir réplicas entre grupos de recursos.

    O Milvus utiliza réplicas para alcançar o equilíbrio de carga entre segmentos distribuídos por vários nós de consulta. Pode mover determinadas réplicas de uma coleção de um grupo de recursos para outro da seguinte forma:

    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 um grupo de recursos.

    Pode eliminar um grupo de recursos que não contenha nenhum nó de consulta (limits.node_num = 0) a qualquer momento. Neste guia, o grupo de recursos rg tem agora um nó de consulta. Tem de alterar primeiro a configuração limits.node_num do grupo de recursos para 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}.")
    

Para mais detalhes, consulte os exemplos relevantes no pymilvus

Uma boa prática para gerir o dimensionamento do cluster

Atualmente, o Milvus não consegue escalar para cima ou para baixo de forma independente em ambientes nativos da nuvem. No entanto, ao utilizar a API Declarative Resource Group em conjunto com a orquestração de contentores, o Milvus consegue facilmente alcançar o isolamento e a gestão de recursos para os QueryNodes. Eis uma boa prática para gerir os QueryNodes num ambiente de nuvem:

  1. Por predefinição, o Milvus cria um __default_resource_group. Este grupo de recursos não pode ser eliminado e serve também como grupo de recursos de carregamento predefinido para todas as coleções, sendo-lhe sempre atribuídos QueryNodes redundantes. Por conseguinte, podemos criar um grupo de recursos pendente para alojar recursos de QueryNode não utilizados, impedindo que os recursos de QueryNode sejam ocupados pelo __default_resource_group.

    Além disso, se aplicarmos rigorosamente a restrição sum(.requests.nodeNum) <= queryNodeNum, podemos controlar com precisão a atribuição de QueryNodes no cluster. Vamos supor que existe atualmente apenas um QueryNode no cluster e inicializar o cluster. Eis um exemplo de configuração:

    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 o código de exemplo acima, criamos um grupo de recursos denominado __pending_nodes para armazenar QueryNodes adicionais. Criamos também dois grupos de recursos específicos do utilizador, denominados rg1 e rg2. Além disso, garantimos que o outro grupo de recursos dê prioridade à recuperação de QueryNodes em falta ou redundantes a partir de __pending_nodes.

  2. Expansão horizontal do cluster

    Partindo do princípio de que temos a seguinte função de escalonamento:

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

    Podemos utilizar a API para dimensionar um grupo de recursos específico para um número designado de QueryNodes sem afetar quaisquer outros grupos 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. Redução da escala do cluster

    Da mesma forma, podemos estabelecer regras de redução de escala que priorizem a seleção de QueryNodes do grupo de recursos __pending_nodes. Esta informação pode ser obtida através da API describe_resource_group. Alcançar o objetivo de reduzir a escala de um grupo de recursos específico.

    # 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
    

Como os grupos de recursos interagem com múltiplas réplicas

  • As réplicas de uma única coleção e os grupos de recursos têm uma relação N-a-N.
  • Quando várias réplicas de uma única coleção são carregadas num único grupo de recursos, os QueryNodes desse grupo de recursos são distribuídos uniformemente entre as réplicas, garantindo que a diferença no número de QueryNodes que cada réplica possui não exceda 1.

Próximos passos

Para implementar uma instância multi-tenant do Milvus, leia o seguinte: