Docker and systemd

Saurav Rana
The Startup
Published in
8 min readNov 10, 2020

Hi there! Today we are going to see some great concepts in docker while learning to setup simple web server on Docker.So let’s jump into it.

What is Docker?

Docker is a set of platform as a service products that use OS-level virtualization to deliver software in packages called containers. Containers are isolated from one another and bundle their own software, libraries and configuration files; they can communicate with each other through well-defined channels

Why Docker?

Developing apps today requires so much more than writing code. Multiple languages, frameworks, architectures, and discontinuous interfaces between tools for each life cycle stage creates enormous complexity. Docker simplifies and accelerates your workflow, while giving developers the freedom to innovate with their choice of tools, application stacks, and deployment environments for each project.

Built for Developers, by Developers.
At Docker, we work to make the developers’ lives easier because we’re developers too. From building Docker tools that improve dev workflows, to creating industry standards for containerizing apps.

Let’s Start

First of all we need docker up and running in your system.I have already setup docker in my system so we can proceed.

We need a docker image to launch docker container.You can think that Container is the instance of a docker image and we can launch multiple containers from single image.

Put simply we can say it's simplest to think of a container as a running image, but this isn't quite accurate.

An image is really a template that can be turned into a container. To turn an image into a container, the Docker engine takes the image, adds a read-write filesystem on top and initializes various settings including network ports, container name, ID and resource limits.

  • To download an docker image
docker pull <image_name:version>
  • To list all the downloaded images
docker images 

I am using centos image for this tutorial.

Now I am making a directory to store webpages in my host machine (Redhat).

Then we go on to create a demo html file for our webserver inside host machine

Now we will launch the docker container from centos image.The command used is

docker run -it -v /webpages/:/var/www/html -p 8085:80 --name websrv centos

Now let’s understand what all this means.

  • -i is for interactive mode and -t is to get a terminal of the container.
  • -v option is for volume.It basically mount host system directory to docker container.Volumes are the preferred way to persist data in Docker containers and services. Some use cases for volumes include: Sharing data among multiple running containers. If you don’t explicitly create it, a volume is created the first time it is mounted into a container.
  • By default, when you create or run a container using docker create or docker run, it does not publish any of its ports to the outside world. To make a port available to services outside of Docker, or to Docker containers which are not connected to the container’s network, use the --publish or -p flag. This creates a firewall rule which maps a container port to a port on the Docker host to the outside world.-p 8080:80 Maps TCP port 80 in the container to port 8080 on the Docker host.
  • — name to give container a name

You can see container has been lauched and we get a terminal of the container which is centos.

Now let’s setup the webserver in it.These are basic container with very limited commands.So you will not find even common commands like clear or ifconfig.But we can install them manually

Now that we have that out of the way, we can proceed to main part.We are installing httpd package for webserver using

yum install httpd -y

Now let’s check if our webpage is there in /var/www/html

Now all that’s left is to start the webserver services but here’s a catch.Traditional methods like :

service start httpd

Or

systemctl start httpd

will not work here and we get an error.

There is a great reason why it doesn’t work.

It is because the Docker Model suggests that it is better to run a single service within a container. If you wanted to build an application that required an Apache service and a MariaDB database, you should generate two different containers.

Thus docker doesn’t need systemd which is tool for monitoring multiple services.Thus it is disabled by default and to enhance security and isolation to container and its service.But we can enable it which i will talk about later.

So now to start httpd service we need to know how actually systemd starts the services.

If you see Loaded block , it loads /usr/lib/systemd/system/httpd.service file.Now if we go into that file.

We can see it runs /usr/sbin/httpd file to start the services.Now we know how to start the service

This will start our services.Let’s check if our webserver works.To check we need host machine IP and port number we used to bind docker to host which is 8085.

IP of host machine

You can see it’s working fine.

This way we can install any package and run it.Lets try with python.

Now we create simple python program and run it.

This completes our basic tutorial of docker.

Enable systemd inside Docker

If we go back to what error we got while using systemctl and observe it.It says

System has not been booted with systemd as init system (PID 1). Can’t operate.

If we look at Process ID (PID) of our above container using ps command.

Here the process with PID 1 is bash.You must be wondering what does this PID 1 means?Process with PID 1 acts as the Entrypoint of the container or simply you can say it is the first process invoked by the kernel.In modern systems PID 1 is actively reserved for the init process but not in docker.

So here bash is having PID 1 but systemd needs to be run as PID 1 to work.But that not all.

systemd requires CAP_SYS_ADMIN capability but Docker drops that capability in the non privileged containers, in order to add more security. This means for now you have to run systemd within a privileged container since privileged containers do not drop any capabilities.But recent patches allow users to add capabilities to a docker container.

Now we can simply add CAP_SYS_ADMIN capability without running container in privileged mode.

But docker container will still fail because systemd insists on looking at the cgroup file system within a container.We can add cgroup file system to the container using the Volume mount

–v /sys/fs/cgroup:/sys/fs/cgroup:ro

which allows systemd to look at the cgroup but only in readonly mode.If your host system doesn’t has cgroup properly configured then you might face issue.

Now we have all the ingredients to enable systemd inside docker.We can do it by single command.

docker run -d -v /sys/fs/cgroup/:/sys/fs/cgroup:ro --cap-add SYS_ADMIN -p 8089:80 -v /webpages/:/var/www/html --name systemd_websrv centos /sbin/init

Here I used port 8089 of the host to differentiate it from above webserver running at 8085.

Here you can see the difference between two.

Now let’s see systemctl working inside docker.We can use this command to get a terminal of container.

docker exec -it systemd_websrv bash
Installed httpd

As you can see now systemctl is working.And our webserver is running too.

But by enabling systemd , systemd also enables extra services inside docker as you can see.So to stop this we can create a Dockerfile and can remove unit file links from the /lib/systemd/system/*wants/ and /etc/systemd/system/*wants/ directories within a systemd based docker container. Removing these links will lead to a systemd container image that would only run systemd and journald.

So that’s it for this tutorial.I know it’s a long one but I hope you got to learn a lot.

I would like to give credit to Daniel Walsh from Redhat whose article helped me to understand all these concepts.Here’s the link to his article.

Thanks for your time.

--

--

Saurav Rana
The Startup

When you want to know how things really work, study them when they’re coming apart.