前回の記事では、 Podman が Docker とほぼ同様に利用できることを紹介しました。今回は Podman を使ってみてつまづいたことを書いていきます。
今回の記事で利用した環境は、 Fedora 37 および Podman 4.4.2 です。
目次
準備: Jenkinsを実行する
前回の記事と同様に、今回も Jenkins を題材にします。まずは Jenkins をポッドとして実行するため、前回と同じ 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
この YAML ファイルを利用して Jenkins のポッドを起動します。
$ podman kube play jenkins-pod.yml Volumes: jenkins_home Pod: 71ff664e0cd82a864e4193c4b17c27cee6a5d3d577f578af5009aded1401bdb1 Containers: 1610ce719463c87ea43a895aaa204bfef53925dda15dd281a4da8044fb90ccb1 a242e344e37f3ab41011589e6aedcd412e5d8b3789469db5874875870ad83ccd
コマンドの実行後にウェブブラウザで http://localhost:8080/
にアクセスすると、しばらくして Unlock Jenkins の画面が表示されます。
つまづき1: コンテナが勝手に停止する
Jenkins が無事起動したので、しばらくの間 Jenkins を実行させたままにしたいと考えます。しかし Linux マシンからログアウトすると、その数秒後に Jenkins にアクセスできなくなってしまいました。
もう一度 Linux マシンにログインしてコンテナを確認すると、コンテナが停止していました。
$ podman container ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4b07e210db44 localhost/podman-pause:4.4.2-1677669779 About a minute ago Exited (0) 4 seconds ago 0.0.0.0:8080->8080/tcp 71ff664e0cd8-infra 1610ce719463 docker.io/jenkins/jenkins:lts About a minute ago Exited (143) 3 seconds ago 0.0.0.0:8080->8080/tcp jenkins-pod-jenkins a242e344e37f docker.io/jenkins/ssh-agent:latest About a minute ago Exited (0) 4 seconds ago 0.0.0.0:8080->8080/tcp jenkins-pod-ssh-agent
Linux では通常、ユーザーセッションが完了するとユーザーのプロセスが強制終了されます。コンテナもユーザープロセスであるため、ログアウトとともにコンテナも強制終了されてしまいます。これを防止するには、コンテナの実行中はログインしっぱなしにするか、あるい、 loginctl
コマンドを使用してユーザーの「残留モード」を設定する必要があります。
$ loginctl enable-linger $UID # 残留モードを設定する $ podman pod start jenkins-pod # ポッドを開始する 71ff664e0cd82a864e4193c4b17c27cee6a5d3d577f578af5009aded1401bdb1
残留モードを設定してコンテナを実行したところ、Linux からログアウトしても Jenkins は実行されたままになりました。なお残留モードを解除するには、 loginctl disable-linger $UID
を実行します。
つまづき2: ボリュームにファイルを書き込めなくなる
Jenkins の設定を変更したり、ポッドにコンテナを追加したりするために、 YAML ファイルを変更してポッドを再作成することがあると思います。そこでポッドの再作成を試してみます。
$ podman kube play --replace jenkins-pod.yml Pods stopped: 71ff664e0cd82a864e4193c4b17c27cee6a5d3d577f578af5009aded1401bdb1 Pods removed: 71ff664e0cd82a864e4193c4b17c27cee6a5d3d577f578af5009aded1401bdb1 Volumes removed: Volumes: jenkins_home Pod: 468dd0a6802590f969d0b608bba1165ad12cdff0f674879e665774eb7541abfd Containers: 6197a46e6e3c98dda52111d5201c354962558ed46cf695756d5a287b780482ea f1e1a3cf1ddce3118b6473862abc52468e327a3533ba1c35e2df8d180e6237df
--replace
オプションを利用すると、 以前の podman kube play
コマンドで作成されたポッドが存在すれば削除して、 YAML ファイルで定義されたポッドを再作成します。ポッドの再作成後に Jenkins にアクセスしてみると、なんと Permission denied というエラーが発生してしまいました。
表示されたエラーの5行目に、 Jenkins のホームディレクトリである /var/jenkins_home
ディレクトリにファイルを作成できないというメッセージが見つかります。先程まで問題なく実行できていたのに、 Jenkins ホームディレクトリにファイルを作成するという基本的なところでエラーが発生するのは衝撃です。
気を取り直してエラーを解決していきます。まずはホスト上でボリュームの状態を調べます。ボリュームのホスト上のパスは podman volume inspect
コマンドで確認できます。このパスはあとで使うため mntPoint
というシェル変数に代入しておきます。
$ mntPoint=$(podman volume inspect jenkins_home --format {{.Mountpoint}}) $ echo $mntPoint /home/username/.local/share/containers/storage/volumes/jenkins_home/_data $ ls -lna $mntPoint 合計 36 drwxr-xr-x 12 1000 1000 4096 3月 24 10:28 . drwx------ 3 1000 1000 19 3月 24 10:14 .. drwxr-xr-x 3 100999 100999 24 3月 24 10:14 .cache drwxr-xr-x 3 100999 100999 19 3月 24 10:14 .java -rw-r--r-- 1 100999 100999 0 3月 24 10:28 .lastStarted -rw-r--r-- 1 100999 100999 1663 3月 24 10:28 config.xml -rw-r--r-- 1 100999 100999 150 3月 24 10:30 copy_reference_file.log (以下略)
1000 はホストのログインユーザーのIDです。しかし 100999 とはどのユーザーでしょうか。この答えは podman top
コマンドで得られました。
$ podman top jenkins-pod-jenkins user uid huser USER UID HUSER jenkins 1000 100999 jenkins 1000 100999 jenkins 1000 ?
コンテナ内のプロセスは、コンテナの中から見ると jenkins
というユーザー(ユーザー ID は 1000 )で実行されていますが、ホスト上では 100999 という ID のユーザーにマッピングされていることが分かります。このような振舞いはセキュリティ上の理由でコンテナをホストから隔離するのに有用と思いますが、ここでは深追いしません。
一方、 podman unshare
コマンドを使用すると、ユーザーがマッピングされた状態でホスト上のコマンドを実行できます。これはコマンドをあたかもコンテナの中で実行したかのような結果が得られます。例えば先程と同じようにボリュームの状態を確認してみます。
$ podman unshare ls -lna $mntPoint 合計 36 drwxr-xr-x 12 0 0 4096 3月 24 10:28 . drwx------ 3 0 0 19 3月 24 10:14 .. drwxr-xr-x 3 1000 1000 24 3月 24 10:14 .cache drwxr-xr-x 3 1000 1000 19 3月 24 10:14 .java -rw-r--r-- 1 1000 1000 0 3月 24 10:28 .lastStarted -rw-r--r-- 1 1000 1000 1663 3月 24 10:28 config.xml -rw-r--r-- 1 1000 1000 150 3月 24 10:30 copy_reference_file.log (以下略)
podman unshare
コマンドを使用しない場合と比較して、ファイルおよびディレクトリの所有者・所有グループの表示が変わりました。ボリューム内のファイルの所有者が 1000 (jenkins
) となっている一方、ボリュームのルートディレクトリの所有者が 0 (root
) となっています。そしてディレクトリのパーミッションは rwxr-xr-x
です。 Jenkins のプロセスは jenkins
ユーザーで実行されますが、 jenkins
ユーザーにはこのディレクトリへの書き込み権限がありません。これで Permission denied のエラーを発生させている原因が分かりました。
暫定的な対策として、ボリュームのルートディレクトリの所有者と所有グループを、 Jenkins のコンテナの中から見て Jenkins のプロセスと同一のものに変更して、 Jenkins を再起動します。
$ podman unshare chown 1000:1000 $mntPoint $ podman pod restart jenkins-pod 468dd0a6802590f969d0b608bba1165ad12cdff0f674879e665774eb7541abfd
これでディレクトリにファイルを作成でき、 Jenkins が正常に起動するようになりました。
しかしながら、 YAML ファイルを変更するたびにボリュームの所有者を変更するのは気が滅入ります。そのためというわけではありませんが、 YAML ファイルでボリュームのルートディレクトリの所有者を指定することができます。所有者を指定するには、 PersistentVolumeClaim
に volume.podman.io/uid
および volume.podman.io/gid
アノテーションを追加します。
--- a/jenkins-pod.yml 2023-03-24 11:33:33.078894478 +0900 +++ b/jenkins-pod.yml 2023-03-24 11:33:40.398939054 +0900 @@ -23,6 +23,9 @@ kind: PersistentVolumeClaim metadata: name: jenkins_home + annotations: + volume.podman.io/uid: 1000 + volume.podman.io/gid: 1000 spec: accessModes: - ReadWriteOnce
変更した YAML ファイルを利用してポッドを再作成します。
$ podman kube play --replace jenkins-pod.yml Pods stopped: 468dd0a6802590f969d0b608bba1165ad12cdff0f674879e665774eb7541abfd Pods removed: 468dd0a6802590f969d0b608bba1165ad12cdff0f674879e665774eb7541abfd Volumes removed: Volumes: jenkins_home Pod: e39f112fa66e3c0d84f071dfb4eb75c3425feff5fb8766840f09d040473819cc Containers: aac35e8eb9b0b352eb6663e1ccc2444e9f389ed5ceeec5eb46a5b032325e4257 fb295a5e5dec791747e799f6d0194a04d718d90f818e66533563ae6925aeab46
ウェブブラウザで http://localhost:8080/
にアクセスして、 Unlock Jenkins の画面が表示されることが確認できました。最後にボリュームのディレクトリも確認します。
$ podman unshare ls -lna $mntPoint 合計 36 drwxr-xr-x 12 1000 1000 4096 3月 24 11:05 . drwx------ 3 1000 1000 19 3月 24 10:14 .. drwxr-xr-x 3 1000 1000 24 3月 24 10:14 .cache drwxr-xr-x 3 1000 1000 19 3月 24 10:14 .java -rw-r--r-- 1 1000 1000 0 3月 24 11:05 .lastStarted -rw-r--r-- 1 1000 1000 1663 3月 24 11:05 config.xml -rw-r--r-- 1 1000 1000 250 3月 24 11:05 copy_reference_file.log (以下略)
コンテナの中から見てボリュームのルートディレクトリの所有者が jenkins
となりました。これにより、ポッドを何度再作成しても Jenkins が正常に起動するようになりました。
まとめ
Podman を使用してポッドを実行する際にはまったことを2点ご紹介しました。ホストからログアウトすることでコンテナが停止してしまうことを防止するには、 loginctl enable-linger $UID
コマンドを実行します。ボリュームのルートディレクトリの所有者を指定するには、 Kubernetes YAML ファイルの PersistentVolumeClaim
にアノテーション volume.podman.io/uid
および volume.podman.io/gid
を追加します。 Podman と Docker で異なることも多いようです。