lunes, 7 de junio de 2021

Flask 21. Desplegar una aplicación Flask en Heroku.

logotipo de heroku


Anteriormente. Apéndice 20. Uso de MYSQL o MARIADB en un proyecto Flask.


En este capítulo vamos a desplegar nuestra aplicación en Heroku, que es un servidor de alojamiento en la nube externo. Muchos proveedores de alojamiento en la nube ofrecen una plataforma administrativa en la que se pueden ejecutar aplicaciones. 

Todo lo que necesitamos para implementar una aplicación de Python en estas plataformas es la aplicación solamente, porque el hardware, el sistema operativo, los interpretes de lenguaje de secuencias de comandos, las bases de datos etc son todos administrados por el servicio. Este tipo de servicio se denomina Plataforma como servicio o PaaS.

Heroku, que es un servicio muy popular, nos facilita además una serie de servicios de forma gratuita en donde podremos implementar nuestra aplicación de Python.


Hosting en Heroku.


La implementación de una aplicación Python en Heroku se realiza a través de la herramienta de control de versiones de git, por lo que deberemos tener nuestra aplicación en un repositorio de git. Heroku busca un archivo llamado Procfile en el directorio raíz de la aplicación para obtener instrucciones sobre como iniciar la aplicación. Para los archivos de Python, Heroku también espera un archivo llamado "requirements.txt" que contendrá todas las dependencias del modulo que deben instalarse. Una vez que nuestra aplicación se cargue en los servidores de Heroku a través de git, solamente tendremos que esperar unos segundos hasta que la aplicación este en línea y funcionando. Así de simple.

Lógicamente al ser un servicio gratuito, que nos facilitan para desarrollar y practicar la programación, la capacidad de computo y almacenamiento es limitada. Si necesitas mayor potencia tendrás que adquirirla a través de lo que Heroku llama "dynos". Pero para lo básico y para practicar, el servicio gratuito nos servirá de sobra.


Crear una cuenta en Heroku.

Antes de poder empezar, lógicamente tenemos que tener una cuenta con ellos. Visita www.heroku.com y crea una cuenta gratuita.

página de inicio de Heroku

Una vez que tengas la cuenta, inicia sesión con tu correo y password (Log in) y tendrás acceso a un panel de administración donde aparecerán nuestros proyectos.

vista de nuevo proyecto en heroku



Instalando el cliente de Heroku en el ordenador.


Heroku nos proporciona una herramienta de línea de comandos para poder interactuar con sus servicios llamada Heroku CLI, que tenemos disponible para Linux, Mac y Windows. En la documentación tenemos las instrucciones de instalación para cada una de las plataformas compatibles. 

página de descargas de Heroku



Como yo esto trabajando con Ubuntu usaré la siguiente:

$sudo snap install --classic heroku

pero también puedes usar la que aparece en su página web más abajo para Debian, que es la que nos funcionará en la Raspberry Pi.

Nota: En ubuntu una instalación alternativa si te diese problemas de actualización (heroku update) de paquetes de snap es curl https://cli-assets.heroku.com/install.sh | sh. En este caso el programa está en /usr/local/bin/heroku

Desinstala previamente el paquete snap con sudo snap remove heroku.

Sigamos.

Lo primero que tenemos que hacer es loguearnos en la cuenta de heroku que creamos antes. Para ello tecleamos en el terminal:

$ heroku login
Heroku CLI abrirá el navegador y nos llevara a una página donde nos loguearemos. Nos pedirá la dirección de correo con la que nos registramos y nuestro password de la cuenta. Una vez hecho esto podemos cerrar la página que se nos ha abierto en el navegador y si vamos al terminal veremos que aparecemos logueados. Esta acción no hace falta volver a hacerla porque se recordará para comandos posteriores.

Preparando la aplicación para subirla a Heroku.


