Cómo servir aplicaciones Flask con Gunicorn y Nginx en Ubuntu 22.04

A previous version of this tutorial was written by Kathleen Juell.

Introducción

En esta guía, construirás una aplicación Python utilizando el microframework Flask en Ubuntu 22.04. La mayor parte de este tutorial tratará sobre cómo configurar el servidor de aplicaciones Gunicorn y cómo lanzar la aplicación y configurar Nginx para actuar como un proxy inverso frontal.

Requisitos previos

Antes de comenzar esta guía, deberías tener:

  • Un servidor con Ubuntu 22.04 instalado y un usuario no root con privilegios sudo. Sigue nuestra guía de configuración inicial del servidor para obtener orientación.

  • Nginx instalado, siguiendo los Pasos 1 y 2 de Cómo Instalar Nginx en Ubuntu 22.04.

  • Un nombre de dominio configurado para apuntar a tu servidor. Puedes comprar uno en Namecheap o obtener uno gratis en Freenom. Puedes aprender cómo apuntar dominios a DigitalOcean siguiendo la documentación relevante sobre dominios y DNS. Asegúrate de crear los siguientes registros DNS:

    • Un registro A con tu_dominio apuntando a la dirección IP pública de tu servidor.
    • Un registro A con www.tu_dominio apuntando a la dirección IP pública de tu servidor.
  • Familiaridad con la especificación WSGI, que el servidor Gunicorn utilizará para comunicarse con tu aplicación Flask. Esta discusión cubre WSGI con más detalle.

Paso 1 — Instalación de los componentes desde los repositorios de Ubuntu

El primer paso será instalar todos los componentes necesarios desde los repositorios de Ubuntu. Esto incluye pip, el gestor de paquetes de Python, que se encargará de gestionar los componentes de Python. También obtendrá los archivos de desarrollo de Python necesarios para compilar algunos de los componentes de Gunicorn.

Primero, actualice el índice de paquetes local e instale los paquetes que le permitirán construir su entorno de Python. Estos incluirán python3-pip, junto con algunos paquetes más y herramientas de desarrollo necesarias para un entorno de programación sólido:

  1. sudo apt update
  2. sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools

Con estos paquetes en su lugar, el siguiente paso es crear un entorno virtual para su proyecto.

Paso 2 — Creación de un Entorno Virtual de Python

A continuación, configurará un entorno virtual para aislar la aplicación Flask de los demás archivos de Python en su sistema.

Comience instalando el paquete python3-venv, que instalará el módulo venv:

  1. sudo apt install python3-venv

A continuación, crea un directorio principal para tu proyecto Flask. Muévete al directorio con el comando cd después de crearlo:

  1. mkdir ~/myproject
  2. cd ~/myproject

Crea un entorno virtual para almacenar los requisitos de Python de tu proyecto Flask escribiendo:

  1. python3 -m venv myprojectenv

Esto instalará una copia local de Python y pip en un directorio llamado myprojectenv dentro de tu directorio de proyecto.

Antes de instalar aplicaciones dentro del entorno virtual, necesitas activarlo. Hazlo escribiendo:

  1. source myprojectenv/bin/activate

El prompt cambiará para indicar que ahora estás operando dentro del entorno virtual. Se verá algo así: (myprojectenv)user@host:~/myproject$.

Paso 3 — Configurar una Aplicación Flask

Ahora que estás en tu entorno virtual, puedes instalar Flask y Gunicorn y comenzar a diseñar tu aplicación.

Primero, instala wheel con la instancia local de pip para asegurarte de que tus paquetes se instalen incluso si faltan archivos de rueda:

  1. pip install wheel

Nota

independientemente de la versión de Python que estés utilizando, cuando el entorno virtual está activado, debes usar el comando pip (no pip3).

A continuación, instala Flask y Gunicorn:

  1. pip install gunicorn flask

Creando una aplicación de ejemplo

Ahora que tienes Flask disponible, puedes crear una aplicación simple. Flask es un microframework. No incluye muchas de las herramientas que podrían tener los frameworks más completos, y principalmente existe como un módulo que puedes importar a tus proyectos para ayudarte a inicializar una aplicación web.

Aunque tu aplicación podría ser más compleja, crearemos nuestra aplicación Flask en un solo archivo, llamado myproject.py:

  1. nano ~/myproject/myproject.py

El código de la aplicación residirá en este archivo. Importará Flask e instanciará un objeto Flask. Puedes usar esto para definir las funciones que deben ejecutarse cuando se solicita una ruta específica:

~/myproject/myproject.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1 style='color:blue'>Hello There!</h1>"

if __name__ == "__main__":
    app.run(host='0.0.0.0')

Esto básicamente define qué contenido presentar cuando se accede al dominio raíz. Guarda y cierra el archivo cuando hayas terminado.

Si seguiste la guía de configuración inicial del servidor, deberías tener un firewall UFW habilitado. Para probar la aplicación, necesitas permitir el acceso al puerto 5000:

  1. sudo ufw allow 5000

Ahora puedes probar tu aplicación Flask escribiendo:

  1. python myproject.py

Verás una salida como la siguiente, incluyendo una útil advertencia que te recuerda no usar esta configuración de servidor en producción:

Output
* Serving Flask app "myproject" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

Visita la dirección IP de tu servidor seguida de :5000 en tu navegador web:

http://your_server_ip:5000

Deberías ver algo así:

Cuando hayas terminado, presiona CTRL-C en la ventana de tu terminal para detener el servidor de desarrollo de Flask.

Creación del Punto de Entrada WSGI

A continuación, crea un archivo que servirá como punto de entrada para tu aplicación. Esto le indicará al servidor Gunicorn cómo interactuar con la aplicación.

Llama al archivo wsgi.py:

  1. nano ~/myproject/wsgi.py

En este archivo, importa la instancia de Flask desde nuestra aplicación y luego ejecútala:

~/myproject/wsgi.py
from myproject import app

if __name__ == "__main__":
    app.run()

Guarda y cierra el archivo cuando hayas terminado.

Paso 4 — Configurando Gunicorn

Tu aplicación ahora está escrita con un punto de entrada establecido. Ahora puedes pasar a configurar Gunicorn.

Antes de continuar, verifica que Gunicorn pueda servir la aplicación correctamente.

Puedes hacer esto pasándole el nombre del punto de entrada de la aplicación. Esto se construye como el nombre del módulo (sin la extensión .py), más el nombre del callable dentro de la aplicación. En este caso, es wsgi:app.

También especifica la interfaz y el puerto a los que enlazar utilizando el argumento 0.0.0.0:5000 para que la aplicación se inicie en una interfaz disponible públicamente:

  1. cd ~/myproject
  2. gunicorn --bind 0.0.0.0:5000 wsgi:app

Deberías ver una salida como la siguiente:

Output
[2020-05-20 14:13:00 +0000] [46419] [INFO] Starting gunicorn 20.0.4 [2020-05-20 14:13:00 +0000] [46419] [INFO] Listening at: http://0.0.0.0:5000 (46419) [2020-05-20 14:13:00 +0000] [46419] [INFO] Using worker: sync [2020-05-20 14:13:00 +0000] [46421] [INFO] Booting worker with pid: 46421

Visite la dirección IP de su servidor con :5000 agregado al final en su navegador web nuevamente:

http://your_server_ip:5000

Debería ver la salida de su aplicación:

Cuando haya confirmado que está funcionando correctamente, presione CTRL-C en la ventana de su terminal.

Cuando haya terminado de usar el entorno virtual, puede desactivarlo:

  1. deactivate

Cualquier comando de Python ahora utilizará nuevamente el entorno de Python del sistema.

A continuación, cree el archivo de unidad de servicio systemd. La creación de un archivo de unidad systemd permitirá que el sistema init de Ubuntu inicie automáticamente Gunicorn y sirva la aplicación Flask cada vez que se inicie el servidor.

Cree un archivo de unidad que termine en .service dentro del directorio /etc/systemd/system para comenzar:

  1. sudo nano /etc/systemd/system/myproject.service

Dentro, comenzará con la sección [Unit], que se utiliza para especificar metadatos y dependencias. Agregue una descripción de su servicio aquí y dígale al sistema init que solo inicie esto después de que se haya alcanzado el objetivo de networking:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

A continuación, agregue una sección [Service]. Esto especificará el usuario y el grupo bajo los cuales desea que se ejecute el proceso. Dele a su cuenta de usuario regular la propiedad del proceso, ya que es la propietaria de todos los archivos relevantes. También dé propiedad del grupo al grupo www-data para que Nginx pueda comunicarse fácilmente con los procesos de Gunicorn. Recuerde reemplazar el nombre de usuario aquí con su nombre de usuario:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data

