DEV Community

DeividFerraz
DeividFerraz

Posted on

Kubernetes local na prática: como eu estruturei ReplicaSet, Service, HPA e Ingress para subir minha aplicação no Minikube ☸️

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. 👇

  1. 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

  1. 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.

  1. 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.

  1. 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

  1. 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.

  1. 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

  1. 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.

  1. 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.

  1. 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.

  1. 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)