Ir para o conteúdo
Ferramenta para Plataforma1510 palavras

Kubernetes para Azure: Guia Completo de Deploy

Introdução

Se você já trabalha com containers e está buscando uma plataforma gerenciada para orquestrar seus workloads, o Azure Kubernetes Service (AKS) é uma das opções mais robustas do mercado. Diferente de montar um cluster Kubernetes do zero (o que ninguém deveria fazer em produção sem um motivo muito forte), o AKS abstrai a complexidade do control plane e permite que você foque no que realmente importa: entregar software funcionando.

Neste guia, vou mostrar como configurar um cluster AKS do zero, fazer deploys automatizados, e integrar tudo isso com o ecossistema Azure usando exemplos práticos em TypeScript. Se você já tem pelo menos dois anos de experiência com desenvolvimento e alguma familiaridade com Docker, este conteúdo foi feito para você.

Se quiser complementar com uma visão mais ampla de DevOps, recomendo também o post DevOps para Devs: Docker, CI/CD, Kubernetes e AWS em Produção, que cobre conceitos fundamentais aplicáveis a qualquer cloud.

Por que usar Kubernetes no Azure

Vantagens do AKS sobre clusters autogerenciados

A primeira razão é simples: o control plane do AKS é gratuito. Você paga apenas pelos nodes (as VMs que executam seus pods). Isso já elimina uma das maiores dores de cabeça do Kubernetes, que é manter os componentes de gerenciamento (API server, etcd, scheduler) rodando e atualizados.

Além disso, o AKS se integra nativamente com outros serviços do Azure: Azure Active Directory para autenticação, Azure Monitor para observabilidade, Azure Container Registry (ACR) para armazenamento de imagens, Azure Key Vault para gestão de segredos, e Azure Policy para governança. Essa integração nativa reduz drasticamente o trabalho de "colar" serviços que, em outras plataformas, exigem configurações manuais complexas.

Casos de uso onde essa combinação brilha

Existem cenários em que o AKS se destaca de forma clara:

Aplicações de microsserviços com alta variação de carga: plataformas de e-commerce que precisam escalar automaticamente durante eventos como Black Friday se beneficiam do Horizontal Pod Autoscaler combinado com o Cluster Autoscaler do AKS.

Ambientes multi-tenant para SaaS: empresas que precisam isolar workloads de diferentes clientes em namespaces separados, com políticas de rede e quotas de recursos bem definidas.

Pipelines de machine learning: times de data science que precisam executar jobs de treinamento em GPUs sob demanda, usando node pools especializados que sobem e descem conforme a necessidade.

Aplicações reguladas (fintech, healthtech): a integração com Azure Policy e Azure AD permite atender requisitos de compliance como PCI-DSS e LGPD com menos esforço.

Setup completo do AKS

Pré-requisitos e ferramentas necessárias

Antes de começar, instale as seguintes ferramentas:

Bash
# Azure CLI
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
 
# kubectl
az aks install-cli
 
# Helm (gerenciador de pacotes para Kubernetes)
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

Faça login na sua conta Azure:

Bash
az login
az account set --subscription "SUA_SUBSCRIPTION_ID"

Criando o cluster AKS via CLI

Vamos criar um cluster pronto para produção com as configurações recomendadas:

Bash
# Criar resource group
az group create \
  --name rg-vivodecodigo-prod \
  --location brazilsouth
 
# Criar Azure Container Registry
az acr create \
  --resource-group rg-vivodecodigo-prod \
  --name acrvivodecod \
  --sku Standard
 
# Criar cluster AKS
az aks create \
  --resource-group rg-vivodecodigo-prod \
  --name aks-vivodecodigo-prod \
  --node-count 3 \
  --node-vm-size Standard_D4s_v3 \
  --enable-managed-identity \
  --attach-acr acrvivodecod \
  --network-plugin azure \
  --network-policy calico \
  --enable-addons monitoring \
  --generate-ssh-keys \
  --zones 1 2 3
 
