CI/CD en Github Actions para un proyecto en Flask+Angular

CI/CD en Github Actions para un proyecto en Flask+Angular
En este artículo compartiré mi experiencia configurando CI/CD utilizando el panel de control Plesk y Github Actions. Hoy aprenderemos a desplegar un sencillo proyecto llamado «Helloworld». Está escrito en el framework de Python Flask, con trabajadores en Celery y un frontend en Angular 8.

Enlaces a los repositorios: para Talk, que es adecuado para realizar videoconferencias con entre 10 y 50 participantes en equipos convencionales., frontend.

En la primera parte del artículo veremos nuestro proyecto y sus partes. En la segunda, entenderemos cómo configurar Plesk e instalar las extensiones y componentes necesarios (BD, RabbitMQ, Redis, Docker, etc.).

En la tercera parte, finalmente nos ocuparemos de cómo configurar el pipeline para desplegar nuestro proyecto en el servidor en entornos dev y prod. Luego, lanzaremos el sitio en el servidor.

Y sí, olvidé presentarme. Me llamo Oleg Borzov, soy desarrollador fullstack en el equipo de CRM para gerentes de hipotecas en Domklik.

Visión general del proyecto

Para empezar, revisaremos dos repositorios del proyecto: el backend y el frontend, y echaremos un vistazo al código.

Parte del backend: Flask+Celery

Para la parte posterior, utilicé un stack bastante popular entre los desarrolladores de Python: el framework Flask (para APIs) y Celery (para la cola de tareas). Se utiliza SQLAlchemy como ORM. Para las migraciones, se aplica Alembic. Para la validación de JSON en endpoints, se usa Marshmallow.

En el repositorio hay un archivo Readme.md con una descripción detallada de la estructura e instrucciones para ejecutar el proyecto.

API de la parte web es bastante sencilla, consta de 6 endpoints:

  • /ping — para verificar la disponibilidad;
  • endpoints para registro, inicio de sesión, cierre de sesión y obtención del usuario autenticado;
  • un endpoint para enviar correos electrónicos, que coloca la tarea en la cola de Celery.

Parte de Celery es aún más simple, solo hay una tarea send_mail_task.

En la carpeta /conf hay dos subcarpetas:

  • docker con dos archivos Docker (base.dockerfile para construir una imagen base que cambia raramente y Dockerfile para las compilaciones principales);
  • .env_files — con archivos de variables de entorno para diferentes entornos.

En la raíz del proyecto hay cuatro archivos docker-compose:

  • docker-compose.local.db.yml para levantar una BD local para desarrollo;
  • docker-compose.local.workers.yml para levantar localmente un trabajador, BD, Redis y RabbitMQ;
  • docker-compose.test.yml para ejecutar pruebas durante el despliegue;
  • docker-compose.yml para el despliegue.

Y la última carpeta que nos interesa es .ci-cd. Contiene scripts shell para el despliegue:

  • deploy.sh — ejecución de migraciones y despliegue. Se ejecuta en el servidor después de la compilación y la ejecución de pruebas en Github Actions;
  • rollback.sh — revertir contenedores a la versión anterior de la compilación;
  • curl_tg.sh — enviar notificaciones sobre el despliegue a Telegram.

Frontend en Angular

Repositorio con el front-end mucho más simple que el back-end. El front-end consta de tres páginas:

  • Página principal con un formulario para enviar correos electrónicos y un botón de salida.
  • Página de acceso.
  • Página de registro.

La página principal tiene un diseño minimalista:

CI/CD en Github Actions para un proyecto en Flask+Angular
En la raíz hay dos archivos Dockerfile y docker-compose.yml, así como la carpeta familiar .ci-cd con un número ligeramente menor de scripts que en el repositorio del back-end (se eliminaron los scripts para ejecutar pruebas).

Creamos el proyecto en Plesk

Comencemos configurando Plesk y creando una suscripción para nuestro sitio.

Instalación de extensiones

En Plesk necesitaremos cuatro extensiones:

  • Docker para gestionar y visualizar el estado de los contenedores en el panel de administración de Plesk;
  • Git para definir el paso de despliegue en el servidor;
  • Let's Encrypt para generar (y auto-renovar) certificados TLS gratuitos;
  • Firewall para configurar la filtración del tráfico entrante.

Se pueden instalar a través del panel de administración de Plesk en la sección de Extensiones:

CI/CD en Github Actions para un proyecto en Flask+Angular
No vamos a considerar la configuración detallada de las extensiones, las configuraciones por defecto son suficientes para nuestros propósitos de demostración.

Creación de la suscripción y el sitio

A continuación necesitamos crear una suscripción para nuestro sitio helloworld.ru y agregar el subdominio dev.helloworld.ru.

  1. Creamos la suscripción para el dominio helloworld.ru y especificamos el nombre de usuario y la contraseña para el usuario del sistema:

    CI/CD en Github Actions para un proyecto en Flask+Angular
    En la parte inferior de la página marcamos la casilla Asegurar el dominio con Let’s Encrypt, si queremos configurar HTTPS para el sitio:

    CI/CD en Github Actions para un proyecto en Flask+Angular

  2. Luego, en esta suscripción, creamos el subdominio dev.helloworld.ru (para el cual también se puede emitir un certificado TLS gratuito):

    CI/CD en Github Actions para un proyecto en Flask+Angular

Instalación de componentes del servidor

Disponemos de un servidor con OS Debian Stretch 9.12 y el panel de control instalado Plesk Obsidian 18.0.27.

Necesitamos instalar y configurar para nuestro proyecto:

  • PostgreSQL (en nuestro caso será un servidor con dos bases de datos para el entorno dev y prod).
  • RabbitMQ (lo mismo, una instancia con diferentes vhosts para los entornos).
  • Dos instancias de Redis (para los entornos dev y prod).
  • Docker Registry (para el almacenamiento local de imágenes Docker compiladas).
  • Interfaz de usuario para el Docker registry.

PostgreSQL

El paquete de Plesk ya incluye la base de datos PostgreSQL, aunque no de la versión más reciente (en el momento de redactar este artículo, Plesk Obsidian soportaba Postgres versiones 8.4–10.8). Queremos la versión más reciente para nuestra aplicación (12.3 en el momento de escribir este artículo), por lo que la instalaremos manualmente.

Hay muchas instrucciones detalladas sobre cómo instalar Postgres en Debian en la red (ejemplo), así que no las describiré en detalle, solo proporcionaré los comandos:

wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O - | sudo apt-key add -
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ stretch-pgdg main" >> /etc/apt/sources.list.d/pgdg.list'

sudo apt-get update
sudo apt-get install postgresql postgresql-contrib

Dado que PostgreSQL tiene configuraciones predeterminadas bastante mediocres, es esencial ajustar la configuración. Para ello, nos ayudará calculador: debemos introducir los parámetros de nuestro servidor y reemplazar la configuración en el archivo /etc/postgresql/12/main/postgresql.confpor los sugeridos. Cabe aclarar que tales calculadoras no son una solución mágica, y la base de datos debe ser ajustada más cuidadosamente, en función de su hardware, aplicación y complejidad de las consultas. Pero esto es suficiente para empezar.

Además de la configuración sugerida por la calculadora, también cambiamos en postgresql.confel puerto predeterminado 5432 por otro (en nuestro ejemplo — 53983).

Después de modificar el archivo de configuración, reiniciamos el servidor de postgresql con el comando:

service postgresql restart

Hemos instalado y configurado PostgreSQL. Ahora crearemos las bases de datos, usuarios para los entornos dev y prod, y otorgaremos a los usuarios los permisos para gestionar las bases de datos:

$ su - postgres
postgres:~$ create database hw_dev_db_name;
CREATE DATABASE
postgres:~$ create user hw_dev_db_user with password 'hw_dev_db_password';
CREATE ROLE
postgres:~$ grant ALL privileges ON database hw_dev_db_name to hw_dev_db_user;
GRANT
postgres:~$ create database hw_prod_db_name;
CREATE DATABASE
postgres:~$ create user hw_prod_db_user with password 'hw_prod_db_password';
CREATE ROLE
postgres:~$ grant ALL privileges ON database hw_prod_db_name to hw_prod_db_user;
GRANT

RabbitMQ

Pasemos a la instalación de RabbitMQ, el intermediario de mensajes para Celery. Se instala de manera bastante sencilla en Debian:

wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
sudo dpkg -i erlang-solutions_1.0_all.deb

sudo apt-get update
sudo apt-get install erlang erlang-nox

sudo add-apt-repository 'deb http://www.rabbitmq.com/debian/ testing main'
wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add -

sudo apt-get update
sudo apt-get install rabbitmq-server

Después de la instalación, tenemos que crear vhosts, usuarios y otorgar los permisos necesarios:

sudo rabbitmqctl add_user hw_dev_amqp_user hw_dev_amqp_password 
sudo rabbitmqctl set_user_tags hw_dev_amqp_user administrator
sudo rabbitmqctl add_vhost hw_dev_vhost
sudo rabbitmqctl set_permissions -p hw_dev_vhost hw_dev_amqp_user ".*" ".*" ".*"

sudo rabbitmqctl add_user hw_prod_amqp_user hw_prod_amqp_password 
sudo rabbitmqctl set_user_tags hw_prod_amqp_user administrator
sudo rabbitmqctl add_vhost hw_prod_vhost
sudo rabbitmqctl set_permissions -p hw_prod_vhost hw_prod_amqp_user ".*" ".*" ".*"

Redis

Ahora vamos a instalar y configurar el último componente para nuestra aplicación: Redis. Se utilizará como backend para almacenar los resultados de las tareas de Celery.

Levantaremos dos contenedores de Docker con Redis para los entornos dev y prod utilizando la extensión Docker para Plesk.

  1. Entramos en Plesk, vamos a la sección de Extensiones, buscamos la extensión Docker y la instalamos (necesitamos la versión gratuita):

    CI/CD en Github Actions para un proyecto en Flask+Angular

  2. Vamos a la extensión instalada, mediante la búsqueda encontramos la imagen redis bitnami y ponemos la última versión:

    CI/CD en Github Actions para un proyecto en Flask+Angular

  3. Entramos en el contenedor descargado y ajustamos la configuración: especificamos el puerto, el tamaño máximo de RAM asignada, la contraseña en las variables de entorno, y montamos el volumen:

    CI/CD en Github Actions para un proyecto en Flask+Angular

  4. Realizamos los pasos 2-3 para el contenedor prod, en la configuración solo cambiamos los parámetros: puerto, contraseña, tamaño de RAM y la ruta a la carpeta de volumen en el servidor:

    CI/CD en Github Actions para un proyecto en Flask+Angular

Docker Registry

Además de los servicios básicos, sería conveniente instalar un repositorio Docker propio en el servidor. Afortunadamente, el espacio en los servidores ahora es bastante barato (definitivamente más barato que una suscripción a DockerHub) y el proceso de instalación de un repositorio privado es muy simple.

Queremos tener instalados:

Para ello:

  1. Crearemos en Plesk dos subdominios en nuestra suscripción: docker.helloworld.ru y docker-ui.helloworld.ru, y configuraremos certificados de Let’s Encrypt para ellos.
  2. En la carpeta del subdominio docker.helloworld.ru, añadiremos el archivo docker-compose.yml con este contenido:
    version: "3"
    
    services:
      docker-registry:
        image: "registry:2"
        restart: always
        ports:
          - "53985:5000"
        environment:
          REGISTRY_AUTH: htpasswd
          REGISTRY_AUTH_HTPASSWD_REALM: basic-realm
          REGISTRY_AUTH_HTPASSWD_PATH: /auth/.htpasswd
          REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
        volumes:
          - ./docker-registry.htpasswd:/auth/.htpasswd
          - ./data:/data
    
      docker-registry-ui:
        image: konradkleine/docker-registry-frontend:v2
        restart: always
        ports:
          - "53986:80"
        environment:
          VIRTUAL_HOST: '*, https://*'
          ENV_DOCKER_REGISTRY_HOST: 'docker-registry'
          ENV_DOCKER_REGISTRY_PORT: 5000
        links:
          - 'docker-registry'
    

  3. Por SSH, generaremos el archivo .htpasswd para la autorización básica en el repositorio Docker:
    htpasswd -bBc .htpasswd hw_docker_admin hw_docker_password
  4. Compilaremos y levantaremos los contenedores:
    docker-compose up -d
  5. Y necesitamos redirigir Nginx a nuestros contenedores. Esto se puede hacer a través de Plesk.

Las siguientes acciones se deben realizar para los subdominios docker.helloworld.ru y docker-ui.helloworld.ru:

En la sección Dev Tools en nuestro sitio, entramos en Reglas de Proxy de Docker:

CI/CD en Github Actions para un proyecto en Flask+Angular
Y añadimos una regla para el proxy del tráfico entrante a nuestro contenedor:

CI/CD en Github Actions para un proyecto en Flask+Angular

  1. Verificamos que podemos autorizar en nuestro contenedor desde la máquina local:
    $ docker login docker.helloworld.ru -u hw_docker_admin -p hw_docker_password
    WARNING! Using --password via the CLI is insecure. Use --password-stdin.
    Login Succeeded
  2. También verificaremos el funcionamiento del subdominio docker-ui.helloworld.ru:

    CI/CD en Github Actions para un proyecto en Flask+Angular
    Al hacer clic en Browse repositories, el navegador mostrará una ventana de autorización donde deberás ingresar el usuario y la contraseña para el repositorio. Después de eso, serás redirigido a una página con la lista de repositorios (en este momento estará vacía):

    CI/CD en Github Actions para un proyecto en Flask+Angular

Abrir puertos en el Firewall de Plesk

Después de instalar y configurar los componentes, necesitamos abrir puertos para que los componentes sean accesibles desde contenedores Docker y la red externa.

Veamos cómo hacer esto con el ejemplo de la extensión Firewall que instalamos previamente para Plesk.

  1. Vamos a Herramientas y Configuración > Configuración > Firewall:
    CI/CD en Github Actions para un proyecto en Flask+Angular
  2. Vamos a Modificar Reglas del Firewall de Plesk > Agregar Regla Personalizada y abrimos los siguientes puertos TCP para la subred de Docker (172.0.0.0/8):
    RabbitMQ: 1883, 4369, 5671-5672, 25672, 61613-61614
    Redis: 32785, 32786

    CI/CD en Github Actions para un proyecto en Flask+Angular

  3. También agregaremos una regla que abrirá al mundo exterior los puertos de PostgreSQL y el panel de gestión de RabbitMQ:

    CI/CD en Github Actions para un proyecto en Flask+Angular

  4. Aplicamos las reglas utilizando el botón Aplicar Cambios:

    CI/CD en Github Actions para un proyecto en Flask+Angular

Configuración de CI/CD en Github Actions

Pasemos a la parte más interesante: configurar la pipeline de integración y entrega continua de nuestro proyecto en el servidor.

Esta pipeline constará de dos partes:

  • construcción de la imagen y ejecución de pruebas (para el backend) — del lado de Github;
  • ejecución de migraciones (para el backend) y despliegue de contenedores — en el servidor.

Despliegue en Plesk

Primero, ocupémonos del segundo punto (ya que depende del primero).

Configurar el proceso de despliegue lo haremos a través de la extensión Git para Plesk.

Veamos un ejemplo con el entorno de producción para el repositorio Backend.

  1. Entramos en la suscripción de nuestro sitio Helloworld y vamos a la subsección Git:

    CI/CD en Github Actions para un proyecto en Flask+Angular

  2. Pegamos en el campo ‘Remote Git repository’ el enlace a nuestro repositorio de Github y cambiamos la carpeta por defecto httpdocs a otra (por ejemplo, /httpdocs/hw_back):

    CI/CD en Github Actions para un proyecto en Flask+Angular

  3. Copiamos la clave pública SSH de la etapa anterior y agregamos la pegamos en la configuración de Github.
  4. Presionamos OK en la pantalla en el punto 2, después de lo cual seremos redirigidos a la página del repositorio en Plesk. Ahora necesitamos configurar la actualización del repositorio al hacer commits en la rama master. Para ello, vamos a Configuración del Repositorio y guardamos el valor Webhook URL (lo necesitaremos más tarde al configurar Github Actions):

    CI/CD en Github Actions para un proyecto en Flask+Angular

  5. En el campo Actions de la pantalla del punto anterior, ingresamos el script para iniciar el despliegue:
    cd {REPOSITORY_ABSOLUTE_PATH}
    .ci-cd/deploy.sh {ENV} {DOCKER_REGISTRY_HOST} {DOCKER_USER} {DOCKER_PASSWORD} {TG_BOT_TOKEN} {TG_CHAT_ID} 

    donde:

    {REPOSITORY_ABSOLUTE_PATH} — ruta a la carpeta del repositorio backend de producción en el servidor;
    {ENV} — entorno (dev/prod), en nuestro caso prod;
    {DOCKER_REGISTRY_HOST} — host de nuestro repositorio Docker
    {TG_BOT_TOKEN} — token del bot de Telegram;
    {TG_CHAT_ID} — ID del chat/canal para enviar notificaciones.

    Ejemplo de script:

    cd /var/www/vhosts/helloworld.ru/httpdocs/hw_back/
    .ci-cd/deploy.sh dev docker.helloworld.ru docker_user docker_password 12345678:AAbcdEfghCH1vGbCasdfSAs0K5PALDsaw -1001234567890
  6. Añadimos un usuario de nuestra suscripción al grupo Docker (para que pueda gestionar contenedores):
    sudo usermod -aG docker helloworld_admin

El entorno de desarrollo para el repositorio backend y frontend se configura de manera similar.

Pipeline de despliegue en Github Actions

Pasamos a la configuración de la primera parte de nuestro pipeline CI/CD en Github Actions.

Backend

El pipeline se describe en el archivo deploy.yml.

Pero antes de analizarlo, llenaremos las variables Secret necesarias en Github. Para ello, nos dirigimos a Configuración -> Secretos:

  • DOCKER_REGISTRY — el host de nuestro repositorio Docker (docker.helloworld.ru);
  • DOCKER_LOGIN — usuario del repositorio Docker;
  • DOCKER_PASSWORD — contraseña para acceder;
  • DEPLOY_HOST — host donde está disponible el panel de Plesk (por ejemplo: helloworld.ru:8443 o 123.4.56.78:8443);
  • DEPLOY_BACK_PROD_TOKEN — token para el despliegue en el repositorio prod en el servidor (lo obtuvimos en Despliegue en Plesk punto 4);
  • DEPLOY_BACK_DEV_TOKEN — token para el despliegue en el repositorio dev en el servidor.

El proceso de despliegue es simple y consta de tres pasos principales:

  • compilación y publicación de la imagen en nuestro repositorio;
  • ejecución de pruebas en el contenedor basado en la imagen recién creada;
  • despliegue en el entorno adecuado según la rama (dev/master).

Frontend

El archivo deploy.yml para el repositorio front no difiere mucho del de backend. Falta el paso de ejecución de pruebas y cambian los nombres de los tokens para el despliegue. Los secretos para el repositorio front, por cierto, deben llenarse por separado.

Configuración del sitio

Proxy de tráfico a través de Nginx

Bueno, hemos llegado al final. Solo queda configurar el proxy del tráfico entrante y saliente a nuestro contenedor a través de Nginx. Este proceso ya lo hemos tratado en el punto 5 de la configuración del Docker Registry. Lo mismo debe repetirse para las partes de backend y frontend en los entornos dev y prod.

Voy a mostrar capturas de pantalla de la configuración.

Backend

CI/CD en Github Actions para un proyecto en Flask+Angular

Frontend

CI/CD en Github Actions para un proyecto en Flask+Angular
Aclaración importante. En el contenedor frontend se proxyarán todas las URL, excepto las que comienzan con /api/ — se proxyarán al contenedor backend (por lo que en el contenedor backend todos los manejadores deben comenzar con /api/).

Resultados

Ahora nuestro sitio debería estar disponible en las direcciones helloworld.ru y dev.helloworld.ru (entorno prod y dev respectivamente).

En resumen, aprendimos cómo preparar una aplicación simple en Flask y Angular y configurar en Github Actions un pipeline para su despliegue en un servidor gestionado por Plesk.

Reiteraré los enlaces a los repositorios con el código: para Talk, que es adecuado para realizar videoconferencias con entre 10 y 50 participantes en equipos convencionales., frontend.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster