Unix: add podman quadlet blog
This commit is contained in:
parent
13136e302d
commit
b6af976671
1 changed files with 215 additions and 0 deletions
215
src/content/unix/docker-compose-to-podman-quadlet.md
Normal file
215
src/content/unix/docker-compose-to-podman-quadlet.md
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
---
|
||||||
|
title: 'Docker -> Podman + Quadlet'
|
||||||
|
description: 'Moving from Docker Compose to Podman with Quadlet'
|
||||||
|
updateDate: 'Nov 17 2024'
|
||||||
|
---
|
||||||
|
|
||||||
|
Containers are light-weight virtual machines, often used to run isolated
|
||||||
|
services on a server. They (typically) use the host kernel and don't virtualise
|
||||||
|
resources, leading to higher performance with minimal overhead compared to
|
||||||
|
traditional virtual machines (like qemu).
|
||||||
|
|
||||||
|
[Docker](https://en.wikipedia.org/wiki/Docker_(software)) is the most widely
|
||||||
|
used container and has been at the head of the industry for a while. It uses a
|
||||||
|
daemon run by the root user to start its containers. This has long been a
|
||||||
|
security concern, as a malicious entity that [breaks out of the container](
|
||||||
|
https://pwning.systems/posts/escaping-containers-for-fun/) will have root access
|
||||||
|
to the system!
|
||||||
|
|
||||||
|
[Podman](https://www.redhat.com/en/topics/containers/what-is-podman), a tool
|
||||||
|
developed by Redhat, is quite similar to docker but crucially doesn't require a
|
||||||
|
daemon to run. This means unprivileged users can still get the benefits of
|
||||||
|
containerization! Docker has recently been experimenting with a similar
|
||||||
|
["rootless mode"](https://docs.docker.com/engine/security/rootless/), but it's
|
||||||
|
not nearly at feature parity. Further, since Podman is developed by Redhat, it
|
||||||
|
integrates with other Redhat projects very nicely, like systemd,
|
||||||
|
[nftables](https://wiki.archlinux.org/title/Nftables), and
|
||||||
|
[cockpit](https://wiki.archlinux.org/title/Cockpit).
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
In this blog, we'll look at migrating an existing Docker Compose service to
|
||||||
|
Podman! We'll cover setting up our new Podman service with systemd's Quadlet
|
||||||
|
integration with a new unprivileged user.
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
- A computer with linux (root access required, if you want to make a new user)
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
- Root access, if you'd like to make a new user
|
||||||
|
- An existing Docker Compose service. You can use the one in this tutorial too
|
||||||
|
|
||||||
|
I'm using archlinux for this example, but any systemd-based linux will work
|
||||||
|
(Ubuntu, Debian, Fedora, Redhat...). You might need to change a few of the
|
||||||
|
user-creating commands on other systems.
|
||||||
|
|
||||||
|
## New User
|
||||||
|
|
||||||
|
We will assume 2 users:
|
||||||
|
- `emily` is an existing sudoer who's running a Docker Compose service
|
||||||
|
- `kate` will be a new unprivileged user who will run our Podman Quadlet
|
||||||
|
|
||||||
|
Start by logging into `root` and setting up `kate` (optional if you want to use
|
||||||
|
`emily` to host your Quadlet):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo su -
|
||||||
|
useradd --create-home --shell /bin/bash kate
|
||||||
|
passwd kate # Set some sort of password
|
||||||
|
```
|
||||||
|
|
||||||
|
For Podman, we'll need to give `kate` a range of sub-ids, which the container
|
||||||
|
can use to differentiate users, while still all being `kate`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat /etc/subuid
|
||||||
|
cat /etc/subgid
|
||||||
|
# Based on output of above, find a range of 65536 ids which aren't overlapping
|
||||||
|
# with another user. For example, here we use 30000
|
||||||
|
usermod --add-subuids 70000-135536 --add-subgids 70000-135536 kate
|
||||||
|
```
|
||||||
|
|
||||||
|
Assuming we don't typically use the `kate` user, we also want to make sure our
|
||||||
|
containers aren't killed once we logout of `kate`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
loginctl enable-linger kate
|
||||||
|
```
|
||||||
|
|
||||||
|
Podman is also quite sensitive to XDG environment variables. Make sure you have
|
||||||
|
them setup properly. For example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat <<FILE >> /home/kate/.bashrc
|
||||||
|
export XDG_CONFIG_HOME=~/.config
|
||||||
|
export XDG_CACHE_HOME=~/.cache
|
||||||
|
export XDG_DATA_HOME=~/.local/share
|
||||||
|
export XDG_STATE_HOME=~/.local/state
|
||||||
|
export XDG_DATA_DIRS='/usr/local/share:/usr/share'
|
||||||
|
export XDG_CONFIG_DIRS='/etc/xdg'
|
||||||
|
export XDG_RUNTIME_DIR="/run/user/$(id -u kate)"
|
||||||
|
FILE
|
||||||
|
chown kate:kate /home/kate/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
Now to make sure that worked:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
su -l kate
|
||||||
|
podman ps # This shouldn't give any warnings, just a blank table
|
||||||
|
podman info | grep rootless # This should give a line like "rootless: true"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migrating Docker Compose to Podman + Quadlet
|
||||||
|
|
||||||
|
Consider this docker-compose.yml:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
open-webui:
|
||||||
|
image: ghcr.io/open-webui/open-webui:v0.3.35
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
OPENAI_API_BASE_URLS: https://api.mistral.ai/v1
|
||||||
|
OPENAI_API_KEYS: <key>
|
||||||
|
ports:
|
||||||
|
- "9130:8080"
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: ./data
|
||||||
|
target: /app/backend/data
|
||||||
|
```
|
||||||
|
|
||||||
|
We could covert this to a simple Podman bash script. It's a good idea to try
|
||||||
|
this step as `kate` before proceeding. Notice that `kate` will need her own
|
||||||
|
`./data` directory. Try running this as `kate`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
podman run \
|
||||||
|
--rm \
|
||||||
|
--name open-webui \
|
||||||
|
-e OPENAI_API_BASE_URLS="https://api.mistral.ai/v1" \
|
||||||
|
-e OPENAI_API_KEYS="<key>" \
|
||||||
|
-p 9130:8080 \
|
||||||
|
-v ./data:/app/backend/data \
|
||||||
|
ghcr.io/open-webui/open-webui:v0.3.35
|
||||||
|
```
|
||||||
|
|
||||||
|
A Quadlet file is similar to a systemd unit file, but describes the same things
|
||||||
|
as a docker-compose.yml:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Unit]
|
||||||
|
Description=Open WebUI container
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
After=local-fs.target
|
||||||
|
|
||||||
|
[Container]
|
||||||
|
ContainerName=open-webui
|
||||||
|
Image=ghcr.io/open-webui/open-webui:v0.3.35
|
||||||
|
|
||||||
|
Environment=OPENAI_API_BASE_URLS="https://api.mistral.ai/v1"
|
||||||
|
Environment=OPENAI_API_KEYS="<key>"
|
||||||
|
|
||||||
|
PublishPort=9130:8080/tcp
|
||||||
|
|
||||||
|
Volume=/home/kate/Documents/servers/openwebui/data:/app/backend/data
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Restart=on-failure
|
||||||
|
TimeoutStartSec=900
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, I put my Quadlet file at
|
||||||
|
`/home/kate/Documents/servers/openwebui/openwebui.container`. I put a
|
||||||
|
corresponding `/home/kate/Documents/servers/openwebui/data` directory to mount
|
||||||
|
in the container.
|
||||||
|
|
||||||
|
See [Erick Patrick's repository](https://github.com/fpatrick/podman-quadlet) for
|
||||||
|
a great Quadlet template with all the important options!
|
||||||
|
|
||||||
|
Quadlet files for a user should be at `~/.config/containers/systemd/`. To be a
|
||||||
|
bit more organized, we'll simply symlink our container out of
|
||||||
|
`~/Documents/servers` to here:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ln -s /home/kate/Documents/servers/openwebui/openwebui.container /home/kate/.config/containers/systemd/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running Quadlets
|
||||||
|
|
||||||
|
Use `kate` to test if our Quadlet is working:
|
||||||
|
|
||||||
|
```
|
||||||
|
/usr/lib/podman/quadlet -dryrun -user
|
||||||
|
```
|
||||||
|
|
||||||
|
This should print out a file it calls `openwebui.service`. It looks similar to
|
||||||
|
our `openwebui.container`, but added more to the `[Service]` section and added a
|
||||||
|
`[X-Container]` section. Quadlet essentially converts our `.container` files to
|
||||||
|
systemd `.service` files, so that systemd can run them normally. Let's try it!
|
||||||
|
|
||||||
|
Refresh your daemon:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
```
|
||||||
|
|
||||||
|
Now check the status:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user status openwebui.service
|
||||||
|
```
|
||||||
|
|
||||||
|
We can start it just like any other systemd service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user enable --now openwebui.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, the analog to `docker log` is `journalctl --user -fu openwebui.service`!
|
Loading…
Reference in a new issue