CI/CD в Github Actions за проект с Flask + Angular

CI/CD в Github Actions за проект с Flask + Angular
В тази статия ще споделя опита си в настройването на CI/CD с помощта на Plesk и Github Actions. Днес ще научим как да деплоираме прост проект с дяволски просто име „Helloworld“. Той е написан на Python фреймворка Flask, с работници на Celery и фронтенд на Angular 8.

Връзки към репозитории: бекенд, фронтенд.

В първата част на статията ще разгледаме проекта и неговите части. Във втората — ще разберем как да настроим Plesk и да инсталираме необходимите разширения и компоненти (БД, RabbitMQ, Redis, Docker и т.н.).

В третата част, най-накрая, ще разберем как да настроим пайплан за деплой на нашия проект на сървър в dev и prod среда. А след това ще стартираме сайта на сървъра.

И да, забравих да се представя. Казвам се Олег Борзов, аз съм fullstack разработчик в екипа за CRM за мениджъри на ипотечни кредити в Домклик.

Преглед на проекта

Първо, нека разгледаме двата репозитория на проекта — бекенда и фронта — и да прегледаме кода.

Бекенд част: Flask + Celery

За бек частта избрах достатъчно популярен сред разработчиците на Python комплект: фреймворк Flask (за API) и Celery (за опашка от задачи). Като ORM се използва SQLAlchemy. За миграции се прилага Alembic. За валидиране на JSON в ръцете — Marshmallow.

В репозитории има файл Readme.md с подробни описания на структурата и инструкции за стартиране на проекта.

API на уеб частта достатъчно прост, състои се от 6 ръце:

  • /ping — за проверка на наличността;
  • ръце за регистрация, авторизация, деавторизация и получаване на авторизиран потребител;
  • ръка за изпращане на имейл, която поставя задача в опашката на Celery.

Частта на Celery е още по-проста, там има само една задача send_mail_task.

В папката /conf има две подпапки:

  • docker с два Docker файла (base.dockerfile за изграждане на рядко променящия се базов образ и Dockerfile за основните изграждания);
  • .env_files — с файлове с променливи на околната среда за различни среди.

В корена на проекта се намират четири файла docker-compose:

  • docker-compose.local.db.yml за стартиране на локална БД за разработка;
  • docker-compose.local.workers.yml за локално стартиране на работник, БД, Redis и RabbitMQ;
  • docker-compose.test.yml за изпълнение на тестове при разгръщане;
  • docker-compose.yml за деплой.

И последната интересна папка за нас — .ci-cd. В нея се намират shell скриптове за разгръщане:

  • deploy.sh — стартиране на миграции и деплой. Стартира се на сървъра след изграждане и изпълнение на тестове в Github Actions;
  • rollback.sh — връщане на контейнери към предишна версия на сборката;
  • curl_tg.sh — изпращане на уведомления за разгръщане в Telegram.

Фронтенд на Angular

Хранилище с фронта значително по-просто от бекенда. Фронтът се състои от три страници:

  • Главната страница с форма за изпращане на email и бутон за изход.
  • Страница за вход.
  • Страница за регистрация.

Главната страница изглежда аскетично:

CI/CD в Github Actions за проект с Flask + Angular
В корена се намират два файла Dockerfile и docker-compose.yml, както и познатата ни папка .ci-cd с малко по-малко скриптове в сравнение с бекенд хранилището (премахнати са скриптовете за стартиране на тестовете).

Създаваме проект в Plesk

Да започнем с настройката на Plesk и създаването на абонамент за нашия сайт.

Инсталиране на разширения

В Plesk ще ни трябват четири разширения:

  • Docker за управление и визуално отразяване на състоянието на контейнерите в административния панел на Plesk;
  • Git за настройка на стъпката за разгръщане на сървера;
  • Let's Encrypt за генериране (и автоматично подновяване) на безплатни TLS сертификати;
  • Firewall за настройка на филтриране на входящия трафик.

Може да ги инсталираме чрез административния панел на Plesk в раздел Extensions:

CI/CD в Github Actions за проект с Flask + Angular
Не ще разглеждаме детайлната настройка на разширенията, настройките по подразбиране ще са достатъчни за нашите демонстрационни цели.

