CI/CD în Github Actions pentru un proiect pe Flask+Angular

CI/CD în Github Actions pentru un proiect pe Flask+Angular
În acest articol, voi împărtăși experiența mea de configurare a CI/CD folosind panoul de control Plesk și Github Actions. Astăzi vom învăța să desfășurăm un proiect simplu cu un nume nepretențios, „Helloworld”. Este scris pe cadrul Python Flask, cu lucrători pe Celery și frontend pe Angular 8.

Linkuri către repozitoriile: backend, frontend.

În prima parte a articolului, ne vom uita la proiectul nostru și la componentele acestuia. În a doua parte, vom analiza cum să configurăm Plesk și să instalăm extensiile și componentele necesare (Baza de date, RabbitMQ, Redis, Docker etc.).

În a treia parte, finalmente, vom învăța cum să configurăm un pipeline pentru desfășurarea proiectului nostru pe server în mediile dev și prod. Apoi, vom lansa site-ul pe server.

Și da, am uitat să mă prezint. Mă numesc Oleg Borzov, sunt dezvoltator fullstack în echipa CRM pentru managerii de credite ipotecare la Domklik.

Prezentarea proiectului

La început, să examinăm cele două repozitorii ale proiectului — backend-ul și frontend-ul — și să trecem în revistă codul.

Partea de backend: Flask+Celery

Pentru partea de backend am ales o combinație destul de populară în rândul dezvoltatorilor Python: framework-ul Flask (pentru API) și Celery (pentru coada de sarcini). Ca ORM, se folosește SQLAchemy. Pentru migrații se utilizează Alembic. Pentru validarea JSON în endpoint-uri — Marshmallow.

În repository există un fișier Readme.md cu descriere detaliată a structurii și instrucțiuni pentru lansarea proiectului.

API-ul părții web este destul de simplu, constând în 6 endpoint-uri:

  • /ping — pentru verificarea disponibilității;
  • endpoint-uri pentru înregistrare, autentificare, deautentificare și obținerea utilizatorului autorizat;
  • un endpoint pentru trimiterea email-ului, care adaugă o sarcină în coada Celery.

Partea Celery este și mai simplă, acolo există doar o sarcină send_mail_task.

În folderul /conf există două subfoldere:

  • docker cu două fișiere Docker (base.dockerfile pentru construirea unei imagini de bază care se schimbă rar și Dockerfile pentru construcțiile principale);
  • .env_files — cu fișiere care conțin variabile de mediu pentru diferite medii.

În rădăcina proiectului se află patru fișiere docker-compose:

  • docker-compose.local.db.yml pentru a ridica o Bază de date locală pentru dezvoltare;
  • docker-compose.local.workers.yml pentru ridicarea locală a unui lucrător, a Bazei de date, Redis și RabbitMQ;
  • docker-compose.test.yml pentru rularea testelor la desfășurare;
  • docker-compose.yml pentru desfășurare.

Și ultimul folder interesant pentru noi — .ci-cd. În el se află scripturi shell pentru desfășurare:

  • deploy.sh — rularea migrațiilor și a desfășurării. Se lansează pe server după construcție și rularea testelor în Github Actions;
  • rollback.sh — revenirea containerelor la versiunea anterioară a construcției;
  • curl_tg.sh — trimiterea de notificări despre desfășurare în Telegram.

Frontend-ul pe Angular

Repository with the front end is significantly simpler than the back end. The front consists of three pages:

  • Home page with an email submission form and a logout button.
  • Login page.
  • Registration page.

The home page looks minimalist:

CI/CD în Github Actions pentru un proiect pe Flask+Angular
There are two files at the root Dockerfile și docker-compose.yml, as well as a familiar folder .ci-cd with slightly fewer scripts than in the back end repository (scripts for running tests have been removed).

Starting a project in Plesk

Let's begin with configuring Plesk and creating a subscription for our site.

Installing extensions

In Plesk, we will need four extensions:

  • Docker for managing and visually displaying the state of containers in the Plesk admin panel;
  • Git for configuring the deployment step on the server;
  • Let's Encrypt for generating (and auto-renewing) free TLS certificates;
  • Firewall for setting up incoming traffic filtering.

They can be installed through the Plesk admin panel in the Extensions section:

CI/CD în Github Actions pentru un proiect pe Flask+Angular
We will not consider the detailed configuration of the extensions, the default settings will suffice for our demo purposes.

Creating a subscription and site

Next, we need to create a subscription for our site helloworld.ru and add the subdomain dev.helloworld.ru.

  1. We create a subscription for the domain helloworld.ru and specify the username-password for the system user:

    CI/CD în Github Actions pentru un proiect pe Flask+Angular
    At the bottom of the page, we check the box Secure the domain with Let’s Encrypt, if we want to configure HTTPS for the site:

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

  2. Next, in this subscription, we create a subdomain dev.helloworld.ru (for which a free TLS certificate can also be issued):

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

Installing server components

We have a server with OS Debian Stretch 9.12 and the control panel installed Plesk Obsidian 18.0.27.

We need to install and configure for our project:

  • PostgreSQL (in our case, there will be one server with two databases for dev and prod environments).
  • RabbitMQ (the same, one instance with different vhosts for environments).
  • Two instances of Redis (for dev and prod environments).
  • Docker Registry (for local storage of collected Docker images).
  • UI interface for Docker registry.

PostgreSQL

Plesk already includes a PostgreSQL DBMS, but not the latest version (at the time of this writing, Plesk Obsidian supported Postgres versions 8.4–10.8). We want the latest version (12.3 at the time of writing), so we will install it manually.

There are plenty of detailed instructions for installing Postgres on Debian available online (exemplu), so I won't describe them in detail, just provide the commands:

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

Având în vedere că PostgreSQL are setări de bază destul de medii, este esențial să corectăm configurația. În acest sens, ne va ajuta un calculator: este necesar să introducem parametrii serverului nostru și să înlocuim setările din fișierul /etc/postgresql/12/main/postgresql.confcu cele sugerate. Aici trebuie menționat că astfel de calculatoare nu sunt o soluție magică, iar baza ar trebui optimizată mai precis, în funcție de hardware-ul, aplicația și complexitatea interogărilor. Dar pentru început, aceasta este suficient.

Pe lângă setările sugerate de calculator, modificăm și postgresql.confportul implicit 5432 cu altul (în exemplul nostru — 53983).

După modificarea fișierului de configurație, repornim serverul postgresql cu comanda:

service postgresql restart

Am instalat și configurat PostgreSQL. Acum să creăm baza de date, utilizatorii pentru medii dev- și prod- și să le oferim utilizatorilor drepturi de gestionare a bazei de date:

$ 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

Să trecem la instalarea RabbitMQ — broker de mesaje pentru Celery. Se instalează destul de simplu pe 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

După instalare, trebuie să creăm vhosts, utilizatori și să oferim drepturile necesare:

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

Acum vom instala și configura ultimul component pentru aplicația noastră — Redis. Acesta va fi folosit ca backend pentru stocarea rezultatelor sarcinilor Celery.

Vom ridica două containere Docker cu Redis pentru medii dev- și prod- folosind extensia Docker pentru Plesk.

  1. Accesați Plesk, mergeți la secțiunea Extensii, căutați extensia Docker și instalați-o (ne trebuie versiunea gratuită):

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

  2. Mergeți la extensia instalată, căutați imaginea redis bitnami și instalați ultima versiune:

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

  3. Accesați containerul descărcat și ajustați configurația: specificați portul, dimensiunea maximă alocată a RAM-ului, parola în variabilele de mediu și montați volumul:

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

  4. Repetați pașii 2-3 pentru containerul prod, doar schimbați parametrii: port, parolă, dimensiune RAM și calea către folderul volum pe server:

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

Docker Registry

Pe lângă serviciile de bază, ar fi bine să instalăm pe server un depozit propriu de imagini Docker. Și, având în vedere că spațiul pe servere este destul de ieftin (cu siguranță mai ieftin decât abonamentul pe DockerHub), iar procesul de instalare a unui depozit privat este foarte simplu.

Vrem să avem instalat:

