3.2.3. Deploying to AKS with Manifest Files
💡 First Principle: Kubernetes is a declarative reconciliation engine: manifests state the world you want ("three replicas of this image, reachable on port 80"), the control plane relentlessly edits reality to match. You never command "start a container" — you update the desired state and let reconciliation act. Every kubectl workflow and every self-healing behavior follows from this loop.
Azure Kubernetes Service (AKS) manages the control plane; you manage what runs on it, chiefly through manifests applied with kubectl apply -f. The exam expects fluency in the core objects and the shape of a working Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: rag-api
spec:
replicas: 3
selector:
matchLabels: { app: rag-api }
template:
metadata:
labels: { app: rag-api }
spec:
containers:
- name: rag-api
image: myreg.azurecr.io/rag-api:1.4.2
ports: [{ containerPort: 8000 }]
env:
- name: COSMOS_DB
valueFrom: { configMapKeyRef: { name: rag-config, key: cosmosDb } }
readinessProbe:
httpGet: { path: /healthz, port: 8000 }
The object cast: a Deployment manages ReplicaSets and rolling updates for stateless workloads; a Service gives pods a stable virtual IP and DNS name (ClusterIP internal, LoadBalancer external); ConfigMaps and Secrets externalize configuration — the same runtime-config doctrine as 3.1.3, in Kubernetes dialect; namespaces partition the cluster; readiness/liveness probes tell the reconciler what "healthy" means, gating traffic and triggering restarts. AKS pulls from ACR via managed identity (attach with az aks update --attach-acr) — AcrPull again, no image-pull secrets needed.
The declarative loop is also the deployment strategy: change the manifest's image tag, kubectl apply, and the Deployment rolls pods gradually, honoring readiness probes — a hand-rolled version of what ACA revisions gave you as a service. That symmetry is the selection question: same outcome, different abstraction level, chosen by who's willing to operate the machinery.
⚠️ Exam Trap: Selector/label mismatch — spec.selector.matchLabels disagreeing with template.metadata.labels (or the Service's selector matching neither) — yields deployments that are "running" while the Service has zero endpoints. When "pods are healthy but the service returns no responses," check label wiring before anything network-shaped.
Day-two mechanics the exam touches: kubectl apply -f ./manifests/ applies a whole directory, which is how estates version manifests in git and apply them atomically per release. kubectl rollout status deployment/rag-api watches a rollout converge, and kubectl rollout undo steps back one revision of the Deployment's history — the AKS analogue of shifting traffic back to a known-good ACA revision. Resource requests (what the scheduler reserves) versus limits (the ceiling enforced at runtime, whose breach produces OOMKilled) is a distinction worth keeping crisp, and namespaces pair with ResourceQuota objects when teams share a cluster.
One more field worth pinning: imagePullPolicy decides whether nodes re-fetch images (Always) or trust their cache (IfNotPresent) — with unique tags, cached pulls are safe and fast, which is another quiet argument against :latest.
Reflection Question: You delete a pod manually and an identical one appears seconds later. Which object noticed, and why is that the same mechanism that performs rolling updates?