TL;DR (Quick Summary)#
- Containers vs VMs: VMs virtualize physical hardware via a Hypervisor (running full guest OSs). Containers isolate Linux processes on a single shared OS kernel.
- Linux Namespaces: Provide isolation (PID, NET, MNT, IPC, UTS, USER).
- Control Groups (cgroups): Provide resource limits (CPU quotas, Memory caps, I/O limits).
- UnionFS (Overlay2): Combines read-only image layers with a thin read-write container layer using Copy-on-Write (CoW).
1. Containers vs Virtual Machines#
graph TD
subgraph VirtualMachineArchitecture ["Virtual Machine Architecture"]
HardwareVM["Physical Server / Hardware"] --> Hypervisor["Hypervisor: ESXi / KVM"]
Hypervisor --> GuestOS1["Guest OS (Linux) + Kernel"]
Hypervisor --> GuestOS2["Guest OS (Windows) + Kernel"]
GuestOS1 --> App1["Application A"]
GuestOS2 --> App2["Application B"]
end
subgraph ContainerArchitecture ["Container Architecture"]
HardwareHost["Physical Server / Hardware"] --> HostOS["Host OS Kernel"]
HostOS --> DockerEngine["Docker Daemon / containerd"]
DockerEngine --> Container1["Isolated Container A"]
DockerEngine --> Container2["Isolated Container B"]
end
2. The Three Pillars of Containerization#
| Linux Primitive | What It Controls | Example Usage |
|---|---|---|
| Namespaces | What a process can SEE. | PID: Isolates process IDs (pid 1 inside container). NET: Isolates virtual network interfaces. |
| cgroups (Control Groups) | What a process can USE. | Restricts a container to maximum 2 CPU cores and 1GB RAM. |
| Overlay2 (UnionFS) | How a process stores FILES. | Merges read-only image layers into a single unified directory structure. |
3. Step-by-Step Lab: Inspecting Container Isolation#
Let’s inspect the underlying Linux primitives of a running container.
Step 1: Launching a Detached Container#
Run an isolated Nginx container:
docker run -d --name test-container -m 512m --cpus="1.0" nginx:alpineStep 2: Finding the Host Process ID (PID)#
Find the host PID of the running container process:
PID=$(docker inspect --format '{{ .State.Pid }}' test-container)
echo "Container Host PID: $PID"Expected Terminal Output:
Container Host PID: 14589Step 3: Inspecting Namespaces#
Compare the PID namespace view:
# Host view of the process
ps aux | grep 14589
# Container internal view of the exact same process
docker exec test-container ps auxInside the container, the process sees itself as PID 1. On the host Linux machine, it is actually PID 14589!
Step 4: Inspecting cgroup Resource Caps#
Inspect the cgroup memory limits imposed on the process:
cat /sys/fs/cgroup/memory/docker/$(docker inspect --format '{{ .Id }}' test-container)/memory.limit_in_bytesExpected Terminal Output:
536870912(Exact value: 512 MB in bytes).
4. Troubleshooting & Common Errors#
Error 1: OOMKilled (Out of Memory)#
The Cause: The container process exceeded its allocated cgroup memory limit. The Linux kernel OOM Killer immediately terminates pid 1.
The Fix: Inspect container exit status:
docker inspect test-container --format '{{ .State.OOMKilled }}'Increase memory allocation (-m 1g) or optimize application memory consumption.
Summary & Next Steps#
In this episode:
- We demystified containers as isolated Linux processes.
- We analyzed Namespaces, cgroups, and Overlay2 Union Filesystems.
- We traced container PIDs and cgroup resource limits directly on the host OS kernel.
- We debugged
OOMKilledcontainer terminations.
Next, we move to Episode 2: Writing Production Dockerfiles!

