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.
📋 Table of Contents
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.
🔗 Share this article
✍️ Leave a Comment