Cluster Kubernetes local com Kind: lab multi-node simples

Crie um cluster Kubernetes local com Kind e multiplos nos. Compare Kind, minikube, k3d e outras opcoes para montar seu lab.

Quando eu precisava de um cluster para testar alguma coisa no Mutirão DevOps da LinuxTips lá no começo da minha carreira, a última coisa que eu queria era transformar o lab em projeto de infraestrutura. O objetivo era aplicar um manifest, quebrar alguma coisa, entender o comportamento e jogar tudo fora depois. Para esse tipo de uso, o Kind resolve bem: ele cria nós Kubernetes como containers Docker e deixa você subir um cluster com vários nós sem entrar na sequência longa do kubeadm.

No meu notebook com WSL2 e Ubuntu, essa separação é importante. O post de instalação do kubeadm no WSL2 continua sendo o caminho para quem quer entender as peças e os passos de um cluster montado manualmente, visando um estudo para certificações Kubernetes, como por exemplo a CKA. Aqui a ideia é outra: ter um laboratório reproduzível em poucos minutos.

Então vamos direto ao lab. A comparação com minikube e k3d, a instalação no macOS e a armadilha que trava o cluster no WSL2 ficam logo depois, quando você já tiver os três nós de pé.

Início rápido: um cluster de três nós

O que você precisa antes de começar Docker em execução e um terminal Linux. No WSL2, rode tudo dentro da distribuição Linux, nunca no PowerShell misturado com outro kubeconfig. Se você está no macOS, pule para a seção de instalação no macOS mais abaixo e volte para cá no passo 2.

Antes de instalar, vale saber o que cada peça determina. A que mais importa é a node image, porque é ela que define a versão do Kubernetes do seu cluster:

ComponenteVersão usada aquiO que ela determina
kindv0.32.0A release do Kind, publicada em junho de 2026
node imageKubernetes 1.36.1A versão do cluster que sobe, default do Kind
kubectlestável do dia (v1.37.0 aqui)O cliente, baixado sempre na estável atual

Esse ecossistema muda de versão rápido, então confira sempre o que está atual no Quick Start oficial do Kind e na documentação do kubectl antes de executar.

1. Instale o kind e o kubectl

O bloco abaixo detecta amd64 ou arm64, instala o binário do Kind e baixa a versão estável atual do kubectl, conferindo o checksum antes de instalar:

set -euo pipefail

case "$(uname -m)" in
  x86_64) ARCH=amd64 ;;
  aarch64 | arm64) ARCH=arm64 ;;
  *) echo "Arquitetura não suportada: $(uname -m)"; exit 1 ;;
esac

KIND_VERSION=v0.32.0
curl -fsSLo kind "https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-${ARCH}"
chmod +x kind
sudo install -m 0755 kind /usr/local/bin/kind
rm kind

KUBECTL_VERSION=$(curl -fsSL https://dl.k8s.io/release/stable.txt)
curl -fsSLO "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${ARCH}/kubectl"
curl -fsSLO "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${ARCH}/kubectl.sha256"
echo "$(cat kubectl.sha256)  kubectl" | sha256sum --check
sudo install -m 0755 kubectl /usr/local/bin/kubectl
rm kubectl kubectl.sha256

Confira o que ficou instalado:

docker version
kubectl version --client
kind version
Terminal mostrando as versões instaladas: Docker 29.7.2, kubectl v1.37.0 e kind v0.32.0

2. Crie o cluster multi-node

Você não precisa abrir editor nenhum. O bloco abaixo cria ~/labs/kind/kind-lab.yaml e sobe o cluster lab-kubernetes de uma vez. O arquivo fica guardado para você versionar, ajustar e recriar o lab depois:

set -euo pipefail

LAB_DIR="${HOME}/labs/kind"
mkdir -p "$LAB_DIR"

cat > "$LAB_DIR/kind-lab.yaml" <<'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: lab-kubernetes
nodes:
  - role: control-plane
  - role: worker
  - role: worker
EOF

kind create cluster --config "$LAB_DIR/kind-lab.yaml" --wait 2m

Esse YAML cria um control plane e dois workers. Os workers não aumentam a capacidade física do notebook, porque todos continuam no mesmo host. Eles servem para exercitar decisões que um cluster de nó único não consegue mostrar, como distribuir réplicas, tornar um nó indisponível ou testar regras de agendamento.

Travou em “Starting control-plane”? Se você está no WSL2 e o comando morreu nessa etapa falando de rate limiter ou deadline, quase certamente é cgroup v1 no host, e não erro seu. A seção sobre cgroup no WSL2, mais abaixo, tem o diagnóstico e a correção.

3. Confirme os três nós

kubectl cluster-info --context kind-lab-kubernetes
kubectl get nodes -o wide

Comigo, os três nós apareceram Ready e o cluster inteiro levou pouco menos de três minutos para ficar de pé, contando o download da node image:

Saída do kubectl get nodes com os três nós do cluster lab-kubernetes em estado Ready na versão v1.36.1

O Kind grava e seleciona um contexto no kubeconfig. Se kubectl get nodes mostrar um cluster diferente, confira antes de aplicar qualquer YAML:

kubectl config current-context
kubectl config get-contexts
Terminal confirmando que o contexto ativo do kubectl é kind-lab-kubernetes

A pegadinha mais comum do lab local Quase sempre o problema não é o Kind ter falhado, é o terminal estar apontando para outro contexto. Em lab isso só gera confusão. Fora do lab, pode ser bem pior.

Suba um workload que dá para observar

Agora vamos criar uma aplicação pequena com três réplicas. Não é uma simulação de produção, é uma forma de fazer o scheduler trabalhar e ter algo para consultar.

kubectl create namespace lab

kubectl -n lab create deployment nginx-lab \
  --image=nginx:stable \
  --replicas=3

kubectl -n lab rollout status deployment/nginx-lab
kubectl -n lab get pods -o wide
Três pods do nginx-lab em Running, com duas réplicas no mesmo worker e uma no worker2

Se o primeiro comando reclamar do etcd Logo depois do cluster ficar Ready, o control plane ainda está assentando. Comigo, o primeiro kubectl create namespace morreu com etcdserver: request timed out. Esperei alguns minutos, repeti o mesmo comando sem mudar nada e passou. Se acontecer com você, não saia reinstalando: espere e repita.

Observe em quais nós os pods foram colocados. Comigo, duas réplicas caíram no mesmo worker e nenhuma no control plane. Se isso acontecer, não trate como erro: sem regras de afinidade ou anti-affinity, o scheduler não promete uma réplica por nó. Esse é exatamente o tipo de comportamento que vale testar num cluster descartável.

O exercício do lab: métricas antes do dashboard

O primeiro comando que eu tentaria nesse lab é kubectl top. Ele é rápido, cabe no terminal e evita instalar uma pilha de observabilidade antes de saber qual pergunta você quer responder. No post sobre métricas no Kubernetes, expliquei de onde esses números vêm, passando por cgroups, kubelet e metrics-server.

Primeiro, tente consultar:

kubectl top nodes
kubectl -n lab top pods

Em um cluster novo do Kind, é normal a Metrics API ainda não estar disponível, e os dois comandos respondem error: Metrics API not available. Instale o metrics-server usando o manifesto da release oficial do projeto e espere o deployment ficar pronto:

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
kubectl -n kube-system rollout status deployment/metrics-server --timeout=2m

Só que no Kind, com o manifesto oficial e nada mais, esse rollout provavelmente não vai ficar pronto. Foi o que aconteceu comigo: o comando estourou o --timeout=2m, o pod ficou parado em 0/1 Running e o Readiness respondeu HTTP 500 mais de vinte vezes seguidas.

A tentação aqui é sair colando --kubelet-insecure-tls, que é a primeira sugestão que aparece em qualquer busca. Antes disso, vá no log, porque são dois comandos e a resposta está lá inteira:

kubectl -n kube-system get pods -l k8s-app=metrics-server
kubectl -n kube-system logs deployment/metrics-server

E o log é bem específico:

"Failed to scrape node" err="Get \"https://<IP-DO-NO>:10250/metrics/resource\":
tls: failed to verify certificate: x509: cannot validate certificate for
<IP-DO-NO> because it doesn't contain any IP SANs"
"Failed probe" probe="metric-storage-ready" err="no metrics to serve"

Traduzindo: o metrics-server precisa raspar a porta 10250 de cada nó por HTTPS, e o certificado que o kubelet apresenta no Kind é autoassinado e não traz o IP do nó como SAN. O metrics-server então recusa a conexão, não coleta nada de nó nenhum, e sem métrica na memória ele nunca se declara pronto. O 500 do Readiness não é sintoma de bug, é o próprio metrics-server dizendo que não tem o que servir.

Repare que isso não é caso de borda: é Kind com o manifesto oficial, sem nenhuma customização. E aqui está a diferença que eu queria marcar: --kubelet-insecure-tls é a resposta certa neste cenário. O que não presta é aplicar sem olhar o log: aí vira sorte, não diagnóstico.

Com o diagnóstico na mão, o ajuste é este:

kubectl -n kube-system patch deployment metrics-server --type=json \
  -p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'

kubectl -n kube-system rollout status deployment/metrics-server --timeout=2m

Não comemore nem xingue no primeiro top Depois do rollout, a Metrics API ainda leva uns quinze a trinta segundos para popular. Comigo, a primeira chamada respondeu Metrics API not available e a segunda já trouxe os números. Espere meio minuto antes de concluir que deu errado.

NAME                           CPU(cores)   CPU(%)   MEMORY(bytes)   MEMORY(%)
lab-kubernetes-control-plane   365m         4%       677Mi           4%
lab-kubernetes-worker          76m          0%       219Mi           1%
lab-kubernetes-worker2         99m          1%       174Mi           1%

O que essa flag significa fora do lab --kubelet-insecure-tls desliga a verificação do certificado do kubelet. Num cluster real, o caminho é o kubelet ter certificado de serving assinado pela CA do cluster, com rotação habilitada, e não desligar a verificação. Aqui é um cluster descartável no seu notebook, então a troca é consciente.

O importante aqui não é decorar o comando. É criar um cenário pequeno o bastante para enxergar a cadeia inteira: workload, pod, nó, certificado e API de métricas.

Kind, minikube ou k3d: qual usar em cada situação

Com o lab de pé, dá para falar de escolha sem atrapalhar quem só queria o caminho curto. Para laboratório de estudo, Kind costuma compensar mais na maioria dos casos, principalmente se a intenção é criar, testar e descartar clusters sem transformar a preparação do ambiente no exercício inteiro.

Kindminikubek3d
Melhor paraLab efêmero e reproduzívelBancada que fica de pé por diasGitOps de longa duração
Multi-nodeYAML de cinco linhasFunciona, porém mais pesadoRápido e leve
LoadBalancerPrecisa de MetalLB ou cloud-provider-kindminikube tunnel resolveServiceLB já incluso
Fidelidade ao upstreamAlta, usa kubeadm por baixoAltak3s faz escolhas próprias
Ponto fracoStorage básico, persistência exige intençãoMais pesado, addons escondem configuraçãoMenos fiel ao upstream
Bom para CKA e CKSSimSimMenos indicado

Kind

Sobe em segundos, com tudo em containers Docker, sem uma VM adicional no meio. Permite um cluster multi-node de verdade apenas editando um YAML, o que é ótimo para testar drain, affinity, topology spread e PodDisruptionBudget. Dá para descartar e recriar sem dó, o que encaixa bem em CI e em testes de Terraform ou manifests.

Foi por isso que escolhi Kind como lab deste post. A configuração cabe num arquivo versionável e o mesmo arquivo pode voltar num teste de CI. A documentação do Kubernetes também posiciona o Kind como opção leve para aprendizado e testes, enquanto descreve kubeadm como instalação avançada, normalmente envolvendo várias máquinas ou VMs.

minikube

Eu gosto do minikube quando quero um cluster de estimação, que fica ligado por dias e guarda estado. Os addons prontos, como Ingress, metrics-server, registry, CSI hostpath e GPU, resolvem muita coisa sem instalar na mão, e ele suporta vários drivers, incluindo Docker, QEMU, HyperKit e KVM, o que ajuda quando Docker não é opção.

O ponto fraco é ser mais pesado. O modo multi-node funciona, mas não é o motivo pelo qual eu o escolheria para esse tipo de teste. E um addon mágico às vezes esconde uma configuração que você deveria aprender a fazer.

k3d

O k3d, que executa k3s dentro do Docker, é provavelmente o melhor meio-termo. Sobe rápido, consome pouca RAM e já traz Traefik e ServiceLB no ecossistema k3s. É uma escolha muito boa para labs de GitOps com Argo CD ou Flux, que ficam de pé por várias horas no notebook.

A ressalva é que k3s faz escolhas próprias para ser leve. Para estudar internals de etcd ou buscar fidelidade maior ao Kubernetes upstream para certificação, eu prefiro Kind.

Não é uma disputa de ferramenta favorita. São tarefas diferentes. Para estudar drain, scheduling, PDB, afinidade ou topology spread, o requisito importante é existir mais de um nó.

Instalação no macOS

No macOS, o Homebrew cuida da arquitetura Intel ou Apple Silicon e entrega as fórmulas atuais:

brew install kind kubectl

Depois disso, volte para o passo 2 do início rápido: o YAML e o kind create cluster são idênticos.

Transparência sobre o que foi testado Eu percorri este lab inteiro no meu WSL2 com Ubuntu, e é de lá que vêm todas as saídas de comando reproduzidas aqui. Os comandos de macOS seguem a documentação oficial das ferramentas e não passaram pela minha bancada.

WSL2: quando o cluster não sobe por causa do cgroup

Se você está no WSL2 e o cluster não subiu no passo 2, provavelmente é aqui que a resposta está. O kubelet das versões atuais se recusa a iniciar num host que oferece cgroup v1, e o WSL2 ainda entrega cgroup v1 em muitas instalações, mesmo com systemd habilitado. O Kubernetes 1.36 foi a versão que removeu o suporte a cgroup v1 de vez (KEP-5573), e é o 1.36.1 que o Kind v0.32.0 usa por padrão.

Confira o que você tem:

stat -fc '%T' /sys/fs/cgroup
cat /sys/fs/cgroup/cgroup.controllers
O que apareceSignificadoO que fazer
cgroup2fs e uma lista de controladoresVocê está em cgroup v2Nada, pode seguir o lab
tmpfs, ou cgroup.controllers inexistenteVocê está em cgroup v1Aplique a correção abaixo
docker info diz Cgroup Version: 1Vale a versão do Docker Desktop, não a da distroCorrija e atualize o Docker Desktop

Em cgroup v1, o kind create cluster morre no Starting control-plane com esta mensagem:

error: error execution phase wait-control-plane: cannot obtain client without bootstrap:
could not bootstrap the admin user in file admin.conf: unable to create ClusterRoleBinding:
client rate limiter Wait returned an error: context deadline exceeded

Repare que o erro fala de rate limiter e de deadline, e não diz uma palavra sobre cgroup. O aviso verdadeiro passou muito antes, na primeira linha da saída do Kind, e é fácil de ignorar: cgroup v1 is deprecated in Kubernetes and will not be supported in a future kind release.

A causa real só aparece se você for atrás do kubelet dentro do nó, que o Kind mantém de pé mesmo quando o cluster falha, se você usar --retain:

kind create cluster --config "$LAB_DIR/kind-lab.yaml" --retain
docker exec <nome-do-no>-control-plane journalctl -u kubelet --no-pager | tail -20

E aí a mensagem é direta:

kubelet.service: Scheduled restart job, restart counter is at 44.
"command failed" err="failed to validate kubelet configuration, error: kubelet is configured
to not run on a host using cgroup v1. cgroup v1 support is unsupported and will be removed in
a future release"

O kubelet entra em crashloop, o control plane nunca aparece e o kubeadm init desiste esperando uma API que não vai existir. Dentro do nó, crictl ps -a mostra zero containers: nada chegou a rodar. Guarde esse par de comandos, porque essa é a diferença entre “não funcionou” e saber o motivo.

Para resolver, habilite o cgroup v2. A configuração fica no .wslconfig, do lado do Windows, em %USERPROFILE%\.wslconfig, e vale para todas as distribuições:

[wsl2]
kernelCommandLine = cgroup_no_v1=all systemd.unified_cgroup_hierarchy=1

Os dois parâmetros andam juntos: cgroup_no_v1=all desliga a hierarquia v1, e systemd.unified_cgroup_hierarchy=1 liga a unificada. Sozinho, o segundo tende a criar as duas hierarquias ao mesmo tempo, que é justamente o que não pode. No /etc/wsl.conf da distribuição, confirme que o systemd está ligado:

[boot]
systemd=true

Depois, no PowerShell, derrube tudo com wsl --shutdown e abra o terminal de novo. Repita o stat -fc '%T' /sys/fs/cgroup para confirmar o cgroup2fs antes de seguir.

Docker Desktop muda quem manda no cgroup Se o seu docker info disser Operating System: Docker Desktop, os nós do Kind rodam na VM do Docker Desktop, não na sua distribuição Ubuntu. Confira com docker info | grep -i cgroup. Se aparecer Cgroup Version: 1, é essa a versão que o cluster vai encontrar, independente do que a sua distro reporta, e vale conferir se o Docker Desktop está atualizado.

E não tente contornar fixando um node image mais antigo. É a primeira ideia que vem à cabeça e não resolve: com kindest/node:v1.35.5, no qual o cgroup v1 é apenas deprecado e não removido, o kubelet recusa exatamente do mesmo jeito, com a mesma mensagem. A validação não olha a versão do Kubernetes, olha o cgroup do host. Sem cgroup v2, não tem cluster.

Onde esse lab para de representar produção

Kind usa kubeadm para bootstrapar cada nó e suporta clusters multi-node, inclusive topologias HA. Ainda assim, ele não substitui um cluster de produção nem um homelab em VMs. Storage persistente, LoadBalancer, limites reais de host e falhas de infraestrutura merecem laboratório próprio. Se o assunto for volume e persistência, os objetos que você vai exercitar estão no post sobre storage no Kubernetes, e o hostPath que o Kind entrega por padrão não representa o comportamento de um CSI real.

O ponto que mais pega quem vem de cloud é o Service do tipo LoadBalancer. No Kind ele não recebe, por mágica, um IP externo como receberia no EKS ou em outro provedor. Para esse cenário, use kubectl port-forward no exercício inicial ou adicione uma solução específica, como cloud-provider-kind, quando o objetivo do lab realmente for testar exposição de serviços. O mesmo vale para Cilium: usar Cilium no Kind é uma ótima próxima etapa para estudar rede, mas não é pré-requisito para subir seu primeiro cluster local.

Outras opções que valem a pena

Kind, minikube e k3d resolvem a maior parte dos labs locais. Quando a pergunta muda, outras opções passam a fazer mais sentido.

OpçãoQuando faz sentido
Talos LinuxHardening, ciclo de vida de nó e CIS Benchmark
vclusterMulti-tenancy, operators isolados, um cluster por pessoa
Colima e Rancher DesktopEscolha de runtime local no macOS
Proxmox com Talos, k3s ou kubeadmHomelab físico, storage real e disaster recovery

Talos Linux

Com talosctl cluster create, em Docker, ou em VMs no Proxmox, o Talos oferece um sistema imutável, sem SSH e operado por API declarativa. É ótimo para quem trabalha com IaC, porque o cluster inteiro vira arquivo de configuração. Eu o colocaria como próximo lab para estudar hardening, ciclo de vida de nó e CIS Benchmark.

vcluster

O vcluster cria clusters virtuais dentro de um cluster host. Ele é útil para simular multi-tenancy, testar operators isolados ou dar um cluster por pessoa do time sem subir infraestrutura nova. Combina bem com um k3d como host.

Colima e Rancher Desktop no macOS

Se o time usa MacBook, Rancher Desktop já entrega k3s, containerd e interface. O Colima entrega uma VM leve com Kubernetes opcional e pode consumir menos recursos que Docker Desktop. Os dois entram mais como escolha de runtime local do que como substitutos diretos do Kind.

Proxmox com Talos, k3s ou kubeadm

Para um homelab físico de verdade, com storage, Longhorn ou Ceph, LoadBalancer com MetalLB e BGP, e testes de falha de nó, o caminho são VMs ou máquinas físicas. É onde você consegue exercitar cenários de disaster recovery que o notebook não reproduz com fidelidade.

Sugestão prática para evoluir o lab

Minha sugestão é manter três camadas, cada uma com uma função clara:

  • Kind como padrão para testes rápidos, pipelines e manifests.
  • k3d para o lab de GitOps que permanece de pé por mais tempo.
  • Talos em VMs quando o foco for hardening e ciclo de vida de nó.

Se o objetivo for aproximar o laboratório do comportamento de rede que você encontra no EKS, use Kind com Cilium no lugar do CNI padrão e adicione cloud-provider-kind para LoadBalancer. Só faça essa evolução quando o cluster básico já estiver confortável. Colocar tudo de uma vez é uma forma eficiente de não saber o que quebrou.

Minha leitura é esta: não comece o laboratório tentando reproduzir a cloud inteira no notebook. Comece com o Kind, três nós e uma aplicação que você consegue apagar sem hesitar. Quando o básico estiver claro, adicione Cilium, Ingress, LoadBalancer e GitOps uma peça de cada vez. Aí o lab deixa de ser só um comando que deu certo e passa a ser ambiente de estudo de verdade.

Perguntas frequentes

Kind substitui kubeadm para estudar Kubernetes?

Em partes. O Kind usa kubeadm no bootstrap dos nós, mas abstrai a instalação para priorizar velocidade e repetibilidade. Use este lab para operar workloads e testar comportamentos. Use kubeadm e VMs quando quiser estudar os passos de montagem e o ciclo de vida de um cluster mais próximo de produção.

Posso usar este YAML no macOS?

Sim. O Kind suporta Linux, macOS e Windows. A diferença fica na instalação do Docker e do binário correto para a arquitetura do seu computador.

Por que não usar o Kubernetes embutido do Docker Desktop?

Nada contra o Docker Desktop: o lab deste post roda justamente em cima dele. A questão é o Kubernetes embutido, que sobe um cluster de nó único. Para exercitar scheduling, drain e anti-affinity você precisa de mais de um nó, e é isso que o YAML do Kind entrega em cinco linhas.

Como apagar tudo depois?

kind delete cluster --name lab-kubernetes

Apagar e recriar faz parte do exercício. Se um manifesto só funciona depois de várias mudanças manuais que ninguém anotou, o problema não é o Kind.

Compartilhe / Share
Fernando Müller Junior
Fernando Müller Junior

SRE Manager na Appmax. 16 anos de infraestrutura, de datacenter a cloud. Escrevo sobre o que opero em produção.

Artigos: 32

Receba as notícias por email / Receive news by email

Insira seu endereço de e-mail abaixo e assine nossa newsletter / Enter your email address below and subscribe to our newsletter

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *