Docker Architecture
The Client/Server Model
At its core, Docker's architecture is a simple client/server model. This consists of three main components: the Docker Client, the Docker Server (or Daemon), and the REST API that connects them.
1. The Docker Client (docker)
The Docker client is the docker command-line tool. It is the primary interface for users to interact with Docker.
Function: The client's job is to accept commands from the user (e.g.,
docker container run,docker image build) and send those instructions to the Docker daemon for execution.Implementation: The client is a Go program that can run on any major operating system (Linux, macOS, Windows).
Flexibility: A single client can be configured to communicate with multiple daemons, allowing you to manage several Docker hosts from one terminal.
2. The Docker Server / Daemon (dockerd)
The Docker server is the dockerd (Docker daemon) command. This is a persistent background process that acts as the "engine" of the Docker platform.
Function: The daemon performs all the heavy lifting. It listens for API requests from the Docker client and manages all Docker objects, including:
Building, running, and managing containers.
Storing and managing images.
Creating and managing volumes and networks.
Host System: On a Linux system,
dockerdruns natively. On macOS or Windows, the Docker Desktop application runsdockerdinside a lightweight Linux virtual machine.Configuration: The daemon's configuration is typically managed in a JSON file located at
/etc/docker/daemon.json.
3. The Docker Engine API (Communication)
The client and daemon do not communicate directly. They speak to each other using the Docker Engine API, which is a public, documented REST API. The docker client is simply an HTTP client for this API.
This design allows any third-party tool to programmatically control the Docker daemon, leading to a rich ecosystem of custom tooling.
Communication Channels:
Unix Sockets (Default): By default,
dockerdlistens on a Unix socket (e.g.,/var/run/docker.sock). This is the most secure method as it requires local root ordockergroup permissions to access.Network Ports (Optional): The daemon can also be configured to listen on a network port, allowing for remote management.
Port 2375: Unencrypted traffic.
Port 2376: Encrypted TLS (SSL) connections.
Port 2377: for Docker Swarm mode.
Security Warning: Exposing the daemon over an unencrypted network port is a significant security risk, as it lacks built-in user authentication and role-based access controls (RBAC), effectively granting root-level access to the host.
Core Components and Runtimes
When the dockerd daemon receives a command like "run a container," it does not execute the container itself. Instead, it orchestrates a stack of more specialized, lower-level components. This separation of concerns follows the OCI standard and allows parts of the stack to be replaced.
The flow is: dockerd ➔ containerd ➔ runc
dockerd(The Daemon): Serves the high-level Docker API. It handles user-facing tasks like managing volumes, networking, logging, and receiving images. When it's time to run a container, it makes a gRPC API call tocontainerd.containerd(The Lifecycle Manager): This is a separate daemon thatdockerdorchestrates.containerdis responsible for the entire container lifecycle. Its jobs include:Managing the container's copy-on-write (CoW) filesystem.
Handling low-level networking.
Pushing and pulling images.
Calling
runcto execute the container.containerdwas donated by Docker to the Cloud Native Computing Foundation (CNCF) and is the default runtime used by Kubernetes.
runc(The Low-Level Runtime): This is the OCI-compliant, command-line tool thatcontainerdexecutes.runc's job is singular: it reads the container's configuration (JSON file), creates the container using the kernel features (namespaces and cgroups), and executes the application process inside it.containerd-shim-runc-v2(The Shim): Whenruncstarts a container, it exits. To handle the running container,containerdlaunches a "shim" process. There is one shim per container. This shim:Handles the container's standard file descriptors (STDIN, STDOUT, STDERR).
Reports the container's exit status back to
containerd.Allows
runcandcontainerdto be upgraded without disrupting running containers.
docker-proxy(The Port Forwarder): This is a user-space process managed bydockerd. When you publish a port (e.g.,docker run -p 8080:80),docker-proxyis created to listen on the host's port (8080) and forward traffic (TCP/UDP) to the correct container's private IP and port (80).
The Kernel Foundations: Isolation Mechanisms
Containers are not magic. They are a product of specific Linux kernel features that isolate processes and manage resources. runc is the component that configures these features.
Linux Namespaces (Isolation): Namespaces are the primary isolation feature. They provide a container with its own view of the system, making it appear to have its own dedicated resources. The key namespaces are:
pid(Process ID): The container gets its own process tree. Process #1 inside the container is the application, not the host'sinitsystem.net(Network): The container gets its own network stack (IP address, routing table, port list).mnt(Mount): The container gets its own set of filesystem mount points, including its root filesystem.uts(UNIX Time-Sharing): The container gets its own hostname and domain name.ipc(Inter-Process Communication): Isolates shared memory and semaphores.user(User ID): Maps user and group IDs, allowing a root user inside the container to be mapped to a non-privileged user on the host.
Control Groups (cgroups) (Resource Limiting): Cgroups are the resource management feature. While namespaces isolate what a process can see, cgroups limit how much it can use. This is critical for preventing a single container from consuming all host resources (a "noisy neighbor" problem). Cgroups manage:
CPU: Throttling or assigning specific CPU shares.
Memory: Enforcing hard or soft limits on memory usage.
I/O: Limiting read/write speed to block devices.
Network: Tagging packets for traffic shaping.
Other Features: Docker also leverages other kernel and OS features, such as:
iptables/nftables: Used to create network address translation (NAT) rules for port forwarding and to manage network policies.Virtual Bridging: Creates the virtual
docker0bridge for default container networking.Linux Capabilities: Divides the "root" user's power into distinct privileges. Docker drops most capabilities by default, so even if a process runs as root inside a container, it cannot perform damaging system-level actions (like loading kernel modules) on the host.
Filesystem Drivers (e.g., OverlayFS): These drivers enable the layered filesystem that makes images efficient.
The Consequence: Limited Isolation
This architecture, which relies on a shared kernel, is the source of the greatest security concern regarding containers.
Contrast with VMs: A Virtual Machine provides strong isolation because it runs its own kernel on top of virtualized hardware. A vulnerability in one VM's kernel is unlikely to affect the host or other VMs.
Container Isolation: In Docker, all containers share the same host kernel. A kernel vulnerability or a simple misconfiguration can potentially allow a container to "escape" its isolation and gain unauthorized access to the host's system, files, and other containers.
The Root User Risk: By default, processes in a container run as
UID 0(root). While namespaces and capabilities limit this user's power, it is not a perfect defense. A vulnerability in a kernel-exposed system call could be exploited by this root user. This is why running containers as a non-root user (USERinstruction in a Dockerfile) is a critical security best practice.Resource Competition: By default, containers share host CPU and memory. Without cgroup limits, they can compete for these resources, just like any other processes on the host.
In summary, containerized applications are more secure than non-containerized applications due to the isolation provided by namespaces and cgroups. However, they are less isolated than virtual machines due to the shared kernel. Stronger isolation can be achieved by applying security systems like SELinux or AppArmor policies.
