Nombre:

Tutorial #3 - Cómo desplegar una aplicación Flask

Duración:

45 minutos

Profesor responsable

Harold Castro, Mario Villamizar

Pre-Requisitos:

Shell GNU/Linux, Git, Postman

Objetivos

Al finalizar el tutorial el estudiante estará en capacidad de:

Requisitos para desarrollar el tutorial

En particular se utilizarán los siguientes recursos:

  1. 1 máquina (virtual) con sistema operativo Ubuntu 20.04
  2. Python 3.8 o superior
  3. Permisos de administrador para instalar paquetes
  4. Conocimientos en comandos de consola

Cipher RC4

Para administrar este proyecto usando Python 3.x, debe crear un nuevo ambiente virtual y una nueva carpeta ejecutando:

$ python3 -m venv lab-flask

Una instalación de Python 3.x es creada de inmediato en la carpeta ambiente. Para activar en nuevo entorno virtual, utilice el comando source:

$ source lab-flask/bin/activate

Para cerrar el ambiente de ejecución, ejecute el comando deactivate:

$ deactivate

La aplicación que se utilizara en este tutorial ya está desarrollada, utilizando Flask. Es un API RESTful, que expone un par de servicios para cifrar y descifrar información con el algoritmo RC4.

Clone el repositorio presentado a continuación:

$ git clone https://github.com/jpadillaa/taller-api-flask-2.git

Para el tutorial necesitamos instalar las siguientes dependencias que están disponibles en el Python Package Index (PyPI), y para instalarlas basta con utilizar pip:

$ pip3 install flask

$ pip3 install flask-restful

$ pip3 install flask-marshmallow

Vamos a realizar un primer despliegue de la aplicación en un ambiente de desarrollo. Para esto, vamos a ingresar al directorio de la aplicación y establecer las variables de entorno de la aplicación:

$ cd taller-api-flask-2

$ export FLASK_APP=app.py

$ export FLASK_DEBUG=1

$ export FLASK_ENV=development

Como puede observar las variables especifican qué aplicación deseamos desplegar y especificamos que el ambiente es desarrollo. Para ejecutar la aplicación, escriba el siguiente comando:

$ flask run -h 0.0.0.0

El comando anterior especifica que la aplicación recibirá solicitudes sobre cualquiera de las direcciones IP que tenga asignada la máquina. Si no tiene clara la dirección IP de su máquina ejecute previamente el comando:

$ ip a

Deberá observar una consola similar a la siguiente:

En este punto la aplicación ya acepta solicitudes. Como se mencionó previamente esta aplicación ofrece dos servicios cifrar y descifrar información utilizando el algoritmo RC4. La api está compuesta por las siguientes rutas:

El endpoint 1 del API REST es el responsable de cifrar un mensaje, para esto es necesario entregarle un documento JSON con el mensaje y la respectiva llave para realizar el cifrado. Este objeto debe ser enviado a través de una solicitud POST; para esto utilice POSTMAN como se presenta a continuación:

Como puede observar en la imagen, los campos numerados corresponden a:

  1. Es una solicitud POST al endpoint 1.
  2. El cuerpo de la solicitud es un documento JSON.
  3. La estructura del documento JSON con los campos:
  1. Respuesta del endpoint, documento JSON con el mensaje cifrado.

El endpoint 2 del API REST es el responsable de descifrar un mensaje, para esto es necesario entregarle un documento JSON con el mensaje cifrado y la respectiva llave para realizar el descifrado. Este objeto debe ser enviado a través de una solicitud POST; para esto utilice POSTMAN como se presenta a continuación:

Como puede observar en la imagen, los campos numerados corresponden a:

  1. Es una solicitud POST al endpoint 2.
  2. El cuerpo de la solicitud es un documento JSON.
  3. La estructura del documento JSON con los campos:
  1. Respuesta del endpoint, documento JSON con el mensaje descifrado.

Gunicorn

En esta sección del tutorial se aborda la configuración del servidor de aplicaciones Gunicorn y la forma de iniciar la aplicación Cipher, además de configurar Nginx para que funcione como un proxy inverso de cliente.

Primero, instalaremos wheel, un paquete estándar para la distribución de aplicaciones Python. El paquete se instala con pip:

$ pip3 install wheel

A continuación, instale Gunicorn en su ambiente virtual activo:

$ pip3 install gunicorn

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

Cree un nuevo archivo llamado wsgi.py y anexe el siguiente código:

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

En este archivo se importa la instancia de la aplicación Flask. Guarde y cierre el archivo. Antes de continuar, debe comprobar que Gunicorn pueda proveer correctamente la aplicación. Debe especificar la dirección IP y el puerto puerto de escucha Gunicorn. Ejecute el comando:

$ sudo ufw allow 5000

$ gunicorn --bind 0.0.0.0:5000 wsgi:app

Si la ejecución es correcta podrá observar una consola similar a la presentada en la siguiente imagen.

Nginx

