
In questo articolo condividerò la mia esperienza nella configurazione di CI/CD utilizzando il pannello di controllo Plesk e Github Actions. Oggi impareremo a distribuire un semplice progetto chiamato «Helloworld». È scritto nel framework Python Flask, con worker su Celery e frontend su Angular 8.
Collegamenti ai repository: , .
Nella prima parte dell’articolo daremo un’occhiata al nostro progetto e alle sue parti. Nella seconda parte vedremo come configurare Plesk e installare le estensioni e componenti necessari (DB, RabbitMQ, Redis, Docker, ecc.).
Nella terza parte scopriremo finalmente come impostare il pipeline per distribuire il nostro progetto sul server negli ambienti dev e prod. E poi lanceremo il sito sul server.
E sì, ho dimenticato di presentarmi. Mi chiamo Oleg Borzov e sono uno sviluppatore fullstack nel team CRM per i manager dei prestiti ipotecari in Domklik.
Panoramica del progetto
Per iniziare, diamo un’occhiata ai due repository del progetto — backend e frontend — e facciamo un breve giro del codice.
Parte backend: Flask+Celery
Per la parte backend ho scelto una combinazione piuttosto popolare tra gli sviluppatori Python: il framework Flask (per le API) e Celery (per la coda delle attività). Come ORM utilizzo SQLAlchemy. Per le migrazioni si usa Alembic. Per la validazione JSON nei controller — Marshmallow.
In c'è un file Readme.md con una descrizione dettagliata della struttura e istruzioni per l'avvio del progetto.
è piuttosto semplice, composta da 6 endpoint:
/ping— per controllare la disponibilità;- endpoint per registrazione, autorizzazione, disautorizzazione e per ottenere l'utente autorizzato;
- endpoint per inviare email, che mette un compito nella coda Celery.
è ancora più semplice, c'è solo un compito send_mail_task.
Nella cartella ci sono due sottocartelle:
dockercon due file Docker (base.dockerfileper costruire un'immagine di base che cambia raramente eDockerfileper le costruzioni principali);.env_files— con i file delle variabili d'ambiente per diversi ambienti.
Nella radice del progetto ci sono quattro file docker-compose:
docker-compose.local.db.ymlper avviare un database locale per lo sviluppo;docker-compose.local.workers.ymlper avviare localmente un worker, database, Redis e RabbitMQ;docker-compose.test.ymlper eseguire test durante il deployment;docker-compose.ymlper il deployment.
E l'ultima cartella che ci interessa — . Contiene script shell per il deployment:
deploy.sh— avvio della migrazione e del deployment. Viene eseguito sul server dopo la build e il passaggio dei test in Github Actions;rollback.sh— rollback dei contenitori alla versione precedente della build;curl_tg.sh— invio di notifiche di deployment su Telegram.
Frontend in Angular
è molto più semplice di quello del backend. Il frontend è composto da tre pagine:
- Pagina principale con un modulo per l'invio di email e un pulsante di logout.
- Pagina di accesso.
- Pagina di registrazione.
La pagina principale appare semplice:

Nella root ci sono due file Dockerfile e docker-compose.yml, insieme alla conosciuta cartella .ci-cd con un numero leggermente inferiore di script rispetto al repository del backend (sono stati rimossi gli script per l'esecuzione dei test).
Creiamo un progetto in Plesk
Iniziamo configurando Plesk e creando un abbonamento per il nostro sito.
Installazione delle estensioni
In Plesk abbiamo bisogno di quattro estensioni:
Dockerper gestire e visualizzare lo stato dei contenitori nell'interfaccia di Plesk;Gitper configurare il passo del deployment sul server;Let's Encryptper generare (e rinnovare automaticamente) certificati TLS gratuiti;Firewallper configurare il filtraggio del traffico in entrata.
È possibile installarli tramite l'interfaccia di Plesk nella sezione Extensions:

Non tratteremo la configurazione dettagliata delle estensioni; per i nostri scopi dimostrativi, le impostazioni di default sono sufficienti.
Creazione di un abbonamento e di un sito
Ora dobbiamo creare un abbonamento per il nostro sito helloworld.ru e aggiungere il sottodominio dev.helloworld.ru.
- Creiamo l'abbonamento per il dominio helloworld.ru e indichiamo login e password per l'utente di sistema:

In fondo alla pagina, selezioniamo la casella Proteggi il dominio con Let’s Encrypt, se vogliamo configurare HTTPS per il sito:
- Successivamente, in questo abbonamento, creiamo il sottodominio dev.helloworld.ru (per cui è possibile emettere anche un certificato TLS gratuito):

Installazione dei componenti del server
Abbiamo a disposizione un server con OS Debian Stretch 9.12 e il pannello di controllo installato Plesk Obsidian 18.0.27.
Dobbiamo installare e configurare per il nostro progetto:
- PostgreSQL (nel nostro caso ci sarà un server con due DB per l'ambiente dev e prod).
- RabbitMQ (lo stesso, un'istanza con diversi vhost per gli ambienti).
- Due istanze di Redis (per gli ambienti dev e prod).
- Docker Registry (per la memorizzazione locale delle immagini Docker costruite).
- Interfaccia UI per il registro Docker.
PostgreSQL
Insieme a Plesk è già inclusa una DBMS PostgreSQL, ma non dell'ultima versione (al momento della scrittura dell'articolo, Plesk Obsidian Postgres versioni 8.4–10.8). Vogliamo installare per la nostra applicazione l'ultima versione disponibile (12.3 al momento della scrittura), quindi la installeremo manualmente.
Ci sono molte istruzioni dettagliate per installare Postgres su Debian (), quindi non entrerò nei dettagli, ma elencherò i comandi:
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
Considerando che PostgreSQL ha impostazioni di default piuttosto mediocri, è fondamentale rivedere la configurazione. In questo ci aiuterà : inserire i parametri del server e sostituire le impostazioni nel file /etc/postgresql/12/main/postgresql.confcon quelle suggerite. Va detto che tali calcolatori non sono una soluzione magica, e il database dovrebbe essere ottimizzato in modo più specifico, tenendo conto dell'hardware, dell'applicazione e della complessità delle query. Ma per iniziare, è sufficiente.
Oltre alle impostazioni suggerite dal calcolatore, cambiamo nel postgresql.confla porta predefinita 5432 con un'altra (nel nostro esempio — 53983).
Dopo aver modificato il file di configurazione, riavviamo il postgresql-server con il comando:
service postgresql restart
Abbiamo installato e configurato PostgreSQL. Ora creeremo un database, gli utenti per gli ambienti dev e prod e daremo agli utenti i diritti per gestire il database:
$ 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
Procediamo con l'installazione di RabbitMQ, il broker di messaggi per Celery. Si installa facilmente su 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
Dopo l'installazione, dobbiamo creare vhosts, utenti e dare i diritti necessari:
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
Ora installiamo e configuriamo l'ultimo componente per la nostra applicazione: Redis. Sarà utilizzato come backend per memorizzare i risultati delle attività di Celery.
Solleveremo due contenitori Docker con Redis per gli ambienti dev e prod utilizzando l'estensione Docker per Plesk.
- Accediamo a Plesk, andiamo nella sezione Estensioni, cerchiamo l'estensione Docker e la installiamo (abbiamo bisogno della versione gratuita):

- Passiamo all'estensione installata, troviamo tramite ricerca l'immagine
redis bitnamie installiamo l'ultima versione:
- Accediamo al contenitore scaricato e regoliamo la configurazione: indichiamo la porta, la dimensione massima della RAM allocata, la password nelle variabili d'ambiente e montiamo il volume:

- Eseguiamo i passi 2-3 per il contenitore prod, cambiando solo le impostazioni: porta, password, dimensione della RAM e percorso della cartella volume sul server:

Docker Registry
Oltre ai servizi di base, sarebbe utile installare sul server un proprio repository di immagini Docker. Fortunatamente, lo spazio sui server è ora abbastanza economico (certamente più economico di un abbonamento a DockerHub), e il processo di installazione di un repository privato è molto semplice.
Vogliamo avere installati:
- un repository Docker protetto da password, accessibile tramite un sottodominio ;
- Interfaccia UI per visualizzare le immagini nel repository, accessibile all'indirizzo .
Per fare ciò:
- Creiamo in Plesk due sottodomini nel nostro abbonamento: docker.helloworld.ru e docker-ui.helloworld.ru, e configuriamo i certificati Let’s Encrypt per essi.
- Nella cartella del sottodominio docker.helloworld.ru aggiungiamo il file
docker-compose.ymlcon il seguente contenuto: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' - Via SSH generiamo il file .htpasswd per l'autenticazione Basic nel Docker repository:
htpasswd -bBc .htpasswd hw_docker_admin hw_docker_password - Costruiamo e avviamo i contenitori:
docker-compose up -d - E dobbiamo reindirizzare Nginx ai nostri contenitori. Questo può essere fatto tramite Plesk.
Le seguenti operazioni devono essere eseguite per i sottodomini docker.helloworld.ru e docker-ui.helloworld.ru:
Nella sezione Dev Tools del nostro sito entriamo in Regole di Proxy Docker:

E aggiungiamo una regola per il proxy del traffico in entrata al nostro contenitore:

- Controlliamo di poter accedere al nostro container dalla macchina locale:
$ docker login docker.helloworld.ru -u hw_docker_admin -p hw_docker_password ATTENZIONE! Utilizzare --password tramite CLI è insicuro. Utilizza --password-stdin. Accesso riuscito - Verificheremo anche il funzionamento del sottodominio docker-ui.helloworld.ru:

Quando si clicca su Browse repositories, il browser mostrerà una finestra per l'autenticazione, dove sarà necessario inserire il nome utente e la password per il repository. Dopodiché verremo reindirizzati a una pagina con l'elenco dei repository (per ora sarà vuota):
Apriamo le porte nel Plesk Firewall
Dopo aver installato e configurato i componenti, dobbiamo aprire le porte in modo che i componenti siano accessibili dai container Docker e dalla rete esterna.
Vediamo come farlo, usando come esempio l'estensione Firewall che abbiamo installato in precedenza per Plesk.
- Andiamo su Strumenti e impostazioni > Impostazioni > Firewall:

- Andiamo su Modifica le regole del firewall di Plesk > Aggiungi regola personalizzata e apriamo le seguenti porte TCP per la sottorete Docker (172.0.0.0/8):
RabbitMQ: 1883, 4369, 5671-5672, 25672, 61613-61614
Redis: 32785, 32786
- Aggiungeremo anche una regola che aprirà al mondo esterno le porte di PostgreSQL e della pannello di gestione di RabbitMQ:

- Applichiamo le regole utilizzando il pulsante Applica modifiche:

Impostazione di CI/CD in Github Actions
Passiamo alla parte più interessante: la configurazione della pipeline di integrazione continua e distribuzione del nostro progetto sul server.
Questo pipeline sarà composto da due parti:
- la creazione dell'immagine e l'esecuzione dei test (per il backend) — sul lato di Github;
- l'esecuzione delle migrazioni (per il backend) e il deployment dei container — sul server.
Deployment in Plesk
Iniziamo con il secondo punto (poiché dipende dal primo).
Configureremo il processo di deployment usando l'estensione Git per Plesk.
Consideriamo un esempio con l'ambiente Prod per il repository Backend.
- Accediamo alla sottoscrizione del nostro sito Helloworld e passiamo alla sezione Git:

- Inseriamo nel campo «Remote Git repository» il link al nostro repository su Github e cambiamo la cartella predefinita
httpdocsin un'altra (ad esempio,/httpdocs/hw_back):
- Copiare la chiave SSH Public dal passaggio precedente e nelle impostazioni di Github.
- Facciamo clic su OK sullo schermo nel punto 2, dopo di che saremo reindirizzati alla pagina del repository in Plesk. Ora dobbiamo configurare l'aggiornamento del repository al momento dei commit nel ramo master. Per fare ciò, passiamo a Impostazioni del Repository e salviamo il valore
Webhook URL(ci servirà più tardi durante la configurazione delle Github Actions):
- Nel campo Actions sullo schermo del passaggio precedente, inseriamo lo script per avviare il deployment:
cd {REPOSITORY_ABSOLUTE_PATH} .ci-cd/deploy.sh {ENV} {DOCKER_REGISTRY_HOST} {DOCKER_USER} {DOCKER_PASSWORD} {TG_BOT_TOKEN} {TG_CHAT_ID}dove:
{REPOSITORY_ABSOLUTE_PATH}— percorso della cartella del backend del repository prod sul server;
{ENV}— ambiente (dev/prod), nel nostro casoprod;
{DOCKER_REGISTRY_HOST}— host del nostro repository docker
{TG_BOT_TOKEN}— token del bot di Telegram;
{TG_CHAT_ID}— ID della chat/canale per l'invio di notifiche.Esempio di 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 - Aggiungiamo l'utente dal nostro abbonamento al gruppo Docker (affinché possa gestire i container):
sudo usermod -aG docker helloworld_admin
L'ambiente Dev per il repository backend e il frontend sono configurati in modo analogo.
Pipeline di deployment in Github Actions
Passiamo alla configurazione della prima parte del nostro piano CI/CD in Github Actions.
Backend
La pipeline è descritta nel .
Ma prima di analizzarlo, compiliamo in Github le Secret-variable necessarie. A questo scopo, andiamo su Impostazioni -> Segreti:
DOCKER_REGISTRY— host del nostro repository Docker (docker.helloworld.ru);DOCKER_LOGIN— login per il repository Docker;DOCKER_PASSWORD— password per accedervi;DEPLOY_HOST— host dove è disponibile il pannello di amministrazione Plesk (esempio: :8443 oppure :8443);DEPLOY_BACK_PROD_TOKEN— token per il deployment nel repository prod sul server (lo abbiamo ottenuto nella fase di Distribuzione in Plesk p. 4);DEPLOY_BACK_DEV_TOKEN— token per il deployment nel repository dev sul server.
Il processo di deployment è semplice e si compone di tre passaggi principali:
- assemblaggio e pubblicazione dell'immagine nel nostro repository;
- esecuzione dei test all'interno di un container basato sull'immagine appena costruita;
- implementazione nell'ambiente desiderato in base al branch (dev/master).
Frontend
difficilmente si differenzia da quello backend. In esso manca il passo con l'esecuzione dei test e cambiano i nomi dei token per il deployment. I segreti per il repository frontend, tra l'altro, devono essere compilati separatamente.
Configurazione del sito
Proxying del traffico attraverso Nginx
Bene, siamo arrivati alla fine. Resta solo da configurare il proxy del traffico in entrata e in uscita nel nostro container tramite Nginx. Questo processo l'abbiamo già esaminato nel punto 5 della configurazione di Docker Registry. Lo stesso va ripetuto per le parti backend e frontend negli ambienti dev e prod.
Riporterò degli screenshot delle impostazioni.
Backend

Frontend

Importante precisazione. Nel container frontend verranno proxyizzati tutti gli URL, tranne quelli che iniziano con /api/ — questi saranno proxyizzati al container backend (quindi nel container backend tutti i gestori devono iniziare con /api/).
Risultati
Ora il nostro sito dovrebbe essere accessibile agli indirizzi helloworld.ru e dev.helloworld.ru (rispettivamente per gli ambienti prod e dev).
In sintesi, abbiamo appreso come preparare una semplice applicazione su Flask e Angular e configurare un pipeline in Github Actions per il deploy su un server gestito da Plesk.
Ripropongo i link ai repository con il codice: , .
Fonte: habr.com
















