The Complete Azure Kubernetes Service Tutorial(2025): Clusters, Scaling, Networking, Security & Real-World DevOps Use Cases

Target Audience: DevOps engineers, Azure beginners → intermediate learners (AZ-104, AZ-204, AZ-305, AZ-400)
Estimated Reading Time: 15–18 minutes


What You’ll Build in This Guide

By following along with this tutorial, you’ll learn how to:

  • Create and configure production-ready AKS clusters using CLI, Portal, and Terraform
  • Integrate with Azure Container Registry (ACR) for secure image management
  • Implement RBAC with Azure AD for enterprise authentication
  • Deploy applications using Helm charts with environment-specific configurations
  • Set up monitoring and observability with Container Insights and Log Analytics
  • Apply security best practices including network policies, Key Vault integration, and pod identity

Imagine deploying hundreds of microservices without ever worrying about managing master nodes, etcd clusters, or control plane upgrades. That’s the power Azure Kubernetes Service (AKS) gives you as a DevOps engineer.

When I first started working with Kubernetes in production, managing self-hosted clusters felt like babysitting a complex machine that demanded constant attention. Patching nodes, upgrading control planes, monitoring etcd health—it was all necessary but time-consuming. Then I discovered AKS, and suddenly I could focus on what actually mattered: deploying applications, optimizing workloads, and building reliable CI/CD pipelines.

In this guide, I’ll walk you through everything you need to know about AKS—from basic architecture to advanced security patterns. Whether you’re studying for AZ-104, AZ-204, or AZ-400, or you’re just trying to get your first microservices deployment running on Azure, this tutorial has you covered.


Introduction to Azure Kubernetes Service (AKS)

Azure Kubernetes Service (AKS) is Microsoft’s managed Kubernetes offering that takes away the operational overhead of running Kubernetes clusters. Think of it like this: AKS is like having a dedicated operations team working behind the scenes, handling all the complex infrastructure tasks while you focus on deploying and scaling your applications.

Here’s what makes AKS special. With traditional self-managed Kubernetes, you’re responsible for everything—the control plane, master nodes, etcd databases, networking, security patches, and upgrades. It’s a full-time job. With AKS, Microsoft manages the control plane for you at no extra cost. The control plane components—API server, scheduler, controller manager, and etcd—run in a Microsoft-managed Azure subscription that you never directly interact with. You only pay for the worker nodes where your applications run.

The real power of AKS comes from its deep integration with the Azure ecosystem. Your AKS clusters can seamlessly connect to Azure Container Registry (ACR) for private container images, Azure Active Directory for enterprise authentication, Azure Key Vault for secrets management, and Azure Monitor for comprehensive observability. This native integration is what makes AKS a production-ready platform right out of the box.

From a DevOps perspective, AKS shines when you’re running microservices architectures. I’ve seen teams deploy dozens of services across multiple environments—dev, staging, production—all orchestrated through GitHub Actions or Azure DevOps pipelines. The automated scaling, self-healing capabilities, and declarative deployment model make AKS ideal for modern cloud-native applications.


Understanding AKS Architecture

Before we dive into creating clusters, let’s understand what’s happening under the hood. AKS architecture consists of several key components that work together to run your containerized workloads.

The control plane is the brain of your Kubernetes cluster. It includes the API server, scheduler, controller manager, and etcd database. Here’s the beautiful part: Microsoft manages all of this for you in a separate Azure subscription. You never see these components, never pay for them directly, and never worry about upgrading them. When you create an AKS cluster, Azure provisions these components automatically, and they remain fully managed throughout the cluster’s lifecycle.

Your workloads run on node pools, which are groups of virtual machines. When you create an AKS cluster, Azure automatically creates a system node pool that runs critical Kubernetes components like CoreDNS, metrics-server, and tunnelfront. These system pods keep your cluster functioning properly. You can then add user node pools specifically for your application workloads.

This separation is important. I learned this the hard way when I ran application pods on the system node pool in my early AKS days. During a traffic spike, application pods consumed so many resources that CoreDNS started failing, and suddenly DNS resolution stopped working across the entire cluster. Since then, I always keep system and user node pools separate.

Each node pool connects to an Azure Virtual Network (VNet). This is where networking gets interesting. You have two main options: Azure CNI and Kubenet. With Azure CNI, every pod gets an IP address from your VNet’s address space. This means pods are directly routable within your Azure network, but it consumes more IP addresses. With Kubenet, nodes get VNet IPs, but pods use a separate internal network with NAT translation. Kubenet is more IP-efficient but adds a layer of network address translation.

Your AKS cluster also integrates tightly with Azure Container Registry (ACR). Instead of using public Docker Hub images or managing registry credentials manually, you can attach an ACR to your AKS cluster. The cluster uses managed identities to pull images securely without storing any credentials—this is configured automatically when you use the --attach-acr flag during cluster creation.

Think of the AKS architecture like a well-designed office building. The control plane is like building management—always working in the background, handling electricity, plumbing, and HVAC. The node pools are the office floors where actual work happens. The VNet is the building’s address and network infrastructure. And ACR is like the secure supply room where you store all your materials.

