DEV Community

Celso Nery
Celso Nery

Posted on

Montando um Cluster Kubernetes On-Premise — Parte 6: Deploy, Atualização e Escalabilidade de uma Aplicação Própria

🇺🇸 English version coming soon.

Na Parte 5 desta série, validamos o cluster de ponta a ponta fazendo o deploy do Nginx. Agora vamos um passo além: construir a imagem Docker de uma aplicação própria, publicá-la, colocá-la para rodar no cluster e explorar operações do dia a dia — atualização de versão, rollback e escalabilidade (manual e automática).

Como exemplo, foi utilizada uma API REST simples (myapp.war), feita em Spring Boot, apenas para fins didáticos — o processo se aplica a qualquer aplicação empacotada como imagem de container.

Criando a imagem Docker da aplicação

O primeiro passo é escrever o Dockerfile da aplicação. Neste exemplo, foi usada uma imagem base leve (alpine), com o Java 11 instalado para rodar a aplicação:

FROM alpine
WORKDIR /opt/app
RUN apk update && apk add vim openjdk11-jre
COPY runapp.sh .
CMD ash runapp.sh
Enter fullscreen mode Exit fullscreen mode

Construindo a imagem

docker image build -t oregontecnologia/myapp-api:1.0.0 .
Enter fullscreen mode Exit fullscreen mode

Publicando a imagem

Antes de usar a imagem no cluster, ela precisa estar disponível em algum registro — seja o Docker Hub, seja um registro privado. Se você optar por hospedar seu próprio registro on-premise (recomendado para ambientes corporativos ou sem acesso à internet), veja o artigo complementar sobre como criar um servidor de registro local.

Para publicar no Docker Hub:

docker login

username:
password:

docker push oregontecnologia/myapp-api:1.0.0
Enter fullscreen mode Exit fullscreen mode

Fazendo o deploy da aplicação

Com a imagem publicada, é possível verificar o estado atual do cluster antes de prosseguir:

kubectl get pods -o wide
kubectl get deploy -o wide
Enter fullscreen mode Exit fullscreen mode

Crie o Deployment diretamente pela linha de comando, apontando para a imagem publicada:

kubectl create deploy myapp-deploy --image=oregontecnologia/myapp-api:1.0.0
Enter fullscreen mode Exit fullscreen mode

Diferente dos exemplos anteriores desta série (onde usamos arquivos YAML com kubectl apply -f), aqui o Deployment é criado diretamente via linha de comando com kubectl create deploy. Ambas as abordagens são válidas — arquivos YAML são mais indicados quando você precisa versionar e reaplicar configurações de forma consistente.

Expondo a aplicação externamente

Para tornar a aplicação acessível fora do cluster, crie um Service do tipo LoadBalancer, associando um IP externo fixo:

kubectl expose deploy myapp-deploy --type=LoadBalancer --external-ip=10.0.10.100 --port=80
Enter fullscreen mode Exit fullscreen mode

Em ambientes on-premise, o tipo LoadBalancer normalmente não provisiona um balanceador automaticamente (isso é um recurso nativo de provedores de nuvem). Por isso, aqui é informado manualmente um IP externo (--external-ip) já disponível na rede local.

Removendo a aplicação

Caso precise remover o Deployment:

kubectl delete deploy myapp-deploy
Enter fullscreen mode Exit fullscreen mode

Atualizando e revertendo versões da aplicação

Uma das grandes vantagens do Kubernetes é gerenciar atualizações de forma controlada, sem downtime perceptível (rolling update).

Atualizando para uma nova versão

kubectl set image deployments/myapp-deploy myapp-api=oregontecnologia/myapp-api:1.0.2 --record
Enter fullscreen mode Exit fullscreen mode

Esse comando atualiza a imagem do container myapp-api dentro do Deployment para a versão 1.0.2. A flag --record registra o comando no histórico de revisões do Deployment, o que facilita identificar depois o que mudou em cada rollout.

Revertendo para a versão anterior

Se a nova versão apresentar problemas, é possível reverter rapidamente para a revisão anterior:

kubectl rollout undo deployments/myapp-deploy
Enter fullscreen mode Exit fullscreen mode

Escalando a aplicação

Escalonamento manual

Para ajustar manualmente o número de réplicas em execução — por exemplo, para 3:

kubectl scale deploy myapp-deploy --replicas=3
Enter fullscreen mode Exit fullscreen mode

Escalonamento automático (HPA)

Para deixar o próprio Kubernetes ajustar o número de réplicas automaticamente, com base no uso de CPU, é possível configurar um Horizontal Pod Autoscaler (HPA). No exemplo abaixo, o cluster começa com 2 réplicas e pode escalar até 10, sempre que a utilização média de CPU ultrapassar 75%:

kubectl autoscale deploy myapp-deploy --min=2 --max=10 --cpu-percent=75
Enter fullscreen mode Exit fullscreen mode

Ajuste os valores de --min, --max e --cpu-percent conforme a capacidade real do seu cluster e o comportamento esperado da aplicação.

Para verificar o estado atual do autoscaler:

kubectl get hpa
Enter fullscreen mode Exit fullscreen mode

Esse comando mostra, entre outras informações, o uso atual de CPU em relação ao alvo configurado, e o número de réplicas em execução no momento.

Para remover o autoscaler:

kubectl delete hpa myapp-deploy
Enter fullscreen mode Exit fullscreen mode

Agora vamos colocar todo este processo manual em um arquivo YAML para facilitar as coisas.

Ao longo desta série, usamos diversos arquivos .yaml para criar Deployments, Services e outros objetos no cluster. Neste artigo, vamos dar um passo atrás e entender a estrutura básica desses manifestos — o que é obrigatório em todo arquivo, como descobrir a apiVersion correta e como ficam, na prática, os principais tipos de objeto que já usamos: Pod, ReplicaSet, Deployment e Service.

A estrutura mínima de um manifesto

Todo objeto do Kubernetes, independente do tipo, é descrito por um YAML com quatro campos principais:

apiVersion: 
kind: 
metadata: 
spec: 
Enter fullscreen mode Exit fullscreen mode
  • apiVersion: a versão da API do Kubernetes usada para criar aquele objeto (por exemplo, v1 ou apps/v1);
  • kind: o tipo de objeto que será criado (Pod, Deployment, Service, etc.);
  • metadata: os metadados do objeto — nome, labels, namespace, entre outros;
  • spec: a especificação propriamente dita do objeto — no caso de um Pod, por exemplo, é aqui que ficam os containers.

Como descobrir a apiVersion correta

Cada tipo de objeto (kind) pertence a uma apiVersion específica, e isso pode variar entre versões do Kubernetes. Para consultar quais recursos estão disponíveis no seu cluster e a qual API cada um pertence, use:

$ kubectl api-resources
Enter fullscreen mode Exit fullscreen mode

Esse comando lista todos os recursos suportados pelo cluster, junto com o grupo de API correspondente (coluna APIVERSION) — é a fonte mais confiável para saber qual apiVersion usar em cada manifesto, já que isso pode mudar conforme a versão do Kubernetes instalada.

Atenção à indentação

YAML é um formato sensível à indentação — diferente de chaves {} ou colchetes [], a hierarquia dos dados é definida puramente pelos espaços. Por isso:

  • Nunca use TAB para indentar um arquivo YAML — use sempre espaços.
  • O padrão recomendado (e usado nos exemplos abaixo) é 2 espaços por nível de indentação.
  • metadata e spec são mapeamentos (chave-valor), não listas — ou seja, seus campos internos (name, labels, etc.) não devem começar com -.

Esse último ponto é um erro comum: é fácil confundir listas (que usam -) com mapeamentos simples. No exemplo de Pod abaixo, note que name e labels ficam diretamente sob metadata, sem traço.

Pod

O objeto mais básico do Kubernetes é o Pod — a menor unidade que pode ser criada e gerenciada no cluster, contendo um ou mais containers.

apiVersion: v1
kind: Pod
metadata:
  name: meupod
  labels:
    app: meupod-label
spec:
  containers:
    - name: myapp-api
      image: oregontecnologia/myapp-api:1.0.2
Enter fullscreen mode Exit fullscreen mode

Criando o objeto: create vs apply

Existem dois comandos principais para aplicar um manifesto no cluster:

$ kubectl create -f arquivo.yaml
$ kubectl apply -f arquivo.yaml
Enter fullscreen mode Exit fullscreen mode

A diferença entre eles é importante:

  • create cria o objeto apenas se ele ainda não existir — se o objeto já foi criado anteriormente, o comando retorna erro.
  • apply cria o objeto caso ele não exista, ou atualiza o objeto existente caso haja alguma mudança no manifesto. Por isso, apply é o comando mais usado no dia a dia, já que permite reaplicar o mesmo arquivo continuamente conforme ele evolui.

Verificando a criação

$ kubectl get pods
Enter fullscreen mode Exit fullscreen mode

A flag -o wide mostra informações adicionais sobre o pod em execução, como o nó onde ele está rodando e seu IP interno.

Para ver todos os detalhes de um pod ou deployment (incluindo eventos recentes, o que ajuda bastante na hora de depurar problemas):

$ kubectl describe pod myapp-api
Enter fullscreen mode Exit fullscreen mode

Ou, para um deployment:

