Saltar al contenido principal

Introducción a Podman

· 10 min de lectura
Álvaro Castillo
Sysadmin & DevOps Enthusiast

Una herramienta nativa para Linux que se ejecuta sin ser un servicio, es open source y está diseñada para ser fácil de utilizar. Se pueden ejecutar, construir, compartir y desplegar aplicaciones utilizando la definición "Open Containers Initiative (OCI)" e imágenes de contenedor. Los contenedores se pueden ejecutar siendo root o usuario sin privilegios sin hacer uso de ningún servicio que gestione prácticamente nada, que es lo contrario a Docker que es un servicio/cliente. Se pueden configurar los contenedores con systemd para que éstos inicien en caso de reinicio del sistema, por lo que es muy bueno.

La Open Containers Initiative es un pequeño gobierno establecido en 2015 por Docker y otros líderes en la industria de los contenedores que tienen como objetivo implementar estándares abiertos en la industria que gira en torno a los contenedores con el fin de evitar que múltiples empresas generen sus propios formas de crear y desplegar contenedores volviendo a un escenario de los 90 como pasó con las redes, que si un edificio tenía el producto de una empresa A, no era compatible con los dispositivos de la empresa B.

Actualmente hay dos tipos de especificaciones:

La especificación de tiempo de ejecución (runtime-spec) y la especificación de la imagen (image-spec). La primera trata de ver cómo se desempaqueta la imagen y se ejecuta, y en la segunda es cómo y dónde se descarga o se almacena la imagen que contiene ese sistema de archivos y que se ejecutará convirtiéndose en un contenedor. Las imágenes contienen tags para definir las versiones, por ejemplo, rockylinux:latest o rockylinux a secas se refiere a latest. Sin embargo, el proyecto puede tener versiones, por ejemplo:

  • rockylinux:8.5
  • rockylinux:7.9
  • rockylinux:alpha-stage
  • [...]

¿Cómo se puede usar?

Se puede utilizar desde usuario rooto desde un usuario sin privilegios siempre y cuando tenga el subid y subgid definidos.

Comandos de creación

Crear un contenedor con bash basado en una imagen de Rocky Linux

podman run -ti rockylinux:latest /bin/bash

Crear un contenedor con bash basado en una imagen cuya tag es la 8.5

podman run -ti rockylinux:8.5 /bin/bash

Crear un contenedor con nombre único

podman run -ti --name micontenedor rockylinux:latest /bin/bash

NOTA: Si no eliminas el contenedor, no podrás lanzar más contenedores con este nombre.

Crear una red

podman create network nombre_red

Crear un volumen

podman create volume nombre_volumen

Crear un contenedor con una red creada

podman run -ti --network hola_mundo rockylinux /bin/bash

Crear un contenedor con un volumen creado o crearlo "al vuelo"

NOTA: Es un volumen persistente, no se elimina después de finalizar el contenedor

podman run -ti --volume nuevo_volumen:/tmp/my_vol rockylinux /bin/bash

Crear un contenedor y utilizar un archivo como volumen persistente

podman run -ti --volume /home/sincorchetes/hello_world.txt:/tmp/hello_world.txt rockylinux /bin/bash

Crear un contenedor y utilizar un directorio como volumen persistente

podman run -ti --volume /home/sincorchetes/hello_world:/tmp/hello_world rockylinux /bin/bash

Crear un contenedor que se ejecute en segundo plano

NOTA: Este contenedor no tiene acceso al exterior, si haces un curl 127.0.0.1:80 no dará respuesta, además estará mal si lo ejecutas como usuario sin privilegios, ya que el kernel por seguridad no permite exponer programas con puertos inferiores a 1024.

podman run -d nginx 

Crear un contenedor que se ejecute en segundo plano y con mapeo de puertos

podman run -d -p 8080:80 nginx

Si haces un curl 127.0.0.1:8080 obtendrás la página de bienvenida de NGINX

Genera una plantilla servicio para que se ejecute en systemd

podman generate systemd nombre_contenedor|id_imagen

NOTA: Nos genera un .service en la salida de la terminal que podemos añadir como servicio del sistema o de usuario. Hay que añadirlo no se configura solo.

Ejemplo:

podman ps 
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a138796be81d docker.io/library/nginx:latest nginx -g daemon o... 21 minutes ago Up 21 minutes ago inspiring_hertz

podman generate systemd inspiring_hertz
# container-a138796be81d92ae13c7390fe65f3544558ec4640ed20446ac6595e24446dc27.service
# autogenerated by Podman 3.4.4
# Thu Jan 20 17:40:51 WET 2022

[Unit]
Description=Podman container-a138796be81d92ae13c7390fe65f3544558ec4640ed20446ac6595e24446dc27.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/run/user/1000/containers

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStart=/usr/bin/podman start a138796be81d92ae13c7390fe65f3544558ec4640ed20446ac6595e24446dc27
ExecStop=/usr/bin/podman stop -t 10 a138796be81d92ae13c7390fe65f3544558ec4640ed20446ac6595e24446dc27
ExecStopPost=/usr/bin/podman stop -t 10 a138796be81d92ae13c7390fe65f3544558ec4640ed20446ac6595e24446dc27
PIDFile=/run/user/1000/containers/overlay-containers/a138796be81d92ae13c7390fe65f3544558ec4640ed20446ac6595e24446dc27/userdata/conmon.pid
Type=forking

[Install]
WantedBy=default.target

Si queremos guardarlo, redirigimos la salida

podman generate systemd inspiring_hertz > nginx.service

Generando una plantilla para Kubernetes

podman generate kube 

Ejemplo:

podman generate kube inspiring_hertz
# Save the output of this file and use kubectl create -f to import
# it into Kubernetes.
#
# Created with podman-3.4.4
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2022-01-20T17:43:58Z"
labels:
app: inspiringhertz
name: inspiringhertz
spec:
containers:
- args:
- nginx
- -g
- daemon off;
image: docker.io/library/nginx:latest
name: inspiringhertz
securityContext:
capabilities:
drop:
- CAP_MKNOD
- CAP_NET_RAW
- CAP_AUDIT_WRITE

Si queremos guardarlo, redirigimos la salida

podman generate kube inspiring_hertz > nginx.yaml

Y para ejecutarlo en nuestro cluster (necesitaremos tener kubectl configurado)

kubectl apply -f nginx.yaml

Comandos de construcción

Modificar una imagen

podman run -ti rockylinux /bin/bash
# dnf install -y tmux
[...]
Installed:
libevent-2.1.8-5.el8.x86_64 tmux-2.7-1.el8.x86_64

Complete!

En otra terminal, recuperamos el contenedor

podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d56ed882cbc1 docker.io/library/rockylinux:latest /bin/bash 59 seconds ago Up About a minute ago funny_stonebraker

Hacemos un commit y lo guardamos con otro nombre

podman commit d56ed882cbc1 rockylinux_custom
Getting image source signatures
Copying blob 65dbea0a4b39 skipped: already exists
Copying blob 741ec203d982 done
Copying config 515a64e7ed done
Writing manifest to image destination
Storing signatures
515a64e7ed4cbb778b043820de1ee9054c62a83c3e0462f21b188263374ba522

Ejecutando la nueva imagen

podman run -ti rockylinux_custom tmux

Ó

podman run -ti rockylinux_custom /usr/bin/tmux

Construir una imagen partiendo de un Containerfile

Podemos hacer lo anterior, pero en vez de usar ese método, utilizar un Containerfile que nos da más libertad y facilidad, también te valen los Dockerfile, recordemos (OCI compliant)

Crearmos un archivo Containerfile

mkdir imagen
touch imagen/Containerfile

Editamos imagen/Containerfile

FROM rockylinux:latest
RUN dnf install -y tmux
CMD ["/usr/bin/tmux"]

Construimos la imagen:

podman build -t rockylinux_custom image

Arrancamos la imagen

podman run -ti rockylinux_custom

Se nos abrirá con tmux directamente.

Comandos para listar

Listar imágenes

podman image ls

Listar redes

podman network ls

Listar volúmenes

podman volume ls

Listar todos los ID de las imágenes

podman image ls | awk 'NR>1 { print $3 }'

Listar todos los ID de los contenedores que se están ejecutando

podman ps | awk 'NR>1 { print $3 }'

Listar todos los ID de los contenedores que se están o hayan sido ejecutados

podman ps -a| awk 'NR>1 { print $3 }'

Listar solo el nombre de los volúmenes

podman volume ls | awk 'NR>1 { print $2 }'

Listar solo el ID de las redes y su nombre

podman network ls | awk 'NR>1 { print $2 }'

Comandos de búsqueda

Buscar en todos los registries

podman search rockylinux

Buscar en un solo registry

podman search docker.io/rockylinux

Comandos para eliminar

Eliminar contenedor en ejecución

podman rm -f ID_contenedor

Eliminar volumen

NOTA: No debe haber ningún contenedor que esté explotando el volumen

podman volume rm nombre_volumen

Eliminar una imagen

podman image rm nombre_imagen

Eliminar una imagen (forma corta)

podman rmi nombre_imagen

Eliminar una red

podman network rm nombre_red

Eliminar imágenes que no se están utilizando

podman image prune

Eliminar todos los contenedores incluyendo los que ya han finalizado

podman rm -f $(podman ps -a | awk 'NR>1 { print $1 }')

Exportación/Importación

Exportar el FS de un contenedor en ejecución

podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c5d483dc9b77 docker.io/library/nginx:latest nginx -g daemon o... About a minute ago Up About a minute ago intelligent_archimedes

podman export c5d483dc9b77 > myfs.tar

Tendremos todo el contenido del contenedor exportado.

Importar un contenedor

podman import myfs.tar
Getting image source signatures
Copying blob 0f3fecb21929 done
Copying config d485510f4c done
Writing manifest to image destination
Storing signatures
sha256:d485510f4c1ba2ba61bbd2bcdd2e632f404be7ae90b81370fda41dd16b9d3936

podman image ls
[...]
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> d485510f4c1b 43 seconds ago 144 MB

¡Perfecto!

Comandos de ejecución

Si tenemos contenedores ejecutándose podemos acceder desde otra consola aunque el contenedor lo tengamos abierto en primer/segundo plano.

Obtenemos el contenedor al que queremos acceder

podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a138796be81d docker.io/library/nginx:latest nginx -g daemon o... 3 seconds ago Up 3 seconds ago inspiring_hertz

Accedemos a él

podman exec -ti a138796be81d /bin/sh
# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

También podemos acceder a él por el nombre

podman exec -ti inspiring_hertz /bin/sh

Troubleshooting

No puedo crear un contenedor con puertos inferiores al 1024 cuando lo trato de exponer, obtengo este mensaje:

podman run -d -p 80:8080 nginx
Error: rootlessport cannot expose privileged port 80, you can add 'net.ipv4.ip_unprivileged_port_start=80' to /etc/sysctl.conf (currently 1024), or choose a larger port number (>= 1024): listen tcp 0.0.0.0:80: bind: permission denied

Esto es debido a seguridad del kernel, solo root puede hacerlo, se puede remediar haciendo caso al mensaje, pero es mejor desplegar un reverse-proxy que atienda a los puertos no privilegiados.

Trato de eliminar una imagen y obtengo este resultado, sin embargo, no tengo ningún contenedor que la use:

podman image rm rockylinux
Error: Image used by 260f1173937ca4c82b345f54c3723908d6c4165936870914922a62e42076da00: image is in use by a container

Para solucionarlo:

podman ps -a
[...]
260f1173937c docker.io/library/rockylinux/rockylinux:latest bash 2 days ago Exited (0) 2 days ago
[...]
podman rm -f 260f1173937c
podman image rm rockylinux
Untagged: docker.io/library/rockylinux:latest
Deleted: 300e315adb2f96afe5f0b2780b87f28ae95231fe3bdd1e16b9ba606307728f55

O también

podman image rm -f rockylinux

NOTA: Esto eliminará también el contenedor que use la imagen.

Otra forma de eliminar una imagen

podman rmi rockylinux
podman rmi -f rockylinux

Trato de eliminar una red y obtengo este resultado, pero no hay ningún contenedor ejecutándose:

podman network rm hola
Error: "hola" has associated containers with it. Use -f to forcibly delete containers and pods: network is being used

Lo mismo sucede aquí, hay que forzar la eliminación y eso implica eliminar el contenedor que finalizó la ejecución.

podman network rm -f hola
hola

No puedo montar los archivos o directorios como volúmenes persistentes, pero forman parte de mi usuario

NOTA: Esto se debe a la protección de SELinux, Z le dice a podman que usas SELinux en el sistema

podman run -ti --volume /home/sincorchetes/Downloads:/tmp/Downloads:Z rockylinux /bin/bash 
podman run -d --volume /home/sincorchetes/Downloads:/tmp/Downloads:Z nginx

No puedo acceder a un contenedor en ejecución con exec

podman exec -ti a138796be81d /bin/ksh
Error: executable file `/bin/ksh` not found in $PATH: No such file or directory: OCI runtime attempted to invoke a command that was not found

Esto se debe porque no soporta esa shell o ese comando, si sabes la ruta

podman exec -ti a138796be81d cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/bin/rbash
/bin/dash
podman exec -ti a138796be81d /bin/dash
# echo $0
dash

Si buscas un archivo, pues un ls

podman exec -ti a138796be81d ls /etc
adduser.conf deluser.conf host.conf localtime pam.conf rc6.d subuid
alternatives dpkg hostname login.defs pam.d rcS.d systemd
apt e2scrub.conf hosts logrotate.d passwd resolv.conf terminfo
bash.bashrc environment init.d machine-id passwd- rmt timezone
bindresvport.blacklist fonts inputrc mke2fs.conf profile security ucf.conf
ca-certificates fstab issue motd profile.d selinux update-motd.d
ca-certificates.conf gai.conf issue.net mtab rc0.d shadow xattr.conf
cron.d group kernel netconfig rc1.d shadow-
cron.daily group- ld.so.cache nginx rc2.d shells
debconf.conf gshadow ld.so.conf nsswitch.conf rc3.d skel
debian_version gshadow- ld.so.conf.d opt rc4.d ssl
default gss libaudit.conf os-release rc5.d subgid

Fuentes