Understanding Container Architecture
Introduction
Today, I explored how Docker Daemon (dockerd
), Docker Socket (docker.socket
), and Container Runtime (containerd
) interact. I also tested what happens when stopping Docker services and learned some interesting behaviors. Let's dive into it!
Docker's Key Components
Before jumping into my experiments, here's a quick breakdown of key Docker components:
dockerd (Docker Daemon): This is the core service responsible for managing containers, images, networks, and volumes. It listens for requests from the Docker CLI and handles the overall lifecycle of containers.
docker.socket: A systemd-managed socket that listens for Docker CLI API requests. If dockerd is not running, docker.socket can automatically start it. It's an essential part of Docker's client-server architecture.
containerd: This is an industry-standard core container runtime that manages the complete container lifecycle, including image management, container execution, and supervision of containerized applications. It operates at a lower level compared to dockerd and is used by Docker to handle containers at runtime.
containerd-shim: This lightweight process is responsible for managing container lifecycles. It ensures containers remain running even if containerd itself stops, facilitating detached execution of containers. It’s part of the reason Docker containers can persist even when containerd restarts.
runc: The low-level runtime that interacts directly with the Linux kernel. runc implements the Open Container Initiative (OCI) runtime specifications, setting up namespaces, cgroups, and other container isolation mechanisms. It is the key component that actually creates and runs the containers.
libcontainer: This is the earlier Go library used by Docker to create containers. It was refactored into runc when Docker started conforming to OCI standards. It provides the core functions of container creation and isolation.
OCI (Open Container Initiative): This set of open standards ensures interoperability between different container runtimes and images, allowing you to use containers across various platforms and runtimes.
What Happens When You Stop dockerd
?
I ran the following command to stop Docker Daemon (dockerd
):
sudo systemctl stop docker
Then, I checked its status:
sudo systemctl status docker
Observations:
docker.service
(dockerd) was stopped.However,
docker.socket
was still active!Running
docker ps
worked, The reason?docker.socket
was still active, so when I randocker ps
, it automatically restarteddockerd
in the background.
Fully Stopping Docker (Including docker.socket
)
To completely stop Docker, I ran:
sudo systemctl stop docker.socket
Now, running docker run busybox
gave an error:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
This confirmed that without docker.socket
, dockerd
could not restart automatically.
The Role of containerd, containerd-shim, runc, and libcontainer
While experimenting with Docker, I also explored how containerd, containerd-shim, runc, and libcontainer play key roles in container lifecycle management.
containerd:
- After dockerd starts a container, containerd takes over. It handles lower-level operations like running and managing the container's lifecycle, including pulling images, creating containers, and managing namespaces and cgroups.
containerd-shim:
- When containerd manages the containers, it uses the containerd-shim to ensure that containers continue running independently of containerd's process. This allows containers to persist even if containerd itself stops.
runc:
- runc is invoked by containerd to create and run containers. It interacts with the Linux kernel to set up the necessary namespaces and cgroups to isolate the container. When containerd needs to run a container, it uses runc to actually launch it in the specified environment.
libcontainer:
- Although libcontainer is no longer used directly in most modern setups, its concepts are the foundation of runc and containerd. It was the original implementation used by Docker to handle container creation and isolation. Today, it is replaced by runc, which conforms to the OCI standards.
Even if containerd
is stopped, any existing containers continue running because they are managed by containerd-shim
, which keeps the container alive. However, you won't be able to start new containers through Docker (dockerd
), as it relies on containerd
to create and manage containers. Instead, you can interact directly with containerd
using the ctr
CLI, which allows you to manage containers even when dockerd
is not functional. Alternatives for dockerd
are podman, rkt, etc
Conclusion
Through this exploration, I learned that Docker relies on a series of components working together to manage container lifecycles and ensure smooth operation. Stopping dockerd doesn't completely stop Docker—docker.socket ensures that it can be restarted automatically when needed. I also explored how containerd, containerd-shim, runc, and libcontainer fit into the container ecosystem, each handling different aspects of container management, from execution to isolation.
By understanding the interactions between these components, you can gain deeper insight into how containers work and how Docker orchestrates everything behind the scenes.