$ kubectl describe deploy myapp-deploy
Enter fullscreen mode Exit fullscreen mode

ReplicaSet

Um Pod sozinho não se recupera automaticamente se falhar. É aí que entra o ReplicaSet: um objeto responsável por garantir que um número definido de réplicas do mesmo Pod esteja sempre em execução.

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: meureplicaset
spec:
  replicas: 5
  selector:
    matchLabels:
      app: meupod-label
  template:
    metadata:
      labels:
        app: meupod-label
    spec:
      containers:
        - name: myapp-api
          image: oregontecnologia/myapp-api:1.0.2
Enter fullscreen mode Exit fullscreen mode

Repare que a estrutura é um pouco mais aninhada que a do Pod: dentro de spec, temos o selector (que define quais Pods pertencem a este ReplicaSet, com base nos labels) e o template (que descreve como cada réplica do Pod deve ser criada).

Verificando a criação:

$ kubectl get replicaset
Enter fullscreen mode Exit fullscreen mode

Na prática, é raro criar ReplicaSets diretamente — normalmente eles são gerenciados automaticamente por um Deployment, que veremos a seguir. Ainda assim, entender essa camada ajuda a compreender como o Kubernetes garante a disponibilidade dos Pods.

Deployment

O Deployment é a forma mais comum de rodar aplicações no Kubernetes. Ele gerencia ReplicaSets automaticamente, adicionando recursos como rolling updates e rollback — que já vimos na prática na Parte 6 desta série.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: meudeploy
spec:
  replicas: 5
  selector:
    matchLabels:
      app: meupod-label
  template:
    metadata:
      labels:
        app: meupod-label
    spec:
      containers:
        - name: myapp-api
          image: oregontecnologia/myapp-api:1.0.2
Enter fullscreen mode Exit fullscreen mode

Note que a estrutura é praticamente idêntica à do ReplicaSet — o que faz sentido, já que um Deployment cria e gerencia ReplicaSets por trás dos panos. A diferença está no kind e nas funcionalidades adicionais que o Deployment oferece.

Verificando a criação:

$ kubectl get deploy
Enter fullscreen mode Exit fullscreen mode

Service

Por fim, o Service é o objeto responsável por expor Pods de forma estável, com um endereço fixo, independente de quantas vezes os Pods sejam recriados.

apiVersion: v1
kind: Service
metadata:
  name: meudservice
spec:
  selector:
    app: meupod-label
  ports:
    - port: 80
  type: LoadBalancer  # ClusterIP | NodePort | LoadBalancer
Enter fullscreen mode Exit fullscreen mode

O campo type define como o serviço será exposto:

  • ClusterIP (padrão): expõe o serviço apenas internamente, dentro do cluster;
  • NodePort: expõe o serviço em uma porta fixa em todos os nós do cluster (usamos esse tipo na Parte 5, com o Nginx);
  • LoadBalancer: solicita um balanceador de carga externo — em nuvem, provisionado automaticamente pelo provedor; em ambientes on-premise, normalmente requer um IP configurado manualmente ou uma solução como o MetalLB.

Verificando a criação:

$ kubectl get services
Enter fullscreen mode Exit fullscreen mode

Resumo

Objeto Função principal
Pod Unidade mínima de execução (um ou mais containers)
ReplicaSet Garante um número fixo de réplicas de um Pod
Deployment Gerencia ReplicaSets, com suporte a rolling update e rollback
Service Expõe Pods de forma estável, com IP e nome fixos

Entender essa hierarquia — Pod → ReplicaSet → Deployment, mais o Service como camada de exposição — é a base para ler (e escrever) qualquer manifesto do Kubernetes com confiança, independente da complexidade da aplicação.

Fechando o ciclo operacional

Com o deploy de uma aplicação própria, atualizações controladas, rollback e escalabilidade (manual e automática), cobrimos as operações mais comuns do dia a dia de um cluster Kubernetes on-premise. A partir daqui, o cluster está pronto não só para hospedar aplicações, mas também para operá-las de forma resiliente conforme a demanda aumenta.

Isso encerra o conteúdo mais conceitual desta série. Nos próximos artigos, seguimos explorando tópicos práticos do dia a dia de um cluster on-premise.

Nos próximos artigos da série, vamos aprofundar em temas como registro de imagens privado on-premise, armazenamento persistente e observabilidade do cluster.

Artigos relacionados:

  • Adicionando e removendo worker ao cluster.
  • Criando um servidor de registros para suas imagens docker.
  • Renovando certificado do kubernetes.
  • Addons uteis para seu kubernetes.
    • Metric server
    • NGinx Ingress controller
    • Cert-Manager
    • Portainer
    • Prometheus
    • Grafana

Top comments (0)