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

144 lines
7.2 KiB
Markdown

---
title: "How I finally got rid of Docker"
date: 2023-11-11 21:56
last_modified_at: 2024-01-07 15:33
url: how-i-finally-got-rid-of-docker
layout: post
category: Security
image: /img/blog/how-i-finally-got-rid-of-docker_1.png
description: "Podman-Compose setup walkthrough on Debian 12"
---
[![A missing blog post image](/img/blog/how-i-finally-got-rid-of-docker_1.png)](/img/blog/how-i-finally-got-rid-of-docker_1.png)
### Introduction
[Docker Compose](https://github.com/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"](https://github.com/compose-spec/compose-spec/blob/master/spec.md), 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)](https://github.com/docker/compose/tree/v1#docker-compose), a binary which was actually a Python program built using [PyInstaller](https://pyinstaller.org/), and invoked by `docker-compose`. On Debian, [it is still packaged in main repositories](https://packages.debian.org/stable/docker-compose).
Since 2021 (and [mostly since last year](https://github.com/docker/compose/pull/9625)), _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 :stuck_out_tongue:), 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](https://github.com/containers/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"](https://www.freedesktop.org/software/systemd/man/latest/loginctl.html#enable-linger%20USER%E2%80%A6) 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 %}
> :warning: [Podman-Compose is packaged in Debian main repositories](https://packages.debian.org/stable/podman-compose), but (already) pretty old so you'll understand why [we go through PyPI to retrieve an up-to-date version](https://pypi.org/project/podman-compose/).
As we like doing clean things and we actually don't wanna [break our system](https://peps.python.org/pep-0668/#motivation), 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](https://github.com/compose-spec/compose-spec/blob/master/spec.md#restart) 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`](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html#Restart=) policy feature), [there is no such thing (yet ?)](https://github.com/containers/podman-compose/issues/254).
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`](https://docs.podman.io/en/stable/markdown/podman-run.1.html#restart-policy) 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 :clown_face:
This issue [has already been mentioned](https://github.com/containers/podman/issues/10539#issuecomment-900992521) 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](/img/blog/how-i-finally-got-rid-of-docker_2.png)](/img/blog/how-i-finally-got-rid-of-docker_2.png)