Aunque ser pude hacer de múltiples formas yo voy a usar la siguiente. Primeramente crearemos un esquema de directorios donde poner el proyecto con la siguiente forma:


Esquema de directorios y archivos del proyecto


1.- Creo una carpeta que se llame proyecto y entro en la misma. 

$ mkdir proyecto
$ cd proyecto
proyecto $
2.- Creo en el entorno virtual que usará la aplicación:

proyecto $ python3 -m venv venv

3.- Creamos la carpeta src que contendrá el código fuente del proyecto.

proyecto $ mkdir src

Dentro de esta carpeta colocaré los archivos del programa de Flask que hemos utilizado a lo largo de este tutorial y que puedes encontrar en esta carpeta del proyecto en github..

Para descargar solamente este directorio y no todo el repositorio, entras en el directorio POST 19, copias el enlace del navegador y puedes utilizar alguno de estos 2 links que te dejo:

https://downgit.github.io/#/home

https://download-directory.github.io/

para obtener un archivo zip con el código fuente. Luego extraes los archivos dentro de la carpeta src.

La carpeta src debería quedar tal como se ve a continuación.


directorios y archivos del proyecto en el directorio padre scr


IMPORTANTE: al tratarse de un entorno de producción asegúrate de entrar en la carpeta config, editar el archivo config.py y poner el DEBUG=False.


Heroku necesita unos cuantos archivos para subir el proyecto y que funcione. Estos son:

requirements.txt  => Este archivo contiene todas las librerías o bibliotecas de Python que el programa utiliza y que son necesarias para su funcionamiento. Todos los paquetes que utilices para el proyecto deben estar aquí para que heroku sepa lo que se necesita. Este archivo en su día lo realizamos entrando en el entorno virtual y ejecutando la siguiente instrucción:

env $ pip freeze > requirements.txt

Aquí ya esta hecho y si lo abres verás todas las bibliotecas que se utilizan en el programa.

Nota: Si en alguna ocasión te sale dentro de requirements.txt el paquete pkg-resources==0.0.0 elimínalo que luego al subirlo a heroku da un error.

Como lo voy a necesitar posteriormente para pasar de una base sqlite a una postgresql (un poco más adelante veras porqué) vamos a instalar todos los paquetes o dependencias del proyecto en local. Para  instalarlos en nuestro ordenador tenemos que entrar en el entorno virtual, en el directorio src que contiene requirements.txt y ejecutar la siguiente instrucción. Vamos a hacerlo:

env $ pip install -r requirements.txt


[runtime.txt] => Es un archivo en el que se recoge la versión de Python del programa. Este es un archivo opcional y sino lo creamos el programa se ejecutará con la versión 3.9.5. No obstante podemos elegir otra versión distinta. Mira el manual ya que hay que escribir en el archivo la versión de una forma particular y con los tres dígitos, ya que sino no funcionará. Para saber que versión de Python estamos tenemos simplemente tecleamos:

>>> python3 --version
Python 3.8.5


Procfile => En este fichero se recoge que archivo se debe ejecutar en Heroku cuando la aplicación se inicie.

En Heroku se necesita para que cualquier proyecto de Flask funcione instalar el complemento gunicorn que ejecuta el servidor http. Como este paquete en el proyecto no estaba instalado necesitamos hacerlo. Para ello entraremos en el entorno virtual (desde el directorio src, $ source ../venv/bin/activate) y ejecutaremos la siguiente instrucción:

(env) /proyecto/src $ pip install gunicorn

Luego creamos el archivo Procfile y escribimos:

web: flask db upgrade; gunicorn inicio:app

Guardamos y salimos. 

inicio es el nombre del archivo de python que ejecuta la aplicación (inicio.py) y app hace referencia al módelo que lanza la aplicación dentro del mismo (app = Flask(__name__)). "flask db upgrade" lo utilizaremos para consolidar la migración de nuestra base de datos sql a la que utiliza heroku como veremos más adelante en el capitulo.

Como esta biblioteca no estaba instalada en su día, en el archivo requirements.txt tenemos que añadirla. Para ver la versión que se nos ha instalado tecleamos:

(env) ~/proyecto/src $ pip freeze
gunicorn==20.1.0

Pues tal cual copiamos la salida "gunicorn==20.1.0" y la añadimos al final del archivo requirements.txt. 

paquetes contenidos en el archivo requeriments.txt


Guardamos y salimos.

Es sistema de archivos es temporal.


En Heroku tenemos un problema. En cualquier momento se puede restablecer el servidor virtual en el que se ejecutará nuestro programa a su estado inicial. Esto ocasiona que no se pueda confiar en que los datos que se guarden en el sistema de archivos persistirán, ya que de hecho, Heroku restablece los servidores con bastante frecuencia.

El problema que se genera es que el motor de la base de datos SQlite escribe datos a un archivo en disco, que se borraría al reiniciarse el servidor al igual que todo lo que se almacene en memoria. Con lo que si se hubieran escrito nuevos registros estos se borrarían dejando la base de datos sql a su estado original.

Afortunadamente lo que si nos proporciona Heroku, con un complemento, es una base de datos propia Postgresql lo que nos solucionará el problema de la persistencia de los datos.

Trabajando con la base de datos Postgresql de Heroku.


En este punto nos encontramos con un segundo problema. Tenemos la base de datos de los usuarios en formato sqlite, la cual no es soportada por Heroku por lo que hemos comentado anteriormente. Por lo cual tenemos que usar una base con un motor de datos diferente. Heroku tiene una oferta de base de datos propia, basada en la base de datos Postgresql que vamos a utilizar. Escogeremos la que nos ofrece de forma gratuita que tiene un tamaño máximo de 8 MB y 20 conexiones simultáneas lo cual es más que suficiente para nuestro proyecto.

Para hacer esto, lo primero que tenemos que hacer es crear nuestra aplicación de Heroku. Aunque se puede hacer desde la pagina web, lo haremos desde el terminal del sistema con la instrucción:

$ heroku create nombre_proyecto_heroku
* Si el nombre_proyecto_heroku ya esta cogido te dará un error y tendrás que ponerle otro nombre distinto, el que más te guste. Yo lo he llamado novatillo.

y para añadirle una base de datos gratuita teclearemos:


heroku addons:create heroku-postgresql:hobby-dev --app nombre_proyecto_heroku


En mi caso la instrucción es como sigue:

$ heroku addons:create heroku-postgresql:hobby-dev --app novatillo
Creating heroku-postgresql:hobby-dev on ⬢ novatillo... free
Database has been created and is available
 ! This database is empty. If upgrading, you can transfer
 ! data from another database with pg:copy
Created postgresql-cylindrical-56001 as DATABASE_URL
Use heroku addons:docs heroku-postgresql to view documentation

La url de la base de datos se almacena en heroku en una variable de entorno llamada DATABASE_URL que estará disponible cuando se ejecute la aplicación. Pero aun que ya tenemos la base de datos tenemos que adaptar el conector en nuestro programa para poder usarla.


Conectando la base de datos de Heroku a nuestra aplicación Python.

Para usar PostgreSQL como nuestra base de datos en una aplicación de Python lo primero que tenemos que hacer es instalar el paquete psycopg2 en el entorno virtual, que es como el driver para que sqlalchemy pueda relacionarse con la la base postgrepsql que utiliza heroku:

(venv) ~/proyecto/src $ pip install psycopg2-binary

y como es un paquete nuevo que también es necesario para que nos funcione el proyecto en heroku tenemos que añadirlo al final del archivo requirements.txt

paquetes necesarios para instalar heroku, requeriments.txt


