02 - Docker Architecture
The Big Picture
Docker uses a client-server architecture:
┌──────────────┐ ┌───────────────────────────────────┐
│ Docker CLI │ REST │ Docker Daemon (dockerd) │
│ (client) │◄───────►│ │
│ │ API │ ┌─────────┐ ┌────────────────┐ │
│ docker build │ │ │ Images │ │ Containers │ │
│ docker run │ │ └─────────┘ └────────────────┘ │
│ docker pull │ │ ┌─────────┐ ┌────────────────┐ │
│ │ │ │ Volumes │ │ Networks │ │
└──────────────┘ │ └─────────┘ └────────────────┘ │
│ │
┌──────────┐ │ ┌──────────────────────────┐ │
│ Registry │◄────►│ │ containerd │ │
│(Hub etc.)│ │ │ ┌──────┐ ┌──────┐ │ │
└──────────┘ │ │ │ runc │ │ runc │ ... │ │
│ │ └──────┘ └──────┘ │ │
│ └──────────────────────────┘ │
└───────────────────────────────────┘
Core Components
1. Docker Client (docker)
The CLI tool you interact with:
bashdocker build . # Sends build context to daemon docker run nginx # Tells daemon to start a container docker ps # Asks daemon for running containers
The client communicates with the daemon via:
- Unix socket:
/var/run/docker.sock(default, local) - TCP:
tcp://host:2376(remote, TLS) - SSH:
ssh://user@host(remote, secure)
bash# Default: talks to local daemon docker ps # Talk to a remote daemon docker -H tcp://remote-host:2376 ps # Using Docker context (modern way) docker context create remote --docker "host=ssh://user@remote" docker context use remote
2. Docker Daemon (dockerd)
The background service that manages everything:
- Builds images
- Runs containers
- Manages networks and volumes
- Pulls/pushes images to registries
- Exposes the REST API
bash# Check daemon status systemctl status docker # Daemon config file cat /etc/docker/daemon.json
Example daemon configuration:
json{ "storage-driver": "overlay2", "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" }, "default-address-pools": [ {"base": "172.80.0.0/16", "size": 24} ], "dns": ["8.8.8.8", "8.8.4.4"], "live-restore": true, "userland-proxy": false }
3. containerd
The high-level container runtime:
- Manages the complete container lifecycle
- Image transfer and storage
- Container execution (delegates to runc)
- Supervises running containers
- Network interfaces (via CNI plugins)
bash# containerd has its own CLI: ctr ctr images list ctr containers list # Or use nerdctl (Docker-compatible CLI for containerd) nerdctl run -d nginx
4. runc
The low-level OCI runtime:
- Actually creates and runs containers
- Sets up namespaces, cgroups, etc.
- Spawns the container process
- Exits after container starts (containerd takes over supervision)
5. Docker Registry
Where images are stored and distributed:
bash# Default registry: Docker Hub docker pull nginx # → docker.io/library/nginx:latest docker pull ubuntu:22.04 # → docker.io/library/ubuntu:22.04 # Private registries docker pull gcr.io/my-project/my-app:v1 # Google Container Registry docker pull 123456.dkr.ecr.us-east-1.amazonaws.com/my-app # AWS ECR docker pull ghcr.io/owner/image:tag # GitHub Container Registry
What Happens When You Run docker run?
bashdocker run -d -p 8080:80 --name web nginx
Step by step:
1. CLI parses command, sends to daemon via REST API
POST /containers/create { Image: "nginx", ... }
2. Daemon checks if image exists locally
→ If not: pulls from registry (docker.io/library/nginx:latest)
3. Daemon asks containerd to create the container
→ containerd prepares the container bundle (rootfs + config)
4. containerd asks runc to create + start the container
→ runc sets up namespaces (PID, NET, MNT, etc.)
→ runc sets up cgroups (resource limits)
→ runc pivots root to container filesystem
→ runc starts the entrypoint process (nginx)
→ runc exits (containerd supervises from here)
5. Daemon sets up networking
→ Creates veth pair (virtual ethernet)
→ Connects container to bridge network
→ Sets up port mapping (8080 → 80) via iptables
6. Container is running!
→ nginx listens on port 80 inside the container
→ Host port 8080 forwards to container port 80
Docker Objects
Images
- Read-only templates for creating containers
- Built from a Dockerfile
- Composed of layers (each instruction = one layer)
- Stored in registries
Containers
- Runnable instances of images
- Isolated processes with their own filesystem, network, PID space
- Can be started, stopped, moved, deleted
- Changes are stored in a writable layer on top of the image
Volumes
- Persistent data storage outside the container lifecycle
- Managed by Docker
- Survive container removal
Networks
- Enable communication between containers
- Multiple network drivers (bridge, host, overlay, etc.)
Docker Desktop vs Docker Engine
| Feature | Docker Desktop | Docker Engine |
|---|---|---|
| Platform | macOS, Windows, Linux | Linux only |
| GUI | Yes | No (CLI only) |
| VM | Runs Linux VM (macOS/Win) | Native |
| Kubernetes | Built-in (optional) | Separate install |
| License | Free for personal, paid for enterprise | Free & open source |
| Components | Engine + Compose + BuildKit + K8s | Engine only |
bash# On macOS, Docker Desktop runs a Linux VM: docker info | grep "Operating System" # Output: Operating System: Docker Desktop (uses LinuxKit VM) # On Linux, Docker Engine runs natively: # Output: Operating System: Ubuntu 22.04.3 LTS
The Docker Socket
The Docker socket (/var/run/docker.sock) is critical to understand:
bash# The CLI uses it to talk to the daemon curl --unix-socket /var/run/docker.sock http://localhost/containers/json | jq # Mounting the socket in a container gives it control over Docker # (used in CI/CD but has security implications!) docker run -v /var/run/docker.sock:/var/run/docker.sock docker:cli docker ps
Security Warning: Mounting the Docker socket in a container effectively gives it root access to the host. Use with extreme caution.
BuildKit
Modern Docker build engine (default since Docker 23.0):
bash# BuildKit features DOCKER_BUILDKIT=1 docker build . # Benefits over legacy builder: # - Parallel build stages # - Better caching # - Build secrets (not stored in image) # - SSH forwarding during build # - Smaller build context transfer
FAANG Interview Angle
Common questions:
- "Walk me through what happens when you run
docker run nginx" - "What's the relationship between Docker daemon, containerd, and runc?"
- "How does a Docker container differ from a process?"
- "What security risks does the Docker socket present?"
Key points:
- Client-server architecture with REST API
- containerd is the actual runtime, Docker is the management layer
- runc creates the container, then exits
- Docker socket = root-equivalent access