Diagram Caption: AKS Architecture — Control Plane, Node Pools, Networking, and Workload Routing
Alt Text: AKS architecture diagram showing control plane, system and user node pools, VNet, ingress/load balancer, and integrations with ACR, Key Vault, and Azure Monitor


Creating and Managing AKS Clusters

Let’s get practical and create an AKS cluster. I’ll show you multiple approaches because different teams have different preferences.

The Azure Portal provides the most visual approach. Navigate to “Create a resource,” search for “Kubernetes Service,” and you’ll see a wizard-style interface. You’ll configure the basics—cluster name, region, Kubernetes version—and then move through networking, authentication, and monitoring settings. The portal is great for learning because you can see all available options, but it’s not ideal for automation or repeatability.

For production environments, I prefer the Azure CLI approach. Here’s a real example of creating a production-ready AKS cluster:

# az cli
# Create a resource group
az group create --name rg-aks-prod-eastus --location eastus

# Create the AKS cluster with system node pool
az aks create \
  --resource-group rg-aks-prod-eastus \
  --name aks-prod-cluster \
  --node-count 3 \
  --node-vm-size Standard_D4s_v3 \
  --network-plugin azure \
  --enable-managed-identity \
  --attach-acr mycompanyacr \
  --enable-addons monitoring \
  --generate-ssh-keys

# Add a user node pool for application workloads
az aks nodepool add \
  --resource-group rg-aks-prod-eastus \
  --cluster-name aks-prod-cluster \
  --name userpool \
  --node-count 5 \
  --node-vm-size Standard_D8s_v3 \
  --mode User \
  --labels workload=applications environment=production

# Verify the cluster
az aks show --resource-group rg-aks-prod-eastus --name aks-prod-cluster --output table

# Get credentials and verify nodes
az aks get-credentials --resource-group rg-aks-prod-eastus --name aks-prod-cluster
kubectl get nodes -o wide

Notice how I’m using separate node pools. The system pool uses smaller VMs (D4s_v3) with just three nodes because system pods don’t need massive resources. The user pool uses larger VMs (D8s_v3) with five nodes to handle application workloads. This gives you better resource utilization and cost control.

For infrastructure-as-code workflows, Terraform is my go-to choice. Here’s a production-grade example:

# terraform
resource "azurerm_kubernetes_cluster" "aks" {
  name                = "aks-prod-cluster"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  dns_prefix          = "aksprod"
  kubernetes_version  = "1.28.3"

  default_node_pool {
    name                = "system"
    node_count          = 3
    vm_size            = "Standard_D4s_v3"
    type               = "VirtualMachineScaleSets"
    enable_auto_scaling = true
    min_count          = 3
    max_count          = 6
    os_disk_size_gb    = 128
  }

  identity {
    type = "SystemAssigned"
  }

  network_profile {
    network_plugin    = "azure"
    load_balancer_sku = "standard"
    outbound_type     = "loadBalancer"
  }

  azure_active_directory_role_based_access_control {
    managed                = true
    azure_rbac_enabled     = true
  }
}

This Terraform configuration enables several production best practices: autoscaling on the system node pool, managed identity for secure authentication, Azure CNI networking, and Azure AD integration for RBAC.

Scaling is where AKS really shines. You have multiple scaling mechanisms available. Cluster autoscaler automatically adjusts the number of nodes in a node pool based on pod resource requests. When pods can’t be scheduled due to insufficient resources, the autoscaler adds nodes. When nodes are underutilized, it removes them.

For application-level scaling, you have the Horizontal Pod Autoscaler (HPA), which scales the number of pod replicas based on CPU, memory, or custom metrics. Then there’s KEDA (Kubernetes Event-Driven Autoscaling), which lets you scale based on external events like Azure Service Bus queue length or HTTP request rates.

Here’s a real-world scenario from my experience. We ran an e-commerce platform on AKS with three node pools: one for the frontend application, one for backend APIs, and one for batch processing jobs. During Black Friday, traffic spiked 10x. The HPA scaled our frontend pods from 10 to 100 replicas within minutes. As pod count increased, the cluster autoscaler kicked in and added 15 new nodes to handle the load. After the traffic spike subsided, everything scaled back down automatically. We didn’t touch a single configuration during the entire event.

Reflection prompt: When should you use multiple node pools in real projects? Think about workload isolation (separating system vs user workloads), different VM sizes for different performance needs (CPU-intensive vs memory-intensive applications), and cost optimization strategies (using spot VMs for fault-tolerant workloads).


AKS Networking Explained

Networking is often the most confusing part of Kubernetes, but understanding it deeply will save you countless hours of troubleshooting. Let me break down the core concepts with real-world context.

Azure CNI versus Kubenet is the first decision you’ll make. With Azure CNI (Container Network Interface), every pod gets an IP address directly from your Azure VNet subnet. This means pods are first-class citizens in your Azure network. You can apply Network Security Groups (NSGs) to pod IPs, use Azure Firewall rules, and communicate directly with other Azure resources without NAT.

