2. Core Concepts
Every noun you'll meet in the portal, the CLI, and a Terraform plan — with an analogy first and the precise definition underneath. AKS has an unusually large vocabulary because it stacks two of them: Kubernetes' own object model and Azure's resource model. Keeping them separate is most of the battle.
The two vocabularies
Say this out loud once and the rest of the page gets easier:
- Azure nouns are ARM resources with resource IDs, governed by Azure RBAC and Azure Policy: the cluster, node pools, scale sets, disks, load balancers, identities.
- Kubernetes nouns are API objects inside the cluster, governed by Kubernetes RBAC and admission control: pods, deployments, services, secrets, namespaces.
Some things exist in both worlds and are the source of most confusion — a Kubernetes Service of
type LoadBalancer is an Azure load-balancer rule; a PersistentVolumeClaim is an Azure
managed disk. AKS is the translator.
The Azure side
Managed cluster
Analogy: the membership, not the building. The managedClusters resource is your contract with
Azure's control plane, plus the settings that shape everything underneath.
Technically: an ARM resource of type Microsoft.ContainerService/managedClusters, regional,
living in a resource group you choose. Its ID shape:
/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.ContainerService/managedClusters/{name}
It carries the Kubernetes version, the tier, the identity configuration, the networking profile, the add-on profile, and at least one node pool. Scoping, inheritance, and what a delete actually deletes follow the standard rules — see the scope hierarchy.
Node resource group (MC_*)
Analogy: the back-of-house area. It's in your building, but the operator holds the keys.
Technically: a second resource group AKS creates and owns in your subscription, named
MC_<cluster-rg>_<cluster-name>_<region> by default (you can override the name at create time, but
never afterwards). It holds the node Virtual Machine Scale Sets, the node OS and data disks, the
cluster's Azure Load Balancer, its outbound public IP(s), the network security group applied to
the node subnet when AKS manages one, and any AKS-managed identities.
Rules that save you a bad afternoon:
- Don't apply a
CanNotDeleteorReadOnlylock to it. Scaling, upgrades, and reconciliation will fail in ways whose error messages don't mention the lock. - Don't hand-edit resources in it. The cluster reconciles; your change will be reverted, or worse, half-reverted.
- Do expect Azure Policy assigned at subscription or management-group scope to apply here. A well-meaning "deny public IPs" policy will stop your cluster provisioning its load balancer.
- Deleting the cluster deletes this group. Deleting this group does not cleanly delete the cluster; it breaks it.
Node pool (agent pool)
Analogy: a shift of identical workers. Same VM size, same OS, same settings, scaled as a unit.
Technically: Microsoft.ContainerService/managedClusters/agentPools, implemented as a Virtual
Machine Scale Set in the node resource group. Every pool has a mode:
- System node pool — must exist, must run Linux, hosts critical system pods (CoreDNS, the metrics server, konnectivity). At least one is mandatory and it cannot scale to zero. Best practice is to taint it so application workloads land elsewhere.
- User node pool — everything else. Can be Linux or Windows, can scale to zero, can be Spot.
Per-pool settings that actually matter: VM size, OS SKU (Ubuntu, Azure Linux — formerly CBL-Mariner — or Windows Server), availability zones, max pods per node, node taints and labels, OS disk type and size (including ephemeral OS disks, which are faster and free but lost on deallocation), autoscaler min/max, and Spot with its eviction policy.
The replacement trap. Several node pool properties cannot be changed in place. Changing the VM size, the OS disk type, or the availability zones of a pool means creating a new pool and draining the old one. In Terraform, changing these on the
default_node_poolhistorically forced replacement of the entire cluster — thetemporary_name_for_rotationargument exists specifically to let the provider rotate the pool instead. Read the plan output; don't skim it.
Tier (sku_tier) and cluster mode
| Tier | What it unlocks | The trap |
|---|---|---|
| Free | Control plane at no charge, best-effort SLO, lower supported cluster size | No financially-backed SLA. Fine for dev; indefensible for prod. Also the tier people forget to change |
| Standard | Uptime SLA on the API server, larger supported node counts, better control-plane scaling | Costs roughly $0.10/cluster/hour ⚠️ verify current pricing — trivial next to the node bill |
| Premium | Everything in Standard plus Long-Term Support (LTS) — roughly two years on a designated Kubernetes version | Roughly $0.60/cluster/hour ⚠️ verify. LTS is a deferral of upgrade work, not a cancellation of it |
Separately from tier, a cluster is created in one of two modes: Standard, where you configure everything, or Automatic, where Azure preconfigures node autoprovisioning, monitoring, Entra integration, Azure RBAC, network policy, and other production defaults, and pins the Standard tier. Automatic is the right default for a team that wants a sane cluster rather than a bespoke one; it deliberately removes knobs.
Identities — there are three, and they do different jobs
| Identity | Analogy | What it's for |
|---|---|---|
| Cluster identity (control plane) | The building manager's badge | Lets the cluster's Azure controllers create load balancers, attach disks, and read the VNet. System-assigned or user-assigned. Needs Network Contributor on a pre-existing VNet if you bring your own |
| Kubelet identity | The delivery driver's badge | The identity the nodes use to pull images — this is the one that gets AcrPull on your container registry |
| Workload identity (per-pod) | The individual chef's badge | Microsoft Entra Workload ID: a Kubernetes service account federated to a user-assigned managed identity, so a pod — not the node — authenticates to Key Vault, Storage, or Cosmos DB with no secret. The successor to the retired AAD Pod Identity |
Conflating the kubelet identity with workload identity is the classic AKS security mistake: granting the kubelet identity access to your Key Vault gives every pod on every node that access.
[Image Prompt: 2D minimalistic labelled diagram of the AKS resource hierarchy showing a managed cluster resource in a customer resource group, an AKS-owned node resource group containing scale sets, disks, and a load balancer, and node pools containing nodes containing pods, flat design, clean vector art style, white background]
Networking — the choice you cannot easily undo
The network plugin is chosen at cluster creation and is, for practical purposes, permanent. Get it wrong and the fix is a new cluster.
| Model | How pods get IPs | When it's right | The cost |
|---|---|---|---|
| kubenet (legacy) | Pods get IPs from an overlay; nodes get VNet IPs; user-defined routes carry pod traffic | Nothing new. Retiring 31 March 2028 | Route table limits cap cluster size; no Windows support in some configurations; no direct pod addressability |
| Azure CNI (traditional / "node subnet") | Every pod gets a real VNet IP from the node subnet | You need pods directly addressable from the VNet or on-prem | Enormous IP consumption — you must pre-allocate max_pods × node_count addresses. This is how teams run out of a /16 |
| Azure CNI Overlay | Pods get IPs from a private overlay CIDR; nodes get VNet IPs; traffic is NAT'd at the node | The default recommendation now. Large clusters, constrained address space | Pods aren't directly routable from outside the cluster without extra work |
| Azure CNI Powered by Cilium | Overlay or node-subnet IPAM, with eBPF dataplane | You want high-performance networking and Cilium network policy | Some feature interactions and policy semantics differ; check compatibility before committing |
| Bring your own CNI | Whatever you install | You have a hard requirement AKS's options don't meet | You own it. AKS support boundaries shrink |
Alongside the plugin sit three more choices:
- Network policy —
azure,calico, or Cilium. Off by default. Without it, every pod can talk to every other pod in the cluster, across namespaces. Turning it on later is possible for some combinations and not others. - Outbound type —
loadBalancer(default; SNAT through the cluster's public LB),managedNATGatewayoruserAssignedNATGateway(far better SNAT port scaling), oruserDefinedRouting(you force egress through a firewall). SNAT port exhaustion under high outbound connection churn is a classic AKS incident, and NAT Gateway is the usual fix. - API server access — public with authorized IP ranges, a private cluster (API server reachable only via Private Link), or API Server VNet Integration. Private clusters solve an exposure problem and create a DNS-and-build-agent problem; decide deliberately.
Service CIDR, DNS service IP, pod CIDR
Three address ranges that must not overlap with each other, with your VNet, or with anything reachable on-prem: the service CIDR (virtual IPs for Kubernetes Services), the DNS service IP (which must sit inside the service CIDR), and the pod CIDR (overlay models only). Overlapping these with a peered network is a mistake you discover months later when a route mysteriously blackholes.
The Kubernetes side — the objects you'll actually type
Defined precisely but briefly; this is Kubernetes, not Azure, and it's portable knowledge.
| Term | Analogy | Technical definition |
|---|---|---|
| Pod | One meal plated together | The smallest schedulable unit: one or more containers sharing a network namespace, IP, and volumes. Ephemeral by design — never manage pods directly |
| ReplicaSet | The order for N identical plates | Maintains a specified number of pod replicas. You rarely create one yourself |
| Deployment | The standing order, with a rollout procedure | Manages ReplicaSets to provide declarative rolling updates and rollbacks for stateless workloads |
| StatefulSet | Numbered stations, each with its own equipment | Pods with stable identities (app-0, app-1) and stable per-pod storage. For databases and anything with identity |
| DaemonSet | One inspector per kitchen | One pod per node (or per matching node). How log collectors, CSI drivers, and monitoring agents run |
| Job / CronJob | A one-off task / a recurring task | Run-to-completion workloads, optionally on a schedule |
| Service | A stable address for a moving target | A stable virtual IP and DNS name front-ending a set of pods. ClusterIP (internal), NodePort, or LoadBalancer — which provisions a real Azure load-balancer rule and public IP |
| Ingress / Gateway API | The front door and its signage | HTTP(S) routing into the cluster. On AKS: the App Routing add-on (managed NGINX), Application Gateway for Containers, or your own controller |
| Namespace | A floor of the building | A soft isolation and naming boundary. Not a security boundary on its own — pair it with network policy, RBAC, and resource quotas |
| ConfigMap / Secret | The recipe card / the safe | Non-secret and "secret" key-value config. A Kubernetes Secret is base64-encoded, not encrypted, to anyone with API read access — which is why Key Vault + the Secrets Store CSI driver exists |
| Requests / limits | The reservation / the hard ceiling | A request is what the scheduler guarantees and bin-packs against; a limit is what the kernel enforces. Getting these wrong is the #1 cause of both waste and OOMKills |
| PodDisruptionBudget | "Never fewer than two chefs on shift" | Constrains how many pods a voluntary disruption (node drain, upgrade) may remove at once. Without one, an upgrade can take your whole service down; with an impossible one, an upgrade hangs forever |
| HorizontalPodAutoscaler | Add plates when it's busy | Scales pod replicas on CPU, memory, or custom metrics |
| PersistentVolumeClaim | "I need a locker this big" | A request for storage that a CSI driver satisfies — on AKS, with an Azure managed disk, Azure Files share, or Blob mount |
| StorageClass | The locker catalogue | Defines which CSI driver, which SKU, and — critically — the reclaim policy that decides whether the underlying Azure disk is deleted with the PVC |
Autoscaling — three different things with similar names
People say "autoscaling" and mean one of three loops that operate on different objects:
- Horizontal Pod Autoscaler (HPA) — more pods, based on metrics.
- Cluster Autoscaler — more nodes in an existing node pool, when pods are unschedulable, and fewer when nodes are underused. Bounded by the pool's min/max.
- Node Autoprovisioning (NAP) — creates node pools of the right shape on demand, based on Karpenter. It removes the need to guess VM sizes up front and is a default in AKS Automatic. Check current availability and preview status before relying on it ⚠️ verify against current Azure docs.
Plus KEDA, the event-driven autoscaler add-on, which drives the HPA from queue depth, topic lag, or a hundred other scalers — and is what makes "scale on Service Bus queue length" a two-line manifest. Vertical Pod Autoscaler (VPA) adjusts requests and limits rather than counts.
Add-ons vs. extensions vs. "just install it"
Three delivery mechanisms, and the distinction affects who patches what:
- Managed add-ons — first-party components AKS installs and upgrades for you: Azure Monitor
(Container Insights and managed Prometheus), Azure Policy, the Key Vault Secrets Store CSI driver,
App Routing, KEDA, the Istio-based service mesh, Workload Identity, Image Cleaner, virtual nodes.
Enabled per-cluster via
az aks enable-addonsor the equivalent Terraform block. Prefer these: you don't own their lifecycle. - Cluster extensions (via Azure Arc's extension model) — Flux GitOps, Dapr, and others, managed as ARM resources on the cluster.
- Anything you
helm installyourself — yours entirely, including its CRDs, its upgrades, and its compatibility with the next Kubernetes version. This is where upgrade pain accumulates.
Terms you'll see and should not confuse
- Azure Container Registry — where images live. Not part of the cluster, but the kubelet
identity needs
AcrPullon it or every pull fails with an unhelpfulImagePullBackOff. - Virtual nodes — a virtual-kubelet integration that schedules pods onto ACI instead of a real node. Burst capacity with real limitations (no DaemonSets, restricted networking).
- Azure Container Storage — a newer managed storage offering for AKS, distinct from the classic disk/file/blob CSI drivers. Check its current GA status and limits before designing on it.
- Fleet Manager — multi-cluster orchestration: propagate configuration and coordinate upgrades across many AKS clusters.
- Draft /
az aks kubectl— developer conveniences, not architecture.
Next: Architecture →
← Back to the Azure Kubernetes Service overview · ← Previous: What & Why