Como desplegar una aplicación PYTHON + FLASK + GUNICORN + NGINX
Este documento trata de cómo crear una aplicación Flask publicarla vía Gunicorn y que Nginx sirva como Forward Proxy, el objetivo es estrictamente técnico dirigido a su configuración.
Esta configuración es una de formas de llevar una aplicación hecha en Flask a producción.
Entorno de trabajo
Como el objetivo es hacer una configuración para producción se utiliza Linux como sistema operativo y las instrucciones que se muestran son de Ubuntu 20.04.6 LTS que es el más común y accesible pero se ha probado en Red Hat; aunque tú puedes utilizar cualquier distribución las instrucciones son muy similares.
Requerimientos
- Distribución Linux, para este caso Ubuntu Ubuntu 20.04.6 LTS
- Python 3
- pip
Instalar dependencias
Una vez teniendo la maquina hay que actualizarla y actualizar los paquetes que vamos a utilizar.
- actualizamos el sistema operativo
1
~$ sudo apt update
- Instalamos pip3 en caso de que no se tenga instalado
1
~$ sudo apt install python3-pip
- Instalamos flask
1
~$ pip3 install flask
- Instalamos NGINX
1
~$ sudo apt install nginx
- Instalamos Gunicorn
1
~$ pip install gunicorn
Creamos la aplicación flask
Flask es un Framework escrito en Python que permite crear aplicaciones web de manera muy fácil y con una configuración mínima, para este ejemplo se ha creado una página simple con fines ilustrativos y que sirva de base para los proyectos que tengas en mente.
- Creamos la carpeta myproject que será nuestro espacio de trabajo
1 2
~$ mkdir ~/myproject ~$ cd ~/myproject/
- Creamos un archivo de aplicación muy básico app.py
1
~/myproject$ touch app.py
- Agregamos el siguiente contenido.
1 2 3 4 5 6 7 8 9 10
from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return 'Hello World Flask' if __name__ == "__main__": app.run(debug=True)
- Lanzamos la aplicación.
1
~/myproject$ python3 app.py
1 2 3 4 5 6 7 8 9
* Serving Flask app 'app' (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 689-240-933
-
Revisamos directamente en el navegador http://127.0.0.1:5000/ y se verá nuestra página funcionando.
Configurando Gunicorn
Si bien Flask nos ayuda a lanzar la aplicación para entornos productivos no es recomendable, muy a pesar de que este ya tiene integrado un WSGI ( Web Server Gateway Interface) este no soporta múltiples peticiones, para eso se necesita utilizar una pieza que nos ayude con toda esa carga de peticiones y Gunicorn nos ayuda con eso.
- Lanzamos Gunicorn
1
~/myproject$ gunicorn --workers=3 app:app
1 2 3 4 5 6
[2023-05-05 00:13:56 +0000] [8891] [INFO] Starting gunicorn 19.7.1 [2023-05-05 00:13:56 +0000] [8891] [INFO] Listening at: http://127.0.0.1:8000 (8891) [2023-05-05 00:13:56 +0000] [8891] [INFO] Using worker: sync [2023-05-05 00:13:56 +0000] [8894] [INFO] Booting worker with pid: 8894 [2023-05-05 00:13:56 +0000] [8895] [INFO] Booting worker with pid: 8895 [2023-05-05 00:13:56 +0000] [8897] [INFO] Booting worker with pid: 8897
Si sale un error donde no encuentra Gunicorn sera necesario agregarlo al PATH
Como podemos ver en la salida al ejecutar
--workers=3
se crearon tres procesos que estarán respondiendo a las peticiones - Comprobamos que se haya desplegado de forma correcta desde el navegador http://127.0.0.1:8000
Configurando Nginx
Nginx se ha convertido en el Web Server preferido por su bajo uso de memoria y alta concurrencia, en esta configuracion se utiliza cono un Fordward Proxy que lo que va hacer es recibir la peticiones del usuario por el puerto 80 y este las va a redirigir al servivio que se creo en el paso anterior al puerto 8000.
Verificamos el estado de Nginx
Previamente ya habíamos instalado Nginx, si queremos saber si ya está activo lo comprobamos de la siguiente forma
- verificamos el estado del Nginx.
1
~/myproject$ systemctl status nginx
1 2 3 4 5 6 7 8 9 10
● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2023-05-05 01:13:56 UTC; 3min 20s ago Docs: man:nginx(8) Main PID: 8447 (nginx) Tasks: 3 (limit: 1151) CGroup: /system.slice/nginx.service ├─8447 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; ├─8448 nginx: worker process └─8449 nginx: worker process
-
Consultamos al puerto 80 veremos un mensaje “Welcome to nginx!”
Actualizamos la configuración de Nginx
- Creamos el archivo de configuración flask_app
1
~/myproject$ touch /etc/nginx/sites-enabled/flask_app
1 2 3 4 5 6 7 8
server { listen 80; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
Si están configurando bajo Red Hat hay que habilitar
sudo setsebool -P httpd_can_network_connect 1
, más informacion de SELinux aqui - Ahora tenemos dos archivos de configuracion, por lo que eliminamos uno (el default)
1 2 3 4 5 6
~/myproject$ ll /etc/nginx/sites-enabled/ total 12 drwxr-xr-x 2 root root 4096 May 4 23:42 ./ drwxr-xr-x 8 root root 4096 May 4 23:30 ../ lrwxrwxrwx 1 root root 34 May 4 23:30 default -> /etc/nginx/sites-available/default -rw-r--r-- 1 root root 233 May 4 23:42 flask_app
1
~/myproject$ sudo unlink /etc/nginx/sites-enabled/default
- Comprobamos el archivo de configuración sea correcto
1 2 3
~/myproject$ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
- Reiniciamos Nginx
1
~/myproject$ sudo nginx -s reload
Lanzar Gunicorn como servicio
Eso funciona, pero para hacerlo mejor podemos convertirlo en servicio de esta forma podemos lanzar y detener el servicio de manera mas facil.
- Se crea el archivo
gunicorn.service
1
~/myproject$ touch /etc/systemd/system/gunicorn.service
1 2 3 4 5 6 7 8 9 10 11 12 13
[Unit] Description=Gunicorn instance to serve myproject After=network.target [Service] User=user Group=user WorkingDirectory=/home/user/myproject Environment="PATH=/home/user/myproject" ExecStart=/usr/bin/gunicorn --workers 3 app:app [Install] WantedBy=multi-user.target
- Se ejecuta el servicio creado
1 2 3 4
sudo systemctl daemon-reload sudo systemctl start gunicorn sudo systemctl enable gunicorn sudo systemctl status gunicorn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
~$ sudo systemctl status gunicorn ● gunicorn.service - Gunicorn instance to serve myproject Loaded: loaded (/etc/systemd/system/gunicorn.service; enabled; vendor preset: enabled) Active: active (running) since Thu 2023-05-05 03:35:12 UTC; 1min 2s ago Main PID: 906 (gunicorn3) Tasks: 4 (limit: 1151) CGroup: /system.slice/gunicorn.service ├─ 906 /usr/bin/python3 /usr/bin/gunicorn3 --workers 3 app:app ├─1170 /usr/bin/python3 /usr/bin/gunicorn3 --workers 3 app:app ├─1174 /usr/bin/python3 /usr/bin/gunicorn3 --workers 3 app:app └─1175 /usr/bin/python3 /usr/bin/gunicorn3 --workers 3 app:app May 05 03:35:12 ubuntu-bionic systemd[1]: Started Gunicorn instance to serve myproject. May 05 03:35:13 ubuntu-bionic gunicorn3[906]: [2023-05-05 03:35:13 +0000] [906] [INFO] Starting gunicorn 19.7.1 May 05 03:35:13 ubuntu-bionic gunicorn3[906]: [2023-05-05 03:35:13 +0000] [906] [INFO] Listening at: http://127.0.0.1:8000 (906) May 05 03:35:13 ubuntu-bionic gunicorn3[906]: [2023-05-05 03:35:13 +0000] [906] [INFO] Using worker: sync May 05 03:35:13 ubuntu-bionic gunicorn3[906]: [2023-05-05 03:35:13 +0000] [1170] [INFO] Booting worker with pid: 1170 May 05 03:35:13 ubuntu-bionic gunicorn3[906]: [2023-05-05 03:35:13 +0000] [1174] [INFO] Booting worker with pid: 1174 May 05 03:35:13 ubuntu-bionic gunicorn3[906]: [2023-05-05 03:35:13 +0000] [1175] [INFO] Booting worker with pid: 1175
Si por laguna razón te equivocas en la configuración hay que hacer un
sudo systemctl daemon-reload
acompañado de unsudo systemctl restart gunicorn
Valida tu conocimiento
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1. ¿Flask es ???? escrito en Python que permite crear aplicaciones web?
- una librería
- un Framework
- un entorno de trabajo
- ninguna de las anteriores
2. Utilizamos Gunicorn porque...
- Flask no tiene un WSGI
- Flask no soporta múltiples peticiones
- La versión de WSGI que tiene integrado Flask no se puede utilizar en todas las distribuciones de Linux
- Gunicorn es más bonito
3. En esta configuración propuesta Nginx funciona como
- WSGI (Web Server Gateway Interface)
- Reverse proxy
- Forward Proxy
- Load balancer
4. Al ejecutar Gunicorn con el parámetro ```--workers=3```
- Se crearán dinámicamente máximo tres procesos como se vallan necesitando
- Se ejecutan tres aplicaciones de forma paralela
- Solo se aceptarán tres usuarios conectados
- Se crean tras procesos que estarán respondiendo a las peticiones
5. Nginx es un mejor que otros web servers porque
- nunca se cae
- es el que más se adapta con una configuración de Flask
- por su bajo uso de memoria y alta concurrencia
- No es mejor solo es el más popular