The trade-off? IP address consumption. If you create a node pool with three nodes, and each node can run up to 30 pods, Azure CNI pre-allocates 90 IP addresses from your VNet (3 nodes × 30 pods). Even if you’re only running 10 pods, those IPs are reserved. For large clusters, this can exhaust your address space quickly.

Kubenet uses a different approach. Nodes get IPs from your VNet, but pods use an internal overlay network (typically 10.244.0.0/16). When pods communicate outside the cluster, their traffic goes through NAT on the node’s IP address. This is incredibly IP-efficient because you only need one IP address per node, not per pod.

The downside is that pod IPs aren’t routable from outside the cluster. If you need to apply Azure network policies at the pod level or integrate deeply with Azure networking services, Kubenet becomes limiting.

I typically recommend Azure CNI for production workloads where you need fine-grained network control and Kubenet for development or test environments where IP conservation matters more than network features.

Network policies control traffic between pods, similar to firewall rules. AKS supports two implementations: Calico and Azure Network Policy. Both work at the pod level, letting you define which pods can communicate with each other.

Here’s a practical example. Imagine you have a three-tier application: frontend pods, API pods, and database pods. You want the frontend to only talk to the API, and the API to only talk to the database. Without network policies, any pod can reach any other pod. Here’s how you’d lock this down:

# kubernetes manifest
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend-api
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

This policy says: “For pods labeled ‘backend-api’, only allow ingress traffic from pods labeled ‘frontend’ on port 8080.” It’s simple but powerful.

Load balancers in AKS come in two flavors. When you create a Kubernetes service of type LoadBalancer, AKS automatically provisions an Azure Load Balancer and assigns it a public IP. This is perfect for exposing applications to the internet. For internal applications that should only be accessible within your Azure network, you can create an internal load balancer using annotations:

# kubernetes manifest - internal load balancer
apiVersion: v1
kind: Service
metadata:
  name: internal-api
  annotations:
    service.beta.kubernetes.io/azure-load-balancer-internal: "true"
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: api

Ingress controllers provide HTTP/HTTPS routing to multiple services through a single load balancer. Instead of creating a separate LoadBalancer service for each application (which means multiple Azure Load Balancers and public IPs), you create one ingress controller and define routing rules.

NGINX Ingress Controller is the most popular choice. You can install it using Helm and then create Ingress resources to route traffic:

# kubernetes manifest - ingress routing
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: api.mycompany.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 80
  - host: admin.mycompany.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: admin-service
            port:
              number: 80

For enterprise applications that need to integrate deeply with Azure networking, Azure Application Gateway Ingress Controller (AGIC) is worth considering. AGIC integrates AKS with Azure Application Gateway, giving you Web Application Firewall (WAF) capabilities, SSL termination, and advanced routing features.

Private clusters are essential for security-conscious organizations. In a private AKS cluster, the Kubernetes API server is only accessible from within your VNet or through private endpoints. This means you can’t use kubectl from your laptop unless you’re on a VPN or have a jump box in Azure. The trade-off is significantly improved security since your cluster’s control plane isn’t exposed to the internet.

Quiz: What networking option gives pods routable VNet IPs directly?
Answer: Azure CNI assigns IP addresses from your VNet subnet directly to pods, making them routable within your Azure network without NAT.


Storage in AKS

Containers are ephemeral by nature—when a pod dies, any data stored in its filesystem disappears. That’s where persistent storage comes in. AKS integrates with Azure’s storage services to provide durable, persistent volumes for your applications.

Persistent Volumes (PVs) are cluster-wide storage resources that exist independently of pods. Persistent Volume Claims (PVCs) are requests for storage by pods. Think of PVs like physical hard drives in a server room, and PVCs like reservation tickets that applications use to claim those drives.

AKS supports two main storage types: Azure Disk and Azure Files. Understanding when to use each is crucial.

Azure Disk provides block storage backed by Azure Managed Disks. It’s perfect for databases, stateful applications, or any workload that needs high-performance block storage. The catch is that Azure Disk can only be mounted to one pod at a time. If you have a multi-replica application where multiple pods need to share data, Azure Disk won’t work.

Here’s how you’d create a PVC for Azure Disk:

# kubernetes manifest - azure disk PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
spec:
  accessModes:
  - ReadWriteOnce  # Only one pod can mount this
  storageClassName: managed-premium
  resources:
    requests:
      storage: 100Gi

Azure Files provides SMB or NFS file shares that multiple pods can mount simultaneously. This is ideal for shared configuration files, media assets, or any scenario where multiple pods need read-write access to the same data. The performance characteristics are different—Azure Files is generally slower than Azure Disk for high-IOPS workloads but much more flexible for shared access patterns.

# kubernetes manifest - azure files PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: shared-storage
spec:
  accessModes:
  - ReadWriteMany  # Multiple pods can mount this
  storageClassName: azurefile
  resources:
    requests:
      storage: 50Gi

Modern AKS clusters use CSI (Container Storage Interface) drivers for both Azure Disk and Azure Files. CSI is the industry standard for storage in Kubernetes, replacing older in-tree volume plugins. The CSI drivers are automatically installed when you create an AKS cluster, so you don’t need to worry about setup.

