blog/_posts/2023-11-11-how-i-finally-go...

7.2 KiB

title date last_modified_at url layout category image description
How I finally got rid of Docker 2023-11-11 21:56 2024-01-07 15:33 how-i-finally-got-rid-of-docker post Security /img/blog/how-i-finally-got-rid-of-docker_1.png Podman-Compose setup walkthrough on Debian 12

A missing blog post image

Introduction

Docker Compose has been a regular way of distributing/running container stacks to deploy services for many years now. What is actually interesting is the "Compose spec", which mainly defines static representation(s) (it used to be multiple versions of it) of these stacks (i.e. how different services share resources [or not], and "work" together).

For this second post of this "Podman series" (also see "[Podman rootless in Podman rootless, the Debian way]({% post_url 2023-09-17-podman-rootless-in-podman-rootless-the-debian-way %})"), I will explain how I replaced Docker-Compose (and hence Docker itself) by Podman-Compose for some personal services.

From Docker-Compose to Docker Compose

Historically, Composing with Docker was achieved by using Docker-Compose (v1), a binary which was actually a Python program built using PyInstaller, and invoked by docker-compose. On Debian, it is still packaged in main repositories.

Since 2021 (and mostly since last year), Composing is now achieved using Docker Compose (v2), a Go plugin for the docker (client) CLI.
On Debian, once Docker repository has been added, setup is pretty straightforward : apt install docker-ce docker-compose-plugin.
Invocation is then done by docker compose (please note the space instead of the dash).

In either case, a docker-compose.yml file is usually downloaded according to vendor recommendations (or carefully handcrafted when missing 😛), and modulo a docker[-]compose up -d invocation, you're all set and running.

Composing with Podman

Whereas Podman has been built as a "drop-in replacement" for Docker (despite internals being fundamentally different), Composing was initially out-of-scope (or very partially supported). But it's 2023, and Podman-Compose is now a robust alternative.

Within this containers ecosystem where (almost) everything is written in Go, please welcome Python again (and mind the coming back of the dash !).

Podman-Compose implements the Compose spec, while still being oriented "rootless" (after all, it's what we expect from Podman, isn't it ?) and "daemon-less".

Podman-Compose setup for Debian 12 (Bookworm)

First we create a system user dedicated to Podman execution :

{% highlight bash %} adduser --system namdop --group --home /opt/namdop {% endhighlight %}

Unlike Docker, there is no such thing as a (privileged) daemon running and waiting for us to give commands.
So we must enable systemd-logind "lingering" for our system user to make systemd start (in user mode) on machine boot, and thus provide a proper runtime for OOTB Podman execution :

{% highlight bash %} loginctl enable-linger namdop {% endhighlight %}

Now we can install Podman and deal with Debian (undesired) pulled dependencies :

{% highlight bash %} apt-get install -y --no-install-recommends
podman
slirp4netns
uidmap
golang-github-containernetworking-plugin-dnsname

systemctl --global disable --now
dirmngr.socket
gpg-agent.socket
gpg-agent-browser.socket
gpg-agent-extra.socket
gpg-agent-ssh.socket {% endhighlight %}

We then define our ranges of UIDs/GIDs dedicated to our system user :

{% highlight bash %} echo "namdop:100000:65536" > /etc/subuid echo "namdop:100000:65536" > /etc/subgid {% endhighlight %}

⚠️ Podman-Compose is packaged in Debian main repositories, but (already) pretty old so you'll understand why we go through PyPI to retrieve an up-to-date version.

As we like doing clean things and we actually don't wanna break our system, we will install podman-compose in a Python virtual environment :

{% highlight bash %} apt-get install -y python3-venv {% endhighlight %}

Now we can finalize the setup as our regular system user :

{% highlight bash %} su - namdop -s /bin/bash

python3 -m venv venv && source venv/bin/activate pip3 install -U pip wheel

pip3 install podman-compose

mkdir my_service && cd my_service/

Here you can setup your docker-compose.yml and required configuration/data, as you would do with Docker !

...

podman-compose up -d {% endhighlight %}

The restart policy pitfall

Sooo, as you may already know, containers do not start on boot by default. This behavior depends on the --restart={always,no,on-failure[:max-retries],unless-stopped} flag, that you can should specify when creating a container (or through the restart key in a Compose file).

Unlike for "standalone" Podman where we could run podman generate systemd <container> to make a container actually managed by systemd as a regular "service" (and thus enjoy the Restart policy feature), there is no such thing (yet ?).

As a workaround, we can rely on the (global) systemd service unit (called podman-restart.service), responsible for restarting containers on boot, that we must enable in our (user mode) systemd runtime :

{% highlight bash %} XDG_RUNTIME_DIR="/run/user/$(id -u)" systemctl --user enable --now podman-restart.service {% endhighlight %}

Whereas unless-stopped is supposed to be identical to always with Podman, packaging hits us hard and that's unfortunate.
podman-restart.service unit specifies --filter=always to only (re)start containers that should be.

So you must add :

{% highlight yaml %} services: service: # ... restart: always # ... {% endhighlight %}

... to your Compose stacks to enjoy containers auto-restart, or you will have to tweak the upstream systemd unit 🤡

This issue has already been mentioned two years ago by @plevart, but it has not been fixed yet.

Conclusion

{% highlight bash %} apt-get autoremove --purge docker-ce docker-compose-plugin

rm -f /etc/apt/keyrings/docker.gpg rm -f /etc/apt/sources.list.d/docker.list apt-get update {% endhighlight %}

A missing blog post image