Tutorial Dockerizando una aplicación

De Wiki de EGC
Revisión del 18:32 29 oct 2024 de Brgutierrez (discusión | contribuciones) (Tutorial sobre Capas en Docker)
Saltar a: navegación, buscar

Prerrequisitos

Instalar Docker Compose

El primer paso es descargar la última versión de Docker Compose. Puedes descargar Docker Compose usando el siguiente comando, pero asegúrate de verificar la última versión en la página de lanzamientos de Docker Compose:

mkdir -p ~/.docker/cli-plugins/

LATEST_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')

sudo curl -SL "https://github.com/docker/compose/releases/download/${LATEST_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o ~/.docker/cli-plugins/docker-compose

Aplicar Permisos Ejecutables: asegúrate de que el archivo de Docker Compose tenga permisos ejecutables:

 
chmod +x ~/.docker/cli-plugins/docker-compose

Verificar la instalación de Docker Compose: para confirmar que Docker Compose se ha instalado correctamente, ejecuta:

docker compose --version

Dockerizando una Aplicación Flask

En este tutorial, aprenderemos a dockerizar la aplicación Flask `flask_testing_project` que creamos en la práctica anterior. Con esta dockerización, podrás ejecutar tu aplicación Flask en un contenedor Docker, lo que facilita la portabilidad y la ejecución en diferentes entornos.

Para lograr esto, utilizaremos un archivo `Dockerfile` y un archivo de dependencias `requirements.txt` que especificarán cómo construir la imagen Docker y qué paquetes necesita la aplicación.

Estructura del Proyecto

La estructura inicial del proyecto `flask_testing_project` es la siguiente:

flask_testing_project/
│
├── app.py                # Archivo principal de la aplicación Flask
├── templates/            # Directorio que contiene la plantilla HTML
│   └── tasks.html        # Plantilla para mostrar y agregar tareas
├── tests/
│   ├── test_app.py       # Pruebas unitarias usando pytest
│   └── test_interfaz.py  # Pruebas de interfaz con Selenium
└── locustfile.py         # Archivo para pruebas de carga con Locust

Para dockerizar la aplicación, añadiremos dos nuevos archivos:

  • Dockerfile: Define las instrucciones para construir la imagen de Docker.
  • requirements.txt: Contiene las dependencias de Python necesarias para ejecutar la aplicación.

La estructura de directorios después de añadir estos archivos será:

flask_testing_project/
│
├── app.py                # Archivo principal de la aplicación Flask
├── templates/            # Directorio que contiene la plantilla HTML
│   └── tasks.html        # Plantilla para mostrar y agregar tareas
├── tests/
│   ├── test_app.py       # Pruebas unitarias usando pytest
│   └── test_interfaz.py  # Pruebas de interfaz con Selenium
├── locustfile.py         # Archivo para pruebas de carga con Locust
├── Dockerfile            # Archivo para construir la imagen Docker de la aplicación
└── requirements.txt      # Dependencias de Python necesarias para la aplicación

Paso 1: Crear el archivo requirements.txt

El archivo `requirements.txt` lista todas las dependencias de la aplicación. Es crucial para Docker porque le indica qué librerías de Python instalar.

Dentro de `flask_testing_project`, crea un archivo llamado `requirements.txt` y copia el siguiente contenido en él:

attrs==24.2.0
blinker==1.8.2
Brotli==1.1.0
certifi==2024.8.30
charset-normalizer==3.4.0
click==8.1.7
ConfigArgParse==1.7
coverage==7.6.4
Flask==3.0.3
Flask-Cors==5.0.0
Flask-Login==0.6.3
Flask-SQLAlchemy==3.1.1
gevent==24.10.3
geventhttpclient==2.3.1
greenlet==3.1.1
h11==0.14.0
idna==3.10
iniconfig==2.0.0
itsdangerous==2.2.0
Jinja2==3.1.4
locust==2.32.0
MarkupSafe==3.0.2
msgpack==1.1.0
outcome==1.3.0.post0
packaging==24.1
pluggy==1.5.0
psutil==6.1.0
PyMySQL==1.1.1
PySocks==1.7.1
pytest==8.3.3
pytest-cov==5.0.0
python-dotenv==1.0.1
pyzmq==26.2.0
requests==2.32.3
selenium==4.25.0
setuptools==75.2.0
sniffio==1.3.1
sortedcontainers==2.4.0
SQLAlchemy==2.0.36
trio==0.27.0
trio-websocket==0.11.1
typing_extensions==4.12.2
urllib3==2.2.3
webdriver-manager==4.0.2
websocket-client==1.8.0
Werkzeug==3.0.4
wsproto==1.2.0
zope.event==5.0
zope.interface==7.1.1

