Podmanの導入

PodmanPOD Manager)は、Linuxコンテナの管理ツールで、Dockerと同様にOCI(Open Container Initiative)標準のコンテナを作成、管理、実行できるオープンソースのソフトウェアです。特に、rootless(非特権ユーザー)モードやシステムデーモン不要という特徴があります。

Podmanの特徴

  1. デーモンレス (Daemonless)
  • PodmanはDockerとは異なり、デーモンを使用せずに直接プロセスとしてコンテナを起動します。
  • 各コンテナは独立したプロセスとして動作するため、デーモンのクラッシュによるダウンタイムのリスクがありません。
  1. Rootless(非特権ユーザー)モード
  • 一般ユーザー権限でコンテナを作成・管理できます。これにより、セキュリティが向上し、システム全体に影響を与えるリスクを軽減できます。ルートレスモードとルートモードで読み込む設定ファイルが異なるため、必要に応じ使い分ける必要があります。

    $ podman info
    or
    $ sudo podman info
    

    使い分けのポイント

    条件 推奨モード
    安全性を優先し、権限を制限したい場合 rootless (podman)
    ネットワーク設定や iptables 制御が必要な場合 root (sudo podman)
    標準ユーザーの環境でポートを使用したい場合 rootless (podman)
    低ポート (80, 443 など) を利用する場合 root (sudo podman)
  1. Docker互換性
  • PodmanのコマンドはDocker CLIとほぼ同様です。
    例:

    $ podman run -it ubuntu bash
    
  • alias docker=podman とすることで、ほとんどのDockerコマンドがPodmanで動作します。

  1. システムユニットとの統合
  • podman generate systemd コマンドを使用することで、コンテナをSystemdサービスとして登録し、ブート時の自動起動が可能です。
  1. セキュリティ
  • PodmanはSELinuxseccompAppArmorなどのセキュリティ機能と連携し、強固な隔離環境を実現します。
  1. Kubernetes対応
  • PodmanはKubernetes YAMLマニフェストをそのまま使用できます。
  • podman play kube コマンドにより、Kubernetes形式のYAMLからコンテナの起動が可能です。
  1. イメージ管理
  • PodmanはDocker HubやQuay.ioなどのレジストリからOCIコンテナイメージを取得し、管理できます。

podmanのインストール

https://www.howtoforge.com/how-to-install-podman-on-ubuntu-22-04/

$ sudo apt install podman -y
$ podman -v
podman version 4.9.3

デフォルトではコンテナイメージの取得先が設定されていないので、以下のファイルで取得先を指定します。

/etc/containers/registries.conf

[registries.search]
registries=["registry.access.redhat.com", "registry.fedoraproject.org", "docker.io"]

dockerをpodmanに置き換えることで、ほぼ従来どおりの操作が可能です。

debianイメージのリストアップ

$ podman search debian
NAME                              DESCRIPTION
docker.io/library/debian          Debian is a Linux distribution that's compos...
docker.io/dockette/debian         My Debian Sid | Jessie | Wheezy Base Images
docker.io/corpusops/debian        debian corpusops baseimage
docker.io/treehouses/debian       
docker.io/rootpublic/debian       
docker.io/debian/snapshot         "debian", but with sources.list pointing to...
docker.io/debian/eol              End of Life Debian versions (pointing at arc...
docker.io/vulhub/debian           
docker.io/debian/buildd           https://hub.docker.com/_/debian/ but --varia...
docker.io/voxpupuli/debian        
docker.io/debian/archvsync        
docker.io/smartentry/debian       debian with smartentry
docker.io/i386/debian             Debian is a Linux distribution that's compos...
docker.io/amd64/debian            Debian is a Linux distribution that's compos...
docker.io/arm64v8/debian          Debian is a Linux distribution that's compos...
docker.io/arm32v7/debian          Debian is a Linux distribution that's compos...
docker.io/telkomindonesia/debian  All-in-One Debian (9.x) Base Image Repositor...
docker.io/arm32v5/debian          Debian is a Linux distribution that's compos...
docker.io/s390x/debian            Debian is a Linux distribution that's compos...
docker.io/ppc64le/debian          Debian is a Linux distribution that's compos...
docker.io/mips64le/debian         Debian is a Linux distribution that's compos...
docker.io/32bit/debian            Debian for i386 (32bit)
docker.io/dalaobanniubi/debian    
docker.io/vptech/debian           Docker images of Debian.
docker.io/dj0x/debian             GNU/Debian-Git-ssh

debianイメージのダウンロード

$ podman pull debian
Resolved "debian" as an alias (/etc/containers/registries.conf.d/shortnames.conf)
Trying to pull docker.io/library/debian:latest...
Getting image source signatures
Copying blob 155ad54a8b28 done   | 
Copying config d4ccddb816 done   | 
Writing manifest to image destination
d4ccddb816ba27eaae22ef3d56175d53f47998e2acb99df1ae0e5b426b28a076

イメージ確認

$ podman images
REPOSITORY                TAG         IMAGE ID      CREATED      SIZE
docker.io/library/debian  latest      d4ccddb816ba  3 weeks ago  121 MB

上記debianイメージからコンテナ起動

$ podman run -dit --name debian-container debian
f9782c009da00e4e521fd395551acc5491c207e957b8c949c26e53813df668ae

プロセス確認

$ podman ps
CONTAINER ID  IMAGE                            COMMAND     CREATED        STATUS        PORTS       NAMES
f9782c009da0  docker.io/library/debian:latest  bash        6 seconds ago  Up 6 seconds              debian-container

コンテナ内へアクセス(lsコマンド)

$ podman attach debian-container
# ls
bin  boot  dev	etc  home  lib	lib64  media  mnt  opt	proc  root  run  sbin  srv  sys  tmp  usr  var

Podmanデスクトップ

AMD64 binary(tar.gz)をダウンロード
任意のディレクトリで展開

展開したフォルダ内のpodman-desktopを起動

案内に従いkubectl,composeをインストール

トラブルシューティング


リセット

# podman system reset --force

This will delete all of your images, containers, and custom networks.

イメージの削除

$ rm -rf ~/.local/share/containers
$ sudo rm -rf /var/lib/containers

設定ファイルの削除

$ rm -f ~/.config/containers/{storage.conf,libpod.conf}
$ sudo rm -f /usr/share/containers/{storage.conf,libpod.conf}

AppArmor the issue?

AppArmor is similar to SELinux in that rules are added to the kernel to control process access to the system. Like SELinux, AppArmor could cause a permission-denied error. You can verify whether it is the problem by turning off AppArmor separation:

$ podman run --security-opt apparmor=unconfined …

Our team has heard of cases where unconfined is still not working. You can try disabling the apparmor profile or AppArmor itself.


ルートレスモードの制限事項


ソケットエラー

podman composeコマンドを実行する場合、ルートレスモードでは直接CLIを実行する権限が制限されているため、API (podman.sock) を介して制御する仕組みとなっています。デフォルトでは、このAPIを使用する設定となっていないため、docker composeコマンドで、このエラーが発生します。

Cannot connect to the Docker daemon at unix:///run/user/1000/podman/podman.sock. Is the docker daemon running?

podman.sockを使用:ユーザモードで実行(DOCKE_HOSTの設定は必要ないかもしれません)

$ export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
$ systemctl enable --now --user podman.socket

解除

$ unset DOCKER_HOST
$ systemctl --user disable podman.socket

ネットワーク

podman-network-create — Podman documentation

podman network create [options ] [name ]

Create a network with a static ipv4 and ipv6 subnet and set a gateway.

$ podman network create --subnet 192.168.55.0/24 --gateway 192.168.55.3 --subnet fd52:2a5a:747e:3acd::/64 --gateway fd52:2a5a:747e:3acd::10 podman4

Linuxの特権ポート(1024未満)にルートレスモードのコンテナをバインドする方法

https://linuxconfig.org/how-to-bind-a-rootless-container-to-a-privileged-port-on-linux

注)上記のルールではOUTPUTに追加しているが、PREROUTINGに追加。

1 : nftablesでポートリダイレクトルールを追加

$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443

注)ufwを使用している場合、ufwはiptables-nft(iptables)をバックエンドで使用しているためiptablesコマンドを使用すること。

ルールの確認(# handle:ハンドル番号の表示)これはnftでも可。

$ sudo nft -a list table ip nat
.....
.....
    chain PREROUTING { # handle 2
		type nat hook prerouting priority dstnat; policy accept;
		fib daddr type local counter packets 0 bytes 0 jump DOCKER # handle 3
		tcp dport 80 counter packets 0 bytes 0 redirect to :8080 # handle 44
		tcp dport 443 counter packets 0 bytes 0 redirect to :8443 # handle 45
	}
.....
.....

$ sudo nft -a list table ip6 nat

.....
.....
    chain PREROUTING { # handle 2
		type nat hook prerouting priority dstnat; policy accept;
		fib daddr type local counter packets 6681 bytes 530912 jump DOCKER # handle 3
		tcp dport 80 counter packets 0 bytes 0 redirect to :9080 # handle 14
		tcp dport 443 counter packets 10 bytes 800 redirect to :9443 # handle 15
	}
.....
.....
        

ルールを削除

$ sudo nft delete rule ip6 nat PREROUTING handle 14

systemdで起動時にルール適用

① スクリプトを作成

まず、iptables_redirect.sh を作成し、実行権限を付与

$ sudo nano /usr/local/bin/iptables_redirect.sh
#!/bin/bash

# IPv4 ルール
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443

# IPv6 ルール
ip6tables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
ip6tables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443

保存して、実行権限を付与:

$ sudo chmod +x /usr/local/bin/iptables_redirect.sh

② systemd サービスを作成

$ sudo nano /etc/systemd/system/iptables_redirect.service
[Unit]
Description=Set iptables NAT rules for local redirection
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/iptables_redirect.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

保存したら、以下のコマンドで有効化:

$ sudo systemctl enable iptables_redirect
$ sudo systemctl start iptables_redirect

2 : redirでポートリダイレクト

$ sudo apt install redir
$ sudo redir :80 127.0.0.1:8080

システムデーモンへ登録
/etc/systemd/system/redir.service

[Unit]
Description=Redirect tcp port 80 to 8080 with redir

[Service]
ExecStart=/bin/redir -sn :80 127.0.0.1:8080

[Install]
WantedBy=multi-user.target

起動

$ sudo systemctl enable --now redir.service

3 : CAP_NET_BIND_SERVICE

capabilities(7) - Linux manual page

CAP_NET_BIND_SERVICE
Bind a socket to Internet domain privileged ports (port
numbers less than 1024).

$ sudo setcap cap_net_bind_service=ep /usr/bin/rootlesskit

setcap コマンドの cap_net_bind_service=ep における ep は、 Effective (有効) と Permitted (許可) の2つのケーパビリティセットを指定するフラグです。

ep の意味:

  • e (Effective) : 実行時にこのケーパビリティを有効にする
  • p (Permitted) : プロセスがこのケーパビリティを保持することを許可する

つまり、 /usr/bin/rootlesskitcap_net_bind_service=ep を設定すると、このバイナリを実行するプロセスは 1024未満のポート (例: 80, 443) を root権限なし でバインドできるようになります。

解除

$ sudo setcap -r /usr/bin/rootlesskit

4 : 非特権ポートへ移行

ポート80以上が非特権ポートになります。

$ echo 80 | sudo tee /proc/sys/net/ipv4/ip_unprivileged_port_start

システムのデフォルトとして登録

$ echo net.ipv4.ip_unprivileged_port_start = 80 | sudo tee /etc/sysctl.d/90-unprivileged_port_start.conf

コンテナ起動時のネットワークオプション

podman-run — Podman documentation

–network=mode, –net

Set the network mode for the container.

Valid mode values are:

  • bridge[:OPTIONS,…]: Create a network stack on the default bridge. This is the default for rootful containers. It is possible to specify these additional options:
    • alias= name: Add network-scoped alias for the container.
    • ip= IPv4: Specify a static IPv4 address for this container.
    • ip6= IPv6: Specify a static IPv6 address for this container.
    • mac= MAC: Specify a static MAC address for this container.
    • interface_name= name: Specify a name for the created network interface inside the container.
    • host_interface_name= name: Specify a name for the created network interface outside the container.Any other options will be passed through to netavark without validation. This can be useful to pass arguments to netavark plugins.For example, to set a static ipv4 address and a static mac address, use --network bridge:ip=10.88.0.10,mac=44:33:22:11:00:99.
  • [:OPTIONS,…]: Connect to a user-defined network; this is the network name or ID from a network created by podman network create. It is possible to specify the same options described under the bridge mode above. Use the –network option multiple times to specify additional networks.
    For backwards compatibility it is also possible to specify comma-separated networks on the first –network argument, however this prevents you from using the options described under the bridge section above.
  • none: Create a network namespace for the container but do not configure network interfaces for it, thus the container has no network connectivity.
  • container: id: Reuse another container’s network stack.
  • host: Do not create a network namespace, the container uses the host’s network. Note: The host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
  • ns: path: Path to a network namespace to join.
  • private: Create a new namespace for the container. This uses the bridge mode for rootful containers and slirp4netns for rootless ones.
  • slirp4netns[:OPTIONS,…]: use slirp4netns(1) to create a user network stack. It is possible to specify these additional options, they can also be set with network_cmd_options in containers.conf:
    • allow_host_loopback=true|false: Allow slirp4netns to reach the host loopback IP (default is 10.0.2.2 or the second IP from slirp4netns cidr subnet when changed, see the cidr option below). The default is false.
    • mtu= MTU: Specify the MTU to use for this network. (Default is 65520).
    • cidr= CIDR: Specify ip range to use for this network. (Default is 10.0.2.0/24).
    • enable_ipv6=true|false: Enable IPv6. Default is true. (Required for outbound_addr6).
    • outbound_addr= INTERFACE: Specify the outbound interface slirp binds to (ipv4 traffic only).
    • outbound_addr= IPv4: Specify the outbound ipv4 address slirp binds to.
    • outbound_addr6= INTERFACE: Specify the outbound interface slirp binds to (ipv6 traffic only).
    • outbound_addr6= IPv6: Specify the outbound ipv6 address slirp binds to.
    • port_handler=rootlesskit: Use rootlesskit for port forwarding. Default.
      Note: Rootlesskit changes the source IP address of incoming packets to an IP address in the container network namespace, usually 10.0.2.100. If the application requires the real source IP address, e.g. web server logs, use the slirp4netns port handler. The rootlesskit port handler is also used for rootless containers when connected to user-defined networks.
    • port_handler=slirp4netns: Use the slirp4netns port forwarding, it is slower than rootlesskit but preserves the correct source IP address. This port handler cannot be used for user-defined networks.
  • pasta[:OPTIONS,…]: use pasta(1) to create a user-mode networking stack.
    This is the default for rootless containers and only supported in rootless mode.
    By default, IPv4 and IPv6 addresses and routes, as well as the pod interface name, are copied from the host. If port forwarding isn’t configured, ports are forwarded dynamically as services are bound on either side (init namespace or container namespace). Port forwarding preserves the original source IP address. Options described in pasta(1) can be specified as comma-separated arguments.
    In terms of pasta(1) options, –config-net is given by default, in order to configure networking when the container is started, and –no-map-gw is also assumed by default, to avoid direct access from container to host using the gateway address. The latter can be overridden by passing –map-gw in the pasta-specific options (despite not being an actual pasta(1) option).
    Also, -t none and -u none are passed if, respectively, no TCP or UDP port forwarding from host to container is configured, to disable automatic port forwarding based on bound ports. Similarly, -T none and -U none are given to disable the same functionality from container to host.
    Some examples:
    • pasta:–map-gw: Allow the container to directly reach the host using the gateway address.
    • pasta:–mtu,1500: Specify a 1500 bytes MTU for the tap interface in the container.
    • pasta:–ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,–dns-forward,10.0.2.3,-m,1500,–no-ndp,–no-dhcpv6,–no-dhcp, equivalent to default slirp4netns(1) options: disable IPv6, assign 10.0.2.0/24 to the tap0 interface in the container, with gateway 10.0.2.3, enable DNS forwarder reachable at 10.0.2.3, set MTU to 1500 bytes, disable NDP, DHCPv6 and DHCP support.
    • pasta:-I,tap0,–ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,–dns-forward,10.0.2.3,–no-ndp,–no-dhcpv6,–no-dhcp, equivalent to default slirp4netns(1) options with Podman overrides: same as above, but leave the MTU to 65520 bytes
    • pasta:-t,auto,-u,auto,-T,auto,-U,auto: enable automatic port forwarding based on observed bound ports from both host and container sides
    • pasta:-T,5201: enable forwarding of TCP port 5201 from container to host, using the loopback interface instead of the tap interface for improved performance

Invalid if using –dns, –dns-option, or –dns-search with –network set to none or container: id.

If used together with –pod, the container joins the pod’s network namespace.


Podman Quadlets with Podman Desktop

podman-systemd.unit - systemd units using Podman Quadlet

Quadletの使用例

REST API

Ver.4.9 リファレンス

ルートレスモード(ユーザレベル)で以下を実行(ルートモードの場合–user を省略してsudoで実行)。

$ systemctl --user status podman.socket

ソケットの有効化

$ systemctl --user enable --now podman.socket

Podman の REST API ソケットパスは以下のようになります:

  • rootless(ユーザー): /run/user/1000/podman/podman.sock
  • root権限: /run/podman/podman.sock

curlによる実行例(ルートレスモード)

podman info

$ curl --unix-socket /run/user/1000/podman/podman.sock http://d/v4.0.0/libpod/info

podman pull quay.io/containers/podman

$ curl -XPOST --unix-socket /run/user/1000/podman/podman.sock -v '[http://d/v4.0.0/images/create?fromImage=quay.io%2Fcontainers%2Fpodman'](http://d/v4.0.0/images/create?fromImage=quay.io%2Fcontainers%2Fpodman%27)

podman list images

$ curl --unix-socket /run/user/1000/podman/podman.sock -v 'http://d/v4.0.0/libpod/images/json' | jq