# Obter credenciais do cluster
az aks get-credentials \
  --resource-group rg-vivodecodigo-prod \
  --name aks-vivodecodigo-prod

Alguns pontos importantes sobre as flags utilizadas:

O parâmetro --enable-managed-identity configura identidade gerenciada, eliminando a necessidade de gerenciar service principals manualmente. O --attach-acr vincula o registry de imagens ao cluster, permitindo que os nodes façam pull sem precisar de credenciais explícitas. O --network-plugin azure usa Azure CNI, que atribui IPs da VNet diretamente aos pods, melhorando a integração com outros recursos Azure. O --zones 1 2 3 distribui os nodes em três zonas de disponibilidade para alta disponibilidade real.

Verifique se tudo está funcionando:

Bash
kubectl get nodes
kubectl cluster-info

Exemplo prático: deploy completo de uma API Node.js com TypeScript

Aplicação e Dockerfile

Vamos criar uma API simples que demonstra integração com Azure Key Vault para gestão de segredos e Azure Monitor para métricas:

TYPESCRIPT
// src/server.ts
import express, { Request, Response } from 'express';
import { DefaultAzureCredential } from '@azure/identity';
import { SecretClient } from '@azure/keyvault-secrets';
import promClient from 'prom-client';
 
const app = express();
const port = process.env.PORT || 3000;
 
// Configuração de métricas Prometheus
const collectDefaultMetrics = promClient.collectDefaultMetrics;
collectDefaultMetrics({ prefix: 'vivodecodigo_api_' });
 
const httpRequestDuration = new promClient.Histogram({
  name: 'vivodecodigo_api_http_duration_seconds',
  help: 'Duração das requisições HTTP em segundos',
  labelNames: ['method', 'route', 'status_code'],
  buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
});
 
const httpRequestTotal = new promClient.Counter({
  name: 'vivodecodigo_api_http_requests_total',
  help: 'Total de requisições HTTP',
  labelNames: ['method', 'route', 'status_code'],
});
 
// Middleware de métricas
app.use((req: Request, res: Response, next) => {
  const end = httpRequestDuration.startTimer();
  res.on('finish', () => {
    const labels = {
      method: req.method,
      route: req.route?.path || req.path,
      status_code: res.statusCode.toString(),
    };
    end(labels);
    httpRequestTotal.inc(labels);
  });
  next();
});
 
// Inicialização do Key Vault (usa Managed Identity do AKS)
const vaultUrl = process.env.AZURE_KEYVAULT_URL || '';
let secretClient: SecretClient | null = null;
 
if (vaultUrl) {
  const credential = new DefaultAzureCredential();
  secretClient = new SecretClient(vaultUrl, credential);
}
 
// Endpoint de saúde para readiness/liveness probes
app.get('/healthz', (_req: Request, res: Response) => {
  res.status(200).json({ status: 'healthy', timestamp: new Date().toISOString() });
});
 
app.get('/ready', async (_req: Request, res: Response) => {
  try {
    if (secretClient) {
      await secretClient.getSecret('health-check-secret');
    }
    res.status(200).json({ status: 'ready' });
  } catch (error) {
    res.status(503).json({ status: 'not ready', error: 'Falha ao acessar Key Vault' });
  }
});
 
// Endpoint de métricas para Prometheus
app.get('/metrics', async (_req: Request, res: Response) => {
  res.set('Content-Type', promClient.register.contentType);
  res.end(await promClient.register.metrics());
});
 
// Endpoint principal
app.get('/api/config', async (_req: Request, res: Response) => {
  try {
    if (!secretClient) {
      res.status(500).json({ error: 'Key Vault não configurado' });
      return;
    }
    const dbConnectionSecret = await secretClient.getSecret('database-connection-string');
    res.json({
      message: 'Configuração obtida com sucesso',
      dbConfigured: !!dbConnectionSecret.value,
    });
  } catch (error) {
    res.status(500).json({ error: 'Erro ao obter configuração' });
  }
});
 
app.listen(port, () => {
  console.log(`Servidor rodando na porta ${port}`);
  console.log(`Key Vault URL: ${vaultUrl || 'não configurado'}`);
});

