Sé un Pythonista: Recibe trucos Python y accede a nuestro espacio de Slack

Tutorial Flask – Lección 17: Desplegar una aplicación Flask en producción con Nginx + Gunicorn

Lección 17 Desplegar una aplicación Flask en producción con nginx + gunicorn
Share on facebook
Share on twitter
Share on linkedin

Estimad@ amig@ y lector@, después de un duro pero ameno camino para crear nuestro miniblog, llegamos a esta lección final. En ella te enseñaré un esquema de cómo desplegar una aplicación Flask en un entorno de producción usando Nginx + Gunicorn.

Cuando acabe esta última lección, tendrás a tu alcance todas las herramientas básicas para adentrarte y seguir profundizando en el fascinante mundo de Flask. Y si tienes alguna duda, aquí me tienes para lo que te haga falta 😉

En esta lección utilizaremos todo el código que hemos desarrollado en las lecciones previas. Puedes descargarte el código de la última lección, en la que vimos cómo procesar ficheros en Flask, como te indico a continuación:

‼️ ATENCIÓN ‼️

🎯 Puedes descargar el código correspondiente a la Lección 16 desde el siguiente repositorio de Github:

git clone https://github.com/j2logo/tutorial-flask.git
git checkout tags/leccion16 -b leccion16

Índice

Aquí te dejo los puntos principales de esta lección:

Por qué no debes usar el servidor de Flask en producción

Ya en la propia documentación de Flask, nos advierten que no usemos el servidor que trae consigo para entornos de producción. Este servidor, basado en Werkzeug, realmente es una librería, no un servidor real y se proporciona para la comodidad del desarrollo. Además no está diseñado para ser particularmente eficiente, estable o seguro.

Gracias a él podemos acceder a la aplicación en nuestra máquina local sin tener que configurar otros servicios y hacer que jueguen bien juntos. Aunque puede servir archivos estáticos, lo hace muy lentamente en comparación con las herramientas creadas para hacerlo mucho mejor.

Una aplicación web en producción, tiene que ser capaz de soportar múltiples usuarios y solicitudes concurrentes. Para ello existen los servidores basados en WSGI, como Gunicorn, uWSGI o CherryPy. Todos ellos son servidores de aplicaciones, capaces de soportar la carga de trabajo para la que están destinados y de entender nuestro código Python.

Además de los servidores de aplicaciones, por otro lado tenemos los servidores web como Apache o Nginx. Este tipo de servidores están pensados para un alto rendimiento, manejar la seguridad, poner en cola solicitudes/respuestas, gestionar errores y servir contenido estático y de otro tipo al mismo tiempo de manera muy eficiente.

Hablaremos más de los servidores web y WSGI en la siguiente sección.

Nginx + Gunicorn una combinación perfecta para Flask

Como te decía en la sección anterior, para desplegar una aplicación Flask en producción haremos uso de dos tipos de servidores: un servidor web y un servidor de aplicaciones.

El servidor web es el responsable de recibir todas las peticiones del cliente, en él se configuran los certificados para SSL/TLS, sirve contenido estático (css, js, imágenes, …) y manda las respuestas al cliente. Además, hace de proxy enviando todas las peticiones que tienen que ver realmente con la aplicación Flask al servidor de aplicaciones para que este las procese.

Por tanto, el servidor de aplicaciones recibe las peticiones del cliente, a través del servidor web, y las transforma para que se ejecute el código correspondiente de la aplicación.

En mi caso, y es lo que explicaré en esta lección, siempre uso la combinación Nginx como servidor web y Gunicorn como servidor de aplicaciones. Esta dupla funciona a la perfección, ya que cada uno se encarga de hacer muy bien el trabajo para el que está pensado.

Nginx

Nginx está realmente optimizado para todas las cosas que debe hacer un servidor web. Por ejemplo:

  • Enrutar nombres de dominio (decide a dónde deben ir las solicitudes, o si una respuesta de error está en orden)
  • Servir archivos estáticos
  • Manejar muchas solicitudes que llegan a la vez
  • Manejar clientes lentos
  • Reenviar las solicitudes que deben ser dinámicas para Gunicorn
  • Manejar las peticiones https

Gunicorn

Principalmente, Gunicorn está pensado para:

  • Ejecutar un grupo de procesos/subprocesos de trabajo
  • Traducir las solicitudes procedentes de Nginx (u otro servidor web) para que sean compatibles con WSGI
  • Llamar al código de Python cuando llega una solicitud
  • Traducir las respuestas WSGI de su aplicación en respuestas http adecuadas

Dónde desplegar Flask con Nginx + Gunicorn

Buena pregunta con difícil respuesta 😩😩😩😩

Por un lado, te diré que existen múltiples posibilidades para desplegar una aplicación Flask con Nginx + Gunicorn. Por ejemplo: AWS (la nube de Amazon), Digital Ocean, Google Cloud, Heroku, Linode, … Yo personalmente trabajo con AWS.

Por otro lado, existen cientos de configuraciones distintas a aplicar que dependerán de cómo esté implementada tu aplicación, de cómo interactúe con otros servicios, de si usas Docker (con o sin ECS o Kubernetes), de si usas un servidor compartido, …

Como ves, opciones hay muchísimas y en este post no te las puedo explicar todas (además de que no podría ni sabría).

Entonces, ¿qué vamos a ver en esta lección? Principalmente, una configuración básica de cómo desplegar una aplicación Flask en producción con Nginx + Gunicorn, cuya esencia podrás aplicar en diferentes configuraciones.

La idea es que aprendas y te queden claros estos tres conceptos:

  • Cómo levantar una aplicación Flask con Gunicorn
  • Cómo configurar Nginx como proxy de Gunicorn
  • Cómo servir el contenido estático con Nginx

En definitiva, lo que aprendas en esta lección te servirá prácticamente para la mayor parte de configuraciones a las que te enfrentes.

Qué no aprenderás en esta lección:

  • Cuestiones básicas sobre seguridad en servidores o servidores web
  • Configurar certificados de servidor para aceptar peticiones https
  • Configurar una base de datos
  • Configurar un balanceador de carga
  • Instalar y configurar una utilidad para monitoreo de aplicaciones

Preparando el servidor

Antes de entrar en materia de la buena, quiero comentarte que he decidido hacer el despliegue de la aplicación Flask, nuestro miniblog, sobre una máquina con Ubuntu 18.04 instalado desde cero. Normalmente, Nginx y Gunicorn funcionan mucho mejor en Linux y esa es la distribución que he elegido. No obstante, puedes seleccionar otra diferente para tu despliegue y no debería haber problema.

❗️¡ATENCIÓN! NO es objeto de este tutorial mostrar cómo securizar un servidor Linux. No lo verás aquí.
Determinadas configuraciones de AWS y Google Cloud ya aplican fuertes directivas de seguridad a los servidores y puedes encontrar mucha documentación al respecto.

Si ya dispones de un servidor Linux instalado, sigue estos pasos (por simplificar estoy utilizando el usuario root, pero no es la práctica recomendada):

1 – Primero instala Python 3. En mi caso, como estoy utilizando Ubuntu, cuento con la utilidad apt:

$> apt install python3-pip python3-dev python3-setuptools

2 – A continuación instala virtualenv para manejar diferentes entornos virtuales Python:

$> pip3 install virtualenv

3 – Por último, instala git, de manera que podamos descargar el código del proyecto en el servidor:

$> apt install git

4 – Una vez que tenemos todos estos componentes instalados, sitúate en el directorio en el que quieres desplegar el código de la aplicación. En mi caso, como estoy usando el usuario root, lo haré en /root (no es lo recomendado):

$> cd /root

$> git clone https://github.com/j2logo/tutorial-flask.git

5 – Ahora accede al directorio del proyecto:

$> cd tutorial-flask

Desplegar Flask con Gunicorn

Una vez dentro del directorio del proyecto y con Python 3 y virtualenv instalados, vamos a desplegar la aplicación para que sea servida por Gunicorn. En el siguiente esquema se muestra la idea.

Esquema de despliegue de una aplicación Flask con Gunicorn

6 – Crea la carpeta env del entorno virtual:

$> virtualenv env

7 – Ahora abre el fichero env/bin/activate y añade las siguientes variables de entorno al final del mismo:

export FLASK_APP="entrypoint"
export FLASK_ENV="production"
export APP_SETTINGS_MODULE="config.prod"

Como ves, estamos indicando a Flask que el despliegue es en un entorno de producción. Con esto, se tendrán en cuenta las variables de configuración definidas en el directorio config/prod.py del miniblog.

8 – Activa el entorno virtual:

$> source env/bin/activate

9 – A continuación, instala todas las dependencias del proyecto que se encuentran en el fichero requirements.txt:

$> pip install -r requirements.txt

❗️¡ATENCIÓN! Revisa que la variable SQLALCHEMY_DATABASE_URI del fichero config/prod.py contiene la cadena de conexión de la base de datos de producción.

10 – Crea las tablas de la base de datos (la base de datos debe existir previamente). Para ello, usaremos el comando upgrade de Flask-Migrate:

$> flask db upgrade

11 – Instala gunicorn:

$> pip install gunicorn

Es probable que tengas que abrir los puertos del servidor que vayas a utilizar. En nuestro caso el puerto 80. Puede que tu proveedor te facilite alguna herramienta gráfica para ello. En el tutorial, como estoy usando una máquina Linux que he instalado yo mismo, tengo que abrir los puertos manualmente. Si es tu caso, continua con el paso 12, si no, salta al paso 14.

12 – Instala el firewall ufw para abrir los puertos:

$> apt install ufw

13 – Una vez instalado el firewall, abre el puerto 80:

$> ufw allow 80

14 – Por último, vamos a levantar Gunicorn indicando dónde puede encontrar una instancia de nuestra aplicación. Si recuerdas bien, tenemos una referencia a la instancia de la aplicación, llamada app, en el fichero entrypoint.py. Estos valores se los tenemos que pasar a Gunicorn como te muestro a continuación:

$> gunicorn --bind 0.0.0.0:80 entrypoint:app

Si accedes al servidor (con su IP o nombre de dominio) desde el navegador, verás la página principal del miniblog.

Mata al proceso de Gunicorn desde el terminal con Ctrl+c para continuar con la siguiente sección.

Configurando Flask en Nginx + Gunicorn

Hasta aquí, lo que hemos hecho ha sido sustituir el servidor interno que trae consigo Flask por Gunicorn. Sin embargo, no es lo habitual ni lo más correcto. Como te dije al comienzo de este post, la mejor solución es configurar Nginx como servidor web para servir el contenido estático y para actuar como proxy de Gunicorn.

Desplegar Flask con Nginx + Gunicorn

15 – Ahora vamos a volver a levantar Gunicorn pero de una manera diferente, asociándolo a un socket UNIX (esta es una de las opciones que tienes si Gunicorn y Nginx se ejecutan en la misma máquina, aunque también puedes indicar una IP/nombre de máquina + puerto):

$> gunicorn --workers 2 --bind unix:miniblog.sock -m 007 entrypoint:app &

Fíjate que he llamado al socket miniblog.sock. Además, se especifica una máscara 007 para que el socket sea creado dando permisos al usuario y su grupo, restringiendo otros accesos. La & del final es para que el servidor se ejecute en segundo plano.

16 – A continuación instala Nginx:

$> apt install nginx

Nginx está preparado para dar soporte a diferentes aplicaciones. Para ello, permite definir configuraciones, conocidas como bloque de servidor, en el directorio /etc/nginx/sites-available. Crea una configuración para cada aplicación que despliegues con Nginx en dicho directorio.

17 – Añade un archivo llamado miniblog al directorio /etc/nginx/sites-available con la siguiente configuración para nuestra aplicación:

server {
    listen 80;

    server_name <tu nombre de dominio>;

    location /static {
        alias /root/tutorial-flask/app/static;
    }

    location /media {
        alias /root/tutorial-flask/media;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/root/tutorial-flask/miniblog.sock;
    }
}

Las directivas location /static y location /media son las que hacen que Nginx sirva directamente los recursos estáticos de los directorios que indican, sin pasar por Gunicorn, cuando el path de una URL comienza por /static o /media. Para el resto de URLs actúa como proxy, redirigiendo las peticiones al socket que creamos para Gunicorn.

18 – Para que Nginx tenga en cuenta la configuración anterior, crea el siguiente enlace simbólico:

$> ln -s /etc/nginx/sites-available/miniblog /etc/nginx/sites-enabled

19 – Comprueba que está todo bien ejecutando el siguiente comando:

$> nginx -t

20 – Por último, ya puedes lanzar el servidor web:

$> /usr/sbin/nginx

Y en principio, eso es todo. Si accedes ahora a <tu nombre de dominio> desde el navegador, tendrás acceso al miniblog montado en un entorno de producción real 🎉💃🏻

Conclusión

Es muy complicado resumir en un único post cómo desplegar una aplicación Flask usando Nginx junto con Gunicorn. Como te explicaba al comienzo, existen diversos proveedores que ofrecen soluciones en la nube para desplegar allí nuestras aplicaciones. Por otro lado, hay múltiples configuraciones posibles para los sistemas, todas válidas. Depende elegir una u otra en función de distintos factores.

El objetivo de esta lección era ser el colofón a este tutorial. Recuerda, hemos visto: cómo levantar una aplicación Flask con Gunicorn, cómo configurar Nginx como proxy de Gunicorn y cómo servir el contenido estático con Nginx. Por tanto, tómatelo como un punto de partida. Investiga, juega, practica y sobre todo, diviértete programando.

Pues si has leído todas (o casi todas, jaja) las lecciones del tutorial, ya eres casi un ninja de Flask. Ahora tan solo queda poner en práctica lo que has aprendido. Y si te pierdes por el camino y/o te surge alguna duda, como siempre, puedes ponerte en contacto conmigo dejándome un mensaje al final del post, a través de mis redes sociales o enviándome un email. Estaré encantado de poder ayudarte.

Si te ha resultado útil, compártelo con tus amigos 🤗

Share on facebook
Facebook
Share on twitter
Twitter
Share on linkedin
LinkedIn

Sé un Pythonista: Recibe trucos Python y accede a nuestro espacio de Slack

También te puede interesar

* Te informo de que los datos de carácter personal que proporciones al comentar serán tratados por Juan José Lozano Gómez como responsable de esta web. La Finalidad es moderar los comentarios. La Legitimación es gracias a tu consentimiento. Destinatarios: tus datos se encuentran alojados en Disqus (disqus.com), mi sistema de comentarios, que está acogido al acuerdo de seguridad EU-US Privacy. Podrás ejercer Tus Derechos de Acceso, Rectificación, Limitación o Suprimir tus datos enviando un email a juanjo@j2logo.com. Encontrarás más información en la POLÍTICA DE PRIVACIDAD.

Sobre j2logo

Quiero ayudarte a que seas mejor programador/a, pero no uno cualquiera, sino uno de los top.

Últimos posts

¿Quieres ser un auténtico Pythonista? 🐍

📩 Recibe de vez en cuando trucos, scripts y tutoriales Python en español para dominar el lenguaje. No es SPAM. Date de baja cuando quieras

🥇 Accede a nuestra comunidad privada de Slack: Pythonistas-es

* Al enviar el formulario confirmas que aceptas la POLITICA DE PRIVACIDAD

Pythonistas-es

SÉ UN AUTÉNTICO PYTHONISTA

📩 Recibe trucos, scripts y tutoriales Python

🥇 Accede a nuestra comunidad privada de Slack