Pentru aceasta:

  1. Vom crea în Plesk două subdomenii în abonamentul nostru: docker.helloworld.ro și docker-ui.helloworld.ro, și le vom configura certificate Let’s Encrypt.
  2. În folderul subdomeniului docker.helloworld.ro vom adăuga fișierul docker-compose.yml cu următorul conținut:
    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. Prin SSH, vom genera fișierul .htpasswd pentru autentificarea Basic în depozitul Docker:
    htpasswd -bBc .htpasswd hw_docker_admin hw_docker_password
  4. Vom construi și ridica containerele:
    docker-compose up -d
  5. Și trebuie să redirecționăm Nginx către containerele noastre. Acest lucru se poate face prin Plesk.

Următoarele acțiuni trebuie realizate pentru subdomeniile docker.helloworld.ro și docker-ui.helloworld.ro:

În secțiune Dev Tools în site-ul nostru, accesați Docker Proxy Rules:

CI/CD în Github Actions pentru un proiect pe Flask+Angular
Și adăugați o regulă pentru a face proxy traficului de intrare către containerul nostru:

CI/CD în Github Actions pentru un proiect pe Flask+Angular

  1. Verificați dacă ne putem autentifica în containerul nostru de pe mașina locală:
    $ docker login docker.helloworld.ro -u hw_docker_admin -p hw_docker_password
    WARNING! Using --password via the CLI is insecure. Use --password-stdin.
    Login Succeeded
  2. De asemenea, să verificăm funcționarea subdomeniului docker-ui.helloworld.ro:

    CI/CD în Github Actions pentru un proiect pe Flask+Angular
    Când faceți clic pe Browse repositories, browserul va deschide o fereastră pentru autentificare, unde va trebui să introduceți numele de utilizator și parola pentru repository. Apoi, veți fi redirecționat pe pagina cu lista de repository-uri (în prezent aceasta va fi goală):

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

Deschidem porturile în Plesk Firewall

După instalarea și configurarea componentelor, trebuie să deschidem porturile pentru ca acestea să fie accesibile din containerele Docker și din rețeaua externă.

Să vedem cum se face acest lucru, folosind ca exemplu extensia Firewall instalată anterior pentru Plesk.

  1. Trecem la Tools & Settings > Settings > Firewall:
    CI/CD în Github Actions pentru un proiect pe Flask+Angular
  2. Trecem la Modificați regulile Plesk Firewall > Adăugați regulă personalizată și deschidem următoarele porturi TCP pentru subnetul Docker (172.0.0.0/8):
    RabbitMQ: 1883, 4369, 5671-5672, 25672, 61613-61614
    Redis: 32785, 32786

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

  3. De asemenea, vom adăuga o regulă care va deschide porturile PostgreSQL și panoul de management RabbitMQ pentru lumea externă:

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

  4. Aplicăm regulile folosind butonul Aplicați modificaările:

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

Configurarea CI/CD în Github Actions

Să trecem la cea mai interesantă parte — configurarea pipeline-ului pentru integrarea și livrarea continuă a proiectului nostru pe server.

Acest pipeline va consta din două părți:

  • construcția imaginii și rularea testelor (pentru backend) — pe partea Github;
  • rularea migrărilor (pentru backend) și implementarea containerelor — pe server.

Implementarea în Plesk

Vom începe cu a doua parte (deoarece prima depinde de aceasta).

Procesul de implementare îl vom configura folosind extensia Git pentru Plesk.

