Compartir comentarios
Las respuestas se generan en base a la documentación.

Usar contenedores para el desarrollo de Python

Requisitos previos

Completa Contenedorizar una aplicación Python.

Descripción general

En esta sección, aprenderás a configurar un entorno de desarrollo para tu aplicación contenedorizada. Esto incluye:

  • Agregar una base de datos local y persistir datos
  • Configurar Compose para actualizar automáticamente tus servicios de Compose en ejecución a medida que editas y guardas tu código

Obtener la aplicación de ejemplo

Necesitarás clonar un nuevo repositorio para obtener una aplicación de ejemplo que incluya la lógica para conectarse a la base de datos.

  1. Ve al directorio donde deseas clonar el repositorio y ejecuta el siguiente comando.

    $ git clone https://github.com/estebanx64/python-docker-dev-example
    
  2. En el directorio del repositorio clonado, crea manualmente los recursos de Docker o ejecuta docker init para crearlos.

    En el directorio del repositorio clonado, ejecuta docker init. Consulta el siguiente ejemplo para responder a las preguntas de docker init.

    $ docker init
    Welcome to the Docker Init CLI!
    
    This utility will walk you through creating the following files with sensible defaults for your project:
      - .dockerignore
      - Dockerfile
      - compose.yaml
      - README.Docker.md
    
    Let's get started!
    
    ? What application platform does your project use? Python
    ? What version of Python do you want to use? 3.12
    ? What port do you want your app to listen on? 8001
    ? What is the command to run your app? python3 -m uvicorn app:app --host=0.0.0.0 --port=8001
    

    Crea un archivo llamado .gitignore con el siguiente contenido.

    .gitignore
    # Archivos compilados en bytes / optimizados / DLL
    __pycache__/
    *.py[cod]
    *$py.class
    
    # Extensiones C
    *.so
    
    # Distribución / empaquetado
    .Python
    build/
    develop-eggs/
    dist/
    downloads/
    eggs/
    .eggs/
    lib/
    lib64/
    parts/
    sdist/
    var/
    wheels/
    share/python-wheels/
    *.egg-info/
    .installed.cfg
    *.egg
    MANIFEST
    
    # Informes de pruebas unitarias / cobertura
    htmlcov/
    .tox/
    .nox/
    .coverage
    .coverage.*
    .cache
    nosetests.xml
    coverage.xml
    *.cover
    *.py,cover
    .hypothesis/
    .pytest_cache/
    cover/
    
    # PEP 582; utilizado por ejemplo por github.com/David-OConnor/pyflow y github.com/pdm-project/pdm
    __pypackages__/
    
    # Entornos
    .env
    .venv
    env/
    venv/
    ENV/
    env.bak/
    venv.bak/

    Si no tienes instalado Docker Desktop o prefieres crear los recursos manualmente, puedes crear los siguientes archivos en el directorio de tu proyecto.

    Crea un archivo llamado Dockerfile con el siguiente contenido.

    Dockerfile
    # syntax=docker/dockerfile:1
    
    # Se proporcionan comentarios a lo largo de este archivo para ayudarte a empezar.
    # Si necesitas más ayuda, visita la guía de referencia de Dockerfile en
    # https://docs-docker.esdocu.com/go/dockerfile-reference/
    
    # ¿Quieres ayudarnos a mejorar esta plantilla? Comparte tus comentarios aquí: https://forms.gle/ybq9Krt8jtBL3iCk7
    
    ARG PYTHON_VERSION=3.12
    FROM python:${PYTHON_VERSION}-slim
    
    # Evita que Python escriba archivos pyc.
    ENV PYTHONDONTWRITEBYTECODE=1
    
    # Evita que Python almacene en búfer stdout y stderr para evitar situaciones en las que
    # la aplicación se bloquee sin emitir ningún registro debido al almacenamiento en búfer.
    ENV PYTHONUNBUFFERED=1
    
    WORKDIR /app
    
    # Crea un usuario no privilegiado bajo el cual se ejecutará la aplicación.
    # Consulta https://docs-docker.esdocu.com/go/dockerfile-user-best-practices/
    ARG UID=10001
    RUN adduser \
        --disabled-password \
        --gecos "" \
        --home "/nonexistent" \
        --shell "/sbin/nologin" \
        --no-create-home \
        --uid "${UID}" \
        appuser
    
    # Descarga las dependencias como un paso separado para aprovechar la caché de Docker.
    # Aprovecha un montaje de caché en /root/.cache/pip para acelerar las compilaciones posteriores.
    # Aprovecha un montaje de tipo bind en requirements.txt para evitar tener que copiarlos
    # en esta capa.
    RUN --mount=type=cache,target=/root/.cache/pip \
        --mount=type=bind,source=requirements.txt,target=requirements.txt \
        python -m pip install -r requirements.txt
    
    # Cambia al usuario no privilegiado para ejecutar la aplicación.
    USER appuser
    
    # Copia el código fuente en el contenedor.
    COPY . .
    
    # Expone el puerto en el que escucha la aplicación.
    EXPOSE 8001
    
    # Ejecuta la aplicación.
    CMD ["python3", "-m", "uvicorn", "app:app", "--host=0.0.0.0", "--port=8001"]

    Crea un archivo llamado compose.yaml con el siguiente contenido.

    compose.yaml
    # Se proporcionan comentarios a lo largo de este archivo para ayudarte a empezar.
    # Si necesitas más ayuda, visita la guía de referencia de Docker Compose en
    # https://docs-docker.esdocu.com/go/compose-spec-reference/
    
    # Aquí las instrucciones definen tu aplicación como un servicio llamado "server".
    # Este servicio se construye a partir del Dockerfile en el directorio actual.
    # Puedes agregar otros servicios de los que pueda depender tu aplicación aquí, como una
    # base de datos o una caché. Para ver ejemplos, consulta el repositorio Awesome Compose:
    # https://github.com/docker/awesome-compose
    services:
      server:
        build:
          context: .
        ports:
          - 8001:8001
    # La sección comentada a continuación es un ejemplo de cómo definir una base de datos PostgreSQL
    # que tu aplicación puede utilizar. `depends_on` le indica a Docker Compose que
    # inicie la base de datos antes de tu aplicación. El volumen `db-data` persiste los
    # datos de la base de datos entre los reinicios del contenedor. El secreto `db-password` se utiliza
    # para establecer la contraseña de la base de datos. Debes crear `db/password.txt` y agregar
    # la contraseña que elijas antes de ejecutar `docker compose up`.
    #     depends_on:
    #       db:
    #         condition: service_healthy
    #   db:
    #     image: postgres:18
    #     restart: always
    #     user: postgres
    #     secrets:
    #       - db-password
    #     volumes:
    #       - db-data:/var/lib/postgresql
    #     environment:
    #       - POSTGRES_DB=example
    #       - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    #     expose:
    #       - 5432
    #     healthcheck:
    #       test: [ "CMD", "pg_isready" ]
    #       interval: 10s
    #       timeout: 5s
    #       retries: 5
    # volumes:
    #   db-data:
    # secrets:
    #   db-password:
    #     file: db/password.txt

    Crea un archivo llamado .dockerignore con el siguiente contenido.

    .dockerignore
    # Incluye aquí cualquier archivo o directorio que no quieras que se copie a tu
    # contenedor (por ejemplo, artefactos de compilación local, archivos temporales, etc.).
    #
    # Para obtener más ayuda, consulta la guía de referencia del archivo .dockerignore en
    # https://docs-docker.esdocu.com/go/build-context-dockerignore/
    
    **/.DS_Store
    **/__pycache__
    **/.venv
    **/.classpath
    **/.dockerignore
    **/.env
    **/.git
    **/.gitignore
    **/.project
    **/.settings
    **/.toolstarget
    **/.vs
    **/.vscode
    **/*.*proj.user
    **/*.dbmdl
    **/*.jfm
    **/bin
    **/charts
    **/docker-compose*
    **/compose.y*ml
    **/Dockerfile*
    **/node_modules
    **/npm-debug.log
    **/obj
    **/secrets.dev.yaml
    **/values.dev.yaml
    LICENSE
    README.md

    Crea un archivo llamado .gitignore con el siguiente contenido.

    .gitignore
    # Archivos compilados en bytes / optimizados / DLL
    __pycache__/
    *.py[cod]
    *$py.class
    
    # Extensiones C
    *.so
    
    # Distribución / empaquetado
    .Python
    build/
    develop-eggs/
    dist/
    downloads/
    eggs/
    .eggs/
    lib/
    lib64/
    parts/
    sdist/
    var/
    wheels/
    share/python-wheels/
    *.egg-info/
    .installed.cfg
    *.egg
    MANIFEST
    
    # Informes de pruebas unitarias / cobertura
    htmlcov/
    .tox/
    .nox/
    .coverage
    .coverage.*
    .cache
    nosetests.xml
    coverage.xml
    *.cover
    *.py,cover
    .hypothesis/
    .pytest_cache/
    cover/
    
    # PEP 582; utilizado por ejemplo por github.com/David-OConnor/pyflow y github.com/pdm-project/pdm
    __pypackages__/
    
    # Entornos
    .env
    .venv
    env/
    venv/
    ENV/
    env.bak/
    venv.bak/

