|
| 1 | +--- |
| 2 | +title: "Azure Kubernetes Service (AKS)" |
| 3 | +description: Get started with Azure Kubernetes Service on LocalStack |
| 4 | +template: doc |
| 5 | +resourceProvider: Microsoft.ContainerService |
| 6 | +resourceType: Microsoft.ContainerService/managedClusters |
| 7 | +--- |
| 8 | + |
| 9 | +import AzureFeatureCoverage from "../../../../components/feature-coverage/AzureFeatureCoverage"; |
| 10 | + |
| 11 | +## Introduction |
| 12 | + |
| 13 | +Azure Kubernetes Service (AKS) is Azure's managed Kubernetes offering. Azure operates the control |
| 14 | +plane while you manage node pools of worker machines that run your workloads. For more information, |
| 15 | +see [What is Azure Kubernetes Service?](https://learn.microsoft.com/en-us/azure/aks/what-is-aks). |
| 16 | + |
| 17 | +LocalStack for Azure creates real, working Kubernetes clusters on your machine. `az aks create` |
| 18 | +produces a cluster backed by [k3d](https://k3d.io/) that you can reach with `kubectl`, so manifests, |
| 19 | +Helm charts, and operators behave as they would against a cluster in the cloud. The supported APIs |
| 20 | +are available on our [API Coverage section](#api-coverage), which provides information on the extent |
| 21 | +of AKS's integration with LocalStack. |
| 22 | + |
| 23 | +## Getting started |
| 24 | + |
| 25 | +This guide is designed for users new to AKS and assumes basic knowledge of the Azure CLI, `kubectl`, |
| 26 | +and our `lstk az` proxy. |
| 27 | + |
| 28 | +Launch LocalStack using your preferred method. For more information, see |
| 29 | +[Introduction to LocalStack for Azure](/azure/getting-started/). Once the container is running, |
| 30 | +enable Azure CLI interception by running: |
| 31 | + |
| 32 | +```bash |
| 33 | +lstk az start-interception |
| 34 | +``` |
| 35 | + |
| 36 | +This command points the `az` CLI away from the public Azure management REST API and toward the |
| 37 | +LocalStack for Azure emulator API. To revert this configuration, run: |
| 38 | + |
| 39 | +```bash |
| 40 | +lstk az stop-interception |
| 41 | +``` |
| 42 | + |
| 43 | +This reconfigures the `az` CLI to send commands to the official Azure management REST API. |
| 44 | + |
| 45 | +### Create a resource group |
| 46 | + |
| 47 | +Create a resource group to hold the cluster: |
| 48 | + |
| 49 | +```bash |
| 50 | +az group create \ |
| 51 | + --name rg-aks-demo \ |
| 52 | + --location westeurope |
| 53 | +``` |
| 54 | + |
| 55 | +```bash title="Output" |
| 56 | +{ |
| 57 | + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-aks-demo", |
| 58 | + "location": "westeurope", |
| 59 | + "managedBy": null, |
| 60 | + "name": "rg-aks-demo", |
| 61 | + "properties": { |
| 62 | + "provisioningState": "Succeeded" |
| 63 | + }, |
| 64 | + "tags": null, |
| 65 | + "type": "Microsoft.Resources/resourceGroups" |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +### Create a cluster |
| 70 | + |
| 71 | +Create a cluster with a single node in its system node pool: |
| 72 | + |
| 73 | +```bash |
| 74 | +az aks create \ |
| 75 | + --resource-group rg-aks-demo \ |
| 76 | + --name aks-demo \ |
| 77 | + --node-count 1 \ |
| 78 | + --generate-ssh-keys |
| 79 | +``` |
| 80 | + |
| 81 | +The command returns when the cluster is ready to use. Locally that takes a couple of minutes: the |
| 82 | +emulator provisions a k3d cluster, so what you get back is a live API server, not a mock. |
| 83 | + |
| 84 | +```bash title="Output" |
| 85 | +{ |
| 86 | + "currentKubernetesVersion": "1.34.4", |
| 87 | + "fqdn": "aks-demo-rg-aks-demo-000000-oq7mpqgx.hcp.westeurope.azmk8s.io", |
| 88 | + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/rg-aks-demo/providers/Microsoft.ContainerService/managedClusters/aks-demo", |
| 89 | + "kubernetesVersion": "1.34", |
| 90 | + "location": "westeurope", |
| 91 | + "name": "aks-demo", |
| 92 | + "nodeResourceGroup": "MC_rg-aks-demo_aks-demo_westeurope", |
| 93 | + "powerState": { |
| 94 | + "code": "Running" |
| 95 | + }, |
| 96 | + "provisioningState": "Succeeded", |
| 97 | + ... |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +### Show and list clusters |
| 102 | + |
| 103 | +Retrieve the details of a single cluster: |
| 104 | + |
| 105 | +```bash |
| 106 | +az aks show \ |
| 107 | + --resource-group rg-aks-demo \ |
| 108 | + --name aks-demo |
| 109 | +``` |
| 110 | + |
| 111 | +```bash title="Output" |
| 112 | +{ |
| 113 | + "currentKubernetesVersion": "1.34.4", |
| 114 | + "dnsPrefix": "aks-demo-rg-aks-demo-000000", |
| 115 | + "kubernetesVersion": "1.34", |
| 116 | + "location": "westeurope", |
| 117 | + "name": "aks-demo", |
| 118 | + "nodeResourceGroup": "MC_rg-aks-demo_aks-demo_westeurope", |
| 119 | + "provisioningState": "Succeeded", |
| 120 | + ... |
| 121 | +} |
| 122 | +``` |
| 123 | + |
| 124 | +List the clusters in a resource group: |
| 125 | + |
| 126 | +```bash |
| 127 | +az aks list \ |
| 128 | + --resource-group rg-aks-demo \ |
| 129 | + --output table |
| 130 | +``` |
| 131 | + |
| 132 | +```bash title="Output" |
| 133 | +Name Location ResourceGroup KubernetesVersion CurrentKubernetesVersion ProvisioningState Fqdn |
| 134 | +-------- ---------- --------------- ------------------- -------------------------- ------------------- ------------------------------------------------------------- |
| 135 | +aks-demo westeurope rg-aks-demo 1.34 1.34.4 Succeeded aks-demo-rg-aks-demo-000000-oq7mpqgx.hcp.westeurope.azmk8s.io |
| 136 | +``` |
| 137 | + |
| 138 | +### Update a cluster |
| 139 | + |
| 140 | +`az aks update` changes the properties of an existing cluster. The following example sets resource |
| 141 | +tags: |
| 142 | + |
| 143 | +```bash |
| 144 | +az aks update \ |
| 145 | + --resource-group rg-aks-demo \ |
| 146 | + --name aks-demo \ |
| 147 | + --tags environment=local team=platform |
| 148 | +``` |
| 149 | + |
| 150 | +```bash title="Output" |
| 151 | +{ |
| 152 | + "name": "aks-demo", |
| 153 | + "provisioningState": "Succeeded", |
| 154 | + "tags": { |
| 155 | + "environment": "local", |
| 156 | + "team": "platform" |
| 157 | + }, |
| 158 | + ... |
| 159 | +} |
| 160 | +``` |
| 161 | + |
| 162 | +### Connect with kubectl |
| 163 | + |
| 164 | +Merge the cluster credentials into your local kubeconfig: |
| 165 | + |
| 166 | +```bash |
| 167 | +az aks get-credentials \ |
| 168 | + --resource-group rg-aks-demo \ |
| 169 | + --name aks-demo \ |
| 170 | + --overwrite-existing |
| 171 | +``` |
| 172 | + |
| 173 | +```bash title="Output" |
| 174 | +Merged "aks-demo" as current context in /home/user/.kube/config |
| 175 | +``` |
| 176 | + |
| 177 | +Query the nodes: |
| 178 | + |
| 179 | +```bash |
| 180 | +kubectl get nodes |
| 181 | +``` |
| 182 | + |
| 183 | +```bash title="Output" |
| 184 | +NAME STATUS ROLES AGE VERSION |
| 185 | +aks-nodepool1-5829393-vmss000000 Ready <none> 99s v1.36.2+k3s1 |
| 186 | +k3d-aks-demo-7da4c24d-server-0 Ready control-plane 2m1s v1.36.2+k3s1 |
| 187 | +``` |
| 188 | + |
| 189 | +:::note |
| 190 | +Unlike in the cloud, where the control plane is hidden, the local cluster also lists its k3d |
| 191 | +control-plane node. Agent nodes carry the same `aks-<pool>-...-vmss` naming scheme as real AKS |
| 192 | +nodes, and the `VERSION` column reflects the underlying k3s runtime rather than the cluster's |
| 193 | +`kubernetesVersion`. |
| 194 | +::: |
| 195 | + |
| 196 | +### Manage node pools |
| 197 | + |
| 198 | +Add a user node pool with two nodes: |
| 199 | + |
| 200 | +```bash |
| 201 | +az aks nodepool add \ |
| 202 | + --resource-group rg-aks-demo \ |
| 203 | + --cluster-name aks-demo \ |
| 204 | + --name workers \ |
| 205 | + --mode User \ |
| 206 | + --node-count 2 |
| 207 | +``` |
| 208 | + |
| 209 | +```bash title="Output" |
| 210 | +{ |
| 211 | + "count": 2, |
| 212 | + "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/rg-aks-demo/providers/Microsoft.ContainerService/managedClusters/aks-demo/agentPools/workers", |
| 213 | + "mode": "User", |
| 214 | + "name": "workers", |
| 215 | + "orchestratorVersion": "1.34", |
| 216 | + "osType": "Linux", |
| 217 | + "provisioningState": "Succeeded", |
| 218 | + ... |
| 219 | +} |
| 220 | +``` |
| 221 | + |
| 222 | +List the node pools of the cluster: |
| 223 | + |
| 224 | +```bash |
| 225 | +az aks nodepool list \ |
| 226 | + --resource-group rg-aks-demo \ |
| 227 | + --cluster-name aks-demo \ |
| 228 | + --output table |
| 229 | +``` |
| 230 | + |
| 231 | +```bash title="Output" |
| 232 | +Name OsType VmSize Count MaxPods ProvisioningState Mode |
| 233 | +--------- -------- -------- ------- --------- ------------------- ------ |
| 234 | +nodepool1 Linux 1 250 Succeeded System |
| 235 | +workers Linux 2 250 Succeeded User |
| 236 | +``` |
| 237 | + |
| 238 | +Inspect a single node pool: |
| 239 | + |
| 240 | +```bash |
| 241 | +az aks nodepool show \ |
| 242 | + --resource-group rg-aks-demo \ |
| 243 | + --cluster-name aks-demo \ |
| 244 | + --name workers |
| 245 | +``` |
| 246 | + |
| 247 | +```bash title="Output" |
| 248 | +{ |
| 249 | + "count": 2, |
| 250 | + "mode": "User", |
| 251 | + "name": "workers", |
| 252 | + "orchestratorVersion": "1.34", |
| 253 | + "osType": "Linux", |
| 254 | + "powerState": { |
| 255 | + "code": "Running" |
| 256 | + }, |
| 257 | + "provisioningState": "Succeeded", |
| 258 | + ... |
| 259 | +} |
| 260 | +``` |
| 261 | + |
| 262 | +Delete the node pool when you no longer need it: |
| 263 | + |
| 264 | +```bash |
| 265 | +az aks nodepool delete \ |
| 266 | + --resource-group rg-aks-demo \ |
| 267 | + --cluster-name aks-demo \ |
| 268 | + --name workers |
| 269 | +``` |
| 270 | + |
| 271 | +### Stop, start, and delete |
| 272 | + |
| 273 | +Stop the cluster to free local resources while preserving its state: |
| 274 | + |
| 275 | +```bash |
| 276 | +az aks stop \ |
| 277 | + --resource-group rg-aks-demo \ |
| 278 | + --name aks-demo |
| 279 | +``` |
| 280 | + |
| 281 | +Verify that the cluster has stopped by confirming that `powerState` reports `Stopped`: |
| 282 | + |
| 283 | +```bash |
| 284 | +az aks show \ |
| 285 | + --resource-group rg-aks-demo \ |
| 286 | + --name aks-demo \ |
| 287 | + --query powerState.code \ |
| 288 | + --output tsv |
| 289 | +``` |
| 290 | + |
| 291 | +Start the cluster again. It restarts with the previous control plane state and number of agent nodes: |
| 292 | + |
| 293 | +```bash |
| 294 | +az aks start \ |
| 295 | + --resource-group rg-aks-demo \ |
| 296 | + --name aks-demo |
| 297 | +``` |
| 298 | + |
| 299 | +Delete the cluster once you are done. A deleted cluster cannot be recovered: |
| 300 | + |
| 301 | +```bash |
| 302 | +az aks delete \ |
| 303 | + --resource-group rg-aks-demo \ |
| 304 | + --name aks-demo \ |
| 305 | + --yes |
| 306 | +``` |
| 307 | + |
| 308 | +### Teardown |
| 309 | + |
| 310 | +Remove the resource group and any resources it still contains: |
| 311 | + |
| 312 | +```bash |
| 313 | +az group delete \ |
| 314 | + --name rg-aks-demo \ |
| 315 | + --yes |
| 316 | +``` |
| 317 | + |
| 318 | +Disable Azure CLI interception to point the `az` CLI back to the official Azure management REST API: |
| 319 | + |
| 320 | +```bash |
| 321 | +lstk az stop-interception |
| 322 | +``` |
| 323 | + |
| 324 | +## Features |
| 325 | + |
| 326 | +The local control plane implements the following capabilities: |
| 327 | + |
| 328 | +- **Networking**: Azure CNI overlay with the Cilium data plane, Cilium and Calico network |
| 329 | + policies, Hubble observability, and the managed Gateway API add-on with NGINX Gateway Fabric |
| 330 | + as its implementation. |
| 331 | +- **Storage**: The Secrets Store CSI driver for Azure Key Vault and the Azure Files CSI driver. |
| 332 | + The Azure Disk CSI driver is in progress. |
| 333 | +- **Scaling**: The cluster autoscaler, node auto-provisioning based on the AKS Karpenter provider, |
| 334 | + the Kubernetes Event-driven Autoscaling (KEDA) add-on, and the Vertical Pod Autoscaler. |
| 335 | +- **Identity**: Microsoft Entra Workload ID with a working OIDC issuer, so pods can exchange |
| 336 | + service account tokens for Azure credentials without secrets. |
| 337 | +- **Operations**: Multiple node pools with tags, labels, and taints, the Azure cloud controller |
| 338 | + manager reconciling `LoadBalancer` services, and cluster stop and start. |
| 339 | +- **Tooling**: The same clusters can be provisioned with the Azure CLI, Terraform, or Bicep. |
| 340 | + |
| 341 | +## Cluster-creation scripts |
| 342 | + |
| 343 | +The [aks-samples](https://github.com/localstack-samples/aks-samples) repository provides two |
| 344 | +interchangeable scripts that provision a production-shaped cluster, complete with a virtual |
| 345 | +network, a container registry, a Log Analytics workspace, and system and user node pools. The |
| 346 | +repository maintains both scripts to run against real Azure and the emulator; they differ in the |
| 347 | +cluster identity: |
| 348 | + |
| 349 | +| Script | Cluster identity | When to use | |
| 350 | +| ------ | ---------------- | ----------- | |
| 351 | +| [01-system-assigned-managed-identity.sh](https://github.com/localstack-samples/aks-samples/blob/main/scripts/01-system-assigned-managed-identity.sh) | System-assigned managed identity | Simplest option: Azure creates and manages the identity lifecycle together with the cluster. | |
| 352 | +| [01-user-assigned-managed-identity.sh](https://github.com/localstack-samples/aks-samples/blob/main/scripts/01-user-assigned-managed-identity.sh) | User-assigned managed identity | Use when you need a stable, pre-created identity that can be reused across resources and granted role assignments ahead of time. | |
| 353 | + |
| 354 | +Both scripts are idempotent and safe to re-run. The same folder also contains optional add-on |
| 355 | +installers for [Prometheus](https://github.com/localstack-samples/aks-samples/blob/main/scripts/02-install-prometheus.sh), |
| 356 | +the [NGINX ingress controller](https://github.com/localstack-samples/aks-samples/blob/main/scripts/03-install-nginx-ingress-controller.sh), |
| 357 | +the [Gateway API CRDs](https://github.com/localstack-samples/aks-samples/blob/main/scripts/04-install-gateway-api.sh), |
| 358 | +[NGINX Gateway Fabric](https://github.com/localstack-samples/aks-samples/blob/main/scripts/05-install-nginx-gateway-fabric.sh), |
| 359 | +and [cert-manager](https://github.com/localstack-samples/aks-samples/blob/main/scripts/06-install-cert-manager.sh). |
| 360 | + |
| 361 | +## Samples |
| 362 | + |
| 363 | +Every sample deploys the same Vacation Planner web application, a small Python |
| 364 | +[Flask](https://flask.palletsprojects.com/) single-page app. Only the data service, its |
| 365 | +provisioning, and the way the app authenticates to it change from one sample to the next. |
| 366 | + |
| 367 | +| Sample | Description | |
| 368 | +| ------ | ----------- | |
| 369 | +| [web-app-sql-database](https://github.com/localstack-samples/aks-samples/tree/main/samples/web-app-sql-database) | Stores activities in an Azure SQL Database, connecting with a SQL login over TDS. | |
| 370 | +| [web-app-mysql-flexible-server](https://github.com/localstack-samples/aks-samples/tree/main/samples/web-app-mysql-flexible-server) | Stores activities in an Azure Database for MySQL flexible server. | |
| 371 | +| [web-app-postgresql-flexible-server](https://github.com/localstack-samples/aks-samples/tree/main/samples/web-app-postgresql-flexible-server) | Stores activities in an Azure Database for PostgreSQL flexible server. | |
| 372 | +| [web-app-in-cluster-postgresql](https://github.com/localstack-samples/aks-samples/tree/main/samples/web-app-in-cluster-postgresql) | Stores activities in an in-cluster PostgreSQL database deployed as a Kubernetes StatefulSet, with a primary and two streaming replicas. | |
| 373 | +| [web-app-cosmosdb-mongodb-api](https://github.com/localstack-samples/aks-samples/tree/main/samples/web-app-cosmosdb-mongodb-api) | Stores activities in a collection of an Azure Cosmos DB for MongoDB account. | |
| 374 | +| [web-app-cosmosdb-nosql-api](https://github.com/localstack-samples/aks-samples/tree/main/samples/web-app-cosmosdb-nosql-api) | Stores activities in a container of an Azure Cosmos DB for NoSQL account. | |
| 375 | +| [web-app-blob-storage](https://github.com/localstack-samples/aks-samples/tree/main/samples/web-app-blob-storage) | Stores activities in an Azure Blob Storage container, using a connection string. | |
| 376 | +| [web-app-file-storage](https://github.com/localstack-samples/aks-samples/tree/main/samples/web-app-file-storage) | Stores activities as text files on an Azure Files share mounted by the Azure Files CSI driver, over either SMB or NFS. | |
| 377 | +| [web-app-managed-identity](https://github.com/localstack-samples/aks-samples/tree/main/samples/web-app-managed-identity) | Stores activities in Azure Blob Storage, authenticating with Microsoft Entra Workload ID instead of a secret, and optionally exposes the app through the Gateway API with a managed TLS certificate. | |
| 378 | + |
| 379 | +## Tutorials |
| 380 | + |
| 381 | +The same repository includes standalone tutorials that exercise individual AKS capabilities. Unlike |
| 382 | +the samples, they do not deploy the web application: |
| 383 | + |
| 384 | +| Tutorial | Description | |
| 385 | +| -------- | ----------- | |
| 386 | +| [policies](https://github.com/localstack-samples/aks-samples/tree/main/tutorials/policies) | Kubernetes network policies that enforce zero-trust traffic control with Calico and Cilium: cluster-wide default-deny, DNS-aware egress, and L3/L4/L7 ingress. | |
| 387 | +| [ccm](https://github.com/localstack-samples/aks-samples/tree/main/tutorials/ccm) | Exercises the Azure cloud controller manager: public and internal `LoadBalancer` services, source ranges, the nodeIP backend-pool variant, and an NGINX ingress controller. | |
| 388 | +| [gateway-api](https://github.com/localstack-samples/aks-samples/tree/main/tutorials/gateway-api) | Enables the managed Gateway API CRDs, installs NGINX Gateway Fabric, and routes traffic to a backend through a `Gateway` and an `HTTPRoute`. | |
| 389 | +| [keda](https://github.com/localstack-samples/aks-samples/tree/main/tutorials/keda) | Event-driven autoscaling with the KEDA add-on: a producer creates a backlog on an Azure event source and a `ScaledObject` scales a consumer from zero to four replicas and back. | |
| 390 | +| [keda/service-bus](https://github.com/localstack-samples/aks-samples/tree/main/tutorials/keda/service-bus) | Scales a consumer on an Azure Service Bus queue with the `azure-servicebus` scaler, authenticating with Microsoft Entra Workload ID. Start here if you are new to KEDA. | |
| 391 | +| [keda/queue-storage](https://github.com/localstack-samples/aks-samples/tree/main/tutorials/keda/queue-storage) | Scales a consumer on an Azure Storage queue with the `azure-queue` scaler. Workload identity is used end to end, so no data-plane secret exists anywhere. | |
| 392 | +| [keda/event-hubs](https://github.com/localstack-samples/aks-samples/tree/main/tutorials/keda/event-hubs) | Scales a consumer on an Azure Event Hubs hub with the `azure-eventhub` scaler, whose backlog is the distance between the last enqueued event and the consumer group's blob checkpoints. | |
| 393 | +| [key-vault-csi-driver](https://github.com/localstack-samples/aks-samples/tree/main/tutorials/key-vault-csi-driver) | Mounts secrets from Azure Key Vault into a pod with the Secrets Store CSI driver, in both the workload identity and the user-assigned managed identity access modes. | |
| 394 | +| [terraform/tags-labels-taints](https://github.com/localstack-samples/aks-samples/tree/main/tutorials/terraform/tags-labels-taints) | Deploys a modular, feature-rich AKS stack with Terraform, steering workloads across agent pools with Azure resource tags, node labels, and taints, then validates them through both the ARM and Kubernetes APIs. | |
| 395 | +| [bicep/tags-labels-taints](https://github.com/localstack-samples/aks-samples/tree/main/tutorials/bicep/tags-labels-taints) | The same modular AKS stack built with Bicep, with parameters and outputs that mirror the Terraform tutorial one-to-one. | |
| 396 | + |
| 397 | +## API Coverage |
| 398 | + |
| 399 | +<AzureFeatureCoverage service="Microsoft.ContainerService" client:load /> |
0 commit comments