在本文中,我将分享我使用 Plesk 控制面板和 Github Actions 设置 CI/CD 的经验。 今天我们将学习如何部署一个名为“Helloworld”的简单项目。 它是用 Flask Python 框架编写的,带有 Celery worker 和 Angular 8 前端。
在本文的第一部分,我们将查看我们的项目及其部分。 在第二部分,我们将了解如何设置 Plesk 并安装必要的扩展和组件(DB、RabbitMQ、Redis、Docker 等)。
在第三部分中,我们将最终弄清楚如何设置管道以将我们的项目部署到开发和生产环境中的服务器。 然后我们将在服务器上启动该站点。
是的,我忘了自我介绍。 我叫 Oleg Borzov,是 Domclick 抵押贷款经理 CRM 团队的一名全栈开发人员。
项目概述
首先,让我们看一下两个项目存储库 - 后端和前端 - 并检查代码。
后端:烧瓶+芹菜
对于后面的部分,我采用了一组在 Python 开发人员中非常流行的:Flask 框架(用于 API)和 Celery(用于任务队列)。 SQLAchemy 用作 ORM。 Alembic 用于迁移。 对于句柄中的 JSON 验证 - Marshmallow。
В
/ping
- 检查可用性;- 处理注册、授权、取消授权和获得授权用户;
- 将任务放入 Celery 队列的电子邮件句柄。
send_mail_task
.
在文件夹中
docker
有两个 Dockerfiles (base.dockerfile
建立一个很少改变的基础形象和Dockerfile
用于主要组件);.env_files
- 带有针对不同环境的环境变量的文件。
项目根目录下有四个 docker-compose 文件:
docker-compose.local.db.yml
为发展建立一个本地数据库;docker-compose.local.workers.yml
用于 worker、数据库、Redis 和 RabbitMQ 的本地提升;docker-compose.test.yml
在部署期间运行测试;docker-compose.yml
用于部署。
我们感兴趣的最后一个文件夹 -
deploy.sh
— 启动迁移和部署。 在 Github Actions 中构建和运行测试后在服务器上运行;rollback.sh
- 将容器回滚到先前版本的程序集;curl_tg.sh
- 向 Telegram 发送部署通知。
Angular 前端
- 带有用于发送电子邮件的表单和退出按钮的主页。
- 登录页面。
- 注册页面。
主页看起来苦行僧:
根目录下有两个文件 Dockerfile
и docker-compose.yml
,以及熟悉的文件夹 .ci-cd
脚本比后台存储库中的脚本略少(删除了用于运行测试的脚本)。
在 Plesk 中启动项目
让我们从设置 Plesk 并为我们的站点创建订阅开始。
安装扩展
在 Plesk 中,我们需要四个扩展:
Docker
在 Plesk 管理面板中管理和可视化显示容器的状态;Git
在服务器上配置部署步骤;Let's Encrypt
生成(并自动更新)免费的 TLS 证书;Firewall
配置传入流量的过滤。
您可以通过扩展部分中的 Plesk 管理面板安装它们:
我们不会考虑扩展的详细设置,默认设置将用于我们的演示目的。
创建订阅和站点
接下来,我们需要为我们的 helloworld.ru 网站创建一个订阅,并在那里添加 dev.helloworld.ru 子域。
- 为 helloworld.ru 域创建订阅并为系统用户指定登录密码:
选中页面底部的框 使用 Let's Encrypt 保护域如果我们想为站点设置 HTTPS: - 接下来,在此订阅中,创建一个子域 dev.helloworld.ru(您还可以为其颁发免费的 TLS 证书):
安装服务器组件
我们有一台服务器 操作系统 Debian Stretch 9.12 并安装了控制面板 Plesk 黑曜石 18.0.27.
我们需要为我们的项目安装和配置:
- PostgreSQL(在我们的例子中,将有一台服务器有两个数据库用于开发和生产环境)。
- RabbitMQ(相同的实例,具有不同的虚拟主机环境)。
- 两个 Redis 实例(用于开发和生产环境)。
- Docker 注册表(用于本地存储构建的 Docker 映像)。
- Docker 注册表的 UI。
PostgreSQL的
Plesk 已经带有 PostgreSQL DBMS,但不是最新版本(在撰写 Plesk Obsidian 时
网上有很多关于在 Debian 上安装 Postgres 的详细说明(
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。 现在让我们创建一个数据库,开发环境和生产环境的用户,并赋予用户管理数据库的权限:
$ 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
让我们继续安装 Celery 的消息代理 RabbitMQ。 在 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 任务结果的后端。
我们将使用扩展为开发和生产环境提供两个带有 Redis 的 Docker 容器 Docker
对于 Plesk。
- 我们转到 Plesk,转到扩展部分,查找 Docker 扩展并安装它(我们需要免费版本):
- 转到已安装的扩展程序,通过搜索找到图像
redis bitnami
并安装最新版本: - 我们进入下载的容器并调整配置:指定端口、分配的最大 RAM 大小、环境变量中的密码以及挂载卷:
- 我们为 prod 容器执行步骤 2-3,在设置中我们只更改参数:端口、密码、RAM 大小和服务器上卷文件夹的路径:
Docker 注册表
除了基础服务,如果服务器上放上自己的Docker镜像仓库就好了。 幸运的是,服务器空间现在相当便宜(肯定比 DockerHub 订阅便宜),而且设置私有存储库的过程非常简单。
我们想要:
- 可在子域上访问的受密码保护的 Docker 存储库
https://docker.helloworld.ru ; - 用于查看存储库中图像的 UI,位于
https://docker-ui.helloworld.ru .
要做到这一点:
- 让我们在订阅的 Plesk 中创建两个子域:docker.helloworld.ru 和 docker-ui.helloworld.ru,并为它们配置 Let's Encrypt 证书。
- 将文件添加到 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'
- 在 SSH 下,我们将在 Docker 存储库中生成用于 Basic 授权的 .htpasswd 文件:
htpasswd -bBc .htpasswd hw_docker_admin hw_docker_password
- 收集和提升容器:
docker-compose up -d
- 我们需要将 Nginx 重定向到我们的容器。 这可以通过 Plesk 完成。
需要为 docker.helloworld.ru 和 docker-ui.helloworld.ru 子域完成以下步骤:
在第 开发工具 我们的网站去 Docker 代理规则:
并添加一条规则来将传入流量代理到我们的容器:
- 我们检查我们是否可以从本地机器登录到我们的容器:
$ 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 子域的运行情况:
当您单击浏览存储库时,浏览器将显示一个授权窗口,您需要在其中输入存储库的用户名和密码。 之后,我们将转到一个包含存储库列表的页面(目前,它对您来说是空的):
在 Plesk 防火墙中打开端口
安装和配置组件后,我们需要打开端口,以便从 Docker 容器和外部网络访问组件。
让我们看看如何使用我们之前安装的 Plesk 防火墙扩展来做到这一点。
- 去吧 工具与设置 > 设置 > 防火墙:
- 去吧 修改 Plesk 防火墙规则 > 添加自定义规则 并为 Docker 子网打开以下 TCP 端口 (172.0.0.0 / 8):
RabbitMQ:1883、4369、5671-5672、25672、61613-61614
雷迪斯:32785、32786 - 我们还将添加一条规则,向外界开放 PostgreSQL 端口和 RabbitMQ 管理面板:
- 使用 Apply Changes 按钮应用规则:
在 Github Actions 中设置 CI/CD
让我们开始讨论最有趣的部分——设置持续集成管道并将我们的项目交付到服务器。
该管道将由两部分组成:
- 构建图像并运行测试(用于后端)——在 Github 端;
- 在服务器上运行迁移(用于后端)和部署容器。
部署到 Plesk
让我们先处理第二点(因为第一点取决于它)。
我们将使用 Plesk 的 Git 扩展配置部署过程。
考虑一个后端存储库的 Prod 环境示例。
- 我们转到我们的 Helloworld 网站的订阅并转到 Git 子部分:
- 将指向我们的 Github 存储库的链接插入“远程 Git 存储库”字段并更改默认文件夹
httpdocs
给另一个(例如。/httpdocs/hw_back
): - 复制上一步中的 SSH 公钥,然后
加 它在 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 文件夹的路径;
{ENV}
- 环境(开发/生产),在我们的例子中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
后端存储库和前端的开发环境以相同的方式设置。
Github Actions 中的部署管道
让我们继续在 Github Actions 中设置 CI/CD 管道的第一部分。
后端
管道描述于
不过在解析之前,我们先在Github中填写我们需要的Secret变量。 为此,请访问 设置 -> 秘密:
DOCKER_REGISTRY
- 我们的 Docker 存储库的主机 (docker.helloworld.ru);DOCKER_LOGIN
- 登录到 Docker 存储库;DOCKER_PASSWORD
- 密码;DEPLOY_HOST
— Plesk 管理面板可用的主机(例如:helloworld.com : 8443 或123.4.56.78 :8443);DEPLOY_BACK_PROD_TOKEN
- 用于部署到服务器上的 prod-repository 的令牌(我们在第 4 页的 Plesk 部署中获得);DEPLOY_BACK_DEV_TOKEN
- 用于部署到服务器上的开发存储库的令牌。
部署过程很简单,包括三个主要步骤:
- 在我们的存储库中构建和发布图像;
- 基于新构建的镜像在容器中运行测试;
- 根据分支(dev/master)部署到所需的环境。
前端
网站设置
通过 Nginx 代理流量
好吧,我们已经走到了尽头。 它仍然只是配置通过 Nginx 将传入和传出流量代理到我们的容器。 我们已经在 Docker Registry 设置的第 5 步中介绍了这个过程。 在开发环境和生产环境中,应该对后面和前面的部分重复相同的操作。
我将提供设置的屏幕截图。
后端
前端
重要的澄清. 所有 URL 都将被代理到前端容器,除了那些以 /api/
- 他们将被代理到后面的容器(所以 在后面的容器中,所有处理程序都必须以 /api/
).
结果
现在我们的站点应该可以在 helloworld.ru 和 dev.helloworld.ru(分别是产品环境和开发环境)上访问。
总的来说,我们学习了如何在 Flask 和 Angular 中准备一个简单的应用程序,并在 Github Actions 中设置一个管道以将其部署到运行 Plesk 的服务器上。