Fork me on GitHub
(Descarga la versión en )

Despliegue en Heroku

Heroku es una plataforma como servicio (PaaS) de computación en la Nube que soporta distintos lenguajes de programación.

Heroku es propiedad de Salesforce.com.1 Heroku, es una de las primeras plataformas de computación en la nube, que fue desarrollada desde junio de 2007, con el objetivo de soportar solamente el lenguaje de programación Ruby, pero posteriormente se ha extendido el soporte a Java, Node.js, Scala, Python, PHP, Go y Clojure. La base del sistema operativo es Debian o, en la nueva plataforma, el sistema basado en Debian Ubuntu.2

Instalación

El primer paso va a ser darnos de alta en Heroku para elegir el nivel gratuito (free tier). Instalamos el cli de Heroku mediante:

1
sudo snap install heroku --classic

Y por último nos logeamos mediante la consola de heroku

1
heroku login

Este comando nos abrirá una ventana del navegador para introducir nuestras credenciales.

image-20220208190639825

Nos obliga a tener una aplicación en el móvil para doble autenticación.

Preparar la aplicación

El siguiente paso va a ser clonar la aplicación de ejemplo:

1
2
git clone https://github.com/heroku/python-getting-started.git
cd python-getting-started

Ahora tenemos un repositorio git en funcionamiento que contiene una aplicación simple, un archivo runtime.txt que especifica qué versión de Python se usará y un archivo requirements.txt, que usa el administrador de dependencias de Python, Pip.

Desplegar la aplicación

En este paso desplegaremos la aplicación en Heroku. Creamos una aplicación en Heroku para que reciba el código fuente:

1
heroku create

image-20220208191349365

Cuando creamos una aplicación, también se crea un de git remote (llamado heroku) y se asocia con su repositorio de git local. Heroku genera un nombre aleatorio (en este caso, whispering-plains-41029.git) para la aplicación, o puedes pasar un parámetro para especificar su propio nombre de aplicación.

Ahora desplegamos el código:

1
git push heroku main

Que genera la siguiente salida:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
Enumerando objetos: 512, listo.
Contando objetos: 100% (512/512), listo.
Compresión delta usando hasta 4 hilos
Comprimiendo objetos: 100% (238/238), listo.
Escribiendo objetos: 100% (512/512), 95.53 KiB | 95.53 MiB/s, listo.
Total 512 (delta 237), reusado 512 (delta 237)
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Building on the Heroku-20 stack
remote: -----> Determining which buildpack to use for this app
remote: -----> Python app detected
remote: -----> Using Python version specified in runtime.txt
remote: -----> Installing python-3.10.2
remote: -----> Installing pip 21.3.1, setuptools 57.5.0 and wheel 0.37.0
remote: -----> Installing SQLite3
remote: -----> Installing requirements with pip
remote:        Collecting django
remote:          Downloading Django-4.0.2-py3-none-any.whl (8.0 MB)
remote:        Collecting gunicorn
remote:          Downloading gunicorn-20.1.0-py3-none-any.whl (79 kB)
remote:        Collecting django-heroku
remote:          Downloading django_heroku-0.3.1-py2.py3-none-any.whl (6.2 kB)
remote:        Collecting sqlparse>=0.2.2
remote:          Downloading sqlparse-0.4.2-py3-none-any.whl (42 kB)
remote:        Collecting asgiref<4,>=3.4.1
remote:          Downloading asgiref-3.5.0-py3-none-any.whl (22 kB)
remote:        Collecting dj-database-url>=0.5.0
remote:          Downloading dj_database_url-0.5.0-py2.py3-none-any.whl (5.5 kB)
remote:        Collecting psycopg2
remote:          Downloading psycopg2-2.9.3.tar.gz (380 kB)
remote:          Preparing metadata (setup.py): started
remote:          Preparing metadata (setup.py): finished with status 'done'
remote:        Collecting whitenoise
remote:          Downloading whitenoise-5.3.0-py2.py3-none-any.whl (19 kB)
remote:        Building wheels for collected packages: psycopg2
remote:          Building wheel for psycopg2 (setup.py): started
remote:          Building wheel for psycopg2 (setup.py): finished with status 'done'
remote:          Created wheel for psycopg2: filename=psycopg2-2.9.3-cp310-cp310-linux_x86_64.whl size=586509 sha256=0b373775a27e5daf48cbc13af11c457b9bbad2565a67947f6be971c9697a2f72
remote:          Stored in directory: /tmp/pip-ephem-wheel-cache-qn7x_pyp/wheels/81/b6/3d/091aad3e8919ea76c84c2674b02ce3ab52de882e091c39249e
remote:        Successfully built psycopg2
remote:        Installing collected packages: sqlparse, asgiref, whitenoise, psycopg2, django, dj-database-url, gunicorn, django-heroku
remote:        Successfully installed asgiref-3.5.0 dj-database-url-0.5.0 django-4.0.2 django-heroku-0.3.1 gunicorn-20.1.0 psycopg2-2.9.3 sqlparse-0.4.2 whitenoise-5.3.0
remote: -----> $ python manage.py collectstatic --noinput
remote:        129 static files copied to '/tmp/build_0f5a1465/staticfiles', 379 post-processed.
remote: 
remote: -----> Discovering process types
remote:        Procfile declares types -> web
remote: 
remote: -----> Compressing...
remote:        Done: 68.1M
remote: -----> Launching...
remote:        Released v5
remote:        https://whispering-plains-41029.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
To https://git.heroku.com/whispering-plains-41029.git
 * [new branch]      main -> main

La aplicación está desplegada en https://whispering-plains-41029.herokuapp.com que podemos abrir mediante heroku open o visitando la url manualmente.

image-20220208191857083

Escalar la aplicación

En este momento, la aplicación se ejecuta en un único banco de pruebas web (dynos en nomenclatura Heroku). Piensa en un dyno como un contenedor ligero que ejecuta el comando especificado en el Procfile. Este archivo le dice a Heroku qué orden debe ejecutar para lanzar la aplicación:

1
web: gunicorn gettingstarted.wsgi

Puedes verificar cuántos dynos se están ejecutando con el comando ps:

1
2
3
4
5
6
7
8
$ heroku ps
Free dyno hours quota remaining this month: 550h 0m (100%)
Free dyno usage for this app: 0h 0m (0%)
For more information on dyno sleeping and how to upgrade, see:
https://devcenter.heroku.com/articles/dyno-sleeping

=== web (Free): gunicorn gettingstarted.wsgi (1)
web.1: up 2022/02/08 19:16:42 +0100 (~ 5m ago)

De forma predeterminada, la aplicación se implementa en un dyno gratuito. Los dynos gratuitos dormirán después de media hora de inactividad (si no reciben tráfico). Esto provoca un retraso de unos segundos para la primera solicitud al despertar. Las solicitudes posteriores se realizarán con normalidad. Los dynos gratuitos también consumen de una cuota mensual a nivel de cuenta de horas de dyno gratuitas; siempre que la cuota no se agote, todas las aplicaciones gratuitas pueden continuar ejecutándose.

Escalar una aplicación en Heroku es equivalente a cambiar la cantidad de dynos que se están ejecutando. Por ejemplo, podemos escalar el número de dynos web a cero:

1
heroku ps:scale web=0

Lo que producirá un error al refrescar la web porque no tenemos ningún dyno disponible.

Vamos a escalarlo de nuevo:

1
heroku ps:scale web=1

Instalar las dependencias de la aplicación localmente

Heroku reconoce una aplicación como una aplicación de Python al buscar archivos clave. Incluir un requirements.txt en el directorio raíz es una forma de que Heroku reconozca la aplicación de Python.

La aplicación de demostración que implementamos ya tiene un archivo requirements.txt y se parece a esto:

1
2
3
django
gunicorn
django-heroku

El archivo requirements.txt enumera las dependencias de la aplicación juntas. Cuando se implementa una aplicación, Heroku lee este archivo e instala las dependencias de Python adecuadas mediante el comando pip install -r.

Para instalar las dependencias localmente, primero queremos crear un “Entorno virtual” (también conocido como “venv”) en el que podamos instalar los paquetes sin afectar la instalación de Python en su sistema. Para hacer esto, instalamos el entorno virtual de python mediante sudo apt install python3.8-venv y posteriormente creamos un nuevo entorno virtual:

1
python3 -m venv venv

Y finalmente lo activamos con

1
source venv/bin/activate

Para instalarla con pip primero hemos de instalarlo mediante

1
sudo apt install python3-pip

Así como instalar el siguiente paquete para la conexión con Postgres

1
sudo apt-get install libpq-dev

Y ahora podemos instalar las dependencias en el entorno virtual:

1
pip install -r requirements.txt