Să luăm în considerare un exemplu cu mediul Prod pentru repository-ul Backend.

  1. Accesăm abonamentul site-ului nostru Helloworld și trecem la subsecțiunea Git:

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

  2. Introducem în câmpul „Remote Git repository” linkul nostru către repository-ul Github și schimbăm folderul implicit httpdocs într-un altul (de exemplu, /httpdocs/hw_back):

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

  3. Copiem cheia publică SSH de la etapa anterioară și adăugăm aceasta în setările Github.
  4. Facem clic pe OK pe ecranul din punctul 2, după care vom fi redirecționați pe pagina repository-ului din Plesk. Acum trebuie să configurăm actualizarea repository-ului la comiterea în ramura master. Pentru aceasta, mergem la Setările repository-ului și salvăm valoarea Webhook URL (ne va fi necesară mai târziu la configurarea Github Actions):

    CI/CD în Github Actions pentru un proiect pe Flask+Angular

  5. În câmpul Actions pe ecranul din punctul anterior, introducem scriptul pentru a iniția implementarea:
    cd {REPOSITORY_ABSOLUTE_PATH}
    .ci-cd/deploy.sh {ENV} {DOCKER_REGISTRY_HOST} {DOCKER_USER} {DOCKER_PASSWORD} {TG_BOT_TOKEN} {TG_CHAT_ID} 

    unde:

    {REPOSITORY_ABSOLUTE_PATH} — calea către folderul prod al repository-ului backend pe server;
    {ENV} — mediu (dev/prod), în cazul nostru prod;
    {DOCKER_REGISTRY_HOST} — hostul repository-ului nostru docker
    {TG_BOT_TOKEN} — token-ul botului Telegram;
    {TG_CHAT_ID} — ID-ul chat-ului/canalului pentru trimiterea notificărilor.

    Exemplu 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. Adăugăm utilizatorul din abonamentul nostru în grupul Docker (pentru a putea gestiona containerele):
    sudo usermod -aG docker helloworld_admin

Mediul de dezvoltare pentru repo-ul backend și frontend este configurat similar.

Pipeline-ul de deploy în Github Actions

Trecem la configurarea primei părți a pipeline-ului nostru CI/CD în Github Actions.

Backend

Pipeline-ul este descris în fișierul deploy.yml.

Dar înainte de a-l analiza, să completăm variabilele Secret de care avem nevoie în Github. Pentru aceasta, mergem la Settings -> Secrets:

  • DOCKER_REGISTRY — gazda repo-ului nostru Docker (docker.helloworld.ru);
  • DOCKER_LOGIN — utilizatorul pentru repo-ul Docker;
  • DOCKER_PASSWORD — parola pentru acesta;
  • DEPLOY_HOST — gazda pe care este disponibilă interfața de administrare Plesk (exemplu: helloworld.ru:8443 sau 123.4.56.78:8443);
  • DEPLOY_BACK_PROD_TOKEN — token-ul pentru deploy în repo-ul prod pe server (l-am obținut în Etapa de desfășurare în Plesk, punctul 4);
  • DEPLOY_BACK_DEV_TOKEN — token-ul pentru deploy în repo-ul dev pe server.

Procesul de deploy este simplu și constă în trei pași principali:

  • construirea și publicarea imaginii în repo-ul nostru;
  • rularea testelor într-un container pe baza imaginii recent construite;
  • desfășurarea în mediul necesar în funcție de ramură (dev/master).

Frontend

Fișierul deploy.yml pentru repo-ul frontend nu se deosebește prea mult de cel backend. Acesta nu conține pașii pentru rularea testelor și se schimbă denumirile token-urilor pentru deploy. Secretele pentru repo-ul frontend, de altfel, trebuie completate separat.

Configurarea site-ului

Proxierea traficului prin Nginx

Ei bine, am ajuns la final. Rămâne doar să configurăm proxierea traficului de intrare și ieșire în containerul nostru prin Nginx. Acest proces l-am abordat deja în punctul 5 privind configurarea Docker Registry. Același lucru trebuie repetat pentru partea de backend și frontend în mediile dev și prod.

Voi prezenta capturi de ecran ale setărilor.

Backend

CI/CD în Github Actions pentru un proiect pe Flask+Angular

Frontend

CI/CD în Github Actions pentru un proiect pe Flask+Angular
O clarificare importantă. În containerul frontend vor fi proxiate toate URL-urile, cu excepția celor care încep cu /api/ — acestea vor fi proxiate în containerul backend (de aceea în containerul backend, toate handler-ele trebuie să înceapă cu /api/).

Concluzii

Acum site-ul nostru ar trebui să fie accesibil pe adresele helloworld.ru și dev.helloworld.ru (mediile prod și dev, respectiv).

În concluzie, am învățat cum să pregătim o aplicație simplă pe Flask și Angular și să configurăm un pipeline în Github Actions pentru desfășurarea acesteia pe un server gestionat de Plesk.

Voi repeta linkurile către repo-urile cu cod: backend, frontend.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster