
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: , .
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 hay un archivo Readme.md con una descripción detallada de la estructura e instrucciones para ejecutar el proyecto.
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.
es aún más simple, solo hay una tarea send_mail_task.
En la carpeta hay dos subcarpetas:
dockercon dos archivos Docker (base.dockerfilepara construir una imagen base que cambia raramente yDockerfilepara 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.ymlpara levantar una BD local para desarrollo;docker-compose.local.workers.ymlpara levantar localmente un trabajador, BD, Redis y RabbitMQ;docker-compose.test.ymlpara ejecutar pruebas durante el despliegue;docker-compose.ymlpara el despliegue.
Y la última carpeta que nos interesa es . 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
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:

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:
Dockerpara gestionar y visualizar el estado de los contenedores en el panel de administración de Plesk;Gitpara definir el paso de despliegue en el servidor;Let's Encryptpara generar (y auto-renovar) certificados TLS gratuitos;Firewallpara 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:

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.
- Creamos la suscripción para el dominio helloworld.ru y especificamos el nombre de usuario y la contraseña para el usuario del sistema:

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:
- Luego, en esta suscripción, creamos el subdominio dev.helloworld.ru (para el cual también se puede emitir un certificado TLS gratuito):

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 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 (), 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á : 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.
- Entramos en Plesk, vamos a la sección de Extensiones, buscamos la extensión Docker y la instalamos (necesitamos la versión gratuita):

- Vamos a la extensión instalada, mediante la búsqueda encontramos la imagen
redis bitnamiy ponemos la última versión:
- 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:

- 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:

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:
- un repositorio Docker protegido por contraseña, accesible a través de un subdominio ;
- Una interfaz de usuario para ver las imágenes en el repositorio, accesible en la dirección .
Para ello:
- 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.
- En la carpeta del subdominio docker.helloworld.ru, añadiremos el archivo
docker-compose.ymlcon 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' - 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 - Compilaremos y levantaremos los contenedores:
docker-compose up -d - 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:

Y añadimos una regla para el proxy del tráfico entrante a nuestro contenedor:

- 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 - También verificaremos el funcionamiento del subdominio docker-ui.helloworld.ru:

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):
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.
- Vamos a Herramientas y Configuración > Configuración > Firewall:

- 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
- También agregaremos una regla que abrirá al mundo exterior los puertos de PostgreSQL y el panel de gestión de RabbitMQ:

- Aplicamos las reglas utilizando el botón Aplicar Cambios:

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.
- Entramos en la suscripción de nuestro sitio Helloworld y vamos a la subsección Git:

- Pegamos en el campo ‘Remote Git repository’ el enlace a nuestro repositorio de Github y cambiamos la carpeta por defecto
httpdocsa otra (por ejemplo,/httpdocs/hw_back):
- Copiamos la clave pública SSH de la etapa anterior y la pegamos en la configuración de Github.
- 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):
- 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 casoprod;
{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 - 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 .
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: :8443 o :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
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

Frontend

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: , .
Fuente: habr.com
















