Podman Container Tools

Linux
Containers
Published

July 29, 2020

Modified

April 4, 2024

Podman 1 container tools…

Simple example using a container from a registry:

# start a dummy container
podman run hello-world

# list local container images
podman image ls -a

# state of containers
podman ps -a

# remove the dummy container
podman rm --latest

Container images are stored in…

/var/run/containers/storage…volatile run-time data for container instances

images & rmi

Download a container image from a container registry to the local machine…

  • pull registry/username/image:tag…downloads image from registry
  • history image:tag…prints details on the image build
  • rmi [-f] image:tag…removes a local image from local cache
# list all container images
podman images

# list container instances and associated container images
podman ps --all --storage

# remove a container image
podman rmi $image_id

run & exec

What is the difference…

  • run creates a temporary container, executes a command and stops the container afterwards
  • exec executes a command in a running container

Example:

# start a shell in a container
>>> podman run -it --name rockylinux quay.io/rockylinux/rockylinux:8.5 /bin/bash
[root@5120e9439b8f /]# exit   # stops the container
# lists the container instance in status exit
>>> podman ps -a
...

# start a background container instance
>>> podman run -d -it --name rockylinux quay.io/rockylinux/rockylinux:8.5 /bin/bash
>>> podman ps
CONTAINER ID  IMAGE                              COMMAND     CREATED        STATUS            PORTS       NAMES
43b7d61ad33a  quay.io/rockylinux/rockylinux:8.5  /bin/bash   6 seconds ago  Up 5 seconds ago              rockylinux

# run a command wihtin the contianer instance
>>> podman exec -it rockylinux ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

# launch an interactive shell within the container instance
>>> podman start --attach rl
...

# stop container instance and remove it
>>> podman stop rockylinux && podman rm rockylinux

Prominent options:

  • --name…the container
  • -i/--interactive & -t/--tty
    • …binds std{in,out,err} to the process in the container instance
    • …allocates a pseudo-TTY, combines std{out,err}
  • -d/--detach backgrounds the container…
    • …list running container instances with podman ps
    • …re-connect to a container shell with podman attach
  • --rm automatically remove the container instance after stop

Example:

# start container instance with auto-remove on stop
>>> podman run -d -it --rm --name rockylinux quay.io/rockylinux/rockylinux:8.5 /bin/bash

# run muliplte commands...
>>> podman exec rockylinux /bin/sh -c 'cat /etc/os-release | grep -i pretty ; pwd ; whoami'
PRETTY_NAME="Rocky Linux 8.5 (Green Obsidian)"
/
root

# container instance removed automatically after stop
>>> podman stop rockylinux && podman ps

Life-cycle

podman sub-commands use to manage container instances…

  • ps lists currently running container instances
  • status resource usage statistics for any running container
  • top displays running processes of a container

Working on container instance…

  • create [--name name] image:tag…new container…not started
  • start…existing container
  • restart…running container
  • pause temporarily halt…freeze processes…resume with unpause
  • stop…graceful shutdown
  • kill…sends a signal
  • rm [-f]…removes container (-f if running)
# remove all containers...
podman container cleanup --rm --all

Introspection

Basic HTTP server for the following examples:

# start an httpd container instance
podman run -d -p 8080:80 --name httpd httpd:latest
podman ps --filter name=httpd

# check if it is working by sending an HTTP request
curl localhost:8080
podman logs httpd
  • log prints the logs generate by a container instance
  • inspect prints a container instance run-time configuration…
    • …in JSON format…
    • …use jq to extract sections from the output
# get the port configuration
>>> podman inspect httpd | jq .[].NetworkSettings.Ports
{
  "80/tcp": [
    {
      "HostIp": "",
      "HostPort": "8080"
    }
  ]
}

Default configuration settings and metadata for a container image queried from the container registry:

podman inspect docker.io/library/rockylinux

Storage

Note the differences between volumes and bind-mounts

Volumes

Mounts a directory into the container instance…

  • Performance …managed by additional layer of indirection
    • …slightly more overhead then bind-mounts in terms of latency
    • …can use dedicated storage to improve I/O performance
  • Security …more secure then bind-mounts …better isolation
  • Operations
    • …ideal for persistent storage
    • …simplify data management and backups

Option -v, --volume=:

  • …argument: <src_path>:<dst_path>:<options>
  • Options …comma-separated list:
    • rw/ro read-only or read-write mode
    • O …mount temporary storage as overlay file-system
    • U …use host UID/GID based on the UID/GID within the container
    • Set proper SELinux labels on volumes:
      • Z private unshared
      • z shared between containers
    • …more options in the manual to podman-run
# start an HTTP server with a document root on the host
podman run --volume $PWD/_public:/usr/local/apache2/htdocs/:ro,z \
           --rm --detach --name httpd --publish-all httpd

Named volumes…take care of proper permissions and SELinux context

  • volume ls lists all volumes
  • volume create <name> for a new volume
  • volume rm <name> to delete a volume
  • volume inspect <name> for the metadata

…with an option like --volume <name>:<path>:...

Bind Mounts

Bind-mount a host directory to a container instance:

  • Performance …minimal overhead
    • …directly link host directories to container paths
    • …I/O directly handled by the host file-system
    • …latency similar to host-performance
    • …performance depending on the host file-system type (ext4,XFS,etc.)
  • Security …can expose sensitive host files to the container

Options --mount=

  • …argument <key>=<value>[,<key>=<value>,...] …order of the keys is not significant
podman run -it --mount type=bind,source=$PWD,target=/mnt alpine:latest /bin/bash

# list current mounts for a container
podman mount $container

Network Connections

Port mapping for non-root user is limited to ports above 1024:

  • --publish, -p=[[ip:][hostPort]:]containerPort[/protocol]
    • /protocol defaults to TCP
    • 0.0.0.0 or no IP address, bounds to all localhost IPs
  • --publish-all, -P publish all ports to random ports on localhost
  • ---expose [port] …open the port …does not forward
# map port 80 (TCP) from the container instance to localhost 8080
>>> podman run --name nginx -d -p 8080:80 nginx:alpine
# ...see the actual mapping
>>> podman port nginx                                 
80/tcp -> 0.0.0.0:8080

# random port selection
>>> podman run --name nginx -d -P nginx:alpine
>>> podman port nginx                         
80/tcp -> 0.0.0.0:34997

Container instances and the host share the same network name space

  • …enables communication between containers…
  • …using IP-address and port of the host
# Nginx container instance mapped to port 8080
podman run --name nginx -d -p 8080:80 nginx:alpine
# HTTP request from another container using the host IP address
podman run -it --rm rockylinux curl http://$(hostname -i | cut -d' ' -f4):8080

When containers are run by the root user…

  • …port forwarding works like described above
  • More advanced network configuration IP assignment…
    • …use of the Container Network Interface (CNI)…
    • …to implement a bridged network stack
    • CNI uses IP masquerading to keep pod networks independent

Dockerfile

Simple example building a container from a Dockerfile 2:

name=httpd
# write a simple container definition file
cat > Dockerfile <<EOF
FROM centos:latest
RUN yum -y install httpd
CMD [“/usr/sbin/httpd”, “-D”, “FOREGROUND”]
EXPOSE 80
EOF

# build the container
podman build -t $name .

# check the image
podman image ls $name

# start the container
podman run -dit -p 80:80 $name

Rootless Containers

Container without root permissions…

  • …no access to network shares
  • …no access to mounted volumes

Rootless Podman is not a setuid binary and gains no privileges when it runs.

  • Makes use of user namespace to shift the UIDs and GIDs of a block of users.
  • Requires the user running it to have a range of UIDs listed…
    • …in the files /etc/subuid and /etc/subgid
    • …with a line-format like USERNAME:UID:RANGE
# check the configuration
>>> grep $USER /etc/sub{uid,gid}
/etc/subuid:vpenso:100000:65536
/etc/subgid:vpenso:100000:65536

# assign UIDs and GIDs to a user
usermod --add-subuids 100000-165535 --add-subgids 100000-165535 johndoe $USER

User/Group IDs

If a user container runs with the root user, then root in the container is actually the host user:

# container to use for these examples...
>>> container=quay.io/rockylinux/rockylinux:latest

# processes inside the container runs as root
>>> id ; podman run -it --rm $container id
uid=1000(vpenso) gid=1000(vpenso) groups=1000(vpenso)...
uid=0(root) gid=0(root) groups=0(root)

# processes inside the container runs as a specified user
>>> id ; podman run -it --rm --user nobody $container id
uid=1000(vpenso) gid=1000(vpenso) groups=1000(vpenso)...
uid=65534(nobody) gid=65534(nobody) groups=65534(nobody)

All rootless containers run by a user run inside the same user namespace. Therefore rootless user containers can share resources with each other without needing to ask for root privileges.

Volume Permissions

When the container runs, any volumes which are shared with it, will appear inside the user namespace as owned by root/root:

# dummy folder to export
mkdir $HOME/folder

# share the host folder/ with the container (:Z creates a private unshared label)
>>> podman run -it --rm --volume $HOME/folder:/mnt/folder:Z $container \
        /bin/sh -c 'ls -l /mnt ; echo content > /mnt/folder/files'
drwxr-xr-x. 1 root root 0 Jun 15 14:18 folder

# permission on the host...
>>> ls -l $HOME/folder ; cat $HOME/folder/files 
-rw-r--r--. 1 vpenso vpenso 8 15. Jun 16:46 files
content

unshare

Useful for troubleshooting unprivileged operations…

  • …clearing storage and other data related to images and containers
  • Executes a command in the rootless podman user namespace

Grant the container user ID permissions to write to a host directory:

# create a folder
>>> mkdir /tmp/folder && ls -ld /tmp/folder
drwxr-xr-x. 2 vpenso vpenso 40 15. Jun 17:11 /tmp/folder/

