Tutorial

Cómo presentar aplicaciones de Flask con Gunicorn y Nginx en Ubuntu 20.04

Published on June 11, 2020
Español
Cómo presentar aplicaciones de Flask con Gunicorn y Nginx en Ubuntu 20.04

Introducción

A través de esta guía, creará una aplicación de Python utilizando el microframework de Flask en Ubuntu 20.04. En la mayor parte de este artículo se abordarán la configuración del servidor de la aplicación Gunicorn y la forma de iniciar la aplicación y configurar Nginx para que funcione como un proxy inverso de cliente.

Requisitos previos

Antes de comenzar con esta guía, deberá contar con lo siguiente:

  • Un servidor con Ubuntu 20.04 instalado y un usuario no root con privilegios sudo. Siga nuestra guía de configuración inicial para servidores a modo de orientación.

  • Nginx instalado conforme a los pasos 1 y 2 de Cómo instalar Nginx en Ubuntu 20.04.

  • Un nombre de dominio configurado para que apunte a su servidor. Puede adquirir uno en Namecheap u obtener uno de forma gratuita en Freenom. Puede aprender a apuntar dominios a DigitalOcean siguiendo la documentación sobre dominios y DNS pertinente. Asegúrese de crear los siguientes registros DNS:

    • Un registro A con your_domain orientado a la dirección IP pública de su servidor.
    • Un registro A con www.your_domain orientado a la dirección IP pública de su servidor.
  • Conocimientos sobre la especificación WSGI, que el servidor de Gunicorn usará para comunicarse con su aplicación Flask. En esta discusión se abarca WSGI de forma más deallada.

Paso 1: Instalar los componentes desde los repositorios de Ubuntu

Nuestro primer paso será instalar todo lo que necesitamos desde los repositorios de Ubuntu. Esto incluye pip, el administrador de paquetes de Python, que gestionará nuestros componentes de Python. También obtendremos los archivos de desarrollo de Python necesarios para crear algunos de los componentes de Gunicorn.

Primero, actualizaremos el índice de paquetes locales e instalaremos los paquetes que nos permitirán crear nuestro entorno de Python. Entre ellos está phyton3-pip, junto con paquetes y herramientas de desarrollo adicionales que se necesitan 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

Una vez implementados estos paquetes, crearemos un entorno virtual para nuestro proyecto.

Paso 2: Crear un entorno virtual de Python

A continuación, configuraremos un entorno virtual para aislar nuestra aplicación de Flask de los otros archivos de Python del sistema.

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

  1. sudo apt install python3-venv

Luego, crearemos un directorio principal para nuestro proyecto de Flask. Después de crearlo, posiciónese en él:

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

Cree un entorno virtual para almacenar los requisitos de Python de su proyecto de Flask escribiendo lo siguiente:

  1. python3 -m venv myprojectenv

Con esto se instalará una copia local de Python y pip en un directorio llamado myprojectenv dentro del directorio de su proyecto.

Antes de instalar aplicaciones dentro del entorno virtual, deberá activarlo. Hágalo escribiendo lo siguiente:

  1. source myprojectenv/bin/activate

Su mensaje cambiará para indicar que ahora realiza operaciones dentro del entorno virtual. Se parecerá a esto: (myprojectenv)user@host:~/myproject$.

Paso 3: Configurar una aplicación de Flask

Ahora que se encuentra en su entorno virtual, podrá instalar Flask y Gunicorn y comenzar a diseñar su aplicación.

Primero, instalaremos wheel con la instancia local de pip para asegurarnos de que nuestros paquetes se instalen aunque falten archivos de wheel:

  1. pip install wheel

Nota

Independientemente de la versión de Phyton que use, cuando se active el entorno virtual deberá utilizar el comando pip (no pip3).

A continuación, instalaremos Flask y Gunicorn:

  1. pip install gunicorn flask

Creación de una aplicación de ejemplo

Ahora que dispone de Flask, puede crear una aplicación sencilla. Flask es un microframework. No cuenta con muchas de las herramientas que podrían incluirse en frameworks con más características y existe sobre todo como un módulo que puede importar a sus proyectos para que pueda inicializar una aplicación web.

Aunque la complejidad podría ser mayor, crearemos nuestra app de Flask en un único archivo, llamado myproject.py:

  1. nano ~/myproject/myproject.py

El código de aplicación residirá en este archivo. Importará Flask y creará una instancia de un objeto de Flask. Puede utilizarlo para definir las funciones que deberían 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 define básicamente el contenido que se presentará al acceder al dominio root. Guarde y cierre el archivo cuando termine.

Si siguió la guía de configuración inicial para servidores, debería tener activado un firewall UFW. Para probar la aplicación, debe permitir el acceso al puerto 5000:

  1. sudo ufw allow 5000

Ahora podrá probar su aplicación de Flask escribiendo lo siguiente:

  1. python myproject.py

Verá un resultado como el siguiente, en el cual se incluirá una advertencia útil que le recordará no utilizar esta configuración de servidor en la 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)

Agregue :5000 al final de la dirección IP de su servidor en su navegador web y visítela:

http://your_server_ip:5000

Debería ver algo como esto:

Aplicación de ejemplo de Flask

Cuanto termine, pulse CTRL-C en la ventana de su terminal para detener el servidor de desarrollo Flask.

Creación de un punto de entrada de WSGI

A continuación, crearemos un archivo que servirá como punto de entrada para nuestra aplicación. Esto indicará a nuestro servidor de Gunicorn cómo interactuar con la aplicación.

Llamemos al archivo wsgi.py:

  1. nano ~/myproject/wsgi.py

En él, importaremos la instancia de Flask desde nuestra aplicación y luego la ejecutaremos:

~/myproject/wsgi.py
from myproject import app

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

Guarde y cierre el archivo cuando termine.

Paso 4: Configurar Gunicorn

Su aplicación quedará, así, escrita con un punto de entrada establecido. Ahora, podemos continuar con la configuración de Gunicorn.

Antes de continuar, debe comprobar que Gunicorn pueda proveer correctamente la aplicación.

Podemos hacerlo con solo pasarle el nombre de nuestro punto de entrada. Se construye como el nombre del módulo (menos la extensión .py) más el nombre del elemento invocable dentro de la aplicación. En nuestro caso, es wsgi:app.

También especificaremos la interfaz y el puerto que se vinculará para que la aplicación se inicie en una interfaz disponible de forma pública:

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

Debería ver un resultado como el 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 de nuevo la dirección IP de su servidor con :5000 agregado al final en su navegador web:

http://your_server_ip:5000

Debería ver el resultado de su aplicación:

Aplicación de ejemplo de Flask

Cuando confirme que funciona correctamente, pulse CTRL-C en la ventana de su terminal.

Ya completamos las tareas de nuestro entorno virtual, por lo que podemos desactivarlo:

  1. deactivate

Ahora todos los comandos de Python usarán de nuevo el entorno de Phyton del sistema.

A continuación, crearemos el archivo de unidad de servicio systemd. Crear un archivo de unidad systemd permitirá que el sistema init de Ubuntu inicie automáticamente Gunicorn y haga funcionar la aplicación de Flask cuando el servidor se cargue.

Cree un archivo de unidad terminado en .service dentro del directorio /etc/systemd/system para empezar:

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

En su interior, empezaremos con la sección [Unit] que se usa para especificar metadatos y dependencias. Aquí agregaremos una descripción de nuestro servicio e indicaremos al sistema init que lo inicie solo tras haber alcanzado el objetivo de red:

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

A continuación, abriremos la sección [Service]. Esto especificará el usuario y el grupo con los cuales deseamos que se ejecute el proceso. Otorgaremos la propiedad del proceso a nuestra cuenta de usuario normal, ya que tiene la propiedad de todos los archivos pertinentes. También otorgaremos la propiedad del grupo al grupo www-data para que Nginx pueda comunicarse fácilmente con los procesos de Gunicorn. No se olvide de sustituir el nombre de usuario por el suyo:

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

[Service]
User=sammy
Group=www-data

A continuación, planearemos los detalles del directorio de trabajo y estableceremos el entorno variable PATH para que el sistema init sepa que los ejecutables para el proceso están ubicados dentro de nuestro entorno virtual. También especificaremos el comando para iniciar el servicio. Este comando hará lo siguiente:

  • Iniciar 3 procesos de trabajadores (debería, no obstante, ajustar esto si es necesario)
  • Crear un archivo de socket de Unix, myproject.sock, dentro del directorio de nuestro proyecto y establecer un vínculo con él. Estableceremos un valor sin máscara de 007 para que se cree el archivo de socket, se proporcione acceso al propietario y, al mismo tiempo, se restrinjan otros accesos.
  • Especifique el nombre del archivo del punto de entrada de WSGI junto con el elemento invocable de Python dentro de ese archivo (wsgi:app).

Systemd necesita que le proporcionemos la ruta completa al ejecutable de Gunicorn, que se instala dentro de nuestro entorno virtual.

No se olvide de sustituir el nombre del usuario y las rutas del proyecto por su 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

Por último, vamos a añadiremos una sección [Install]. Esto indicará a systemd a qué deberá vincular este servicio si lo habilitamos para que se cargue en el inicio. Queremos que este servicio se inicie cuando el sistema multiusuario normal esté 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 eso, nuestro archivo de servicio de systemd quedará completo. Guárdelo y ciérrelo ahora.

Ya podemos iniciar el servicio Gunicorn que creamos y activarlo para que se cargue en el inicio:

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

Comprobemos el estado:

  1. sudo systemctl status myproject

Debería ver el siguiente resultado:

Output
● myproject.service - Gunicorn instance to serve myproject Loaded: loaded (/etc/systemd/system/myproject.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-05-20 14:15:18 UTC; 1s ago Main PID: 46430 (gunicorn) Tasks: 4 (limit: 2344) Memory: 51.3M CGroup: /system.slice/myproject.service ├─46430 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─46449 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app ├─46450 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app └─46451 /home/sammy/myproject/myprojectenv/bin/python3 /home/sammy/myproject/myprojectenv/bin/gunicorn --workers 3 --bind unix:myproject.sock -m 007 wsgi:app

Si detecta errores, asegúrese de resolverlos antes de continuar con el tutorial.

Paso 5: Configurar Nginx para solicitudes de proxy

Ahora, nuestro servidor de aplicación Gunicorn debería estar funcionando, esperando solicitudes en el archivo de socket del directorio del proyecto. Configuraremos Nginx para que transmita las solicitudes web al socket haciendo algunas pequeñas adiciones a su archivo de configuración.

Comencemos creando un nuevo archivo de configuración de bloque de servidor en el directorio sites-available de Nginx. Lo llamaremos myproject para que se adecue al resto de esta guía:

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

Abra un bloque de servidor e indique a Nginx que escuche en el puerto predeterminado 80. También le indicaremos que utilice este bloque para solicitudes 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, agregaremos un bloque de ubicación que coincida con cada solicitud. Dentro de este bloque, incluiremos el archivo proxy_params que especifica algunos parámetros de proxy generales que deben configurarse. Luego, pasaremos las solicitudes al socket que definimos 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;
    }
}

Guarde y cierre el archivo al finalizar.

Para habilitar la configuración del bloque de servidor de Nginx que acaba de crear, vincule 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, puede realizar una verificación en busca de errores de sintaxis:

  1. sudo nginx -t

Si no se indican problemas, reinicie el proceso de Nginx para que lea la nueva configuración:

  1. sudo systemctl restart nginx