Backup strategy is often overlooked until something goes wrong. I’ve seen teams lose critical data because they assumed Kubernetes would magically protect their volumes. Azure Disk volumes are durable—they survive pod restarts and even node failures—but they don’t protect against accidental deletion or corruption.

For production environments, implement a backup strategy using Azure Backup for Kubernetes or Velero. These tools can snapshot your persistent volumes, back up Kubernetes resources, and restore entire applications if disaster strikes.

Quiz: Which AKS storage type is recommended for shared workloads?
Answer: Azure Files, because it supports ReadWriteMany access mode, allowing multiple pods to mount the same volume simultaneously—perfect for shared configuration, media files, or collaborative workloads.


AKS Security Best Practices

Security in Kubernetes is multi-layered, and AKS provides tools at every layer. Let me walk you through the essential security practices I implement in production clusters.

Role-Based Access Control (RBAC) is your first line of defense. RBAC controls who can do what in your cluster. The principle is simple: grant the minimum permissions necessary for each user or service to do their job.

When you integrate AKS with Azure Active Directory, your developers can use their existing corporate credentials to access the cluster. No more sharing cluster admin certificates or hardcoding credentials in CI/CD pipelines. You create Azure AD groups, assign them Kubernetes roles, and suddenly you have enterprise-grade identity management.

Here’s a real-world example. I worked with a team that had developers, DevOps engineers, and DBAs all needing different levels of cluster access. We created three Azure AD groups and bound them to different Kubernetes roles:

# kubernetes manifest - RBAC binding
# Developer role - can deploy to dev namespace
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: dev-team-binding
  namespace: development
subjects:
- kind: Group
  name: "developers@mycompany.com"  # Azure AD group
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: edit  # Built-in role that allows deploying but not cluster-wide changes
  apiGroup: rbac.authorization.k8s.io

Managed identities for pods solve the credential management problem. Instead of storing Azure service credentials (like storage account keys or SQL connection strings) in your code or Kubernetes secrets, pods can use managed identities to authenticate to Azure services.

With Azure AD Workload Identity, each pod gets its own managed identity that can access specific Azure resources. The identity is federated through OpenID Connect, which means no secrets are stored anywhere. The pod proves its identity using Kubernetes service account tokens, and Azure AD issues temporary access tokens.

Network policies restrict pod-to-pod communication. In production, I always implement a default-deny policy and then explicitly allow required traffic. This zero-trust approach means even if an attacker compromises one pod, they can’t easily pivot to other parts of your application.

Secrets management needs special attention. Kubernetes secrets are base64-encoded, not encrypted. For sensitive data like API keys, database passwords, or certificates, use Azure Key Vault with the CSI driver. The CSI driver mounts secrets from Key Vault directly into your pods as files or environment variables.

Here’s how you’d configure it:

# kubernetes manifest - Key Vault CSI integration
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: azure-keyvault-sync
spec:
  provider: azure
  parameters:
    keyvaultName: "mycompany-keyvault"
    objects: |
      array:
        - |
          objectName: database-password
          objectType: secret
    tenantId: "your-tenant-id"

Azure Policy for AKS lets you enforce organizational standards across all clusters. You can create policies that require all containers to run as non-root, prevent privileged containers, enforce resource limits, or mandate specific labels. Policies are evaluated at admission time, so non-compliant workloads are rejected before they’re deployed.

Container image scanning should happen at multiple stages. During the CI/CD pipeline, scan images for vulnerabilities before pushing them to ACR. Azure Container Registry has built-in scanning using Microsoft Defender for Cloud. Then, enable Microsoft Defender for Containers on your AKS cluster to continuously scan running workloads for vulnerabilities and runtime threats.

Reflection prompt: How do you ensure containers don’t run with privileged access in production? Implement pod security standards using Azure Policy, configure pod security admission controllers to enforce restricted policies, use security context constraints to drop unnecessary capabilities, and regularly audit running containers with tools like KubeAudit or Kube-bench to identify privilege escalation risks.


Monitoring and Observability

You can’t manage what you can’t measure. Monitoring AKS goes beyond checking if pods are running—you need deep visibility into resource usage, application performance, and cluster health.

Container Insights is Azure Monitor’s Kubernetes monitoring solution. When you enable it on your AKS cluster, it deploys an agent to every node that collects metrics and logs. You get pre-built dashboards showing node CPU and memory usage, pod counts, container restarts, and more.

The real power comes from Log Analytics, where all your container logs and metrics are stored. You can write Kusto Query Language (KQL) queries to analyze patterns, troubleshoot issues, and create custom alerts.

Here’s a practical example. One morning, users reported that our API was slow. I opened Container Insights and immediately saw that several pods in the API node pool were experiencing CPU throttling. Using this query in Log Analytics, I identified the exact pods and time ranges:

// KQL query - identify CPU throttled pods
ContainerInventory
| where Image contains "api"
| join (
    Perf
    | where ObjectName == "K8SContainer"
    | where CounterName == "cpuUsageNanoCores"
    | summarize AvgCPU = avg(CounterValue) by Computer, InstanceName
) on Computer
| where AvgCPU > 80
| project TimeGenerated, Computer, InstanceName, AvgCPU