# permission in the user-namespace
>>> podman unshare ls -ld /tmp/folder 
drwxr-xr-x. 2 root root 40 15. Jun 17:11 /tmp/folder

# try to access the folder (which is denied(
>>> podman run --rm \
               --user nobody \
               --volume /tmp/folder:/mnt/folder:Z \
               $container \
        /bin/sh -c 'ls -l /mnt ; echo nobody > /mnt/folder/file'
total 0
drwxr-xr-x. 2 root root 40 Jun 15 15:11 folder
/bin/sh: /mnt/folder/file: Permission denied

# grant the user nobody permission
>>> podman unshare chown nobody:nobody /tmp/folder                                                    
>>> podman unshare ls -ld /tmp/folder
drwxr-xr-x. 2 nobody nobody 40 15. Jun 17:11 /tmp/folder
>>> ls -ld /tmp/folder 
drwxr-xr-x. 2 165533 165533 40 15. Jun 17:11 /tmp/folder/

# with sufficient access privileges
>>> podman run --rm \
               --user nobody \
               --volume /tmp/folder:/mnt/folder:Z \
               $container \
        /bin/sh -c 'ls -l /mnt ; echo nobody > /mnt/folder/file' 
total 0
drwxr-xr-x. 2 nobody nobody 40 Jun 15 15:11 folder
>>> ls -ld /tmp/folder/file && cat /tmp/folder/file      
-rw-r--r--. 1 165533 165533 7 15. Jun 17:16 /tmp/folder/file
nobody

Systemd Integration

Podman facilitates integration with Systemd services…

  • …configure containers or pods to start at system boot
  • …automatically generate Systemd service files
  • …works on user and system-level
# if selinux=enforcing turn on...
sudo setsebool -P container_manage_cgroup on

Services Units

Running Podman inside of systemd services…

  • systemd starts a containerized application and manages its entire life-cycle
  • podman generate systemd creates a service unit for a single container
    • --name name for the unit-file
    • --files creates a file in working directory (instead of printing to stdout)
    • Additional options…
      • --restart-policy defaults to on-failure …may use always
      • --wants set Wants= option in the unit file
cd /etc/systemd/system
podman generate systemd --files --name $container_name
systemctl enable container-${container_name}.service

Systemd units using Podman Quadlet 3

Container PID 1

Podman support Systemd in a container…

  • Requires to run systemd as its initial command
  • Automatically sets up the tmpfs and Cgroups for systemd

Why run Systemd inside a container…

  • …allows to run multiple services in the container
  • …allows to use Systemd service units in the container
  • …enables to use Systemd for process management

Example:

container=rockylinux
buildah from --name $container rockylinux

# install a database and web-server...
# ...enable the systemd service units
buildah run $container -- /bin/sh -c '
        dnf install -y systemd mariadb-server nginx
        systemctl enable mariadb.service nginx.service
'

# config systemd and inital command
buildah config --cmd /usr/sbin/init $container

# make the container available on the local registry
buildah commit $container localhost/$container-nginx:latest

# start the container
podman run -d --name $container-nginx localhost/rockylinux-nginx

# list the processes in the container started by systemd
podman container top $container-nginx

Pods

Why using pods?

  • Group containers into a shared network name space…
  • Every pod includes an infra container
    • …holds namespaces associated to the pod
    • …port bindings …cgroup-parent …security context

podman pod commands…

# create a pod without extra attributes
podman pod create

# list all pods 
podman pod list
podman ps -a --pod

# remove a pod
podman pod rm $pod_id

Containers & Ports

Assign a name when creating a pod with option --name

  • …add containers by referencing the pod by name with option --pod
  • Networking within the pod…
    • …pods reach each other over the localhost
    • …external networks reach the pod (not the containers)
    • …map ports on the pod with option --publish

Prometheus example:

# Create a pod and expose the Prometheus server port
podman pod create --publish '9090:9090' --name prometheus

# Add the node-exporter to the pod
podman run --pod=prometheus --name=node-exporter -d \
           --volume=/:/host:ro,rslave \
           quay.io/prometheus/node-exporter:v1.7.0

# Simple Prometheus configuration …scraps from the local node-exporter
cat > prometheus.yml <<EOF
global:
  scrape_interval: 15s
scrape_configs:
  - job_name: 'localhost'
    static_configs:
      - targets: ['localhost:9100']
EOF

# Add the Prometheus server to the pod
podman run --pod=prometheus --name=prometheus-server -d \
           --volume=$PWD/prometheus.yml:/etc/prometheus/prometheus.yml:ro \
           --volume=$PWD/data:/prometheus,ro=false \
           quay.io/prometheus/prometheus:v2.45.4

YAML Definition

Generate a YAML definition from an existing pod:

podman generate kube prometheus > prometheus-pod.yml

Start a pod from a YAML definition file:

podman play kube prometheus-pod.yml