Flask y Nginx. WebHooks y Telegram.

Este es uno de los capítulos del tutorial Exprimiendo Telegram. Crea tu propio bot para Telegram.. Encontrarás los enlaces a todos los de capítulos, al final de este artículo.

Probablemente te extrañe ver este artículo, aquí dentro del tutorial sobre crear un bot para Telegram, pero todo tiene su sentido. La cuestión, es que para continuar exprimiendo Telegram con Wehbooks, necesito apoyarme en un servidor web. La necesidad reside en el hecho de que cada vez que se produzca una modificación en el canal o grupo donde tenemos añadido nuestro bot, se va a producir una llamada a nuestro servidor. Podía haberme decantado por la solución del servidor sencillo con Flask. Sin embargo, si queremos hacer algo medianamente potente, creo que utilizar un servicio como Apache o Nginx, es la mejor solución. En este caso, me he decantado por Nginx, como podía haberme decantado por Apache. Actualmente, es el servicio de páginas web que estoy utilizando, y creo que la combinación Flask y Nginx es una buena solución.

Desde luego, podíamos haber utilizado otro framework, o incluso no utilizar ninguno. Sin embargo, Flask nos va a facilitar enormemente la tarea, y va a simplificar mucho nuestro trabajo, y de ahí la combinación Flask y Nginx.

Flask y Nginx. WebHooks y Telegram.

Flask y Nginx

Lo primero decirte que aunque la configuración de la combinación Flask y Nginx no es trivial, tampoco tiene gran complejidad. La ventaja es que hecha una vez, hechas todas. Una vez lo tenemos configurado, añadir rutas para uno o varios bots, es realmente sencillo. Y esto que comento, también se puede aplicar a otras soluciones. Así por ejemplo, si lo que quieres hacer es un API, Flask es una solución fantástica.

Requisitos

Lo primero es instalar y configurar Nginx. Para eso, te recomiendo que le des un vistazo al artículo, sobre como instalar Nginx en tu Raspberry.

Una vez instalado y configurado Nginx, el siguiente paso, es atacar Flask. Antes de comenzar con la configuración de nuestro servicio con Flask, vamos a instalar las dependencias necesarias. Para ello, abre un terminal, y ejecuta las siguientes órdenes,

sudo apt install python3-pip python3-dev python3-venv

Instalación y configuración

Ahora que ya tenemos todos los ingredientes necesarios de nuestra receta, vamos a ponernos manos a la obra. Vamos al queso…

Los siguientes pasos son crear el directorio y el entorno virtual. Si no sabes a que me refiero, dale un vistazo al artículo sobre como crear un entorno virtual en Python. Aunque aquí vamos a reproducir los pasos necesarios para crear nuestro entorno virtual.
Actualmente, yo tengo el proyecto en /var/www/ejemplo.com/mibot/, pero tu lo puedes ubicar donde mejor consideres. Respecto a ejemplo.com indicarte que solo es a efectos de este artículo. No se si existe este dominio, y en caso de existir, no lo tengo yo.

mkdir -p /var/www/ejemplo.com/mibot/
cd /var/www/ejemplo.com/mibot/
python3 -m venv .venv
source /var/www/ejemplo.com/mibot/.venv/bin/activate

Ahora tenemos que instalar Flask y uwsgi

pip3 install uwsgi flask

WSGI y uWSGI

Aclaremos estos dos conceptos, para entender lo que estamos haciendo y porque.

WSGI, Web Server Gateway Interface, es una interfaz entre el servidor web y la aplicación web. Es decir, se trata del medio de comunicación entre nuestra aplicación y el servidor web, en nuestro caso Nginx.

Así, del lado de la aplicación se indica una función o método, donde se especifican las variables de entorno y la función que da la respuesta. Mientras que el lado del servidor se invoca la aplicación con cada una de las llamadas.

Existen diferentes soluciones que contienen aplicaciones WSGI, como puede ser Gunicorn, uWSGI, Gevent. En mi caso me he decantado por uWSGI. Este es un servidor de aplicaciones implementado en C, rápido y altamente configurame.

Nuestra primera ruta con Flash y Nginx

Ahora vamos a crear nuestra primera ruta. Para ello, crearemos el archivo /var/www/misitio.com/mibot/app.py con el siguiente contenido,

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "<h1>¿Que miras?</h1>"

if __name__ == "__main__":
    app.run(host='0.0.0.0')

Si quieres probarlo, tan solo tienes que ejecutar la orden python3 app.py. Vas a la dirección IP donde tengas instalado el servidor y utilizas el puerto 5000.

En este módulo, podemos crear tantas rutas como sea necesario, o como nos haga falta a nosotros para nuestra aplicación web. Se trata de un archivo que según nuestras necesidades podemos ir ampliado, para que de esa forma cumpla con nuestras expectativas.

El punto de entrada a nuestra aplicación

Este nuevo archivo, es el punto de entrada a nuestra aplicación. Se encontrará en /var/www/misitio.com/mibot/wsgi.py, y el contenido será el siguiente,

from app import app

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

Probemos esta aplicación. Para ello, ejecutaremos la siguiente orden,

uwsgi --socket 0.0.0.0:5000 --protocol=http -w wsgi:app

De nuevo nos vamos a nuestro navegador preferido, y consultamos la dirección IP y el puerto 5000.

