Skip to content
AI-200 Learn

Create Kubernetes deployment manifests

Write AKS Deployment manifests for AI-200: apiVersion, selectors, ACR image paths, replicas, CPU/memory requests and limits, env vars and Secrets.

What this covers

A Deployment manifest is the YAML file that tells Azure Kubernetes Service (AKS) what to run: which container image, how many copies, how much CPU and memory each copy gets, and which settings it receives. You declare the desired state, apply it, and the Deployment controller keeps reality matching it, replacing Pods that crash or disappear. This page walks through every field the exam expects you to read or fix.

Exam weight: Develop containerized solutions on Azure (20–25%)

Skills measured

  • "Deploy and manage applications to Azure Kubernetes Service (AKS) by using manifest files"

Key concepts

Manifest anatomy

Every Kubernetes object has four top-level fields: apiVersion, kind, metadata and spec. For a Deployment, apiVersion is apps/v1 (not v1, which is for Pods, Services, ConfigMaps and Secrets). Inside spec you find replicas, a selector, and a Pod template.

Labels and selectors

spec.selector.matchLabels tells the Deployment which Pods it owns. spec.template.metadata.labels stamps those labels onto every Pod it creates. The two must match, or the API server rejects the manifest. In apps/v1 the selector is also immutable after creation: changing it means deleting and recreating the Deployment. The same Pod labels are what a Service selector targets later.

Container image

The image reference has the form <registry>.azurecr.io/<repository>:<tag>, for example myregistry.azurecr.io/inference-api:v1.0. The image must already be pushed, and the cluster must be allowed to pull it. On AKS you normally grant that with az aks update --attach-acr, which gives the kubelet managed identity the AcrPull role. Pin explicit tags (or digests) rather than latest: with latest the default imagePullPolicy becomes Always and rollbacks become ambiguous.

Replicas

replicas sets how many identical Pods run. One replica means any crash is an outage until the Pod restarts. Rough guidance from the module:

ReplicasWhen
2Basic resilience; dev and non-critical services
3Recommended for production APIs serving external users
4+Only when traffic needs load distribution; each copy costs resources

Requests and limits

  • Requests are what the scheduler reserves. A Pod requesting 2 GiB stays Pending if no node has 2 GiB unreserved.
  • Limits are the ceiling. Exceeding the memory limit gets the container killed (OOMKilled, exit code 137) and restarted. Exceeding the CPU limit does not kill anything; the container is throttled, which shows up as higher latency.

CPU uses millicores (1000m = 1 vCPU); memory uses binary units (Gi, Mi). For inference Pods, size memory from the model's documented footprint plus roughly 20% headroom, start with 1–2 vCPU, and tune from observed usage.

Environment variables and Secrets

Plain settings go in env with value. Sensitive values use valueFrom.secretKeyRef so the key never appears in the manifest or in source control. Larger sets of settings belong in ConfigMaps (see Define ConfigMaps for application settings).

How it works

Diagram source: flowchart LR A[deployment.yaml] -->|kubectl apply| B[API server] B --> C[Deployment controller] C --> D[ReplicaSet] D --> E[Pod 1] D --> F[Pod 2] G[Azure Container Registry] -->|image pull via AcrPull| E G --> F

The Deployment creates a ReplicaSet for each Pod template version. The ReplicaSet keeps the replica count; the scheduler places each Pod on a node whose free allocatable capacity covers the Pod's requests; the kubelet pulls the image and starts the container. Changing the Pod template (for example a new image tag) creates a new ReplicaSet and triggers a rolling update.

A complete manifest:

YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ai-inference-api
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: inference-api          # Deployment manages Pods with this label
  template:
    metadata:
      labels:
        app: inference-api        # must match the selector above
    spec:
      containers:
      - name: api
        image: myregistry.azurecr.io/inference-api:v1.0
        ports:
        - containerPort: 8080     # port the app listens on
        resources:
          requests:
            memory: "2Gi"
            cpu: "1000m"          # 1 vCPU
          limits:
            memory: "4Gi"
            cpu: "2000m"
        env:
        - name: MODEL_NAME
          value: "gpt-4"
        - name: API_ENDPOINT
          value: "https://api.example.com"
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: api-secrets   # Secret object name
              key: api-key        # key inside the Secret

Hands-on

Azure portal

  1. Open your AKS cluster and select Kubernetes resources > Workloads.
  2. Select Create > Apply a YAML.
  3. Paste the Deployment manifest and select Add.
  4. On the Deployments tab, open ai-inference-api and confirm Ready shows 2/2.

Azure CLI

Azure CLI / Bash
# Allow the cluster to pull from ACR (grants AcrPull to the kubelet identity)
az aks update --resource-group <resource-group> --name <aks-name> --attach-acr <acr-name>
az aks get-credentials --resource-group <resource-group> --name <aks-name>

# Create the Secret the manifest references (never commit the literal value)
kubectl create secret generic api-secrets --from-literal=api-key=<your-secret-key>

# Validate, preview and apply
kubectl apply -f deployment.yaml --dry-run=server
kubectl diff -f deployment.yaml
kubectl apply -f deployment.yaml

# Generate a starter manifest instead of typing it
kubectl create deployment ai-inference-api \
  --image=myregistry.azurecr.io/inference-api:v1.0 --replicas=2 \
  --dry-run=client -o yaml > deployment.yaml

Python

Python
# pip install kubernetes  (run `az aks get-credentials` first)
from kubernetes import client, config, utils

config.load_kube_config()
api = client.ApiClient()

# Apply the same YAML file the CLI uses
utils.create_from_yaml(api, "deployment.yaml", namespace="default")

apps = client.AppsV1Api()
dep = apps.read_namespaced_deployment("ai-inference-api", "default")
print(dep.status.ready_replicas, "/", dep.spec.replicas)

Exam traps

NeedField / option
Decide which Pods the Deployment ownsspec.selector.matchLabels (must equal template labels)
Guarantee scheduling capacityresources.requests
Stop one Pod starving a noderesources.limits
Pass an API keyenv[].valueFrom.secretKeyRef
Pass many plain settingsConfigMap via envFrom.configMapRef
Pull from private ACRaz aks update --attach-acr (AcrPull)

Quick review

  • Deployments are apps/v1; selector labels must match Pod template labels.
  • Image format: <registry>.azurecr.io/<repo>:<tag>; pin tags, avoid latest.
  • --attach-acr grants the kubelet identity AcrPull on the registry.
  • Requests drive scheduling; requests too large for any node = Pending.
  • Memory over limit = OOMKilled (exit 137); CPU over limit = throttling.
  • 2 replicas for basic resilience, 3 for production APIs.
  • Secrets are referenced with secretKeyRef, never hard-coded.

Practice

Practice: AKS manifest questions

Official references