Com a vinculação tardia dinâmica de armazenamento, é possível injetar volumes do agente do Filestore diretamente em pods do sandbox do agente do Google Kubernetes Engine (GKE) em execução e pré-aquecidos após a reivindicação. Ao ignorar o ciclo de vida padrão de VolumeAttachment do Kubernetes, a vinculação tardia dinâmica atinge uma latência de vinculação de armazenamento de menos de 100 ms sem exigir reinicializações de pod.
Essa arquitetura permite que plataformas de agentes de alta densidade e baixa latência:
- Elimine a inicialização a frio do pod e os atrasos na inicialização do contêiner.
- Anexe e desconecte dinamicamente espaços de trabalho persistentes sob demanda.
- Pause ou hibernar sessões de agentes inativos e retome-as em qualquer pod de sandbox pré-aquecido disponível, preservando o estado do sistema de arquivos.
Antes de começar
- Conclua a configuração inicial em Configurar o ambiente do GKE para volumes de agentes do Filestore.
- Verifique se o cluster do GKE executa a versão
1.36.0-gke.3302001ou mais recente. Essa versão é compatível com a anotaçãoforce-sharednecessária para a propagação de montagem do gVisoremptyDir. - Verifique se o
volume-pool-scStorageClassespecificavolumeBindingMode: ImmediateereclaimPolicy: Delete.
Informações gerais da arquitetura
A arquitetura de vinculação tardia consiste em quatro componentes:
- Orquestrador de plataforma ou controlador personalizado:um serviço de plano de controle ou
controlador do Kubernetes que gerencia ciclos de vida de sessão. Ele monitora eventos
SandboxClaim, resolve metadados de volume do locatário, chama a API daemon do nó de armazenamento para vincular ou desvincular o armazenamento e gerencia finalizadores de exclusão. - Daemon do nó de armazenamento:um
DaemonSetprivilegiado executado em cada nó do gVisor que expõe uma API de montagem. SandboxTemplatecomforce-shared:um modelo de pod de sandbox do gVisor que permite que as montagens do host se propaguem dinamicamente para o contêiner de sandbox do gVisor.SandboxWarmPool:um pool de pods de sandbox em execução pré-aquecidos prontos para receber solicitações de montagem instantaneamente após a reivindicação.
Implante o daemon do nó de armazenamento
Crie um manifesto chamado storage-node-daemon.yaml que contenha o DaemonSet
privilegiado:
Aplique o manifesto:
kubectl apply -f storage-node-daemon.yaml
Implante o SandboxTemplate e o SandboxWarmPool
Crie um manifesto chamado sandbox-latebind.yaml que contenha o modelo e o pool
quente:
Aplique o manifesto:
kubectl apply -f sandbox-latebind.yaml
Reivindicar um sandbox e vincular o armazenamento de forma dinâmica
Crie um manifesto de reivindicação chamado
late-bind-claim.yamlque inclua um finalizador de exclusão (agent.sandbox/storage-cleanup):apiVersion: extensions.agents.x-k8s.io/v1alpha1 kind: SandboxClaim metadata: name: late-bind-session-1 namespace: default finalizers: - agent.sandbox/storage-cleanup spec: sandboxTemplateRef: name: late-bind-templateFaça a reivindicação:
kubectl apply -f late-bind-claim.yaml
Provisione dinamicamente um volume usando um manifesto de PVC chamado
agent-volume-pvc.yaml:apiVersion: v1 kind: PersistentVolumeClaim metadata: name: session-1-pvc namespace: default spec: accessModes: [ReadWriteMany] storageClassName: volume-pool-sc resources: requests: storage: 1GiAplique o PVC:
kubectl apply -f agent-volume-pvc.yaml
Recupere o UID do pod atribuído, o nó e os detalhes da exportação do PV de suporte:
POD_NAME=$(kubectl get pods \ -l extensions.agents.x-k8s.io/claimed-by=late-bind-session-1 \ -o jsonpath='{.items[0].metadata.name}') POD_UID=$(kubectl get pod "${POD_NAME}" -o jsonpath='{.metadata.uid}') NODE_NAME=$(kubectl get pod "${POD_NAME}" -o jsonpath='{.spec.nodeName}') PV_NAME=$(kubectl get pvc session-1-pvc -o jsonpath='{.spec.volumeName}') NFS_IP=$(kubectl get pv "${PV_NAME}" \ -o jsonpath='{.spec.csi.volumeAttributes.ip}') NFS_PATH="/$(kubectl get pv "${PV_NAME}" \ -o jsonpath='{.spec.csi.volumeAttributes.volume}')"Envie o sinal de montagem para o daemon do nó no host do pod:
DAEMON_POD=$(kubectl get pods -l app=storage-node-daemon \ --field-selector spec.nodeName="${NODE_NAME}" \ -o jsonpath='{.items[0].metadata.name}') kubectl exec "${DAEMON_POD}" -c daemon -- python3 -c " import urllib.request, json payload = json.dumps({ 'action': 'bind_nfs', 'pod_uid': '${POD_UID}', 'volume_name': 'workspace-volume', 'sub_dir': 'user_data', 'nfs_server': '${NFS_IP}', 'nfs_path': '${NFS_PATH}' }).encode() req = urllib.request.Request( 'http://localhost:9090', data=payload, headers={'Content-Type': 'application/json'}) print(urllib.request.urlopen(req).read().decode()) "Aplique um finalizador de exclusão ao pod reivindicado para evitar uma limpeza prematura enquanto a montagem do host estiver ativa:
kubectl patch pod "${POD_NAME}" --type=merge \ -p '{"metadata":{"finalizers":["agent.sandbox/storage-cleanup"]}}'Verifique a ativação de volume no pod em execução:
kubectl logs "${POD_NAME}" -c agentA saída confirma que o volume foi ativado com sucesso:
Waiting for late-bind signal... Filestore volume mounted successfully! drwxr-xr-x 2 1000 1000 4096 ... user_data
Pausar e retomar uma sessão
Quando uma sessão de agente é concluída ou entra em hibernação, seu orquestrador precisa desmontar o armazenamento do host antes de permitir que o Kubernetes encerre ou recicle o pod.
Inicie a exclusão da
SandboxClaimem segundo plano. Devido ao finalizador, o Kubernetes marca a solicitação para exclusão, mas pausa o encerramento do pod:kubectl delete sandboxclaim late-bind-session-1 --wait=false
Desmonte o compartilhamento NFS no nó host:
kubectl exec "${DAEMON_POD}" -c daemon -- python3 -c " import urllib.request, json payload = json.dumps({ 'action': 'unbind', 'pod_uid': '${POD_UID}', 'volume_name': 'workspace-volume', 'sub_dir': 'user_data' }).encode() req = urllib.request.Request( 'http://localhost:9090', data=payload, headers={'Content-Type': 'application/json'}) print(urllib.request.urlopen(req).read().decode()) "Remova os finalizadores do
SandboxClaime do pod para concluir o encerramento:kubectl patch sandboxclaim late-bind-session-1 --type=merge \ -p '{"metadata":{"finalizers":[]}}' kubectl patch pod "${POD_NAME}" --type=merge \ -p '{"metadata":{"finalizers":[]}}'
Para retomar a sessão mais tarde, reivindique um novo pod pré-aquecido e envie a solicitação de vinculação usando o PVC atual (session-1-pvc). O novo pod de sandbox ganha acesso imediato ao estado preservado do espaço de trabalho.
Considerações Production e design de controlador personalizado
Os comandos manuais neste documento demonstram a mecânica de baixo nível da vinculação tardia dinâmica. Para executar essa arquitetura de maneira confiável em produção, é necessário desenvolver um controlador do Kubernetes ou um orquestrador de plataforma personalizado adaptado ao ciclo de vida da sessão do aplicativo.
Ao projetar o controlador de produção e o daemon de nó, implemente os seguintes padrões de arquitetura:
Automatizar o ciclo de vida do reconciliador e do finalizador
Como o daemon do nó de armazenamento realiza montagens no nível do host fora do gerenciamento padrão do ciclo de vida da interface de armazenamento de contêineres (CSI) do Kubernetes, o Kubelet não tem conhecimento das montagens ativas no emptyDir do pod. Se um pod for excluído enquanto a montagem estiver ativa, o Kubelet não vai remover o diretório emptyDir e vai gerar um erro Device or resource busy, deixando o pod preso em um estado Terminating.
Seu controlador personalizado precisa automatizar uma máquina de estado estrita usando finalizadores
(como agent.sandbox/storage-cleanup):
- Criação e vinculação de reivindicações:
- Anexe um finalizador estático a cada
SandboxClaimna criação. - Acompanhe a API Kubernetes para atualizações de status
SandboxClaim. Quando uma reivindicação é vinculada a um pod de pool quente, extraia os atributos atribuídospod_uid,nodeNamee do volume de suporte do Filestore. - Envie uma solicitação
bindautenticada ao daemon do nó de armazenamento em execução no nó de destino. - Faça o patch imediatamente no objeto
Podem execução para adicionar o finalizador dinâmico. Como as especificações doSandboxTemplatenão oferecem suporte a finalizadores de pod estático, é necessário fazer o patch do pod de forma dinâmica para protegê-lo durante a drenagem de nós ou eventos de reagendamento em que o pod é removido, mas oSandboxClaimpermanece ativo.
- Anexe um finalizador estático a cada
- Encerramento normal e tratamento de remoção:
- Procure
deletionTimestampnos recursosSandboxClaimePod. - Quando uma exclusão ou remoção é detectada, chame o endpoint
unbinddo daemon do nó para desmontar corretamente o diretório do host (umount -l). - Verifique se a desmontagem foi concluída e se todas as gravações pendentes foram
descarregadas antes de corrigir o
Pode oSandboxClaimpara remover os finalizadores. Isso pode ajudar você a realizar um encerramento limpo em exclusões de reivindicações normais, upgrades de nós do GKE, preempções de VM spot e eliminações por falta de memória (OOM).
- Procure
Proteja e reforce o daemon do nó de armazenamento
- Substitua
kubectl execpor APIs autenticadas:em produção, não usekubectl execnem vincule o daemon alocalhost. Configure o daemon do nó de armazenamento para expor um endpoint gRPC ou HTTPS dedicado na rede do cluster protegida com TLS mútuo (mTLS) ou autenticação de tokenServiceAccountdo Kubernetes. - Isolar namespaces de daemon e acesso à rede:implante o
storage-node-daemonDaemonSetprivilegiado em um namespace administrativo restrito (por exemplo,sandbox-storage-system) em vez dos namespacesdefaultou de locatário. Aplique regras do KubernetesNetworkPolicyque permitem a entrada na API do daemon exclusivamente dos pods do controlador personalizado e bloqueiam todo o tráfego dos pods do agente em sandbox. - Use imagens de contêiner predefinidas:evite instalar pacotes como
nfs-commonno ambiente de execução em uminitContainer. Use uma imagem de contêiner imutável e pré-criada com todos os utilitários de montagem necessários pré-instalados para eliminar atrasos na inicialização do nó e dependências de repositórios externos.
Aplicar cotas de armazenamento e isolamento multilocatário
- Monitorar o uso do armazenamento por agente:quando você vincula dinamicamente um subdiretório de um volume compartilhado do Filestore
ReadWriteMany(RWX) a umemptyDir, as configurações padrão do KubernetesemptyDir.sizeLimitnão podem aplicar cotas de armazenamento por agente no caminho NFS ativado. Para evitar que um único agente descontrolado esgote o volume compartilhado e cause uma negação de serviço (DoS), implemente o monitoramento de cota de diretório no seu orquestrador ou provisione volumes dedicados usando pools de volume. - Adapte os payloads de montagem para diferentes modos de acesso ao espaço de trabalho:seu
controlador pode oferecer suporte a várias topologias de armazenamento de agentes variando os
parâmetros enviados ao daemon do nó:
- Espaços de trabalho isolados particulares:vincule um subdiretório exclusivo do locatário ou um PVC dedicado a um único pod de sandbox com permissões de leitura e gravação.
- Espaços de trabalho colaborativos:vincule simultaneamente o mesmo subdiretório RWX compartilhado em vários pods de agentes coordenadores para compartilhamento de arquivos em tempo real.
- Espaços de trabalho de ramificação de análise detalhada:monte um diretório de modelo base
como somente leitura (
ro) para que os agentes possam ler recursos compartilhados sem modificar a cópia principal, enquanto roteiam novas gravações para um caminho de trabalho gravável separado ou um diretório de cópia na restauração.
Coordenar snapshots e limpeza pontuais
- Faça a quiescência de gravação antes dos snapshots:para capturar snapshots consistentes do espaço de trabalho em um determinado momento sem corromper os dados, o orquestrador precisa pausar as gravações ativas iniciando o fluxo de trabalho de desvinculação (ou liberando os buffers do sistema de arquivos) antes de arquivar o diretório do espaço de trabalho ou acionar um snapshot do Filestore.
- Automatize o desprovisionamento de locatários:quando uma sessão de usuário ou um espaço de trabalho expirar permanentemente, verifique se o controlador primeiro desvincula todas as montagens ativas em todos os nós antes de executar tarefas assíncronas em segundo plano para excluir os diretórios persistentes do locatário do volume de suporte.
Para uma implementação de referência completa que mostra o gerenciamento dinâmico de finalizadores, o isolamento multitenant e os fluxos de trabalho de restauração de snapshots, consulte o exemplo de armazenamento de vinculação tardia do GKE Sandbox no GitHub.
A seguir
- Conheça a integração estática do sandbox do agente.
- Implante cargas de trabalho do GKE autogerenciadas.
- Saiba como criar e gerenciar pools de volume.