O Dockerfile para essa aplicação:

Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json tsconfig.json ./
RUN npm ci
COPY src/ ./src/
RUN npm run build
 
FROM node:20-alpine
WORKDIR /app
RUN addgroup -g 1001 appgroup && adduser -u 1001 -G appgroup -s /bin/sh -D appuser
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
USER appuser
EXPOSE 3000
CMD ["node", "dist/server.js"]

Build e push para o ACR:

Bash
az acr build \
  --registry acrvivodecod \
  --image vivodecodigo-api:v1.0.0 .

Manifestos Kubernetes para produção

YAML
# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: vivodecodigo
  labels:
    app.kubernetes.io/part-of: vivodecodigo
 
---
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: vivodecodigo
  labels:
    app: api
    version: v1.0.0
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
        version: v1.0.0
    spec:
      serviceAccountName: api-sa
      containers:
        - name: api
          image: acrvivodecod.azurecr.io/vivodecodigo-api:v1.0.0
          ports:
            - containerPort: 3000
              protocol: TCP
          env:
            - name: PORT
              value: "3000"
            - name: AZURE_KEYVAULT_URL
              value: "https://kv-vivodecodigo.vault.azure.net"
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi
          livenessProbe:
            httpGet:
              path: /healthz
              port: 3000
            initialDelaySeconds: 10
            periodSeconds: 15
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /ready
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10
            failureThreshold: 3
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: api
 
---
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: api-service
  namespace: vivodecodigo
spec:
  selector:
    app: api
  ports:
    - port: 80
      targetPort: 3000
      protocol: TCP
  type: ClusterIP
 
---
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-hpa
  namespace: vivodecodigo
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

Aplique os manifestos:

Bash
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/
kubectl get pods -n vivodecodigo -w

Exemplo prático: automação de infraestrutura com Pulumi e TypeScript

Para quem prefere Infrastructure as Code com TypeScript em vez de YAML puro, o Pulumi é uma alternativa poderosa ao Terraform:

TYPESCRIPT
// infra/index.ts
import * as pulumi from '@pulumi/pulumi';
import * as azure from '@pulumi/azure-native';
import * as k8s from '@pulumi/kubernetes';
 
const config = new pulumi.Config();
const clusterName = config.require('clusterName');
const nodeCount = config.getNumber('nodeCount') || 3;
const nodeSize = config.get('nodeSize') || 'Standard_D4s_v3';
 
// Resource Group
const resourceGroup = new azure.resources.ResourceGroup('rg-vivodecodigo', {
  location: 'brazilsouth',
  resourceGroupName: 'rg-vivodecodigo-prod',
});
 
// Azure Container Registry
const acr = new azure.containerregistry.Registry('acrvivodecod', {
  resourceGroupName: resourceGroup.name,
  registryName: 'acrvivodecod',
  sku: { name: 'Standard' },
  adminUserEnabled: false,
});
 
// AKS Cluster
const aksCluster = new azure.containerservice.ManagedCluster('aks-cluster', {
  resourceGroupName: resourceGroup.name,
  resourceName: clusterName,
  location: resourceGroup.location,
  dnsPrefix: clusterName,
  identity: { type: 'SystemAssigned' },
  agentPoolProfiles: [
    {
      name: 'systempool',
      count: nodeCount,
      vmSize: nodeSize,
      osType: 'Linux',
      mode: 'System',
      availabilityZones: ['1', '2', '3'],
      enableAutoScaling: true,
      minCount: 3,
      maxCount: 10,
    },
    {
      name: 'workerpool',
      count: 2,
      vmSize: 'Standard_D8s_v3',
      osType: 'Linux',
      mode: 'User',
      availabilityZones: ['1', '2', '3'],
      enableAutoScaling: true,
      minCount: 2,
      maxCount: 30,
    },
  ],
});

Guias Relacionados

Explore mais guias técnicos

Mais de 100 guias práticos de integração entre frameworks, ferramentas e plataformas.

Ver todos os guias