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 з докладним описом структури та інструкціями для запуску проекту.
Головна сторінка з формою для відправки email та кнопкою виходу.
Сторінка входу.
Сторінка реєстрації.
Головна сторінка виглядає аскетично:
У корені лежать два файли Dockerfile и docker-compose.yml, а також знайома нам папка .ci-cd з трохи меншою кількістю скриптів, ніж у беківському репозиторії (прибрано скрипти для запуску тестів).
Заводимо проект у Plesk
Почнемо з налаштування Plesk та створення підписки для нашого сайту.
Встановлення розширень
У Plesk нам знадобляться чотири розширення:
Docker для керування та візуального відображення стану контейнерів в адмінці Plesk;
Git для налаштування кроку деплою на сервері;
Let's Encrypt для генерації (та автоподовження) безкоштовних TLS-сертифікатів;
Firewall для налаштування фільтрації вхідного трафіку.
Встановити їх можна через адмінку Plesk у розділі Extensions:
Детальне налаштування розширень ми не розглядатимемо, для наших демо-цілей підійдуть налаштування за замовчуванням.
Створення підписки та сайту
Далі нам потрібно створити передплату для нашого сайту helloworld.ru і додати туди піддомен dev.helloworld.ru.
Створюємо передплату для домену helloworld.ru та вказуємо логін-пароль для системного користувача:
Внизу сторінки ставимо галочку Secure the domain with Let's Encrypt, якщо хочемо налаштувати HTTPS для сайту:
Далі у цій підписці створюємо піддомен dev.helloworld.ru (для якого також можна випустити безкоштовний TLS-сертифікат):
Встановлення серверних компонентів
У нас є сервер з 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 у мережі повно (приклад), тому докладно описувати їх не буду, просто наведу команди:
Враховуючи, що у 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 досить просто:
Тепер встановимо та налаштуємо останній компонент для нашої програми – Redis. Він використовуватиметься як бекенд для зберігання результатів завдань Celery.
Ми піднімемо два Docker-контейнери з Redis під dev- та prod-середовища за допомогою розширення Docker для Plesk.
Заходимо до Plesk, переходимо до розділу Розширення, шукаємо розширення Docker та встановлюємо його (нам потрібна безкоштовна версія):
Переходимо у встановлене розширення, знаходимо через пошук образ redis bitnami і ставимо останню версію:
Заходимо в завантажений контейнер і коригуємо конфігурацію: вказуємо порт, максимальний розмір ОЗУ, що виділяється, пароль у змінних оточення, і монтуємо том:
Виконуємо кроки 2-3 для prod-контейнера, в налаштуваннях лише змінюємо параметри: порт, пароль, розмір ОЗП та шлях до папки volume на сервері:
Реєстр Docker
Крім базових сервісів було б непогано поставити на сервер свій репозиторій Docker-образів. Благо місце на серверах зараз досить дешеве (точніше дешевше підписки на DockerHub), та й процес встановлення приватного репозиторію дуже простий.
І потрібно перенаправити Nginx на наші контейнери. Це можна зробити через Plesk.
Наступні дії потрібно зробити для піддоменів docker.helloworld.ru та docker-ui.helloworld.ru:
У розділі Інструменти розробки нашого сайту заходимо у Docker Proxy Rules:
І додаємо правило для проксування вхідного трафіку в наш контейнер:
Перевіряємо, що можемо авторизуватись у нашому контейнері з локальної машини:
$ 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
Також перевіримо роботу піддомену docker-ui.helloworld.ru:
При натисканні на Browse repositories браузер видасть віконце для авторизації, куди потрібно буде ввести логін та пароль для репозиторію. Після чого нас перекине на сторінку зі списком репозиторіїв (у вас поки що вона буде порожньою):
Відкриваємо порти у Plesk Firewall
Після встановлення та налаштування компонентів нам потрібно відкрити порти, щоб компоненти були доступні з Docker-контейнерів та зовнішньої мережі.
Подивимося, як це робити, на прикладі встановленого раніше розширення Firewall для Plesk.
Переходимо в Tools & Settings > Settings > Firewall:
Переходимо в 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
Також додамо правило, яке відкриє зовнішньому світу порти PostgreSQL та management-панелі RabbitMQ:
Застосовуємо правила за допомогою кнопки Apply Changes:
Налаштування CI/CD у Github Actions
Приступимо до найцікавішої частини – налаштування пайплайн безперервної інтеграції та доставки нашого проекту до сервера.
Цей пайплайн складатиметься з двох частин:
збирання образу та прогін тестів (для бекенда) - на стороні Github;
запуск міграцій (для бекенда) та деплой контейнерів – на сервері.
Деплой у Plesk
Розберемося спочатку з другим пунктом (оскільки від нього залежить перший).
Процес деплою ми налаштовуватимемо за допомогою розширення Git для Plesk.
Розглянемо приклад із Prod оточенням для Backend репозиторію.
Заходимо у підписку нашого сайту Helloworld і переходимо до підрозділу Git:
Вставляємо в поле «Remote Git repository» посилання на наш Github-репозиторій та змінюємо папку за замовчуванням httpdocs на іншу (наприклад, /httpdocs/hw_back):
Копіюємо SSH Public key з попереднього етапу та додаємо його в налаштуваннях Github.
Натискаємо ОК на екрані в пункті 2, після чого нас перекидає на сторінку репозиторію Plesk. Тепер нам потрібно налаштувати оновлення репозиторію при коммітах у гілку master. Для цього переходимо в Налаштування сховища і зберігаємо значення Webhook URL (воно нам знадобиться пізніше при налаштуванні Github Actions):
У полі 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
Додаємо користувача з нашої підписки до групи Docker (щоб він міг керувати контейнерами):
sudo usermod -aG docker helloworld_admin
Dev-середовище для backend-репозиторію та frontend настроюються аналогічно.
Pipeline деплою в Github Actions
Переходимо до налаштування першої частини нашого CI/CD-пайплана у Github Actions.
Але перед його розбором заповнимо 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
Frontend
важливе уточнення. У фронтенд-контейнер будуть проксуватися всі URL, крім початківців на /api/ — вони будуть проксовані до бек-контейнера (тому у бек-контейнері всі обробники повинні починатися з /api/).
Підсумки
Тепер наш сайт має бути доступний за адресами helloworld.ru та dev.helloworld.ru (prod- та dev-оточення відповідно).
Разом, ми дізналися, як підготувати простий додаток на Flask та Angular і налаштувати в Github Actions пайплайн для його викочування на сервер під керуванням Plesk.
Продублюю посилання на репозиторії з кодом: бекенд, фронтенд.