🌐 Detecting your location…

Como configurar o Kubernetes localmente com kind e k3d em 2026: guia completo

⏱️6 min read  ·  1,251 words

Executar o Kubernetes localmente costumava significar uma máquina virtual pesada e uma longa espera. kind ek3d mudou isso executando clusters como contêineres, que iniciam em segundos e custam quase nada quando ociosos. Este guia configura um cluster local funcional com entrada, carregamento de imagem local e um loop de desenvolvimento que não envolve envio para um registro a cada alteração.

kind ou k3d: Qual escolher

Ambos executam Kubernetes dentro do Docker. Eles diferem naquilo que executam e no que otimizam.

gentil k3d
Kubernetes A montante, não modificado k3s — leve, alguns componentes trocados
Hora de início Rápido Mais rápido
Uso de memória Superior Inferior
Melhor para Testando o comportamento upstream real, CI Iteração rápida, laptops restritos

Escolhagentil se você precisar de um comportamento idêntico ao Kubernetes upstream de produção, especialmente para testar operadores ou controladores de admissão. Escolhak3d se você deseja o loop mais rápido e leve para desenvolvimento de aplicativos. Ambos são excelentes; este guia cobre cada um.

Pré-requisitos

# Docker must be running
docker version

# kubectl
brew install kubectl        # macOS
# or: curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"

# kind
brew install kind

# k3d
brew install k3d

Opção A: tipo

Um cluster de nó único é um comando, mas vale a pena escrever um arquivo de configuração imediatamente porque o ingresso requer mapeamentos de portas que não podem ser adicionados posteriormente sem recriar o cluster.

# kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
        protocol: TCP
      - containerPort: 443
        hostPort: 443
        protocol: TCP
  - role: worker
  - role: worker
kind create cluster --name dev --config kind-config.yaml

# kubectl context is switched automatically
kubectl cluster-info --context kind-dev
kubectl get nodes

OextraPortMappings block encaminha as portas do host 80 e 443 para o nó do cluster, que é o que fazhttp://localhost alcançar seu controlador de entrada. Adicione-o antecipadamente – modernizar significa excluir e recriar o cluster.

Entrada em espécie

kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml

kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=90s

Opção B: k3d

k3d lida com mapeamento de portas e entrada com sinalizadores, portanto a configuração é mais curta. k3s inclui Traefik como controlador de entrada por padrão.

k3d cluster create dev \
  --agents 2 \
  --port "80:80@loadbalancer" \
  --port "443:443@loadbalancer"

kubectl get nodes

Para usar o ingress-nginx em vez do Traefik, desative o pacote no momento da criação.

k3d cluster create dev \
  --agents 2 \
  --port "80:80@loadbalancer" \
  --k3s-arg "--disable=traefik@server:0"

Implantando um aplicativo

# app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: my-app:dev
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 3000
          readinessProbe:
            httpGet:
              path: /healthz
              port: 3000
            initialDelaySeconds: 2
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 3000
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
spec:
  ingressClassName: nginx
  rules:
    - host: app.localhost
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

imagePullPolicy: IfNotPresent é essencial para o desenvolvimento local. O padrãoAlways faz o Kubernetes tentar puxarmy-app:dev de um registro, que não existe, e o pod falha comErrImagePull.

Carregando imagens locais — a etapa que todos perdem

Seu cluster é executado em seus próprios contêineres com seu próprio armazenamento de imagens. Uma imagem criada em seu host fica invisível até que você a carregue explicitamente.

# Build normally
docker build -t my-app:dev .

# kind
kind load docker-image my-app:dev --name dev

# k3d
k3d image import my-app:dev -c dev

Então aplique e confira.

kubectl apply -f app.yaml
kubectl get pods -w
curl -H "Host: app.localhost" http://localhost/

Se os pods estiverem emImagePullBackOff, você quase certamente pulou a etapa de carregamento ou saiuimagePullPolicy em seu padrão.

Um Loop Interno Rápido

Reconstruir, carregar e reimplantar manualmente para cada mudança é muito lento para ser sustentado. Duas abordagens resolvem isso.

Inclinação ou Andaime observe sua origem, reconstrua, carregue a imagem e atualize a implantação automaticamente.

# Tiltfile
docker_build('my-app', '.')
k8s_yaml('app.yaml')
k8s_resource('web', port_forwards='3000:3000')
tilt up

Encaminhamento de porta é suficiente quando você só precisa acessar um serviço sem ingresso.

kubectl port-forward svc/web 3000:80
# now http://localhost:3000

Volumes Persistentes

Ambas as ferramentas fornecem uma classe de armazenamento padrão, portanto, um PersistentVolumeClaim funciona sem configuração.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data
spec:
  accessModes: [ReadWriteOnce]
  resources:
    requests:
      storage: 1Gi

Para manter os dados em seu host para que sobrevivam à exclusão do cluster, monte um diretório de host no momento da criação.

# kind-config.yaml
nodes:
  - role: control-plane
    extraMounts:
      - hostPath: /Users/me/k8s-data
        containerPath: /data

Depuração

# Why is a pod not starting? Events are at the bottom.
kubectl describe pod <pod-name>

# Logs, including from a crashed previous container
kubectl logs <pod-name>
kubectl logs <pod-name> --previous

# Shell into a running container
kubectl exec -it <pod-name> -- sh

# Debug a container with no shell using an ephemeral container
kubectl debug -it <pod-name> --image=busybox --target=web

# Cluster-wide recent events, newest last
kubectl get events --sort-by=.metadata.creationTimestamp

kubectl describe pod é o primeiro comando a ser executado para quase todos os problemas. A seção Eventos na parte inferior indica claramente se a imagem falhou ao extrair, se o nó não tinha recursos ou se uma análise falhou.

Limpando

# kind
kind delete cluster --name dev

# k3d
k3d cluster delete dev

# k3d can also stop and restart without losing state
k3d cluster stop dev
k3d cluster start dev

Erros Comuns

Esquecendo de carregar a imagem. O cluster não pode ver as imagens Docker do seu host. Carregue-os explicitamente.

SaindoimagePullPolicy no padrão. Com um:latest tag o padrão éAlways, que falha para imagens somente locais.

Adicionando mapeamentos de portas após a criação. Eles são corrigidos na criação do cluster. Planeje as portas de entrada antes de criar.

Omitindo solicitações de recursos. Sem eles, o agendador não pode raciocinar sobre a capacidade e os clusters locais se comprometem demais até que os pods sejam removidos.

Assumindo que local é igual à produção. Os clusters locais não têm balanceador de carga real, classes de armazenamento em nuvem e nenhuma aplicação de política de rede por padrão. Trate-os como uma ferramenta de desenvolvimento, não como um simulador de produção.

Conclusão

Uma configuração local produtiva do Kubernetes precisa de quatro coisas certas:crie o cluster com os mapeamentos de portas necessários para entrada, carregue imagens locais explicitamente no cluster, definaimagePullPolicy: IfNotPresent portanto, o Kubernetes não persegue um registro e automatiza o loop de reconstrução com Tilt ou Skaffold. Use kind quando precisar de um comportamento idêntico ao upstream e k3d quando quiser o loop mais leve e rápido. Ambos permitem excluir e recriar um cluster em segundos, o que é a verdadeira vantagem – um cluster local quebrado deixa de ser um problema que vale a pena depurar.

MD Rafikul Islam

Written by

MD Rafikul Islam is a software developer and the editor of TechPulse. He writes about developer tooling, hardware, and the practical decisions that come up in day-to-day engineering work — which laptop to buy, which framework to commit to, why a build broke at 2am. He tests the tools he writes about and says plainly when something is not worth the money. Corrections and corrections requests are welcome at rony.yf25@gmail.com.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা