Projetar sua infraestrutura para o Agent Substrate no GKE

O Google Kubernetes Engine (GKE) permite escalonar clusters do Agent Substrate no GKE para oferecer suporte a cargas de trabalho de agentes de IA de alta simultaneidade, minimizando os custos de computação ociosa. Ao combinar pools de nós de infraestrutura de sistema dedicados com ComputeClasses personalizadas e buffers de capacidade, é possível isolar componentes críticos do sistema, automatizar o fallback de hardware em séries de máquinas do Compute Engine e manter a capacidade de worker em espera ativa.

Leia este documento para dimensionar corretamente os componentes do cluster do GKE, isolar a infraestrutura do sistema e os pools de nós do banco de dados, configurar o provisionamento automático de nós para recursos do WorkerPool e planejar a manutenção e os upgrades do cluster. Este documento é destinado a administradores de plataforma que provisionam e gerenciam o Agent Substrate na infraestrutura do GKE.

Antes de começar

Antes de configurar a infraestrutura do cluster para escalonamento, verifique se o ambiente atende aos seguintes requisitos:

  • Modo e versão do cluster do GKE: use um cluster do GKE Standard em que o plano de controle e os pools de nós executem a versão 1.37 ou mais recente. Os clusters do Autopilot do GKE não são compatíveis com o Agent Substrate no GKE.
  • Instalação do Agent Substrate no GKE: é necessário ter uma instalação do Agent Substrate no GKE ou planejar a instalação do Agent Substrate no GKE com o isolamento de nós da infraestrutura do sistema ativado. Para conferir as etapas de instalação, consulte Instalar o Agent Substrate no GKE.

Dimensionar o cluster do Agent Substrate

Dimensione o cluster para picos simultâneos e taxas de retomada de pico, em vez de médias. Os nós de trabalho precisam conter o número máximo de atores em execução ao mesmo tempo, e os componentes da infraestrutura do sistema e os caminhos de armazenamento precisam acompanhar a taxa mais rápida de retomadas e suspensões de atores.

Estimar os picos de carga de trabalho

Se você não tiver medições de produção, estime a simultaneidade de pico e a taxa de retomada com base no número total de atores registrados (total_actors), na fração média de atores em execução simultânea (average_active_fraction), no multiplicador de pico de tráfego (burst_factor) e na duração média em que um ator permanece ativo por turno (average_seconds_active_per_resume):

peak_running_actors = total_actors × average_active_fraction × burst_factor
peak_resume_rate    = peak_running_actors / average_seconds_active_per_resume

Por exemplo, se você tiver 100.000 atores no total, com 10% ativos em média (10.000 atores) e um fator de pico de dois, seu cluster precisará oferecer suporte a 20.000 atores em execução no pico. Se cada ator for executado por uma média de 100 segundos antes de ser suspenso, para manter esse pico, serão necessárias 200 retomadas por segundo e 200 suspensões por segundo.

O Agent Substrate no GKE não suspende automaticamente os atores inativos. Seu aplicativo ou orquestrador precisa acionar a suspensão.

Dimensionar o plano de dados

Nesta seção, abordamos as práticas recomendadas para dimensionar o plano de dados, incluindo pods de worker, nós do GKE, largura de banda de snapshot e o agente de nó atelet.

Calcular pods de worker e nós do GKE

Cada pod de worker em um WorkerPool hospeda vários atores. Um worker pode aceitar um novo ator enquanto hospeda menos atores do que o limite definido pela flag --max-actors no contêiner ateworker (padrão 1000) e tem capacidade de CPU e memória restante suficiente para se ajustar aos limites de ActorTemplate. Para mais informações sobre como os limites de workers interagem com os limites de atores, consulte o guia de código aberto Recursos de dimensionamento adequado na API Agent Substrate.

Calcule o número necessário de pods de worker com 20% de capacidade de programação disponível (o fator 0.8):

slots_per_worker = min(max_actors, worker_memory / actor_memory, worker_cpu / actor_cpu)
total_workers    = peak_running_actors / (slots_per_worker × 0.8)

Nessa fórmula, a variável max_actors assume como padrão 1000 (configurada pela flag --max-actors). Subtraia a memória que o worker reserva para o próprio processo do valor de worker_memory. Para recursos de WorkerPool de microVM, adicione a sobrecarga de memória do monitor de máquina virtual (VMM) por ator informada pelo Worker ao valor actor_memory, porque o posicionamento conta essa sobrecarga em relação à capacidade do Worker.

Com um pod de worker por nó, o número de workers é igual ao número de nós de trabalho do GKE. Não aloque 100% da CPU e da memória alocáveis de um nó para o pod de worker. Deixe capacidade em cada nó para o DaemonSet atelet (no namespace ate-system) e os daemons do sistema do GKE. O GKE Standard aceita até 65.000 nós por cluster e até 512 pods por nó. O máximo padrão é de 110 pods por nó (configurável com a flag --max-pods-per-node), e clusters grandes têm requisitos adicionais.

Dimensionar a largura de banda do snapshot e os discos de nós

Cada suspensão do Actor grava um snapshot no disco do nó local no caminho do diretório /var/lib/ate. No modo SYNCHRONOUS, o agente atelet faz upload do snapshot para o Cloud Storage antes que a operação de suspensão seja retornada. No modo ASYNCHRONOUS, o agente atelet programa o upload para o Cloud Storage após um atraso de aproximadamente o valor da flag --snapshot-upload-delay (padrão 5m) a partir do momento em que o snapshot é criado. O Agent Substrate armazena em cache o snapshot mais recente no nó pelo período configurado na flag --snapshot-detach-delay, que tem como padrão um valor de 30m. Um ator que é retomado dentro dessa janela é programado no mesmo nó e restaurado do disco local, em vez de fazer o download do snapshot do Cloud Storage.

Estime o tamanho do snapshot descompactado (snapshot_size) como o limite de memória do ActorTemplate mais os dados gravados no sistema de arquivos raiz gravável ou nos diretórios duráveis. O processamento de arquivos esparsos e a compactação zstd reduzem o tamanho do snapshot compactado (compressed_snapshot_size) transferido de e para o Cloud Storage, onde o Agent Substrate armazena até dois snapshots por ator (o anterior e o em andamento):

  • Largura de banda de pico do Cloud Storage (cada direção): peak_resume_rate × compressed_snapshot_size
  • Capacidade de processamento de gravação de disco de pico por nó: 2 × peak_resume_rate × snapshot_size / total_nodes (tanto os downloads de retomada quanto os checkpoints de suspensão gravam no disco do nó).

O disco de inicialização hyperdisk-balanced padrão de 100 GiB no GKE oferece 3.600 IOPS e 290 MiB/s de capacidade de processamento (o desempenho padrão é dimensionado de acordo com o tamanho do disco), o que pode limitar as operações de snapshot em caso de E/S pesada do convidado. Ao criar pools de nós de trabalho, monte um disco dedicado no caminho do diretório /var/lib/ate/snapshots ou aumente o desempenho do disco de inicialização usando as flags --boot-disk-provisioned-iops e --boot-disk-provisioned-throughput com o comando gcloud container node-pools create.

Verificar a cota do Security Token Service

Cada transferência de snapshot de ou para o Cloud Storage requer um token com escopo para o ator, que o componente ate-api-server solicita da API Security Token Service (STS). O componente ate-api-server faz até uma solicitação de STS por ator a cada 20 a 40 minutos (normalmente na primeira retomada nessa janela) e reutiliza o token até 20 minutos antes da expiração.

A cota Exchange token global requests do STS tem como padrão 6.000 solicitações por projeto por minuto e é compartilhada com a Federação de Identidade da Carga de Trabalho para GKE:

  • Cota necessária do STS (solicitações por minuto): estimativa de até 60 vezes a taxa de pico de retomadas para atores cuja última transferência de snapshot foi há mais de 20 minutos, além da capacidade para a Federação de Identidade da Carga de Trabalho para GKE. Se você não souber essa taxa, use a fórmula 60 × peak_resume_rate como substituto. Por exemplo, 200 currículos por segundo exigem até 12.000 solicitações por minuto.
  • Como aumentar a cota: solicite um aumento de cota na página Cotas e limites do sistema no console do Google Cloud . As transferências de snapshot funcionam sem a API Security Token Service ativada no projeto, mas é necessário ativar a API para que a cota apareça na página Cotas e limites do sistema. Para mais informações, consulte Cota para a API Exchange Token no Security Token Service na documentação da Federação de Identidade da Carga de Trabalho para GKE.

Ajustar o agente de nó atelet

O DaemonSet atelet executa um pod por nó de trabalho para restaurar e criar checkpoints de atores. Em cada pod atelet, o contêiner objectstore-plugin compacta e transfere snapshots de e para o Cloud Storage.

A tabela a seguir descreve as principais configurações de dimensionamento para os contêineres atelet e objectstore-plugin:

Configuração Padrão Orientações de dimensionamento
CPU e memória do contêiner atelet e objectstore-plugin Solicitações: 50 mCPU, 128 MiB de memória; sem limites Configure a QoS com capacidade de burst sem limites de memória omitindo os limites de memória em ambos os contêineres, porque atingir um limite de memória interrompe o agente do nó e todas as retomadas no nó. Aumente as solicitações de CPU e memória para corresponder ao pico de uso medido em um nó completo na taxa de retomada de pico para que os pods de worker não consumam os recursos necessários para o agente do atelet.
--upload-memory-budget (contêiner objectstore-plugin) e --snapshot-upload-workers (contêiner atelet) Orçamento de 512 MiB; 2 workers de upload

Para evitar que uploads simultâneos de snapshots fiquem em fila em nós com vários atores com altas taxas de suspensão, faça o seguinte:

  • Aumente o valor da flag --upload-memory-budget (e defina a solicitação de memória do contêiner objectstore-plugin como maior que esse orçamento).
  • Aumente o valor da flag --snapshot-upload-workers.
Sinalização --drain-timeout e campo terminationGracePeriodSeconds 5 minutos (flag --drain-timeout); 330 segundos (campo terminationGracePeriodSeconds) Controla por quanto tempo o agente do atelet conclui as restaurações em andamento e os uploads de snapshot quando o pod é interrompido durante uma drenagem ou upgrade de nó. Se você aumentar o valor da flag --drain-timeout para snapshots grandes, aumente também o valor do campo terminationGracePeriodSeconds no DaemonSet atelet para que o agente kubelet não interrompa o agente atelet antes que os uploads sejam concluídos.

Dimensionar os componentes da infraestrutura do sistema e o banco de dados

Para implantações de componentes do sistema sem estado no namespace ate-system que escalonam horizontalmente, estime a contagem de réplicas necessária com 35% de capacidade disponível (o fator 0.65) mais uma réplica sobressalente (+ 1) para tolerar interrupções de nós:

replicas = ceil(peak_load / (capacity_per_replica × 0.65)) + 1

O escalonamento horizontal para de melhorar a capacidade de processamento depois que uma dependência compartilhada (como a CPU do PostgreSQL ou os limites de conexão) atinge a capacidade máxima. Dimensione os componentes da infraestrutura do sistema e o banco de dados juntos. Para mais informações sobre as flags de componente, consulte o guia de instalação do operador de código aberto.

A tabela a seguir descreve as regras de recursos e escalonamento para cada componente de infraestrutura do sistema:

Componente Regras de QoS e de recursos Escalonamento e ajuste de conexão
Roteador de entrada (atenet-router) Configure a QoS garantida: defina solicitações e limites de CPU e memória correspondentes para que o ambiente de execução do Go alinhe a contagem de linhas de execução com o orçamento de CPU do contêiner. Escalone as réplicas horizontalmente com base no pico de solicitações de entrada por segundo, na sobrecarga de mTLS e nos tamanhos de payload de solicitação.
Servidor de API (ate-api-server) Configure a QoS garantida: defina solicitações e limites de CPU e memória correspondentes para que o ambiente de execução do Go alinhe a contagem de linhas de execução com o orçamento de CPU do contêiner. Escalone as réplicas com base nos ciclos de retomada e suspensão de pico por segundo (aproximadamente 4 ms de CPU por ciclo de retomada e suspensão) e na carga de pesquisa de solicitações de entrada. Defina explicitamente a configuração ateapi.postgres.poolMaxConns. Cada réplica abre até poolMaxConns + 6 conexões de banco de dados, e a soma de todas as réplicas precisa ser menor que a configuração max_connections do PostgreSQL.
Banco de dados (PostgreSQL) Configure a QoS com capacidade de intermitência com memória garantida: defina solicitações de memória iguais aos limites para bloquear o cache do shared_buffers na memória e ajudar a evitar a remoção por falta de memória (OOM, na sigla em inglês). Omita os limites de CPU ao executar em um nó ate-postgres dedicado para que a limitação de CPU do kernel não desacelere as intermitências de checkpoint e autovacuum. Escalone verticalmente esse StatefulSet de instância única. Provisione CPU suficiente para ficar com menos de 80% de utilização no pico de carga, RAM suficiente para armazenar em cache a tabela Actor (com o parâmetro shared_buffers definido como aproximadamente 25% da memória) e um StorageClass com suporte de SSD definido no momento da instalação com a configuração ateapi.postgres.storageClass (por exemplo, o StorageClass dynamic-rwo em C4 ou N4 ou o StorageClass premium-rwo em C3 ou N2). Aumente o parâmetro max_connections no ConfigMap postgres-config (o arquivo postgresql.conf) para que todas as réplicas ate-api-server caibam.
Proxy de saída (atenet-egress) Regras de recursos: defina uma solicitação de CPU de 512 mCPU (limite de 8 vCPUs) e uma solicitação de memória de 512 MiB (limite de 1 GiB). Aumente as solicitações e os limites se o tráfego de saída do Actor apresentar aumento de latência. Escalonar réplicas horizontalmente com base no tráfego de solicitações de saída do ator.
Controlador de worker (ate-controller) Regras de recursos: mantenha uma réplica (sem eleição de líder). Defina solicitações de CPU e memória com base no pico de uso durante grandes eventos de escalonamento horizontal do WorkerPool. Se os novos pods de worker demorarem muito para serem registrados em frotas grandes, aumente o valor da flag --worker-sync-concurrency (padrão 2).
Controlador de certificado (podcertificate-controller) Regras de recursos: são executadas no namespace podcertificate-controller-system para assinar certificados mTLS para novos pods de worker antes que eles possam aceitar atores. Para frotas de milhares de workers, aumente os valores da configuração podcert.workersPerSigner (padrão 0, que mantém o padrão do manifesto de 1 worker por signatário), a flag --kube-api-qps (padrão 5) e a flag --kube-api-burst (padrão 10).
Provedor de identidade (ate-idp-server) Regras de recursos: exibe o documento de descoberta do OpenID Connect (OIDC) e o conjunto de chaves da Web JSON (JWKS) no namespace ate-system para verificar os JSON Web Tokens (JWTs) do ator. Defina as solicitações de CPU e memória com base no volume máximo de consultas JWKS. Escalone as réplicas horizontalmente se as partes confiáveis consultarem o endpoint JWKS com altas taxas de solicitação sem armazenar as chaves de cache.
Provedor de credenciais (k8s-credential-provider) Regras de recursos: são executadas no namespace ate-system quando a injeção de credenciais do Secret do Kubernetes está ativada. Defina as solicitações de CPU e memória com base no volume de injeção de credenciais de saída. Escalone as réplicas horizontalmente com base na taxa de solicitações de saída do ator que exigem a injeção de credenciais do Secret do Kubernetes no proxy atenet-egress.

Isolar a infraestrutura do sistema e os pools de nós de trabalho

Em clusters de produção, o tráfego de snapshots e a programação de atores com alta rotatividade nos nós de trabalho podem competir com os componentes da infraestrutura do sistema por CPU, memória e E/S de disco. Separe o cluster do GKE em três níveis distintos de pool de nós:

  • Pool de nós do banco de dados (rótulo ate-postgres): um pool de nós dedicado para o StatefulSet do PostgreSQL, que armazena todos os metadados de atores e workers.
  • Pool de nós de infraestrutura do sistema (rótulo ate-system-infra): um pool de nós dedicado para os serviços restantes de infraestrutura do sistema (os componentes atenet-router, ate-api-server, atenet-egress, ate-controller, ate-idp-server e k8s-credential-provider).
  • Pools de nós de trabalho (WorkerPool): pools de nós escalonados dinamicamente gerenciados por ComputeClasses personalizadas do GKE e buffers de capacidade do GKE para hospedar sandboxes de atores ativos.

É possível isolar os componentes de infraestrutura do banco de dados e do sistema em nós dedicados ao instalar o Agent Substrate no GKE usando a configuração cordonSystemInfraComponents (ou a flag --cordon-system-infra-components). Antes de instalar, crie os dois pools de nós com os rótulos necessários e os taints NoSchedule:

# 1. Create the dedicated PostgreSQL node pool
gcloud container node-pools create ate-postgres-pool \
    --cluster=CLUSTER_NAME \
    --location=LOCATION \
    --node-locations=POSTGRES_ZONE \
    --machine-type=POSTGRES_MACHINE_TYPE \
    --num-nodes=1 \
    --node-labels=ate.dev/workloadType=ate-postgres \
    --node-taints=ate.dev/workloadType=ate-postgres:NoSchedule

# 2. Create the dedicated system infrastructure node pool
gcloud container node-pools create ate-system-infra-pool \
    --cluster=CLUSTER_NAME \
    --location=LOCATION \
    --machine-type=SYSTEM_INFRA_MACHINE_TYPE \
    --num-nodes=2 \
    --node-labels=ate.dev/workloadType=ate-system-infra \
    --node-taints=ate.dev/workloadType=ate-system-infra:NoSchedule

Substitua:

  • CLUSTER_NAME: o nome do cluster do GKE Standard.
  • LOCATION: a zona ou região do Compute Engine do plano de controle do cluster.
  • POSTGRES_ZONE: uma única zona do Compute Engine para o StatefulSet do PostgreSQL de réplica única. Para um cluster regional, especifique uma zona na região do cluster para que o pool de nós provisione um único nó na zona em que o disco permanente do StatefulSet será criado. Para um cluster zonal, especifique a zona do cluster.
  • POSTGRES_MACHINE_TYPE: o tipo de máquina do Compute Engine dimensionado para sua instância do PostgreSQL.
  • SYSTEM_INFRA_MACHINE_TYPE: o tipo de máquina do Compute Engine dimensionado para os componentes da infraestrutura do sistema (por exemplo, o tipo de máquina c3-standard-8).

Em um cluster regional, a flag --num-nodes se aplica por zona. Por exemplo, definir a flag --num-nodes=2 em três zonas cria seis nós no total. Para colocar o pool de nós ate-system-infra-pool em zonas específicas, adicione a flag --node-locations.

Se você ativar a configuração cordonSystemInfraComponents durante a instalação e um dos pool de nós estiver ausente, os pods do sistema correspondentes vão permanecer no estado Pending.

Automatizar a segmentação de hardware usando ComputeClasses personalizadas

As ComputeClasses personalizadas do GKE permitem definir listas priorizadas de séries de máquinas do Compute Engine para os nós do WorkerPool e usar automaticamente séries de máquinas secundárias se uma zona tiver indisponibilidade de recursos.

Selecionar a série de máquinas do Compute Engine por ambiente de execução

Configure uma ComputeClass separada para cada ambiente de execução de sandbox:

  • Ambiente de execução de MicroVM (Cloud Hypervisor): defina o campo enableNestedVirtualization: true nas prioridades do ComputeClass e segmente as séries de máquinas do Compute Engine x86 que oferecem suporte à virtualização aninhada (como N4, C3, C4 ou N2) ou use tipos de máquina bare metal x86, que executam o KVM de forma nativa e não exigem o campo enableNestedVirtualization. As VMs baseadas em Arm e as séries de máquinas listadas em Restrições de virtualização aninhada não são compatíveis.
  • Ambiente de execução do gVisor: use como destino séries de máquinas do Compute Engine x86 de uso geral ou otimizadas para computação (como N4, C3, C4 ou N2) sem a virtualização aninhada ativada. As séries de máquinas que são executadas em microarquiteturas de CPU mistas (como E2) e VMs baseadas em Arm não são compatíveis com o Agent Substrate no GKE. Todos os recursos WorkerPool selecionados por um ActorTemplate precisam compartilhar a mesma arquitetura de CPU.

Para uma comparação detalhada, consulte Selecionar um ambiente de execução de sandbox.

Regras de ComputeClass necessárias para o Agent Substrate

Ao criar uma ComputeClass para o Agent Substrate em nós de trabalho do GKE, siga estas regras:

  • Use ComputeClasses baseadas em nós: não use o faturamento baseado em pods (o campo podFamily), que não é compatível com a virtualização aninhada.
  • Não defina o campo nodePoolConfig.sandbox.type: microvm: essa configuração ativa o ambiente de execução do Kata para o GKE Sandbox em cargas de trabalho baseadas em pods e aplica um taint sandbox.gke.io/runtime=microvm que impede que o Agent Substrate nos workers do GKE programe. O Agent Substrate no GKE gerencia o próprio ambiente de execução de microVM e requer apenas a virtualização aninhada do Compute Engine em nós padrão do Container-Optimized OS (tipo de imagem cos_containerd).
  • Defina as configurações do kernel do Linux sysctl para alta densidade de sandbox: se um nó de trabalho executar mais de aproximadamente 800 sandboxes simultâneos, será necessário aumentar os limites do kernel do Linux para monitoramento do sistema de arquivos (parâmetros fs.inotify.*) e para a tabela de vizinhos do protocolo de resolução de endereço (ARP, na sigla em inglês) (parâmetros net.ipv4.neigh.default.gc_thresh*). O instalador aplica essas configurações apenas aos clusters que ele cria. Ao configurar ComputeClasses personalizadas (usando o campo priorityDefaults.nodeSystemConfig.linuxNodeConfig.sysctls) ou pré-criar pools de nós de trabalho (usando a flag --system-config-from-file), aplique os seguintes valores de sysctl:

    • fs.inotify.max_user_instances: 65536
    • fs.inotify.max_user_watches: 1048576
    • net.ipv4.neigh.default.gc_thresh1: 4096
    • net.ipv4.neigh.default.gc_thresh2: 16384
    • net.ipv4.neigh.default.gc_thresh3: 32768

    Para mais informações sobre como configurar os parâmetros sysctl do nó, consulte Opções de configuração do Sysctl.

Escolher um padrão de escalonamento vertical para recursos do WorkerPool de microVMs

Os pods de worker da MicroVM solicitam o recurso estendido ate.dev/kvm: 1, que é anunciado pelo plug-in de dispositivo atelet somente depois que um nó é inicializado e o agente atelet é iniciado. Como o provisionamento automático de nós do GKE não vê o recurso estendido ate.dev/kvm em modelos de nós não provisionados, um pod de worker de microVM pendente não pode acionar o GKE para criar automaticamente um novo pool de nós a partir de zero réplicas por conta própria. No entanto, se um pool de nós já tiver pelo menos um nó em execução, o GKE poderá escaloná-lo verticalmente.

A tabela a seguir compara os padrões compatíveis para escalonamento de recursos do WorkerPool de microVM no GKE:

Padrão Como funciona Escalona para 0 nós? Fallback de várias prioridades para indisponibilidade de recursos? Ideal quando
Pools criados automaticamente com o campo minimumCapacity Define os campos nodePoolAutoCreation.enabled: true e minimumCapacity.targetNodeCount: 1 com prioridades machineType explícitas. Não (mantém um nó de espera em execução) Somente no provisionamento inicial para o campo minimumCapacity no nível da classe. O substituto de intermitência média exige a definição do campo minimumCapacity.targetNodeCount: 1 em cada entrada de substituto no campo spec.priorities[] (mantendo um nó de espera por prioridade) Você quer a criação automatizada de pool de nós e sempre manter pelo menos um nó de trabalho em execução.
Pools criados automaticamente com buffers de capacidade Combina o campo nodePoolAutoCreation.enabled: true com um CapacityBuffer ativo ou em espera cujo PodTemplate omite o recurso estendido ate.dev/kvm. Suspende para o modo de espera (por exemplo, aproximadamente 8% do custo de computação em execução em um nó c3-standard-4 com um disco de inicialização padrão de 100 GiB) Sim (os marcadores de posição de buffer acionam o substituto do provisionamento automático de nós em todas as prioridades) Você quer um escalonamento rápido de burst (1 a 4 segundos ativo ou 20 a 35 segundos para retomar os nós em espera e 50 a 85 segundos até que os novos workers estejam prontos) com baixo custo de inatividade.
Pools de 0 nós pré-criados com rótulos de modelo de nó Pré-cria pools de nós com escalonamento automático de 0 nós com o rótulo clusterautoscaler-nodetemplate-resources.ate.dev/kvm=4096 e os referencia no campo spec.priorities[].nodepools da ComputeClass. Sim (0 nós verdadeiros, sem custo de computação ociosa) Sim, nos pools de nós pré-criados Você quer uma redução da escala a zero real sem manter buffers de capacidade em espera.

Exemplo: ComputeClass com o campo minimumCapacity

Ao usar o campo minimumCapacity, cada entrada no campo spec.priorities precisa especificar um campo machineType explícito, em vez de especificar apenas o campo machineFamily:

apiVersion: cloud.google.com/v1
kind: ComputeClass
metadata:
  name: substrate-microvm
spec:
  priorityDefaults:
    enableNestedVirtualization: true
    nodeSystemConfig:
      linuxNodeConfig:
        sysctls:
          fs.inotify.max_user_instances: 65536
          fs.inotify.max_user_watches: 1048576
          net.ipv4.neigh.default.gc_thresh1: 4096
          net.ipv4.neigh.default.gc_thresh2: 16384
          net.ipv4.neigh.default.gc_thresh3: 32768
  priorities:
  - machineType: n4-standard-4
  - machineType: c3-standard-4
  - machineType: n2-standard-4
  nodePoolAutoCreation:
    enabled: true
  minimumCapacity:
    targetNodeCount: 1
  nodePoolConfig:
    imageType: cos_containerd
  whenUnsatisfiable: DoNotScaleUp