Cuando confirme que Gunicorm funciona correctamente, presione Ctrl + C en la consola. Ya desarrollada estas tareas en el entorno virtual, es necesario desactivarlo:

$ deactivate

Se debe crear un archivo de servicio systemd. Crear un archivo de servicio systemd permitirá que Ubuntu inicie automáticamente Gunicorn con la aplicación de Flask cuando el servidor inicio.

Cree un archivo con extension .service dentro del directorio /etc/systemd/system:

$ sudo nano /etc/systemd/system/app.service

Inicie describiendo la sección [Unit] que se usa para especificar metadatos y dependencias. Aquí agregaremos una descripción del API REST y se indica al sistema que lo inicie después de que el sistema operativo alcanzó el objetivo de red:

[Unit]
Description=Gunicorn instance to serve APP
After=network.target

Agregue la sección [Service]. Esto especificará el usuario y el grupo que ejecutan el proceso. Asigne la propiedad del proceso a la cuenta de usuario normal, ya que tiene la propiedad de todos los archivos pertinentes (para este ejemplo el usuario local se llama "ubuntu"). Asigne la propiedad del grupo al grupo www-data para que Nginx pueda comunicarse con los procesos de Gunicorn:

[Unit]
Description=Gunicorn instance to serve APP
After=network.target

[Service]
User=ubuntu
Group=ubuntu

Es necesario configurar la variable PATH para que el sistema conozca los ejecutables ubicados dentro de nuestro entorno virtual. Nuestra configuración incluye:

No se olvide de sustituir el nombre del usuario y las rutas del proyecto que tenga en uso.

[Unit]
Description=Gunicorn instance to serve APP
After=network.target

[Service]
User=ubuntu
Group=ubuntu
WorkingDirectory=/home/ubuntu/taller-api-flask-2
Environment="PATH=/home/ubuntu/lab-flask/bin/"
ExecStart=/home/ubuntu/lab-flask/bin/gunicorn --workers 1 --bind unix:app.sock -m 007 wsgi:app

Finalmente, agregue la sección [Install]. Esto asigna a systemd que debe vincular este servicio para que se cargue en el arranque:

[Unit]
Description=Gunicorn instance to serve APP
After=network.target

[Service]
User=ubuntu
Group=ubuntu
WorkingDirectory=/home/ubuntu/taller-api-flask-2
Environment="PATH=/home/ubuntu/lab-flask/bin/"
ExecStart=/home/ubuntu/lab-flask/bin/gunicorn --workers 1 --bind unix:app.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target

El archivo de servicio de systemd está completo, guarde y cierre. Inicie el servicio Gunicorn creado y actívelo para que se cargue en el arranque:

$ sudo systemctl start app

$ sudo systemctl enable app

Si la configuración es correcta, la consola le presentará un mensaje igual a este:

Created symlink /etc/systemd/system/multi-user.target.wants/app.service → /etc/systemd/system/app.service.

Verifique el estado del proceso con el comando (Cierre con q):

$ sudo systemctl status app

Si detecta errores revise los pasos anteriores. Gunicorn está configurado, escuchando solicitudes en el archivo de socket del proyecto. Ahora es necesario configurar Nginx para que transmita las solicitudes web al socket. Instale Nginx:

$ sudo apt install nginx

Verifique el firewall de Ubuntu y habilite el puerto de escucha de Nginx para solicitudes HTTP:

$ sudo ufw app list

$ sudo ufw allow 'Nginx HTTP'

Verifique que Nginx este activo (Cierre con q):

$ sudo systemctl status nginx

Cree un nuevo archivo de configuración en el directorio sites-available de Nginx, llamado app para que se adecue al resto de esta guía:

$ sudo nano /etc/nginx/sites-available/app

server {
    listen 80;
    server_name ip_servidor;

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/ubuntu/taller-api-flask-2/app.sock;
    }
}

Guarde y cierre el al finalizar. Habilite la configuración de Nginx que acaba de crear, vincule el archivo al directorio sites-enabled​​​:

$ sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled

Con el archivo en ese directorio, puede realizar una verificación en busca de errores de sintaxis:

$ sudo nginx -t

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

$ sudo systemctl restart nginx

Ya no se requiere acceso a través del puerto 5000, por lo que debe eliminar esta regla. Luego, podemos permitir el acceso completo al servidor de Nginx:

$ sudo ufw delete allow 5000

$ sudo ufw allow 'Nginx Full'

Acceda a la aplicación vía POSTMAN y realice las pruebas respectivas. Ya no es necesario especificar el puerto.

Si encuentra algún error, verifique los logs:

$ sudo less /var/log/nginx/error.log

$ sudo less /var/log/nginx/access.log

$ sudo journalctl -u nginx

$ sudo journalctl -u app

[1] Gunicorn Docs - https://gunicorn.org/#docs

[2] Nginx - https://www.nginx.com/blog/maximizing-python-performance-with-nginx-parti-web-serving-and-caching/