Ahora lo que tenemos que hacer es modificar el conector en nuestro programa para que SQLAlchemy se pueda comunicar con la base de datos de heroku una vez que lo hayamos subido a su servidor . Este conector es una variable del sistema llamada DATABASE_URL por lo que tenemos que capturarla y usarla en nuestro código tal como dice el manual de Heroku. 

Para saber cual es el conector con la base de datos de heroku podemos hacerlo desde la pagina web de administración de proyectos de heroku o desde el terminal usando la siguiente instrucción:

heroku config:get DATABASE_URL -a nombre_aplicación


# Como mi aplicación se llama novatillo  
(venv) ~/proyecto/scr $ heroku config:get DATABASE_URL -a novatillo
salida por pantalla del resultado de conectarnos a la base de datos de heroku

Copiamos todo esto que nos sale, que es la uri del conector.

Ahora tendríamos que entrar en config.py dentro de la carpeta config y modificar la línea 17 para capturar la variable de entorno en donde esta la base de datos en Heroku. Le tendríamos que decir al programa, que el conector  se encuentra primero en la variable del sistema DATABASE_URL y si no es así que use la antigua de sqlite.

Sin embargo por la nota siguiente voy a usar la uri que acabamos de copiar, y la pegaremos directamente. Ya que es un string la pegaremos dentro de un par de comillas " ".  Como ves la cadena comienza por "postgres:// ", pues bien cambia la palabra "postgres" por "postgresql". Quedarías así:


código del archivo config.py


¿Por qué? Lee lo siguiente:


Nota importante a la fecha en la que estoy escribiendo esto.


 El dialecto predeterminado que utiliza heroku en su base de datos es postgres y así nos lo pasan en DATABASE_URL. Sin embargo en un cambio reciente en la líbrería SQLAlchemy en la intrucción SQLALCHEMY_DATABASE_URI se espera encontrar un conector que comience por 'postgresql' para manejar este tipo de base de datos. De ahí que utilice todo el contenido de la variable y la modifique, y no capture solamente el valor de las misma. Es necesario para que la cosa funcione. Esto no quiere decir que en un futuro heroku pueda actualizar su complemento de postgres a postgresql ya no siendo necesario realizar este cambio. Si lo dejáramos tal cual tenemos este bonito error:
sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgres
Otra posible solución es utilizar una libreria de SQLALchemy inferior a la versión 1.4.0  (1.3.23 por ejemplo debería funcionar)


Nos queda otra cuestión por resolver. Hasta ahora la base de datos que teníamos era sqlite y ahora necesitamos pasar su estructura a otro sistema: PostgreSql. Para ello tenemos que migrar la base de datos. Esto lo puedes ver en más detalle en el capitulo 14, así que aquí no lo explicaré y solo pondré los pasos:

Empezamos instalando el paquete flask-migrate en nuestro entorno virtual:

(venv) ~/proyecto/src $ pip install flask-migrate

y añadimos el paquete al archivo requirements.txt (Flask-Migrate==3.0.1)

Ahora en el archivo principal de la aplicación inicio.py tenemos que importar Migrate desde flask_migrate:

código del archivo inicio.py

y declararla después de db=SQLAlchemy(app)

insertar migrate en el archivo de inicio de Python

El proceso de migración es sencillo. Desde el terminal del entorno virtual inicializamos la migración. Entramos en el directorio src, si no lo estamos ya. Lo primero le decimos a Flask cual es la aplicación principal, más tarde haremos lo mismo en heroku. Lo hacemos creando una variable de entorno.


(venv) ~/proyecto/src $ export FLASK_APP=inicio.py
Seguidamente iniciamos la migración con:

(venv) ~/proyecto/src $ flask db init

iniciando migración en Python con flask db init


y realizamos la primera migración:

(venv) ~/proyecto/src $ flask db migrate -m "preparando para heroku"

primera migración

Si te fijas dentro del directorio src que tenemos en local se ha creado un nuevo directorio llamado "migrations". 