Prometheus and Grafana are the go-to open-source monitoring stack for Kubernetes. You can deploy them on AKS to collect detailed metrics from your applications. Prometheus scrapes metrics endpoints exposed by your containers, and Grafana provides beautiful dashboards for visualization.

For microservices architectures, Dapr (Distributed Application Runtime) adds distributed tracing, metrics, and logging capabilities. When you enable Dapr on AKS, every service call generates trace spans that you can visualize in Application Insights or Jaeger, making it easy to track requests across dozens of microservices.

Alerting is where monitoring becomes proactive. I configure alerts for critical scenarios: pod crash loops, node not ready, high memory usage, persistent volume nearing capacity, and webhook failures. The key is finding the right balance—too many alerts and people ignore them; too few and you miss critical issues.

Here’s a real-world troubleshooting scenario. We had pods randomly getting killed with OOMKilled (Out Of Memory) errors. Container Insights showed that memory usage was spiking right before the kills. I used Log Analytics to correlate these spikes with application logs and discovered that a memory leak was triggered by a specific API endpoint. We fixed the code, deployed an update, and the OOMKills stopped. Without proper monitoring, we would have spent days trying to reproduce the issue manually.


Integrating AKS with DevOps Tools

AKS really shines when integrated into your CI/CD pipelines. Let me show you how to automate deployments using modern DevOps practices.

GitHub Actions with OpenID Connect (OIDC) provides secure, keyless authentication from your workflows to Azure. Instead of storing service principal credentials as GitHub secrets, you configure a federated trust between GitHub and Azure AD. Your workflow proves its identity using GitHub’s OIDC token, and Azure issues temporary credentials—no secrets stored anywhere.

Here’s a complete workflow that builds a Docker image, pushes it to ACR, and deploys to AKS:

# GitHub Actions workflow - AKS deployment with OIDC
name: Deploy to AKS
on:
  push:
    branches: [main]

permissions:
  id-token: write  # Required for OIDC authentication
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Azure Login via OIDC
        uses: azure/login@v1
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

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

      - name: Get AKS credentials
        run: |
          az aks get-credentials --resource-group rg-aks-prod \
            --name aks-prod-cluster

      - name: Deploy to AKS
        run: |
          kubectl set image deployment/myapp \
            myapp=mycompanyacr.azurecr.io/myapp:${{ github.sha }} \
            --namespace production
          kubectl rollout status deployment/myapp --namespace production

Note that with OIDC, the AZURE_CLIENT_ID, TENANT_ID, and SUBSCRIPTION_ID are not secrets—they’re just identifiers. The authentication happens through the federated trust, with no client secrets involved.

Azure DevOps Pipelines offer similar capabilities with native Azure integration. You create service connections to your AKS cluster and ACR, then define pipelines using YAML.

For infrastructure as code, the Terraform AKS module is production-ready and handles most common scenarios. Here’s a pattern I use frequently:

# terraform - AKS module
module "aks" {
  source  = "Azure/aks/azurerm"
  version = "7.0.0"

  resource_group_name = azurerm_resource_group.rg.name
  cluster_name        = "aks-prod-cluster"
  kubernetes_version  = "1.28.3"

  agents_size         = "Standard_D4s_v3"
  agents_count        = 3

  network_plugin      = "azure"
  vnet_subnet_id      = azurerm_subnet.aks.id

  rbac_aad_managed    = true
  rbac_aad_admin_group_object_ids = [
    azuread_group.aks_admins.object_id
  ]
}

Helm charts simplify Kubernetes deployments by packaging related resources together. Instead of managing dozens of YAML files, you create a Helm chart that parameterizes common values. Then deploying to different environments becomes as simple as changing values.

Here’s a production Helm deployment with environment-specific configuration:

# helm deployment command
helm upgrade --install myapp ./charts/myapp \
  --namespace production \
  --values values-prod.yaml \
  --set image.tag=$VERSION

And here’s what a values-prod.yaml file might look like:

# values-prod.yaml - production Helm values
replicaCount: 5

image:
  repository: mycompanyacr.azurecr.io/myapp
  pullPolicy: IfNotPresent
  tag: "latest"

resources:
  limits:
    cpu: 1000m
    memory: 2Gi
  requests:
    cpu: 500m
    memory: 1Gi

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

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

This automated approach lets you deploy confidently multiple times per day. I’ve seen teams go from monthly manual deployments to 50+ automated deployments per day using AKS with GitHub Actions and Helm.


Advanced AKS Features

Once you’ve mastered the basics, AKS offers advanced features that solve specific production challenges.

Virtual Nodes integrate AKS with Azure Container Instances (ACI), providing serverless Kubernetes. When you enable virtual nodes, you get an infinite node pool that can scale in seconds without VM provisioning time. Virtual nodes are perfect for burst workloads—batch jobs, scheduled tasks, or sudden traffic spikes.

AKS Fleet Manager helps organizations manage multiple AKS clusters across regions or environments. Instead of configuring each cluster individually, Fleet Manager lets you apply updates, policies, and configurations across an entire fleet. For global applications that run in ten different Azure regions, Fleet Manager is a game-changer.

Multi-region architectures provide high availability and disaster recovery. I’ve designed systems where user traffic routes to the nearest AKS cluster using Azure Front Door or Traffic Manager. Each cluster runs independently, but they share data through globally replicated databases and storage accounts. If an entire Azure region goes down, traffic automatically fails over to healthy regions.

Pod Identity and Workload Identity provide fine-grained access control to Azure resources. Each pod can have its own managed identity with specific permissions. Your frontend pods might have read-only access to Azure Storage, while your backend pods have read-write access to Azure SQL Database. No credentials are ever stored in code or configuration.

Spot node pools offer significant cost savings for fault-tolerant workloads. Spot VMs can be evicted with 30 seconds notice when Azure needs the capacity, but they cost 70-90% less than regular VMs. For batch processing, CI/CD agents, or development environments, spot nodes are perfect.

Windows workloads run alongside Linux workloads in the same AKS cluster. You create Windows node pools, and the scheduler automatically places Windows containers on Windows nodes. This is valuable for organizations migrating .NET Framework applications to Kubernetes without rewriting them in .NET Core.

ACR private cluster integration ensures your container images never traverse the public internet. When you enable private endpoint on ACR and configure your AKS cluster to use it, all image pulls happen over Azure’s backbone network. Combined with private AKS clusters, you achieve complete network isolation.

Zero-trust networking implements microsegmentation where every connection requires authentication and authorization. Using service mesh technologies like Istio or Linkerd on AKS, you can encrypt all pod-to-pod traffic, implement mutual TLS, and apply fine-grained access policies at the application layer.

Reflection prompt: How would you design rolling upgrades for stateful workloads? Consider using StatefulSets with ordered updates, implementing pre-stop hooks to drain connections gracefully, using PodDisruptionBudgets to prevent too many replicas going down simultaneously, and testing rollback procedures in lower environments before production deployments.


Common AKS Mistakes to Avoid

Let me share the mistakes I’ve seen teams make—and made myself—so you can avoid them.

Not using node pools correctly is the most common mistake. Running application workloads on the system node pool can destabilize your entire cluster. During traffic spikes, application pods consume resources needed by system components, and suddenly CoreDNS or kube-proxy starts failing. Always separate system and user workloads into different node pools.

Running everything in the default namespace creates an organizational nightmare. Without proper namespaces, you can’t apply different RBAC policies, resource quotas, or network policies to different applications. Use namespaces to isolate teams, environments, or applications.

Exposing applications with public IPs unintentionally is a security risk. When you create a LoadBalancer service without annotations, AKS assigns it a public IP by default. Always be explicit about whether services should be internal or external using the service.beta.kubernetes.io/azure-load-balancer-internal: "true" annotation.

Storing secrets in ConfigMaps is dangerous. ConfigMaps are designed for non-sensitive configuration data. Secrets should go in Kubernetes Secrets at minimum, or better yet, Azure Key Vault with the CSI driver.

Using unmanaged ACR pull authentication creates credential management headaches. Instead of creating service principals and storing credentials as Kubernetes secrets, use AKS’s built-in ACR integration with managed identities via the --attach-acr flag.

Over-allocating expensive VM SKUs wastes money. I’ve seen clusters running on D16s_v3 nodes (16 vCPUs, 64 GB RAM) for applications that barely used 2 vCPUs and 4 GB RAM. Right-size your node pools based on actual resource usage measured through Container Insights, not guesses.

Not implementing resource requests and limits leads to resource contention. Without requests, the scheduler can’t make informed placement decisions. Without limits, one misbehaving pod can starve others of resources. Always define both in your deployment manifests.

Reflection prompt: Where in your app lifecycle would you add spot node pools versus guaranteed nodes? Use spot node pools for stateless, fault-tolerant workloads like batch processing, CI/CD build agents, development environments, and data processing pipelines where 30-second eviction notices are acceptable. Reserve guaranteed (regular) node pools for production user-facing applications, databases, stateful workloads, and any service requiring high availability and predictable performance.


Troubleshooting Common AKS Issues

Even well-designed clusters encounter issues. Here are the most common problems I’ve troubleshot and the commands to diagnose them quickly.

Pod CrashLoopBackOff means your pod is starting, crashing, and Kubernetes keeps restarting it. This usually indicates application errors, missing dependencies, or misconfigured liveness probes.

# Diagnose CrashLoopBackOff
kubectl describe pod <pod-name> -n <namespace>
kubectl logs <pod-name> -n <namespace> --previous  # View logs from crashed container
kubectl get events -n <namespace> --sort-by='.lastTimestamp'

# Check for resource limits causing OOMKilled
kubectl top pod <pod-name> -n <namespace>

DNS failures manifest as pods unable to resolve service names or external domains. This often happens when CoreDNS pods are unhealthy or overwhelmed.

# Check CoreDNS health
kubectl get pods -n kube-system | grep coredns
kubectl logs -n kube-system -l k8s-app=kube-dns

# Test DNS resolution from a pod
kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup kubernetes.default

# Check CoreDNS ConfigMap for misconfigurations
kubectl get configmap coredns -n kube-system -o yaml

NodeNotReady status means a node can’t communicate with the control plane or is experiencing resource exhaustion.

# Diagnose NodeNotReady
kubectl describe node <node-name>
kubectl get nodes -o wide

# Check node conditions
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.conditions[?(@.type=="Ready")].status}{"\n"}{end}'

# View kubelet logs (requires SSH access to node or Azure diagnostic logs)
az vm run-command invoke --resource-group <rg-name> --name <vm-name> --command-id RunShellScript --scripts "journalctl -u kubelet -n 100"

ImagePullBackOff means Kubernetes can’t pull your container image from the registry. Common causes include incorrect image names, missing ACR integration, or network issues.

# Diagnose ImagePullBackOff
kubectl describe pod <pod-name> -n <namespace>

# Check ACR integration
az aks check-acr --resource-group <rg-name> --name <cluster-name> --acr <acr-name>

# Verify image exists
az acr repository show-tags --name <acr-name> --repository <image-name>

PersistentVolumeClaim stuck in Pending means storage provisioning failed. Check for quota limits, incorrect storage class, or node affinity issues.

# Diagnose pending PVC
kubectl describe pvc <pvc-name> -n <namespace>
kubectl get events -n <namespace> | grep <pvc-name>

# List available storage classes
kubectl get storageclass

# Check node affinity and zone constraints
kubectl get pv -o yaml | grep -A 5 nodeAffinity

High CPU throttling occurs when containers hit their CPU limits frequently, causing performance degradation.

# Identify throttled containers
kubectl top pods -n <namespace>

# View detailed metrics in Container Insights KQL query
ContainerInventory
| where Namespace == "production"
| join (Perf | where CounterName == "cpuLimitNanoCores") on Computer, Name
| where CounterValue > 0.8 * ThresholdValue

AKS Pricing and Cost Optimization

Understanding AKS pricing helps you make cost-effective architectural decisions.

The control plane is free—Microsoft manages the Kubernetes master nodes, API server, and etcd at no charge. This is documented in the AKS pricing page. You never pay for the master node VMs. This is a huge advantage over self-managed Kubernetes where you pay for master node VMs.

You pay for the node pools, which are just Azure Virtual Machines. A Standard_D4s_v3 VM costs roughly $0.23 per hour in East US, so a three-node cluster costs about $500 per month just for compute. Add Azure Disk storage, load balancers, and egress bandwidth, and costs add up quickly.

Before-and-After Cost Example: I audited a client’s cluster that had five Standard_D8s_v3 nodes (8 vCPUs, 32 GB RAM each) running at just 15% CPU and 20% memory utilization. By switching to Standard_D4s_v3 nodes and enabling cluster autoscaling, we reduced the monthly compute cost from $850 to $340—a 60% reduction—without impacting performance. The cluster now scales from 3 to 6 nodes based on actual demand.

Autoscaling strategies directly impact costs. The cluster autoscaler helps by removing unused nodes, but you need to configure it correctly. Set appropriate scale-down thresholds and cool-down periods. I typically configure scale-down to wait 10 minutes before removing underutilized nodes, preventing thrashing.

Spot VMs provide the biggest cost savings. For non-production environments or fault-tolerant workloads, spot node pools can reduce compute costs by 70-90%. Enable cluster autoscaler on spot node pools, and they’ll scale up during the day when developers are active and scale down at night.

Using ACR for efficient image pulls reduces data transfer costs. When ACR and AKS are in the same region, image pulls are free and fast. Pulling images from Docker Hub or other external registries incurs egress bandwidth charges and is slower.

Use Azure Cost Management to track AKS spending. Tag your resources consistently—tag node pools by application, environment, or cost center—and you can generate detailed reports showing which teams or applications are driving costs.


Before You Go to Production: AKS Checklist

Before deploying your AKS cluster to production, verify you’ve implemented these critical configurations. This checklist is drawn from production incidents I’ve troubleshot and best practices I’ve learned over years of running production Kubernetes workloads.

✓ RBAC and Authentication

  • Azure AD integration enabled for cluster access
  • Role bindings configured for teams with least-privilege access
  • Service accounts for workloads follow principle of least privilege
  • Break-glass admin access documented and tested

✓ Network Policies

  • Default-deny network policy implemented
  • Explicit allow rules for required pod-to-pod communication
  • Ingress controller properly configured with TLS termination
  • Internal vs external services clearly separated

✓ Resource Management

  • Resource requests defined for all pods (CPU and memory)
  • Resource limits configured to prevent resource exhaustion
  • LimitRanges and ResourceQuotas applied to namespaces
  • PodDisruptionBudgets set for critical workloads

✓ Health Probes

  • Liveness probes configured for all application pods
  • Readiness probes prevent traffic to unhealthy pods
  • Startup probes for slow-starting applications
  • Probe timeouts appropriate for application characteristics

✓ Secrets Management

  • Azure Key Vault CSI driver installed and configured
  • No secrets stored in ConfigMaps or plain Kubernetes Secrets
  • Workload Identity or Pod Identity implemented for Azure resource access
  • Secrets rotation strategy documented

✓ Monitoring and Alerting

  • Container Insights enabled with Log Analytics workspace
  • Critical alerts configured (node health, pod crashes, resource exhaustion)
  • Application Insights or Prometheus integrated for application metrics
  • Dashboards created for key metrics

✓ Backup and Disaster Recovery

  • Persistent volume backup strategy implemented
  • Cluster configuration backed up (using Velero or Azure Backup)
  • Disaster recovery runbook documented and tested
  • RTO and RPO requirements met

✓ Security Hardening

  • Microsoft Defender for Containers enabled
  • Container images scanned for vulnerabilities
  • Azure Policy for AKS enforcing organizational standards
  • Pod Security Standards enforced
  • Private cluster or network restrictions on API server

✓ Cost Controls

  • Resource tagging strategy implemented consistently
  • Cluster autoscaler configured appropriately
  • Cost monitoring alerts set for budget thresholds
  • Spot node pools used for appropriate workloads

✓ Emergency Access

  • Break-glass procedure documented for cluster access failures
  • Jump box or VPN access configured for private clusters
  • On-call runbooks available for common incidents
  • Post-incident review process established

Frequently Asked Questions

What is Azure Kubernetes Service (AKS)?

Azure Kubernetes Service is Microsoft’s managed Kubernetes offering that automates deployment, scaling, and management of containerized applications. AKS handles control plane management, patching, and upgrades, letting you focus on your applications rather than cluster infrastructure. The control plane components run in a Microsoft-managed subscription at no additional cost.

How does AKS work in Azure?

AKS provisions and manages the Kubernetes control plane (API server, scheduler, controller manager, and etcd) in a Microsoft-managed Azure subscription. You create node pools that run as Virtual Machine Scale Sets in your subscription. Pods run on these nodes, while the control plane components remain fully managed by Microsoft with automatic updates and high availability.

What is the difference between Azure CNI and Kubenet?

Azure CNI assigns VNet IP addresses directly to pods from your subnet, making them routable within your Azure network but consuming more IPs (one per pod). Kubenet gives VNet IPs only to nodes and uses a private overlay network (10.244.0.0/16) for pods with NAT, which is more IP-efficient but adds network complexity. Azure CNI is recommended for production workloads requiring fine-grained network control; Kubenet is better for development environments with IP constraints.

How do you secure AKS clusters?

Secure AKS clusters using Azure AD integration for RBAC and identity management, managed identities (Workload Identity) for Azure resource access, network policies for pod-to-pod traffic segmentation, Azure Key Vault CSI driver for secrets management, and Microsoft Defender for Containers for image scanning and runtime threat detection. Implement pod security standards using Azure Policy to prevent privileged containers and enforce security baselines.

Does AKS support autoscaling?

Yes, AKS supports multiple autoscaling mechanisms. Cluster autoscaler adjusts the number of nodes in node pools based on pod resource requests and scheduling constraints. Horizontal Pod Autoscaler (HPA) scales the number of pod replicas based on CPU, memory, or custom metrics. KEDA (Kubernetes Event-Driven Autoscaling) scales workloads based on external events like Azure Service Bus queue length, HTTP traffic, or database connections.

How much does AKS cost?

The AKS control plane is free—you pay nothing for the Kubernetes master nodes or management layer. Costs include node pool VMs (e.g., Standard_D4s_v3 at ~$0.23/hour), Azure Disk storage for persistent volumes, Azure Load Balancers for exposed services, and network egress charges. A typical production cluster with three Standard_D4s_v3 nodes costs approximately $500-600 per month for compute, plus additional costs for storage, load balancers, and bandwidth. Use Azure Cost Management to track actual spending.


Wrapping Up: Your AKS Journey Starts Here

Kubernetes is powerful—but AKS makes it practical for production. A well-designed AKS cluster becomes the backbone of modern microservices, handling deployments, scaling, and operations that would take a team of engineers to manage manually.

Throughout this guide, we’ve covered everything from basic architecture to advanced security patterns. You’ve learned how to create clusters, configure networking, implement security best practices, integrate with DevOps tools, and optimize costs. These aren’t just theoretical concepts—they’re patterns I’ve used in production to run applications serving millions of users.

The key to mastering AKS is hands-on practice. Start small: deploy a simple application, expose it with a load balancer, add monitoring, implement RBAC. Then gradually add complexity—multiple node pools, network policies, private clusters, multi-region deployments. Each layer builds on the previous one.

Remember that AKS is constantly evolving. New features like Fleet Manager, improved networking options, and enhanced security capabilities are added regularly. Stay current by following the AKS release notes and experimenting with new features in test environments before rolling them to production.

Your next step? Get hands-on experience. Deploy your first AKS cluster, make mistakes, troubleshoot them, and learn from the process. That’s how you transform theoretical knowledge into practical expertise.

👉 Ready to dive deeper? Take the Free AKS Fundamentals Course at thedevopstooling.com and learn to deploy, scale, and secure Kubernetes clusters on Azure with hands-on labs and real-world scenarios.


Written by Srikanth Ch, Senior DevOps Engineer | thedevopstooling.com

Similar Posts

Leave a Reply