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.

¿Qué es la OCI?

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