こんにちは、テクマトリックスの酒井です。
弊社では CI 環境などの構築サービスを実施していますが、その中でコンテナを使って構築してほしいというご要望をいただくことが多くなりました。コンテナと言えば Docker がもっともよく知られているかと思いますが、 Linux をご利用のお客様からは Docker ではなく Podman を利用したいとのご要望も少なからずいただきます。
そこで今回は Fedora 37 に Podman をインストールして、コンテナの実行やコンテナイメージのビルドなどを行い、 Docker と同様に利用できるかを見ていきます。コンテナの題材として Jenkins の公式 Docker イメージ を使って説明します。
目次
Podman とは
Podman のウェブサイト には次のように記載されています。
What is Podman? Podman is a daemonless container engine for developing, managing, and running OCI Containers on your Linux System. Containers can either be run as root or in rootless mode. Simply put: alias docker=podman.
https://podman.io/, (最終閲覧日:2023年2月24日)
これを日本語に翻訳すると次のようになります。
ポッドマンとは? Podman は、 Linux システムで OCI コンテナーを開発、管理、および実行するためのデーモンレス コンテナー エンジンです。コンテナーは、ルートとして実行することも、ルートレス モードで実行することもできます。簡単に言えば、 alias docker=podman です。
Google 翻訳を基に一部修正
alias docker=podman ということは、 docker
コマンドと同じ使い方が podman
コマンドでできると理解できます。実際のところどうなのかをこれから試していきます。
インストール
まずは Fedora 37 に Podman をインストールします。このあとの話を単純にするため、 Fedora の SELinux とファイアウォールは無効にしてあります。
Fedora では、標準のパッケージ管理ツール DNF を利用して Podman をインストールできます。次のコマンド一つを実行するだけでインストールできます。
sudo dnf install podman
インストール後にバージョンを確認すると、 4.4.1 がインストールされていました。
$ podman version
Client: Podman Engine
Version: 4.4.1
API Version: 4.4.1
Go Version: go1.19.5
Built: Thu Feb 9 10:58:53 2023
OS/Arch: linux/amd64
コンテナの実行
Jenkins の公式 Docker イメージの README に従って、 Jenkins のコンテナを実行してみます。実行するコマンドは次のとおりです。
podman run -p 8080:8080 -p 50000:50000 --restart=on-failure -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts
Jenkins の README の記載と異なるのは、先頭の docker
を podman
に置き換えたことと、この後で利用するコンテナイメージ名を一致させるためにイメージ名のタグを lts-jdk11
から lts
に変更しただけです。
コマンドを実行すると、イメージの選択が求められました。
$ podman run -p 8080:8080 -p 50000:50000 --restart=on-failure -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts
? Please select an image:
▸ registry.fedoraproject.org/jenkins/jenkins:lts
registry.access.redhat.com/jenkins/jenkins:lts
docker.io/jenkins/jenkins:lts
quay.io/jenkins/jenkins:lts
Docker の場合はすぐに docker.io/jenkins/jenkins:lts
というイメージのダウンロードが始まるのですが、Podmanではデフォルトのコンテナレジストリがないようです。入力待ちにならないよう、イメージ名はレジストリ名とともに指定するのが良さそうに思います。今回は docker.io/jenkins/jenkins:lts
を選択して先に進みます。
$ podman run -p 8080:8080 -p 50000:50000 --restart=on-failure -v jenkins_home:/var/jenkins_home jenkins/jenkins:lts
✔ docker.io/jenkins/jenkins:lts
Trying to pull docker.io/jenkins/jenkins:lts...
Getting image source signatures
Copying blob b51e09c8a0bd [==>-----------------------------------] 7.0MiB / 89.7MiB
(中略)
Writing manifest to image destination
Storing signatures
Running from: /usr/share/jenkins/jenkins.war
webroot: EnvVars.masterEnvVars.get("JENKINS_HOME")
(以下、 Jenkins のログが続く)
イメージがダウンロードされ、しばらくして Jenkins が起動しました。ブラウザで http://localhost:8080/ にアクセスすると、 Jenkins の「Unlock Jenkins」のページが表示されました。
では Ctrl-C
を入力してコンテナを停止して、コンテナの一覧を確認します。
$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
adad0e39bf34 docker.io/jenkins/jenkins:lts 2 minutes ago Up 44 seconds 0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp strange_ramanujan
コンテナを止めたはずなのにまだ実行されています。これは --restart=on-failure
オプションの指定によりコンテナが再起動されたためです。このコンテナを停止して削除します。
$ podman stop strange_ramanujan
strange_ramanujan
$ podman rm strange_ramanujan
strange_ramanujan
$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
コンテナを削除できました。ここまでは podman
コマンドが docker
コマンドの代わりとしてまったく同じに利用できました。
コンテナイメージのビルド
次に、 Jenkins の公式 Docker イメージをベースにしたコンテナイメージをビルドします。
先程と同じく、 Jenkins の公式 Docker イメージの README に従って2つのファイルを作成します。まずは executors.groovy
を作ります。
import jenkins.model.*
Jenkins.instance.setNumExecutors(0) // Recommended to not run builds on the built-in node
次に Dockerfile
を作ります。 Podman の作法としては Containerfile
というファイル名の方が望ましいと思われますが、 Dockerfile
でも動作します。
FROM jenkins/jenkins:lts
COPY --chown=jenkins:jenkins executors.groovy /usr/share/jenkins/ref/init.groovy.d/executors.groovy
必要なファイルを作ったので、コンテナイメージをビルドしてみます。
$ podman build -t myjenkins:lts-1 .
STEP 1/2: FROM jenkins/jenkins:lts
STEP 2/2: COPY --chown=jenkins:jenkins executors.groovy /usr/share/jenkins/ref/init.groovy.d/executors.groovy
COMMIT myjenkins:lts-1
--> d29a954eb4c
Successfully tagged localhost/myjenkins:lts-1
d29a954eb4cec58ca509b4ebe0e14d03b8273907043a9e143d65397cfd319f72
イメージをビルドできました。 Docker と違って、 -t
オプションで指定したイメージ名にレジストリ名が含まれない場合、作成されるイメージ名の先頭に localhost/
が追加されます。イメージの一覧でも確認してみます。
$ podman image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/myjenkins lts-1 d29a954eb4ce 52 seconds ago 476 MB
docker.io/jenkins/jenkins lts f16216f97fcb 2 weeks ago 476 MB
続いて、今ビルドしたイメージを使ってコンテナを実行します。
$ podman run -p 8080:8080 -p 50000:50000 --restart=on-failure -v jenkins_home:/var/jenkins_home localhost/myjenkins:lts-1
Running from: /usr/share/jenkins/jenkins.war
webroot: EnvVars.masterEnvVars.get("JENKINS_HOME")
(以下、 Jenkins のログが続く)
無事起動できました。 Ctrl-C
でコンテナ止め、コンテナを確認します。
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b880ee90d824 localhost/myjenkins:lts-1 About a minute ago Up 23 seconds 0.0.0.0:8080->8080/tcp, 0.0.0.0:50000->50000/tcp hopeful_cerf
コンテナが再起動していることが確認できます。コンテナのログも見てみます。
$ podman logs hopeful_cerf
Running from: /usr/share/jenkins/jenkins.war
webroot: EnvVars.masterEnvVars.get("JENKINS_HOME")
(以下、 Jenkins のログが続く)
Jenkins のログが表示されました。最後にコンテナを停止し、コンテナとボリュームを削除します。
$ podman stop strange_ramanujan
hopeful_cerf
$ podman rm hopeful_cerf
hopeful_cerf
$ podman volume rm jenkins_home
jenkins_home
コンテナとボリュームを削除できました。ここまででも docker
コマンドと同じく podman
コマンドを利用できました。
Docker Compose はない、でもポッドがある
Jenkins の公式 Docker イメージの README には Docker Compose を使ってJenkins を実行する方法も記載されています。その方法とは、次のような内容の docker-compose.yml
ファイルを作成して、 docker compose up -d
コマンドを実行するというものです。
services:
jenkins:
image: jenkins/jenkins:lts
ports:
- "8080:8080"
volumes:
- jenkins_home:/var/jenkins_home
ssh-agent:
image: jenkins/ssh-agent
volumes:
jenkins_home:
長いコマンドを打つ代わりに YAML ファイルでコンテナを定義できるのは魅力的です。しかし、 Podman には Docker Compose に対応する機能はありません。では別のツール、例えば Podman Compose を利用しなければならないのでしょうか。
いいえ、別の方法で実現できます。
Podman にはポッド(Pod)という形で複数のコンテナをまとめて管理する機能があります。この機能を利用すると、 Kubernetes の Pod などの設定ファイルに記述されたポッド・コンテナを実行することができます。
早速ポッドを試してみます。まずは Jenkins の README に記載の docker-compose.yml
の例とおおむね同じポッドの設定ファイルを作成します。今回は jenkins-pod.yml
というファイルに以下の内容を記述しました。実際のところ、このファイルにはポッド以外の設定(永続ボリューム要求)が含まれていますが、ここでは「ポッドの設定ファイル」と呼ぶことにします。
apiVersion: v1
kind: Pod
metadata:
name: jenkins-pod
spec:
containers:
- name: jenkins
image: docker.io/jenkins/jenkins:lts
ports:
- hostPort: 8080
containerPort: 8080
volumeMounts:
- name: jenkins_home-pvc
mountPath: /var/jenkins_home
- name: ssh-agent
image: docker.io/jenkins/ssh-agent
volumes:
- name: jenkins_home-pvc
persistentVolumeClaim:
claimName: jenkins_home
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins_home
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
ではコンテナを実行します。ポッドの設定ファイルからコンテナを作成・実行するには、 podman kube play
コマンドを使用します。
$ podman kube play jenkins-pod.yml
Trying to pull docker.io/jenkins/ssh-agent:latest...
Getting image source signatures
(中略)
Writing manifest to image destination
Storing signatures
Volumes:
jenkins_home
Pod:
8116d152ca06993a764aec6fc1b55f8cea79b9bb9c33d7742f23cf038972f11f
Containers:
d25ac4c0ff31038661d4c2dbcbb24c199d3f7d3642371f33fd731b9fee4adde6
90d2313bb0529eb2d980e9f8a967ca6acb06ebf06ec2d03621f059f965e411aa
ボリューム、ポッド、そして2つのコンテナが作成され、コンテナが起動しました。コンテナを単独で起動したときと同様、ウェブブラウザで http://localhost:8080/ にアクセスすると「Unlock Jenkins」画面が表示されました。
実行中のポッドを確認するには、 podman pod ls
コマンドを実行します。
$ podman pod ls
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
8116d152ca06 jenkins-pod Running 3 minutes ago 1a7c48ca265c 3
コンテナの一覧を見ると、ポッドの設定ファイルで定義した2つのコンテナ(とポッド管理用のコンテナ)が実行中であることが分かります。
$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1a7c48ca265c localhost/podman-pause:4.4.1-1675940333 3 minutes ago Up About a minute 0.0.0.0:8080->8080/tcp 8116d152ca06-infra
d25ac4c0ff31 docker.io/jenkins/jenkins:lts 3 minutes ago Up About a minute 0.0.0.0:8080->8080/tcp jenkins-pod-jenkins
90d2313bb052 docker.io/jenkins/ssh-agent:latest About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp jenkins-pod-ssh-agent
このように、Docker Compose とは異なるものの、ポッドの設定ファイルから複数のコンテナを実行することができました。
ちなみに、 podman
コマンドを使ってポッドを作成し、そこから設定ファイルを生成することができます。次のようにコマンドを実行すると、前述の jenkins-pod.yml
に近い YAML ファイルが得られます。
podman pod create -p 8080:8080 jenkins-pod
podman container run --pod=jenkins-pod -d -v jenkins_home:/var/jenkins_home docker.io/jenkins/jenkins:lts
podman container run --pod=jenkins-pod -d docker.io/jenkins/ssh-agent
podman generate kube jenkins-pod > jenkins-pod.yml
クリーンアップ
最後に、これまで実行したコンテナやダウンロードしたイメージをすべて削除します。
まずはすべてのポッドを停止します。次のコマンドを実行します。
$ podman pod stop jenkins-pod
8116d152ca06993a764aec6fc1b55f8cea79b9bb9c33d7742f23cf038972f11f
ポッドに含まれるコンテナも停止します。当然ながら、 podman pod stop
に対応する Docker のコマンドはありません。
そして、コンテナに関するあらゆるものを削除します。
$ podman system prune -a --volumes
WARNING! This command removes:
- all stopped containers
- all networks not used by at least one container
- all volumes not used by at least one container
- all images without at least one container associated with them
- all build cache
Are you sure you want to continue? [y/N] y
ERRO[0004] Checking if infra needs to be stopped: stopping some containers: some containers failed
ERRO[0004] Checking if infra needs to be stopped: stopping some containers: some containers failed
ERRO[0004] Checking if infra needs to be stopped: pod has no infra container: no such container
Deleted Pods
8116d152ca06993a764aec6fc1b55f8cea79b9bb9c33d7742f23cf038972f11f
Deleted Volumes
jenkins_home
Deleted Images
f16216f97fcb99c68e03372f08859d8efd86e075e8bc22a0707d991b059a624a
504048d783865519a652773d9a5191eb0deab3314dab0c53c90f3fe8538bf288
c724b7bb09ece1d3004c08a8682de40be725b9b8694ccd47be762369c06c3f83
25a1e72a11d662de43279d3a701c0e8fad9daed0e3d28d2c78c566cba645143f
Deleted Networks
podman-default-kube-network
Total reclaimed space: 1.411GB
エラーが出力されたものの、コンテナ・ポッド・ネットワーク・ボリューム・イメージ・ビルドキャッシュすべてが削除されました。このコマンドは docker
コマンドと同じです。
まとめ
Podman を利用してコンテナを実行しました。基本的に podman
コマンドは docker
コマンドと同じに利用できましたが、イメージ名は常にレジストリ名とともに指定するのが良さそうです。また docker-compose.yml
の代わりにポッドの設定ファイルからコンテナを実行することができました。
また Podman を試した中で他にもいくつか Docker との違いに気づきました。これらについては次回の記事で紹介できればと思います。
参考
- Podman
- Podman とは (RedHatのサイト)
- Pod | Kubernetes