DEV Community

Raül Martínez i Peris
Raül Martínez i Peris

Posted on

Python. Project Structure (VI)

Índice:


En este apartado crearemos lo necesario para trabajar con la virtualización de entornos.

Crear una nueva rama

Empezamos con la creación de la rama release/000-virtualization:

git checkout -b 'release/000-virtualization'
Enter fullscreen mode Exit fullscreen mode

Ahora actualizamos el archivo .gitignore añadiendo:

.env.production
Enter fullscreen mode Exit fullscreen mode

Docker

Empezaremos añadiendo el archivo .dockerignore:

_temp_/
.git
.gitignore
.env*
.vscode/
.venv/
.pytest*
tests/
Dockerfile
compose.yml
compose.override.yml
Enter fullscreen mode Exit fullscreen mode

Ahora prepararemos los entornos para Linux y Windows por separado.

Docker en el entorno Linux

Creamos el archivo Dockerfile:

# Dockerfile
FROM python:3.12-slim AS base

# Configuración de entorno
ARG APP_PATH=/opt/la_fragua
ENV APP_PATH=${APP_PATH}
ARG APP_ENV=production
ENV APP_ENV=${APP_ENV}
ARG APP_PORT=8080
ENV APP_PORT=${APP_PORT}

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    POETRY_VIRTUALENVS_CREATE=false \
    PATH="/root/.local/bin:$PATH" \
    DISPLAY=:99 \
    QT_QPA_PLATFORM=offscreen

# Crear directorio de la aplicación
WORKDIR ${APP_PATH}

# Instalar dependencias del sistema necesarias para nicegui (qt, chrome, etc.)
RUN apt-get update && apt-get install -y \
    git curl build-essential \
    libglib2.0-0 libnss3 libx11-6 libxcomposite1 \
    libxdamage1 libxrandr2 libxkbcommon0 libxshmfence1 \
    libasound2 libatk1.0-0 libatk-bridge2.0-0 libcups2 \
    libdrm2 libxext6 libgbm1 libxfixes3 libxrender1 \
    xvfb x11vnc fluxbox wmctrl \
    && rm -rf /var/lib/apt/lists/*

# ---- desarrollo ----
FROM base AS dev
COPY requirements.txt .
COPY requirements-dev.txt .
RUN pip install --upgrade pip && pip install -r requirements.txt -r requirements-dev.txt
CMD ["python3", "-m", "app.main"]

# ---- producción ----
FROM base AS production
COPY requirements.txt .
RUN pip install --upgrade pip && pip install -r requirements.txt
COPY . ${APP_FOLDER}
EXPOSE ${APP_PORT}
RUN echo '#!/bin/bash\nXvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &\npython3 -m app.main' > /opt/start.sh && \
    chmod +x /opt/start.sh
CMD ["/opt/start.sh"]
Enter fullscreen mode Exit fullscreen mode

Una vez que tenemos el archivo Dockerfile vamos a preparar los archivos 'compose' para automatizar la creación y ejecución de los entornos.

Creamos el archivo compose.yml:

# compose.yml para producción
services:
  la_fragua:
    image: "${DOCKER_LOGIN}/${APP_NAME}-linux:latest"
    build:
      context: .
      dockerfile: "Dockerfile"
      target: production
    env_file:
      - .env.production
    ports:
      - "${APP_PORT}:8080"
    environment:
      - APP_ENV=container
      - APP_PATH=/opt/la_fragua
    restart: always
Enter fullscreen mode Exit fullscreen mode

Creamos el archivo compose.override.yml:

# compose.override.yml para dev
services:
  la_fragua:
    image: "${APP_NAME}-linux:dev"
    build:
      context: .
      dockerfile: "Dockerfile"
      target: dev
    env_file:
      - .env
    ports:
      - "${APP_PORT}:8080"
    environment:
      - APP_ENV=container
      - APP_PATH=/opt/la_fragua
    volumes:
      - .:/opt/la_fragua
    stdin_open: true
    tty: true
Enter fullscreen mode Exit fullscreen mode

Docker en el entorno Windows

Añadimos al archivo .dockerignore el siguiente contenido:

Dockerfile.windows
compose.windows.yml
compose.windows.override.yml
Enter fullscreen mode Exit fullscreen mode

Creamos el archivo Dockerfile.windows:

# Dockerfile.windows
FROM mcr.microsoft.com/windows/servercore:ltsc2022 AS base

# Shell Powershell
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';"]

# Instalar Python
RUN Invoke-WebRequest -UseBasicParsing https://www.python.org/ftp/python/3.12.0/python-3.12.0-amd64.exe -OutFile python-installer.exe; \
    Start-Process python-installer.exe -Wait -ArgumentList '/quiet', 'InstallAllUsers=1', 'PrependPath=1', 'DefaultAllUsersTargetDir=C:\Python312'; \
    Remove-Item python-installer.exe -Force

# Actualizar pip
RUN python -m pip install --upgrade pip --disable-pip-version-check

# Configuración de entorno
ARG APP_PATH="C:/la_fragua"
ENV APP_PATH=${APP_PATH}
ARG APP_ENV=production
ENV APP_ENV=${APP_ENV}
ARG APP_PORT=8080
ENV APP_PORT=${APP_PORT}

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    APP_VIRTUALIZATION=container \
    QT_QPA_PLATFORM=minimal

# Crear directorio de la aplicación
WORKDIR ${APP_PATH}

# ---------- Dev stage ----------
FROM base AS dev
COPY requirements.txt .
COPY requirements-dev.txt .
RUN pip install --no-cache-dir -r requirements.txt -r requirements-dev.txt
CMD ["python", "-m", "app.main"]

# ---------- Production stage ----------
FROM base AS production
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . ${APP_FOLDER}
EXPOSE ${APP_PORT}
CMD ["python", "-m", "app.main"]
Enter fullscreen mode Exit fullscreen mode

Creamos el archivo compose.windows.yml

# compose.windows.yml para producción
services:
  la_fragua:
    image: "${DOCKER_LOGIN}/${APP_NAME}-windows:latest"
    build:
      context: .
      dockerfile: "Dockerfile.windows"
      target: production
    env_file:
      - .env.production
    ports:
      - "${APP_PORT}:8080"
    environment:
      - APP_ENV=container
      - APP_PATH="C:/la_fragua"
    restart: always
Enter fullscreen mode Exit fullscreen mode

Creamos el archivo compose.windows.override.yml

# compose.windows.override.yml para dev
services:
  la_fragua:
    image: "${APP_NAME}-windows:dev"
    build:
      context: .
      dockerfile: "Dockerfile.windows"
      target: dev
    env_file:
      - .env
    ports:
      - "${APP_PORT}:8080"
    environment:
      - APP_ENV=container
      - APP_PATH="C:/la_fragua"
    volumes:
      - .:C:/la_fragua
    stdin_open: true
    tty: true
Enter fullscreen mode Exit fullscreen mode

Ya tenemos preparados los entornos docker de Linux y Windows.

Hemos cometido un par de errores en los Dockerfile para obligarnos a realizar una corrección. En el siguiente artículo trabajaremos sobre ello.

Apuntes

En los archivos Dockerfile habrás visto que utilizamos FROM python:3.12-slim AS base al inicio, y posteriormente FROM base AS dev y FROM base AS production. Esto, unido al target de los archivos compose nos define qué parte del Dockerfile está generándose. Con esta dinámica hemos conseguido no repetir información en innecesariamente. En el siguiente artículo continuamos trabajando sobre los entornos.

En los archivos compose verás que en producción tenemos la variable DOCKER_LOGIN pero en desarrollo no lo está. Esto tiene una razón: la imagen docker que se genere para producción querremos poder llevarla al hub de docker, pero la imagen de desarrollo no debemos llevarla nunca al hub.

Enlaces

Repositorio del proyecto:

Enlaces de interés:

Siguiente artículo:

Top comments (0)