La instalación de las dependencias también provocó la instalación de varias otras dependencias. Podemos verlas usando la lista de funciones de pip:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ pip list
Package            Version
------------------ -------
asgiref            3.5.0  
backports.zoneinfo 0.2.1  
dj-database-url    0.5.0  
Django             4.0.2  
django-heroku      0.3.1  
gunicorn           20.1.0 
pip                20.0.2 
pkg-resources      0.0.0  
psycopg2           2.9.3  
setuptools         44.0.0 
sqlparse           0.4.2  
whitenoise         5.3.0  

Ejecutar la aplicación localmente

La aplicación está casi lista para iniciarse localmente. Django usa activos locales (assets), por lo que primero debemos ejecutar collectstatic:

1
2
$ python3 manage.py collectstatic
129 static files copied to '/home/victor/Documentos/python-getting-started/staticfiles', 379 post-processed.

Y ejecutamos la aplicación en local mediante:

1
heroku local

Ahora podemos abrir http://localhost:5000 para visitar nuestra página.

Subir cambios locales

En este paso, aprenderemos cómo propagar un cambio local a la aplicación a través de Heroku. Como ejemplo, modificaremos la aplicación para agregar una dependencia adicional y el código para usarla.

Agregamos el paquete de requests al archivo requirements.txt:

1
2
3
4
django
gunicorn
django-heroku
requests

Y ahora actualizamos las dependencias

1
pip install -r requirements.txt

Modificamos hello/views.py para que importe el módulo requests al principio:

1
import requests

Y también modificamos el método index para que quede como sigue:

1
2
3
def index(request):
    r = requests.get('https://httpbin.org/status/418')
    return HttpResponse('<pre>' + r.text + '</pre>')

Volvemos a lanzar la aplicación en local:

1
heroku local

Y este es el resultado:

image-20220208200459474

Ahora subimos los cambios al repositorio de Heroku

1
2
3
git add .
git commit -m "Demo"
git push heroku main

Y finalmente comprobamos que todo funciona:

1
heroku open

Aprovisionar una base de datos

El marketplace de complementos tiene una gran cantidad de almacenes de datos, desde proveedores Redis y MongoDB, hasta Postgres y MySQL. En este paso, obtendremos información sobre el complemento Heroku Postgres gratuito que se aprovisionó automáticamente cuando se implementó su aplicación.

Para comprobar qué complementos tenemos instalados se usa el comando heroku addons:

image-20220210113011177

Y para comprobar la cadena de conexión que muestra la url que la aplicación usa para conectarse a la base de datos, usamos el comando heroku config

image-20220210113233030

Si queremos obtener más información sobre la conexión, usamos el comando heroku pg

image-20220210113557858

También provee de un comando que muestra información del estado de nuestro plan: heroku pg

image-20220210113351951

La aplicación de ejemplo que implementamos ya tiene funcionalidad de base de datos, a la que deberíamps poder acceder visitando la URL de la aplicación y agregando /db. Por ejemplo, si la aplicación se implementó en https://vast-citadel-33308.herokuapp.com/ visita https://vast-citadel-33308.herokuapp.com/db.

Pero si la visitamos ahora mismo nos dará un error de base de datos:

image-20220210113918173

porque todavía no la hemos creado.

Para crear la base de datos se usa el comando de Django de migración heroku run python manage.py migrate

image-20220210114121865

Ahora si accedemos a https://vast-citadel-33308.herokuapp.com/db veremos que se actualiza cada vez que la visitamos:

image-20220210114427154

El código para acceder a la base de datos es sencillo y utiliza un modelo simple de Django llamado Greetings que puede encontrar en hello/models.py.

Cada vez que visita la ruta /db de su aplicación, se invoca el siguiente método en el archivo hello/views.py que crea un nuevo saludo y luego presenta todos los saludos existentes:

>> hello/models.py

1
2
3
4
5
6
7
8
9
def db(request):

    greeting = Greeting()
    greeting.save()

    greetings = Greeting.objects.all()

    return render(request, 'db.html', {'greetings': greetings})

>> hello/views.py

1
2
3
4
5
6
7
8
def db(request):

    greeting = Greeting()
    greeting.save()

    greetings = Greeting.objects.all()

    return render(request, 'db.html', {'greetings': greetings})

Conclusiones

Ahora ya sabemos cómo implementar una aplicación, cambiar su configuración, escalar y adjuntar complementos

Créditos

Traducción libre de https://devcenter.heroku.com/articles/getting-started-with-python