Създаване на абонамент и сайт

След това трябва да създадем абонамент за нашия сайт helloworld.ru и да добавим поддомейн dev.helloworld.ru.

  1. Създаваме абонамент за домейна helloworld.ru и указваме потребителско име и парола за системния потребител:

    CI/CD в Github Actions за проект с Flask + Angular
    В долната част на страницата поставяме отметка Secure the domain with Let’s Encrypt, ако искаме да настроим HTTPS за сайта:

    CI/CD в Github Actions за проект с Flask + Angular

  2. След това в този абонамент създаваме поддомейн dev.helloworld.ru (за който също може да се издаде безплатен TLS сертификат):

    CI/CD в Github Actions за проект с Flask + Angular

Инсталиране на сървърни компоненти

Разполагаме със сървър с OS Debian Stretch 9.12 и инсталирана управляваща панел Plesk Obsidian 18.0.27.

Трябва да инсталираме и настроим за нашия проект:

  • PostgreSQL (в нашия случай ще имаме един сървър с две БД за dev и prod среда).
  • RabbitMQ (същото, един инстанс с различни vhosts за средите).
  • Два инстанса Redis (за dev и prod среда).
  • Docker Registry (за локално съхранение на събраните Docker изображения).
  • UI интерфейс за Docker registry.

PostgreSQL

В комплект с Plesk идва база данни PostgreSQL, но не и най-новата версия (към момента на написване на статията Plesk Obsidian поддържаше Postgres версии 8.4–10.8). Ние обаче искаме за нашето приложение най-новата версия (12.3 към момента на написване на статията), затова ще я инсталираме ръчно.

В мрежата има много подробни инструкции за инсталиране на Postgres на Debian (пример), затова няма да ги описвам подробно, просто ще дам командите:

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

Като се има предвид, че PostgreSQL предлага доста посредствени настройки по подразбиране, е необходимо задължително да коригираме конфигурацията. Тук ни помага калькулятор: нужно да въведем параметрите на сървъра си и да заменим настройките в файла /etc/postgresql/12/main/postgresql.confс предложените. Тук трябва да уточня, че подобни калкулатори не са вълшебна пръчка и базата трябва да бъде настройвана по-точно, в зависимост от вашето оборудване, приложение и сложността на запитванията. Но за старт това е повече от достатъчно.

Освен предложените от калкулатора настройки, също така променяме в postgresql.confпо подразбиране зададения порт 5432 на друг (в нашия случай — 53983).

След промяна на конфигурационния файл рестартираме postgresql-server с командата:

service postgresql restart

Ние инсталирахме и настроихме PostgreSQL. Сега ще създадем БД, потребители за dev и prod среди и ще предоставим права на потребителите за управление на БД:

$ 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

Преминаваме към инсталирането на RabbitMQ — брокер за съобщения за Celery. Инсталира се доста лесно на 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

След инсталирането трябва да създадем vhosts, потребители и да предоставим нужните права:

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

Сега ще инсталираме и настроим последния компонент за нашето приложение — Redis. Той ще се използва като бекенд за съхранение на резултатите от задачите на Celery.

Ние ще стартираме два Docker контейнери с Redis за dev и prod среди с помощта на разширение Docker за Plesk.

  1. Влизаме в Plesk, преминаваме в секция Разширения, търсим разширението Docker и го инсталираме (нуждаем се от безплатната версия):

    CI/CD в Github Actions за проект с Flask + Angular

  2. Преминаваме в инсталираното разширение, намираме образа чрез търсене redis bitnami и поставяме последната версия:

    CI/CD в Github Actions за проект с Flask + Angular

  3. Влизаме в изтегления контейнер и коригираме конфигурацията: посочваме порт, максимален размер на RAM, парола в променливите на средата и монтираме обем:

    CI/CD в Github Actions за проект с Flask + Angular

  4. изпълняваме стъпки 2-3 за prod контейнера, само в настройките променяме параметрите: порт, парола, размер на RAM и пътя до папката volume на сървера:

    CI/CD в Github Actions за проект с Flask + Angular

Docker Registry

Освен основните услуги, би било добре да инсталираме на сървера собствен хранилище на Docker образи. Ще кажем, че пространството на сървърите в момента е доста евтино (определено поевтино от абонамента за DockerHub), а процесът на инсталиране на частен хранилище е много прост.

Искаме да имаме инсталирано:

За целта:

  1. Ще създадем в Plesk два поддомена в нашия абонамент: docker.helloworld.ru и docker-ui.helloworld.ru и ще настроим сертификати Let’s Encrypt за тях.
  2. В папката на поддомена docker.helloworld.ru добавяме файл docker-compose.yml с такова съдържание:
    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. Под SSH генерираме файл .htpasswd за Basic авторизация в Docker хранилището:
    htpasswd -bBc .htpasswd hw_docker_admin hw_docker_password
  4. Събираме и стартираме контейнерите:
    docker-compose up -d
  5. И трябва да пренасочим Nginx към нашите контейнери. Това може да се направи през Plesk.

Следните действия трябва да се извършат за поддомените docker.helloworld.ru и docker-ui.helloworld.ru:

В секцията Dev Tools на нашия сайт влизаме в Docker Proxy Rules:

CI/CD в Github Actions за проект с Flask + Angular
И добавяме правило за проксиране на входящия трафик към нашия контейнер:

CI/CD в Github Actions за проект с Flask + Angular

  1. Проверяваме дали можем да се авторизираме в нашия контейнер от локалната машина:
    $ 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. Също така ще проверим работата на поддомейна docker-ui.helloworld.ru:

    CI/CD в Github Actions за проект с Flask + Angular
    При натискане на Browse repositories, браузърът ще изведе прозорец за удостоверяване, където трябва да въведете потребителското си име и паролата за репозитория. След това ще бъдем пренасочени към страницата със списъка на репозиториите (понастоящем тя ще е празна):

    CI/CD в Github Actions за проект с Flask + Angular

Отваряме портовете в Plesk Firewall

След инсталирането и настройката на компонентите, трябва да отворим портовете, за да са достъпни компонентите от Docker контейнерите и външната мрежа.

Нека видим как да го направим, на примера на инсталираното от нас предишно разширение Firewall за Plesk.

  1. Преминаваме към Инструменти & Настройки > Настройки > Защитна стена:
    CI/CD в Github Actions за проект с Flask + Angular
  2. Преминаваме към Промяна на правилата на Plesk Firewall > Добавяне на персонализирано правило и отваряме следните TCP портове за подмрежата на Docker (172.0.0.0/8):
    RabbitMQ: 1883, 4369, 5671-5672, 25672, 61613-61614
    Redis: 32785, 32786

    CI/CD в Github Actions за проект с Flask + Angular

  3. Също така, ще добавим правило, което ще отвори портовете на PostgreSQL и управленския панел на RabbitMQ за външния свят:

    CI/CD в Github Actions за проект с Flask + Angular

  4. Прилагаме правилата с бутона Apply Changes:

    CI/CD в Github Actions за проект с Flask + Angular

Настройка на CI/CD в Github Actions

Нека преминем към най-интригуващата част — настройка на pipeline за непрекъсната интеграция и доставка на нашия проект до сървъра.

Този pipeline ще се състои от две части:

  • сборка на образа и изпълнение на тестовете (за backend) — от страната на Github;
  • стартиране на миграции (за backend) и деплой на контейнерите — на сървъра.

Деплой в Plesk

Нека първо се занимаем с втория пункт (тъй като от него зависи първият).

Процесът на деплой ще настроим с помощта на разширението Git за Plesk.

