~12 min
A container image is a read-only, layered filesystem plus
metadata — the entry point, environment variables, exposed
ports — packaged to the OCI (Open Container Initiative) image
format that Docker, containerd and Kubernetes all understand.
You build one from a Dockerfile: each instruction (FROM,
RUN, COPY) adds a new, cached layer, and the builder only
redoes the layers from the line you changed onward, which is
why ordering matters — put the slow-changing dependency install
before the fast-changing source copy.
A multi-stage build uses more than one FROM in the same
Dockerfile so the final image ships only what the running
application needs, not the compiler, package manager cache, or
test framework that built it. The build stage installs
everything required to compile the code; the final stage starts
fresh from a minimal base image and copies across only the
compiled binary or the interpreted source plus its runtime
dependencies. A smaller image pulls faster, scans faster for
vulnerabilities, and gives an attacker fewer tools to work with
if the container is ever compromised.
dockerfile
FROM golang:1.23 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app .
FROM gcr.io/distroless/static-debian12
COPY --from=build /app /app
ENTRYPOINT ["/app"]Kubernetes rarely runs a Pod on its own — instead you describe the Pod you want inside a workload resource, and a controller keeps that many Pods running. The CKAD exam expects you to pick the right one for a scenario rather than default to a Deployment out of habit.
A Deployment manages a set of interchangeable, stateless replicas and is the right default for anything that just needs "N copies running, roll updates in and out safely." A DaemonSet instead runs exactly one Pod on every node (or every node matching a selector) and adds or removes that Pod automatically as nodes join or leave — the fit for a log collector or a node-level metrics agent, where the requirement is "one per node," not "N copies." A Job runs a Pod to completion once — the Pod exits successfully and the Job is done, which suits a database migration or a one-off data import. A CronJob wraps a Job in a schedule, using standard cron syntax — the fit whenever the requirement is "run this on a timer," such as a nightly backup.
The question to ask is not "which resource have I used before" but "what does this Pod's lifecycle actually need": does it run forever and get replaced, does it need exactly one copy per node, does it run once to completion, or does it run on a schedule? Answering that correctly is worth more marks than memorising YAML syntax, because the exam grades the running result, not the command you typed to get there.
DaemonSet — it places exactly one Pod per matching node and follows the node set automatically, which a Deployment's fixed replica count does not do.