0%
Cloud Architecture Patterns
Cloud Foundations
Storage and Databases
Application Patterns
Infrastructure as Code
Reliability and Operations
Advanced Patterns
Containers and Orchestration
Containers solve the "it works on my machine" problem by packaging an application with everything it needs to run: code, runtime, libraries, and system tools. Unlike virtual machines, containers share the host operating system kernel. This single difference changes everything about how you deploy, scale, and reason about infrastructure.
Containers vs Virtual Machines
A virtual machine runs a full guest operating system on top of a hypervisor. Each VM includes its own kernel, system libraries, and init process. Booting a VM means booting an entire OS, which takes 30-90 seconds and consumes hundreds of megabytes of RAM before your application even starts.
A container is a process (or group of processes) running on the host kernel with isolated namespaces and resource limits. There is no guest OS. There is no hypervisor overhead. A container starts in milliseconds because it is just a process fork with some isolation flags. A typical container image is 50-200 MB versus 1-10 GB for a VM image.
The tradeoff is isolation. VMs provide hardware-level isolation: a kernel exploit in one VM cannot reach another. Containers share the host kernel, so a kernel vulnerability affects all containers on that host. For multi-tenant environments where you run untrusted code, VMs are safer. For your own microservices on trusted infrastructure, containers give you 10x density with acceptable isolation.
In system design interviews, default to containers for microservice deployments. VMs are the right choice when you need strong tenant isolation (multi-tenant SaaS running customer code) or when you need a different OS than the host (Windows containers on Linux hosts). For everything else, containers win on startup speed, resource efficiency, and deployment velocity.
Docker Images and Layers
A Docker image is a read-only template for creating containers. Images are built in layers, where each layer represents a filesystem change: install a package, copy source code, set an environment variable. Layers stack on top of each other using a union filesystem.
The layering system is why Docker builds are fast. If you change your application code but not your dependencies, Docker reuses the cached dependency layer and only rebuilds the application layer. This turns a 5-minute build into a 10-second build. The key is ordering your Dockerfile instructions from least-frequently-changed (base OS, system packages) to most-frequently-changed (application code) so that cache invalidation happens as late as possible.
Container Registries
Images need a place to live. Container registries store and distribute images. Docker Hub is the public default. AWS ECR, Google GCR, and GitHub Container Registry are private alternatives integrated with their respective cloud platforms.
A registry stores images by name and tag: mycompany/api-server:v2.3.1. Tags are mutable pointers. The latest tag moves with every push. Version-specific tags (v2.3.1) should be immutable after release. In production, always reference images by their SHA256 digest (mycompany/api-server@sha256:abc123...) rather than a tag, because digests are content-addressable and cannot be overwritten. This guarantees that the image running in production is exactly the image you tested.
Container Runtime and Isolation
When you run a container, the container runtime (containerd, CRI-O) uses Linux kernel features to create isolation:
Namespaces isolate what the container can see. The PID namespace gives each container its own process tree starting at PID 1. The network namespace gives each container its own network stack with its own IP address. The mount namespace gives each container its own filesystem view.
Cgroups (control groups) limit what the container can use. You set CPU limits (this container gets at most 2 cores), memory limits (this container gets at most 512 MB), and I/O limits. When a container exceeds its memory limit, the kernel OOM-kills it. When it hits its CPU limit, it gets throttled.
These are not container-specific technologies. They are kernel features that Docker and Kubernetes wrap with a user-friendly interface. Understanding them helps you debug container resource issues: if a container is slow, check whether it is being CPU-throttled by cgroup limits.