Agregar una base de datos local y persistir datos

Puedes usar contenedores para configurar servicios locales, como una base de datos. En esta sección, actualizarás el archivo compose.yaml para definir un servicio de base de datos y un volumen para persistir los datos.

En el directorio del repositorio clonado, abre el archivo compose.yaml en un IDE o editor de texto. docker init se encargó de crear la mayoría de las instrucciones, pero tendrás que actualizarlo para tu aplicación específica.

En el archivo compose.yaml, debes descomentar todas las instrucciones de la base de datos. Además, debes agregar el archivo de contraseña de la base de datos como una variable de entorno al servicio server y especificar el archivo de secretos que se utilizará.

El siguiente es el archivo compose.yaml actualizado.

services:
  server:
    build:
      context: .
    ports:
      - 8001:8001
    environment:
      - POSTGRES_SERVER=db
      - POSTGRES_USER=postgres
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    depends_on:
      db:
        condition: service_healthy
    secrets:
      - db-password
  db:
    image: postgres:18
    restart: always
    user: postgres
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/postgresql
    environment:
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    expose:
      - 5432
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt
Note

Para obtener más información sobre las instrucciones en el archivo Compose, consulta la referencia del archivo Compose.

Antes de ejecutar la aplicación usando Compose, observa que este archivo Compose especifica un archivo password.txt para contener la contraseña de la base de datos. Debes crear este archivo, ya que no está incluido en el repositorio fuente.

En el directorio del repositorio clonado, crea un nuevo directorio llamado db y dentro de ese directorio crea un archivo llamado password.txt que contenga la contraseña para la base de datos. Usando tu IDE o editor de texto favorito, agrega el siguiente contenido al archivo password.txt.

mysecretpassword

Guarda y cierra el archivo password.txt.

Ahora deberías tener el siguiente contenido en tu directorio python-docker-dev-example.

├── python-docker-dev-example/
│   ├── db/
│   │   └── password.txt
│   ├── app.py
│   ├── config.py
│   ├── requirements.txt
│   ├── .dockerignore
│   ├── .gitignore
│   ├── compose.yaml
│   ├── Dockerfile
│   ├── README.Docker.md
│   └── README.md

Ahora, ejecuta el siguiente comando docker compose up para iniciar tu aplicación.

$ docker compose up --build

Ahora prueba tu punto de conexión de la API. Abre una nueva terminal y realiza una solicitud al servidor utilizando los comandos curl:

Vamos a crear un objeto con un método POST:

$ curl -X 'POST' \
  'http://localhost:8001/heroes/' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "id": 1,
  "name": "my hero",
  "secret_name": "austing",
  "age": 12
}'

Deberías recibir la siguiente respuesta:

{
  "age": 12,
  "id": 1,
  "name": "my hero",
  "secret_name": "austing"
}

Hagamos una solicitud GET con el siguiente comando curl:

$ curl -X 'GET' \
  'http://localhost:8001/heroes/' \
  -H 'accept: application/json'

Deberías recibir la misma respuesta que arriba porque es el único objeto que tenemos en la base de datos.

{
  "age": 12,
  "id": 1,
  "name": "my hero",
  "secret_name": "austing"
}

Presiona ctrl+c en la terminal para detener tu aplicación.

Actualizar servicios automáticamente

Utiliza Compose Watch para actualizar automáticamente tus servicios de Compose en ejecución a medida que editas y guardas tu código. Para obtener más detalles sobre Compose Watch, consulta Usar Compose Watch.

Abre tu archivo compose.yaml en un IDE o editor de texto y luego agrega las instrucciones de Compose Watch. El siguiente es el archivo compose.yaml actualizado.

services:
  server:
    build:
      context: .
    ports:
      - 8001:8001
    environment:
      - POSTGRES_SERVER=db
      - POSTGRES_USER=postgres
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    depends_on:
      db:
        condition: service_healthy
    secrets:
      - db-password
    develop:
      watch:
        - action: rebuild
          path: .
  db:
    image: postgres:18
    restart: always
    user: postgres
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/postgresql
    environment:
      - POSTGRES_DB=example
      - POSTGRES_PASSWORD_FILE=/run/secrets/db-password
    expose:
      - 5432
    healthcheck:
      test: ["CMD", "pg_isready"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt

Ejecuta el siguiente comando para ejecutar tu aplicación con Compose Watch.

$ docker compose watch

En una terminal, haz un curl a la aplicación para obtener una response.

$ curl http://localhost:8001
Hello, Docker!

Cualquier cambio en los archivos fuente de la aplicación en tu máquina local se reflejará inmediatamente en el contenedor en ejecución.

Abre python-docker-dev-example/app.py en un IDE o editor de texto y actualiza la cadena Hello, Docker! agregando algunos signos de exclamación más.

-    return 'Hello, Docker!'
+    return 'Hello, Docker!!!'

Guarda los cambios en app.py y luego espera unos segundos a que la aplicación se vuelva a compilar. Haz un curl a la aplicación nuevamente y verifica que aparezca el texto actualizado.

$ curl http://localhost:8001
Hello, Docker!!!

Presiona ctrl+c en la terminal para detener tu aplicación.

Resumen

En esta sección, viste cómo configurar tu archivo Compose para agregar una base de datos local y persistir datos. También aprendiste a usar Compose Watch para compilar y ejecutar automáticamente tu contenedor cuando actualizas tu código.

Información relacionada:

Pasos siguientes

En la siguiente sección, aprenderás cómo puedes configurar el análisis de código (linting), el formateo y la comprobación de tipos para seguir las mejores prácticas en aplicaciones Python.