Ejecutar Contenedores con Podman en RHEL

Red Hat Enterprise LinuxBeginner
Practicar Ahora

Introducción

En este laboratorio, aprenderá a desplegar una aplicación web de múltiples niveles utilizando Podman en Red Hat Enterprise Linux (RHEL). Construirá una solución completa desplegando un contenedor de base de datos MariaDB como backend y un contenedor de servidor web Apache como frontend. Esta experiencia práctica le guiará a través de los pasos esenciales del despliegue de aplicaciones en contenedores, desde la configuración inicial hasta hacer que el servicio sea accesible públicamente.

Comenzará ejecutando un contenedor MariaDB y configurándolo al inicio con variables de entorno. A continuación, configurará el almacenamiento persistente para garantizar la durabilidad de los datos de la base de datos y creará una red personalizada para la comunicación entre contenedores. Luego, desplegará el servidor web Apache, expondrá su puerto para probar la conectividad y, finalmente, aprenderá a gestionar el contenedor como un servicio systemd para una operación robusta y automatizada.

Este es un Guided Lab, que proporciona instrucciones paso a paso para ayudarte a aprender y practicar. Sigue las instrucciones cuidadosamente para completar cada paso y obtener experiencia práctica. Los datos históricos muestran que este es un laboratorio de nivel principiante con una tasa de finalización del 93%. Ha recibido una tasa de reseñas positivas del 98% por parte de los estudiantes.

Ejecutar un Contenedor de Base de Datos MariaDB con Variables de Entorno

En este paso, aprenderá a ejecutar una aplicación en contenedor y a configurarla al inicio utilizando variables de entorno. Esta es una habilidad fundamental en la gestión de contenedores, que permite despliegues flexibles y seguros. Usaremos la imagen oficial de MariaDB como ejemplo, ya que requiere varios parámetros de configuración para inicializar una base de datos.

Primero, asegúrese de estar en el directorio de trabajo correcto. Todo el trabajo para este laboratorio se realizará dentro del directorio ~/project.

cd ~/project

Antes de ejecutar un contenedor, es una buena práctica extraer explícitamente la imagen del registro. Esto asegura que tiene la versión correcta localmente. Usaremos la imagen mariadb:10.6 para este laboratorio para asegurar la consistencia.

podman pull mariadb:10.6

Seleccione la imagen mariadb:10.6 del registro de Docker.

Debería ver una salida que indica que la imagen se está descargando y extrayendo.

10.6: Pulling from library/mariadb
...
Status: Downloaded newer image for mariadb:10.6
docker.io/library/mariadb:10.6

Ahora, puede ejecutar el contenedor MariaDB. El comando podman run crea e inicia un nuevo contenedor. Usaremos varias flags (banderas):

  • -d: Ejecuta el contenedor en modo detached (en segundo plano).
  • --name mariadb_server: Asigna un nombre memorable a nuestro contenedor.
  • -e VARIABLE=value: Establece una variable de entorno dentro del contenedor. La imagen MariaDB las utiliza para configurar la base de datos en el primer lanzamiento.

Ejecute el siguiente comando para iniciar su contenedor MariaDB. Estamos estableciendo la contraseña de root y también creando una nueva base de datos llamada webappdb con un usuario dedicado webappuser.

podman run -d \
  --name mariadb_server \
  -e MARIADB_ROOT_PASSWORD=supersecret \
  -e MARIADB_DATABASE=webappdb \
  -e MARIADB_USER=webappuser \
  -e MARIADB_PASSWORD=userpass \
  mariadb:10.6

El comando generará un ID de contenedor largo, lo que confirma que el contenedor ha sido creado.

a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2

Para verificar que el contenedor se está ejecutando, use el comando podman ps.

podman ps

Debería ver mariadb_server en la lista de contenedores en ejecución.

CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS      NAMES
a1b2c3d4e5f6   mariadb:10.6   "docker-entrypoint.s…"   15 seconds ago   Up 14 seconds   3306/tcp   mariadb_server

Finalmente, verifiquemos los logs (registros) del contenedor para asegurarnos de que la base de datos se inicializó correctamente utilizando las variables de entorno que proporcionamos.

podman logs mariadb_server

Desplácese por los logs. Está buscando una línea que indique que el servidor está listo para las conexiones, lo que confirma un inicio exitoso. La salida será extensa, pero un mensaje clave de éxito cerca del final se ve así:

...
2024-05-20 10:30:00+00:00 [Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/
...
2024-05-20 10:30:15+00:00 [Note] mariadbd: ready for connections.
Version: '10.6.x-MariaDB-1:10.6.x+maria~ubu2004'  socket: '/run/mysqld/mysqld.sock'  port: 3306  mariadb.org binary distribution

Ha lanzado y configurado con éxito un contenedor MariaDB utilizando variables de entorno.

Configurar Almacenamiento Persistente para el Contenedor MariaDB

En este paso, aprenderá a configurar el almacenamiento persistente para un contenedor. Por defecto, cualquier dato creado dentro de un contenedor se almacena en una capa escribible que está ligada al ciclo de vida del contenedor. Si elimina el contenedor, todos esos datos se pierden. Para aplicaciones con estado (stateful applications) como las bases de datos, esto no es ideal. Para solucionar esto, usamos volúmenes o bind mounts (montajes de enlace) de Podman para almacenar los datos en el sistema de archivos del host, independientemente del contenedor.

Primero, necesitamos eliminar el contenedor que creamos en el paso anterior, ya que lo relanzaremos con una nueva configuración de almacenamiento.

Detenga el contenedor mariadb_server en ejecución:

podman stop mariadb_server

Verá el nombre del contenedor como salida, confirmando que el comando fue recibido.

mariadb_server

Ahora, elimine el contenedor detenido:

podman rm mariadb_server

De nuevo, el nombre del contenedor se repetirá.

mariadb_server

A continuación, cree un directorio en su máquina host dentro del directorio ~/project. Este directorio contendrá los archivos de la base de datos MariaDB.

mkdir ~/project/mariadb_data

Cuando se usa Podman en modo rootless (sin root), necesitamos establecer los permisos correctos para el directorio montado. El contenedor MariaDB se ejecuta como un usuario específico (UID 999), por lo que necesitamos asegurarnos de que el directorio sea accesible. Usaremos la flag --userns=keep-id y estableceremos los permisos apropiados:

chmod 755 ~/project/mariadb_data

Ahora, ejecute el contenedor MariaDB de nuevo. Este comando es similar al del paso anterior, pero con la adición de la flag -v y --userns=keep-id para manejar correctamente la asignación del espacio de nombres del usuario. La flag -v monta el directorio ~/project/mariadb_data desde su host en el directorio /var/lib/mysql dentro del contenedor, que es donde MariaDB almacena sus datos. Usamos $(pwd)/mariadb_data para proporcionar la ruta absoluta requerida al comando podman.

podman run -d \
  --name mariadb_server \
  --userns=keep-id \
  -e MARIADB_ROOT_PASSWORD=supersecret \
  -e MARIADB_DATABASE=webappdb \
  -e MARIADB_USER=webappuser \
  -e MARIADB_PASSWORD=userpass \
  -v $(pwd)/mariadb_data:/var/lib/mysql:Z \
  mariadb:10.6

El sufijo :Z en el montaje del volumen le dice a Podman que reetiquete el contenido con una etiqueta privada no compartida, lo cual es importante para la compatibilidad con SELinux.

Después de que el contenedor se inicie, puede verificar que los datos se están almacenando en su máquina host. Enumere el contenido del directorio ~/project/mariadb_data.

ls -l ~/project/mariadb_data

Debido a que el motor de la base de datos del contenedor se ha inicializado, verá varios archivos y directorios creados dentro de ~/project/mariadb_data. Esto confirma que sus datos ahora son persistentes. Incluso si elimina el contenedor, estos datos permanecerán.

total 110632
-rw-rw---- 1 labex labex    16384 May 20 10:45 aria_log.00000001
-rw-rw---- 1 labex labex       52 May 20 10:45 aria_log_control
-rw-rw---- 1 labex labex      983 May 20 10:45 ib_buffer_pool
-rw-rw---- 1 labex labex 12582912 May 20 10:45 ibdata1
-rw-rw---- 1 labex labex 50331648 May 20 10:45 ib_logfile0
-rw-rw---- 1 labex labex 50331648 May 20 10:45 ib_logfile1
drwx------ 2 labex labex     4096 May 20 10:45 mysql
drwx------ 2 labex labex     4096 May 20 10:45 performance_schema
drwx------ 2 labex labex     4096 May 20 10:45 sys
drwx------ 2 labex labex     4096 May 20 10:45 webappdb

Ha configurado con éxito su contenedor MariaDB para usar almacenamiento persistente, asegurando que los datos de su base de datos sobrevivan a los reinicios y eliminaciones del contenedor.

Crear una Red Personalizada y Desplegar un Servidor Web Apache

En este paso, creará una red personalizada para sus contenedores e implementará un servidor web Apache. Si bien Podman proporciona una red predeterminada, usar redes personalizadas es una práctica recomendada. Proporcionan un mejor aislamiento y, lo más importante, habilitan la resolución DNS automática entre contenedores. Esto permite que los contenedores se comuniquen entre sí utilizando sus nombres, lo cual es más confiable que usar direcciones IP que pueden cambiar.

Primero, creemos una red bridge (puente) personalizada para nuestra aplicación. La nombraremos webapp-network.

podman network create webapp-network

El comando generará el nombre de la red recién creada.

webapp-network

Puede listar todas las redes de Podman para confirmar que la suya se creó correctamente.

podman network ls

Debería ver webapp-network en la lista, junto con las redes predeterminadas.

NETWORK ID     NAME               DRIVER    SCOPE
...
f1e2d3c4b5a6   webapp-network     bridge    local
...

A continuación, necesitamos recrear nuestro contenedor mariadb_server en esta nueva red. Debido a la configuración del backend de la red en este entorno, no podemos conectar un contenedor existente a una nueva red. En cambio, detendremos y recrearemos el contenedor con la nueva configuración de red.

Detenga el contenedor mariadb_server en ejecución:

podman stop mariadb_server

Elimine el contenedor detenido:

podman rm mariadb_server

Ahora, recree el contenedor MariaDB con la nueva red. Este comando es similar al del paso anterior, pero con la adición de la flag --network webapp-network:

podman run -d \
  --name mariadb_server \
  --network webapp-network \
  --userns=keep-id \
  -e MARIADB_ROOT_PASSWORD=supersecret \
  -e MARIADB_DATABASE=webappdb \
  -e MARIADB_USER=webappuser \
  -e MARIADB_PASSWORD=userpass \
  -v $(pwd)/mariadb_data:/var/lib/mysql:Z \
  mariadb:10.6

Ahora, implementemos nuestro servidor web. Usaremos la imagen oficial de Apache httpd. Primero, cree un directorio en el host para almacenar los archivos de su sitio web.

mkdir ~/project/webapp_content

Cree un archivo index.html simple en este nuevo directorio. Esta será la página de inicio de nuestra aplicación web.

echo "<h1>Welcome to My Web App</h1>" > ~/project/webapp_content/index.html

Establezca los permisos correctos para el directorio de contenido de la aplicación web para asegurar que el contenedor Apache pueda acceder a los archivos:

chmod 755 ~/project/webapp_content

Ahora, ejecute el contenedor Apache httpd. Lo conectaremos a nuestra webapp-network y montaremos el directorio webapp_content como un volumen. Esto asegura que el servidor web pueda servir el archivo index.html que acabamos de crear.

podman run -d \
  --name web_server \
  --network webapp-network \
  -v $(pwd)/webapp_content:/usr/local/apache2/htdocs/:Z \
  httpd:2.4

Desglosemos las opciones:

  • --network webapp-network: Conecta el nuevo contenedor a nuestra red personalizada.
  • -v $(pwd)/webapp_content:/usr/local/apache2/htdocs/:Z: Esto monta nuestro directorio local webapp_content en el contenedor en /usr/local/apache2/htdocs/, que es el directorio predeterminado desde el cual Apache sirve los archivos. El sufijo :Z le dice a Podman que reetiquete el contenido con una etiqueta privada no compartida para la compatibilidad con SELinux.

Verifique que ambos contenedores se estén ejecutando.

podman ps

Ahora debería ver tanto mariadb_server como web_server en la lista de contenedores en ejecución.

CONTAINER ID  IMAGE                           COMMAND           CREATED         STATUS         PORTS       NAMES
6a3f46c0ab3a  docker.io/library/mariadb:10.6  mariadbd          29 seconds ago  Up 29 seconds  3306/tcp    mariadb_server
da5d52ce9c41  docker.io/library/httpd:2.4     httpd-foreground  7 seconds ago   Up 7 seconds   80/tcp      web_server

Ambos contenedores ahora están en la misma red personalizada y pueden comunicarse entre sí por nombre.

Exponer el Puerto del Servidor Web y Probar la Conectividad

En este paso, aprenderá a exponer el puerto de un contenedor a la máquina host, haciendo que el servicio sea accesible desde fuera de la red aislada del contenedor. Nuestro servidor web Apache está en ejecución, pero aún no podemos acceder a él desde el navegador o la línea de comandos de nuestro host. Solucionaremos esto publicando el puerto del contenedor.

Los mapeos de puertos se definen cuando se crea un contenedor. Por lo tanto, primero debemos detener y eliminar el contenedor web_server que creamos en el paso anterior. No se preocupe por el contenido del sitio web; está seguro en el directorio ~/project/webapp_content en nuestro host porque usamos un bind mount (montaje de enlace).

Primero, detenga el contenedor:

podman stop web_server
web_server

A continuación, elimine el contenedor detenido:

podman rm web_server
web_server

Ahora, ejecutaremos el contenedor web_server de nuevo, pero esta vez agregaremos la flag -p (o --publish) para mapear un puerto del host a un puerto en el contenedor. Mapearemos el puerto 8080 en el host al puerto 80 (el puerto HTTP predeterminado) dentro del contenedor.

podman run -d \
  --name web_server \
  --network webapp-network \
  -v $(pwd)/webapp_content:/usr/local/apache2/htdocs/:Z \
  -p 8080:80 \
  httpd:2.4

La nueva flag -p 8080:80 le dice a Podman que reenvíe todo el tráfico del puerto 8080 en el host al puerto 80 dentro del contenedor web_server.

Verifiquemos que el contenedor se está ejecutando y que el puerto está correctamente mapeado usando podman ps.

podman ps

Observe la columna PORTS para el contenedor web_server. Ahora muestra el mapeo de 0.0.0.0:8080 a 80/tcp, lo que indica que el puerto se expuso correctamente.

CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                  NAMES
c5d4e3f2a1b6   httpd:2.4      "httpd-foreground"       10 seconds ago   Up 9 seconds    0.0.0.0:8080->80/tcp   web_server
a1b2c3d4e5f6   mariadb:10.6   "docker-entrypoint.s…"   25 minutes ago   Up 25 minutes   3306/tcp               mariadb_server

Finalmente, probemos la conectividad desde nuestra máquina host usando el comando curl. Esto envía una solicitud HTTP a localhost en el puerto 8080.

curl http://localhost:8080

Debería ver el contenido HTML de su archivo index.html como salida, confirmando que su servidor web ahora es accesible desde el host.

<h1>Welcome to My Web App</h1>

Ha expuesto con éxito su servidor web en contenedores a la máquina host, un paso crítico para hacer que las aplicaciones estén disponibles para los usuarios.

Gestionar el Contenedor del Servidor Web como un Servicio systemd

En este paso final, aprenderá a configurar un contenedor para que se inicie automáticamente, asegurando que su servicio sea resistente a fallos o reinicios del sistema. En un sistema Red Hat Enterprise Linux estándar, systemd es la herramienta principal para administrar servicios. Sin embargo, el entorno Podman en este laboratorio no usa systemd para administrar contenedores directamente.

En cambio, lograremos el mismo resultado, los reinicios automáticos del servicio, utilizando las políticas de reinicio integradas de Podman. Esta es la forma estándar, nativa de contenedor, de asegurar que un contenedor se inicie automáticamente por el demonio Podman. Configuraremos nuestro web_server para que siempre se reinicie si se detiene por cualquier motivo.

Primero, debemos eliminar el contenedor existente, ya que las políticas de reinicio solo se pueden aplicar cuando se crea un contenedor.

Detenga el contenedor web_server:

podman stop web_server
web_server

Y ahora elimínelo:

podman rm web_server
web_server

A continuación, vuelva a crear el contenedor web_server con la misma configuración que antes, pero agregue la flag --restart always. Esta flag indica al demonio Podman que supervise el contenedor y lo reinicie si alguna vez sale.

podman run -d \
  --name web_server \
  --network webapp-network \
  -v $(pwd)/webapp_content:/usr/local/apache2/htdocs/:Z \
  -p 8080:80 \
  --restart always \
  httpd:2.4

El contenedor se iniciará como de costumbre. Para confirmar que la política de reinicio está activa, puede inspeccionar la configuración del contenedor.

podman inspect web_server --format '{{.HostConfig.RestartPolicy.Name}}'

El comando debería devolver always, confirmando que la política está configurada.

always

Ahora, demostremos cómo funciona la política de reinicio reiniciando manualmente el contenedor para simular lo que sucedería después de un reinicio del sistema o una falla del contenedor.

Primero, verifiquemos la configuración actual de la política de reinicio:

podman inspect web_server --format '{{.HostConfig.RestartPolicy.Name}}'

Esto debería mostrar always, confirmando que nuestra política de reinicio está configurada.

always

Ahora, probemos el reinicio manual para simular la recuperación después de una falla:

podman start web_server
web_server

Verifique que el contenedor se esté ejecutando:

podman ps

Debería ver ambos contenedores en ejecución con la política de reinicio en su lugar:

CONTAINER ID   IMAGE          COMMAND                  CREATED              STATUS              PORTS                  NAMES
e7f6g5h4i3j2   httpd:2.4      "httpd-foreground"       About a minute ago   Up 5 seconds        0.0.0.0:8080->80/tcp   web_server
a1b2c3d4e5f6   mariadb:10.6   "docker-entrypoint.s…"   About an hour ago    Up About an hour    3306/tcp               mariadb_server

Finalmente, confirme que el servicio es accesible:

curl http://localhost:8080
<h1>Welcome to My Web App</h1>

Comprendiendo las políticas de reinicio:

La política --restart always que ha configurado asegura que:

  • El contenedor se reiniciará automáticamente si sale inesperadamente
  • El contenedor se iniciará automáticamente cuando se inicie el servicio Podman (como después de un reinicio del sistema)
  • Esto proporciona resiliencia para implementaciones de producción

Nota: En algunos entornos de laboratorio, el comportamiento de reinicio automático puede variar según la configuración de Podman y si el servicio del sistema Podman se está ejecutando. El objetivo clave del aprendizaje es comprender cómo configurar las políticas de reinicio para las implementaciones de producción.

Ha configurado con éxito su contenedor para que se administre como un servicio, asegurando que permanezca disponible automáticamente. Esto completa la gestión básica del ciclo de vida de una aplicación en contenedores.

Resumen

En este laboratorio, aprendió el proceso fundamental de implementar una aplicación web de múltiples contenedores en RHEL utilizando Podman. Comenzó ejecutando un contenedor de base de datos MariaDB, configurando su estado inicial, incluyendo la contraseña de root, una nueva base de datos y un usuario dedicado, pasando variables de entorno en tiempo de ejecución. Luego configuró el almacenamiento persistente para el contenedor de la base de datos, asegurando que los datos críticos se conserven a través de los reinicios del contenedor.

Para completar la pila de aplicaciones, creó una red personalizada para habilitar la comunicación segura y aislada entre los contenedores. Implementó un contenedor de servidor web Apache en esta red y expuso su puerto para permitir el acceso de usuarios externos. Finalmente, integró el contenedor del servidor web con systemd, administrándolo como un servicio del sistema para asegurar que se inicie automáticamente al arrancar y se ejecute de manera confiable, demostrando un patrón de implementación listo para producción.