Voltar para o blog

Kamaji e HAMi: como testar Kubernetes multi-tenant para workloads de GPU

Uma plataforma de processamento acelerado precisa responder a duas perguntas diferentes: quem controla cada ambiente Kubernetes e como os workloads utilizam as GPUs disponíveis. Misturar essas decisões costuma produzir testes enganosos: dois Pods executando simultaneamente não demonstram isolamento entre clientes, e duas APIs Kubernetes independentes não demonstram compartilhamento seguro de uma GPU.

Kamaji organiza os control planes; HAMi gerencia o compartilhamento de aceleradores dentro do cluster em que está instalado. A combinação é útil, mas não cria automaticamente um pool de GPUs compartilhado entre clusters independentes. A documentação do Kamaji estabelece que cada worker pertence a um único tenant cluster. (kamaji.clastix.io)

Este artigo propõe um laboratório verificável, seguido de critérios para evolução à produção. Os valores são didáticos; os comandos não representam um ambiente executado nem resultados de benchmark.

1. Conceitos e fronteiras de responsabilidade

Kamaji: Kubernetes com control planes hospedados

Kamaji é um operador que reconcilia recursos TenantControlPlane, definidos por uma Custom Resource Definition, ou CRD. Cada recurso representa um control plane Kubernetes hospedado como Pods no management cluster, com API server, scheduler e controller manager próprios. Os workers conectam-se ao endpoint desse tenant. (kamaji.clastix.io)

O DataStore representa o armazenamento do estado dos control planes. Compartilhar sua infraestrutura não significa compartilhar uma única API Kubernetes. Entretanto, cria uma dependência operacional que deve entrar no planejamento de falhas e recuperação. (clastix.io)

HAMi: alocação e controle de recursos de GPU

HAMi significa Heterogeneous AI Computing Virtualization Middleware. No caminho NVIDIA com HAMi-core, os principais componentes são:

  • Webhook: participa da admissão e prepara workloads para o fluxo de scheduling.
  • Scheduler e extender: selecionam nós e dispositivos considerando os recursos solicitados.
  • Device plugin: integra a alocação dos dispositivos ao kubelet.
  • HAMi-core: intercepta chamadas CUDA dentro do contêiner para controlar memória e utilização computacional. (project-hami.io)

Isso não deve ser tratado como uma fronteira de segurança equivalente a uma máquina virtual ou ao particionamento por hardware. Para este projeto, considere o compartilhamento por software apropriado somente depois de avaliar confiança, privilégios e compatibilidade das aplicações. O próprio mecanismo depende da interceptação de chamadas da aplicação. (github.com)

Três dimensões que precisam de testes separados

  1. Isolamento administrativo: identidades, APIs, Secrets e permissões.
  2. Isolamento de execução: workers, rede, armazenamento e runtime.
  3. Isolamento de recursos: memória de GPU, concorrência e interferência de desempenho.

Essa separação é importante porque multi-tenancy envolve tanto o control plane quanto o data plane; namespaces, isoladamente, não resolvem todas essas dimensões. (kubernetes.io)

2. Arquitetura do laboratório

Adote a seguinte topologia proposta:

Management cluster
├── Kamaji + datastore
├── TenantControlPlane tenant-a
└── TenantControlPlane tenant-b

Tenant A: API A → HAMi A → worker GPU A → Pods A1 e A2
Tenant B: API B → HAMi B → worker GPU B → Pods B1 e B2

Neste desenho, A1 e A2 podem compartilhar a GPU do worker A. B1 e B2 podem compartilhar a GPU do worker B. A e B não disputam a mesma GPU física por ação automática do Kamaji ou do HAMi. Essa é uma consequência da associação exclusiva dos workers aos tenant clusters. (kamaji.clastix.io)

Se houver somente uma GPU disponível, teste um tenant cluster e dois namespaces para avaliar compartilhamento interno. Para testar duas APIs, o segundo tenant pode começar sem GPU, mas isso não valida isolamento entre dois pools acelerados.

3. Pré-requisitos e controle de versões

Prepare previamente:

  • Management cluster com Kamaji, suas CRDs, cert-manager e datastore funcionando.
  • StorageClass adequada ao datastore e mecanismo de exposição dos endpoints dos tenants.
  • Workers Linux separados para A e B.
  • kubectl, Helm, kubeadm e envsubst na estação de administração.
  • Plugin Container Network Interface, ou CNI, homologado para cada tenant.

O guia oficial do Kamaji documenta esse bootstrap. Aqui, o ponto de partida é um management cluster já preparado, não uma instalação completa de infraestrutura. (kamaji.clastix.io)

Nos workers GPU, prepare driver NVIDIA compatível com o hardware e a imagem CUDA, NVIDIA Container Toolkit e runtime. Este laboratório assume driver instalado no host e runtime NVIDIA configurado como padrão. Não mantenha dois device plugins registrando nvidia.com/gpu nos mesmos nós; se utilizar GPU Operator, desabilite seu device plugin para esse pool. (project-hami.io)

Antes de executar, registre em Git uma matriz de versões: management cluster, tenant Kubernetes, Kamaji, CNI, HAMi, driver, Toolkit e imagem de teste. Fixe artefatos e digests homologados; não interprete exemplos antigos das documentações como uma recomendação de patch para produção.

4. Criar e verificar os tenant control planes

Salve como tenant.yaml.tpl:

apiVersion: kamaji.clastix.io/v1alpha1
kind: TenantControlPlane
metadata:
  name: ${TENANT_NAME}
  namespace: tenants
spec:
  dataStore: default
  controlPlane:
    deployment:
      replicas: 1
    service:
      serviceType: LoadBalancer
  kubernetes:
    version: "${TENANT_K8S_VERSION}"
    kubelet:
      cgroupfs: systemd
    admissionControllers:
      - ResourceQuota
      - LimitRanger
  networkProfile:
    port: 6443
    serviceCidr: "${TENANT_SERVICE_CIDR}"
    podCidr: "${TENANT_POD_CIDR}"
    dnsServiceIPs:
      - "${TENANT_DNS_IP}"
  addons:
    coreDNS: {}
    kubeProxy: {}
    konnectivity:
      server:
        port: 8132

Esse template usa campos da API do Kamaji. dataStore precisa apontar para um datastore existente; replicas: 1 é uma escolha de laboratório. Ajuste CIDRs, versão e exposição do Service. Um LoadBalancer não é necessariamente privado: configure a implementação da infraestrutura antes de aplicar. (kamaji.clastix.io)

Defina MGMT_CONTEXT com o contexto administrativo e TENANT_K8S_VERSION com uma versão exata homologada, incluindo o prefixo v:

: "${MGMT_CONTEXT:?Defina o contexto do management cluster}"
: "${TENANT_K8S_VERSION:?Defina a versao Kubernetes homologada}"
export TENANT_K8S_VERSION

kubectl --context "$MGMT_CONTEXT" create namespace tenants \
  --dry-run=client -o yaml | kubectl --context "$MGMT_CONTEXT" apply -f -

export TENANT_NAME=tenant-a
export TENANT_SERVICE_CIDR=10.96.0.0/16
export TENANT_POD_CIDR=10.244.0.0/16
export TENANT_DNS_IP=10.96.0.10

envsubst '${TENANT_NAME} ${TENANT_K8S_VERSION} ${TENANT_SERVICE_CIDR} ${TENANT_POD_CIDR} ${TENANT_DNS_IP}' \
  < tenant.yaml.tpl > tenant-a.yaml

kubectl --context "$MGMT_CONTEXT" apply --dry-run=server -f tenant-a.yaml
kubectl --context "$MGMT_CONTEXT" apply -f tenant-a.yaml
kubectl --context "$MGMT_CONTEXT" -n tenants get tenantcontrolplanes,pods,svc

Repita para tenant-b, usando, por exemplo, 10.97.0.0/16, 10.245.0.0/16 e 10.97.0.10. Esses endereços são ilustrativos: verifique conflitos com management, redes corporativas e rotas futuras.

Quando o control plane estiver pronto, extraia seu kubeconfig administrativo:

umask 077
kubectl --context "$MGMT_CONTEXT" -n tenants \
  get secret tenant-a-admin-kubeconfig \
  -o jsonpath='{.data.admin\.conf}' | base64 --decode > tenant-a.kubeconfig

kubectl --kubeconfig tenant-a.kubeconfig cluster-info
kubeadm --kubeconfig tenant-a.kubeconfig token create --print-join-command

Execute o comando de join gerado somente nos workers de A. Repita a extração e o join para B. Instale o CNI em cada tenant, com os CIDRs correspondentes, e valide os nós. O kubeconfig administrativo é um Secret operacional, não um arquivo para commit. (kamaji.clastix.io)

kubectl --kubeconfig tenant-a.kubeconfig get nodes -o wide
kubectl --kubeconfig tenant-b.kubeconfig get nodes -o wide

5. Instalar HAMi dentro de cada tenant

Salve hami-values.yaml para o cenário com driver e Toolkit administrados no host:

devicePlugin:
  deviceListStrategy: envvar
  nvidiaDriverRoot: /
  gpuOperatorToolkitReady:
    enabled: false

O arquivo seleciona a estratégia por variável de ambiente, o diretório do driver no host e desabilita a espera pelo marcador do GPU Operator. Ele pressupõe runtime NVIDIA padrão. Não reutilize esses valores em uma instalação com driver containerizado sem adaptar os caminhos e o runtime. (project-hami.io)

Escolha uma versão do chart cuja documentação contenha esses parâmetros:

: "${HAMI_CHART_VERSION:?Defina a versao homologada do chart}"
: "${GPU_NODE_A:?Defina o nome do worker GPU de A}"

helm repo add hami-charts https://project-hami.github.io/HAMi/
helm repo update
helm show values hami-charts/hami --version "$HAMI_CHART_VERSION" > hami-defaults.yaml

kubectl --kubeconfig tenant-a.kubeconfig label node "$GPU_NODE_A" gpu=on --overwrite

helm upgrade --install hami hami-charts/hami \
  --kubeconfig tenant-a.kubeconfig \
  --namespace kube-system \
  --version "$HAMI_CHART_VERSION" \
  --values hami-values.yaml \
  --wait --timeout 10m

kubectl --kubeconfig tenant-a.kubeconfig -n kube-system get pods,daemonsets,deployments
kubectl --kubeconfig tenant-a.kubeconfig describe node "$GPU_NODE_A"

O chart, a seleção de nós por gpu=on e os componentes de verificação seguem o fluxo oficial. Repita a instalação usando o kubeconfig e o worker de B. HAMi instalado apenas no management cluster não gerencia automaticamente os workers dos tenants. (project-hami.io)

Para a primeira rodada, mantenha overcommit de memória desabilitado e configure pelo menos dois slots compartilháveis por GPU. Arquive os valores efetivos do Helm para reproduzir o ensaio.

6. Executar um workload CUDA de verdade

Namespace e quota

Salve como gpu-namespace.yaml:

apiVersion: v1
kind: Namespace
metadata:
  name: gpu-lab
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: gpu-budget
  namespace: gpu-lab
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    requests.nvidia.com/gpu: "2"

A quota limita o orçamento agregado do namespace. Aqui, requests.nvidia.com/gpu contabiliza unidades solicitadas, não duas placas necessariamente exclusivas. Não confunda quota com reserva física. O Kubernetes permite quotas de extended resources usando o prefixo requests.. (kubernetes.io)

HAMi também documenta uma extensão de quota com chaves limits.nvidia.com/..., incluindo tratamento de solicitações multi-GPU. Isso é comportamento do HAMi, não uma equivalência com a quota nativa acima; valide sua versão antes de adotar a extensão. (project-hami.io)

Job com duas execuções concorrentes

Salve como gpu-job.yaml:

apiVersion: batch/v1
kind: Job
metadata:
  name: gpu-smoke
  namespace: gpu-lab
spec:
  completions: 2
  parallelism: 2
  backoffLimit: 0
  activeDeadlineSeconds: 600
  ttlSecondsAfterFinished: 3600
  template:
    metadata:
      labels:
        app: gpu-smoke
      annotations:
        hami.io/node-scheduler-policy: binpack
        hami.io/gpu-scheduler-policy: binpack
    spec:
      restartPolicy: Never
      automountServiceAccountToken: false
      nodeSelector:
        gpu: "on"
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 1000
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: cuda-test
          image: pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime
          command: [python, -u, -c]
          args:
            - |
              import json
              import time
              import torch

              assert torch.cuda.is_available(), 'CUDA unavailable'
              torch.manual_seed(42)
              a = torch.randn((2048, 2048), device='cuda')
              b = torch.randn((2048, 2048), device='cuda')
              for _ in range(10):
                  c = a @ b
              torch.cuda.synchronize()
              start = time.monotonic()
              iterations = 0
              while time.monotonic() - start < 120:
                  c = a @ b
                  torch.cuda.synchronize()
                  iterations += 1
              elapsed = time.monotonic() - start
              print(json.dumps({
                  'device': torch.cuda.get_device_name(0),
                  'iterations': iterations,
                  'seconds': elapsed,
                  'iterations_per_second': iterations / elapsed,
                  'allocated_bytes': torch.cuda.memory_allocated(),
                  'reserved_bytes': torch.cuda.memory_reserved()
              }))
          env:
            - name: HOME
              value: /tmp
          securityContext:
            allowPrivilegeEscalation: false
            capabilities:
              drop: [ALL]
          resources:
            requests:
              cpu: "1"
              memory: 2Gi
            limits:
              cpu: "2"
              memory: 4Gi
              nvidia.com/gpu: "1"
              nvidia.com/gpumem: "4096"
              nvidia.com/gpucores: "25"