¿Recuerdas como instalar estas dependencias en un único paso?

Paso 2: Crear el archivo Dockerfile

El `Dockerfile` es el núcleo de la dockerización, ya que define las instrucciones paso a paso para crear una imagen de Docker que ejecutará nuestra aplicación Flask.

Dentro de `flask_testing_project`, crea un archivo llamado `Dockerfile` con el siguiente contenido:

# Usar una imagen base de Python
FROM python:3.12-slim

# Establecer el directorio de trabajo dentro del contenedor
WORKDIR /app

# Copiar el archivo de requisitos al directorio de trabajo del contenedor
COPY requirements.txt .

# Instalar las dependencias desde requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# Copiar el contenido de tu aplicación al directorio de trabajo del contenedor
COPY . .

# Exponer el puerto que usa Flask
EXPOSE 5000

# Comando para ejecutar la aplicación Flask
CMD ["python", "app.py"]

Explicación línea a línea del Dockerfile

  • `FROM python:3.12-slim`: Utiliza una imagen base de Python ligera (versión 3.12) para optimizar el espacio que ocupa la imagen.
  • `WORKDIR /app`: Crea y define `/app` como el directorio de trabajo donde se copiarán los archivos de la aplicación.
  • `COPY requirements.txt .`: Copia el archivo `requirements.txt` desde el sistema de archivos local al directorio actual del contenedor (`/app`). Esto asegura que el contenedor tenga acceso a las dependencias de la aplicación.
  • `RUN pip install --no-cache-dir -r requirements.txt`: Instala las dependencias especificadas en `requirements.txt` sin cachear archivos temporales, reduciendo el tamaño final de la imagen.
  • `COPY . .`: Copia todos los archivos y carpetas desde el sistema local al directorio `/app` del contenedor, incluyendo `app.py` y otras carpetas de proyecto.
  • `EXPOSE 5000`: Indica que el contenedor usará el puerto 5000, donde Flask ejecutará la aplicación.
  • `CMD ["python", "app.py"]`: Define el comando que se ejecutará cuando el contenedor inicie, en este caso, lanzando la aplicación Flask en `app.py`.

Paso 3: Modificar app.py para Ejecutar en Docker

Flask, por defecto, ejecuta su servidor solo en `localhost`, lo que hace que el contenedor no sea accesible desde fuera. Para resolver esto, edita `app.py` y asegúrate de que contenga el siguiente código al final del archivo:

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

Al añadir `host='0.0.0.0'`, permitimos que Flask acepte conexiones desde cualquier IP externa, asegurando que podamos acceder a la aplicación desde fuera del contenedor Docker.

Paso 4: Construir y Ejecutar la Imagen Docker

Con los archivos `Dockerfile`, `requirements.txt` y `app.py` preparados, estamos listos para construir la imagen Docker y ejecutar un contenedor con la aplicación.

En la terminal, navega al directorio `flask_testing_project` y ejecuta los siguientes comandos:

# Construir la imagen Docker y asignarle el nombre 'flask-testing_project'
docker build -t flask-testing_project .

# Ejecutar el contenedor desde la imagen, mapeando el puerto 5000 al mismo puerto en el host
docker run -p 5000:5000 flask-testing_project


  • `docker build -t flask-testing_project .`: Construye una imagen Docker basada en el `Dockerfile` del directorio actual. El argumento `-t flask-testing_project` asigna un nombre a la imagen creada.
  • `docker run -p 5000:5000 flask-testing_project`: Ejecuta la imagen en un contenedor, mapeando el puerto 5000 del contenedor al puerto 5000 de la máquina local. De esta forma, la aplicación Flask es accesible desde el navegador en `localhost:5000`.

Paso 5: Verificación en el Navegador

Una vez que el contenedor esté en ejecución, abre tu navegador y accede a [1](http://localhost:5000). Si ves la aplicación funcionando, ¡felicidades! Has dockerizado exitosamente la aplicación Flask.

Resumen

Este tutorial te ha guiado a través del proceso completo de dockerización de una aplicación Flask:

  • Creamos el archivo `requirements.txt` para listar las dependencias de la aplicación.
  • Construimos un `Dockerfile` detallado para definir los pasos de construcción de la imagen.
  • Modificamos `app.py` para hacer la aplicación accesible externamente.
  • Construimos la imagen y ejecutamos el contenedor, logrando que la aplicación esté disponible en el navegador.

Al seguir estos pasos, has asegurado que la aplicación Flask pueda ejecutarse en cualquier entorno compatible con Docker, simplificando la portabilidad y la escalabilidad del proyecto.


Dockerizando una Aplicación Flask con Base de Datos usando Docker Compose

En este tutorial, aprenderemos a dockerizar la aplicación Flask `flask_testing_project` que creamos en la práctica anterior y añadiremos una base de datos externa. Esto nos permitirá ejecutar la aplicación en contenedores separados para la aplicación Flask y la base de datos, facilitando la escalabilidad y el mantenimiento.

Para lograr esto, usaremos Docker Compose y ajustaremos nuestra aplicación para que utilice una base de datos MariaDB.

Estructura del Proyecto

La estructura inicial de `flask_testing_project` es la siguiente:

flask_testing_project/
│
├── app.py                # Archivo principal de la aplicación Flask
├── templates/            # Directorio con la plantilla HTML
│   └── tasks.html        # Plantilla para mostrar y agregar tareas
├── tests/                # Directorio con pruebas unitarias y de interfaz
│   ├── test_app.py
│   └── test_interfaz.py
├── locustfile.py         # Archivo para pruebas de carga con Locust
├── Dockerfile            # Archivo para construir la imagen Docker de la aplicación
└── requirements.txt      # Dependencias de Python necesarias para la aplicación

Para agregar Docker Compose, crearemos un nuevo archivo `docker-compose.yml`, y realizaremos algunos cambios en `app.py` para que la aplicación se conecte a MariaDB.

La estructura final del proyecto será:

flask_testing_project/
│
├── app.py                # Archivo principal de la aplicación Flask
├── templates/            # Directorio con la plantilla HTML
│   └── tasks.html        # Plantilla para mostrar y agregar tareas
├── tests/
│   ├── test_app.py
│   └── test_interfaz.py
├── locustfile.py         # Archivo para pruebas de carga con Locust
├── Dockerfile            # Archivo para construir la imagen Docker de la aplicación
├── requirements.txt      # Dependencias de Python necesarias para la aplicación
└── docker-compose.yml    # Archivo de configuración de Docker Compose

Paso 1: Crear el archivo docker-compose.yml

Docker Compose permite definir y gestionar múltiples contenedores en un solo archivo de configuración. En este archivo, definiremos dos servicios: uno para la aplicación Flask (`web`) y otro para la base de datos MariaDB (`db`).

Dentro de `flask_testing_project`, crea un archivo llamado `docker-compose.yml` y copia el siguiente contenido:

version: '3'

services:
  web:
    build: .
    ports:
      - "5000:5000"
    environment:
      - FLASK_ENV=development
      - DATABASE_HOST=db
      - DATABASE_USER=root
      - DATABASE_PASSWORD=my-secret-pw
      - DATABASE_DB=flaskdb
    depends_on:
      - db

  db:
    image: mariadb:10.5
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: my-secret-pw
      MYSQL_DATABASE: flaskdb
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:

Explicación línea a línea del docker-compose.yml

  • `version: '3'`: Especifica la versión de Docker Compose.
  • `services`: Define los servicios que Docker Compose manejará, en este caso `web` y `db`.
  • `web`: Servicio para la aplicación Flask.
    • `build: .`: Indica que el servicio usará el Dockerfile en el directorio actual.
    • `ports`: Mapea el puerto 5000 del contenedor al puerto 5000 de la máquina local.
    • `environment`: Define variables de entorno para configurar la conexión a la base de datos.
      • `FLASK_ENV=development`: Ejecuta Flask en modo de desarrollo.
      • `DATABASE_HOST=db`: Dirección del contenedor `db` para la base de datos.
      • `DATABASE_USER=root`, `DATABASE_PASSWORD=my-secret-pw`, `DATABASE_DB=flaskdb`: Credenciales y base de datos que usará Flask.
    • `depends_on`: Asegura que el contenedor `db` esté ejecutándose antes de iniciar `web`.
  • `db`: Servicio para la base de datos MariaDB.
    • `image: mariadb:10.5`: Usa la imagen de MariaDB versión 10.5.
    • `restart: always`: Reinicia el contenedor automáticamente en caso de errores.
    • `environment`: Define las credenciales y la base de datos por defecto.
    • `volumes`: Almacena los datos en un volumen persistente `db_data`.
  • `volumes`: Define `db_data` para que los datos de la base de datos persistan en el sistema anfitrión.

Paso 2: Modificar app.py para Conectarse a MariaDB

Actualizaremos `app.py` para usar SQLAlchemy y conectarse a MariaDB mediante las variables de entorno definidas en Docker Compose.

Reemplaza el contenido de `app.py` por el siguiente:

from flask import Flask, jsonify, request, render_template, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
import os

app = Flask(__name__)

# Configuración de la base de datos MariaDB
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+pymysql://{os.getenv('DATABASE_USER')}:{os.getenv('DATABASE_PASSWORD')}@{os.getenv('DATABASE_HOST')}/{os.getenv('DATABASE_DB')}"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)


# Definición del modelo de Tarea
class Task(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    done = db.Column(db.Boolean, default=False)

    def to_dict(self):
        return {
            'id': self.id,
            'title': self.title,
            'done': self.done
        }


# Ruta para obtener la lista de tareas (versión HTML)
@app.route('/')
def task_list():
    tasks = Task.query.all()
    return render_template('tasks.html', tasks=tasks)


# Ruta para obtener la lista de tareas en JSON (API)
@app.route('/tasks', methods=['GET'])
def get_tasks():
    tasks = Task.query.all()
    return jsonify({'tasks': [task.to_dict() for task in tasks]})


# Ruta para crear una nueva tarea desde un formulario HTML
@app.route('/add_task', methods=['POST'])
def add_task_html():
    title = request.form.get('title')
    if not title:
        return "El título es necesario", 400
    new_task = Task(title=title, done=False)
    db.session.add(new_task)
    db.session.commit()
    return redirect(url_for('task_list'))


# Ruta para crear una nueva tarea (API JSON)
@app.route('/tasks', methods=['POST'])
def create_task():
    if not request.json or 'title' not in request.json:
        return jsonify({'error': 'El título es necesario'}), 400
    new_task = Task(title=request.json['title'], done=False)
    db.session.add(new_task)
    db.session.commit()
    return jsonify(new_task.to_dict()), 201


if __name__ == '__main__':
    # Crear las tablas en la base de datos si no existen
    with app.app_context():
        db.create_all()

    app.run(host='0.0.0.0', port=5000, debug=True)


Paso 3: Construir y Ejecutar los Contenedores con Docker Compose

Ahora que `docker-compose.yml` y `app.py` están configurados, estamos listos para construir y ejecutar la aplicación con Docker Compose.

En la terminal, navega al directorio `flask_testing_project` y ejecuta los siguientes comandos:

# Construir y ejecutar los contenedores
docker compose up
  • `docker compose up`: Construye y ejecuta los contenedores en segundo plano. Si la imagen no existe, la creará usando el Dockerfile y las configuraciones de Docker Compose.

¿Aparece algún error? ¿A qué crees que se debe?

Para detener los contenedores y eliminar el entorno, ejecuta:

# Parar y remover los contenedores
docker compose down
  • `docker compose down`: Detiene todos los contenedores y elimina el entorno definido en `docker-compose.yml`, sin eliminar los volúmenes de datos.

Paso 4: Verificación en el Navegador

Una vez que los contenedores estén en ejecución, abre tu navegador y accede a [2](http://localhost:5000). Si ves la aplicación funcionando y puedes agregar tareas, ¡felicidades! Has dockerizado exitosamente la aplicación Flask con una base de datos MariaDB.

Resumen

En este tutorial:

  • Creamos un archivo `docker-compose.yml` para gestionar múltiples contenedores.
  • Modificamos `app.py` para usar SQLAlchemy y conectar la aplicación Flask a una base de datos MariaDB.
  • Ejecutamos y verificamos la aplicación con Docker Compose.

Ahora tienes una aplicación Flask lista para desarrollarse y desplegarse fácilmente en cualquier entorno.


Tutorial sobre Capas en Docker

Las capas son uno de los conceptos fundamentales de Docker, ya que permiten la eficiencia en el almacenamiento y la distribución de imágenes. En este tutorial, aprenderás sobre cómo funcionan las capas en Docker y cómo puedes trabajar con ellas utilizando comandos en la terminal.

¿Qué son las Capas en Docker?

Docker utiliza un sistema de capas para construir imágenes. Cada capa representa una modificación sobre la capa anterior. Esto significa que las imágenes de Docker son inmutables y se construyen de manera incremental. Cada vez que realizas un cambio, como instalar un nuevo paquete o copiar archivos, se crea una nueva capa.

Ventajas de las Capas

  • Eficiencia de almacenamiento: Las capas son compartidas entre imágenes, lo que significa que si dos imágenes utilizan la misma capa, solo se almacena una vez en el disco.
  • Despliegue rápido: Las imágenes se pueden construir rápidamente reutilizando las capas existentes.
  • Desarrollo ágil: Las capas permiten revertir cambios fácilmente al utilizar imágenes anteriores.

Comandos y Trabajo en la Terminal con Capas

Para interactuar y trabajar con capas en Docker, puedes utilizar varios comandos útiles en la terminal. A continuación, se presentan los pasos que puedes seguir para experimentar y observar cómo funcionan las capas en acción.

Paso 1: Listar Imágenes y Capas

Para ver las imágenes y sus capas, abre tu terminal y ejecuta el siguiente comando:

docker images

Esto mostrará una lista de las imágenes en tu sistema, incluyendo el `REPOSITORY`, `TAG`, `IMAGE ID`, `CREATED`, y `SIZE`. Observa que las imágenes más pequeñas suelen tener capas compartidas con otras.

Paso 2: Inspeccionar una Imagen

Selecciona una imagen de la lista y utiliza el siguiente comando para inspeccionarla y ver detalles sobre sus capas:

docker inspect <IMAGE_ID>

Reemplaza `<IMAGE_ID>` con el ID de la imagen que te interesa. Este comando te mostrará un JSON con información sobre la imagen, incluyendo sus capas y metadatos.

Ahora que has inspeccionado un contenedor, ¿qué otra información relevante crees que puedes obtener aquí?

Paso 3: Ver el Historial de una Imagen

Para ver el historial de las capas de una imagen, usa el siguiente comando:

docker history <IMAGE_ID>

Esto mostrará el tamaño de cada capa, la fecha en que se creó y el comando que se utilizó para crearla. Es una excelente manera de entender cómo se construyó la imagen.

Paso 4: Limpiar Capas No Utilizadas

Docker almacena capas que pueden no ser necesarias después de la construcción de imágenes. Para limpiar las capas no utilizadas y liberar espacio en disco, ejecuta:

docker image prune

Este comando eliminará las capas y las imágenes no utilizadas que no tienen un contenedor asociado. Confirma la acción cuando se te pida.

Paso 5: Construir Imágenes con Caching

Cuando construyes una imagen, Docker utiliza caché para acelerar el proceso. Para forzar a Docker a no utilizar la caché y reconstruir todas las capas, usa el siguiente comando:

docker build --no-cache -t <IMAGE_NAME> .

Reemplaza `<IMAGE_NAME>` con el nombre que deseas dar a la imagen. Esto es útil si has realizado cambios en las capas anteriores que no se reflejan debido a la caché.

Ejercicios Prácticos con Capas

Ahora que conoces los conceptos básicos y los comandos para trabajar con capas en Docker, vamos a realizar algunos ejercicios prácticos.

Ejercicio 1: Crear una Nueva Imagen

1. Crea un nuevo directorio para tu proyecto y navega a él:

    mkdir docker-layers-demo
    cd docker-layers-demo

2. Crea un archivo `Dockerfile` con el siguiente contenido:

    FROM ubuntu:latest
    RUN apt-get update && apt-get install -y curl
    CMD ["bash"]

3. Construye la imagen:

    docker build -t my-curl-image .

4. Lista las imágenes para verificar que tu nueva imagen se haya creado:

    docker images

Ejercicio 2: Modificar la Imagen y Ver las Capas

1. Edita el `Dockerfile` para agregar un nuevo comando que instale `git`:

    RUN apt-get install -y git

2. Vuelve a construir la imagen usando el comando:

    docker build -t my-curl-image .

3. Verifica el historial de la imagen para observar cómo se han creado las capas:

    docker history my-curl-image

Preguntas:

  • ¿Cuántas capas se han creado después de instalar `git`?
  • ¿Cuál es el tamaño de cada capa?

Ejercicio 3: Explorar Capas de una Imagen Existente

1. Elige una imagen existente en tu sistema (por ejemplo, `ubuntu`) y usa el siguiente comando para inspeccionarla:

    docker inspect ubuntu

Observa las capas y responde:

  • ¿Cuántas capas tiene la imagen `ubuntu`?
  • ¿Cuál es la información más relevante que puedes obtener de la inspección?

Ejercicio 4: Limpiar Imágenes y Capas

1. Usa el comando de limpieza para eliminar las capas no utilizadas:

    docker image prune

2. Verifica nuevamente la lista de imágenes para asegurarte de que las no utilizadas han sido eliminadas:

    docker images

Resumen

Experimentar con estos comandos y ejercicios te permitirá entender mejor cómo funcionan las capas en Docker y cómo puedes gestionarlas eficazmente. Al manipular capas y explorar imágenes, podrás optimizar tus flujos de trabajo y crear entornos más eficientes.