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:

bash
docker 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?

bash
docker 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

FeatureDocker DesktopDocker Engine
PlatformmacOS, Windows, LinuxLinux only
GUIYesNo (CLI only)
VMRuns Linux VM (macOS/Win)Native
KubernetesBuilt-in (optional)Separate install
LicenseFree for personal, paid for enterpriseFree & open source
ComponentsEngine + Compose + BuildKit + K8sEngine 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:

  1. "Walk me through what happens when you run docker run nginx"
  2. "What's the relationship between Docker daemon, containerd, and runc?"
  3. "How does a Docker container differ from a process?"
  4. "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

Official Links