Containers 101
Containers vs virtual machines
Section titled “Containers vs virtual machines”A container is an isolated process that shares the host kernel. Unlike a VM, there is no guest OS — the container sees its own filesystem, network stack, and PID namespace but runs directly on the host. This makes containers lighter and faster to start than VMs.
On Kestrel, every workload runs as a container inside a Kubernetes Pod. You never SSH into a node; you interact with containers through kubectl.
Images and layers
Section titled “Images and layers”A container image is a read-only filesystem snapshot packaged as a stack of layers. Each layer corresponds to a step in the build process. Layers are cached and shared between images, so two images that start from the same base only store the diff.
FROM python:3.12-slimWORKDIR /appCOPY requirements.txt .RUN pip install --no-cache-dir -r requirements.txtCOPY . .CMD ["python", "main.py"]Key points:
- Order matters. Put layers that change least (base image, dependency install) first. Layers that change often (your source code) go last.
- Each instruction creates a layer. Combine related
RUNcommands with&&to reduce layer count. - Don’t store secrets in layers. Anything written during build is baked into the image permanently.
Registries
Section titled “Registries”A registry is a server that stores and distributes images. When you docker pull nginx:1.27, the client fetches layers from a registry (Docker Hub by default).
Common registries:
| Registry | Use case |
|---|---|
| Docker Hub | Public images, upstream base images |
GitHub Container Registry (ghcr.io) | Project images tied to GitHub repos |
| Harbor / GitLab Registry | Self-hosted, often used by institutional clusters |
Dockerfile basics
Section titled “Dockerfile basics”A Dockerfile is a text recipe that builds an image. The most common instructions:
| Instruction | Purpose |
|---|---|
FROM | Base image to start from |
WORKDIR | Set the working directory inside the image |
COPY / ADD | Copy files from the build context into the image |
RUN | Execute a command during build (install packages, compile code) |
ENV | Set environment variables |
EXPOSE | Document which port the app listens on (does not publish the port) |
CMD / ENTRYPOINT | Default command when the container starts |
Multi-stage builds
Section titled “Multi-stage builds”Use multi-stage builds to keep final images small. Build in one stage, copy only the artifact into a minimal runtime stage:
FROM golang:1.22 AS buildWORKDIR /srcCOPY . .RUN go build -o /app .
FROM gcr.io/distroless/staticCOPY --from=build /app /appCMD ["/app"]The final image contains only the compiled binary — no compiler, no source code.
Container lifecycle
Section titled “Container lifecycle”When Kubernetes starts a container:
- The kubelet pulls the image (if not cached on the node).
- A new container process starts with the image’s filesystem as its root.
- The process runs until it exits, is killed, or the Pod is deleted.
- Logs from stdout/stderr are captured and accessible via
kubectl logs.
Containers are ephemeral. Any data written inside the container is lost when it stops unless you mount a persistent volume.
Next steps
Section titled “Next steps”- Core Kubernetes objects — how Pods, Deployments, and Services wrap containers
- kubectl basics — commands for inspecting and managing containers on the cluster