Deploy Kubernetes on Azure with Terraform

☸️ Kubernetes πŸ”· Microsoft Azure πŸ—οΈ Terraform

Configuration Files

5 files
Production-ready configuration files with detailed comments and best practices. Each file works together as a complete deployment solution.
Kubernetes deployment configuration for your application
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: myregistry.azurecr.io/my-app:latest
        ports:
        - containerPort: 3000
        env:
        - name: NODE_ENV
          value: "production"
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000

Pro Tips

  • πŸ”„ replicas: 3 ensures high availability
  • πŸ’° Resource limits prevent cost overruns
  • πŸ₯ Health checks enable auto-healing
  • ⚑ LoadBalancer gets public IP automatically
  • πŸ“Š Adjust resources based on app needs
  • 🎯 Use Azure Container Registry (ACR) for images
  • πŸ’‘ kubectl apply -f deployment.yaml to deploy
  • πŸ” kubectl get pods to check status
Ingress configuration with HTTPS support
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  annotations:
    kubernetes.io/ingress.class: azure/application-gateway
    cert-manager.io/cluster-issuer: letsencrypt-prod
    appgw.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - myapp.example.com
    secretName: myapp-tls
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-app-service
            port:
              number: 80

Pro Tips

  • πŸ”’ Automatic HTTPS with cert-manager + Let's Encrypt
  • 🌐 Azure Application Gateway Ingress Controller
  • πŸ”„ SSL redirect forces HTTPS
  • πŸ’‘ Install cert-manager: kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
  • πŸ“ Update host to your domain
  • 🎯 Configure DNS to point to ingress IP
  • ⚠️ Application Gateway costs ~$125/month
  • πŸ’° Alternative: Use nginx-ingress for ~$10/month
GitHub Actions workflow for CI/CD to AKS
yaml
name: Deploy to Azure Kubernetes Service

on:
  push:
    branches:
      - main

env:
  AZURE_RESOURCE_GROUP: my-aks-rg
  CLUSTER_NAME: my-aks-cluster
  ACR_NAME: myregistry
  IMAGE_NAME: my-app

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Azure Login
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

      - name: Build and push to ACR
        run: |
          az acr build --registry ${{ env.ACR_NAME }} \
            --image ${{ env.IMAGE_NAME }}:${{ github.sha }} \
            --image ${{ env.IMAGE_NAME }}:latest \
            --file Dockerfile .

      - name: Get AKS credentials
        run: |
          az aks get-credentials \
            --resource-group ${{ env.AZURE_RESOURCE_GROUP }} \
            --name ${{ env.CLUSTER_NAME }} \
            --overwrite-existing

      - name: Deploy to AKS
        run: |
          kubectl set image deployment/my-app \
            my-app=${{ env.ACR_NAME }}.azurecr.io/${{ env.IMAGE_NAME }}:${{ github.sha }}
          kubectl rollout status deployment/my-app

      - name: Verify deployment
        run: |
          kubectl get services
          kubectl get pods

Pro Tips

  • πŸ” Create Azure service principal: az ad sp create-for-rbac --name "myApp" --role contributor --scopes /subscriptions/{subscription-id}/resourceGroups/{resource-group} --sdk-auth
  • πŸ”‘ Add output JSON to GitHub Secrets as AZURE_CREDENTIALS
  • πŸ“¦ ACR (Azure Container Registry) stores Docker images
  • πŸš€ Rolling updates with zero downtime
  • ⚑ Automatic rollback on failure
  • πŸ’‘ Use GitHub Environments for staging/prod
  • 🎯 kubectl rollout undo to rollback manually
  • πŸ“Š Monitor with Azure Monitor for containers
Multi-stage Dockerfile for Node.js apps
dockerfile
# Build stage
FROM node:20-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

# Production stage
FROM node:20-alpine

WORKDIR /app

# Copy dependencies and built app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./

# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

USER nodejs

EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"

CMD ["node", "dist/index.js"]

Pro Tips

  • πŸ—οΈ Multi-stage build reduces image size by 70%+
  • πŸ”’ Non-root user improves security
  • πŸ₯ Health check enables auto-healing
  • ⚑ Alpine Linux = smaller images (~50MB vs 900MB)
  • πŸ“¦ Production dependencies only
  • πŸ’‘ Adjust paths based on your framework
  • 🎯 Build locally: docker build -t my-app .
  • πŸš€ Test: docker run -p 3000:3000 my-app
Terraform variables configuration
hcl
resource_group_name = "my-aks-rg"
location            = "eastus"
cluster_name        = "my-aks-cluster"
node_count          = 2

# Optional: Customize node pool
# node_vm_size = "Standard_D4s_v3"  # 4 vCPU, 16GB RAM
# min_node_count = 1
# max_node_count = 10

Pro Tips

  • πŸ“ Copy to terraform.tfvars for your values
  • 🌍 Available regions: eastus, westus2, westeurope, etc.
  • πŸ’° Standard_D2s_v3: ~$70/month per node
  • πŸ’° Standard_D4s_v3: ~$140/month per node
  • ⚑ Auto-scaling adjusts nodes based on load
  • 🎯 Start with 2 nodes for high availability
  • πŸ“Š Monitor costs in Azure Cost Management
  • ⚠️ Don't commit terraform.tfvars to git

Prerequisites

  • Azure account with active subscription
  • Azure CLI installed
  • Terraform 1.0+ installed
  • kubectl installed
  • Docker installed (for building images)
  • GitHub account (for CI/CD)

Deployment Steps

  • 1. Install Azure CLI: curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
  • 2. Login to Azure: az login
  • 3. Create service principal: az ad sp create-for-rbac --role="Contributor"
  • 4. Install Terraform: https://www.terraform.io/downloads
  • 5. Install kubectl: az aks install-cli
  • 6. Copy terraform.tfvars.example to terraform.tfvars and customize
  • 7. Run: terraform init
  • 8. Run: terraform plan
  • 9. Run: terraform apply
  • 10. Get credentials: az aks get-credentials --resource-group my-aks-rg --name my-aks-cluster
  • 11. Verify: kubectl get nodes
  • 12. Create ACR: az acr create --resource-group my-aks-rg --name myregistry --sku Basic
  • 13. Attach ACR to AKS: az aks update --resource-group my-aks-rg --name my-aks-cluster --attach-acr myregistry
  • 14. Deploy app: kubectl apply -f k8s/deployment.yaml
  • 15. Get external IP: kubectl get service my-app-service

Additional Notes

  • ☸️ AKS: Fully managed Kubernetes service
  • πŸ’° Cost: ~$140-300/month (2 nodes + load balancer)
  • πŸ”’ Azure AD integration for RBAC
  • ⚑ Auto-scaling for nodes and pods
  • πŸ“Š Azure Monitor integration included
  • 🌍 99.95% SLA with availability zones
  • 🎯 Perfect for production workloads
  • πŸ’‘ Free cluster management (only pay for nodes)
  • πŸ”„ Rolling updates with zero downtime
  • ⚠️ Application Gateway adds ~$125/month
  • πŸ’° Alternative: Use nginx-ingress for lower cost
  • πŸš€ Supports Windows and Linux containers