Despliegues con Python – Fabric y Clouding

Despliegues web - Fabric y clouding

En este post vamos a ver paso a paso cómo utilizar Fabric para hacer despliegues automáticos de aplicaciones, usando Clouding.io como servicio de hosting para las aplicaciones.

Como aplicación de ejemplo se usará la que incluye en mi libro python a fondo que se puede encontrar en el repositorio de código gratuito en github de Python a fondo – 10 Django a fondo.

Si quieres entrar en profundidad en cómo se crea una aplicación Django te recomiendo que leas el capítulo 10 de Python a Fondo libro donde se explica paso a paso cómo se crea esta aplicación.

Para qué sirve Fabric

En pocas palabras:

Fabric permite ejecutar comandos en una shell en servidores de forma remota de forma pythonica.

ElPythonista

La clave de fabric es su simplicidad y que permite automatizar la ejecución de comandos para evitar errores cuando se va a desplegar en servidores.

Más vale un ejemplo que mil palabras, por tanto, ¿cómo se podría saber el uptime de un servidor de forma simple?

env.hosts = ['85.208.23.67']
env.user = "root"
env.password = 'pass_del_usuario_remoto'

@task
def uptime():
    run('uptime')
(venv) ➜ fab uptime 
[85.208.23.67] Executing task 'uptime'
[localhost] local: uptime
16:54  up 6 days, 22:54, 1 user, load averages: 2.78 2.91 3.30

Done.

Así se pueden configurar comandos para no solo 1 servidor, sino varios y hacer deployments de aplicaciones en segundos totalmente programados.

Como si de un python manage.py o de un comando de flask se tratara, se pueden configurar comandos para hacer deploys en cada entorno haciendo tareas rutinarias como:

  • Activar el entorno virtual de Python.
  • Hacer un git pull de los cambios que haya en una rama en concreto.
  • Instalar las nuevas posibles dependencias que haya.
  • Actualizar los estáticos.
  • Hacer las migraciones necesarias para la base de datos.
  • Reiniciar la aplicación de gunicorn (usando supervisor por ejemplo)

Esas tareas sería las usuales a realizar en cualquier despliegue, pero como se puede ver, son tediosas y pueden tener errores, si se automatizan se evitan errores humanos.

La configuración de estas tareas sería algo parecido a esto:

@task
def deploy():
    with access_app(), virtualenv():
        update_code()
        deploy_django()
    restart_supervisor()

Las posibilidades que brinda Fabric son infinitas dado que se puede hacer cualquier tarea que se pueda hacer por ssh en una shell de comandos, pero además permite poder hacerlo en varios servidores a la vez!

Para poder desplegar en varios servidores a la vez, simplemente hay que añadir más servidores en env.hosts.

Lo siguientes sería explicar qué es el hosting y Clouding, ¡vamos a ello!

Qué es Clouding y por qué usarlo

Clouding.io es una plataforma española de hosting con diferentes opciones y servicios.

En pocas palabras:

Clouding.io

Clouding permite contratar servidores por horas en los que alojar nuestras aplicaciones, escalarlas y controlarlas con una interfaz simple pero eficaz.

ElPythonista

Para nuestro ejemplo, se utiliza Clouding para contratar máquinas que estén disponibles 24 horas/7 días a la semana y que puedan ser replicables fácilmente y a golpe de click.

De esta forma se paga por el rendimiento que necesitemos a cada momento e iremos incrementando la potencia, la memoria ram o el espacio conforme vayamos necesitando en el proyecto y no todo desde el principio.

Porqué uso Clouding frente a otros servicios:

  • Ofrecen escalar conforme al proyecto, pagando 3€ al mes (más IVA) mientras el proyecto crece y sin límite si realmente funciona.
  • Permiten hacer réplicas en segundos y backups automáticos sobre los servidores, cosa que ojalá no necesites, pero cuando lo necesitan te salvan el día.
  • Cada servidor tiene una IP fija pública.
  • Tienen servidores de diferentes sistemas operativos y diferentes distribuciones, y el almacenamiento es SSD con triple réplica lo que garantiza el rendimiento.
  • Es una empresa española y con soporte en español, y sinceramente lo he usado varias veces y estoy muy contento de lo técnico y eficaz que ha sido hasta ahora, hay que reconocer cuando la gente trabaja bien 😀

Por todo eso los escogí para algunos proyectos personales y los recomiendo dado que yo mismo los uso.

Correr la aplicación django en local

Antes de desplegar la aplicación, vamos a ver cómo descargar el repositorio de código y cómo correr la aplicación en un servidor linux en local.

Primero hay que descargar el repositorio de código usando git clone, para ello abran su consola de comandos preferida y escriban el siguiente comando:

$ git clone https://github.com/Marcombo/python-a-fondo.git

Esto les crea una carpeta llamada python-a-fondo donde pueden encontrar la aplicación django en python-a-fondo/Capitulo_10/django_a_fondo

Ejecutando los siguientes comandos tendremos el servidor corriendo y con contenido añadido:

$ git clone https://github.com/Marcombo/python-a-fondo.git
$ cp -r python-a-fondo/Capitulo_10/django_a_fondo/ django_a_fondo
$ cd django_a_fondo
$ python -m venv venv
$ source venv/bin/activate
$ pip install django
$ python manage.py migrate
$ python manage.py loaddata blog_app/fixtures/*
$ python manage.py runserver

Ahora simplemente accediendo a http://127.0.0.1:8000/ debemos de ver el servidor corriendo y con contenido.

Ahora viene la parte divertida, ya lo tenemos en local y podemos jugar con la aplicación pero,

¿cómo hacemos para que se vea público?

Cómo desplegar Django en Clouding con Fabric

A la hora de desplegar una aplicación django hay que tener en cuenta algunas consideraciones:

  • Necesitamos un servidor dentro de ouding donde desplegar la aplicación.
  • Se necesita ejecutar la aplicación en modo producción y como un servidor, para esto gunicorn con supervisor.
  • Necesitamos un servidor que se encargue del enrutado, por el que usaremos nginx (también se puede usar apache).
  • Desplegar el código.
  • Configurar la app django y los demás archivos.

Darnos de alta y crear un hosting en Clouding

Para crear un hosting y un servidor simplemente seguid los siguientes pasos:

Daros de alta en louding como nuevo usuario, tenemos que:

  • Seleccionar usuario y contraseña.
Registro en clouding
  • Validar el email.
  • Validar un número de teléfono.
  • Validar una tarjeta con dispositivo 3D Secure. Para la validación, se cobra 1€ que se devuelve en el transcurso de unas horas o unos días, dependiendo de la entidad bancaria. No os preocupéis, porque el 1€ se devuelve siempre, sin excepciones. Como Clouding tiene un sistema pre-pago, nunca se os cobrará nada a menos que hagáis recargas de saldo, una vez agotados los 5€ de saldo que os regalan para hacer pruebas sin ningún compromiso.
Validación de cuenta
  • Acceder al panel de administración de hosting.
Información básica del VPS

Cuando os deis de alta podéis contactar con el servicio al cliente diciendo que vais de parte de ElPythonista para apoyar a la web 😉

  • Crear un nuevo servidor
  • Comprobar que se ha creado correctamente y evaluar configuración

Configuración de gunicorn y de nginx para Django a Fondo – daf

Ahora hay que configurar los archivos de supervisor y de nginx que para agilizar el tutorial son los siguientes:

Guardar como: supervisor.conf

[program:daf]
directory=/root/django_a_fondo
command=/root/django_a_fondo/venv/bin/gunicorn --workers 3 django_a_fondo.wsgi:application -b localhost:8888
user=root
autostart=true
autorestart=true
process_name=%(program_name)s
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.err

[supervisord]
nodaemon=true

Ahora el archivo de nginx, guardar como: daf_nginx.conf

server {
    listen 80;
    # server_name default_server;

    location /static/ {
        root /user/root/django_a_fondo/static/;
    }

    location / {
        include proxy_params;
        proxy_pass http://127.0.0.1:8888;
    }

}

Como se puede ver, se está usando el usuario por defecto que es root por simplificar el proceso, pero se pueden crear grupos y usuarios para mejorar la seguridad y la granularidad de permisos de los usuarios en el servidor.

Al fin y al cabo, es el potencial que tiene usar VPS (servidores virtuales) que se pueden configurar como se quiera a cualquier nivel de la máquina.

Configuración de Fabric con fabfile.py

Teniendo los dos archivos ahora podemos desplegar usando fabric, crea otro archivo llamado fabfile.py con el contenido:

@task
def setup():
    install_package()
    setup_code()
    with access_app():
        create_virtualenv()
        with virtualenv():
            deploy_django()
            add_fixtures()
    setup_services()

@contextmanager
def virtualenv():
    with prefix("source %s" % posixpath.join(env.VENV_PATH, "bin/activate")):
        yield


@contextmanager
def access_app():
    with cd(env.APP_PATH):
        yield

PACKAGE_LIST = ['git', 'supervisor', 'nginx', 'python3.8-venv']


def install_package():
    sudo('apt-get update')
    sudo('apt-get install %s' % (' '.join(PACKAGE_LIST)))

def create_virtualenv():
    run('python3 -m venv venv')


def restart_supervisor():
    sudo(f'supervisorctl restart {app_name}')


def deploy_django():
    run('pip install -r requirements.txt')
    run('python manage.py migrate')
    run('python manage.py collectstatic')


def add_fixtures():
    run('python manage.py loaddata blog_app/fixtures/*')


def setup_code():
    with warn_only():
        run("git clone https://github.com/Marcombo/python-a-fondo.git")
    run("cp -r python-a-fondo/Capitulo_10/django_a_fondo/ django_a_fondo")


def setup_services():
    with cd("/tmp"):
        with warn_only():
            run("rm -r elpythonista")
            run("git clone https://github.com/tuxskar/elpythonista.git")

        with cd("/tmp/elpythonista/fabric_y_clouding"):
            run('cp settings.py /root/django_a_fondo/django_a_fondo/settings.py')

            run('cp supervisor.conf /etc/supervisor/conf.d/daf.conf')
            sudo('systemctl restart supervisor')

            run('cp daf_nginx.conf /etc/nginx/sites-enabled/default')
            sudo('systemctl restart nginx')

Como se puede ver es una configuración básica para que sirva de punto de inicio para desplegar la aplicación.

Vamos a analizar algunos aspectos:

@contextmanager

@contextmanager
def virtualenv():
    with prefix("source %s" % posixpath.join(env.VENV_PATH, "bin/activate")):
        yield

Se utiliza para utilizarlo dentro de un contexto de with y permite que todos los comandos que se ejecutan después usen este entorno virtual.

run y sudo

run('cp daf_nginx.conf /etc/nginx/sites-enabled/default')
sudo('systemctl restart nginx')

Se utilizan para lanzar comandos en el servidor remoto.

with y cd

with cd("/tmp/elpythonista/fabric_y_clouding"):
   algun_comando

Se utilizan para que todo el código en este bloque de ejecución se ejecute dentro de la carpeta que define el comando cd.

Despliegue de cambios con fabric

En las sucesivas actualizaciones del servidor, el código a ejecutar es diferente, dado que solo hay que:

  • Actualizar el código.
  • Instalar potenciales nuevas dependencias.
  • Hacer la potencial migración de la base de datos.
  • Recargar los estáticos.
  • Reiniciar los servicios de gunicorn y de nginx.
@task
def deploy():
    with access_app(), virtualenv():
        update_code()
        deploy_django()
    restart_supervisor()

Normalmente se suelen tener diferentes entornos de desarrollo (dev, pre y prod normalmente) y para cada uno de ellos se especifican las configuraciones específicas, usuarios, directorios, passwords, etc y se pueden automatizar los deployments de forma simple.

Despliegue de la aplicación en Clouding usando fabric

El archivo completo de fabfile y los ficheros de configuración se pueden encontrar en el repositorio de código de elpythonista -> repositorio de código

Para hacer el despliegue solo hay que modificar el archivo fabfile.py para añadir la IP del servidor de Clouding si se quiere se puede añadir la contraseña también, aunque es más recomendable no poner contraseñas en el código y copiarlo manualmente o usar claves ssh para acceder a los servidores:

env.hosts = ['85.208.23.73']

env.user = "root"

env.password = 'k2sC8frNDsmeyqfb'

Y ahora solo hay que ejecutar:

$ pip install fabric3 # para la primera vez que se quiera ejecutar fabric
$ fab setup
[85.208.22.205] Executing task 'setup'
[85.208.22.205] sudo: apt-get update
[85.208.22.205] out: 
[85.208.22.205] out: 0% [Working]
[85.208.22.205] out:             
[85.208.22.205] out: Hit:1 http://ubuntu.repos.clouding.io focal InRelease
[85.208.22.205] out: 
[85.208.22.205] out: 0% [Working]
[85.208.22.205] out:             
[85.208.22.205] out: Hit:2 http://ubuntu.repos.clouding.io focal-security InRelease
[85.208.22.205] out: 
[85.208.22.205] out:             
[85.208.22.205] out: Hit:3 http://ubuntu.repos.clouding.io focal-updates InRelease
[85.208.22.205] out: 
[85.208.22.205] out: 0% [Working]
[85.208.22.205] out: 0% [Working]
[85.208.22.205] out: 0% [Working]
[85.208.22.205] out: 20% [Working]
......
......
[85.208.22.205] out: Unpacking objects:  82% (38/46)
[85.208.22.205] out: Unpacking objects:  84% (39/46)
[85.208.22.205] out: Unpacking objects:  86% (40/46)
[85.208.22.205] out: Unpacking objects:  89% (41/46)
[85.208.22.205] out: Unpacking objects:  91% (42/46)
[85.208.22.205] out: Unpacking objects:  93% (43/46)
[85.208.22.205] out: Unpacking objects:  95% (44/46)
[85.208.22.205] out: Unpacking objects:  97% (45/46)
[85.208.22.205] out: Unpacking objects: 100% (46/46)
[85.208.22.205] out: Unpacking objects: 100% (46/46), 9.38 KiB | 686.00 KiB/s, done.
[85.208.22.205] out: 

[85.208.22.205] run: cp settings.py /root/django_a_fondo/django_a_fondo/settings.py
[85.208.22.205] run: cp supervisor.conf /etc/supervisor/conf.d/daf.conf
[85.208.22.205] run: systemctl restart supervisor
[85.208.22.205] sudo: cp daf_nginx.conf /etc/nginx/sites-enabled/default
[85.208.22.205] sudo: systemctl restart nginx

Done.
Disconnecting from 85.208.22.205... done.

Tarda unos segundos en desplegar todo lo necesario y al acceder a la IP del servidor tendremos el servidor desplegado nos encontramos ¡la aplicación corriendo en segundos!

Tareas de mantenimiento – el estado de supervisor o reiniciar nginx con fabric

Fabric es muy útil para conocer el estado de las aplicaciones en diferentes servidores, en nuestro caso podríamos tener varios servidores y lanzar el mismo comando de status:

env.hosts = ['85.208.23.73']
env.user = "root"
env.password = 'k2sC8frNDsmeyqfb'
env.VENV_PATH = 'venv'
env.APP_PATH = '/root/django_a_fondo'

app_name = 'daf'

@task
def restart_nginx():
    sudo('systemctl restart nginx')


@task
def super_status():
    run(f'supervisorctl status')

Y cuando se ejecutar:

$ fab super_status
[85.208.22.205] Executing task 'super_status'
[85.208.22.205] run: supervisorctl status
[85.208.22.205] Login password for 'root': 
[85.208.22.205] out: daf                              RUNNING   pid 4000, uptime 0:05:28
[85.208.22.205] out: 

[46.183.112.108] Executing task 'super_status'
[46.183.112.108] run: supervisorctl status
[46.183.112.108] Login password for 'root': 
[46.183.112.108] out: daf                              RUNNING   pid 3858, uptime 0:00:52
[46.183.112.108] out: 


Done.
Disconnecting from 85.208.22.205... done.
Disconnecting from 46.183.112.108... done.

Como vemos se pueden hacer auténticas maravillas que ahorran muchísimo tiempo y además evitan errores en el largo plazo.

Cambios de código y actualización del servidor

Cuando se quieran hacer cambios en el código se envian a git y se ejecuta:

(.virtualenvs) $ fab deploy
[85.208.22.205] Executing task 'deploy'
[85.208.22.205] run: pip install -r requirements.txt
[85.208.22.205] out: Requirement already satisfied: django in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 1)) (3.2.7)
[85.208.22.205] out: Requirement already satisfied: gunicorn in ./venv/lib/python3.8/site-packages (from -r requirements.txt (line 2)) (20.1.0)
[85.208.22.205] out: Requirement already satisfied: sqlparse>=0.2.2 in ./venv/lib/python3.8/site-packages (from django->-r requirements.txt (line 1)) (0.4.2)
[85.208.22.205] out: Requirement already satisfied: asgiref<4,>=3.3.2 in ./venv/lib/python3.8/site-packages (from django->-r requirements.txt (line 1)) (3.4.1)
[85.208.22.205] out: Requirement already satisfied: pytz in ./venv/lib/python3.8/site-packages (from django->-r requirements.txt (line 1)) (2021.1)
[85.208.22.205] out: Requirement already satisfied: setuptools>=3.0 in ./venv/lib/python3.8/site-packages (from gunicorn->-r requirements.txt (line 2)) (44.0.0)
[85.208.22.205] out: 

[85.208.22.205] run: python manage.py migrate
.........
.........
[85.208.22.205] out: 0 static files copied to '/root/django_a_fondo/static', 128 unmodified.
[85.208.22.205] out: 

[85.208.22.205] sudo: supervisorctl restart daf
[85.208.22.205] out: daf: stopped
[85.208.22.205] out: daf: started
[85.208.22.205] out: 


Done.
Disconnecting from 85.208.22.205... done.

Espero que os haya servido este mini-tutorial de cómo hacer deploys con Fabric de una aplicación Django en Clouding, cualquier duda o comentario podéis dejarlo abajo.

El código de fabfile, nginx y gunicorn los podeis encontrar en el repositorio de código de elpythonista:

github elpythonista fabric-clouding

En mi caso personal, uso Clouding para proyectos profesionales por muchos motivos pero principalmente por:

  • El soporte técnico: he acudido varias veces a ellos para ver cómo montar algunos servicios y siempre están dispuestos a ayudar
  • Las respuestas técnicas del soporte: que parece una obviedad pero hablar el mismo lenguaje técnico cuando necesitas resolver una tarea técnica no es común de encontrar y se agradece mucho.
  • La simpleza de los dashboards: al principio me parecían demasiado simples viniendo de otros hostings, pero tras usarlo he visto que tienen lo que se necesita sin añadir cosas superfluas y hace que la interfaz sea muy limpia.
  • La rapidez de los discos: habiendo usado desde otros servicios de hosting hasta una Raspberry Pi, la velocidad que se nota al ejecutar comandos y cómo funcionan los discos, incluso con el mínimo de CPU contratado es una pasada.
  • Es una empresa española: sinceramente me parece oportuno mencionar que es una empresa española en un sector que está copado por empresas de todo el mundo y si se puede apoyar a un producto nacional mejor que mejor!

Recordad que si os dais de alta y contactais al servicio al cliente ElPythonista apoyais que la creación de contenido como este.

Compartir

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Ver más

  • Responsable: Oscar Ramirez.
  • Finalidad:  Moderar los comentarios.
  • Legitimación:  Por consentimiento del interesado.
  • Destinatarios y encargados de tratamiento: No se ceden o comunican datos a terceros para prestar este servicio. El Titular ha contratado los servicios de alojamiento web a ionos (1&1) que actúa como encargado de tratamiento.
  • Derechos: Acceder, rectificar y suprimir los datos.
  • Información Adicional: Puede consultar la información detallada en la Política de Privacidad.

Publicar un comentario