Despliegue de aplicación de Django con Github Actions para un servidor propio

josemiguelsandoval

Jose Miguel Sandoval

Posted on June 3, 2024

Despliegue de aplicación de Django con Github Actions para un servidor propio

Para proyectos en donde la actualización del código es algo recurrente, es ideal realizar un proceso de automatización del despliegue del proyecto, además de asegurarse que este se subió correctamente. Esto se conoce como CI/CD (Integración continua/Despliegue continuo).

Hay varias formas de realizar este proceso para una aplicación de Django, por ejemplo se pueden utilizar contenedores de Docker, automatización con SSH, usando herramientas como Jenkins, etc. Sin embargo, para esta publicación se utilizará la herramienta de Github Actions para realizar este proceso.

Para desplegar este proyecto se utilizará también: Nginx como servidor web/Proxy, Supervisor que es un controlador de procesos, Gunicorn para servir la app y Postgresql que será el gestor de la base de datos.

Esta publicación es un ejemplo básico para subir una aplicación de Django usando Github Actions junto con un testeo previo, para verificar que el código este correctamente configurado antes de realizar el deploy.

Paso 1. Creación de un usuario en el servidor

Para utilizar Github Actions en Ubuntu, se debe utilizar un usuario que no sea el usuario root, si ya tienes otro usuario puedes saltar este paso.

Se creará el usuario ubuntu, para esto ingresamos al servidor que contratamos y como usuario root ejecutamos el siguiente comando:

sudo adduser ubuntu
Enter fullscreen mode Exit fullscreen mode

Paso 1.1: Se agrega al usuario dentro del grupo sudo

sudo usermod -aG sudo ubuntu
Enter fullscreen mode Exit fullscreen mode

Paso 1.2 (opcional): Configurar sudo para que no necesite contraseña

Solo en caso que se requiera que el nuevo usuario no requiera contraseña al ejecutar sudo, se puede editar el archivo de configuración de sudo:

sudo visudo
Enter fullscreen mode Exit fullscreen mode

En el final del archivo se debe agregar la siguiente línea:

ubuntu ALL=(ALL) NOPASSWD:ALL
Enter fullscreen mode Exit fullscreen mode

Paso 1.3: Ingresar con el nuevo usuario

Ahora puedes ingresar al servidor mediante SSH con el nuevo usuario creado.

Paso 2. Hacer las instalaciones necesarias en el servidor

Primero actualizaremos el servidor y todos sus paquetes ejecutando el siguiente comando:

sudo apt-get update
sudo apt-get upgrade
Enter fullscreen mode Exit fullscreen mode

Una vez actualizado el servidor, instalaremos los paquetes necesarios para realizar el despliegue

sudo apt install python3-pip python3-venv nginx git supervisor nano postgresql postgresql-contrib
Enter fullscreen mode Exit fullscreen mode

Paso 3: Creación de la base de datos

Para manejar la base de datos del proyecto, usaremos PostgreSQL.

Paso 3.1: Ingresar a la terminal de Postgres

Para ingresar a la terminal de Postgres sin necesidad de cambiar de usuario, se puede ejecutar el siguiente comando:

sudo -u postgres psql
Enter fullscreen mode Exit fullscreen mode

Este comando nos abrirá la terminal de Postgres y veremos algo así:

postgres=#
Enter fullscreen mode Exit fullscreen mode

Paso 3.2: Creación de la base de datos y usuario configurado en Django

Una vez dentro de la terminal de Postgres, primero crearemos el usuario, se debe reemplazar "username" y "password" por la configuración que tienes en tu proyecto:

CREATE USER username WITH PASSWORD 'password';
Enter fullscreen mode Exit fullscreen mode

Ahora creamos la base de datos:

CREATE DATABASE db_name;
Enter fullscreen mode Exit fullscreen mode

Para asignarles privilegios un usuario para el uso de la bbdd:

GRANT ALL PRIVILEGES ON DATABASE db_name TO username;
Enter fullscreen mode Exit fullscreen mode

Para establecer un usuario como propietario de la bbdd:

ALTER DATABASE db_name OWNER TO username;
Enter fullscreen mode Exit fullscreen mode

Para salir de la terminal de postgres se ejecuta lo siguiente:

\q
Enter fullscreen mode Exit fullscreen mode

Paso 4: Realizar la configuración de Github Actions Runner para Self Host

Paso 4.1: Crear Runner en Github:

Dentro de la sección "Settings" del repositorio de nuestro proyecto

Seccion settings github

se debe ir a Actions y luego a Runners.

Actions runners

Una vez en Runners, se debe hacer clic en el botón New self-hosted runner. Se abrirá una nueva página con las instrucciones necesarias para configurar el servidor linux.

Eleccion de servidor linux

Paso 4.2: Realizar la configuración en el servidor

Copiamos los comandos de la sección Download uno por uno y lo vamos ejecutando en el servidor siguiendo las instrucciones que aparece en pantalla. Si se quiere manejar los nombres por default, se debe apretar enter cuando al ejecutar un comando se pida algún texto.

Copiamos el primer comando de la sección Configure para crear el Runner. Si todo sale bien hasta este punto, puedes ejecutar los siguientes comandos para indicarle al runner que mantenga corriendo el servicio:

sudo ./svc.sh install
sudo ./svc.sh start
sudo ./svc.sh status
Enter fullscreen mode Exit fullscreen mode

Finalmente para verificar que todo esta correcto se puede ejecutar:

./run.sh
Enter fullscreen mode Exit fullscreen mode

Se debiese imprimir en la consola que la conexión con Gihtub es un éxito, para salir del run se puede apretar ctrl + c.

Paso 5: Configuración de Supervisor

Primero crearemos el archivo de configuración de nuestro servicio gunicorn, para esto nos movemos al directorio conf.d dentro de la carpeta de supervisor:

cd /etc/supervisor/conf.d
Enter fullscreen mode Exit fullscreen mode

Una vez dentro, creamos el archivo gunicorn.conf que contendrá las instrucciones que debe servir supervisor:

sudo nano gunicorn.conf
Enter fullscreen mode Exit fullscreen mode

A continuación mostraré un ejemplo de lo que debe estar escrito dentro del archivo. Lo importante es verificar que las rutas estén escritas correctamente, para esto debes cambiar {name_repository}por el nombre de tu repositorio y myproject por el nombre de tu proyecto django:

[program:gunicorn]
directory=/home/ubuntu/actions-runner/_work/name_repository/name_repository
command=/home/ubuntu/actions-runner/_work/name_repository/name_repository/venv/bin/gunicorn --workers 3 --bind unix:/home/ubuntu/app.sock myproject.wsgi:application  
autostart=true
autorestart=true
stderr_logfile=/var/log/gunicorn/gunicorn.err.log
stdout_logfile=/var/log/gunicorn/gunicorn.out.log

[group:gunicorn]
programs:gunicorn
Enter fullscreen mode Exit fullscreen mode

Para guardar los cambios apretamos ctrl + o, y para volver a la terminal ctrl + x.

Ahora creamos el directorio donde se escribirán los logs de gunicorn:

sudo mkdir /var/log/gunicorn
Enter fullscreen mode Exit fullscreen mode

Para que supervisor lea el archivo de configuración recién creado, se lo indicamos con el siguiente comando:

sudo supervisorctl reread
Enter fullscreen mode Exit fullscreen mode

Nos debería aparecer la siguiente respuesta

gunicorn: available
Enter fullscreen mode Exit fullscreen mode

Por último, para que supervisor ejecute los cambios recién leidos, ejecutamos:

sudo supervisorctl update
Enter fullscreen mode Exit fullscreen mode

Al verificar si el servicio gunicorn esta corriendo correctamente al ejecutar el siguiente comando:

sudo supervisorctl status
Enter fullscreen mode Exit fullscreen mode

Debiese aparecer un error FATAL con la descripción: can't find command '/home/ubuntu/actions-runner/_work/name_repository/name_repository/venv/bin/gunicorn', ya que aun no hemos subido el código a nuestro servidor, eso lo haremos a continuación.

Paso 6: Configuración del workflow de Github Actions

Para este caso se mostrará un ejemplo de como realizar el workflow separandolo en 2 pasos:

  1. Realizar tests para verificar que el código se suba correctamente.
  2. Si los tests pasaron correctamente, se subirá el código en el servidor.

Paso 6.1: Creación del archivo YAML de Github Actions

En la raíz del repositorio de github, crearemos la carpeta .github y dentro de esta la carpeta workflows. Dentro de esta última carpeta crearemos un archivo con la extensión .yml, por ejemplo ci-cd.yml. Por lo que, el repositorio debiese tener una estructura básica como se muestra a continuación:

.
├── .github
│   └── workflows
│       └── ci-cd.yml
├── myproject
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── requirements.txt
│
...
Enter fullscreen mode Exit fullscreen mode

Dentro del archivo ci-cd.yml escribimos las instrucciones que necesita Github Actions para automatizar las tareas:

name: CI CD Django

on:
  push:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest  # Puedes utilizar un runner predeterminado de GitHub Actions para los tests

    services:
      postgres:
        image: postgres:13
        env:
          POSTGRES_DB: db_name
          POSTGRES_USER: username
          POSTGRES_PASSWORD: password
        ports:
          - 5432:5432

    strategy:
      max-parallel: 4
      matrix:
        python-version: [3.10.12]

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v2
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install dependencies
        run: |
          python -m venv venv
          source venv/bin/activate
          pip install -r requirements.txt

      - name: Run migrations
        run: |
          source venv/bin/activate
          python manage.py migrate

      - name: Run tests
        run: |
          source venv/bin/activate
          python manage.py test

  deploy:
    needs: test  # Este trabajo solo se ejecuta si el trabajo 'test' pasa
    runs-on: self-hosted

    strategy:
      max-parallel: 4
      matrix:
        python-version: [3.10.12]

    steps:
      - name: Checkout main branch
        uses: actions/checkout@v3
        with:
          clean: false

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v2
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install dependencies # Se instala gunicorn pero debiese estar dentro de requiriments.txt
        run: |
          python -m venv venv
          source venv/bin/activate
          pip install -r requirements.txt
          pip install gunicorn

      - name: Run migrations
        run: |
          source venv/bin/activate
          python manage.py migrate

      - name: Reset supervisor
        run: sudo service supervisor restart
Enter fullscreen mode Exit fullscreen mode

Paso 6.2: Subir el archivo YML a Github para iniciar Github Actions

Hacemos un commit y un push de los cambios para subir nuestra configuración.

Al realizar un push, Github inmediatamente reconocerá el workflow y comenzará a realizar las tareas que se indicaron. Esto lo puedes visualizar dentro de la sección Actions en tu repositorio de Github.

Seccion actions

En caso que la operación sea un éxito, se mostrará un check verde al lado del nombre de la tarea, en caso contrario, aparecerá una x con color rojo.

Paso 6.3: Verificamos que el proyecto se subió al servidor

En caso que la operación haya resultado exitosa, podemos ver que dentro de la carpeta actions-runner se creo una carpeta llamada _work, esta carpeta contiene nuestro repositorio, para este caso name_repository, por lo tanto el directorio de la aplicación de Django debiese estar en la siguiente altura:

cd /home/ubuntu/actions-runner/_work/name_repository/name_repository
Enter fullscreen mode Exit fullscreen mode

Dentro de esta carpeta podemos encontrar la carpeta venv que contendrá el entorno virtual del proyecto, por lo que podemos activarla y generar comandos con managa.py como por ejemplo, crear un superusario.

Ahora podemos verificar si Supervisor reconoció el proyecto y se encuentra corriedo, para esto ejecutamos:

sudo supervisorctl status
Enter fullscreen mode Exit fullscreen mode

Si aparece que el servicio esta corriendo RUNNING, es porque el archivo y el código se encuentran configuradas correctamente. En caso de error, verifica las rutas dentro del archivo gunicorn.conf

Paso 7: Configuración de Nginx

Paso 7.1: Editar nginx.conf

Para configurar Nginx, primero debemos modificar el usuario que tiene configurado por defecto, para esto nos movemos dentro de la carpeta de nginx:

cd /etc/nginx
Enter fullscreen mode Exit fullscreen mode

Una vez ahí, editamos el archivo nginx.conf de la siguiente forma:

sudo nano nginx.conf
Enter fullscreen mode Exit fullscreen mode

Al principio del archivo, aparecerá la siguiente instrucción:

user www-data;
# resto de la configuracion
Enter fullscreen mode Exit fullscreen mode

Borramos la parte que dice www-data y la cambiamos por root:

user root;
# resto de la configuracion
Enter fullscreen mode Exit fullscreen mode

Guardamos los cambios con ctrl + o seguido de ctrl + x.

Paso 7.2: Configurar Nginx para subir nuestro proyecto

Para configurar Nginx e indicarle que tiene que desplegar nuestro proyecto, nos vamos a la siguiente ruta:

cd /etc/nginx/sites-available
Enter fullscreen mode Exit fullscreen mode

Una vez aquí, creamos un arhivo de configuración, que contendra todas las instrucciones necesarias para que nginx sirva nuestra aplicación, el nombre del archivo puede ser cualquiera pero con extensión .conf. Ejemplo:

sudo nano django.conf
Enter fullscreen mode Exit fullscreen mode

A continuación, mostraré un ejemplo básico de como configurar el archivo, pero pueden haber muchas más configuraciones para personalizar el funcionamiento de Nginx. Ejemplo:

server{

 listen 80;
 server_name 198.23.227.182; # Aqui va el dominio. Ejemplo: midominio.com o una dirección IP (no recomendado)


 location / {
  include proxy_params;
  proxy_pass http://unix:/home/ubuntu/app.sock;
 }

  location /static {
      alias /home/ubuntu/actions-runner/_work/name_repository/name_repository/static;
  }

  location /media {
      alias /home/ubuntu/actions-runner/_work/name_repository/name_repository/media;
  }

}
Enter fullscreen mode Exit fullscreen mode

Deberías verificar que las rutas a los archivos estáticos y de media sea correcta cambiando name_repository, por el nombre de tu repositorio.

El dominio indicado, debe estar configurado dentro del archivo settings.py de Django en ALLOWED_HOSTS. Ejemplo:

ALLOWED_HOSTS = ['*']  # El * permite cualquier HOST (no recomendado). Aquí debiese ir tu dominio
Enter fullscreen mode Exit fullscreen mode

Para verificar que la sintaxis del archivo este escrita correctamente, ejecutamos:

sudo nginx -t
Enter fullscreen mode Exit fullscreen mode

Debiese aparecer un mensaje como el siguiente:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Enter fullscreen mode Exit fullscreen mode

Ahora ejecutamos el siguiente comando para crear un enlace simbólico que referenciara al archivo de configuración recién creado:

sudo ln /etc/nginx/sites-available/django.conf /etc/nginx/sites-enabled
Enter fullscreen mode Exit fullscreen mode

Reiniciamos el servicio de nginx:

sudo service nginx restart
Enter fullscreen mode Exit fullscreen mode

Y si ahora buscamos el dominio ingresado dentro de nuestro navegador, debiésemos ver nuestro proyecto desplegado.

Conclusiones

Siguiendo estos pasos, podrás tener tu proyecto de Django desplegado usando las herramientas mencionadas en este artículo.

Hay varias formas de realizar un despliegue usando Github Actions y Django, lo importante es realizar testeos previos a realizar el deploy de tu proyecto, para que no existan errores en producción.

Espero que te haya sido de utilidad :) Cualquier duda, consulta o sugerencia, házmela llegar a través de los comentarios.

💖 💪 🙅 🚩
josemiguelsandoval
Jose Miguel Sandoval

Posted on June 3, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related