Necessity is the mother of invention, same happens here in case of docker. With the pressure of splitting monolithic applications for the purpose of ease, we arrived at docker and it made our life much simpler. We all access docker with docker-cli command but I wonder what it does behind the scene, to run a container. Let’s get deeper into it in this very blog.
There’s a saying that “Behind every successful man, there is a woman”. I would love to give my perspective on this. One of the things that I have actively observed from the life of successful people I know is that there is a lot of truth in this statement but it varies with different situations and in most of the cases these women are not directly helping men in their prime work but taking care of other important work around so that they can concentrate on their prime work. Keeping this in my mind, I am expecting that there are other components as well which are behind docker-cli command that leads to the successful creation of containers. Whenever I talk about docker containers with developers who are new to docker in my organization the only thing I hear from them is “docker-cli command is used to invoke docker daemon to run container”
But, Docker daemon is not the process that gets executed when a container is meant to be run – it delegates the action to containerd which then controls a list of runtimes (runc by default) which is then responsible for creating a new process (calling the defined runtime as specified in the configuration parameters) with some isolation and only then executing the entrypoint of that container.
Docker-cli: Used to make Docker API calls.
Dockerd: dockerd listens for Docker API requests, dockerd can listen for Docker Engine API requests via three different types of Socket: unix, tcp, and fd and manages host’s container life-cycles with the help of containerd. Hence, actual container life-cycle management is outsourced to containerd.
Containerd: Actually manages container life-cycle through the below mentioned tasks:
- Image push and pull
- Management of storage
- Of course executing containers by calling runc with the right parameters to run containers.
Let’s go through some subsystems of containerd :
Runc: Containerd uses RunC to run containers according to the OCI specification.
Containerd shim: With docker 1.11 version, this component has been added. This is the parent process of every container started and it also allows daemon-less containers. First it allows the runtimes, i.e. runc, to exit after which it starts the container. This way we don’t have to have the long running processes for containers. When you start nginx you should only see the nginx process and the shim.
Daemon-less: When I say daemon-less containers in the above paragraph, it means there is an advantage of this. When containerd shim was not there, upgrading docker daemon without restarting all your containers was a big pain. Hence, containerd shim got introduced to solve this problem.
The communication between Dockerd and ContainerD
We can see how docker delegates all the work of setting up the container to containerd. Regarding interactions between docker, containerd, and runc, we can understand that without even looking at the source code – plain strace and pstree can do the job.
when no containers running: ps fxa | grep docker -A 3
Working of all components together
Well, To see how all these components work together? We need to initialize a container i.e. Nginx in our case. We will be firing the same command after running an Nginx container.
This shows us that we have two daemons running – the docker daemon and the docker-containerd daemon.
Given that dockerd interacts heavily with containerd all the time and the later is never exposed to the internet, it makes sense to bet that its interface is unix-socket based.
High-Level overview of initializing container
Initializing container to see involvement of all components
docker run --name docker-nginx -p 80:80 -d nginx docker ps
pstree -pg | grep -e docker -e containerd
ps fxa | grep -i “docker” -A 3 | grep -v “java”
By now, it might be clear that dockerd is not only the single component involved while running a container. We got to know what all components are backing a running container beside dockerd and how they work together to manage the lifecycle of a container.
I hope we have a good understanding of the docker components involved. Now it’s time to see things practically on your own with commands discussed in this blog without mugging up theoretical concepts.
That’s all till next time, thanks for reading, I’d really appreciate your feedback, please leave your comment below if you guys have any feedback or any queries.
Happy Containerization !!