EngineeringSolution ArchitectAzure Architecture

Session 03 - AKS & Container Orchestration

Tổng quan

Azure Kubernetes Service (AKS) là managed Kubernetes trên Azure - Microsoft quản lý control plane, bạn chỉ cần quản lý worker nodes. Đây là lựa chọn hàng đầu cho .NET microservices cần container orchestration.

AKS Architecture Overview

Node Pools - System vs User

Đặc điểmSystem Node PoolUser Node Pool
Mục đíchChạy system pods (CoreDNS, kube-proxy)Chạy application workloads
Bắt buộcÍt nhất 1 poolTùy chọn (nhưng khuyến nghị)
VM SKU khuyến nghịStandard_D4s_v5 (4 vCPU, 16 GB)Tùy workload
TaintCriticalAddonsOnly=true:NoScheduleCustom taints
Min nodes>= 2 cho HA>= 2 cho production
bash
# Tạo AKS cluster với system + user node pools
az aks create \
  --resource-group rg-app \
  --name aks-myapp-prod \
  --location southeastasia \
  --node-count 2 \
  --node-vm-size Standard_D4s_v5 \
  --nodepool-name system \
  --network-plugin azure \
  --network-policy calico \
  --enable-managed-identity \
  --attach-acr acrMyApp \
  --generate-ssh-keys

# Thêm user node pool cho application
az aks nodepool add \
  --resource-group rg-app \
  --cluster-name aks-myapp-prod \
  --name apppool \
  --node-count 3 \
  --node-vm-size Standard_D8s_v5 \
  --mode User \
  --labels workload=api

Scaling Strategies

Horizontal Pod Autoscaler (HPA)

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: dotnet-api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: dotnet-api
  minReplicas: 2
  maxReplicas: 20
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300

KEDA - Event-Driven Autoscaling

yaml
# KEDA ScaledObject - scale dựa trên Azure Service Bus queue depth
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: order-processor-scaler
spec:
  scaleTargetRef:
    name: order-processor
  minReplicaCount: 1
  maxReplicaCount: 50
  triggers:
  - type: azure-servicebus
    metadata:
      queueName: orders
      messageCount: "10"
      connectionFromEnv: SB_CONNECTION

Cluster Autoscaler

bash
# Enable cluster autoscaler cho user node pool
az aks nodepool update \
  --resource-group rg-app \
  --cluster-name aks-myapp-prod \
  --name apppool \
  --enable-cluster-autoscaler \
  --min-count 2 \
  --max-count 10

Scaling hierarchy: HPA/KEDA scale pods -> Cluster Autoscaler scale nodes khi pods pending.

.NET on AKS Playbook - End to End

Step 1: Multi-stage Dockerfile

dockerfile
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyApp.Api/MyApp.Api.csproj", "MyApp.Api/"]
RUN dotnet restore "MyApp.Api/MyApp.Api.csproj"
COPY . .
RUN dotnet publish "MyApp.Api/MyApp.Api.csproj" \
    -c Release -o /app/publish --no-restore

# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
RUN adduser --disabled-password --no-create-home appuser
USER appuser
COPY --from=build /app/publish .
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:8080/healthz || exit 1
ENTRYPOINT ["dotnet", "MyApp.Api.dll"]

Step 2: Build & Push lên ACR

bash
# Login vào ACR
az acr login --name acrMyApp

# Build và push
az acr build \
  --registry acrMyApp \
  --image myapp-api:v1.0.0 \
  --file MyApp.Api/Dockerfile .

Step 3: Kubernetes Deployment

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dotnet-api
  labels:
    app: dotnet-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: dotnet-api
  template:
    metadata:
      labels:
        app: dotnet-api
    spec:
      nodeSelector:
        workload: api
      containers:
      - name: dotnet-api
        image: acrmyapp.azurecr.io/myapp-api:v1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: "250m"
            memory: "256Mi"
          limits:
            cpu: "1000m"
            memory: "512Mi"
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 15
          periodSeconds: 20
        env:
        - name: ASPNETCORE_ENVIRONMENT
          value: "Production"
        - name: ConnectionStrings__Default
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: db-connection
---
apiVersion: v1
kind: Service
metadata:
  name: dotnet-api-svc
spec:
  selector:
    app: dotnet-api
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP

Step 4: Deploy

bash
# Lấy credentials
az aks get-credentials \
  --resource-group rg-app \
  --name aks-myapp-prod

# Apply manifests
kubectl apply -f k8s/deployment.yaml
kubectl rollout status deployment/dotnet-api

Helm Chart cho .NET Apps

bash
# Tạo Helm chart
helm create dotnet-api-chart

# Cấu trúc quan trọng trong values.yaml
yaml
# values.yaml
replicaCount: 3
image:
  repository: acrmyapp.azurecr.io/myapp-api
  tag: "v1.0.0"
  pullPolicy: IfNotPresent

resources:
  requests:
    cpu: 250m
    memory: 256Mi
  limits:
    cpu: 1000m
    memory: 512Mi

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 20
  targetCPUUtilizationPercentage: 70

ingress:
  enabled: true
  className: nginx
  hosts:
    - host: api.myapp.com
      paths:
        - path: /
          pathType: Prefix

Ingress - NGINX vs AGIC

Tiêu chíNGINX Ingress ControllerAGIC (App Gateway)
LayerL7 (in-cluster)L7 (Azure-managed)
WAFKhông built-inAzure WAF tích hợp
SSL terminationCert-manager + Let's EncryptApp Gateway certificates
CostChỉ tốn pod resourcesApplication Gateway pricing
Phù hợpStartup, mid-sizeEnterprise, compliance cao

Pod Identity với Managed Identity

bash
# Enable workload identity trên AKS
az aks update \
  --resource-group rg-app \
  --name aks-myapp-prod \
  --enable-oidc-issuer \
  --enable-workload-identity

# Tạo managed identity
az identity create \
  --resource-group rg-app \
  --name id-dotnet-api

# Tạo federated credential
AKS_OIDC=$(az aks show -g rg-app -n aks-myapp-prod \
  --query "oidcIssuerProfile.issuerUrl" -o tsv)

az identity federated-credential create \
  --name fc-dotnet-api \
  --identity-name id-dotnet-api \
  --resource-group rg-app \
  --issuer $AKS_OIDC \
  --subject system:serviceaccount:default:sa-dotnet-api
csharp
// Sử dụng Managed Identity trong .NET
builder.Services.AddAzureClients(clientBuilder =>
{
    clientBuilder.AddSecretClient(
        new Uri("https://kv-myapp.vault.azure.net/"));
    clientBuilder.UseCredential(new DefaultAzureCredential());
});

Trade-off Analysis

Quyết địnhOption AOption B
AKS vs App ServiceToàn quyền control, phức tạpĐơn giản, ít control
Azure CNI vs KubenetIP per pod, tốn IP range, nhanhNAT, tiết kiệm IP, chậm hơn
KEDA vs HPAEvent-driven, scale-to-zeroCPU/Memory only, luôn >= 1 pod
Managed NGINX vs self-hostedÍt config, limited tuningFull control, cần maintain