Нека разгледаме пример с Prod среда за Backend репозитория.

  1. Влизаме в абонамента на нашия сайт Helloworld и преминаваме в подраздела Git:

    CI/CD в Github Actions за проект с Flask + Angular

  2. Въвеждаме в полето «Remote Git repository» връзка към нашия Github репозиторий и променяме папката по подразбиране httpdocs на друга (например, /httpdocs/hw_back):

    CI/CD в Github Actions за проект с Flask + Angular

  3. Копираме SSH Public key от предишния етап и добавяме го в настройките на Github.
  4. Натискаме ОК на екрана в пункт 2, след което ще бъдем пренасочени към страницата на репозитория в Plesk. Сега трябва да настроим актуализирането на репозитория при комити в ветка master. За целта преминаваме в Настройки на репозитория и запазваме стойността Webhook URL (тя ще ни бъде нужна по-късно при настройката на Github Actions):

    CI/CD в Github Actions за проект с Flask + Angular

  5. В полето Actions на екрана от предишната точка въвеждаме скрипта за стартиране на деплоя:
    cd {REPOSITORY_ABSOLUTE_PATH}
    .ci-cd/deploy.sh {ENV} {DOCKER_REGISTRY_HOST} {DOCKER_USER} {DOCKER_PASSWORD} {TG_BOT_TOKEN} {TG_CHAT_ID} 

    където:

    {REPOSITORY_ABSOLUTE_PATH} — пътят към папката prod backend репозиторий на сървера;
    {ENV} — среда (dev/prod), в нашия случай prod;
    {DOCKER_REGISTRY_HOST} — хост на нашия docker репозиторий
    {TG_BOT_TOKEN} — токен на Telegram бота;
    {TG_CHAT_ID} — ИД на чата/канала за изпращане на известия.

    Примерен скрипт:

    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. Добавяме потребителя от нашата абонаментна услуга в групата Docker (за да може да управлява контейнерите):
    sudo usermod -aG docker helloworld_admin

Dev-среда за backend-репозитория и frontend се настройват по аналогичен начин.

Pipeline за деплой в Github Actions

Преминаваме към настройването на първата част от нашия CI/CD пайплайн в Github Actions.

Backend

Пайплайнът е описан в файл deploy.yml.

Но преди да преминем към него, нека да попълним нужните Secret-переменные в Github. За целта, преминаваме в Settings -> Secrets:

  • DOCKER_REGISTRY — хост на нашия Docker-репозитория (docker.helloworld.ru);
  • DOCKER_LOGIN — логин за Docker-репозитория;
  • DOCKER_PASSWORD — парола за него;
  • DEPLOY_HOST — хост, на който е налична админката Plesk (пример: helloworld.ru:8443 или 123.4.56.78:8443);
  • DEPLOY_BACK_PROD_TOKEN — токен за деплой в prod-репозитория на сървера (получихме го в Развъртыване в Plesk п. 4);
  • DEPLOY_BACK_DEV_TOKEN — токен за деплой в dev-репозитория на сървера.

Процесът на деплой е прост и се състои от три основни стъпки:

  • сборка и публикуване на образа в нашия репозиторий;
  • стартиране на тестове в контейнер на базата на новосъздадения образ;
  • развъртане в необходимата среда в зависимост от клона (dev/master).

Frontend

Файлът deploy.yml за фронт-репозитория не се различава много от бековия. В него липсва стъпката с пускането на тестове и се променят имената на токените за деплой. Секретите за фронт-репозитория, между другото, трябва да се попълват отделно.

Настройка на сайта

Проксиране на трафика през Nginx

Е, стигнахме до края. Остава само да настроим проксирането на входящия и изходящия трафик в нашия контейнер през Nginx. Този процес вече разгледахме в точка 5 от настройването на Docker Registry. Същото трябва да повторим и за бек- и фронт-частта в dev- и prod-окруженията.

Ще покажа екрани на настройките.

Backend

CI/CD в Github Actions за проект с Flask + Angular

Frontend

CI/CD в Github Actions за проект с Flask + Angular
Важно уточнение. В фронтенд-контейнера ще се проксирият всички URL, освен тези, започващи с /api/ — те ще се проксирият в бек-контейнера (поради което в бек-контейнера всички обработвачи трябва да започват с /api/).

Итог

Сега нашият сайт трябва да бъде достъпен на адресите helloworld.ru и dev.helloworld.ru (prod- и dev-окружение съответно).

В крайна сметка, научихме как да подготвим простото приложение на Flask и Angular и да настроим пайплайн в Github Actions за неговото разполагане на сървера, управляван от Plesk.

Повтарям връзките към репозиториите с кода: бекенд, фронтенд.

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster