Descrição
Eu quis aprender Kubernetes entendendo a responsabilidade de cada recurso, em vez de apenas copiar manifestos prontos. Nesse processo, subi minha aplicação localmente no Minikube e conectei ReplicaSet, Service, HPA e Ingress até o fluxo fazer sentido de ponta a ponta. 🚀
Tem uma forma muito comum de começar em Kubernetes:
copiar alguns YAMLs, aplicar tudo e comemorar quando a aplicação responde.
Só que eu quis fazer diferente.
Em vez de focar apenas em “subir a app”, eu quis entender o que cada recurso faz de verdade dentro do cluster.
Então montei minha estrutura localmente no Minikube, usando:
ReplicaSet
Service
HPA
Ingress
namespace dedicado
domínio local via hosts
E isso me ajudou muito a visualizar o fluxo real da aplicação. 👇
- Primeiro eu organizei o cluster 🧭
Antes de aplicar qualquer recurso, eu preferi deixar meu ambiente mais organizado.
Criei um namespace próprio para a aplicação e já apontei meu contexto atual para ele:
kubectl config use-context multinode
kubectl create namespace meusite
kubectl config set-context --current --namespace=meusite
Isso foi importante por dois motivos:
Tudo ficou isolado no mesmo namespace
Ficou muito mais fácil de navegar no kubectl e no k9s
Inclusive, para abrir o K9s já no lugar certo:
k9s --context multinode -n meusite
- Meu primeiro recurso foi o ReplicaSet 🧱
Aqui eu fiz uma escolha intencional.
Eu sei que, no mundo real, aplicações geralmente sobem com Deployment.
Mas como meu foco era estudar a base, usei ReplicaSet.
Queria ver mais de perto como o Kubernetes mantém a quantidade desejada de Pods.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: site-soren-rs
spec:
replicas: 1
selector:
matchLabels:
app: site-soren
template:
metadata:
labels:
app: site-soren
spec:
containers:
- name: site-soren
image: deivid/nova-soren:latest
ports:
- containerPort: 8080
Ele ficou com uma réplica só, justamente porque meu objetivo não era fazer escala fixa manual ali, e sim conectar isso depois com HPA.
- Também defini recursos do container 💻
Dentro do ReplicaSet, eu já aproveitei para declarar CPU e memória.
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
Esses números parecem estranhos no começo, mas depois fazem sentido:
100m = 0.1 core
500m = 0.5 core
128Mi = 128 mebibytes
Na prática:
requests = o mínimo reservado
limits = o teto
Isso ajuda tanto no agendamento quanto no comportamento da aplicação dentro do cluster.
- O Service entrou para estabilizar a comunicação 🔄
Depois criei o Service.
Esse recurso foi um divisor de águas no meu entendimento.
Porque o Pod não é fixo.
Ele pode cair, subir de novo, mudar IP.
Então o Service entra como um endereço estável e encontra os Pods pela label.
apiVersion: v1
kind: Service
metadata:
name: site-soren-service
spec:
selector:
app: site-soren
ports:
- protocol: TCP
port: 4500
targetPort: 8080
No meu caso:
o Service expõe a porta 4500
o container recebe na 8080
Foi aqui que comecei a entender a relação:
Ingress → Service → Pods
- Escala automática com HPA 📊
Para não deixar minha aplicação “engessada”, adicionei um HPA.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: site-soren-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: ReplicaSet
name: site-soren-rs
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
Uma dúvida que eu tinha e que muita gente tem:
o HPA precisa que CPU e memória batam juntas?
Não.
Ele olha as métricas separadamente.
Se a CPU pedir mais escala antes, ele sobe.
Se a memória pedir antes, ele sobe também.
Então ele considera o cenário que está mais pressionando a aplicação naquele momento.
- O Ingress foi a camada de entrada 🌍
Depois veio o Ingress, que foi quem amarrou o domínio à aplicação.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: site-soren-ingress
spec:
ingressClassName: nginx
rules:
- host: devid.ferraz.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: site-soren-service
port:
number: 4500
Aqui eu aprendi um detalhe importante:
O objeto Ingress sozinho não faz milagre.
Ele precisa de um Ingress Controller rodando no cluster.
No Minikube, eu habilitei isso assim:
minikube addons enable ingress -p multinode
E dependendo do ambiente local, também usei:
minikube tunnel -p multinode
Então eu passei a explicar assim para mim mesmo:
o Ingress define a regra
o controller executa a regra
o tunnel ajuda a expor isso localmente, quando necessário
- Como resolvi o domínio local 🏡
Como eu estava rodando tudo localmente, precisava que o meu domínio resolvesse para a máquina local.
Então usei o arquivo hosts:
127.0.0.1 devid.ferraz.test
Isso fez com que minha máquina entendesse que devid.ferraz.test deveria apontar para o IP local.
Esse detalhe também explica uma dúvida comum:
por que localhost não funciona igual?
Porque o Ingress estava esperando o host devid.ferraz.test.
Então não bastava só chegar no IP certo — a requisição precisava chegar com o host certo também.
- Build da imagem e erro antes do image load 🐳
Outro ponto que achei legal mostrar foi a diferença entre:
a imagem existir na minha máquina
a imagem existir dentro do cluster
Eu buildava localmente:
docker build -t deivid/nova-soren:latest .
Mas antes de fazer o load, eu fazia questão de mostrar o erro.
Porque, se a imagem não estiver disponível no cluster, o Pod não sobe do jeito esperado.
Depois disso eu carregava a imagem no Minikube:
minikube image load deivid/nova-soren:latest -p multinode
E aí sim o cluster conseguia usar a imagem local.
- Aplicando os manifests ✅
Depois de organizar contexto, namespace, ingress e imagem, o apply ficou limpo:
kubectl apply -f site-soren.yaml
kubectl apply -f site-soren-service.yaml
kubectl apply -f site-soren-hpa.yaml
kubectl apply -f site-soren-ingress.yaml
E depois era só conferir:
kubectl get pods
kubectl get svc
kubectl get ingress
kubectl get hpa
Esse foi um ponto que eu gostei bastante de ajustar:
em vez de repetir --context multinode -n meusite em todo comando, eu defini isso antes uma vez e deixei o terminal preparado.
- O que eu realmente aprendi com esse processo 💡
O maior ganho não foi só ver minha aplicação abrir no navegador.
Foi entender a responsabilidade de cada peça.
ReplicaSet mantém os Pods
Service estabiliza a comunicação
HPA escala quando a carga sobe
Ingress recebe o tráfego HTTP
hosts resolve o domínio local
image load leva a imagem para dentro do cluster local
Depois que enxerguei isso, Kubernetes deixou de parecer só um monte de YAML.
Passou a parecer uma arquitetura que conversa entre si.
E, honestamente, esse tipo de estudo na prática me ensinou muito mais do que só assistir explicação isolada.
Top comments (0)