Podman Container Tools
Podman 1 container tools…
- Designed with security in mind…
- …support for rootless container instances build-in
- …does not depend on a service daemon
- …provides more isolation than Docker
- …multiple users on a single host can all run their own containers and local image stores
- Default for Fedora & Enterprise Linux
- Integrates easily Systemd service units
- Docker compatible command line experience, as a drop-in replacement
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
podman info
displays information about the configurationpodman version
print the program version
Container images are stored in…
/var/lib/containers
for the root users$HOME/.local/share/containers/
for users
/var/run/containers/storage
…volatile run-time data for container instances
pull
& search
Supports all transports from man containers-transports
:
# Pull from a container registry
podman pull quay.io/username/myimage
# Pull from a container registry with short-name resolution
podman pull fedora
# Pull from a container registry via the docker transport
podman pull docker://quay.io/username/myimage
# Pull from a local directory
podman pull dir:/tmp/myimage
# Pull from a tarball in the docker-archive format
podman pull docker-archive:/tmp/myimage
# Pull from a local docker daemon
sudo podman pull docker-daemon:docker.io/library/myimage:33
# Pull from a tarball in the OCI-archive format
podman pull oci-archive:/tmp/myimage
Registry configuration is read in by this order:
/etc/containers/registries.conf
/etc/containers/registries.d/*
$HOME/.config/containers/registries.conf
Search for images on remote registries with search
sub-command:
--filter=is-official
for am officially curated list of images…- …ensures that security updates are applied in a timely manner
- …clear documentation, promote best practices, designed for common use cases
--list-tags
lists container image name and tag (version)
# search with filter
>>> podman search --filter=is-official rockylinux
INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
docker.io docker.io/library/rockylinux The official build of Rocky Linux. 23 [OK]
# list tags of a specific image
>>> podman search --list-tags quay.io/rockylinux/rockylinux
NAME TAG
quay.io/rockylinux/rockylinux 8.4-rc1
quay.io/rockylinux/rockylinux 8.4
quay.io/rockylinux/rockylinux 8.5
quay.io/rockylinux/rockylinux latest
quay.io/rockylinux/rockylinux 8
images
& rmi
Download a container image from a container registry to the local machine…
pull registry/username/image:tag
…downloads image from registryhistory image:tag
…prints details on the image buildrmi [-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}
- …binds
-d
/--detach
backgrounds the container…- …list running container instances with
podman ps
- …re-connect to a container shell with
podman attach
- …list running container instances with
--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 instancesstatus
resource usage statistics for any running containertop
displays running processes of a container
Working on container instance…
create [--name name] image:tag
…new container…not startedstart
…existing containerrestart
…running containerpause
temporarily halt…freeze processes…resume withunpause
stop
…graceful shutdownkill
…sends a signalrm [-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 instanceinspect
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 modeO
…mount temporary storage as overlay file-systemU
…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 volumesvolume create <name>
for a new volumevolume rm <name>
to delete a volumevolume 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
- …in the files
# 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 \
-c 'ls -l /mnt ; echo content > /mnt/folder/files'
/bin/sh 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
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 toon-failure
…may usealways
--wants
setWants=
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
- …pods reach each other over the
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
Footnotes
Podman Project
https://podman.io/docs
https://github.com/containers↩︎Dockerfile
Reference
https://docs.docker.com/reference/dockerfile/↩︎podman-systemd.unit
Manual
https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html↩︎