CKAD — Kubernetes Application Developer
Pod Design — Labels, Commands, Jobs, CronJobs
Goal
You set a Pod's metadata (labels and annotations) and its execution method (command/args/env/restartPolicy) directly, and fill in the key fields of the batch workloads Job and CronJob.
Why it matters
Half of the CKAD questions come down to "which field does this value go in?" Labels and annotations are both string maps under metadata, but their roles are opposite. A label is a value that a selector picks, so Services, Deployments, and NetworkPolicies all look at it. An annotation is a note that a selector never looks at, so it holds settings that tools read (change-cause, controller hints). If you confuse the two, you get the incident where a Service has no endpoints attached.
The same goes for command and args. They override the image's ENTRYPOINT and CMD respectively; they are not simply "a command and its arguments." If you change only command, the CMD is still appended and you get a strange combination; if you change only args, the new arguments are appended to the original ENTRYPOINT.
A Job is a controller that "counts successes." Where a Deployment maintains "how many are running right now," a Job counts "how many times it has succeeded so far." That is why, if you set the Pod's restartPolicy to Always, the Pod never ends and neither does the Job.
Steps
- Create the namespace
ckad-design, then create a Podwebin it. Imagenginx:1.27, labelsapp=webandtier=frontend. - Add the annotation
owner=platform-teamto the Podweb. In the same namespace, create a Podapi— imagenginx:1.27, labelsapp=apiandtier=backend. - Create a Pod
runner. Imagebusybox:1.36,commandis["/bin/sh"],argsis["-c", "sleep 3600"]. - Create a Pod
envtest. Imagebusybox:1.36, labeltier=backend. Set the environment variablesAPP_ENV=productionandAPP_TIER=backendas fixed values, and havePOD_NAMEreferencemetadata.namethrough the Downward API.commandis["/bin/sh","-c","sleep 3600"]. - Create a Pod
oneshot. Imagebusybox:1.36,restartPolicyisNever,commandis["/bin/sh","-c","echo done"]. - Create a Job
batch-sum.completions: 4,parallelism: 2,backoffLimit: 3, the Pod'srestartPolicyisOnFailure, container imagebusybox:1.36. - Create a CronJob
nightly-report.schedule: "17 3 * * *",concurrencyPolicy: Forbid,startingDeadlineSeconds: 120,successfulJobsHistoryLimit: 3, container imagebusybox:1.36, the Pod'srestartPolicyisOnFailure. - In the
ckad-designnamespace, save only the names of the Pods with the labeltier=backendto/root/ckad-design/backend-pods.txt, one per line (names only, without thepod/prefix).
Notes
- It is faster to extract a skeleton with
kubectl run web --image=nginx:1.27 -n ckad-design --labels=app=web,tier=frontend --dry-run=client -o yaml > web.yamland then edit it. kubectl create job/kubectl create cronjobalso have--dry-run=client -o yaml. The Job spec'scompletionsandparallelismhave no flags, so edit the YAML directly.- Common mistake 1: writing
restartPolicyunder the container. It is a Pod-level field (spec.restartPolicy); native sidecars are the only exception. - Common mistake 2: putting
"-c sleep 3600"intoargsas a single string. It must be split into separate array elements. - In step 8,
kubectl get pods -l tier=backend -o nameprints names likepod/api. Keep only the names withcut -d/ -f2or-o jsonpath='{.items[*].metadata.name}'.
Namespace and first Pod
Create the namespace ckad-design, then create a Pod web in it. Image nginx:1.27, labels app=web and tier=frontend.
Create the namespace first, then create the Pod in it. Put the labels in metadata.labels. It is quicker to give kubectl run the --labels flag, or to extract a skeleton with --dry-run=client -o yaml and edit it.
Telling labels and annotations apart
Add the annotation owner=platform-team to the Pod web. In the same namespace, create a Pod api — image nginx:1.27, labels app=api and tier=backend.
A label is a value a selector picks; an annotation is a note a selector does not look at. You can add them later to an existing Pod with kubectl label and kubectl annotate.
Overriding image defaults with command and args
Create a Pod runner. Image busybox:1.36, command is ["/bin/sh"], args is ["-c", "sleep 3600"].
command overrides the Dockerfile's ENTRYPOINT and args overrides CMD. Both are YAML string arrays, and to use shell syntax (;, &&) you have to invoke a shell yourself.
Environment variables and the Downward API
Create a Pod envtest. Image busybox:1.36, label tier=backend. Set the environment variables APP_ENV=production and APP_TIER=backend as fixed values, and have POD_NAME reference metadata.name through the Downward API. command is ["/bin/sh","-c","sleep 3600"].
Fixed values go in env[].value; the Pod's own metadata comes in through env[].valueFrom.fieldRef.fieldPath. The field path for the Pod name starts with metadata..
Making a one-shot Pod with restartPolicy
Create a Pod oneshot. Image busybox:1.36, restartPolicy is Never, command is ["/bin/sh","-c","echo done"].
spec.restartPolicy is a Pod-level field. The default Always is for long-running work; for a task that runs once and ends, use a different value. You cannot modify this field after creation, so delete the Pod and create it again.
A Job's completion count and parallelism
Create a Job batch-sum. completions: 4, parallelism: 2, backoffLimit: 3, the Pod's restartPolicy is OnFailure, container image busybox:1.36.
completions is how many successes are needed in total for the Job to finish, and parallelism is how many to run at the same time. backoffLimit is the upper bound on failure retries, and its default is 6. There are only two values you can use for a Job Pod's restartPolicy.
CronJob schedule and concurrency policy
Create a CronJob nightly-report. schedule: "17 3 * * *", concurrencyPolicy: Forbid, startingDeadlineSeconds: 120, successfulJobsHistoryLimit: 3, container image busybox:1.36, the Pod's restartPolicy is OnFailure.
schedule is a five-field cron expression (minute, hour, day of month, month, day of week). concurrencyPolicy is the behavior when the previous run has not finished yet, and startingDeadlineSeconds is how many seconds late the Job may still start when the scheduled time has been missed.
Listing with a label selector (comprehensive)
In the ckad-design namespace, save only the names of the Pods with the label tier=backend to /root/ckad-design/backend-pods.txt, one per line (names only, without the pod/ prefix).
Filter with kubectl get pods -l key=value (the placeholders are the label key and value), and keep only the names with -o name or -o jsonpath. -o name produces the pod/이름 form (the placeholder is the Pod name), so you have to cut off the prefix. The file must contain only the names, one per line.