AKS elimina la complejidad de gestionar el control plane de Kubernetes. aquí está el setup mínimo para producción con autoscaling, availability zones y networking correcto.
Azure Kubernetes Service (AKS) es Kubernetes managed donde Azure gestiona:
- Control plane (API server, etcd, scheduler) - Sin coste
 
- Actualizaciones y patches - Automatizadas
 
- Alta disponibilidad - SLA 99.95% con uptime SLA
 
Tú solo pagas y gestionas los worker nodes.
# Variables
RG="aks-prod-rg"
LOCATION="westeurope"
CLUSTER_NAME="aks-prod-cluster"
NODE_COUNT=2
# Crear resource group
az group create \
  --name $RG \
  --location $LOCATION
# Crear AKS con best practices
az aks create \
  --resource-group $RG \
  --name $CLUSTER_NAME \
  --node-count $NODE_COUNT \
  --node-vm-size Standard_DS2_v2 \
  --vm-set-type VirtualMachineScaleSets \
  --load-balancer-sku standard \
  --enable-cluster-autoscaler \
  --min-count 1 \
  --max-count 5 \
  --network-plugin azure \
  --generate-ssh-keys \
  --zones 1 2 3
 
Availability Zones
--zones 1 2 3 distribuye nodes entre 3 AZs para alta disponibilidad. Esto NO se puede cambiar después de crear el cluster.
 
# Configurar kubectl
az aks get-credentials \
  --resource-group $RG \
  --name $CLUSTER_NAME
# Verificar conexión
kubectl get nodes
 
El Cluster Autoscaler escala nodes automáticamente basándose en pods pendientes:
Cómo funciona:
1. Pod no puede ser scheduled (falta capacidad)
2. Autoscaler añade node nuevo
3. Pod se programa en el nuevo node
4. Cuando nodes están infrautilizados, autoscaler los elimina
# Autoscaler agresivo (escala rápido, libera rápido)
az aks update \
  --resource-group $RG \
  --name $CLUSTER_NAME \
  --cluster-autoscaler-profile \
    scan-interval=30s \
    scale-down-delay-after-add=0m \
    scale-down-unneeded-time=3m \
    scale-down-unready-time=3m
# Autoscaler conservador (para workloads bursty)
az aks update \
  --resource-group $RG \
  --name $CLUSTER_NAME \
  --cluster-autoscaler-profile \
    scan-interval=20s \
    scale-down-delay-after-add=10m \
    scale-down-unneeded-time=5m \
    scale-down-unready-time=5m
 
Parámetros clave:
- scan-interval: Frecuencia de evaluación (defecto 10s)
- scale-down-delay-after-add: Espera después de añadir node
- scale-down-unneeded-time: Tiempo antes de eliminar node infrautilizado
# Cambiar min/max de un node pool
az aks nodepool update \
  --resource-group $RG \
  --cluster-name $CLUSTER_NAME \
  --name nodepool1 \
  --update-cluster-autoscaler \
  --min-count 2 \
  --max-count 10
 
Escala pods basándose en CPU/memoria o métricas custom:
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
 
kubectl apply -f hpa.yaml
# Ver estado HPA
kubectl get hpa
 
Ajusta CPU/memoria requests automáticamente:
# Habilitar VPA en el cluster
az aks update \
  --resource-group $RG \
  --name $CLUSTER_NAME \
  --enable-vpa
 
# vpa.yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: my-app-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  updatePolicy:
    updateMode: "Auto"  # Auto | Off | Initial
 
Modos VPA:
- Off: Solo recomendaciones, no aplica cambios
- Auto: Actualiza resources durante pod restart
- Initial: Solo establece resources en creación
HPA + VPA
NO uses HPA y VPA juntos en las mismas métricas (CPU/memoria). Crea conflictos. Puedes combinar HPA en custom metrics con VPA en CPU/memoria.
 
