Trabajando con Pods en Podman
¿Qué es un Pod?
- Un Pod es un concepto que proviene de Kubernetes.
- En Kubernetes los contenedores se ejecutan en Pods. En inglés Pod significa “vaina”, y podemos entender un Pod como una envoltura que contiene uno o varios contenedores.
- Un Pod representa un conjunto de contenedores que comparten almacenamiento y una única IP.
- Un Pod recibe una dirección IP que es compartida por todos los contenedores.
- Esto significa que todos los servicios que se ejecutan en los diferentes contenedores pueden referirse entre sí como localhost, mientras que los contenedores externos seguirán contactando con la dirección IP del Pod.
Gestión de Pods con Podman
- Cada Pod en Podman incluye un contenedor llamado “infra”.
- Este contenedor no hace nada más que dormir.
- Su propósito es mantener los espacios de nombres asociados con el pod y permitir a Podman conectar otros contenedores al pod.
- Esto le permite iniciar y detener contenedores dentro del Pod.
- El contenedor infra por defecto está basado en la imagen
localhost/podman-pause
.
- Como vemos en la imagen el Pod puede estar formada por varios contenedores:
- Si seguimos la filosofía de Kubernetes cada Pod tendrá un contenedor principal encargado de ofrecer el servicio.
- Si necesitamos realizar procesos auxiliares fuertemente acoplados con el principal, podemos tener contenedores secundarios que llamamos contenedores sidecar. Por ejemplo: Un servidor web nginx con un servidor de aplicaciones PHP-FPM, que se implementaría mediante un solo Pod, pero ejecutando un proceso de nginx en un contenedor y otro proceso de php-fpm en otro contenedor.
- Evidentemente también podemos utilizar Pods multicontenedores para desplegar aplicaciones que necesitan más de un servicio para su funcionamiento.
- En el diagrama anterior, también observamos el proceso conmon, este es el monitor del contenedor. Es un pequeño programa en C cuyo trabajo es monitorizar los contenedores y permitimos conectarnos a ellos. Cada contenedor tiene su propia instancia de conmon, independientemente se ejecute dentro de un Pod:
$ podman run -d quay.io/libpod/banner $ pstree systemd─┬─... ├─conmon───nginx───2*[nginx]
- En Podman podemos trabajar con Pods en entornos rootful y rootless.
Gestión de Pods
La gestión de los Pods la realizaremos utilizando el comando podman pod
.
Creación de un Pod
Para crear un Pod, usamos el comando podman pod create
, por ejemplo para crear un Pod llamado pod1
, ejecutamos:
$ podman pod create --name pod1
Podemos visualizar el Pod que hemos creado:
$ podman pod ps
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
45d3c5f01747 pod1 Created 8 seconds ago 41f153d29f02 1
Como vemos esta compuesto por un contenedor, que será el contenedor infra. Si queremos mostrar los nombres de los contenedores de los que está formado, usamos el parámetro --ctr-names
:
$ podman pod ps --ctr-names
POD ID NAME STATUS CREATED INFRA ID NAMES
45d3c5f01747 pod1 Created 32 seconds ago 41f153d29f02 45d3c5f01747-infra
Añadir contenedores a un Pod
Para añadir un contenedor a un Pod, a la hora de crearlo debemos indicar el nombre del Pod:
$ podman run -dt --pod pod1 --name principal alpine:latest top
Al iniciar el contenedor principal, el Pod se ha iniciado:
$ podman pod ps
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
45d3c5f01747 pod1 Running 8 minutes ago 41f153d29f02 2
Comprobamos que tenemos dos contenedores en el Pod. Podemos visualizar los contenedores con la información del Pod al que pertenecen, ejecutando:
$ podman ps --pod
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES POD ID PODNAME
41f153d29f02 localhost/podman-pause:4.9.4-1711445992 9 minutes ago Up 3 minutes 45d3c5f01747-infra 45d3c5f01747 pod1
dde078d7cf4e docker.io/library/alpine:latest top 3 minutes ago Up 3 minutes principal 45d3c5f01747 pod1
Ciclo de vida de un Pod
Aunque podemos controlar el ciclo de vida independientemente de cada contenedor, al operar sobre el Pod estaremos actuando sobre todos los contenedores. Por ejemplo, para pausar y continuar los contenedores de un Pod:
$ podman pod pause pod1
$ podman pod unpause pod1
Para parar e iniciar todos los contenedores:
$ podman pod stop pod1
$ podman pod start pod1
Para reiniciar los contenedores de un Pod:
$ podman pod restart pod1
Obteniendo información de un Pod
Podemos obtener la información detallada de un Pod ejecutando:
$ podman pod inspect pod1
Si tenemos un sólo contenedor principal en el Pod podemos ver los logs de salida, ejecutando:
$ podman pod logs pod1
Sin embargo, si añadimos un segundo contenedor:
$ podman run -dt --pod pod1 --name secundario alpine:latest top
$ podman ps --pod
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES POD ID PODNAME
41f153d29f02 localhost/podman-pause:4.9.4-1711445992 19 minutes ago Up 13 minutes 45d3c5f01747-infra 45d3c5f01747 pod1
dde078d7cf4e docker.io/library/alpine:latest top 13 minutes ago Up 13 minutes primario 45d3c5f01747 pod1
b220cb7dfdf1 docker.io/library/alpine:latest top About a minute ago Up About a minute secundario 45d3c5f01747 pod1
Para obtener los logs hay que especificar el contenedor con el parámetro -c
:
$ podman pod logs -c secundario pod1
Finalmente para ver los procesos que se están ejecutando en los distintos contenedores:
$ podman pod top pod1
USER PID PPID %CPU ELAPSED TTY TIME COMMAND
0 1 0 0.000 14m54.557309601s ? 0s /catatonit -P
root 1 0 0.000 14m54.559080387s pts/0 0s top
root 1 0 0.000 2m55.562434639s pts/0 0s top
Eliminar los Pods
Al borrar un Pod, se eliminaran todos los contenedores que forman parte de él. Si queremos borrar un Pod debe estar parado:
$ podman pod stop pod1
$ podman pod rm pod1
Si queremos forzar la eliminación de un Pod aunque este en ejecución:
$ podman pod rm -f pod1
Si queremos borrar todos los Pods parados y en ejecución:
$ podman pod rm --all
$ podman pod rm -f --all
Por último para eliminar los Pods que están parados:
$ podman pod prune
WARNING! This will remove all stopped/exited pods..
Are you sure you want to continue? [y/N]
Configuración de red en los contenedores de un Pod
Cuando creamos un Pod, este recibe una dirección IP, que será compartida con todos los contenedores.
- En el caso de Pods rootful, si no indicamos lo contrario, el Pod se conectará con la red bridge por defecto.
- En el caso de Pods rootless, por defecto se conectará a la red slirp4netns.
A la hora de trabajar con Pods tenemos que tener en cuenta las siguientes características.
- Si queremos ofrecer algún servicio en un puerto, el mapeo del puerto se indicará en la creación del Pod.
- Los contenedores se pueden comunicar entre ellos usando la interfaz loopback: la dirección IP
127.0.0.1
o el nombrelocalhost
.
Podemos crear un Pod conectado a cualquier otra red:
$ podman network create mired
$ podman pod create --name pod2 --network mired
$ podman pod ps
POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS
1f6ed1602460 pod2 Created 6 minutes ago be8baecabdc2 1
$ podman pod start pod2
$ podman inspect --format='{{range .NetworkSettings.Networks }}{{.IPAddress}}{{end}}' be8baecabdc2
10.89.0.2
Exponer un servicio en un Pod
Al crear un Pod indicamos el puerto que vamos a mapear con la opción -p
o --publish
. Evidentemente sólo podremos tener un contenedor que ofrezca el servicio en el puerto que estamos mapeando:
$ podman pod create --name pod3 -p 8080:80
A continuación añadimos un contenedor que ofrece un servicio web en el puerto 80:
$ podman run -d --pod pod3 --name webserver quay.io/libpod/banner
Vemos el Pod y el contenedor que hemos creado:
$ podman pod ps --ctr-names
POD ID NAME STATUS CREATED INFRA ID NAMES
688e66fd19dd pod3 Running 2 minutes ago 2d994796c3f2 688e66fd19dd-infra,webserver
$ podman ps --pod
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES POD ID PODNAME
2d994796c3f2 localhost/podman-pause:4.9.4-1711445992 2 minutes ago Up About a minute 0.0.0.0:8080->80/tcp 688e66fd19dd-infra 688e66fd19dd pod3
b7f0b9aa1aa0 quay.io/libpod/banner:latest nginx -g daemon o... About a minute ago Up About a minute 0.0.0.0:8080->80/tcp webserver 688e66fd19dd pod3
Podríamos crear un Pod y añadir un contenedor en la misma instrucción, para ello al indicar el nombre del Pod donde se crea el contenedor ponemos el valor new
:
$ podman run -d --pod new:pod4 --name webserver2 -p 8081:80 quay.io/libpod/banner
Para acceder al servicio, utilizamos la IP del host y el puerto que hemos mapeado:
$ curl http://localhost:8080
___ __
/ _ \___ ___/ /_ _ ___ ____
/ ___/ _ \/ _ / ' \/ _ `/ _ \
/_/ \___/\_,_/_/_/_/\_,_/_//_/
Comunicación entre contenedores dentro del Pod
Como hemos indicado para que un contenedor acceda al servicio de otro contenedor dentro del Pod, deberá usar la interfaz loopback: la dirección IP 127.0.0.1
o el nombre localhost
:
$ podman run -it --pod pod3 --name cliente quay.io/libpod/banner curl http://localhost
___ __
/ _ \___ ___/ /_ _ ___ ____
/ ___/ _ \/ _ / ' \/ _ `/ _ \
/_/ \___/\_,_/_/_/_/\_,_/_//_/
Almacenamiento compartido entre los contenedores de un Pod
Aunque cada contenedor puede tener su medio de almacenamiento independiente. Al crear un Pod podemos indicar un punto de montaje, que será compartido entre todos los contenedores del Pod.
El punto de montaje se puede indicar usando volúmenes o usando bind mount.
Ejemplo de almacenamiento compartido en un Pod
En este ejemplo vamos a tener el siguiente escenario:
- Creamos un Pod, llamado
pod5
donde vamos a mapear el puerto 8082 al puerto 80 del Pod, y vamos a indicar un punto de montaje con un volumen. - El contenedor
web
se crea a partir de la imagen nginx, es el contenedor principal, encargado de servir la web. En este contenedor montamos el volumen en su DocumentRoot (/usr/share/nginx/html
). Este servidor web va a servir el ficheroindex.html
que está modificando el otro contenedor. - El contenedor
sidecar
es el auxiliar. En este caso, cada segundo, va a modificar el ficheroindex.html
que sirve el contenedor principal.
Para ello primero creamos el Pod, en el este caso usando un volumen:
$ podman pod create --name pod5 -p 8082:80 -v vol1:/usr/share/nginx/html
Utilizando bind mount, quedaría de la siguiente forma. Utilizaremos la opción :z
si estamos usando SELinux:
$ podman pod create --name pod5 -p 8082:80 -v ${PWD}/directorio_compartido:/usr/share/nginx/html:z
A continuación añadimos los contenedores:
$ podman run --pod pod5 -d --name web docker.io/nginx
$ podman run --pod pod5 -d --name sidecar docker.io/debian bash -c "while true; do date >> /usr/share/nginx/html/index.html;sleep 1;done"
Podemos comprobar que los dos contenedores tienen el volumen montado en el directorio indicado:
$ podman inspect --format='{{json .Mounts}}' web
[{"Type":"volume","Name":"vol1","Source":"/var/lib/containers/storage/volumes/vol1/_data","Destination":"/usr/share/nginx/html","Driver":"local","Mode":"","Options":["nosuid","nodev","rbind"],"RW":true,"Propagation":"rprivate"}]
$ podman inspect --format='{{json .Mounts}}' sidecar
[{"Type":"volume","Name":"vol1","Source":"/var/lib/containers/storage/volumes/vol1/_data","Destination":"/usr/share/nginx/html","Driver":"local","Mode":"","Options":["nosuid","nodev","rbind"],"RW":true,"Propagation":"rprivate"}]
Por último, podemos acceder al puerto 8082 de la dirección IP del host, para acceder al servicio web:
$ curl http://localhost:8082
Thu Apr 4 18:03:12 UTC 2024
Thu Apr 4 18:04:44 UTC 2024
Thu Apr 4 18:04:45 UTC 2024
...
Leave a Comment
Your email address will not be published. Required fields are marked *