🇧🇷 Kubernetes Parte 2
🇬🇧 To read this article in English click here
Kubernetes Parte 2
Nesta série de artigos, vamos explorar o Kubernetes em 3 partes. Esta é a segunda parte, onde explicarei sobre ferramentas e apresentarei exercícios para que você coloque em prática o conhecimento adquirido. Assim, será possível observar os pods rodando dentro do cluster e entender como cada peça desse sistema se conecta para criar aplicações escaláveis e resilientes.
Ao longo do artigo, apresentarei diversos componentes e exercícios separadamente, mas é muito importante que você entenda que, juntos, eles criam uma infraestrutura robusta e escalável. O Kubernetes organiza recursos usando arquivos YAML, que descrevem configurações como pods, as unidades básicas de execução. Elementos como ReplicaSets e Deployments garantem alta disponibilidade e atualizações consistentes. Namespaces ajudam a isolar e organizar os recursos, enquanto Services e a Networking Infrastructure conectam os componentes entre si e ao mundo externo. Recursos como Liveness Probes, Volumes e Gerenciamento de Recursos garantem monitoramento, armazenamento e eficiência em ambientes complexos.
Com isso em mente, vamos começar!
MINIKUBE:
Como vamos aprender e executar exercícios, precisamos preparar o ambiente Kubernetes para realizarmos os testes. Caso você ainda não tenha o Kubernetes e o Minikube instalados, vou deixar um tutorial para te ajudar com isso. Mas antes, o que é o Minikube?
Minikube é uma ferramenta que permite criar um cluster Kubernetes local no seu computador. Ele é ideal para testes, desenvolvimento e aprendizado, porque você pode rodar o Kubernetes inteiro sem precisar de uma infraestrutura complexa ou na nuvem.
Na prática, o Minikube cria uma máquina virtual ou container (dependendo do seu sistema) e instala um cluster Kubernetes básico dentro dela. Assim, você pode testar suas aplicações como faria em um ambiente Kubernetes “de verdade”, mas tudo localmente. Ele também oferece comandos fáceis para gerenciar o cluster, como iniciar, parar, adicionar extensões e acessar o painel do Kubernetes.
Esse video do canal “CODIGO FLUENTE” vai te ensinar a configurar o seu ambiente
Assista, e depois volte aqui.
Com o Minikube instalado, execute o comando
minikube start.
Agora estamos prontos para começar.
YAML
YAML (Yet Another Markup Language, ou atualmente YAML Ain’t Markup Language) é um formato de serialização de dados que é fácil de ler e escrever para humanos. Ele é muito usado para configuração de aplicativos porque é simples e direto, mas ainda é poderoso o suficiente para suportar estruturas mais complexas como listas e mapas aninhados.
A ideia principal do YAML é ser minimalista. Em vez de usar um monte de chaves e colchetes como no JSON, ele usa recuo para organizar os dados. Isso deixa o arquivo mais limpo e fácil de entender. Aqui vai um exemplo básico:
nome: Gustavo
idade: 25
hobbies:
– assistir séries
– programar
– ler
trabalho:
cargo: Data Engineer
experiência: 6.5 anos
No Kubernetes, o YAML é a espinha dorsal. Tudo que você configura lá – pods, deployments, serviços, volumes – é descrito em arquivos YAML. Esses arquivos dizem ao Kubernetes o que você quer, e ele faz acontecer.
Como funciona no Kubernetes?
O YAML é usado para definir manifests, que descrevem o estado desejado dos recursos. Você escreve o que precisa, aplica com o kubectl, e o Kubernetes gerencia o resto.
Estrutura básica de um YAML no Kubernetes:
Um arquivo geralmente tem:
apiVersion: Versão da API que você está usando (apps/v1, v1, etc.).
kind: Tipo do recurso que você está criando (Pod, Deployment, Service…).
metadata: Informações como nome e labels.
spec: Os detalhes do que você quer configurar.
Exemplo básico: Criar um Pod
apiVersion: v1
kind: Pod
metadata:
name: my-pod
labels:
app: my-app
spec:
containers:
– name: nginx-container
image: nginx:latest
ports:
– containerPort: 80
O que ele faz:
kind: Pod: Diz que você está criando um pod.
metadata: Nome do pod e labels para identificar.
spec: Configura o container, define a imagem (nginx) e qual porta será exposta.
Pontos para ficar esperto:
YAML não perdoa erros de indentação. Um espaço fora do lugar e você quebra o arquivo.
kubectl ajuda a validar: Use kubectl apply -f e veja se deu erro antes de surtar.
Fique atento às versões de API – elas mudam (por exemplo, extensions/v1beta1 já era).
Você pode encadear vários recursos num único arquivo, separando com —.
Com YAML no Kubernetes, você basicamente descreve o que quer, e o cluster faz o trabalho pesado.
Mais adiante nós vamos criar alguns pods e recursos usando YAML.
Pods:
Criando Pods – Forma Imperativa:
Criar pods de forma imperativa no Kubernetes é direto, rápido, e bom pra testar algo sem criar um arquivo YAML.
1. Comando simples:
Use kubectl run no cluster do minikube que iniciamos agora pouco para criar um pod diretamente.
kubectl run my-pod –image=nginx:latest
pod criado e rodando.
O que acontece:
- Cria um pod chamado my-pod.
- Usa a imagem nginx:latest do Docker Hub.
2. Adicionar uma porta:
Se seu container expõe alguma porta, você pode informar isso no comando:
kubectl run my-pod –image=nginx:latest –port=80
Isso é útil, por exemplo, se quiser conectar via um serviço depois.
3. Criar pods com variáveis de ambiente:
Você pode passar variáveis para o container:
kubectl run my-pod –image=nginx:latest –env=”ENV=production” –env=”DEBUG=false”
4. Gerar o YAML do pod:
Se quiser ver o YAML equivalente e aprender como ficaria:
kubectl run my-pod –image=nginx:latest –dry-run=client -o yaml
Isso só imprime o YAML sem aplicar nada. Se quiser guardar em um arquivo:
kubectl run my-pod –image=nginx:latest –dry-run=client -o yaml > my-pod.yaml
Depois você pode editar e aplicar dentro do cluster do minikube:
kubectl apply -f my-pod.yaml
5. Listar pods criados:
Para ver se seu pod está no ar:
kubectl get pods
E pra ver os detalhes:
kubectl describe pod my-pod
6. Deletar o pod:
Quando não precisar mais dele:
kubectl delete pod my-pod
Por que usar o modo imperativo?
- Testes rápidos: Não precisa criar e editar YAML pra algo simples.
- Aprendizado: Ajuda a entender as opções disponíveis no Kubernetes.
- Debugging: Rápido pra rodar e testar containers diretamente no cluster.
Mas, se for algo mais sério, YAML é a melhor prática.
Criando Pods – Manifest Files:
Criar pods com manifest files é a maneira declarativa e organizada no Kubernetes.
1. Crie o arquivo YAML do Pod
Você pode usar qualquer IDE. Crie um arquivo chamado pod.yaml:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
labels:
app: my-app
spec:
containers:
– name: nginx-container
image: nginx:latest
ports:
– containerPort: 80
O que cada parte faz:
apiVersion: Define a versão da API. Para Pods, é v1.
kind: Diz ao Kubernetes que você está criando um Pod.
metadata: Inclui informações como o nome (my-pod) e labels (app: my-app).
spec: Define o que o Pod terá, como os containers dentro dele.
containers: Lista de containers no Pod.
- name: Nome do container.
- image: Imagem Docker que será usada.
- ports: Porta que o container vai expor.
2. Aplicar o arquivo no cluster
Use o comando kubectl apply para criar o Pod:
kubectl apply -f pod.yaml
Se tudo der certo, o Kubernetes vai criar o Pod.
Por que usar manifest files?
Organização: Fácil de versionar no Git.
Reusável: Pode usar o mesmo YAML em outros clusters.
Escalável: É a base para criar recursos mais avançados como Deployments e Services.
Essa abordagem é essencial para produção e para entender o Kubernetes no dia a dia.
ReplicaSets:
ReplicaSets são como os “guardiões” de Pods no Kubernetes. Eles garantem que você sempre tenha o número desejado de réplicas rodando, mesmo se algum Pod morrer ou falhar.
O que é um ReplicaSet?
Objetivo: Manter um número fixo de Pods ativos.
Como funciona: Se um Pod morre, o ReplicaSet cria outro. Se tem Pods sobrando, ele mata os extras.
Diferença de um Deployment: O ReplicaSet não gerencia atualizações de versão dos Pods; isso é trabalho do Deployment.
Quando usar ReplicaSets?
Na real, quase sempre você usará Deployments, porque eles já criam e gerenciam ReplicaSets pra você. Só use um ReplicaSet diretamente se precisar de algo muito básico ou para aprendizado.
Vamos testar na prática?
Criando um ReplicaSet
Crie um arquivo replicaset.yaml com o seguinte conteúdo:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-replicaset
labels:
app: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
– name: nginx-container
image: nginx:latest
ports:
– containerPort: 80
O que faz:
replicas: 3: Mantém 3 Pods rodando.
selector: Define que só gerencia Pods com a label app: my-app.
template: Diz como os Pods devem ser criados.
Aplicando o ReplicaSet
Rode o comando abaixo no cluster do minikube para criar o ReplicaSet:
kubectl apply -f replicaset.yaml
Verifique se foi criado:
kubectl get replicasets
Você verá algo assim:
NAME DESIRED CURRENT READY AGE
my-replicaset 3 3 3 10s
Verificando os Pods
O ReplicaSet gerou os Pods automaticamente. Confira:
kubectl get pods
Você verá que os seguintes pods foram criados(o nome após “replicaset-” pode estar diferente para você):
my-replicaset-abc12
my-replicaset-def34
my-replicaset-ghi56
Testando o ReplicaSet
Vamos testar para ver o ReplicaSet trabalhando na prática.
Delete um Pod manualmente:
kubectl delete pod my-replicaset-abc12
Agora, veja o que acontece:
kubectl get pods
O ReplicaSet vai recriar o Pod que você deletou. Ele garante que sempre existam 3 Pods rodando.
Dica Final
Você quase nunca cria ReplicaSets diretamente em produção. Normalmente, usa Deployments, que fazem todo o trabalho do ReplicaSet e ainda gerenciam atualizações. Mas, pra entender a base, ReplicaSets são um ótimo começo.
Deployments:
Deployments no Kubernetes são como um “manual” que você entrega para o cluster, dizendo como rodar e gerenciar seus pods. Com um deployment, você define como sua aplicação deve ser implantada e mantida em operação. Aqui vai a ideia:
O que é?
É um recurso usado para criar e gerenciar réplicas de pods. Ele garante que sua aplicação esteja sempre rodando na quantidade de instâncias que você definiu.
O que você configura?
No YAML do deployment, você especifica coisas como:
A imagem do contêiner que quer usar (ex.: nginx:latest).
O número de réplicas (ex.: 3 pods).
Estratégias de atualização (tipo “substituir um por vez” ou “corta tudo e sobe tudo novo”).
Labels e seletores, que ajudam a identificar quais pods fazem parte do deployment.
O que ele faz por você?
Se um pod falha, o deployment automaticamente cria outro. Se você quer atualizar sua aplicação, ele faz isso gradualmente (ou do jeito que você preferir), para evitar downtime.
Na prática:
É isso que roda sua aplicação e garante que ela fique estável. Mas, quando algo dá errado (ex.: um pod fica preso no estado CrashLoopBackOff), você vai precisar debugar direto no pod com kubectl logs ou olhar os eventos do deployment.
Criação de deployment:
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
– name: my-app
image: nginx:1.23
ports:
– containerPort: 80
Explicação curta:
apiVersion e kind: Define que é um Deployment na versão apps/v1.
metadata: Nome do Deployment.
spec.replicas: Quantidade de réplicas do pod.
spec.selector.matchLabels: Determina quais pods pertencem ao Deployment (com base no label app: my-app).
spec.template: Template do Pod.
- metadata.labels: Label do Pod.
- spec.containers: Configura o contêiner:
- name: Nome do contêiner.
- image: Imagem do contêiner.
- ports: Porta exposta pelo contêiner.
Use o comando kubectl apply para criar o Deployment com base no arquivo YAML.
kubectl apply -f deployment.yaml
Verificar se o Deployment está rodando:
kubectl get deployments
Você verá algo como:
NAME READY UP-TO-DATE AVAILABLE AGE
my-app 2/2 2 2 10s
Deployment Rollout:
Deployment rollout é o processo de lançar uma nova versão de uma aplicação em produção de forma gradual e controlada, para evitar problemas em larga escala caso algo dê errado. Basicamente, você troca a versão antiga por uma nova, mas com cuidado, testando as mudanças enquanto elas são aplicadas.
Por que fazer rollout?
Se você simplesmente atualizar toda a aplicação de uma vez (o famoso “big bang deployment”) e houver um bug crítico, isso pode derrubar tudo. Com rollout, você pode perceber os problemas em etapas menores, o que facilita o rollback (voltar para a versão anterior).
Exemplos de rollout
Rolling Update (Atualização contínua):
Imagine que você tem 10 réplicas da sua aplicação rodando no Kubernetes. No rollout, você atualiza uma ou duas réplicas por vez. Durante o processo, algumas réplicas ainda estarão rodando a versão antiga, garantindo estabilidade enquanto você verifica se a nova versão está funcionando como esperado.
Exemplo com Kubernetes:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
Aqui, você garante que nunca mais de uma réplica ficará indisponível.
Canary Deployment (Implantação canário):
Essa abordagem distribui a nova versão apenas para uma pequena porcentagem dos usuários no início. Se funcionar bem, você aumenta gradualmente a parcela até atingir 100%.
É como testar uma ponte com um carro antes de liberar o tráfego total.
Exemplo prático: Você tem uma aplicação servindo 100% do tráfego. Configura um balanceador de carga para enviar 10% das requisições para a nova versão (v2), enquanto os outros 90% continuam na versão estável (v1).
Rollout History:
Rollout history é basicamente o histórico das versões que foram implantadas na sua aplicação. Ele é útil para saber o que mudou, identificar quando algo deu errado, e até mesmo voltar para uma versão anterior. Como estamos usando Kubernetes, isso é muito fácil de rastrear e gerenciar.
Na prática, com Kubernetes
Ver o histórico do rollout
Para visualizar o histórico de deployments:
kubectl rollout history deployment <nome-do-deployment>
Exemplo:
kubectl rollout history deployment my-app
Saída esperada:
deployment.apps/my-app
REVISION CHANGE-CAUSE
1 Criado o deployment inicial
2 Adicionado uma nova feature X
3 Correção de bug na API
Descrever uma revisão específica
Se você quiser detalhes sobre uma revisão específica:
kubectl rollout history deployment <nome-do-deployment> –revision=<número>
Exemplo:
kubectl rollout history deployment my-app –revision=2
Saída:
deployment.apps/my-app with revision #2
Containers:
– Name: my-app
Image: my-image:2.0
Port: 8080
Environment Variables:
– ENV: production
Rollback para uma versão anterior
Se a versão atual está quebrando, volte para a última versão estável:
kubectl rollout undo deployment <nome-do-deployment>
Exemplo:
kubectl rollout undo deployment my-app
Ou para uma revisão específica:
kubectl rollout undo deployment my-app –to-revision=2
Resumo
Use rollout history para entender o que mudou.
Adicione motivos (–record) para facilitar o rastreamento.
Faça rollback rapidamente em caso de problemas.
Faz sentido?
Rollout Pause e Rollout Resume:
Rollout pause e rollout resume são comandos do Kubernetes usados para controlar o processo de atualização de um deployment. Eles são úteis para situações em que você precisa interromper um rollout em andamento ou retomar uma atualização que foi pausada.
O que é rollout pause?
Quando você pausa um rollout, o Kubernetes para de criar ou atualizar pods com a nova versão. É útil quando você quer:
Analisar se a nova versão está estável antes de prosseguir.
Fazer ajustes manuais no deployment enquanto o rollout está pausado.
Como pausar um rollout
kubectl rollout pause deployment <nome-do-deployment>
Exemplo:
kubectl rollout pause deployment my-app
O que acontece?
O deployment para de atualizar pods para a nova versão.
Os pods já criados continuam rodando, mas nenhuma nova réplica é atualizada.
O que é rollout resume?
Depois de ajustar ou confirmar que a nova versão está funcionando bem, você pode retomar o rollout pausado. O Kubernetes continuará o processo de onde parou.
Como retomar um rollout
kubectl rollout resume deployment <nome-do-deployment>
Exemplo:
kubectl rollout resume deployment my-app
O que acontece?
O Kubernetes retoma a atualização dos pods restantes.
O rollout continua seguindo a estratégia definida (ex: RollingUpdate).
Deployment Scale:
Deployment Scale é a capacidade de ajustar dinamicamente o número de réplicas de uma aplicação rodando em um cluster Kubernetes. Em termos simples, é como aumentar ou reduzir o “tamanho” da aplicação para lidar com mais (ou menos) carga.
Qundo usar?
Alta demanda: Se o tráfego para sua aplicação aumenta, você escala para cima (scale up) adicionando mais réplicas.
Custo: Durante horários de menor uso, você pode escalar para baixo (scale down) para economizar recursos.
Resiliência: Mais réplicas ajudam a manter a aplicação disponível mesmo se alguns pods falharem.
Como funciona na prática?
Escalar manualmente
Você ajusta o número de réplicas manualmente usando o comando kubectl scale.
Comando básico:
kubectl scale deployment <nome-do-deployment> –replicas=<número-desejado>
Exemplo:
kubectl scale deployment my-app –replicas=5
Nesse caso, o Kubernetes vai garantir que 5 pods estejam rodando para o deployment my-app.
Verificar o estado após escalar
Depois de escalar, cheque o status:
kubectl get pods
Isso mostrará os novos pods sendo criados ou deletados.
Escalando automaticamente (Horizontal Pod Autoscaler)
Em vez de escalar manualmente, você pode configurar o Kubernetes para ajustar o número de réplicas automaticamente, com base em métricas como uso de CPU ou memória.
Criar um autoscaler
Use o comando kubectl autoscale:
kubectl autoscale deployment <nome-do-deployment> –min=<mínimo> –max=<máximo> –cpu-percent=<percentual-alvo>
Exemplo:
kubectl autoscale deployment my-app –min=2 –max=10 –cpu-percent=80
O Kubernetes manterá entre 2 e 10 réplicas.
Escalará para cima se o uso de CPU de uma réplica ultrapassar 80%.
Monitorar o autoscaler
Verifique o status do autoscaler:
kubectl get hpa
Você verá algo como:
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS
my-app Deployment/my-app 75%/80% 2 10 4
Aqui, 75%/80% indica que o uso de CPU atual está em 75%, próximo ao limite definido (80%).
Recreate Strategy Type:
Recreate é uma estratégia de deployment no Kubernetes onde todos os pods existentes de uma aplicação são removidos antes que os novos sejam criados. É simples e direto: mata os antigos e sobe os novos. Sem complicação, mas também sem overlap.
Quando usar a estratégia Recreate?
Incompatibilidade: Se a versão nova e antiga da aplicação não podem rodar juntas.
Dependências únicas: Quando a aplicação depende de um recurso exclusivo, como uma conexão de banco de dados, e várias instâncias podem causar conflito.
Simplicidade: Para atualizações rápidas em sistemas que podem tolerar downtime.
Como configurar Recreate
Você define isso no arquivo de configuração do Deployment, em spec.strategy.type.
Exemplo YAML com Recreate:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
strategy:
type: Recreate
template:
metadata:
labels:
app: my-app
spec:
containers:
– name: my-app
image: my-app:2.0
ports:
– containerPort: 8080
Nesse caso:
Quando você aplicar uma nova versão, o Kubernetes vai deletar todos os pods antigos primeiro.
Depois, ele criará os novos pods.
Kubernetes Networking Infrastructure
Kubernetes Networking é como um sistema de “ruas e endereços” que garante que tudo dentro do cluster consiga se comunicar. Cada Pod ganha um “endereço IP” próprio, e todo mundo consegue conversar sem bloqueios por padrão (isso é perigoso, mas é assim que começa).
Aqui vai um resumo direto:
Conceitos Básicos:
Pods falam com Pods: Não importa onde eles estejam, eles sempre se encontram pela rede.
Services são placas de rua: Criam endereços estáveis para acessar os Pods, já que eles vivem morrendo e nascendo.
Ingress é o porteiro: Decide quem de fora pode entrar e como.
Exemplos Rápidos:
ClusterIP (interno):
Exemplo: Um banco de dados que só os apps dentro do cluster acessam.
NodePort (abrir porta fixa):
Exemplo: Precisa testar localmente um app sem um balanceador bonitinho.
LoadBalancer (expor pro mundo):
Exemplo: Subir um site direto na nuvem, tipo AWS ou GCP.
# Service simples para um app rodando na porta 8080
apiVersion: v1
kind: Service
metadata:
name: meu-app
spec:
selector:
app: meu-app
ports:
– port: 80
targetPort: 8080
- Ingress (roteamento HTTP):
Quer que meusite.com/api vá para o backend e meusite.com vá para o frontend? Usa isso.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: meu-ingress
spec:
rules:
– host: meusite.com
http:
paths:
– path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
– path: /api
pathType: Prefix
backend:
service:
name: backend
port:
number: 80
- NetworkPolicy (controlar tráfego):
Sem bagunça. Só deixa o Frontend acessar o Backend.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: policy-backend
spec:
podSelector:
matchLabels:
app: backend
ingress:
– from:
– podSelector:
matchLabels:
app: frontend
Resumo do resumo:
Pod = Casa.
Service = Placa de endereço.
Ingress = Porteiro do condomínio.
NetworkPolicy = Portão com senha.
Namespaces
Namespaces no Kubernetes são como “divisórias” dentro do cluster. Eles ajudam a organizar e isolar recursos. Pense neles como pastas em um sistema de arquivos: você pode separar ambientes (produção, homologação, dev) ou times (frontend, backend).
Por que usar Namespaces?
Organização: Separar recursos para ambientes ou equipes.
Isolamento: Limitar quem pode acessar o quê (usando RBAC e NetworkPolicies. Vamos falar deles na parte 3).
Cotas: Definir limites de CPU, memória ou recursos específicos.
Por padrão, o Kubernetes já vem com alguns namespaces:
default: Onde tudo cai se você não especificar nada.
kube-system: Coisas internas do Kubernetes (como DNS).
kube-public: Dados acessíveis por todos.
Como criar um Namespace:
Você pode criar manualmente com YAML ou via linha de comando.
Linha de Comando (kubectl):
kubectl create namespace meu-namespace
Com YAML:
apiVersion: v1
kind: Namespace
metadata:
name: meu-namespace
Depois aplica com:
kubectl apply -f namespace.yaml
Como usar Namespaces:
Criando recursos dentro de um namespace específico: Adicione -n <namespace> ao comando:
kubectl create deployment nginx –image=nginx -n meu-namespace
Definir o namespace no YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: meu-namespace
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
– name: nginx
image: nginx
Trocar de namespace padrão: Para evitar digitar -n o tempo todo:
kubectl config set-context –current –namespace=meu-namespace
Dica: Cotas em Namespaces
Você pode limitar o uso de recursos no namespace com ResourceQuota. Exemplo: limitar CPU e memória.
apiVersion: v1
kind: ResourceQuota
metadata:
name: cotas
namespace: meu-namespace
spec:
hard:
requests.cpu: “2”
requests.memory: 4Gi
limits.cpu: “4”
limits.memory: 8Gi
Aplica com:
kubectl apply -f resourcequota.yaml
Services
No Kubernetes, Services são usados para expor um conjunto de Pods para comunicação dentro ou fora do cluster. Eles criam uma camada de abstração que garante que as aplicações possam encontrar os Pods, mesmo que os IPs desses Pods mudem.
Por que usar Services?
Descoberta de Serviço: Permite encontrar Pods sem depender de IPs dinâmicos.
Balanceamento de Carga: Distribui o tráfego entre os Pods associados.
Conexão Externa: Exponha aplicativos fora do cluster.
Tipos de Services:
ClusterIP (Padrão)
Expondo dentro do cluster.
Usado para comunicação entre Pods.
NodePort
Abre uma porta fixa em cada nó para acesso externo.
Exemplo: Desenvolvimento ou teste local.
LoadBalancer
Cria um IP público (geralmente com suporte de um provedor de nuvem).
Ideal para produção.
ExternalName
Redireciona o tráfego para um nome DNS externo.
Exemplo: Conectar com APIs externas.
Criando um Service
ClusterIP (Interno no Cluster):
apiVersion: v1
kind: Service
metadata:
name: meu-servico
spec:
selector:
app: minha-app
ports:
– protocol: TCP
port: 80
targetPort: 8080
Explicação:
port: Porta do Service.
targetPort: Porta do Pod.
selector: Define os Pods que serão associados ao serviço.
Criar e aplicar:
kubectl apply -f service-clusterip.yaml
NodePort (Expor externamente):
apiVersion: v1
kind: Service
metadata:
name: meu-nodeport
spec:
type: NodePort
selector:
app: minha-app
ports:
– protocol: TCP
port: 80
targetPort: 8080
nodePort: 30007
Acessar:
http://<IP_do_Nó>:30007
LoadBalancer (IP Público):
apiVersion: v1
kind: Service
metadata:
name: meu-loadbalancer
spec:
type: LoadBalancer
selector:
app: minha-app
ports:
– protocol: TCP
port: 80
targetPort: 8080
Explicação:
Requer um provedor de nuvem como AWS, GCP ou Azure. Eles criam automaticamente um IP público.
ExternalName (Nome DNS externo):
apiVersion: v1
kind: Service
metadata:
name: meu-externalname
spec:
type: ExternalName
externalName: api.exemplo.com
Acessar:
Esse service redireciona para api.exemplo.com.
Verificar Services:
Use o comando:
kubectl get services
Resumo:
ClusterIP: Comunicação interna.
NodePort: Acesso externo fixo.
LoadBalancer: IP público (produção).
ExternalName: Redireciona para um DNS externo.
Com Services, você conecta tudo de forma simples e escalável no Kubernetes.
Agora, vamos colocar em prática e consumir alguns deles?
Consumindo ClusterIP Service
Para consumir um ClusterIP Service, você precisa garantir que os Pods que irão consumir o serviço estejam no mesmo cluster (ou namespace, dependendo do contexto). Aqui está um exemplo prático:
Cenário:
Temos uma aplicação chamada backend que roda em Pods expostos por um Service backend-service (ClusterIP).
Uma aplicação frontend consome esse serviço.
Configuração do Service e Backend:
Deployment do Backend:
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 2
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
– name: backend
image: hashicorp/http-echo
args:
– “-text=Hello from Backend!”
ports:
– containerPort: 8080
Service ClusterIP:
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
selector:
app: backend
ports:
– protocol: TCP
port: 80
targetPort: 8080
Deployment do Frontend:
Frontend Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
– name: frontend
image: curlimages/curl
command: [ “sh”, “-c”, “while true; do curl http://backend-service; sleep 5; done” ]
Passos:
Aplicar os manifests:
kubectl apply -f backend-deployment.yaml
kubectl apply -f backend-service.yaml
kubectl apply -f frontend-deployment.yaml
Verificar os Pods e Services:
kubectl get pods
kubectl get services
Observar o log do Frontend (Consumidor):
kubectl logs -l app=frontend -f
Você verá algo como:
Hello from Backend!
Hello from Backend!
Hello from Backend!
Como funciona:
O Service backend-service é criado com um IP interno acessível apenas no cluster.
O frontend chama http://backend-service (DNS interno gerado pelo Kubernetes).
O Service ClusterIP roteia o tráfego para os Pods do backend.
Dica:
Use o DNS interno do Kubernetes para referenciar o Service:
O nome do serviço (backend-service) funciona automaticamente como DNS dentro do cluster.
Isso é suficiente para conectar aplicações sem se preocupar com os IPs dos Pods.
Consumindo NodePort Service
Quando se usa um NodePort Service, o serviço expõe uma porta fixa em cada nó do cluster, permitindo que seja acessado de fora do cluster usando o IP de qualquer nó e a porta designada.
Cenário:
Um serviço backend é exposto como NodePort.
Vamos consumir o serviço de fora do cluster usando o IP de um nó.
Configuração do NodePort Service e Backend:
Deployment do Backend:
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 2
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
– name: backend
image: hashicorp/http-echo
args:
– “-text=Hello from Backend (NodePort)!”
ports:
– containerPort: 8080
NodePort Service:
apiVersion: v1
kind: Service
metadata:
name: backend-service-nodeport
spec:
type: NodePort
selector:
app: backend
ports:
– protocol: TCP
port: 80
targetPort: 8080
nodePort: 30007
Passos:
Aplicar os manifests:
kubectl apply -f backend-deployment.yaml
kubectl apply -f backend-service-nodeport.yaml
Verificar o Service:
kubectl get service backend-service-nodeport
Você verá algo como:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
backend-service-nodeport NodePort 10.96.0.234 <none> 80:30007/TCP 5m
Aqui, 30007 é a porta aberta em todos os nós.
Encontrar o IP de um nó:
kubectl get nodes -o wide
Isso exibirá os endereços IPs externos/internos dos nós do cluster.
Consumir o serviço: Use um navegador, cURL ou ferramenta HTTP para acessar:
http://<NODE_IP>:30007
Exemplo com cURL:
curl http://<NODE_IP>:30007
Saída esperada:
Hello from Backend (NodePort)!
Teste em Minikube:
Como estamos usando o Minikube, obtenha o IP do nó assim:
minikube ip
Depois, acesse:
http://<Minikube_IP>:30007
Como funciona:
O Service com type: NodePort cria uma porta fixa (nodePort) em todos os nós do cluster.
Qualquer requisição feita para http://<Node_IP>:30007 será roteada para os Pods selecionados pelo Service.
O Kubernetes faz o balanceamento de carga automaticamente entre os Pods.
Dica:
Evitar NodePort em produção: É melhor usar LoadBalancer ou ingress para expor serviços externos.
Usar NodePort em dev/testes: É útil para cenários locais ou sem provedores de nuvem.
Liveness Probes
Liveness Probes são a forma que o Kubernetes usa para checar se seu aplicativo ainda está “vivo” e funcionando. Se o probe falhar, o container é reiniciado. Isso é útil quando sua aplicação trava ou entra em um estado que não consegue se recuperar sozinha.
Como funciona:
Você define uma regra para o Kubernetes verificar o estado do container. Existem três tipos principais de probes:
HTTP: Faz uma requisição HTTP para um endpoint.
Command: Executa um comando dentro do container.
TCP: Verifica se consegue conectar em uma porta específica.
Exemplo básico:
Liveness Probe HTTP
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-com-liveness
spec:
replicas: 1
selector:
matchLabels:
app: exemplo
template:
metadata:
labels:
app: exemplo
spec:
containers:
– name: app
image: nginx
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
Explicação:
httpGet: Faz uma requisição HTTP no caminho / da porta 80.
initialDelaySeconds: Espera 5 segundos antes de começar as verificações.
periodSeconds: Faz o check a cada 10 segundos.
Liveness Probe com Command
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-com-liveness-command
spec:
replicas: 1
selector:
matchLabels:
app: exemplo
template:
metadata:
labels:
app: exemplo
spec:
containers:
– name: app
image: busybox
command:
– sh
– -c
– echo “Iniciando app”; sleep 1000
livenessProbe:
exec:
command:
– cat
– /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 10
Explicação:
O container deve criar o arquivo /tmp/healthy para ser considerado “vivo”.
Se o arquivo sumir, o probe falha, e o Kubernetes reinicia o container.
Liveness Probe TCP
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-com-liveness-tcp
spec:
replicas: 1
selector:
matchLabels:
app: exemplo
template:
metadata:
labels:
app: exemplo
spec:
containers:
– name: app
image: nginx
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
periodSeconds: 10
Explicação:
Verifica se a porta 80 está aberta e aceita conexões.
Testando:
Aplique o YAML:
kubectl apply -f <arquivo.yaml>
Verifique o status dos Pods:
kubectl get pods
Veja os eventos:
kubectl describe pod <nome-do-pod>
Se o Liveness Probe falhar, você verá algo como:
Warning Unhealthy Liveness probe failed: HTTP probe failed with statuscode: 500
Quando usar:
Travas na aplicação: Se seu app pode congelar, use liveness probes para reiniciar automaticamente.
Dependências externas: Se seu app depende de algo e pode entrar em estado “quebrado”.
Dica rápida: Combine liveness probes com readiness probes para garantir que o app só receba tráfego quando estiver realmente pronto.
Gerenciamento de recursos
Gerenciar recursos no Kubernetes é basicamente garantir que seus Pods não abusem (ou fiquem sem) CPU e memória. Você controla isso configurando limites e solicitações diretamente nos containers.
Por que isso é importante?
Evitar Brigas: Se um container usar muita CPU/memória, pode derrubar outros containers ou até o nó.
Garantir Performance: Reservar o que a aplicação precisa para rodar bem.
Balanceamento: O Kubernetes usa essas informações para distribuir Pods nos nós de forma inteligente.
Como funciona?
Requests (Solicitações):
A quantidade mínima de CPU/memória que o container precisa.
O Kubernetes usa isso para agendar o Pod em um nó que tenha capacidade suficiente.
Limits (Limites):
A quantidade máxima de CPU/memória que o container pode usar.
Se ultrapassar, ele será limitado ou até mesmo encerrado (no caso de memória).
Exemplo Simples:
apiVersion: v1
kind: Pod
metadata:
name: meu-pod
spec:
containers:
– name: minha-app
image: nginx
resources:
requests:
memory: “128Mi”
cpu: “250m”
limits:
memory: “256Mi”
cpu: “500m”
O que significa isso?
O container precisa de 128Mi de memória e 250m (0.25 vCPU) para iniciar.
No máximo, ele pode usar 256Mi de memória e 500m (0.5 vCPU).
Se tentar usar mais memória, o container será encerrado por OOM (Out of Memory).
Se tentar usar mais CPU, ele será estrangulado (throttled).
Testando o uso de recursos:
Rode um container que usa CPU:
apiVersion: v1
kind: Pod
metadata:
name: cpu-tester
spec:
containers:
– name: cpu-tester
image: busybox
command: [“sh”, “-c”, “while true; do :; done”]
resources:
limits:
cpu: “100m”
Verifique o uso:
kubectl top pod
Você verá que ele fica limitado a ~10% de um núcleo de CPU.
Dicas:
Sempre defina requests e limits nos containers.
Para apps críticos, use Pod Priority e Preemption para garantir que eles rodem.
Monitore o cluster com ferramentas como Metrics Server, Prometheus, ou Grafana.
Resumindo: Gerenciar recursos no Kubernetes é sobre garantir que todo mundo tenha o que precisa, sem ninguém roubar mais do que deveria.
Volumes
Volumes no Kubernetes resolvem um problema simples: o ciclo de vida dos containers. Quando um container reinicia, seus dados são perdidos, mas às vezes você precisa armazenar algo por mais tempo. Um Volume conecta armazenamento persistente ou compartilhado com seus Pods.
Tipos de Volumes
emptyDir
Um diretório vazio criado quando o Pod é iniciado.
Apaga tudo quando o Pod é excluído.
Bom para armazenamento temporário entre containers no mesmo Pod.
volumes:
– name: meu-volume
emptyDir: {}
hostPath
Usa um diretório no nó onde o Pod está rodando.
Pode ser arriscado em produção porque depende do nó.
volumes:
– name: meu-volume
hostPath:
path: /dados
persistentVolumeClaim (PVC)
Liga o Pod a um armazenamento persistente (como discos na nuvem).
Sobrevive mesmo que o Pod ou o cluster reinicie.
volumes:
– name: meu-volume
persistentVolumeClaim:
claimName: meu-pvc
configMap e secret
VAMOS FALAR NA PARTE 3, mas é usado para armazenar configurações e dados sensíveis.
nfs
Conecta seu Pod a um servidor NFS.
Útil para compartilhamento de dados entre Pods.
volumes:
– name: meu-nfs
nfs:
server: 192.168.1.1
path: /export
Exemplo Completo: emptyDir
apiVersion: v1
kind: Pod
metadata:
name: pod-com-volume
spec:
containers:
– name: app
image: busybox
command: [“sh”, “-c”, “echo ‘dados’ > /dados/teste.txt && sleep 3600”]
volumeMounts:
– name: meu-volume
mountPath: /dados
volumes:
– name: meu-volume
emptyDir: {}
O que acontece?
O diretório /dados no container é ligado ao volume emptyDir.
Qualquer arquivo criado lá é compartilhado entre containers no mesmo Pod.
Exemplo com PVC
Criar um PersistentVolume (PV):
apiVersion: v1
kind: PersistentVolume
metadata:
name: meu-pv
spec:
capacity:
storage: 1Gi
accessModes:
– ReadWriteOnce
hostPath:
path: /mnt/dados
Criar um PersistentVolumeClaim (PVC):
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: meu-pvc
spec:
accessModes:
– ReadWriteOnce
resources:
requests:
storage: 500Mi
Usar o PVC no Pod:
apiVersion: v1
kind: Pod
metadata:
name: pod-com-pvc
spec:
containers:
– name: app
image: nginx
volumeMounts:
– name: meu-volume
mountPath: /dados
volumes:
– name: meu-volume
persistentVolumeClaim:
claimName: meu-pvc
Dicas Práticas
Armazenamento Persistente: Use PVC e PV para dados importantes (banco de dados, logs, etc.).
Arquivos Temporários: Use emptyDir.
Segurança: Armazene segredos com secrets.
Cuidado com hostPath: Evite em produção, pois vincula seu app a um nó específico.
Resumindo: Volumes são o jeito do Kubernetes lidar com armazenamento de forma flexível. Você só escolhe o tipo certo para o problema e manda bala!
Aqui finalizamos a parte 2 da nossa série sobre Kubernetes. Ficou claro? Não se preocupe caso não tenha entendido tudo por completo. O mais importante é colocar os exemplos em prática para realmente compreender a fundo. Muito obrigado pela leitura!