Containers & Orchestration
Docker images and containers, container networking, Kubernetes and OpenShift core objects.
20 questions
JuniorTheoryVery commonWhat are containers, and what is their main advantage over running on the host directly?
What are containers, and what is their main advantage over running on the host directly?
A container packages an app with its dependencies and runs it as an isolated process sharing the host kernel — namespaces for isolation, cgroups for resource limits. The main advantage is consistency and density: the same image runs identically everywhere, with far less overhead and faster startup than a VM.
Common mistakes
- ✗Calling a container a VM — it shares the host kernel rather than booting its own
- ✗Thinking containers give stronger isolation than VMs (it is generally weaker, shared kernel)
- ✗Believing a container can emulate a different CPU architecture for free
Follow-up questions
- →Which Linux kernel features (namespaces, cgroups) make container isolation possible?
- →When would you still choose a virtual machine over a container?
JuniorTheoryVery commonWhat is the difference between a Docker image and a Docker container?
What is the difference between a Docker image and a Docker container?
An image is a read-only, layered template — a packaged filesystem plus metadata from a Dockerfile. A container is a running instance of an image: the engine adds a thin writable layer and starts the process. One image yields many isolated containers; writes go to the per-container layer and vanish when it is removed.
Common mistakes
- ✗Saying the container is read-only and the image writable — it is the reverse
- ✗Thinking changes inside a container persist into the image automatically
- ✗Believing each container runs its own kernel like a VM rather than sharing the host's
Follow-up questions
- →How do image layers enable caching and sharing across multiple images?
- →What happens to data written inside a container when it is removed, and how do volumes change that?
JuniorTheoryVery commonWhat is a Linux namespace, and which namespaces isolate a container?
What is a Linux namespace, and which namespaces isolate a container?
A namespace is a kernel feature giving a process group its own isolated instance of one global resource, so it sees only its own. A container combines several: PID (processes), net (interfaces), mnt (mounts), uts (hostname), ipc and user (UID mapping).
Common mistakes
- ✗Confusing a namespace (isolates a view) with a cgroup (limits resource usage)
- ✗Thinking one namespace covers everything rather than several, one per resource
- ✗Believing a namespace loads a separate kernel instead of partitioning the host's
Follow-up questions
- →How does the user namespace let container root map to an unprivileged host UID?
- →Why does the PID namespace make the container's first process become PID 1?
MiddleTheoryVery commonWhat is a Kubernetes Pod, and how do a Deployment and a Service relate to it?
What is a Kubernetes Pod, and how do a Deployment and a Service relate to it?
A Pod is the smallest deployable unit — one or more co-located containers sharing a network namespace and storage. A Deployment manages a ReplicaSet that keeps a desired number of identical Pods running and handles rolling updates. A Service gives those ephemeral Pods one stable virtual IP and DNS name, load-balancing traffic across the matching Pods.
Common mistakes
- ✗Equating a Pod with a single container instead of one or more sharing a namespace
- ✗Thinking Pods have stable IPs rather than the Service providing the stable endpoint
- ✗Confusing a Deployment (manages replicas + rollouts) with a one-shot Job
Follow-up questions
- →Why do containers in the same Pod reach each other over
localhost? - →How does a Service select which Pods to route to, and what role do labels play?
JuniorTheoryCommonWhat is a container registry, and what happens on docker push and docker pull?
What is a container registry, and what happens on docker push and docker pull?
A registry stores and distributes images, addressed as repository:tag. docker push uploads the manifest and each layer, skipping any the registry already has by digest. docker pull fetches the manifest, then downloads and reassembles the missing layers.
Common mistakes
- ✗Thinking push/pull transfer source code rather than prebuilt image layers
- ✗Assuming every layer is re-transferred even when the registry already has it
- ✗Forgetting a private registry needs docker login before push or pull
Follow-up questions
- →How does content-addressing let push and pull skip layers that already exist?
- →What is the difference between referencing an image by tag versus by digest?
JuniorTheoryCommonHow do COPY and ADD differ in a Dockerfile, and which should you prefer?
How do COPY and ADD differ in a Dockerfile, and which should you prefer?
COPY copies files and directories from the build context into the image. ADD does the same plus two extras: it auto-extracts a local tar archive and can fetch a remote URL. Prefer COPY; use ADD only for its tar extraction, and download URLs with RUN curl.
Common mistakes
- ✗Reversing them — ADD auto-extracts tars and fetches URLs, not COPY
- ✗Using ADD for a plain file copy where COPY is clearer and safer
- ✗Fetching a URL with ADD instead of a RUN curl that you can verify
Follow-up questions
- →Why can fetching a URL with ADD hurt build-cache reuse and reproducibility?
- →When is ADD's tar auto-extraction genuinely the right tool?
MiddleTheoryCommonWhat do cgroups limit, and how does that differ from what namespaces isolate?
What do cgroups limit, and how does that differ from what namespaces isolate?
cgroups control how much a process group may consume — capping CPU, memory, PIDs and I/O. Namespaces control what it can see: namespaces isolate a view, cgroups impose limits. Exceeding a memory cgroup triggers an OOM kill; hitting a CPU limit throttles the process.
Common mistakes
- ✗Swapping the roles — namespaces isolate the view, cgroups cap resource usage
- ✗Thinking a CPU-limit hit kills the process rather than throttling it
- ✗Believing a memory-limit breach is silently ignored instead of an OOM kill
Follow-up questions
- →How does the container runtime map a
--memoryflag onto a memory cgroup? - →What changed between cgroup v1 and v2 in how limits are organized?
MiddleCodeCommonRewrite this Node Dockerfile to apply container best practices.
Rewrite this Node Dockerfile to apply container best practices.
Pin the base (node:20.11-slim), then COPY package*.json and run npm ci --omit=dev before COPY . ., so a source-only edit reuses the cached dependency layer. Add a .dockerignore for node_modules/.git to keep the context small, and finish with USER node to run unprivileged.
Common mistakes
- ✗Copying source before the manifest, so npm install re-runs on every code edit
- ✗Leaving the base on the floating
node/latesttag instead of pinning it - ✗Running as root and shipping node_modules into the context with no .dockerignore
Follow-up questions
- →Why is
npm cipreferable tonpm installin a reproducible build? - →What does pinning the base image by digest add beyond pinning by tag?
MiddlePerformanceCommonAn image is 1.2 GB. What techniques reduce it, and in what order?
An image is 1.2 GB. What techniques reduce it, and in what order?
Start with the biggest wins: a multi-stage build so the compiler and SDK never reach the final image, and a smaller base (slim, alpine, distroless). Then trim layers — combine RUN steps and clean the package cache in the same layer. Add a .dockerignore and drop unshipped deps.
Common mistakes
- ✗Deleting files in a later layer — the bytes stay in the earlier layer that added them
- ✗Thinking flattening or faster hardware shrinks the image rather than a smaller base
- ✗Splitting every command into its own layer instead of combining and cleaning in one
Follow-up questions
- →Why does
rmin a later RUN layer not reclaim the space the earlier layer added? - →What does distroless remove versus alpine, and what does each trade away?
MiddleCodeCommonWrite a multi-stage Dockerfile that produces a minimal final image for a Go app.
Write a multi-stage Dockerfile that produces a minimal final image for a Go app.
A builder on golang:1.22 copies go.mod/go.sum, runs go mod download, then builds a static binary. The final stage is FROM gcr.io/distroless/static and does COPY --from=builder of only the binary, running as non-root. The image holds no compiler or source, so it is a few megabytes.
Common mistakes
- ✗Removing the toolchain in a later layer instead of using a separate final stage
- ✗Copying source before go.mod/go.sum, busting the module-download cache on code edits
- ✗Thinking a Go binary needs the toolchain present at run time to execute
Follow-up questions
- →Why can a statically linked Go binary run on
scratchor distroless with no libc? - →How would the equivalent Node multi-stage build differ, given it has no static binary?
JuniorTheoryOccasionalHow does a container isolate a process with namespaces and cgroups while sharing the host kernel?
How does a container isolate a process with namespaces and cgroups while sharing the host kernel?
A container is a normal host process confined by namespaces — each gives it a private view of one kernel resource (PIDs, mounts, network) — while cgroups cap its CPU, memory and I/O. It shares the host kernel with no guest OS, so it stays lightweight.
Common mistakes
- ✗Calling the container a VM — it is a host process sharing the kernel, not a guest OS
- ✗Swapping the roles — namespaces isolate the view, cgroups limit resource usage
- ✗Thinking isolation is stronger than a VM's when the shared kernel makes it weaker
Follow-up questions
- →Which specific namespaces isolate a container, and what does each one virtualize?
- →What happens when a process exceeds its memory cgroup limit?
JuniorTheoryOccasionalWhat do the core Dockerfile instructions FROM, RUN, COPY, ENTRYPOINT and CMD do?
What do the core Dockerfile instructions FROM, RUN, COPY, ENTRYPOINT and CMD do?
FROM sets the base image later layers build on. RUN runs a command at build time and commits a new layer. COPY adds files from the build context. ENTRYPOINT fixes the executable run at start, and CMD gives default arguments that docker run can override.
Common mistakes
- ✗Swapping RUN (build time) with the container's start command
- ✗Thinking COPY runs a command rather than adding files from the context
- ✗Confusing ENTRYPOINT (fixed executable) with CMD (overridable defaults)
Follow-up questions
- →Why does each RUN create a separate layer, and when should you combine them?
- →What is the difference between the exec form and the shell form of CMD?
JuniorTheoryOccasionalWhat is an image layer, and how does the union (overlay) filesystem combine layers?
What is an image layer, and how does the union (overlay) filesystem combine layers?
Each filesystem-changing Dockerfile instruction adds one read-only layer, identified by its digest. A union filesystem like overlay stacks them into one merged view where an upper file shadows the path below; the container adds a thin writable top layer.
Common mistakes
- ✗Thinking each layer is a full image copy rather than a content-addressed diff
- ✗Believing the writable layer is part of the image instead of added at run time
- ✗Assuming layers cannot be shared or cached across images
Follow-up questions
- →How does content-addressed layering let two images share a common base?
- →Why does deleting a file in a later layer not shrink the image?
MiddleTheoryOccasionalHow do Docker containers communicate by default, and how do you publish a port?
How do Docker containers communicate by default, and how do you publish a port?
By default a container joins a bridge network; containers on a user-defined bridge reach each other by container name via Docker's embedded DNS. To expose a container port to the host you publish it: -p 8080:80 maps host 8080 to container 80. host networking drops isolation and shares the host's stack directly.
Common mistakes
- ✗Reversing the
-p host:containerorder — the host port comes first - ✗Expecting name-based resolution on the default bridge instead of a user-defined network
- ✗Thinking
hostmode increases isolation when it removes the network namespace
Follow-up questions
- →Why does name-based DNS work on a user-defined bridge but not the default
bridge? - →What does
host.docker.internalresolve to, and when do you need it?
MiddleDebuggingOccasionalA container exits immediately with code 0 — diagnose the startup problem.
A container exits immediately with code 0 — diagnose the startup problem.
Exit code 0 means the main process finished successfully — it did not crash. The CMD runs a one-shot migration that completes and returns, and a container lives only as long as its PID 1. Make PID 1 the long-running server and run the migration as a separate init step.
Common mistakes
- ✗Reading exit 0 as a crash rather than the main process finishing cleanly
- ✗Making a one-shot command the container's PID 1 instead of a long-running server
- ✗Trying to keep it alive with sleep instead of running the actual service as CMD
Follow-up questions
- →How would exit code 127 instead of 0 change your diagnosis?
- →Where should the migration run in a Kubernetes deployment — init container or Job?
MiddleTheoryOccasionalHow does the build cache decide to reuse a layer, and how do you order a Dockerfile for cache hits?
How does the build cache decide to reuse a layer, and how do you order a Dockerfile for cache hits?
The builder keys each layer on its instruction text plus, for COPY/ADD, a checksum of the copied files. The first miss invalidates every later layer. So order stable-to-volatile: install deps before copying the source, so a code edit never busts the deps layer.
Common mistakes
- ✗Copying all source before installing dependencies, busting the deps layer on every code edit
- ✗Thinking a miss invalidates only that instruction, not every layer after it
- ✗Assuming the cache keys on timestamps rather than instruction text and file checksums
Follow-up questions
- →Why does
COPY package.jsonbeforeCOPY . .survive a source-only change? - →How does a
.dockerignorefile change what aCOPY . .checksums?
MiddleTheoryOccasionalHow do ENTRYPOINT and CMD differ in a Dockerfile, and how do they combine?
How do ENTRYPOINT and CMD differ in a Dockerfile, and how do they combine?
ENTRYPOINT sets the executable the container always runs; CMD provides default arguments. In exec form CMD is appended as arguments to ENTRYPOINT. Arguments passed after the image name on docker run override CMD but not ENTRYPOINT. So ENTRYPOINT fixes the program and CMD supplies overridable defaults.
Common mistakes
- ✗Swapping the roles — ENTRYPOINT is the fixed program, CMD the default args
- ✗Thinking
docker runargs override ENTRYPOINT rather than CMD - ✗Believing they run at build time instead of container start
Follow-up questions
- →How does exec form differ from shell form for ENTRYPOINT, and why prefer exec?
- →How does
docker run --entrypointchange the default behaviour?
MiddleTheoryOccasionalWhat is container image scanning, and where does it fit in the pipeline?
What is container image scanning, and where does it fit in the pipeline?
A scanner inspects an image's layers, lists the installed OS and library packages, and matches their versions against CVE databases to flag known vulnerabilities. Run it in CI on the built image, fail on high or critical, and keep re-scanning it in the registry, because new CVEs appear after an image is pushed.
Common mistakes
- ✗Thinking a scan is a runtime traffic monitor rather than a package-version CVE check
- ✗Scanning only once at build and never re-scanning as new CVEs appear
- ✗Assuming the build should not fail on high or critical findings
Follow-up questions
- →Why must you re-scan an image in the registry even after it passed at build time?
- →How do you keep scanning from blocking releases on unfixable base-image CVEs?
MiddleDesignOccasionalA polyglot org ships services in Go, Java and Node. Each team writes its own Dockerfile, and the resulting images are 800 MB to 1.5 GB, carry compilers and package managers into production, and trip the security scanner on toolchain CVEs. Builds are slow and every team pins a different base image. You are asked for a standard build strategy that shrinks the images, removes build tooling from the runtime, and keeps the approach uniform across the three languages without forcing one runtime on everyone. What do you propose, and why does it work?
A polyglot org ships services in Go, Java and Node. Each team writes its own Dockerfile, and the resulting images are 800 MB to 1.5 GB, carry compilers and package managers into production, and trip the security scanner on toolchain CVEs. Builds are slow and every team pins a different base image. You are asked for a standard build strategy that shrinks the images, removes build tooling from the runtime, and keeps the approach uniform across the three languages without forcing one runtime on everyone. What do you propose, and why does it work?
Standardize on multi-stage builds: a builder stage carries the SDK and builds the artifact, then a slim final stage does COPY --from=builder of only that artifact onto a minimal or distroless base. The runtime image has no compiler or package manager, so it is small and clears the scanner.
Common mistakes
- ✗Thinking flattening layers removes build tooling — it keeps the files, just in one layer
- ✗Shipping the SDK to production instead of copying only the built artifact forward
- ✗Believing faster CI hardware shrinks the final image rather than the build strategy
Follow-up questions
- →How does
COPY --fromlet the final stage take only the artifact and drop the toolchain? - →When is a distroless base preferable to alpine, and what does it trade away?
MiddleTheoryRareWhat is rootless Docker, and what is its main security benefit?
What is rootless Docker, and what is its main security benefit?
Rootless Docker runs the daemon and containers as an unprivileged user, mapping container root (UID 0) to a non-privileged host UID via a user namespace. The benefit is a smaller blast radius: an escape or daemon compromise lands as an ordinary host user, not host root. Some features are limited.
Common mistakes
- ✗Thinking the daemon still runs as host root when rootless moves it off root
- ✗Confusing rootless with merely adding a USER line inside the image
- ✗Believing it costs nothing rather than limiting some privileged features
Follow-up questions
- →How does the user namespace map container UID 0 to a non-root host UID?
- →Which Docker features become harder or unavailable under rootless mode?