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 використовується SQLAchemy. Для міграцій застосовується Alembic. Для валідації JSON у ручках – Marshmallow.

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

API веб-частини досить нехитрий, складається з 6 ручок:

  • /ping - Для перевірки доступності;
  • ручки для реєстрації, авторизації, деавторизації та отримання авторизованого користувача;
  • ручка для відправки email, яка кладе завдання в чергу Celery.

Celery-частина набагато простіше, там всього одне завдання send_mail_task.

В папці /конф лежать дві підпапки:

  • 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

Після встановлення нам потрібно створити віртуальних доменів, користувачів та видати потрібні права:

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. Заходимо в завантажений контейнер і коригуємо конфігурацію: вказуємо порт, максимальний розмір ОЗУ, що виділяється, пароль у змінних оточення, і монтуємо том:

    CI/CD у Github Actions для проекту на Flask+Angular

  4. Виконуємо кроки 2-3 для prod-контейнера, в налаштуваннях лише змінюємо параметри: порт, пароль, розмір ОЗП та шлях до папки volume на сервері:

    CI/CD у Github Actions для проекту на Flask+Angular

Реєстр Docker

Крім базових сервісів було б непогано поставити на сервер свій репозиторій Docker-образів. Благо місце на серверах зараз досить дешеве (точніше дешевше підписки на DockerHub), та й процес встановлення приватного репозиторію дуже простий.

Ми хочемо, щоб у нас було встановлено:

  • захищений паролем Docker-репозиторій, доступний за піддоменом https://docker.helloworld.ru;
  • UI-інтерфейс для перегляду образів у репозиторії, доступний за адресою https://docker-ui.helloworld.ru.

Для цього:

  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:

У розділі Інструменти розробки нашого сайту заходимо у 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. Переходимо в Tools & Settings > Settings > Firewall:
    CI/CD у Github Actions для проекту на Flask+Angular
  2. Переходимо в Modify Plesk Firewall Rules > Add Custom Rule і відкриваємо наступні 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 та management-панелі RabbitMQ:

    CI/CD у Github Actions для проекту на Flask+Angular

  4. Застосовуємо правила за допомогою кнопки Apply Changes:

    CI/CD у Github Actions для проекту на Flask+Angular

Налаштування CI/CD у Github Actions

Приступимо до найцікавішої частини – налаштування пайплайн безперервної інтеграції та доставки нашого проекту до сервера.

Цей пайплайн складатиметься з двох частин:

  • збирання образу та прогін тестів (для бекенда) - на стороні Github;
  • запуск міграцій (для бекенда) та деплой контейнерів – на сервері.

Деплой у 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} — 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.

Але перед його розбором заповнимо Github потрібні нам Secret-змінні. Для цього переходимо в 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

Додати коментар або відгук