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.
Главная страница с формой для отправки 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 Registry
Помимо базовых сервисов было бы неплохо поставить на сервер собственный репозиторий Docker-образов. Благо место на серверах сейчас достаточно дешевое (уж точно дешевле подписки на DockerHub), да и процесс установки приватного репозитория очень прост.
И нужно перенаправить Nginx на наши контейнеры. Это можно сделать через Plesk.
Следующие действия нужно проделать для поддоменов docker.helloworld.ru и docker-ui.helloworld.ru:
В разделе Dev Tools нашего сайта заходим в 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. Для этого переходим в Repository Settings и сохраняем значение 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.
Продублирую сссылки на репозитории с кодом: бэкенд, фронтенд.