Ejemplo 2: Implementación de servidor con Nginx, Flask y mySQL

El objetivo de esta práctica es configurar y desplegar una aplicación web basada en una pila Nginx + Flask + MySQL utilizando Docker.

Nuestra práctica tendrá la siguiente estructura de carpetas:

  • docker-compose.yaml

  • /python

    • app.py

    • requirements.txt

    • Dockerfile

    • /templates

      • form.html

  • /nginx

    • nginx.conf

    • Dockerfile

  • /initdb

    • script.sql


1. Creamos un servidor web con Flask

  • Realizaremos esta práctica con una pequeña aplicación web en Flask que tenga un endpoint básico de flask (/) que muestre un formulario de HTML donde te pida nombre y email y al pulsar "enviar" te muestre todos los resultados de la base de datos.

  • El código podría ser para app.py:

from flask import Flask, render_template, request
import mysql.connector

app = Flask(__name__)

# Ruta para mostrar el formulario y los datos enviados
@app.route('/', methods=['GET', 'POST'])
def form():
    if request.method == 'POST':
        name = request.form['name']
        email = request.form['email']

        try:
            # Conectar a la base de datos
            conn = mysql.connector.connect(
                host="db",
                user="alex",
                password="P@ssw0rd",
                database="fskalex"
            )
            cursor = conn.cursor()
            cursor.execute("INSERT INTO basic_table (name, email) VALUES (%s, %s)", (name, email))
            conn.commit()

            # Recuperar los datos recién enviados
            cursor.execute("SELECT * FROM basic_table ORDER BY id DESC LIMIT 5")  # Muestra los últimos 5 registros
            data = cursor.fetchall()

            conn.close()
            return render_template('form.html', data=data)

        except Exception as e:
            return str(e), 500

    return render_template('form.html', data=[])

# Ejecuta la app en localhost puerto 5000
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)

Y el documento siguiente de html guárdalo como form.html en una carpeta /templates :

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Formulario de Usuario</title>
</head>
<body>
    <h2>Ingresar datos</h2>
    <form method="POST">
        <label for="name">Nombre:</label>
        <input type="text" id="name" name="name" required><br><br>

        <label for="email">Correo:</label>
        <input type="email" id="email" name="email" required><br><br>

        <button type="submit">Enviar</button>
    </form>

    {% if data %}
    <h2>Datos enviados</h2>
    <table border="1">
        <tr>
            <th>ID</th>
            <th>Nombre</th>
            <th>Email</th>
        </tr>
        {% for row in data %}
        <tr>
            <td>{{ row[0] }}</td>
            <td>{{ row[1] }}</td>
            <td>{{ row[2] }}</td>
        </tr>
        {% endfor %}
    </table>
    {% endif %}
</body>
</html>

Ahora crea un archivo requirements.txt con el siguiente contenido:

flask
mysql-connector-python

2. Creamos una imagen de docker personalizada de Flask

Escribe un Dockerfile para el servidor Flask:

#Exportamos la imagen estable mas reciente
FROM python:latest

# Copiamos los archivos de la aplicación (fijate en las rutas)
WORKDIR /app
COPY . /app

# Instala dependencias a partir del txt de antes
RUN pip install --no-cache-dir -r requirements.txt
# O bien puedes usar la siguiente sentencia que hace lo mismo:
# RUN pip install --no-cache-dir flask mysql-connector-python

# Exponemos el puerto de Flask
EXPOSE 5000

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

Y construye la imagen de Flask ejecutando el comando:

  • docker build -t flask-app .

3. Configurar un proxy inverso con Nginx

Para ello primero debemos configurar un archivo nginx.conf:

  • Este archivo manejará las solicitudes entrantes y las redirigirá al servidor Flask:

    server {
        listen 80;
    
        location / {
            proxy_pass http://flask-app:5000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
  • Creamos ahora un Dockerfile para Nginx:

    FROM nginx:latest
    
    # Copiar la configuración personalizada de Nginx
    COPY nginx.conf /etc/nginx/conf.d/default.conf
  • Construir la imagen de Nginx:

    docker build -t nginx-proxy .

4. Preparación del contenedor de mysql

Para el contenedor de MySQL utilizaremos la imagen oficial de MySQL configurando las variables de entorno (ten en cuenta que esto es un comando entero y que deberá ser lo mismo que en la app de python) y añadiendo un script de iniciación de la base de datos.

Recuerda que nuestra bariables de entorno para el docker-compose serán las siguientes:

MYSQL_ROOT_PASSWORD=1234
MYSQL_DATABASE=fskalex
MYSQL_USER=alex
MYSQL_PASSWORD=P@ssw0rd

Creamos una carpeta ./initdb en la carpeta de docker-compose con el siguiente script script.sql para crear una tabla, dar privilegios y agregar unos datos simples:

CREATE DATABASE IF NOT EXISTS fskalex;

CREATE USER IF NOT EXISTS 'alex'@'%' IDENTIFIED BY 'P@ssw0rd';
GRANT ALL PRIVILEGES ON fskalex.* TO 'alex'@'%';
FLUSH PRIVILEGES;

USE fskalex;

CREATE TABLE IF NOT EXISTS basic_table (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL
);

INSERT INTO basic_table (name, email) VALUES ('Rosa Melano', 'bienrosita@yahoo.es'), ('Armando Jaleo', 'jaleito@copy.org');

Esto veremos que lo añadiremos a la carpeta de iniciación del contenedor (/docker-entrypoint-initdb.d), más info: https://tecadmin.net/using-mysql-with-docker-compose/


5. Orquestar los contenedores con Docker Compose

El último paso es ya crear el archivo de docker compose agregando todas las imágenes creadas. Para ello creamos un archivo docker-compose.yml:

version: '3.8'

services:
  db:
    image: mysql:latest
    container_name: mysql-db
    environment:
      MYSQL_ROOT_PASSWORD: 1234
      MYSQL_DATABASE: fskalex
      MYSQL_USER: alex
      MYSQL_PASSWORD: P@ssw0rd
    volumes:
      - mysql_data:/var/lib/mysql  # Volumen persistente para los datos de la DB
      - ./initdb:/docker-entrypoint-initdb.d  # Montamos el script de inicialización
    networks:
      - webnet

  flask-app:
    image: flask-app:latest
    container_name: flask-app
    depends_on:
      - db
    networks:
      - webnet
    ports:
      - "5000:5000"
    restart: always  # Asegura que reinicie si falla por la DB no estar lista

  nginx:
    image: nginx-proxy
    container_name: nginx-proxy
    depends_on:
      - flask-app
    networks:
      - webnet
    ports:
      - "80:80"

volumes:
  mysql_data:

networks:
  webnet:

Vamos ahora a iniciar todos los servicios:

docker-compose up --build
#o bien para enviarlo a segundo plano:
docker-compose up -d --build 

Probemos ahora la aplicación abriendo un navegador y accediendo a:

  • http://localhost/ → Deberías ver el formulario HTML.


Mas ideas para añadir/modificar:

  • Añade persistencia de datos configurando volúmenes para Nginx y MySQL.

  • Integra una herramienta de monitoreo como Prometheus o Grafana.

  • Implementa SSL en el proxy Nginx con Let's Encrypt.

Última actualización