01 - Containers & Virtualization
What Problem Do Containers Solve?
Before containers, deploying software was painful:
- "It works on my machine" syndrome
- Dependency conflicts between applications
- Slow provisioning of new environments
- Wasted resources running full VMs
Virtual Machines vs Containers
Virtual Machines (VMs)
┌─────────────────────────────┐
│ Your App │
├─────────────────────────────┤
│ Guest OS (Linux) │ ← Full operating system (~GBs)
├─────────────────────────────┤
│ Hypervisor │ ← VMware, VirtualBox, KVM
├─────────────────────────────┤
│ Host OS (macOS) │
├─────────────────────────────┤
│ Hardware │
└─────────────────────────────┘
- Each VM runs a full OS (kernel + userspace)
- Heavy: GBs of memory, minutes to boot
- Strong isolation (separate kernels)
- Examples: VMware, VirtualBox, Hyper-V, KVM
Containers
┌──────────┐ ┌──────────┐ ┌───────────┐
│ App A │ │ App B │ │ App C │
├──────────┤ ├──────────┤ ├───────────┤
│ Libs/Bins│ │ Libs/Bins│ │ Libs/Bins │
├──────────┴─┴──────────┴─┴───────────┤
│ Container Runtime │ ← Docker Engine
├─────────────────────────────────────┤
│ Host OS Kernel │ ← Shared!
├─────────────────────────────────────┤
│ Hardware │
└─────────────────────────────────────┘
- Containers share the host kernel
- Lightweight: MBs, seconds to start
- Process-level isolation
- Uses Linux kernel features (namespaces, cgroups)
Key Comparison
| Feature | VM | Container |
|---|---|---|
| Boot time | Minutes | Seconds |
| Size | GBs | MBs |
| Performance | Near-native with overhead | Near-native |
| Isolation | Strong (hardware-level) | Process-level |
| OS | Full guest OS | Shares host kernel |
| Density | 10s per host | 100s-1000s per host |
| Portability | Less portable | Highly portable |
The Linux Kernel Features Behind Containers
1. Namespaces (Isolation)
Namespaces make a process think it's alone on the system:
| Namespace | What It Isolates |
|---|---|
| PID | Process IDs -- container sees its own PID 1 |
| NET | Network interfaces, IPs, ports |
| MNT | Filesystem mount points |
| UTS | Hostname and domain name |
| IPC | Inter-process communication |
| USER | User and group IDs |
| CGROUP | Cgroup root directory |
bash# See namespaces of a process ls -la /proc/self/ns/ # Create a new namespace (what Docker does under the hood) unshare --pid --fork --mount-proc bash # Now you're in a new PID namespace! ps aux # Only shows processes in this namespace
2. Control Groups (cgroups) -- Resource Limits
Cgroups limit how much resources a process can use:
bash# Docker uses cgroups to limit resources docker run --memory=512m --cpus=1.5 nginx # Under the hood, this creates cgroup entries at: # /sys/fs/cgroup/memory/docker/<container-id>/memory.limit_in_bytes # /sys/fs/cgroup/cpu/docker/<container-id>/cpu.cfs_quota_us
3. Union Filesystems (Layered Storage)
Containers use layered filesystems for efficiency:
┌─────────────────────┐
│ Container Layer │ ← Read-Write (your changes)
├─────────────────────┤
│ App Layer │ ← Read-Only
├─────────────────────┤
│ Dependencies │ ← Read-Only
├─────────────────────┤
│ Base OS Layer │ ← Read-Only (e.g., Ubuntu)
└─────────────────────┘
- Each layer is read-only except the top (container layer)
- Layers are shared between containers (saves disk space)
- Copy-on-write: only modified files are duplicated
Container Runtimes
The container runtime is the software that actually runs containers:
| Runtime | Level | Description |
|---|---|---|
| containerd | High-level | Industry standard, used by Docker & K8s |
| CRI-O | High-level | Lightweight, Kubernetes-native |
| runc | Low-level | OCI reference implementation |
| gVisor | Sandboxed | Google's secure container runtime |
| Kata Containers | VM-based | Containers in lightweight VMs |
Docker CLI → Docker Daemon → containerd → runc → Container
↓
Kubernetes → CRI → containerd/CRI-O → runc → Container
OCI (Open Container Initiative)
The OCI defines open standards for containers:
- Runtime Spec: How to run a container
- Image Spec: How container images are structured
- Distribution Spec: How images are distributed (registries)
This means images built with Docker work with Podman, containerd, etc.
Quick Hands-On
bash# Run your first container docker run hello-world # Run an interactive Ubuntu container docker run -it ubuntu bash # Inside the container: hostname # Random container ID cat /etc/os-release # Ubuntu ps aux # Only your bash process (PID namespace!) exit # The container is isolated but shares the host kernel uname -r # Same kernel version as host
FAANG Interview Angle
Common questions:
- "Explain the difference between VMs and containers"
- "What Linux kernel features enable containers?"
- "What is the OCI and why does it matter?"
- "When would you choose VMs over containers?"
Key answers:
- Containers share the host kernel, VMs have their own
- Namespaces provide isolation, cgroups provide resource limits
- OCI ensures portability across container runtimes
- Use VMs when you need different OS kernels or stronger security isolation