A imagem existe no repositório PyTorch; a tag é uma referência didática fixa, não uma recomendação de atualização ou segurança para outubro de 2026. Homologue uma imagem suportada e fixe seu digest antes da produção. (hub.docker.com)

Com memoryFactor: 1, gpumem: 4096 corresponde a 4096 MiB por GPU solicitada; use inteiro sem sufixo Mi. gpucores: 25 expressa um percentual de controle computacional, não 25 núcleos físicos dedicados. O controle do HAMi-core utiliza time slicing e pode apresentar flutuações. (project-hami.io)

Os recursos estendidos foram definidos em limits; o Kubernetes utiliza esses valores como requests quando estes não são explicitados. CPU e memória do host continuam sendo dimensões separadas. (kubernetes.io)

O Job limita duração e tentativas para preservar falhas como evidência. Ele não serve tráfego, portanto não precisa de readiness HTTP. A sincronização CUDA evita medir somente o enfileiramento assíncrono das operações. (kubernetes.io)

kubectl --kubeconfig tenant-a.kubeconfig apply -f gpu-namespace.yaml
kubectl --kubeconfig tenant-a.kubeconfig apply -f gpu-job.yaml
kubectl --kubeconfig tenant-a.kubeconfig -n gpu-lab get pods -o wide
kubectl --kubeconfig tenant-a.kubeconfig -n gpu-lab \
  wait --for=condition=complete job/gpu-smoke --timeout=650s
kubectl --kubeconfig tenant-a.kubeconfig -n gpu-lab \
  logs -l app=gpu-smoke --prefix=true

Repita no tenant B. Em cada cluster, confirme sobreposição temporal e o UUID físico alocado aos Pods. binpack favorece consolidação, mas a anotação sozinha não prova compartilhamento da mesma placa. (project-hami.io)

7. Plano de testes e critérios de aceite

Use esta matriz como proposta de homologação, não como resultado já observado:

TesteProcedimentoEvidência exigida
BaselineUma execução, sem concorrentes, mantendo os mesmos limitesTempo, throughput e memória registrados
CompartilhamentoDuas execuções simultâneas na mesma GPUMesmo UUID e intervalo de execução sobreposto
Pressão de memóriaEm pool descartável, tentar alocar mais que o limite concedidoFalha controlada da alocação e comportamento do vizinho registrado
QuotaManter dois Pods ativos e tentar admitir um terceiroRejeição por quota, não simples falta de capacidade
Isolamento administrativoUsar uma identidade de A contra a API BAusência de acesso indevido
Falha de workerInterromper um worker de testeTempo de detecção e recuperação mensurados
Falha do control planeRemover um Pod do control plane em configuração redundanteDisponibilidade da API e reconciliação registradas

Para gerar pressão de memória, crie uma cópia do Job, com outro nome, substituindo o código por uma alocação CUDA maior que 4096 MiB. Faça isso apenas no pool descartável. Diferencie erro CUDA de falta de memória de OOMKilled por memória do host. Não deduza isolamento de memória apenas do valor mostrado por nvidia-smi.

Faça uma rodada de aquecimento e várias repetições. Compare workloads iguais, imagens iguais e políticas iguais. Para inferência, meça latências p50, p95 e p99, taxa de erros, throughput e fila. Defina o Service Level Objective, ou SLO, antes de decidir quanto compartilhamento aceitar.

8. Segurança e isolamento de rede

Como configuração inicial do namespace de teste, salve deny-all.yaml:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: gpu-lab
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

Essa política bloqueia ingress e egress dos Pods selecionados quando o CNI implementa NetworkPolicy. O teste numérico não precisa de rede. Aplicações reais precisarão de permissões explícitas para DNS, armazenamento, filas e serviços. A política não substitui controles no host nem isola memória de GPU. (kubernetes.io)

Como critérios de produção, exija Role-Based Access Control, ou RBAC, mínimo; identidades distintas por tenant; APIs privadas quando possível; TLS; Secrets fora do Git; criptografia e rotação de credenciais. Restrinja Pods privilegiados, hostPath e alteração de políticas pelos consumidores.

Um usuário com cluster-admin dentro do tenant não deve ser considerado sujeito a quotas e políticas que ele próprio pode modificar. Para orçamento corporativo, imponha também limites no provisionamento dos workers e na camada de infraestrutura. Essa distinção entre confiança e isolamento é central ao desenho multi-tenant. (kubernetes.io)

9. Do cenário corporativo à produção

Problema: equipes de visão computacional precisam de APIs Kubernetes independentes, enquanto seus serviços menores não justificam uma GPU exclusiva por processo.

Solução proposta: um tenant por domínio administrativo, workers separados por tenant e compartilhamento HAMi dentro de cada pool autorizado.

Implementação: disponibilizar templates via GitOps, quotas, perfis de GPU e testes automatizados de compatibilidade.

Resultado esperado: maior autonomia com evidências de utilização e interferência. A economia deve ser medida; não é consequência garantida da instalação.

Para um desenho próximo de produção, proponho:

  • Control planes redundantes distribuídos entre nós e zonas, quando disponíveis.
  • Datastore com quorum, armazenamento confiável e capacidade reservada.
  • Workers GPU sobressalentes compatíveis com a carga crítica.
  • Para serviços de inferência: múltiplas réplicas, startupProbe, readinessProbe, livenessProbe, PodDisruptionBudget e distribuição topológica.
  • Autoscaling baseado em fila e demanda, validando também a criação e a preparação de novos workers GPU.
  • Checkpoints e retomada para processamento interrompível.

Esses são critérios de projeto: duas réplicas na mesma GPU não devem ser aceitas como prova de tolerância à falha dessa GPU.

Desenvolvimento versus produção

DimensãoLaboratórioProdução proposta
Control planeUma réplicaRedundância e distribuição verificadas
GPUUm worker por tenantCapacidade de recuperação e pools por criticidade
ImagensTag fixa didáticaDigest homologado e análise de vulnerabilidades
ObservabilidadeEventos e logsMétricas, alertas, rastreamento e retenção
RecuperaçãoRecriação manualBackups e restauração ensaiada

10. Observabilidade, custos e manutenção

Estruture dashboards por tenant, namespace, workload, worker e UUID físico. Recomendo acompanhar:

  • Control plane: disponibilidade da API, latência, erros, reinícios e saúde do datastore.
  • Scheduling: tempo em Pending, recusas, quota e capacidade disponível.
  • GPU: memória alocada e usada, utilização, temperatura e falhas do dispositivo.
  • Aplicação: throughput, latência, fila, erros CUDA e tempo de carregamento.

Use Prometheus e Grafana como componentes possíveis; centralize logs e conecte traces da API à fila e ao processamento. Antes de fazer chargeback por Pod, valide a atribuição das métricas: compartilhamento pode limitar a associação entre métricas físicas e contêineres, como documentado no time slicing do device plugin NVIDIA. (docs.nvidia.com)

Para FinOps, compare custo por tarefa concluída dentro do SLO, não apenas quantidade de GPUs provisionadas. Inclua workers ociosos, storage, tráfego entre zonas, load balancers, datastore e observabilidade. Considere Spot somente para cargas com retomada testada; reserve capacidade mais previsível para serviços críticos.

Na manutenção, promova alterações primeiro em um tenant canário. Faça backup do datastore, recursos do management cluster e material necessário à recuperação. Um snapshot isolado não substitui o procedimento completo de restauração do Kamaji. (kamaji.clastix.io)

Upgrades de tenant exigem tratar control plane e workers separadamente. Reserve capacidade temporária para o rollout. Não trate a alteração inversa de spec.kubernetes.version como rollback seguro; prefira procedimentos suportados, restauração ensaiada ou migração para outro cluster. (kamaji.clastix.io)

11. Troubleshooting orientado por sintomas

SintomaPossível causaInvestigaçãoCorreção
Tenant sem endpointLoad balancer ou rede indisponívelStatus do TenantControlPlane e ServiceCorrigir exposição e conectividade
Worker NotReadyCNI ausente ou configuração incompatívelCondições do nó e Pods do CNIAjustar CNI, CIDR e runtime
Job sem PodsQuota recusou criaçãoEventos do Job e ResourceQuotaAjustar orçamento ou liberar consumo
Pod PendingCapacidade, taints ou schedulingEventos, nó e logs do HAMiCorrigir restrição identificada
CUDA indisponívelDriver, runtime ou injeçãoLogs, configuração do runtime e device pluginHomologar a combinação
Latência elevadaContenção, CPU ou entrada de dadosBaseline, GPU, fila e CPUReduzir compartilhamento ou separar pools

Comandos de investigação:

kubectl --kubeconfig tenant-a.kubeconfig get pods -A
kubectl --kubeconfig tenant-a.kubeconfig get deployments -A
kubectl --kubeconfig tenant-a.kubeconfig get nodes
kubectl --kubeconfig tenant-a.kubeconfig -n gpu-lab describe job gpu-smoke
kubectl --kubeconfig tenant-a.kubeconfig -n gpu-lab describe resourcequota gpu-budget
kubectl --kubeconfig tenant-a.kubeconfig get events -A --sort-by=.metadata.creationTimestamp

# Substitua POD pelo nome observado.
kubectl --kubeconfig tenant-a.kubeconfig -n gpu-lab describe pod POD
kubectl --kubeconfig tenant-a.kubeconfig -n gpu-lab logs POD

Use logs --previous somente quando houver uma instância anterior do contêiner. O Job apresentado usa restartPolicy: Never; não espere encontrar automaticamente logs anteriores no mesmo Pod. (kubernetes.io)

Evite ausência de requests, privilégios excessivos, credenciais hardcoded, descarte precoce dos logs e cópia direta de configurações de laboratório. Antes de desmontar, exporte as evidências e confirme o kubeconfig de cada operação destrutiva.

12. Quando utilizar e alternativas

Considere Kamaji com HAMi quando houver necessidade real de APIs independentes, automação de vários clusters e workloads compatíveis com compartilhamento interno. Evite essa combinação se uma equipe pequena consegue operar adequadamente um único cluster com namespaces e políticas.

AlternativaPrincipal trade-off
Um cluster com namespacesMenor operação, mas API e governança compartilhadas
Clusters independentes com GPU exclusivaMenor densidade, fronteiras mais simples de avaliar
NVIDIA time slicingCompartilhamento sem isolamento de memória e falhas entre réplicas
NVIDIA Multi-Instance GPU, ou MIGParticionamento por hardware, limitado aos modelos e perfis suportados

As diferenças de isolamento entre time slicing e MIG são documentadas pela NVIDIA. MIG pode integrar um desenho com HAMi, mas isso exige outro perfil de homologação; não é o backend testado neste artigo. (docs.nvidia.com)

Resumo prático

  • Separe o teste de APIs do teste de GPUs.
  • Instale HAMi no tenant que possui os workers acelerados.
  • Confirme compartilhamento pelo dispositivo físico e pela execução simultânea.
  • Teste quotas, pressão de memória, interferência e recuperação.
  • Preserve versões, manifests, métricas e logs como evidências.
  • Só avance para produção após demonstrar isolamento adequado e atendimento ao SLO.

Conclusão

O valor dessa arquitetura não está em instalar dois projetos, mas em transformar autonomia, utilização e confiabilidade em contratos verificáveis. Platform Engineering fornece os caminhos padronizados; DevOps automatiza sua entrega; SRE estabelece os objetivos e os ensaios de falha.

A decisão madura pode ser compartilhar algumas GPUs, dedicar outras e manter certos tenants em infraestrutura separada. O laboratório deve produzir evidências para essa decisão, não apenas uma captura de tela com Pods em Running.

Quer aplicar isso no seu ambiente?

Agende uma conversa de 30 minutos sem compromisso. Eu olho o seu cenário e digo por onde começar.

Agendar uma conversa

Receba os novos artigos por e-mail

Um e-mail quando sair artigo novo sobre infraestrutura, backend e IA. Sem spam, cancele quando quiser.