1. Containerization Fundamentals: What Are Containers?
What Are Containers and Why Do They Matter?
Before you write a single line of Dockerfile, you must understand what containers actually are, how they differ from virtual machines, and why they completely changed how software is built and deployed. This module builds the conceptual foundation you will use every day as a container engineer.
The Problem Containers Solve
For decades, the phrase "it works on my machine" haunted every software team. A developer writes code on their laptop, tests it thoroughly, and pushes to production — only for the application to crash immediately. The culprit is almost always environment differences: different operating system versions, different library versions, different configuration files, or missing dependencies. Containers solve this by packaging the application AND its entire environment — including the operating system, system tools, libraries, and configuration — into a single, portable unit that runs exactly the same way everywhere. When you run a container, you are not just running your code; you are running your code inside a carefully crafted environment that you control completely.
Virtual Machines vs Containers: A Detailed Comparison
Virtual machines and containers both provide isolation, but they achieve it in fundamentally different ways. A virtual machine runs a complete guest operating system on top of a hypervisor. Each VM includes its own kernel, its own system libraries, and a full OS installation — typically consuming 2-10 GB of disk space and taking 30-60 seconds to boot. Containers, by contrast, share the host operating system's kernel. Instead of virtualizing hardware, containers use Linux kernel features to isolate processes. A container typically consumes 10-200 MB and starts in milliseconds. This efficiency means you can run dozens of containers on a single server where you could only run a few virtual machines. The trade-off is that containers provide less isolation — a kernel exploit in the host affects all containers, while VMs have stronger isolation because each has its own kernel.
Each VM runs a full guest OS kernel. Disk usage: 2-10 GB. Boot time: 30-60 seconds. Strong isolation. Memory overhead: 1-4 GB per VM.
Containers share the host kernel. Disk usage: 10-200 MB. Boot time: 10-100 milliseconds. Weaker isolation. Memory overhead: 5-50 MB per container.
Linux Namespaces: The Technology Behind Container Isolation
Containers would not exist without Linux namespaces. A namespace wraps a global system resource in an abstraction that makes it appear to processes within the namespace that they have their own isolated instance of that resource. Docker uses six namespaces: PID namespace isolates process IDs (processes inside the container cannot see processes outside), NET namespace gives each container its own network stack (interfaces, routing tables, firewall rules), MNT namespace isolates mount points (containers have their own filesystem view), UTS namespace isolates hostnames (each container can have its own hostname), IPC namespace isolates inter-process communication resources, and USER namespace isolates user IDs (a non-root user outside can appear as root inside). When you run a container, Docker creates new namespaces for it, then launches your process inside those namespaces. The process THINKS it is running on its own dedicated machine, but it is actually running in a carefully constrained part of the host system.
Try This Mental Model
Imagine an apartment building. Virtual machines are like separate houses — each has its own foundation, walls, roof, and utilities. Containers are like apartments — they share the same building, elevator, and plumbing (the host kernel), but each apartment has its own lock, its own view, and cannot see into the other apartments (namespaces).
The Container Stack: From Docker CLI to runc
When you type `docker run`, a chain of components works together to start your container. First, the Docker CLI sends a REST API request to the Docker daemon (dockerd). The daemon is the brain of Docker — it manages images, containers, networks, and volumes. The daemon then calls containerd, which is the industry-standard container runtime interface. containerd manages the complete container lifecycle: pulling images, creating container directories, and managing network interfaces. containerd then creates a container by calling runc, the low-level OCI runtime. runc is a lightweight tool that directly interfaces with Linux kernel namespaces and cgroups to actually start the container process. Finally, the container process runs, and your application is live. Understanding this stack helps you debug issues: if `docker` commands hang, check the daemon; if containers crash immediately, check runc logs; if networking fails, check containerd network setup.
The command-line tool you interact with. Sends API commands to the daemon.
The background service that manages images, containers, networks, and volumes.
Industry-standard container runtime. Manages container lifecycle, image distribution, and storage.
OCI-compliant runtime. Directly creates and runs containers using Linux kernel features.
Images vs Containers: The Blueprint and the Building
Beginners often confuse images and containers, but the distinction is simple once you understand it. An image is a read-only template that contains everything needed to run an application: the operating system files, system libraries, runtime, application code, and configuration. Think of an image as a class in object-oriented programming — it defines the structure but is not a running entity. A container is a running instance of an image. When you run an image, Docker adds a thin writable layer on top of the read-only image layers. This writable layer stores any changes made during the container's lifetime — logs, temporary files, application state. When you delete the container, this writable layer is destroyed, but the underlying image remains intact. This design allows you to run dozens of containers from the same image without duplicating the image data; each container shares the read-only layers and only maintains its own tiny writable layer.
Open your terminal. Run `docker run hello-world`. Docker will download the hello-world image, create a container, run it, and the container will exit after printing a message. Then run `docker ps -a` to see the stopped container, and `docker images` to see the cached image. This one command exercises the entire container lifecycle.
Knowledge Check
Ready to test your understanding of 1. Containerization Fundamentals: What Are Containers??