Una vez realizados todos estos pasos ya tenemos la parte principal de nuestra aplicación. Lo siguiente es crear el archivo de configuración de uWSGI. Para ello creamos el archivo /var/www/misitio.com/mibot/mibot.ini, con el siguiente contenido,

[uwsgi]
module = wsgi:application

master = true
#shared-socket = :443
processes = 5
uid = www-data
gid = www-data

socket = mibot.sock
chmod-socket = 660
vacuum = true

die-on-term = true
logger = file:mibot.log

Systemd

El siguiente paso dentro consiste en crear un archivo para una unidad de Systemd. Si quieres conocer mas sobre Systemd, te recomiendo el tutorial sobre Trabajando con Systemd, y en particular el capítulo sobre como crear un servicio con Systemd.

El contenido de este archivo será como el que puedes ver a continuación,

[Unit]
Description=uWSGI instance to serve mibot
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/misitio.com/mibot
Environment="PATH=/var/www/misitio.com/mibot/env/bin"
ExecStart=/var/www/misitio.com/mibot/env/bin/uwsgi --ini mibot.ini

[Install]
WantedBy=multi-user.target

Con esto nuestro servicio /etc/systemd/system/mibot.service está completo. Ahora solo nos queda iniciarlo y habilitarlo para que se inicie con el sistema.

Para ello, ejecutaremos las siguientes órdenes,

sudo systemctl start mibot
sudo systemctl enable mibot

El siguiente paso, es comproabr que nuestro servicio está funcionando correctamente. Para ello, ejecutaremos la siguiente orden,

sudo systemctl status mibot

Esto nos arrojará un resultado como el que puedes ver a continuación,

mibot.service - uWSGI instance to serve mibot
   Loaded: loaded (/etc/systemd/system/mibot.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2018-11-18 10:31:42 UTC; 1 weeks 0 days ago
 Main PID: 18024 (uwsgi)
    Tasks: 6 (limit: 1152)
   CGroup: /system.slice/mibot.service
           ├─18024 /var/www/misitio.com/mibot/env/bin/uwsgi --ini mibot.ini
           ├─18066 /var/www/misitio.com/mibot/env/bin/uwsgi --ini mibot.ini
           ├─18067 /var/www/misitio.com/mibot/env/bin/uwsgi --ini mibot.ini
           ├─18068 /var/www/misitio.com/mibot/env/bin/uwsgi --ini mibot.ini
           ├─18069 /var/www/misitio.com/mibot/env/bin/uwsgi --ini mibot.ini
           └─18070 /var/www/misitio.com/mibot/env/bin/uwsgi --ini mibot.ini

Configurando Nginx

Ahora nos queda configurar Nginx. Nuestra aplicación tiene que estar siempre levantada y en funcionamiento, a la espera de cualquier llamada. Así, tenemos que configurar Nginx para que pasar cualquier llamada utilizando el protocolo uwsgi. Para esto tenemos que añadir lo siguiente en el archivo de configuración /etc/nginx/sites-available/misitio.com,

location /mibot/ {
            include uwsgi_params;
            uwsgi_pass unix:/var/www/misitio.com/mibot/mibot.sock;
        }

Recuerda ponerlo al principio de tu archivo de configuración, después de server_name y antes de location /, porque el orden es importante.

Para habilitar la configuración, tenemos que ejecutar la siguiente orden,

sudo ln -s /etc/nginx/sites-available/misitio.com /etc/nginx/sites-enabled

Ahora podemos comprobar si todo es correcto o hay algún error. Para ello, ejecutaremos la orden,

sudo nginx -t

Si todo ha ido bien, podemos reiniciar Nginx,

sudo systemctl restart nginx

En busca de problemas

No se trata de que te metas en lios, si no mas bien que intentes resolver los problemas que puedan surgir de una configuración errónea. Para ello, puedes revisar lo siguiente,

  • /var/log/nginx/error.log es el registro de errores de Nginx
  • /var/log/nginx/access.log en este archivo se encuentran las llamadas
  • sudo journalctl -u nginx comprueba los registros de Nginx.
  • sudo journalctl -u mibot comprueba los registros de tu aplicación.

Asegurando la aplicación

Por último conviene que la comunicación entre tu y tu servidor esté cifrada. Para ello, es recomendable que utilices un certificado firmado por un externo como puede ser Let’s Encrypt. Para hacerlo, te recomiendo, que leas el artículo sobre Let’s Encrypt en tu VPS.

Conclusiones

Espero que este artículo no te haya resultado exccesivamente pesado. No es que sea muy complicado, aunque si que es tedioso por la cantidad de pasos que tenemos que realizar para tener nuestra aplicación en funcionamiento. La ventaja es que una vez la tengamos en marcha solo tenemos que ir ampliando, y esto es muy sencillo. Se trata de que hagas una aplicación mínima y poco a poco, la vayas ampliando según tus necesidades o las necesidades de tu cliente.

El siguiente paso, que vendrá en un nuevo capítulo de este tutorial será introducir las primeras llamadas a la API de Telegram. De esta manera podrás ver como se relaciona tu servicio con Telegram. Al igual que hicimos en capítulos anteriores, vamos a ir de menos a mas, empezando por sencillas llamadas a la API para ir complicándolo de forma progresiva.


Más información,