Entrada

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.

  1. actualizamos el sistema operativo
    1
    
     ~$ sudo apt update
    
  2. Instalamos pip3 en caso de que no se tenga instalado
    1
    
     ~$ sudo apt install python3-pip
    
  3. Instalamos flask
    1
    
    ~$ pip3 install flask
    
  4. Instalamos NGINX
    1
    
    ~$ sudo apt install nginx
    
  5. 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.

  1. Creamos la carpeta myproject que será nuestro espacio de trabajo
    1
    2
    
     ~$ mkdir ~/myproject
     ~$ cd ~/myproject/
    
  2. Creamos un archivo de aplicación muy básico app.py
    1
    
     ~/myproject$ touch app.py
    
  3. 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)
    
  4. 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
    
  5. Revisamos directamente en el navegador http://127.0.0.1:5000/ y se verá nuestra página funcionando.

    Hola mundo flask

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.

  1. 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

  2. Comprobamos que se haya desplegado de forma correcta desde el navegador http://127.0.0.1:8000 gunicorn

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

  1. 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
    
  2. Consultamos al puerto 80 veremos un mensaje “Welcome to nginx!”

    Hola mundo flask

Actualizamos la configuración de Nginx

  1. 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

  2. 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
    
  3. 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
    
  4. 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.

  1. 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
    
  2. 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 un sudo 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  
Esta entrada está licenciada bajo CC BY 4.0 por el autor.