Quadlet: Podmanコンテナをsystemdで実行する
(mo8it.com)- Podmanはデーモンを持たないため、サーバー起動後にコンテナを安定して立ち上げる実行主体が必要であり、Quadlet はこれをsystemdサービスファイル方式で解決する
- 従来の
podman generate systemdではコンテナ作成とサービスファイル生成を別々に処理する必要があり、生成ファイルを変更するたびに 手動編集 の負担が生じていた - Quadletは
~/.config/containers/systemdの.containerファイルに[Container],[Service],[Install]の設定をまとめて置き、Podmanオプションとsystemdの動作を一か所で管理する - rootless Podmanでは起動時の自動開始のために
WantedBy=default.targetと lingerの有効化 が必要で、multi-user.targetはユーザーモードでは定義されていない AutoUpdate=registry、systemdの依存関係、ディレクトリごとのファイル整理、podlet変換ツールまで活用すれば、Quadletは rootless・daemonless なPodman運用に適している
なぜQuadletが必要なのか
- Quadlet はPodmanコンテナをsystemdサービスのように実行できるようにする仕組み
- コンテナをバックグラウンドで実行できる
- サーバー再起動後にコンテナを自動起動できる
- Podmanコンテナをsystemd配下で実行する方法自体は新しくない
- 従来は
podman generate systemdコマンドを使っていた - このコマンドは現在、Quadletへ移行するよう 非推奨の警告 を表示する
- 従来は
- Podmanはdaemonlessな構造のため、デーモンなしでコンテナを起動してくれる実行主体が必要になる
従来の podman generate systemd 方式の不便さ
- 従来の方法では、まず
podman createでコンテナを作成する必要があった- 例のコンテナでは
docker.io/library/postgres:16イメージを使用する -p 5432:5432、ボリュームマウント、POSTGRES_PASSWORD環境変数、io.containers.autoupdate=registryラベルを含める
- 例のコンテナでは
- その後
podman generate systemd test-db -fn --newを実行してcontainer-test-db.serviceファイルを生成していた - 生成されたサービスファイルは
~/.config/systemd/userに配置し、次のコマンドで有効化・起動していたsystemctl --user enable --now container-test-db
- この流れでは、コンテナ作成、サービスファイル生成、必要に応じたファイル移動、サービス有効化を繰り返すことになる
- コンテナ作成コマンドが長くなると、後で再実行するために シェルスクリプト を別途管理しなければならなかった
- 生成されたsystemdサービスファイルをカスタマイズするには、毎回あらためて 手動修正 が必要だった
Quadletファイルの構成
- Quadlet方式では
~/.config/containers/systemdディレクトリを作成し、その中に.containerファイルを置く - 例の
test-db.containerファイルには次の要素が含まれる[Container]Image=docker.io/library/postgres:16AutoUpdate=registryPublishPort=5432:5432Volume=%h/volumes/test-db:/var/lib/postgresql/data:ZEnvironment=POSTGRES_PASSWORD=CHANGE_ME
[Service]Restart=always
[Install]WantedBy=default.target
.containerファイルは通常のsystemdサービスファイルだが、特別な[Container]セクションを含む[Container]の多くのオプションはpodman createで使うコマンドラインオプションに対応しているImage: 使用するイメージとタグAutoUpdate=registry:--label "io.containers.autoupdate=registry"に対応PublishPort:-pに対応Volume:-vに対応Environment:-eに対応
- ユーザーのホームディレクトリでは
~の代わりにsystemd指定子の%hを使う必要がある [Service]のRestart=alwaysは、手動で停止しない限りコンテナを常に再起動させる[Install]のWantedBy=default.targetは、コンテナを起動時に自動開始させる
rootless Podmanで必要なsystemd設定
- rootlessコンテナでは
multi-user.targetではなくdefault.targetを使う必要があるmulti-user.targetはsystemdユーザーモードでは定義されていないsystemctl --user status multi-user.targetで確認できる- システムモードでは
systemctl status multi-user.targetで確認可能
- systemdユーザーサービスとしてコンテナを実行するため、ユーザーがログインしていなくてもコンテナが起動するようにするにはlingerを有効化する必要がある
loginctl enable-linger
- サーバー再起動後の自動起動が必要なら、lingerの有効化は必須
- 新しいサービスファイルをsystemdに認識させるには、次のコマンドを実行する
systemctl --user daemon-reload
- コンテナの起動と状態確認は、systemdとPodmanの両方から行える
systemctl --user start test-dbsystemctl --user status test-dbpodman ps
- デフォルトのコンテナ名は、サービスファイル名の前に
systemd-が付いた形になるtest-db.containerのデフォルトのコンテナ名はsystemd-test-db- 衝突を避けるための命名規則
[Container]セクションのContainerNameオプションで明示的に指定することもできる
Quadletの運用上の利点
- Quadletはコンテナサービス設定を1つのファイルで管理できるようにする
- 従来方式のように、サービスファイルを生成するスクリプトと生成済みサービスファイルを別々に管理する必要がない
- systemdの
[Unit]と[Service]セクションで使えるオプションを利用できる- たとえばコンテナ起動前に実行するコマンドを
StartExecPreで指定できる - 生成されたファイルを後から手動で修正する工程が減る
- たとえばコンテナ起動前に実行するコマンドを
- シェルスクリプトを書いてデバッグする代わりに、設定ファイル 中心でコンテナを運用できる
- コンテナ間の 依存関係 をsystemd方式で表現しやすい
コンテナ間の依存関係の表現
- アプリコンテナがデータベースコンテナに依存するなら、systemdの
[Unit]セクションで関係を指定できる - OxiTraffic コンテナは
test-dbコンテナに依存する例[Unit]にRequires=test-db.serviceを設定し、データベースが起動するときだけアプリが起動するようにするAfter=test-db.serviceを設定し、2つのコンテナが並列に起動しないようにする
- 参照時には
.containerファイル名ではなく サービス名 を使うtest-db.container: ファイル名test-db.service: サービス名systemd-test-db: デフォルトのコンテナ名
- アプリがデータベースと通信するには、両方のコンテナの
[Container]セクションにNetworkオプションを追加する必要があるが、ネットワーク設定はここでは扱わない
複数ファイルとグループ化
- Quadletはコンテナごとに別々の
.containerファイルを置く構成にできる - マルチコンテナアプリを1つのDocker Composeファイルにすべて入れる方式より、コンテナごとのファイルのほうが 認知負荷 を下げられる場合がある
- Docker Composeファイルが数百行・数十コンテナを含むと、保守が難しくなりがち
- MailcowのDocker Composeファイル は大きなComposeファイルの例
- Docker Composeにも複数ファイルへ分割する機能はある
- Quadletでは
~/.config/containers/systemd内のディレクトリにunitファイルを置ける- 例では
oxitrafficディレクトリを作成し、アプリとデータベースのファイルを一緒に置ける
- 例では
イメージ更新
AutoUpdate=registryを設定すると、podman auto-updateでイメージ更新を確認できる- レジストリに使用中のタグと互換性のある新しいイメージがあれば、Podmanがイメージをpullしてコンテナを再起動する
- Dockerではこの用途に Watchtower のようなツールが必要になることがあるが、Podmanはこの機能を標準で備えている
latestのようなタグは危険な場合がある- OxiTrafficで
latestを使うと、次のバージョンのbreaking changeを含む可能性がある - PostgreSQLで
latestを使うと、新しいメジャーバージョンへ上がる可能性があり、PostgreSQLのメジャーアップグレードには常に手動マイグレーションが必要
- OxiTrafficで
- 本番運用では、breaking changeにつながらないタグを使うべき
podman auto-updateを数日ごとに手動実行して、何が更新されたかを確認し、その後コンテナが正常かどうかを確認する運用も可能
podman-compose と移行ツール
podman-composeはComposeファイルをPodmanで実行するPythonスクリプト- 長期的なDocker Compose代替として見づらい理由は次のとおり
- Compose仕様とPodman・systemdの間の翻訳レイヤーであり、systemdのすべての機能を使えるようにはしない
- 公式PodmanプロジェクトはRustやGoのようなコンパイル言語で書かれている
- 最終コミットが5か月前で、活発に保守されていない
- QuadletはPodmanの rootless・daemonless な設計とより相性がよい
- ComposeファイルからQuadletを試すなら podlet を使える
- PodmanコマンドやDocker ComposeファイルからQuadletファイルを生成できるRust製ツール
さらに読む資料
- Quadletをより深く理解するには
podman-systemd.unitのmanページが役立つ - Quadletはコンテナだけでなく、pod、network、volumeも管理できる
- systemd unitファイルの作成に慣れていないなら
systemd.unitとsystemd.serviceのmanページを見るとよい - 別の視点と2つ目の例は blog.while-true-doの類似記事 で確認できる
1件のコメント
Hacker News の意見
Quadlet は Podman から出てきたものの中でも最高に近く、Podman に興味がある人やコンテナベースのワークロードへ移行しようとしている人に強くおすすめする
コンテナを通常のシステムサービスのように組み込んで扱え、一緒に動かすために別途オーケストレーション層を学んだり、非コンテナのリソースに依存したりする必要がない
既存の systemd ユニット をそのまま書けばよく、自動更新や失敗時のサービス再起動/通知はおまけとして付いてくる
Docker で似たことをしようとするやり方では、巨大な
runコマンドで Docker デーモンを迂回した結果、幽霊のようなサービスやコンテナが残ることが多かったが、Quadlet ははるかにすっきりしていて、設定も/etc/systemd/、.config/systemd、/usr/local/lib/systemdのような systemd ユニットの場所にまとまっているのでバックアップしやすいただしローカル開発における docker-compose の答えではなく、Podman チームもその領域にはあまり関心がないように見える
ユーザーコンテナは、バックグラウンドデータベースのような長時間実行するローカルテストインフラには向いているが、一般的な「コンパイル →
docker compose up→ テスト →docker compose down」のループには手間がかかりすぎる現実的な答えは
.kubeQuadlet(Kubernetes play)か、Podman ソケットに対してdocker composeを使うことだここ数か月、余暇に Quadlet を管理する GitOps ツールを作るほど気に入っており、コンテナ化されたサーバーを管理する正しいやり方のように感じている
[0]が podman-compose ではない点が重要だ。記事でも podman-compose はいまひとつで、開発が不足していると指摘している。Podman は compose 仕様の大半を実装しているので、ほとんどの状況でdocker composeを使えるRH が最初に Podman を推し始めたときに試した人たちは、Podman 3 の中途半端さと podman-compose の悪い体験のせいで離れてしまった可能性が高い
いくつものフラグを変え、バージョンを上げ、podlet 自体が別のツールへ乗り換えるよう勧める流れに従っているうちに、終わりのない迷路にはまり込んだ
結局、私の systemd は単に
podman compose upを実行しているcompose に本来の仕事をさせるのと比べて、Quadlet の利点が何なのか気になる
ちなみに Podman は自分のユーザーで実行されており、デーモンはまったくない
docker-composeを使うことだpodman composeだけが残る最近 Quadlet を見つけて、atomic OS とルートレス Quadlet をベースにホームラボ全体を作ったが、強くおすすめする
systemd のソケットアクティベーションも可能で、たとえば systemd の http/https ソケットを作り、
ssh.socketとpodman.socketがそれぞれsshd.service、podman.serviceを有効化するように、Traefik を自動で有効化できるルートレス構成で元の IP を保持する実質的に唯一の方法なので、命綱に近い。ルートレス Podman/Docker は通常、大きな欠点なしに元の IP の保持を簡単には許可しない
https://github.com/savely-krasovsky/homelab
Quadlet は、「本番環境に近い環境で相互依存するコンテナ群をすべて実行する」という docker compose の用途には良い代替手段です。
ただし、docker compose のもう一つの用途である開発用途については、もっと良くなってほしいところです。
docker compose なら、データベース、Redis、OpenSearch、その他の依存関係、nginx プロキシ、
.:/appをボリュームとしてマウントした開発用コンテナをまとめて起動し、まとめて削除でき、docker-compose.ymlをリポジトリに入れられます。Quadlet はすべてのファイルを
~/.config/containers/systemdに置くよう求めるため、もはやプロジェクト内に隔離されず、他の開発者とチェックインして共有するのにも便利ではありません。加えて、彼らも Podman を使う必要があります。ほとんどの人はまだ Docker を使っており、Codespaces のようなホスト型開発環境でも Docker が提供されています。
そのため、チェックインされた YAML ファイルで docker compose を使っています。私は Podman を使っているので、すべてのボリュームに手動で
:Zを付ける必要がありますが、通常の Docker はそれを処理できません。開発用の docker compose 代替があるとよいのですが、Quadlet はあまり合っていないように見えます。
podman-compose --in-pod=1 systemd -a create-unitを使うとpodman-compose@サービスを作成でき、その後podman-compose systemd -a registerでcompose.ymlファイルを$nameとともに登録できます。すると
podman-compose@$name.serviceで compose ファイルベースの pod を管理でき、完全にルートレスでも動作します。systemd-runは任意のコマンドを基に 一時的な systemd サービス を実行できるようにしてくれます。必要ならタイマーまで含めて、systemd のスコープ規則に従うきれいな一時ユニットを作ってくれます。
[1] https://www.freedesktop.org/software/systemd/man/systemd-run...
Kubernetes に似た形式に、コンテナビルドなどの便利機能が少し追加されています。
kube play ファイルをプロジェクトルートに置き、
podman kube play project.yaml --buildを実行すると、pod やボリュームなどが一緒に実行されます。Quadlet ファイルはデプロイ段階で有用です。プロジェクトを VPS に載せたいなら、すでに持っている
project.yamlとともに Kube Quadlet[2] を使えばよいです。[1] https://docs.podman.io/en/latest/markdown/podman-kube-play.1...
[2] https://docs.podman.io/en/latest/markdown/podman-systemd.uni...
docker-compose.ymlを使っているので、そのつらさは分かります。概ねとてもよく動きますが、OSX ではあの忌々しい中間 VM のせいで、ときどき妙な挙動をすることがあります。
たまに Podman 互換性を確認していますが、今ではルートレスの哲学があまりにも違うため、完全互換は不可能だと受け入れています。
それでもこのツールは本番環境には素晴らしいです。趣味プロジェクトをいくつかベアメタルノード上で自作の systemd サービスとして動かしており、現在はユーザーモードの containerd です。
時間ができたら移行してみるかもしれません。
この記事が Podman-Compose を「活発にメンテナンスされていない」とし、最後のコミットが5か月前だと指摘しておきながら、すぐ代替として最後のコミットが同じく5か月前の Podlet を勧めているのが面白い
Podlet は有用かもしれないが、Docker Compose の多くの機能をサポートしておらず、常にきれいに変換してくれるわけでもない
特に
-f docker-compose.yml -f docker-compose.override.ymlのように複数の YAML ファイルを重ねる機能をサポートしていないcompose CLI はソケット経由でエンジンを制御しているようで、Podman と Docker エンジンはほぼ同じ API を持っている
podman-compose が期待どおりに動かなかったので、この方法を使っている
Docker-compose は通常 docker クライアントプラグインとしてインストールされるが、私は Podman と使うためにスタンドアロンアプリケーションとして使っている
また、連携設定には
DOCKER_HOST環境変数より dockercontextsを好んで使っている純粋な Quadlet だけでは十分に強力でないなら、Quadlet[3] と通常の Podman[4] も限定的な Kubernetes マニフェストの実行をサポートしている
まだ compose ファイルの
restartオプションを Podman がどう扱うのかは把握できていない。Podman には監視デーモンがないためだ一方で
healthcheckオプションは systemd タイマーに依存していることは知っているsystemd ではないディストリビューションである Gentoo で Podman を使ったとき、自動ヘルスチェックは動かなかったが、手動でヘルスチェックを実行すると残りの設定は最後まで進んだ
[1] https://github.com/docker/compose
[2] https://docs.podman.io/en/latest/markdown/podman-system-serv...
[3] https://docs.podman.io/en/latest/markdown/podman-systemd.uni...
[4] https://docs.podman.io/en/latest/markdown/podman-kube-play.1...
Podlet は補助ツールであり、長期的には Quadlet で作業するほうがよい
5か月はメンテナンスされていないという意味ではなく、2年くらいは必要だ
それでも懸念を強めはする。ただし、5か月というのはメンテナンス中である証拠には決してならない
メンテナンスされていないリポジトリにも更新される理由はいろいろあり、最近のいまひとつな GitHub のアーカイブ機能だけがそれを防いでくれる
数年前にセルフホスティング構成を初期化する際、別のものを試したくて openSUSE MicroOS を選び、結局 systemd/Quadlet の下で Podman コンテナを動かすことになったが、現在の構成にはかなり満足している
コンテナは Podman 内蔵ツールで自動更新され、ログ確認と監視は普段の systemd ツールで処理している
何かを変更する必要があるときも、設定ファイルの場所を忘れていても見つけやすく、読みやすく修正もしやすい
ルートレスでデーモンレスなのも良い
途中でいくつか試したが、podman compose は鈍重に感じられ、deprecated になったのは歓迎で、Quadlet が進むべき道であることは明らかだ
学習曲線があり、Docker より資料が少ない点は考慮する必要がある
ローカル開発で複数サービスのスタックを立ち上げたり停止したりする用途なら、今でも Docker と docker compose のほうを選ぶ
設定を合わせるのには少し時間がかかったが、メンテナンスがほとんど不要な点に非常に感銘を受けた。今のところはまったく不要だった
同じように試したい人は私の設定を参考にできる
https://github.com/jeppester/coreos-nextcloud
すべてのノードをゆっくり MicroOS に移行しているところで、会社でも MicroOS かそれに近いものを推している
ベース OS の自動ロールバックと、宣言的なコンテナ設定および自動更新の組み合わせが、互いにぴったり噛み合っている感じがする
形式は
podman generate systemdや Kubernetes YAML よりも明確で、systemd 統合も素晴らしいいら立たしいのは、Podman upstream が Debian/Ubuntu 向けリポジトリを提供していないことだ
Debian stable では 4.3.1 に縛られて新機能を多く逃し、結局 Docker compose に戻ることにした
提案された回避策は Podman を自分でコンパイルするか、
debian/testingを使うことだったこの新しいアプローチが、人々がDockerからPodmanへ移行する助けになることを心から願っています。
Docker Composeは多くの人が移行をためらう理由で、私もそうでした。
正直なところ、Quadlet以前のPodmanにはきちんとした答えがありませんでした。
Docker Composeが理由でDockerからの移行をためらっていたなら、Quadletを組み合わせたPodmanはずっと対抗しうる代替案です。
思ったほどDockerが恋しくなることはなく、ルートレスコンテナの実行によるセキュリティ向上も得られます。
すべてを
~/.config/quadlet/systemdに入れる方式ではだめです。開発とテストにはこの点が必須で、現状のQuadletは厳密にはデプロイ用ソリューションです。
Quadletはコンテナを通常のシステムサービスのように扱えるようにしてくれるので、本当に気に入っています。
ただし、ルートレスコンテナのユーザー体験はこの概念化とうまく合いません。
通常、システムサービスはシステムのsystemdセッションでシステムユーザーとして実行されますが、ルートレスコンテナのサービスはシステムユーザーのユーザーsystemdセッションに存在します。
システムセッション内でルートレスQuadletを実行できるとよいのですが。
user-lingerを有効にして、ユーザーsystemd経由でルートレスに実行するのと実質的な違いがあるのか気になります。私はいつもそうしてきました。
また、
DynamicUser=オプションでルートレスQuadletを実行できるとよいです。DynamicUser=はシステムサービスの権限を制限する優れた方法でしたが、今のPodmanとはあまり相性がよくありません。Hacker NewsのトップページでQuadletを見かけて興味深いです。十分に注目されていないと思います。
プロジェクトのYgalとValentinが、inletsトンネルクライアントを実行する方法についてゲスト記事を送ってくれたことがあります。Ngrok/Cloudflaredに似ていますが、SaaSの制限なしでセルフホストする方式です。
https://inlets.dev/blog/2023/10/03/client-quadlet.html
彼らは
[container]の代わりに[kube]を使うことで標準のKubernetes YAMLを持ち込め、かなり移植性が高くなりました。興味のある人向けに、最近のQuadletデプロイがどれほど簡単かを示すAnsibleテンプレートを作りました。
GH: https://github.com/Mati365/hetzner-podman-bunjs-deploy
site.example.orgではなくexample.site.orgを使っています。https://en.wikipedia.org/wiki/Example.com