Para segmentar um WorkerPool para essa ComputeClass, adicione o campo nodeSelector: {cloud.google.com/compute-class: substrate-microvm} ao modelo de pod do WorkerPool.

Quando uma ComputeClass faz fallback em diferentes séries de máquinas x86 (como de N4 para C3 ou N2), a restauração de um snapshot durável com fidelidade de memória (o modo SNAPSHOT_FIDELITY_MEMORY) requer um worker com recursos de CPU que sejam um superconjunto dos recursos de CPU do snapshot. Se nenhum Worker compatível tiver capacidade, o Agent Substrate vai fazer o downgrade da restauração para a fidelidade do sistema de arquivos raiz (o modo SNAPSHOT_FIDELITY_ROOTFS), que preserva o sistema de arquivos raiz e os dados de volume duráveis ao reiniciar os processos do Actor.

Exemplo: escalonar a partir de zero com pools de nós zero pré-criados

  1. Salve as configurações de sysctl do kernel do Linux de alta densidade como um arquivo system-config.yaml:

    linuxConfig:
      sysctl:
        fs.inotify.max_user_instances: '65536'
        fs.inotify.max_user_watches: '1048576'
        net.ipv4.neigh.default.gc_thresh1: '4096'
        net.ipv4.neigh.default.gc_thresh2: '16384'
        net.ipv4.neigh.default.gc_thresh3: '32768'
    
  2. Crie previamente os pools de nós de virtualização aninhada primários e de fallback em 0 nó com a flag --system-config-from-file=system-config.yaml e o rótulo clusterautoscaler-nodetemplate-resources.ate.dev/kvm=4096 para que o escalonador automático de cluster do GKE saiba que o pool vazio fornece o recurso estendido ate.dev/kvm:

    # Primary N4 pool (0 nodes)
    gcloud container node-pools create microvm-n4-pool \
        --cluster=CLUSTER_NAME \
        --location=LOCATION \
        --machine-type=n4-standard-4 \
        --enable-nested-virtualization \
        --system-config-from-file=system-config.yaml \
        --num-nodes=0 \
        --enable-autoscaling --total-min-nodes=0 --total-max-nodes=10 \
        --node-labels=cloud.google.com/compute-class=substrate-microvm-zero,clusterautoscaler-nodetemplate-resources.ate.dev/kvm=4096 \
        --node-taints=cloud.google.com/compute-class=substrate-microvm-zero:NoSchedule
    
    # Fallback C3 pool (0 nodes)
    gcloud container node-pools create microvm-c3-pool \
        --cluster=CLUSTER_NAME \
        --location=LOCATION \
        --machine-type=c3-standard-4 \
        --enable-nested-virtualization \
        --system-config-from-file=system-config.yaml \
        --num-nodes=0 \
        --enable-autoscaling --total-min-nodes=0 --total-max-nodes=10 \
        --node-labels=cloud.google.com/compute-class=substrate-microvm-zero,clusterautoscaler-nodetemplate-resources.ate.dev/kvm=4096 \
        --node-taints=cloud.google.com/compute-class=substrate-microvm-zero:NoSchedule
    

    Substitua:

    • CLUSTER_NAME: o nome do cluster do GKE Standard.
    • LOCATION: a zona ou a região do Compute Engine do cluster. O uso das sinalizações --total-min-nodes e --total-max-nodes define os limites de escalonamento automático em todo o pool de nós, em vez de por zona.
  3. Faça referência aos pools pré-criados em ordem de prioridade na sua ComputeClass:

    apiVersion: cloud.google.com/v1
    kind: ComputeClass
    metadata:
      name: substrate-microvm-zero
    spec:
      nodePoolAutoCreation:
        enabled: false
      priorities:
      - nodepools:
        - microvm-n4-pool
      - nodepools:
        - microvm-c3-pool
      whenUnsatisfiable: DoNotScaleUp
    

    O campo priorityDefaults não se aplica a prioridades que usam o campo nodepools, porque as configurações de nó vêm dos pools de nós pré-criados. Assim como nas prioridades de fallback criadas automaticamente, retomar um ator em uma série de máquinas de fallback com recursos de CPU que não são um superconjunto dos recursos de CPU do snapshot faz o downgrade da restauração para o modo SNAPSHOT_FIDELITY_ROOTFS e reinicia os processos do ator.

Para mais informações sobre a configuração da ComputeClass, consulte Controlar atributos de nós escalonados automaticamente com ComputeClasses personalizadas.

Configurar buffers de capacidade para o escalonamento de burst do WorkerPool

