¿Cómo ejecutar un script de Python al iniciar un contenedor Docker?

DockerBeginner
Practicar Ahora

Introducción

Docker es una herramienta poderosa para la contenerización de aplicaciones, lo que facilita el desarrollo, la implementación y la gestión de software. En este tutorial, exploraremos cómo ejecutar un script de Python cuando se inicia un contenedor Docker. Este enfoque le permite automatizar la implementación de su aplicación y optimizar su flujo de trabajo de desarrollo. Al final de este laboratorio, podrá crear un contenedor Docker que ejecute automáticamente su script de Python al inicio.

Creación de un Script de Python Simple

En este primer paso, crearemos un script de Python simple que se ejecutará cuando se inicie nuestro contenedor Docker. Este script imprimirá un mensaje y la fecha y hora actuales.

Comencemos creando un nuevo directorio para nuestro proyecto:

mkdir -p ~/project/python-docker
cd ~/project/python-docker

Ahora, creemos un script de Python simple llamado app.py usando el editor de texto nano:

nano app.py

En el editor nano, agregue el siguiente código Python:

import datetime
import time

print("¡Hola desde el contenedor Docker!")
print(f"Fecha y hora actuales: {datetime.datetime.now()}")

## Keep the container running to demonstrate it's working
print("El contenedor se está ejecutando...")
while True:
    print("El contenedor sigue ejecutándose... Presione Ctrl+C para detenerlo.")
    time.sleep(10)

Guarde el archivo presionando Ctrl+O, luego Enter, y salga de nano presionando Ctrl+X.

Este script:

  1. Imprimirá un mensaje de bienvenida
  2. Mostrará la fecha y hora actuales
  3. Mantendrá el contenedor en ejecución con un mensaje cada 10 segundos

Probemos nuestro script de Python para asegurarnos de que funcione correctamente:

python3 app.py

Debería ver una salida similar a:

¡Hola desde el contenedor Docker!
Fecha y hora actuales: 2023-10-12 14:30:45.123456
El contenedor se está ejecutando...
El contenedor sigue ejecutándose... Presione Ctrl+C para detenerlo.

Presione Ctrl+C para detener el script.

Ahora que tenemos un script de Python funcional, podemos proceder a crear un contenedor Docker que ejecutará este script cuando se inicie.

Creación de un Dockerfile

Ahora que tenemos nuestro script de Python listo, creemos un Dockerfile para definir cómo se debe construir nuestro contenedor Docker.

Un Dockerfile es un archivo de texto que contiene todos los comandos necesarios para construir una imagen Docker. Esta imagen contendrá nuestro script de Python y todas las dependencias necesarias.

En el mismo directorio (~/project/python-docker), cree un nuevo archivo llamado Dockerfile:

nano Dockerfile

Agregue el siguiente contenido al Dockerfile:

## Use the official Python image as base
FROM python:3.9-slim

## Set working directory
WORKDIR /app

## Copy the Python script to the container
COPY app.py .

## Command to run when the container starts
CMD ["python", "app.py"]

Guarde y salga de nano presionando Ctrl+O, luego Enter, y luego Ctrl+X.

Entendamos qué hace cada línea en el Dockerfile:

  1. FROM python:3.9-slim: Esto especifica la imagen base a utilizar. Estamos utilizando la imagen oficial de Python 3.9 con una variante slim para mantener el tamaño pequeño.

  2. WORKDIR /app: Esto establece el directorio de trabajo dentro del contenedor en /app.

  3. COPY app.py .: Esto copia nuestro script de Python desde la máquina host al directorio de trabajo actual en el contenedor (que es /app).

  4. CMD ["python", "app.py"]: Esto especifica el comando a ejecutar cuando se inicia el contenedor. En este caso, ejecutará nuestro script de Python.

Ahora, construyamos nuestra imagen Docker usando este Dockerfile:

docker build -t python-app .

Este comando construye una imagen Docker desde el Dockerfile en el directorio actual (.) y la etiqueta con el nombre python-app (-t significa tag).

Debería ver una salida similar a:

Sending build context to Docker daemon  3.072kB
Step 1/4 : FROM python:3.9-slim
 ---> 1bc6a8a2a52f
Step 2/4 : WORKDIR /app
 ---> Using cache
 ---> 2a3b7a28c9e5
Step 3/4 : COPY app.py .
 ---> Using cache
 ---> 9a0b7a1c8c2d
Step 4/4 : CMD ["python", "app.py"]
 ---> Using cache
 ---> 3b0c7a2d9f1e
Successfully built 3b0c7a2d9f1e
Successfully tagged python-app:latest

La salida puede variar, pero debería ver los mensajes "Successfully built" y "Successfully tagged" si todo salió bien.

Ahora ha creado con éxito una imagen Docker que contiene su script de Python y lo ejecutará cuando se inicie un contenedor desde esta imagen.

Ejecución del Contenedor Docker

Ahora que hemos construido nuestra imagen Docker, podemos ejecutar un contenedor basado en esta imagen. El contenedor ejecutará automáticamente nuestro script de Python cuando se inicie.

Para ejecutar un contenedor desde nuestra imagen, use el siguiente comando:

docker run --name python-container python-app

Este comando inicia un nuevo contenedor llamado python-container desde la imagen python-app que creamos en el paso anterior.

Debería ver una salida similar a:

Hello from the Docker container!
Current date and time: 2023-10-12 15:45:30.123456
Container is running...
Container still running... Press Ctrl+C to stop.
Container still running... Press Ctrl+C to stop.

El contenedor ahora se está ejecutando y ejecutando nuestro script de Python. El script está diseñado para seguir ejecutándose indefinidamente, imprimiendo un mensaje cada 10 segundos.

Presione Ctrl+C para dejar de ver los registros (logs), pero tenga en cuenta que el contenedor todavía se está ejecutando en segundo plano.

Para verificar que el contenedor se está ejecutando, use el siguiente comando:

docker ps

Debería ver su contenedor en la lista de contenedores en ejecución:

CONTAINER ID   IMAGE        COMMAND           CREATED         STATUS         PORTS     NAMES
1a2b3c4d5e6f   python-app   "python app.py"   1 minute ago    Up 1 minute              python-container

Para detener el contenedor, use el siguiente comando:

docker stop python-container

Para iniciar el contenedor de nuevo, use:

docker start python-container

Y para ver los registros (logs) del contenedor en ejecución:

docker logs python-container

Esto mostrará la salida de nuestro script de Python. Puede presionar Ctrl+C para salir de la vista de registros.

Para eliminar el contenedor cuando haya terminado, primero deténgalo si se está ejecutando, luego elimínelo:

docker stop python-container
docker rm python-container

Ahora ha creado y ejecutado con éxito un contenedor Docker que ejecuta un script de Python al inicio.

Uso de Variables de Entorno con Python en Docker

Las variables de entorno son una excelente manera de configurar su aplicación sin cambiar el código. En este paso, modificaremos nuestro script de Python para usar variables de entorno y actualizaremos nuestro Dockerfile para proporcionar estas variables.

Primero, actualicemos nuestro script de Python para leer una variable de entorno:

nano app.py

Modifique el script para incluir soporte de variables de entorno:

import datetime
import time
import os

## Get the environment variable with a default value if not set
user_name = os.environ.get('USER_NAME', 'Guest')

print(f"Hello, {user_name}, from the Docker container!")
print(f"Current date and time: {datetime.datetime.now()}")

## Keep the container running to demonstrate it's working
print("Container is running...")
while True:
    print("Container still running... Press Ctrl+C to stop.")
    time.sleep(10)

Guarde y salga de nano presionando Ctrl+O, luego Enter, y luego Ctrl+X.

Ahora, actualicemos nuestro Dockerfile para incluir una variable de entorno:

nano Dockerfile

Actualice el Dockerfile para incluir la variable de entorno:

## Use the official Python image as base
FROM python:3.9-slim

## Set working directory
WORKDIR /app

## Set environment variable
ENV USER_NAME="Docker User"

## Copy the Python script to the container
COPY app.py .

## Command to run when the container starts
CMD ["python", "app.py"]

Guarde y salga de nano, luego reconstruya la imagen Docker:

docker build -t python-app-env .

Ahora, ejecute un contenedor con la variable de entorno predeterminada:

docker run --name python-env-container python-app-env

Debería ver un saludo con "Docker User" en la salida:

Hello, Docker User, from the Docker container!
Current date and time: 2023-10-12 16:30:45.123456
Container is running...

Presione Ctrl+C para dejar de ver los registros (logs).

Detenga y elimine el contenedor:

docker stop python-env-container
docker rm python-env-container

También puede anular la variable de entorno al ejecutar el contenedor:

docker run --name python-env-container -e USER_NAME="LabEx Student" python-app-env

Esta vez, debería ver su nombre personalizado en el saludo:

Hello, LabEx Student, from the Docker container!
Current date and time: 2023-10-12 16:35:15.789012
Container is running...

Presione Ctrl+C para dejar de ver los registros (logs).

Detenga y elimine el contenedor:

docker stop python-env-container
docker rm python-env-container

El uso de variables de entorno es una práctica común en Docker para hacer que sus contenedores sean más configurables sin tener que reconstruir la imagen para pequeños cambios.

Uso de ENTRYPOINT vs CMD en Dockerfile

En Docker, hay dos instrucciones para especificar qué comando debe ejecutarse cuando se inicia un contenedor: CMD y ENTRYPOINT. Tienen diferentes propósitos y comportamientos, y comprender la diferencia es importante para la gestión de contenedores.

Entendiendo ENTRYPOINT y CMD

  • ENTRYPOINT: Define el ejecutable que se ejecutará cuando se inicie el contenedor. Es más difícil de anular en tiempo de ejecución.
  • CMD: Proporciona argumentos predeterminados para el ENTRYPOINT o puede especificar todo el comando si no se usa ENTRYPOINT. Es fácil de anular en tiempo de ejecución.

Exploremos la diferencia creando dos Dockerfiles diferentes.

Primero, cree un nuevo script de Python que acepte argumentos de línea de comandos:

nano greeting.py

Agregue el siguiente código:

import sys

print("Script started!")

if len(sys.argv) > 1:
    print(f"Arguments provided: {sys.argv[1:]}")
    for arg in sys.argv[1:]:
        print(f"- {arg}")
else:
    print("No arguments provided.")

print("Script finished!")

Guarde y salga de nano.

Ahora, creemos un Dockerfile que use CMD:

nano Dockerfile.cmd

Agregue el siguiente contenido:

FROM python:3.9-slim

WORKDIR /app

COPY greeting.py .

CMD ["python", "greeting.py", "default", "arguments"]

Guarde y salga de nano.

Construya la imagen:

docker build -t python-cmd -f Dockerfile.cmd .

Ahora, cree otro Dockerfile que use ENTRYPOINT:

nano Dockerfile.entrypoint

Agregue el siguiente contenido:

FROM python:3.9-slim

WORKDIR /app

COPY greeting.py .

ENTRYPOINT ["python", "greeting.py"]
CMD ["default", "arguments"]

Guarde y salga de nano.

Construya la imagen:

docker build -t python-entrypoint -f Dockerfile.entrypoint .

Probando CMD vs ENTRYPOINT

Ejecutemos contenedores desde ambas imágenes y observemos las diferencias.

Primero, ejecute un contenedor usando la imagen CMD sin argumentos adicionales:

docker run --name cmd-container python-cmd

La salida debería ser similar a:

Script started!
Arguments provided: ['default', 'arguments']
- default
- arguments
Script finished!

Ahora, ejecute un contenedor con la imagen CMD pero proporcione argumentos personalizados:

docker run --name cmd-container-custom python-cmd hello world

Salida:

Script started!
No arguments provided.
Script finished!

Observe que todo el comando fue reemplazado con hello world, que no se pasan como argumentos a nuestro script.

Ahora, ejecutemos un contenedor usando la imagen ENTRYPOINT sin argumentos adicionales:

docker run --name entrypoint-container python-entrypoint

Salida:

Script started!
Arguments provided: ['default', 'arguments']
- default
- arguments
Script finished!

Finalmente, ejecute un contenedor con la imagen ENTRYPOINT y proporcione argumentos personalizados:

docker run --name entrypoint-container-custom python-entrypoint hello world

Salida:

Script started!
Arguments provided: ['hello', 'world']
- hello
- world
Script finished!

Esta vez, nuestros argumentos se pasan correctamente al script de Python porque ENTRYPOINT define el ejecutable, y cualquier argumento adicional proporcionado a docker run se pasa a ese ejecutable.

Mejores Prácticas

  • Use ENTRYPOINT para contenedores que siempre deben ejecutar un comando específico (como nuestro script de Python)
  • Use CMD para proporcionar argumentos predeterminados que se pueden anular fácilmente
  • Combine ambos usando ENTRYPOINT para el comando y CMD para los argumentos predeterminados

Limpie los contenedores:

docker rm cmd-container cmd-container-custom entrypoint-container entrypoint-container-custom

Ahora ha aprendido la diferencia entre CMD y ENTRYPOINT en Docker, lo cual es esencial para controlar cómo se ejecutan sus scripts de Python cuando se inicia un contenedor.

Resumen

En este laboratorio, ha aprendido a ejecutar scripts de Python cuando se inicia un contenedor Docker. Esto es lo que ha logrado:

  1. Creó un script de Python simple que se ejecuta en un contenedor Docker
  2. Construyó una imagen Docker con un Dockerfile
  3. Ejecutó un contenedor Docker que ejecuta automáticamente el script de Python
  4. Usó variables de entorno para configurar su aplicación Python en Docker
  5. Comprendió la diferencia entre CMD y ENTRYPOINT en Dockerfiles

Estas habilidades son fundamentales para la contenerización de aplicaciones Python y la automatización de su despliegue. Ahora puede:

  • Empaquetar sus aplicaciones Python con todas sus dependencias
  • Configurar sus aplicaciones utilizando variables de entorno
  • Controlar cómo se inician sus aplicaciones dentro de los contenedores Docker
  • Elegir el mecanismo de inicio apropiado (CMD o ENTRYPOINT) para su caso de uso específico

Con este conocimiento, puede construir aplicaciones Docker más complejas que ejecutan scripts de Python automáticamente al inicio, haciendo que su proceso de despliegue sea más eficiente y confiable.