El último paso de la migración se realizará directamente cuando se ejecute la aplicación en heroku ya que si recuerdas al principio del capitulo creamos un archivo llamado Procfile, en el que usamos la última instrucción que necesitamos "flask db upgrade". Esta creará las tablas en la base de datos de heroku y pasará automáticamente los datos que tengamos en ella.

web: flask db upgrade; gunicorn inicio:app


Creación del repositorio git, configuración y subida del proyecto a heroku.


Para subir nuestro programa a Heroku hay que crear un repositorio de git. 

Claro esta que si no lo tienes instalado lo primero es descargarlo y configurarlo. Si no lo tienes ve al paso 1) y si ya lo tienes pasa al 2)

1) Ve a la página de github y create una cuenta (sign up). Te pedirá un usuario y una cuenta de correo.

Para instalar el cliente git en el ordenador, en una pi que usa Debian, o en ubuntu como estoy yo usamos:

$ sudo apt-get install git

Una vez instalado el cliente de git tenemos que configurar nuestro nombre de usuario y dirección de correo electrónico.
$ git config --global user.name "nombre_usuario"
$ git config --global user.email tucorreo@correo.com

Si quieres ver tu configuración puedes usar la siguiente orden en el terminal:

git config --list


2) Nos metemos en la carpeta src, si no lo estamos ya y tecleamos. No hace falta estar dentro del entorno virtual pero si es importante estar en la carpeta src.

$ git init

Inicia el repositorio.

$ git status 

Vemos los archivos preparados para subir (en rojo)

$ git add .

Le decimos a git que añada todo lo que hay en el directorio en el que estamos (src) al repositorio.

$ git commit -m "paquete git para subir a heroku"

y con esto creamos el repositorio. Si te fijas se ha creado un directorio llamado .git oculto dentro del directorio src. Ahí esta el repositorio. Si más adelante quieres borrar el repositorio solo tienes que borrarlo.

Ahora enlazamos la aplicación que hemos creado en Heroku (novatillo en mi caso) con la aplicación o repositorio de flask que tenemos en el ordenador.

# heroku git:remote nombre_aplicación
$ heroku git:remote novatillo

Todo esto lo hacemos dentro de la carpeta src.

Ahora que tenemos el proyecto enlazado lo subimos a heroku (¡por fin!)

$ git push heroku master

y si todo va bien, comprimirá la aplicación, subirá los archivos, instalará los paquetes y ya tendremos la aplicación subida y el link de nuestra aplicación en Heroku:

subida del proyecto a Heroku


Pero antes de acceder y como el primero de los dos subcomandos que usamos en el archivo Procfile

Procfile: Heroku Procfile.

web: flask db upgrade; gunicorn inicio:app

está basado en un comando de flask, al igual que hicimos cuando iniciamos la migración en local, necesitamos añadir la variable FLASK_APP al entorno de Heroku 

$ heroku config:set FLASK_APP=inicio.py

y ya esta todo. 

Si navegamos a la página que nos ha facilitado heroku https://(nombre_proyecto).herokuapp.com veremos nuestro proyecto funcionando en el servidor:

proyecto funcionando en Heroku



Anexo.

Otros comandos de heroku cli que nos pueden ser útiles son:

Establecer una variable de entorno en Heroku

$ heroku config:set FLASK_APP=inicio.py

Ejecutar un comando en la consola de la aplicación en heroku. En este caso una que nos muestre el historial de 
migraciones

# heroku run -a nombre_aplicación (orden a ejecutar)
$ heroku run -a novatillo flask db history

Podríamos haber hecho el upgrade de la migración no poniendo el comando automáticamente en el archivo Procfiles sino usando la consola de esta forma:

Upgrade de la migración

$ heroku run -a novatillo flask db upgrade


Próximo Capítulo. Flask 22. Desplegar una aplicación de Flask en un contenedor de Docker.

No hay comentarios:

Publicar un comentario