Quando um pico de tráfego de atores excede a capacidade dos pods do WorkerPool em execução, os novos pods do Worker precisam aguardar o provisionamento de nós do Compute Engine, a inicialização do agente atelet e o pré-aquecimento de recursos do sandbox antes de atender aos atores. Os buffers de capacidade do GKE removem o provisionamento de nós e o pré-aquecimento de recursos do atelet do caminho crítico:

  • Buffers de capacidade ativos (estratégia buffer.x-k8s.io/active-capacity): mantêm os nós pré-provisionados em execução e prontos para que novos pods de worker sejam programados em um a quatro segundos, pelo custo total de execução da VM do Compute Engine.
  • Buffers de capacidade em espera (estratégia buffer.gke.io/standby-capacity): provisione nós e permita que o agente atelet inicialize e pré-aqueça os recursos do sandbox (o binário runsc ou as imagens do Cloud Hypervisor e do Kata) no disco durante a janela de inicialização em espera antes que o GKE suspenda as VMs do Compute Engine. Essa janela tem como padrão cinco minutos (5m) e é configurada pela anotação buffer.gke.io/standby-capacity-init-time no CapacityBuffer. Aumente o valor dessa anotação se o pré-aquecimento de recursos levar mais de cinco minutos. Quando ocorre uma intermitência, o GKE retoma os nós suspensos em aproximadamente 20 a 35 segundos, e a prontidão de ponta a ponta do WorkerPool (programação de pods de worker, emissão de certificados de pod e registro de capacidade com o componente ate-api-server) é concluída em aproximadamente 50 a 85 segundos. Enquanto suspensa, você paga apenas pela memória da VM suspensa e pelo armazenamento do disco de inicialização. Por exemplo, o armazenamento suspenso custa aproximadamente 8% do custo de computação em execução em um nó c3-standard-4 com um disco de inicialização hyperdisk-balanced padrão de 100 GiB. A porcentagem exata varia de acordo com o formato da máquina, o tamanho do disco de inicialização e as IOPS ou a capacidade de processamento provisionadas.

Para mais informações sobre estratégias de buffer, consulte Estratégias para reduzir a latência de inicialização.

Limitações e regras para buffers de capacidade

Ao configurar buffers de capacidade para o Agent Substrate no GKE, aplique as seguintes regras:

  • Restrições de formato de máquina do Compute Engine para buffers de espera: a suspensão de instâncias do Compute Engine não é compatível com VMs com SSDs locais anexados ou em formatos de máquina com mais de 208 GB de memória (como o tipo de máquina c3-highmem-88 ou formatos bare metal). Para formas de nós de trabalho grandes ou nós com SSDs locais, use buffers de capacidade ativos em vez de buffers de espera.
  • Evite apontar o campo scalableRef para um WorkerPool de 0 réplica ou uma implantação de microVM:
    • O controlador CapacityBuffer do GKE não pode resolver um recurso personalizado (kind: WorkerPool) quando o pool é escalonado para 0 réplicas.
    • Para recursos WorkerPool de microVM, também não aponte o campo scalableRef para a implantação subjacente, porque o modelo de pod de implantação inclui o recurso estendido ate.dev/kvm: 1 (que impede que o provisionamento automático de nós escalone horizontalmente um pool vazio). Em vez disso, configure buffers referenciando um PodTemplate (no campo podTemplateRef) que selecione a ComputeClass sem solicitar o recurso estendido ate.dev/kvm.
    • Para recursos do WorkerPool do gVisor (que não solicitam o recurso estendido ate.dev/kvm), é possível apontar o campo scalableRef diretamente para a implantação subjacente do WorkerPool ou usar um PodTemplate.

Exemplo: CapacityBuffer em espera usando um PodTemplate

O manifesto a seguir define um PodTemplate e um CapacityBuffer em espera que funcionam para recursos de microVM ou gVisor WorkerPool:

apiVersion: v1
kind: PodTemplate
metadata:
  name: substrate-worker-buffer-template
  namespace: WORKERPOOL_NAMESPACE
template:
  spec:
    nodeSelector:
      cloud.google.com/compute-class: COMPUTE_CLASS_NAME
    containers:
    - name: buffer-placeholder
      image: registry.k8s.io/pause:3.9
      resources:
        requests:
          cpu: "WORKER_CPU_REQUEST"
          memory: "WORKER_MEMORY_REQUEST"
---
apiVersion: autoscaling.x-k8s.io/v1beta1
kind: CapacityBuffer
metadata:
  name: substrate-standby-buffer
  namespace: WORKERPOOL_NAMESPACE
spec:
  podTemplateRef:
    name: substrate-worker-buffer-template
  replicas: BUFFER_REPLICAS
  provisioningStrategy: buffer.gke.io/standby-capacity

Substitua:

  • WORKERPOOL_NAMESPACE: o namespace do Kubernetes do WorkerPool (por exemplo, my-workerpool).
  • COMPUTE_CLASS_NAME: o nome da ComputeClass personalizada visada pelo WorkerPool (por exemplo, substrate-microvm).
  • WORKER_CPU_REQUEST e WORKER_MEMORY_REQUEST: as solicitações de CPU e memória de um único pod de worker no WorkerPool (por exemplo, 2 CPUs e 4Gi de memória em um nó de 4 vCPUs), deixando a capacidade do nó para o agente atelet e os daemons do sistema do GKE. A correspondência desses valores com as solicitações de um pod de worker ajuda a garantir que cada réplica de buffer provisione um nó de trabalho em espera, em vez de empacotar vários marcadores de buffer em um único nó.
  • BUFFER_REPLICAS: o número de nós de trabalho em espera a serem mantidos pré-aquecidos (por exemplo, nós 4).

Ao usar buffers de capacidade de espera, adicione uma tolerância para o taint buffer.gke.io/standby-capacity-node-suspended (com o campo operator: "Exists") ao DaemonSet atelet para que os pods atelet de substituição possam permanecer programados em nós de espera suspensos (por exemplo, durante uma atualização gradual do DaemonSet). Para ver um exemplo de configuração do DaemonSet, consulte Pré-carregar imagens em buffers de espera.

Se várias cargas de trabalho compartilharem a mesma ComputeClass, adicione tolerâncias de separação de carga de trabalho correspondentes e rótulos nodeSelector ao modelo de pod do WorkerPool e ao PodTemplate do buffer para que o GKE provisione nós com taints dedicados que outras cargas de trabalho não reivindicam. Adicione também uma tolerância correspondente ao DaemonSet atelet para que o agente atelet possa programar nesses nós dedicados. Para instruções detalhadas, consulte Configurar buffers de capacidade.

Gerenciar upgrades e interações do escalonador automático

Nesta seção, descrevemos como gerenciar atualizações do DaemonSet atelet, eventos de redução de escala vertical do escalonador automático de cluster e upgrades de nós sem interromper os atores ativos.

Atualizar o DaemonSet atelet com buffers de espera

Os nós de buffer em espera suspensos carregam o taint buffer.gke.io/standby-capacity-node-suspended e um taint inacessível enquanto suspensos. Portanto, o agente kubelet em um nó suspenso não pode confirmar as exclusões de pods. Verifique se o DaemonSet atelet tolera o taint buffer.gke.io/standby-capacity-node-suspended (com o campo operator: "Exists"). Mesmo com essa tolerância, durante uma atualização gradual padrão do DaemonSet atelet (com o campo maxUnavailable: 1), os pods em nós suspensos entram no estado Terminating e podem impedir que a atualização avance nos nós em execução.

Antes de atualizar o DaemonSet atelet em um cluster com nós de espera suspensos, faça o seguinte:

  • Para que os nós de espera inacessíveis não esgotem o orçamento de atualização, defina o campo spec.updateStrategy.rollingUpdate.maxUnavailable no DaemonSet atelet como um valor maior que o número máximo de nós de espera suspensos.
  • Antes de atualizar o DaemonSet atelet, escalone temporariamente as réplicas de CapacityBuffer em espera para 0 e aguarde até que o escalonador automático de cluster remova todos os nós de buffer em espera suspensos (normalmente de 10 a 15 minutos). Para verificar se algum nó ainda informa um status de condição Suspended de True, execute o seguinte comando:

    kubectl get nodes -o custom-columns='NAME:.metadata.name,SUSPENDED:.status.conditions[?(@.type=="Suspended")].status'
    

Proteger atores ativos durante a redução de escala vertical e os upgrades

Como os atores ativos são executados em sandboxes gerenciados pelo agente atelet, em vez de pods individuais do Kubernetes, as solicitações de CPU e memória de um nó podem parecer baixas para o escalonador automático de cluster do GKE, mesmo quando um pod de worker nesse nó está hospedando atores ativos. Quando um nó é drenado, o agente kubelet envia um sinal SIGTERM para o pod do worker, e o worker encaminha o sinal SIGTERM para o processo principal de cada contêiner de cada ator que ele hospeda. Cada ator ativo tem até 30 minutos para ser suspenso antes que o worker seja interrompido. Dê a cada ator com estado um gerenciador SIGTERM que não saia no sinal e que chame o método SuspendActor com o modo DURABILITY_MODE_SYNCHRONOUS. Um ator cujo processo principal é encerrado no sinal SIGTERM perde o estado não salvo imediatamente. Se o pod de worker parar enquanto ainda estiver hospedando um ator ativo, o ator vai entrar no estado ACTOR_STATE_CRASHED.

Para minimizar as interrupções durante eventos de redução de escala vertical e upgrades de cluster do GKE, aplique as seguintes práticas:

  • Evite usar VMs spot para nós de WorkerPool com estado, porque a preempção de VM spot fornece apenas de 15 segundos a 2 minutos de aviso de encerramento antes de interromper o nó.
  • Coordene o esvaziamento no seu orquestrador para que os atores ativos sejam verificados e suspensos antes que os pods de worker sejam interrompidos ou impeça que o escalonador automático de clusters remova os pods de worker ativos (por exemplo, usando a anotação de pod cluster-autoscaler.kubernetes.io/safe-to-evict: "false"). Se você definir a anotação safe-to-evict: "false" de forma estática no modelo de pod do WorkerPool, o escalonador automático de clusters não vai reduzir a escala vertical de um nó enquanto um pod de worker estiver programado nele, mesmo quando esse worker hospedar zero atores ativos. Para liberar nós inativos, reduza a escala vertical das réplicas do WorkerPool ou gerencie a anotação dinamicamente. Para mais informações sobre o ciclo de vida de remoção de workers e a recuperação de atores que falharam com o método RevertActor, consulte Remoção no guia da API Agent Substrate.
  • Configure os upgrades súbitos do GKE e revise o Upgrade em um ambiente com restrição de recursos para que novos nós de trabalho sejam provisionados antes que os nós de trabalho atuais sejam isolados e drenados.

A seguir