# Node pool para cargas batch (spot instances)
az aks nodepool add \
  --resource-group $RG \
  --cluster-name $CLUSTER_NAME \
  --name spotpool \
  --node-count 1 \
  --priority Spot \
  --eviction-policy Delete \
  --spot-max-price -1 \
  --enable-cluster-autoscaler \
  --min-count 0 \
  --max-count 5 \
  --node-taints kubernetes.azure.com/scalesetpriority=spot:NoSchedule
# Node pool para sistema (garantizado)
az aks nodepool add \
  --resource-group $RG \
  --cluster-name $CLUSTER_NAME \
  --name systempool \
  --node-count 2 \
  --node-vm-size Standard_DS3_v2 \
  --mode System \
  --zones 1 2 3
 
Azure CNI (recomendado para producción):
- Pods tienen IPs de la VNet
- Integración directa con servicios Azure
- Soporte para Network Policies
Kubenet (más simple, menos IPs):
- Pods usan IPs privadas (NAT)
- Menos consumo de IPs de subnet
- No soporta Windows node pools
# Crear cluster con Azure CNI Overlay (más eficiente IPs)
az aks create \
  --resource-group $RG \
  --name aks-cni-overlay \
  --network-plugin azure \
  --network-plugin-mode overlay \
  --pod-cidr 10.244.0.0/16
 
# Ver versiones disponibles
az aks get-upgrades \
  --resource-group $RG \
  --name $CLUSTER_NAME \
  --output table
# Upgrade a versión específica
az aks upgrade \
  --resource-group $RG \
  --name $CLUSTER_NAME \
  --kubernetes-version 1.28.3
# Habilitar auto-upgrade
az aks update \
  --resource-group $RG \
  --name $CLUSTER_NAME \
  --auto-upgrade-channel stable
 
Canales auto-upgrade:
- none: Manual
- patch: Auto-upgrade a patches (1.27.3 → 1.27.5)
- stable: Versión N-1 estable
- rapid: Última versión disponible
Garantiza disponibilidad durante upgrades:
# pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: my-app-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: my-app
 
kubectl apply -f pdb.yaml
# Verificar PDB
kubectl get pdb
 
# Habilitar Container Insights
az aks enable-addons \
  --resource-group $RG \
  --name $CLUSTER_NAME \
  --addons monitoring
# Ver logs en tiempo real
az aks browse \
  --resource-group $RG \
  --name $CLUSTER_NAME
 
Queries útiles en Log Analytics:
// Pods con más restarts
KubePodInventory
| where ClusterName == "aks-prod-cluster"
| summarize RestartCount = sum(PodRestartCount) by Name
| order by RestartCount desc
| take 10
// Nodes con alta CPU
Perf
| where ObjectName == "K8SNode"
| where CounterName == "cpuUsageNanoCores"
| summarize AvgCPU = avg(CounterValue) by Computer
| order by AvgCPU desc
 
- Availability Zones: Siempre usa 3 AZs para producción
 
- System node pool separado: Aísla workloads de componentes del sistema
 
- Resource limits: Define requests y limits en todos los pods
 
- PodDisruptionBudgets: Evita downtime en upgrades
 
- Azure CNI: Usa Azure CNI para integración completa
 
- Autoscaling: Combina cluster autoscaler + HPA
 
- No B-series VMs: Usa D/E/F series para workloads serios
 
- Premium Disks: Para bases de datos y cargas I/O intensivas
 
Costos
- Control plane: Gratis (o €0.10/hora con uptime SLA)
 
- Nodes: Pagas VMs estándar (~€50-150/mes por node DS2_v2)
 
- Usa Spot VMs para workloads batch (70% descuento)
 
 
# Ver eventos del cluster
kubectl get events --sort-by='.lastTimestamp'
# Pods que no arrancan
kubectl describe pod <pod-name>
# Logs de un pod
kubectl logs <pod-name> --previous
# Ejecutar comando en pod
kubectl exec -it <pod-name> -- /bin/bash
# Ver resource usage
kubectl top nodes
kubectl top pods