Skip to content
Back to Blog
1 min read

Azure Kubernetes Service 1.24: New Features and Breaking Changes

I wrote “Azure Kubernetes Service 1.24: New Features and Breaking Changes” to share practical, production-minded guidance on this topic.

Key Changes in 1.24

  • Dockershim removal
  • Seccomp by default
  • Pod security admission
  • Ephemeral containers GA
  • Storage capacity tracking

Upgrading to AKS 1.24

# Check current version
az aks show --resource-group myResourceGroup --name myAKSCluster --query kubernetesVersion

# Check available versions
az aks get-versions --location eastus --output table

# Upgrade cluster
az aks upgrade \
    --resource-group myResourceGroup \
    --name myAKSCluster \
    --kubernetes-version 1.24.0

# Verify upgrade
kubectl get nodes

Container Runtime Migration

With Dockershim removed, containerd is now the default:

# Check container runtime
apiVersion: v1
kind: Pod
metadata:
  name: runtime-check
spec:
  containers:
  - name: check
    image: alpine
    command: ["sh", "-c", "cat /etc/os-release && sleep 3600"]
# Verify containerd
kubectl get nodes -o jsonpath='{.items[*].status.nodeInfo.containerRuntimeVersion}'
# Output: containerd://1.6.x

Updating Docker-Specific Configurations

Replace Docker socket mounts:

# Old: Docker socket (no longer works)
# volumes:
# - name: docker-sock
#   hostPath:
#     path: /var/run/docker.sock

# New: Use containerd or avoid privileged access
apiVersion: v1
kind: Pod
metadata:
  name: build-pod
spec:
  containers:
  - name: kaniko
    image: gcr.io/kaniko-project/executor:latest
    args:
    - "--dockerfile=Dockerfile"
    - "--context=git://github.com/my-repo/app"
    - "--destination=myregistry.azurecr.io/app:latest"
    volumeMounts:
    - name: kaniko-secret
      mountPath: /kaniko/.docker
  volumes:
  - name: kaniko-secret
    secret:
      secretName: regcred
      items:
      - key: .dockerconfigjson
        path: config.json

Seccomp by Default

Kubernetes 1.24 enables Seccomp profiles by default:

apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  securityContext:
    seccompProfile:
      type: RuntimeDefault  # Now default in 1.24
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      allowPrivilegeEscalation: false
      runAsNonRoot: true
      runAsUser: 1000
      capabilities:
        drop:
          - ALL

Custom Seccomp profile:

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64"],
  "syscalls": [
    {
      "names": [
        "accept4",
        "bind",
        "clone",
        "close",
        "connect",
        "epoll_create1",
        "epoll_ctl",
        "epoll_pwait",
        "execve",
        "exit_group",
        "fcntl",
        "fstat",
        "futex",
        "getdents64",
        "getpid",
        "getsockname",
        "getsockopt",
        "listen",
        "mmap",
        "mprotect",
        "nanosleep",
        "openat",
        "read",
        "recvfrom",
        "rt_sigaction",
        "rt_sigprocmask",
        "sendto",
        "setsockopt",
        "socket",
        "stat",
        "write"
      ],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

Pod Security Admission

Replace Pod Security Policies with Pod Security Admission:

# Namespace-level enforcement
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: v1.24
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: v1.24
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: v1.24

Compliant pod example:

apiVersion: v1
kind: Pod
metadata:
  name: restricted-pod
  namespace: production
spec:
  securityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: myapp:latest
    securityContext:
      allowPrivilegeEscalation: false
      runAsNonRoot: true
      runAsUser: 65534
      capabilities:
        drop:
          - ALL
      readOnlyRootFilesystem: true
    volumeMounts:
    - name: tmp
      mountPath: /tmp
  volumes:
  - name: tmp
    emptyDir: {}

Ephemeral Containers GA

Debug running pods without modification:

# Attach ephemeral container to running pod
kubectl debug -it myapp-pod --image=busybox:1.35 --target=myapp

# Debug with specific tools
kubectl debug -it myapp-pod \
    --image=nicolaka/netshoot \
    --target=myapp \
    -- /bin/bash

# Create debug copy of pod
kubectl debug myapp-pod -it \
    --copy-to=myapp-debug \
    --container=debug \
    --image=ubuntu

Using ephemeral containers programmatically:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
spec:
  containers:
  - name: myapp
    image: myapp:latest
  ephemeralContainers:
  - name: debugger
    image: busybox:1.35
    command: ["sh"]
    stdin: true
    tty: true
    targetContainerName: myapp

Storage Capacity Tracking

New storage features:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: azure-disk-premium
provisioner: disk.csi.azure.com
parameters:
  skuName: Premium_LRS
  cachingMode: ReadOnly
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true\n\n## Takeaways\n\n*Add a concise, personal takeaway and recommended next steps here.*\n
Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.