Red Hatでソリューションアーキテクトをしている田中司恩(@tnk4on)です。
Red Hat Enterprise Linux(以下、RHEL) 8向けPodmanの新しいパッケージ(v3.2.3)が公開され、ルートレスモードでdocker-composeが使用できるようになりました。以前の記事ではrootモードでdocker-composeを動かす方法を紹介しました。
今回はPodman v3.2以降で対応したルートレスモードでdocker-composeを実行する方法について紹介します。
- Podman v3.2.3のアップデート
- ルートレスでpodman.socketサービスを起動する
- docker-composeコマンドをコンテナで実行する
- ルートレスでdocker-composeを実行してみる
- ルートレスモードとpodman network
- ルートレスモードにおける所有権の問題
- まとめ
Podman v3.2.3のアップデート
2021年8月10日にRHEL 8 向けのcontainer-toolsのアップデートが公開されています。
Podman v3.2.3のパッケージ情報はこちら。
アップデート後のPodmanのバージョンは下記のようになっています。
$ podman version Version: 3.2.3 API Version: 3.2.3 Go Version: go1.15.7 Built: Tue Jul 27 16:29:39 2021 OS/Arch: linux/amd64
Podman v3.2.3へアップデートするにはdnfコマンドでcontainer-toolsまたはpodman単体のアップデートを実施して下さい。
$ sudo dnf update podman
または
$ sudo dnf module update container-tools
ルートレスでpodman.socketサービスを起動する
ルートレスでdocker-composeを実行する場合もrootモードと同じくPodman APIソケット(podman.socketサービス)の起動が必要です。起動するには一般ユーザーで下記コマンドを実行します。
パッケージのアップデート後、念の為systemd マネージャー設定を再読み込み
$ systemctl --user daemon-reload
podman.socketサービスを有効にし、すぐにサービスを起動
$ systemctl --user enable --now podman.socket Created symlink /home/user/.config/systemd/user/sockets.target.wants/podman.socket → /usr/lib/systemd/user/podman.socket.
サービスが起動しているか確認
$ systemctl --user status podman.socket
● podman.socket - Podman API Socket
Loaded: loaded (/usr/lib/systemd/user/podman.socket; enabled; vendor preset: enabled)
Active: active (listening) since Mon 2021-08-16 01:16:36 JST; 26s ago
Docs: man:podman-system-service(1)
Listen: /run/user/1000/podman/podman.sock (Stream)
CGroup: /user.slice/user-1000.slice/user@1000.service/podman.socket
8月 16 01:16:36 localhost.localdomain systemd[12425]: Listening on Podman API Socket.
Podman APIの動作確認
$ curl -s --unix-socket /run/user/1000/podman/podman.sock http://d/v3.0.0/libpod/info | jq .version
出力結果を開く
{
"APIVersion": "3.2.3",
"Version": "3.2.3",
"GoVersion": "go1.15.7",
"GitCommit": "",
"BuiltTime": "Tue Jul 27 16:29:39 2021",
"Built": 1627370979,
"OsArch": "linux/amd64"
}
- ルートレスモードのPodman APIソケットのパスは
systemctl --user status podman.socketの出力内容を参照 - Podman APIソケットの詳細については以前の記事を参考にしてください
次はdocker-composeコマンドの準備を行います。
(参考)RHEL 8製品ドキュメントの該当項目について
RHEL 8の製品ドキュメントにも該当項目がありますが内容が古いのであまり参考になりません。現時点では参考程度にご参照下さい。
14.2. ルートレスモードで systemd を使用した Podman API の有効化 Red Hat Enterprise Linux 8 | Red Hat Customer Portal
- RHEL 8.4 + Podman v3では
podman.socketやpodman.serviceの手動作成は不要 - 検証手順の
podman-remoteの導入不要
- RHEL 8.4 + Podman v3では
docker-composeコマンドをコンテナで実行する
以前の記事ではdocker-composeのインストールにpipを使用しましたが、Python/pipの環境に余計なものを入れたくない場合もあると思います。また、Docker公式ドキュメントではGitHubリポジトリからdocker-composeバイナリを入手して使用する方法が紹介されています。*1
これらの方法に代わり、docker-compose自体をコンテナで実行することで不要なものを追加せずにdocker-composeコマンドを実行することが可能となります。コンテナイメージはDocker公式が公開しているものがあるのでそちらを利用します。
コンテナでdocker-composeを実行する時の構成は下記のようになります。

下記の内容を~/.bashrcに追記することで、コンテナ実行のコマンドをdocker-composeのエイリアスとして使用できます。
alias docker-compose='podman run --rm \
-v /run/user/1000/podman/podman.sock:/var/run/docker.sock:z \
-v "$PWD:/$PWD:z" \
-w "/$PWD" \
docker/compose:1.29.2'
- オプションの補足
-v /run/user/1000/podman/podman.sock:/var/run/docker.sock:z:ルートレスモードのAPIソケットをコンテナ内のdocker.sockにマウントする-v "$PWD:/$PWD:z":カレントディレクトリをコンテナ内のWORKDIRにマウントする-w "/$PWD":コンテナ内のWORKDIRを指定docker/compose:1.29.2:実行するdocker-composeのイメージタグを指定。最新は1.29.2。
編集後、ファイルの再読み込み
$ source ~/.bashrc
初回実行時のみコンテナイメージを先に取得しておく(2回目以降の実行は不要)
$ podman pull docker.io/docker/compose:1.29.2
docker-composeの動作確認
1) 作業ディレクトリを作成し、SELinuxコンテキストを変更する
$ mkdir test && cd test $ chcon -R -t container_file_t . $ ls -lZ ../ 合計 0 drwxrwxr-x. 2 user user unconfined_u:object_r:container_file_t:s0 6 8月 16 21:02 test
- docker-composeのエイリアス実行時にカレントディレクトリをマウントするため、
container_file_tのラベルが設定されていることが必要です
2) docker-compose versionを実行し、正常にコマンドが実行できることを確認
$ docker-compose version docker-compose version 1.29.2, build 5becea4 docker-py version: 5.0.0 CPython version: 3.7.10 OpenSSL version: OpenSSL 1.1.1k 25 Mar 2021
これでルートレスでdocker-composeを実行する環境が整いました。
ルートレスでdocker-composeを実行してみる
以前の記事と同じくdocker-composeでWordPressを実行します。 docker-compose.ymlも同様にDocker Hubに記載のサンプルを使用します。
作業ディレクトリを作成し、docker-compose.ymlファイルを作成
$ mkdir wordpress && cd wordpress $ chcon -R -t container_file_t . $ vi docker-compose.yml
docker-compose.ymlの内容
version: '3.1'
services:
wordpress:
image: wordpress
restart: always
ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: exampleuser
WORDPRESS_DB_PASSWORD: examplepass
WORDPRESS_DB_NAME: exampledb
volumes:
- wordpress:/var/www/html
db:
image: mysql:5.7
restart: always
environment:
MYSQL_DATABASE: exampledb
MYSQL_USER: exampleuser
MYSQL_PASSWORD: examplepass
MYSQL_RANDOM_ROOT_PASSWORD: '1'
volumes:
- db:/var/lib/mysql
volumes:
wordpress:
db:
docker-compose upの実行(※SELinuxのエラーが出た場合は次項の対応を実施)
$ docker-compose up -d Creating network "wordpress_default" with the default driver Creating volume "wordpress_wordpress" with default driver Creating volume "wordpress_db" with default driver Pulling wordpress (wordpress:)... Pulling db (mysql:5.7)... Creating wordpress_wordpress_1 ... Creating wordpress_db_1 ... Creating wordpress_db_1 ... done Creating wordpress_wordpress_1 ... done
- エイリアスコマンドにPodman APIソケット(及びdocker.sock)を直接指定しているので事前にDOCKER_HOST環境変数の設定は不要です
起動したコンテナの確認
$ podman ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6098cc19a0d7 docker.io/library/mysql:5.7 mysqld 8 seconds ago Up 7 seconds ago wordpress_db_1 6f6d0aa34eb0 docker.io/library/wordpress:latest apache2-foregroun... 8 seconds ago Up 7 seconds ago 0.0.0.0:8080->80/tcp wordpress_wordpress_1
ブラウザでhttp://<ホストのIPアドレス>:8080へアクセスしてWordPressのインストール画面が表示されたらOKです。
SELinuxエラーの対応
docker-compose up時に下記のようなエラーが出た場合はSELinuxのポリシーに引っかかっています
$ docker-compose up -d [8] Failed to execute script docker-compose Traceback (most recent call last): File "urllib3/connectionpool.py", line 677, in urlopen File "urllib3/connectionpool.py", line 392, in _make_request File "http/client.py", line 1277, in request File "http/client.py", line 1323, in _send_request File "http/client.py", line 1272, in endheaders File "http/client.py", line 1032, in _send_output File "http/client.py", line 972, in send File "docker/transport/unixconn.py", line 43, in connect PermissionError: [Errno 13] Permission denied (以下省略)
audit2allowコマンドを使ってモジュールの作成とインストールを行います
$ sudo ausearch -c 'docker-compose' --raw | audit2allow -M my-dockercompose $ sudo semodule -i my-dockercompose.pp
my-dockercompose.te の内容
$ cat my-dockercompose.te
module my-dockercompose 1.0;
require {
type container_t;
type container_runtime_t;
class unix_stream_socket connectto;
}
#============= container_t ==============
allow container_t container_runtime_t:unix_stream_socket connectto;
再度docker-compose upを実行してエラーが出ないことを確認します。
ルートレスモードとpodman network
ルートレスモードでdocker-composeが使えるようになった背景には、ルートレスモードでのネットワーク構成機能の追加があります(下記のRFEを参照)。
ルートレスモードでdocker-compose実行時のネットワーク構成の変化を確認してみます。
docker-compose起動前
$ podman network ls NETWORK ID NAME VERSION PLUGINS
docker-compose起動後
$ podman network ls NETWORK ID NAME VERSION PLUGINS b089aa3ab1f2 wordpress_default 0.4.0 bridge,portmap,firewall,tuning
$ podman network inspect wordpress_default
出力結果を開く
[
{
"args": {
"podman_labels": {
"com.docker.compose.network": "default",
"com.docker.compose.project": "wordpress",
"com.docker.compose.version": "1.29.2"
}
},
"cniVersion": "0.4.0",
"name": "wordpress_default",
"plugins": [
{
"bridge": "cni-podman1",
"hairpinMode": true,
"ipMasq": true,
"ipam": {
"ranges": [
[
{
"gateway": "10.89.0.1",
"subnet": "10.89.0.0/24"
}
]
],
"routes": [
{
"dst": "0.0.0.0/0"
}
],
"type": "host-local"
},
"isGateway": true,
"type": "bridge"
},
{
"capabilities": {
"portMappings": true
},
"type": "portmap"
},
{
"backend": "",
"type": "firewall"
},
{
"type": "tuning"
}
]
}
]
このようにdocker-compose upと同時に新しくネットワーク構成が作成されたことが確認できます。なお、docker-compose downを実行するとこのネットワーク構成は削除されます。
ルートレスモードにおける所有権の問題
ルートレスモードでコンテナ実行時の課題として、コンテナ内にホストのディレクトリをマウントして使用する場合の所有権の問題があります。 なお、PodmanのルートレスモードにおけるUIDの取り扱いについてはこの記事では詳細には触れませんが、過去の翻訳記事が参考になります。
例えばWordPressを使用する場合、wordpressおよびmysqlコンテナにマウントするディレクトリをホスト上に作成し、再度docker-compose upしてみると作成したフォルダのパーミッションがユーザー自身のものから変更されていることが確認できます。
再現手順
先程のwordpressフォルダの中にwordpressとdbの2つのフォルダを作成する
$ mkdir wordpress db $ chmod 777 wordpress db $ vi docker-compose.yml
docker-compose.ymlの内容
version: '3.1'
services:
wordpress:
image: wordpress
restart: always
ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: exampleuser
WORDPRESS_DB_PASSWORD: examplepass
WORDPRESS_DB_NAME: exampledb
volumes:
- ./wordpress:/var/www/html
db:
image: mysql:5.7
restart: always
environment:
MYSQL_DATABASE: exampledb
MYSQL_USER: exampleuser
MYSQL_PASSWORD: examplepass
MYSQL_RANDOM_ROOT_PASSWORD: '1'
volumes:
- ./db:/var/lib/mysql
#volumes:
# wordpress:
# db:
docker-compose up後のカレントディレクトリの所有権を確認
$ ls -l 合計 12 drwxrwxrwx. 6 100998 user 4096 8月 17 01:15 db -rw-rw-r--. 1 user user 585 8月 17 01:12 docker-compose.yml drwxr-xr-x. 5 100032 100032 4096 8月 17 01:15 wordpress
この場合、UIDの100998と100032はコンテナ内で実行しているプロセスのホスト上のUIDです。下記ではwww-dataとmysqlが該当します。
$ podman top wordpress_wordpress_1 user huser USER HUSER root 1000 www-data 100032 www-data 100032 www-data 100032 www-data 100032 www-data 100032 $ podman top wordpress_db_1 user huser USER HUSER mysql 100998
ホスト上では一般ユーザーとしてuser、UID:1000で実行されていますので異なる所有権のファイル/フォルダは直接操作が不可となります。
$ rm -rf db wordpress rm: 'db/mysql' を削除できません: 許可がありません rm: 'db/performance_schema' を削除できません: 許可がありません rm: 'db/sys' を削除できません: 許可がありません rm: 'db/exampledb' を削除できません: 許可がありません rm: 'wordpress/.htaccess' を削除できません: 許可がありません (以下、省略)
ホスト側とファイルのやり取りを行うにしても都度sudoコマンドを使用すればできなくも無いですが、所有権を意識しながらの操作となるのでかなり手間です。
対処としてはPodmanにはpodman unshareという便利なコマンドが用意されています。
$ podman unshare # ls -l 合計 12 drwxrwxrwx. 6 systemd-coredump root 4096 8月 17 01:15 db -rw-rw-r--. 1 root root 585 8月 17 01:12 docker-compose.yml drwxr-xr-x. 5 33 tape4096 8月 17 01:15 wordpress
podman unshareコマンドを使用する実行する一般ユーザーのUser Namespaceに入った状態になる- 先程のUID
100998と100032から所有者の表示が変更されていることが確認できる
このようにpodman unshareを使うことでsudoを使わずにUser Namespace内で直接ファイル、ディレクトリの操作が行うことができるようになります。
podman unshareについては下記のブログも参考にしてください。
- Running rootless Podman as a non-root user | Enable Sysadmin
- Dealing with user namespaces and SELinux on rootless containers | Enable Sysadmin
まとめ
Podman v3.2以降で対応したルートレスでdocker-composeを実行する方法を紹介しました。
RHEL 8向けのPodmanパッケージもv3.2.3へと更新されたのでRHEL 8でもすぐに使うことが可能です。
docker-composeの実行についてはコンテナとして実行する方法を紹介しました。これにより実行元ホストに追加でインストール作業が不用になります。
また、ルートレスモードの課題としてディレクトリマウント時に所有権の問題を紹介しました。podman unshareを使うことで一般ユーザーのままホスト側とコンテナ間でのファイル操作などが容易になります。
なお、docker-composeを使用せずにpodman runで直接コンテナを起動する場合は--userns keep-idという便利なオプションが使えます。しかしながらこれはPodman固有のオプションのためDockerのためのアプリであるdocker-composeでは実装されておらず使用できません。複数のコンテナを実行しつつホスト側のディレクトリをマウントする場合はPodman podsの利用を推奨します。
docker-compose から Podman podsへ移行する方法についてはまた別の機会に紹介したいと思います。
*1:pipでのインストールは代替え手段として書かれているので、本来はこちらがメインのインストール方法の模様