Wednesday, January 8, 2020

Testing Systemd Services with Docker-Compose

I've been using Docker containers to test out the install process for a project I've been working on (Furem Cape), and have found it can be a little tricky to get systemd booted up and running in Docker. Normally running a service manager like systemd within a container would be redundant and unnecessary, but in this case I'm specifically trying to test out systemd service files (and directory paths, and user permissions, etc) that have been set up by my install process.

With the base 18.04 Ubuntu image, there are 4 key steps to getting systemd running and testable in a Docker container:

  1. Install systemd
  2. Map a few key system directories
  3. Start up with /sbin/init
  4. Use docker exec to test

1. Install systemd

With the base Ubuntu image, it's as simple as installing the systemd package with apt — like this Dockerfile:

FROM ubuntu:18.04 ENV DEBIAN_FRONTEND noninteractive RUN apt update && apt install -y systemd CMD ["/sbin/init"]

2. Map a few key system directories

I found I had to mount /run and /run/lock as tmpfs directories and map /sys/fs/cgroup to my local /sys/fs/cgroup directory. You can do that with this docker-compose.yml file:

version: '3' services: my_test_container: build: . image: my_test_image tmpfs: - /run - /run/lock volumes: - /sys/fs/cgroup:/sys/fs/cgroup:ro

Or, alternately, when using the docker run command, specifying the --tmpfs /run --tmpfs /run/lock --volume /sys/fs/cgroup:/sys/fs/cgroup:ro flags.

3. Start up with /sbin/init

I added CMD ["/sbin/init"] to my Dockerfile to start the container with /sbin/init by default; but you can instead add command: /sbin/init to a service in your docker-compose.yml file, or just run /sbin/init directly with the docker run command.

4. Use docker exec to test

With the above docker-compose.yml and Dockerfile, you can start up the test container with one command:

docker-compose up -d my_test_container

And then, with systemd running, use a second command to execute a shell on the container to test it out:

docker-compose exec my_test_container /bin/bash

Or use exec to run whatever other commands you need to test systemd:

docker-compose exec my_test_container systemctl list-units

Alternately, if you built and ran the above Dockerfile with docker commands instead of docker-compose, you'd use the following command to test the container out:

docker exec -it my_test_container /bin/bash

Cleaning up

To clean everything up, stop the container with docker-compose:

docker-compose stop my_test_container

Then remove the container:

docker-compose rm my_test_container

And finally, remove the image:

docker image rm my_test_image

Or execute all 3 clean-up steps at once (as well as removing all other containers/images referenced by your docker-compose.yml file), in a single command:

docker-compose down --rmi all

Without docker-compose

The following docker commands would allow you to build, run, and clean up the above Dockerfile without using docker-compose at all:

docker build --tag my_test_image . docker run \ --tmpfs /run --tmpfs /run/lock \ --volume /sys/fs/cgroup:/sys/fs/cgroup:ro \ --detach --rm \ --name my_test_container my_test_image docker exec --interactive --tty my_test_container /bin/bash docker stop my_test_container docker image rm my_test_image