A continuación, mapea el directorio de trabajo y establece la variable de entorno PATH para que el sistema init sepa que los ejecutables del proceso se encuentran dentro de nuestro entorno virtual. También especifica el comando para iniciar el servicio. Este comando hará lo siguiente:

  • Inicia 3 procesos de trabajador (aunque debes ajustarlo según sea necesario)
  • Crea y vincula a un archivo de socket Unix, myproject.sock, dentro de nuestro directorio de proyecto. Estableceremos un valor de máscara de permisos de 007 para que el archivo de socket se cree otorgando acceso al propietario y al grupo, mientras se restringe el acceso a otros
  • Especifica el nombre del archivo de punto de entrada WSGI, junto con la función Python dentro de ese archivo (wsgi:app)

Systemd requiere que proporciones la ruta completa al ejecutable de Gunicorn, que está instalado dentro de tu entorno virtual.

Recuerda reemplazar el nombre de usuario y las rutas del proyecto con tu propia información:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

Finalmente, agrega una sección [Install]. Esto le indicará a systemd a qué enlazar este servicio si lo habilitas para que se inicie durante el arranque. Querrás que este servicio se inicie cuando el sistema multiusuario regular esté activo y en funcionamiento:

/etc/systemd/system/myproject.service
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target

[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myproject
Environment="PATH=/home/sammy/myproject/myprojectenv/bin"
ExecStart=/home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target

Con esto, tu archivo de servicio de systemd está completo. Guárdalo y ciérralo ahora.

Ahora puedes iniciar el servicio de Gunicorn que creaste y habilitarlo para que se inicie durante el arranque:

  1. sudo systemctl start myproject
  2. sudo systemctl enable myproject

Vamos a verificar el estado:

  1. sudo systemctl status myproject

Deberías ver una salida como esta:

Output
● myproject.service - Gunicorn instance to serve myproject Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2022-05-10 19:40:41 UTC; 9s ago Main PID: 17300 (gunicorn) Tasks: 4 (limit: 2327) Memory: 56.0M CPU: 514ms CGroup: /system.slice/myproject.service ├─17300 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─17301 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─17302 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app └─17303 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app May 10 19:40:41 r systemd[1]: Started Gunicorn instance to serve myproject. . . .

Si ves algún error, asegúrate de resolverlo antes de continuar con el tutorial.

Paso 5 — Configurar Nginx para Proxificar Peticiones

El servidor de aplicaciones Gunicorn debería estar funcionando ahora, esperando peticiones en el archivo de socket en el directorio del proyecto. Ahora puedes configurar Nginx para enviar peticiones web a ese socket haciendo algunas pequeñas adiciones a su archivo de configuración.

Comienza creando un nuevo archivo de configuración de bloque de servidor en el directorio sites-available de Nginx. Llámalo myproject para seguir la línea del resto de la guía:

  1. sudo nano /etc/nginx/sites-available/myproject

Abre un bloque de servidor y dile a Nginx que escuche en el puerto predeterminado 80. También dile que utilice este bloque para peticiones para el nombre de dominio de nuestro servidor:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;
}

A continuación, agrega un bloque de ubicación que coincida con cada solicitud. Dentro de este bloque, incluirás el archivo proxy_params que especifica algunos parámetros generales de proxy que deben establecerse. Luego, pasarás las solicitudes al socket que definiste usando la directiva proxy_pass:

/etc/nginx/sites-available/myproject
server {
    listen 80;
    server_name your_domain www.your_domain;

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/sammy/myproject/myproject.sock;
    }
}

Guarda y cierra el archivo cuando hayas terminado.

Para habilitar la configuración de bloque de servidor de Nginx que acabas de crear, enlaza el archivo al directorio sites-enabled:

  1. sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

Con el archivo en ese directorio, puedes verificar errores de sintaxis:

  1. sudo nginx -t

Si esto regresa sin indicar problemas, reinicia el proceso de Nginx para leer la nueva configuración:

  1. sudo systemctl restart nginx

Finalmente, ajusta el firewall nuevamente. Ya no necesitas acceso a través del puerto 5000, así que puedes eliminar esa regla. Luego puedes permitir acceso completo al servidor Nginx:

  1. sudo ufw delete allow 5000
  2. sudo ufw allow 'Nginx Full'

Ahora deberías poder navegar hacia el nombre de dominio de tu servidor en tu navegador web:

http://your_domain

Deberías ver la salida de tu aplicación:

Nota: Recibirás un error HTTP 502 de puerta de enlace si Nginx no puede acceder al archivo de socket de gunicorn. Por lo general, esto se debe a que el directorio principal del usuario no permite que otros usuarios accedan a los archivos dentro de él.

Si tu archivo de socket se llama /home/sammy/myproject/myproject.sock, asegúrate de que /home/sammy tenga al menos permisos 0755. Puedes usar una herramienta como chmod para cambiar los permisos así:

  1. sudo chmod 755 /home/sammy

Luego recarga la página para ver si desaparece el error HTTP 502.

Si encuentras algún error, intenta revisar lo siguiente:

  • sudo less /var/log/nginx/error.log: revisa los registros de error de Nginx.
  • sudo less /var/log/nginx/access.log: revisa los registros de acceso de Nginx.
  • sudo journalctl -u nginx: revisa los registros de proceso de Nginx.
  • sudo journalctl -u myproject: revisa los registros de Gunicorn de tu aplicación Flask.

Paso 6 — Asegurando la Aplicación

Para asegurarse de que el tráfico a su servidor permanezca seguro, obtengamos un certificado SSL para su dominio. Hay varias formas de hacerlo, incluyendo obtener un certificado gratuito de Let’s Encrypt, generar un certificado autofirmado, o comprar uno de otro proveedor y configurar Nginx para usarlo siguiendo los Pasos 2 al 6 de Cómo Crear un Certificado SSL Autofirmado para Nginx en Ubuntu 22.04. Utilizaremos la opción uno (Let’s Encrypt) por razones de rapidez.

Instale el paquete Nginx de Certbot con apt:

  1. sudo apt install python3-certbot-nginx

Certbot proporciona una variedad de formas de obtener certificados SSL a través de complementos. El complemento Nginx se encargará de reconfigurar Nginx y recargar la configuración cuando sea necesario. Para usar este complemento, escriba lo siguiente:

  1. sudo certbot --nginx -d your_domain -d www.your_domain

Esto ejecuta certbot con el complemento --nginx, usando -d para especificar los nombres para los cuales queremos que el certificado sea válido.

Si es la primera vez que ejecutas certbot, se te pedirá que ingreses una dirección de correo electrónico y aceptes los términos del servicio. Después de hacerlo, certbot se comunicará con el servidor de Let’s Encrypt, luego ejecutará un desafío para verificar que controlas el dominio para el que estás solicitando un certificado.

Si eso tiene éxito, certbot preguntará cómo te gustaría configurar tu configuración de HTTPS:

Output
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. ------------------------------------------------------------------------------- 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. ------------------------------------------------------------------------------- Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

Selecciona tu elección y luego presiona ENTER. La configuración se actualizará y Nginx se recargará para recoger la nueva configuración. certbot concluirá con un mensaje que te indicará que el proceso fue exitoso y dónde se almacenan tus certificados:

Output
IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain/privkey.pem Your cert will expire on 2020-08-18. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le

Si seguiste las instrucciones de instalación de Nginx en los requisitos previos, ya no necesitarás la concesión redundante de perfil HTTP:

  1. sudo ufw delete allow 'Nginx HTTP'

Para verificar la configuración, navega nuevamente a tu dominio, usando https://:

https://your_domain

Deberías ver la salida de tu aplicación nuevamente, junto con el indicador de seguridad de tu navegador, que debería indicar que el sitio está asegurado.

Conclusión

En esta guía, creaste y aseguraste una aplicación Flask simple dentro de un entorno virtual de Python. Creaste un punto de entrada WSGI para que cualquier servidor de aplicaciones compatible con WSGI pueda interactuar con él, y luego configuraste el servidor de aplicaciones Gunicorn para proporcionar esta función. Posteriormente, creaste un archivo de servicio systemd para lanzar automáticamente el servidor de aplicaciones al arrancar. También creaste un bloque de servidor Nginx que pasa el tráfico del cliente web al servidor de aplicaciones, transmitiendo solicitudes externas y asegurando el tráfico a tu servidor con Let’s Encrypt.

Flask es un framework muy simple pero extremadamente flexible destinado a proporcionar funcionalidad a tus aplicaciones sin ser demasiado restrictivo sobre la estructura y el diseño. Puedes utilizar la pila general descrita en esta guía para servir las aplicaciones Flask que diseñes.

Source:
https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-22-04