Chapter 7. Running containers as systemd services with Podman
Podman (Pod Manager) is a fully featured container engine that is a simple daemonless tool. Podman provides a Docker-CLI comparable command line that eases the transition from other container engines and allows the management of pods, containers and images. It was not originally designed to bring up an entire Linux system or manage services for such things as start-up order, dependency checking, and failed service recovery. That is the job of a full-blown initialization system like systemd.
Red Hat has become a leader in integrating containers with systemd, so that OCI and Docker-formatted containers built by Podman can be managed in the same way that other services and features are managed in a Linux system. This chapter describes how you can use the systemd initialization service to work with containers in two different ways:
- Starting Containers with systemd: By setting up a systemd unit file on your host computer, you can have the host automatically start, stop, check the status, and otherwise manage a container as a systemd service.
- Starting services within a container using systemd: Many Linux services (Web servers, file servers, database servers, and so on) are already packaged for Red Hat Enterprise Linux to run as systemd services. If you are using the latest RHEL container image, you can set the RHEL container image to start the systemd service, then automatically start selected services within the container when the container starts up.
The following two sections describe how to use systemd container in those ways.
7.1. Starting containers with systemd
When you set up a container to start as a systemd service, you can define the order in which the containerized service runs, check for dependencies (like making sure another service is running, a file is available or a resource is mounted), and even have a container start by using the
This section provides an example of a container that is configured to run directly on a RHEL system as a systemd service. To learn about automatic generation of systemd service file, see Generate systemd unit file.
Get the image you want to run on your system. For example, to use a minimal image based on Alpine Linux from docker.io, run the following command:
# podman pull docker.io/library/alpine:latest
Configure the container as a systemd service by creating the generic unit configuration file in the
~/.config/systemd/userdirectory. For example, the contents of the
~/.config/systemd/user/container.servicecan look as follows:
# cat ~/.config/systemd/user/container.service [Unit] Description=Podman in Systemd [Service] Restart=on-failure ExecStartPre=/usr/bin/rm -f /%t/%n-pid /%t/%n-cid ExecStart=/usr/bin/podman run --conmon-pidfile /%t/%n-pid --cidfile /%t/%n-cid -d alpine:latest top ExecStop=/usr/bin/sh -c "/usr/bin/podman rm -f `cat /%t/%n-cid`" KillMode=none Type=forking PIDFile=/%t/%n-pid [Install] WantedBy=multi-user.target
Restart=on-failureline sets the restart policy and instructs systemd to restart the service when it cannot be started or stopped cleanly, or when the process exits with a non-zero status.
ExecStartline describes how we start the container.
ExecStopline describes how we stop and remove the container.
You can run an
alpine:latestcontainer in the background that runs
podman runcommand includes two command-line options:
--conmon-pidfileoption points to a path to store the process ID for the
conmonprocess running on the host. The
conmonprocess terminates with the same exit status as the container, which allows
systemdto report the correct service status and restart the container if needed.
--cidfileoption points to the path that stores the container ID.
%tis the path to the run time directory root, for example
%nis the full name of the service.
For example, if the service name is
containerand the user ID is 1000, the above configuration places the
To reload systemd manager configuration, type:
# systemctl --user daemon-reload
To enable the service and start it at boot time, type:
# systemctl --user enable container.service
To start the service immediately and check the status of the service, type the following:
# systemctl --user start container.service # systemctl --user status container.service * container.service - Podman in Systemd Loaded: loaded (/home/valentin/.config/systemd/user/container.service; disabled; vendor preset: enabled) Active: active (running) since Mon 2019-11-18 15:32:56 CET; 1min 5s ago Process: 189705 ExecStartPre=/usr/bin/rm -f //run/user/1000/container.service-pid //run/user/1000/container.service-cid (code=exited, status=0/SUCCESS) Process: 189706 ExecStart=/usr/bin/podman run --conmon-pidfile //run/user/1000/container.service-pid --cidfile //run/user/1000/container.service-cid -d alpine:latest top (code=exited, status=0/SUCCESS) Main PID: 189731 (conmon) CGroup: /email@example.com/container.service ├─189724 /usr/bin/fuse-overlayfs [...] ├─189726 /usr/bin/slirp4netns [...] ├─189731 /usr/bin/conmon [...] └─189737 top
To list containers that are running or have exited, type:
# podman ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f20988d59920 docker.io/library/alpine:latest top 12 seconds ago Up 11 seconds ago funny_zhukovsky
# systemctl --user stop container.service
To learn more about configuring services with systemd, refer to the System Administrator’s Guide chapter called Managing Services with systemd and article Running containers with Podman and shareable systemd services.
7.2. Starting services within a container using systemd
A package with the systemd initialization system is included in the official Red Hat Enterprise Linux Init base image named registry.access.redhat.com/ubi8/ubi-init This means that applications created to be managed with systemd can be started and managed inside a container. A container running systemd will:
- Start the /sbin/init process (the systemd service) to run as PID 1 within the container.
- Start all systemd services that are installed and enabled within the container, in order of dependencies.
- Allow systemd to restart services or kill zombie processes for services started within the container.
The general steps for building a container that is ready to be used as a systemd services is:
- Install the package containing the systemd-enabled service inside the container. This can include dozens of services that come with RHEL, such as Apache Web Server (httpd), FTP server (vsftpd), Proxy server (squid), and many others. For this example, we simply install an Apache (httpd) Web server.
- The httpd and vsftpd packages are included in the UBI repositories. You would need a RHEL subscription to install the squid package.
- Use the systemctl command to enable the service inside the container.
- Add data for the service to use in the container (in this example, we add a Web server test page). For a real deployment, you would probably connect to outside storage.
- Expose any ports needed to access the service.
In this example, we build a container by creating a Dockerfile that installs and configures a Web server (httpd) to start automatically by the systemd service (/sbin/init) when the container is run on a host system.
Create Dockerfile: In a separate directory, create a file named Dockerfile with the following contents:
FROM registry.access.redhat.com/ubi8/ubi-init RUN yum -y install httpd; yum clean all; systemctl enable httpd; RUN echo "Successful Web Server Test" > /var/www/html/index.html RUN mkdir /etc/systemd/system/httpd.service.d/; echo -e '[Service]\nRestart=always' > /etc/systemd/system/httpd.service.d/httpd.conf EXPOSE 80
The Dockerfile installs the httpd package, enables the httpd service to start at boot time (i.e. when the container starts), creates a test file (index.html), exposes the Web server to the host (port 80), and starts the systemd init service (/sbin/init) when the container starts.
Build the container: From the directory containing the Dockerfile, type the following:
# podman build -t mysysd .
Open Selinux permission. If SELinux is enabled on your system, you must turn on the
container_manage_cgroupboolean to run containers with systemd as shown here (see the Containers running systemd solution for details):
# setsebool -P container_manage_cgroup 1
Run the container: Once the container is built and named mysysd, type the following to run the container:
# podman run -d --name=mysysd_run -p 80:80 mysysd
From this command, the mysysd image runs as the mysysd_run container as a daemon process, with port 80 from the container exposed to port 80 on the host system.
Check that the container is running: To make sure that the container is running and that the service is working, type the following commands:
# podman ps | grep mysysd_run a282b0c2ad3d localhost/mysysd:latest /sbin/init 15 seconds ago Up 14 seconds ago 0.0.0.0:80->80/tcp mysysd_run # curl localhost/index.html Successful Web Server Test
At this point, you have a container that starts up a Web server as a systemd service inside the container. Install and run any services you like in this same way by modifying the Dockerfile and configuring data and opening ports as appropriate.