Pods, init containers and sidecars
The Pod spec fields the exam keeps returning to, how command and args override an image, and the multi-container shapes built on a shared volume: init containers and native sidecars.
After this lesson
- 01You can produce a Pod manifest with kubectl run --dry-run=client and add command, args, env, ports and restartPolicy to it without looking anything up.
- 02You can predict what a container runs when command and args are combined with an image's ENTRYPOINT and CMD.
- 03You can build a Pod whose init container prepares a shared volume, and a Pod or Job with a native sidecar declared as an init container with restartPolicy: Always.
- 04You can read logs from, and open a shell in, one specific container of a multi-container Pod.
Every workload ends up as a Pod, so every other lesson in this module assumes you can write one fast. The exam rarely wants a bare single-container Pod. It wants a second container, an init step, a shared volume or an exact command, and the grader then reads container names, images and where each container sits in the spec. Getting a container into containers when the question wanted initContainers scores zero even if the Pod runs.
Start from kubectl run, never from a blank file
# A manifest to edit, not a running Pod
kubectl run web --image=nginx:1.27 --port=80 --labels=app=web,tier=front --env=MODE=production --dry-run=client -o yaml > web.yaml
# A one-shot Pod that is not restarted when its command exits
kubectl run once --image=busybox:1.36 --restart=Never -- sh -c 'echo hello; sleep 5'
# Everything after -- becomes args ...
kubectl run box1 --image=busybox:1.36 -- sleep 3600
# ... unless you add --command, then it becomes command
kubectl run box2 --image=busybox:1.36 --command -- sleep 3600
# A throwaway client, deleted when it exits (interactive, fine in the exam terminal)
kubectl run tmp --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- http://webkubectl run creates exactly one Pod. Old guides mention --generator values that produced Deployments or Jobs; those were removed years ago, and there are no --requests or --limits flags either. Use kubectl create deployment|job|cronjob for controllers, and add resources in the YAML or with kubectl set resources.
apiVersion: v1
kind: Pod
metadata:
name: web
namespace: wl-pods
labels:
app: web
spec:
restartPolicy: Always # Always (default) | OnFailure | Never; applies to the whole Pod
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80 # documents the port and lets probes/Services use the name
name: http
env:
- name: MODE
value: production
- name: clock
image: busybox:1.36
command: ["sh", "-c"] # replaces the image ENTRYPOINT
args: ["while true; do date; sleep 10; done"] # replaces the image CMDcommand and args versus ENTRYPOINT and CMD
busybox has no ENTRYPOINT (its CMD is sh), so args alone happen to work there. With an image whose ENTRYPOINT is the application binary, args alone become flags to that binary. When a question says the container must run something, set command. Both fields are arrays; a shell pipeline needs ["sh", "-c", "..."].
Init containers
Init containers run one at a time in the order listed, and each must exit 0 before the next starts. The regular containers start only after the last one succeeds. A failed init container is retried according to the Pod's restartPolicy; with Never the Pod fails. While they run, kubectl get pods shows Init:0/2, Init:Error or Init:CrashLoopBackOff. Regular init containers cannot have liveness, readiness or startup probes.
apiVersion: v1
kind: Pod
metadata:
name: site
namespace: wl-pods
spec:
initContainers:
- name: render
image: busybox:1.36
command: ["sh", "-c", "echo \"built on $(hostname) at $(date)\" > /data/index.html"]
volumeMounts:
- name: html
mountPath: /data
containers:
- name: nginx
image: nginx:1.27
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
emptyDir: {}The volume is the contract between containers. An emptyDir lives as long as the Pod (it survives container restarts, not Pod deletion), and each container may mount it at a different path. The same pattern carries a rendered config file, a downloaded artifact or a Unix socket.
Native sidecar containers
A sidecar is a helper that must live as long as the main container: a log shipper, a proxy, a config reloader. Listed as a second entry under containers it has no start order, and in a Job it keeps the Pod alive forever because it never exits. A native sidecar is an entry under initContainers with restartPolicy: Always. The kubelet starts it in init order but only waits for it to start (or for its startupProbe to pass), keeps it running beside the app containers, restarts it if it dies, and stops it after the main containers finish. It has been on by default since v1.29 and is GA since v1.33.
apiVersion: v1
kind: Pod
metadata:
name: app
namespace: wl-pods
spec:
initContainers:
- name: log-tail
image: busybox:1.36
restartPolicy: Always # this one line makes it a sidecar
command: ["sh", "-c", "touch /var/log/app/app.log; tail -F /var/log/app/app.log"]
volumeMounts:
- name: logs
mountPath: /var/log/app
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "while true; do date >> /var/log/app/app.log; sleep 2; done"]
volumeMounts:
- name: logs
mountPath: /var/log/app
volumes:
- name: logs
emptyDir: {}kubectl -n wl-pods get pod app # READY 2/2: the sidecar is counted
kubectl -n wl-pods logs app -c log-tail # one container
kubectl -n wl-pods logs app --all-containers --prefix
kubectl -n wl-pods logs app -c app --previous # the instance that crashed last
kubectl -n wl-pods exec app -c app -- cat /var/log/app/app.log
kubectl -n wl-pods exec -it app -c app -- sh
kubectl -n wl-pods describe pod app | sed -n '/Init Containers/,/Conditions/p'Without -c, kubectl uses the container named in the Pod annotation kubectl.kubernetes.io/default-container, or else the first entry in containers, and prints a Defaulted container note. Read that note; it is how people end up grading the wrong container's logs.
In the docs · the tab you get on exam day
- Init Containers ↗search: “init containers”
- Sidecar Containers ↗search: “sidecar containers”
- Define a Command and Arguments for a Container ↗search: “command and arguments”
- Communicate Between Containers in the Same Pod Using a Shared Volume ↗search: “shared volume”
context · kubectl config use-context kind-cka
Serve a file prepared by an init container
wl-pods create a Pod named web. An init container named prepare (image busybox:1.36) writes the single line prepared by init to a file index.html on an emptyDir volume named content. A container named nginx (image nginx:1.27) mounts the same volume at /usr/share/nginx/html and serves that file on port 80.- 01
Set up the scenario
curl -fsSL https://codewithmuh.com/labs/cka/t/wl-pods-1/setup | bash - 02
Solve it in your terminal
Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under
/tmp/cka/. - 03
Check your work
curl -fsSL https://codewithmuh.com/labs/cka/t/wl-pods-1/verify | bashCounts toward your readiness score. - 04
Clean up
curl -fsSL https://codewithmuh.com/labs/cka/t/wl-pods-1/cleanup | bash
context · kubectl config use-context kind-cka
Let a Job finish despite its log shipper
report in namespace wl-pods never completes. Its main container finishes its work, but the log-shipper container keeps the Pod running. Change the Job so that log-shipper runs as a native sidecar: it starts before main, runs while main works, and is stopped automatically when main exits. Keep both containers' names, images and commands and the shared volume. The Job must reach the Complete condition.- 01
Set up the scenario
curl -fsSL https://codewithmuh.com/labs/cka/t/wl-pods-2/setup | bash - 02
Solve it in your terminal
Work the way the exam makes you: kubectl, the docs, nothing else. Put any answer files under
/tmp/cka/. - 03
Check your work
curl -fsSL https://codewithmuh.com/labs/cka/t/wl-pods-2/verify | bashCounts toward your readiness score. - 04
Clean up
curl -fsSL https://codewithmuh.com/labs/cka/t/wl-pods-2/cleanup | bash
Finished reading?
0 of 2 tasks passed. Readiness counts tasks, so run them before you move on.