
In this article, I will share my experience of setting up CI/CD using the Plesk control panel and Github Actions. Today we will learn how to deploy a simple project with the straightforward name "Helloworld." It is written in the Python framework Flask, with workers using Celery and a frontend built with Angular 8.
Links to repositories: , .
In the first part of the article, we will look at our project and its components. In the second part, we will figure out how to set up Plesk and install the necessary extensions and components (DB, RabbitMQ, Redis, Docker, etc.).
In the third part, we will finally figure out how to set up a pipeline to deploy our project on the server in both dev and prod environments. Then we will launch the site on the server.
Oh, and I forgot to introduce myself. My name is Oleg Borzov, and I am a full-stack developer on the CRM team for mortgage loan managers at Domclick.
Project Overview
First, let's take a look at the two project repositories β the backend and the frontend β and go through the code.
Backend part: Flask+Celery
For the backend, I chose a combination that is quite popular among Python developers: the Flask framework (for the API) and Celery (for the task queue). SQLAlchemy is used as the ORM. Alembic is used for migrations. Marshmallow is used for JSON validation in the endpoints.
In There is a Readme.md file with detailed descriptions of the structure and instructions for running the project.
is quite straightforward, consisting of 6 endpoints:
/pingβ for availability checks;- endpoints for registration, authorization, deauthorization, and retrieving the authorized user;
- an endpoint for sending emails, which places a task in the Celery queue.
is even simpler, with just one task send_mail_task.
In the folder there are two subfolders:
dockerwith two Docker files (base.dockerfilefor building a rarely changing base image andDockerfilefor main builds);.env_filesβ with files containing environment variables for different environments.
In the root of the project, there are four docker-compose files:
docker-compose.local.db.ymlfor setting up a local database for development;docker-compose.local.workers.ymlfor locally spinning up a worker, DB, Redis, and RabbitMQ;docker-compose.test.ymlfor running tests during deployment;docker-compose.ymlfor deployment.
And the last interesting folder for us β contains shell scripts for deployment:
deploy.shβ starting migrations and deployment. It is run on the server after the build and test runs in Github Actions;rollback.shβ rolls back containers to the previous version of the build;curl_tg.shβ sending deployment notifications to Telegram.
Frontend on Angular
is much simpler than the backend. The frontend consists of three pages:
- The main page with a form for sending email and a logout button.
- Login page.
- Registration page.
The main page looks minimalist:

There are two files in the root Dockerfile and docker-compose.yml, as well as a familiar folder .ci-cd with slightly fewer scripts than in the backend repository (scripts for running tests have been removed).
Create a project in Plesk
Let's start by configuring Plesk and creating a subscription for our website.
Installing extensions
In Plesk, we will need four extensions:
Dockerfor managing and visually displaying the status of containers in the Plesk admin panel;Gitto configure the deployment step on the server;Let's Encryptfor generating (and auto-renewing) free TLS certificates;Firewallfor setting up incoming traffic filtering.
You can install them through the Plesk admin panel in the Extensions section:

We will not go into 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.
- We create a subscription for the domain helloworld.ru and specify a username-password for the system user:

At the bottom of the page, check the box Secure the domain with Letβs Encrypt, if we want to configure HTTPS for the site:
- Next, in this subscription, we create the subdomain dev.helloworld.ru (for which a free TLS certificate can also be issued):

Installing server components
We have a server with OS Debian Stretch 9.12 and installed control panel 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 built Docker images).
- UI interface for Docker registry.
PostgreSQL
The PostgreSQL database is already included with Plesk, but it's not the latest version (as of the time of writing, Plesk Obsidian Postgres versions 8.4β10.8). We want the very latest version for our application (12.3 at the time of writing), so we will install it manually.
There are plenty of detailed instructions for installing Postgres on Debian online (), so I wonβt describe them in detail, just list 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
Given that PostgreSQL has quite mediocre default settings, it is essential to adjust the configuration. This can be aided by : you need to enter your server parameters and replace the settings in the file /etc/postgresql/12/main/postgresql.confwith the suggested ones. It should be noted that such calculators are not a silver bullet, and the database should be tuned more precisely based on your hardware, application, and query complexity. But this is enough to start.
In addition to the settings suggested by the calculator, we also change the postgresql.confdefault port 5432 to another one (in our case - 53983).
After changing the configuration file, restart the postgresql-server with the command:
service postgresql restart
We have installed and configured PostgreSQL. Now let's create the databases, users for the dev and prod environments, and grant the users permissions to manage the databases:
$ 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
Now let's move on to the installation of RabbitMQ β the message broker for Celery. It installs quite easily on 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
After installation, we need to create vhosts, users, and assign the necessary permissions:
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
Now let's install and configure the last component for our application β Redis. It will be used as a backend for storing Celery task results.
We will bring up two Docker containers with Redis for the dev and prod environments using the extension Docker for Plesk.
- We log into Plesk, navigate to the Extensions section, search for the Docker extension, and install it (we need the free version):

- We go to the installed extension, find the image via search
redis bitnamiand install the latest version:
- We enter the downloaded container and adjust the configuration: specify the port, maximum allocated RAM size, password in environment variables, and mount the volume:

- Perform steps 2-3 for the prod container, only changing settings like: port, password, RAM size, and the path to the server volume folder:

Docker Registry
In addition to basic services, it would be nice to set up our own Docker image repository on the server. Fortunately, server space is quite cheap now (certainly cheaper than a DockerHub subscription), and the process of installing a private repository is very straightforward.
We want to have the following installed:
- a password-protected Docker repository, accessible via a subdomain ;
- a UI interface for viewing images in the repository, available at .
For this:
- We will create two subdomains in Plesk within our subscription: docker.helloworld.ru and docker-ui.helloworld.ru, and configure Let's Encrypt certificates for them.
- In the folder of the subdomain docker.helloworld.ru, we will add the file
docker-compose.ymlwith the following content: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' - Via SSH, we will generate the .htpasswd file for Basic authentication in the Docker repository:
htpasswd -bBc .htpasswd hw_docker_admin hw_docker_password - We will build and start the containers:
docker-compose up -d - And we need to redirect Nginx to our containers. This can be done through Plesk.
The following actions need to be performed for the subdomains docker.helloworld.ru and docker-ui.helloworld.ru:
In the section Dev Tools of our website, we go to Docker Proxy Rules:

And we add a rule for proxying incoming traffic to our container:

- We check that we can authenticate in our container from the local machine:
$ 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 - We will also check the operation of the subdomain docker-ui.helloworld.ru:

When you click on Browse repositories, the browser will display a login window where you need to enter the login and password for the repository. After that, you will be taken to a page with a list of repositories (it will be empty for you for now):
Opening Ports in Plesk Firewall
After installing and configuring the components, we need to open ports to make the components accessible from Docker containers and the external network.
Let's see how to do this using the example of the Firewall extension we installed earlier for Plesk.
- Navigate to Tools & Settings > Settings > Firewall:

- Navigate to Modify Plesk Firewall Rules > Add Custom Rule and open the following TCP ports for the Docker subnet (172.0.0.0/8):
RabbitMQ: 1883, 4369, 5671-5672, 25672, 61613-61614
Redis: 32785, 32786
- We will also add a rule to open the PostgreSQL ports and RabbitMQ management panel to the outside world:

- Apply the rules using the Apply Changes button:

Setting Up CI/CD in Github Actions
Let's move on to the most interesting part β configuring the continuous integration and delivery pipeline for our project to the server.
This pipeline will consist of two parts:
- building the image and running tests (for the backend) β on the Github side;
- running migrations (for the backend) and deploying containers β on the server.
Deployment in Plesk
Let's first address the second point (since the first depends on it).
We will configure the deployment process using the Git extension for Plesk.
Let's consider an example with the Prod environment for the Backend repository.
- Go to the subscription for our Helloworld site and navigate to the Git section:

- In the 'Remote Git repository' field, paste the link to our Github repository and change the default folder
httpdocsto another one (for example,/httpdocs/hw_back):
- Copy the SSH Public key from the previous step and add it in the Github settings.
- Click OK on the screen in step 2, after which you will be redirected to the repository page in Plesk. Now we need to set up the repository update on commits to the master branch. For this, go to Repository Settings and save the value
Webhook URL(we will need it later when setting up Github Actions):
- In the Actions field on the screen from the previous step, enter the script to trigger the deployment:
cd {REPOSITORY_ABSOLUTE_PATH} .ci-cd/deploy.sh {ENV} {DOCKER_REGISTRY_HOST} {DOCKER_USER} {DOCKER_PASSWORD} {TG_BOT_TOKEN} {TG_CHAT_ID}where:
{REPOSITORY_ABSOLUTE_PATH}β path to the prod backend repository folder on the server;
{ENV}β environment (dev/prod), in our caseprod;
{DOCKER_REGISTRY_HOST}β host of our docker repository
{TG_BOT_TOKEN}β Telegram bot token;
{TG_CHAT_ID}β chat/channel ID for sending notifications.Example 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 - Adding a user from our subscription to the Docker group (so they can manage containers):
sudo usermod -aG docker helloworld_admin
The Dev environment for the backend repository and frontend is configured similarly.
Deployment pipeline in Github Actions
Let's move on to setting up the first part of our CI/CD pipeline in Github Actions.
Backend
The pipeline is described in .
But before we dissect it, let's fill in the necessary Secret variables in Github. For this, go to Settings -> Secrets:
DOCKER_REGISTRYβ the host of our Docker repository (docker.helloworld.ru);DOCKER_LOGINβ the login to the Docker repository;DOCKER_PASSWORDβ the password for it;DEPLOY_HOSTβ the host where the Plesk admin panel is available (example: :8443 or :8443);DEPLOY_BACK_PROD_TOKENβ the token for deploying to the prod repository on the server (we obtained it in Deployment in Plesk paragraph 4);DEPLOY_BACK_DEV_TOKENβ the token for deploying to the dev repository on the server.
The deployment process is simple and consists of three main steps:
- building and publishing the image in our repository;
- running tests in a container based on the freshly built image;
- deploying to the necessary environment depending on the branch (dev/master).
Frontend
doesn't differ much from the backend one. It lacks the step for running tests and the names of the tokens for deployment are changed. By the way, secrets for the frontend repository need to be filled in separately.
Configuring the site
Proxying traffic through Nginx
Well, we have reached the end. Now we just need to set up proxying of incoming and outgoing traffic to our container through Nginx. We have already covered this process in section 5 of the Docker Registry setup. The same needs to be repeated for the backend and frontend in both dev and prod environments.
I will provide screenshots of the settings.
Backend

Frontend

Important clarification. In the frontend container, all URLs will be proxied except those starting with /api/ β they will be proxied to the backend container (therefore, in the backend container, all handlers must start with /api/).
Summary
Now our site should be accessible at helloworld.ru and dev.helloworld.ru (prod and dev environments respectively).
In summary, we learned how to prepare a simple application using Flask and Angular and set up a pipeline in Github Actions for deploying it to a server managed by Plesk.
I will duplicate the links to the repositories with the code: , .
Source: habr.com
