Por último, ajustaremos el firewall de nuevo. Ya no necesitamos acceso a través del puerto 5000, por lo que podemos eliminar esta regla. Luego, podemos permitir el acceso completo al servidor de Nginx:

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

Ahora debería poder visitar el nombre de dominio de su servidor en su navegador web:

http://your_domain

Debería ver el resultado de su aplicación:

Aplicación de ejemplo de Flask

Si encuentra algún error, intente verificar lo siguiente:

  • sudo less /var/log/nginx/error.log: verifica los registros de error de Nginx.
  • sudo less /var/log/nginx/access.log: verifica los registros de acceso de Nginx.
  • sudo journalctl -u nginx: verifica los registros de proceso de Nginx.
  • sudo journalctl -u myproject: verifica los registros de Gunicorn de su app de Flask.

Paso 6: Proteger la aplicación

Para asegurarse de que el tráfico hacia su servidor siga siendo seguro, obtendremos un certificado SSL para su dominio. Existen varias formas de hacer esto, incluyendo obtener un certificado gratuito de Let´s Encrypt, generar un certificado auto firmado, o comprar uno de otro proveedor, y configurar Nginx para usarlo siguiendo los Pasos 2 al 6 de Cómo crear un certificado SSL auto firmado para Nginx en Ubuntu 20.04. Por motivos de conveniencia, elegiremos la primera opción.

Instale el paquete de Nginx de Certbot con apt:

  1. sudo apt install python3-certbot-nginx

Certbot ofrece varias alternativas para obtener certificados SSL a través de complementos. El complemento de Nginx se encargará de reconfigurar Nginx y volver a cargar la configuración cuando sea necesario. Para utilizar este complemento, escriba lo siguiente:

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

Con esto, se ejecuta certbot con el complemento --nginx usando -d para especificar los nombres para los cuales deseamos que el certificado tenga validez.

Si es la primera vez que ejecuta certbot, se le solicitará introducir una dirección de correo electrónico y aceptar las condiciones de servicio. A continuación, certbot se comunicará con el servidor de Let’s Encrypt y, luego, realizará una comprobación para verificar que usted controle el dominio para el que solicita un certificado.

Si la comprobación se realiza correctamente, certbot le preguntará cómo desea configurar sus ajustes 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):

Seleccione su elección y luego ENTER. La configuración se actualizará y Nginx se volverá a cargar para aplicar los ajustes nuevos. certbot concluirá con un mensaje que le indicará que el proceso tuvo éxito e indicará la ubicación de almacenamiento de sus 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 siguió las instrucciones de instalación de Nginx en los requisitos previos, ya no necesitará la asignación de perfil HTTP redundante:

  1. sudo ufw delete allow 'Nginx HTTP'

Para verificar la configuración, acceda una vez más a su dominio utilizando https://:

https://your_domain

Una vez más, debería ver el resultado de su aplicación junto con el indicador de seguridad de su navegador, el cual debería indicar que el sitio está protegido.

Conclusión

A través de esta guía, creó y aseguró una aplicación de Flask simple dentro de un entorno virtual de Python. Creó un punto de entrada de WSGI para que cualquier servidor de aplicación con capacidad para WSGI pueda interactuar con él y configuró el servidor de aplicación de Gunicorn para proporcionar esta función. Luego, creó un archivo de servicio systemd para iniciar automáticamente el servidor de aplicación en el inicio. También creó un bloque de servidor de Nginx que transmite el tráfico de clientes web al servidor de la aplicación, y reenvía solicitudes externas, y protegió el tráfico hacia su servidor con Let’s Encrypt.

Flask es un framework muy sencillo, pero extremadamente flexible, diseñado para proporcionar funcionalidad a sus aplicaciones sin ser demasiado restrictivo respecto de la estructura y del diseño. Puede utilizar la pila general descrita en esta guía para hacer funcionar las aplicaciones de Flask que diseñe.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.