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:
# 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 | bashFaça login na sua conta Azure:
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:
# 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-prodAlguns 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:
kubectl get nodes
kubectl cluster-infoExemplo 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:
// 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:
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:
az acr build \
--registry acrvivodecod \
--image vivodecodigo-api:v1.0.0 .Manifestos Kubernetes para produção
# 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: 80Aplique os manifestos:
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/
kubectl get pods -n vivodecodigo -wExemplo 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:
// 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