根据拉取请求创建审查应用程序
[](https://res.cloudinary.com/practicaldev/image/fetch/s--UmYjfts6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1. medium.com/max/300/1%2AJKrc7lJZGa3_R1jA8yXxuQ.png) 在这篇文章中,
[](https://res.cloudinary.com/practicaldev/image/fetch/s--UmYjfts6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1. medium.com/max/300/1%2AJKrc7lJZGa3_R1jA8yXxuQ.png)
在这篇文章中,我将展示一个关于如何为每个拉取请求创建应用程序的简单示例(或者,创建您自己的 Gitlab/Heroku Review 应用程序)。
让我们想象一个开发团队的以下场景:
-
开发者创建一个新的特性分支。
-
开发者推送分支
-
开发人员打开一个拉取请求,以便其他开发人员可以检查他的代码并测试该功能
-
CI 运行测试,如果通过则将分支变为绿色
现在开发人员应该将该功能发送给其他人进行测试。该功能不应合并到 master 中,以便对其进行测试。它们应该被隔离测试,因为一个特性可能会干扰另一个特性,而 master 应该只有可部署的代码。考虑到这一点,我们将使用 Docker 为每个拉取请求创建一个环境。
我将使用 Jenkins 作为 CI,因为我想使用开源工具来演示这一点,但是您可以使用任何您喜欢的 CI 工具。
这篇文章将假设您已经安装了 Jenkins。我将以这个开源 Rails 应用为例。将此 repo 复制到您的帐户中并将其克隆到您的计算机中。
git clone [https://github.com/yourgithubusername/opensanca\_jobs.git](https://github.com/duduribeiro/openjobs_experiment.git)
您可以获取我在https://github.com/duduribeiro/openjobs_jenkins_test上使用的完整代码仓库
配置Docker
我们需要的第一步是使用 docker 配置我们的应用程序。
Dockerfile 是一个文件,其中包含构建包含我们应用程序的映像的指令。您可以在他们的文档中了解有关 Docker 的更多信息。
在 app 文件夹中创建一个名为 Dockerfile 的文件:
这是我们构建映像的说明。第一个命令 FROM 向 docker 指定我们将使用镜像ruby:2.4.1作为我们的基础镜像。
之后,第一个 RUN 命令会安装应用程序所需的所有依赖项:yarn、imagemagick 和 node(用于预编译资产。一个奇特的解决方案是使用仅带有 node 和 sprocket 的不同容器来预编译资产)。第二个 RUN 命令创建一个文件夹 /var/app 来负责存储应用程序。 COPY 命令将当前文件夹移动到 /var/app 文件夹中的容器中。下一个 RUN 命令安装 Rails 和 yarn 的所有依赖项。 CMD 指定容器在运行映像时应执行的命令。您可以在此处](https://docs.docker.com/engine/reference/builder/)了解有关 Dockerfile[的更多信息。
使用此文件,我们可以构建我们的图像。
docker build -t myimage .
使用此命令,我们正在构建 Dockerfile 并生成带有标签 myimage 的图像。
运行 docker image ls 我们可以检查我们的图像。
[](https://res.cloudinary.com/practicaldev/image/fetch/s--UabC3LXY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/520/1%2Aq2LJXXVz4fAXwOD2wp2jjA.png)
现在我们可以启动我们的应用程序了:
docker run -d -p 3000:3000 myimage
-d 选项告诉 docker 这个容器将在后台运行,并且 -p 3000:3000 将本地端口 3000 与容器中暴露的 3000 端口连接起来。
我们现在可以导航到http://localhost:3000。
[](https://res.cloudinary.com/practicaldev/image/fetch/s--BAiExJ2i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/1%2AfT58lZr0Pz8zUfEnn43y1Q.png)
我们收到此错误。这是因为我们的应用程序需要数据库连接。我们需要使用数据库运行一个新容器并链接两个容器,以便它们可以相互通信。每个容器应该只有一个用途,因此,您不应在单个容器中运行超过 1 个服务(即:具有应用程序和数据库的容器)。
代替手动运行每个容器,我们将使用docker compose,一个帮助我们运行多容器应用程序的工具。
使用 docker ps 和 docker kill 来销毁你的应用容器/
[](https://res.cloudinary.com/practicaldev/image/fetch/s--NlDA-6B9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images- 1.medium.com/max/414/1%2AM2HI_2fGRmo8knwM9VGQgQ.png)
获取容器的id
[](https://res.cloudinary.com/practicaldev/image/fetch/s--3vBGLUyH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/374/1%2A3GpKXZWWfJIR5Qa603m_0w.png)
杀死容器
创建一个 docker-compose.yml 文件:
在这个文件中,我们创建了 2 个服务。一个用于使用 postgres 映像的数据库,另一个用于使用我们的 Dockerfile 映像的 Web 应用程序。
在 web 服务的环境项中,我设置了 DATABASE_URL 环境变量。如果设置了这个环境变量,rails 将使用它替换从 config/database.yml 加载的配置。在此处阅读有关此的更多信息。 DATABASE_URL 遵循 postgres 的这种模式:
postgres://user:password@host/database_name。由于我们的数据库用户没有密码,我们在 : 后面留空。我们不填写 database_name,因为我们想要在 config/database.yml 中配置一个(每个环境一个数据库名称)。
创建数据库并运行迁移:
docker-compose run --rm web rake db:create db:migrate
在此命令中,我们将使用 Web 服务运行一个容器并执行命令 rake db:create db:migrate 。 --rm 选项是在执行后删除容器。
运行测试:
docker-compose run --rm -e RAILS\_ENV=test web rake db:drop db:create db:migrate
docker-compose run --rm web rspec
[](https://res.cloudinary.com/practicaldev/image/fetch/s--DOg5nkz---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1 .medium.com/max/1024/1%2AqzMCx1X0ZNlKkKNYSsN3MA.png)
成功
现在我们可以启动应用程序了:
docker-compose up
再次访问http://localhost:3000现在我们的应用程序正在运行。
[](https://res.cloudinary.com/practicaldev/image/fetch/s--XXxqgFcp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1. medium.com/max/1024/1%2ARlLvjbA3ZZL6CR5U2hBKeQ.png)
配置我们的管道
我们将使用 Jenkins 管道作为代码来配置我们的管道。阅读更多关于它在这里。我们需要在 CI 服务器上安装docker、jq和 Jenkins。
我们将使用以下 Jenkins 插件:Blue Ocean,Blue Ocean Pipeline Editor,GitHub Pipeline for Blue Ocean
在项目根目录下创建一个名为 Jenkinsfile 的文件,内容如下:
我们在这条管道上有 4 个阶段:
-
Build :它将构建我们的 Dockerfile 并从此构建生成一个带有 openjobs 标记的映像,其版本名为 latest 。它将使用 docker-compose 构建、安装依赖项、创建和迁移使用 compose 创建的数据库。
-
Tests :它将运行 2 个并行步骤,一个运行单元测试,另一个运行功能测试。
-
Deploy to staging : 如果是master分支,会部署到staging.domain。我们将在接下来的步骤中介绍这一点。
-
创建特性环境 : 如果不是 master 分支,它会将应用部署到 branchname.domain 。我们将在接下来的步骤中介绍这一点。
让我们在 Jenkins 上创建我们的管道。推送你的代码,进入你的 Jenkins,进入蓝海界面,点击 New Pipeline 按钮:
[](https://res.cloudinary.com/practicaldev/image/fetch/s--_bG1pWjI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/1%2AeXA3HCPFSBQ-mvUNWHV8gw.png)
在下一个屏幕中,选择 Github,选择您的帐户并找到存储库。单击创建管道
[](https://res.cloudinary.com/practicaldev/image/fetch/s--rcsfYN----/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images- 1.medium.com/max/1024/1%2A_2gQbUaarl7LFVDOKNcqxw.png)
我们的构建正在通过🎉
[](https://res.cloudinary.com/practicaldev/image/fetch/s--w6P8hSk0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/1%2A--GrJVBvaS8b7WMavEazew.png)
构建上的红色图标是因为这一步不会运行,因为构建的分支是 master 。
现在我们需要在构建和动态路由域到特定容器之后创建我们的环境。
进入,Traefik
[](https://res.cloudinary.com/practicaldev/image/fetch/s--AN1UKjP---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1 .medium.com/max/340/1%2A0A8y77LMVEQtxLSQ_J0zJA.png)
Traefik 是一个工具,可以帮助我们进行动态路由并充当负载均衡器。示例:如果我访问http://mybranch.mydomain.com,我想访问包含应该由 Jenkins 启动的带有 mybranch 的应用程序的容器。
创建动态环境
应在 CI 服务器上执行以下步骤。
我们将使用Docker Swarm。创建 docker 集群非常有帮助。我将仅使用一台服务器进行演示,但您也可以创建一个集群。
初始化 swarm 集群:
docker swarm init --advertise-addr=10.10.0.5
这会将我们的服务器初始化为 swarm master。它将生成一个命令,您可以使用该命令将集群加入其他服务器。
创建一个可以与 traefik 和我们的容器一起使用的网络:
docker network create --driver=overlay --attachable traefik-net
初始化traefik:
docker service create \
--name traefik \
--constraint 'node.role==manager' \
--publish 80:80 \
--publish 8081:8080 \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
--network traefik-net \
traefik \
--docker \
--docker.swarmmode \
--docker.domain=apps.carlosribeiro.me \
--docker.watch \
--logLevel=DEBUG \
--web
这将初始化 traefik。您应该在服务器上的某些启动脚本上处理此初始化。
如果我们在 8081 端口访问我们的服务器,我们可以访问 traefik 仪表板。
[](https://res.cloudinary.com/practicaldev/image/fetch/s--32P2pjpX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/1%2ALet1rhq73CWwjCyWSZgKLw.png)
docker.domain 指定我们将通过 appname.apps.carlosribeiro.me 访问应用程序。为此,我在我的 DNS 服务器上创建了两个别名,指向 CI 服务器的 IP:
-
apps.carlosribeiro.me
-
*.apps.carlosribeiro.me
编辑 Jenkinsfile 以创建环境
让我们在 Jenkinsfile 上添加一个方法,该方法将在触发构建时创建一个环境。
此方法将接受一个参数来告知环境的名称。此名称将用于作为服务名称的前缀。它将停止所有仍在运行的 docker-compose 容器。之后,如果存在,它将删除同名的服务(这是为了在我们再次推送到分支时重新创建环境)。之后,我们创建 3 个服务。一个用于 Postgres,另一个用于 Redis,应用程序本身使用我们在构建步骤中构建的映像。最后一个命令,运行一个临时 docker 容器来创建和迁移数据库并预编译资产(您也可以从生产中获取之前的转储)。
看在app服务中,我们指定了一些环境变量。
-
REDIS_URL, DATABASE_URL :使用先前创建的服务主机连接两个服务的端点。
-
RAILS_ENV: 我们将其设置为生产。它将强制我们的应用程序应该像生产环境一样运行。
-
RAILS_SERVE_STATIC_FILES :由于我们在应用服务器前面没有 nginx,我们需要设置它来告诉 rails 为我们服务静态文件。
— 标签“traefik.port”将告知 traefik 容器的暴露端口是什么。在我们的例子中,是 3000。
— name 告知 traefik 将识别为域前缀的服务名称。
现在,让我们在管道的阶段调用此方法。
如果是 master 分支,我们将部署到一个名为 staging 的应用程序中。如果没有,它将调用具有相同分支名称的应用程序。
查看最终的 Jenkinsfile:
Push master,让我们等待 Jenkins 为我们创建我们的 staging。
我们的构建通过...
[](https://res.cloudinary.com/practicaldev/image/fetch/s--IbKZRQZB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/1%2AdXnyUalMlB61BcsFeyDApA.png)
......我们的应用部署在 staging 上:
[](https://res.cloudinary.com/practicaldev/image/fetch/s--J8mFTgOW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/1%2A160K0xkSl3dJ83duQ5Mq8g.png)
现在让我们打开一个拉取请求来更改此按钮的颜色。
git checkout -b 01-change-button-color
编辑 app/assets/stylesheets/application.scss
.btn-register-vacancy {
background-color: #2769ff;
width: 300px;
height: 35px;
border-radius: 5px;
}
推送这个分支,并打开一个拉取请求。
[](https://res.cloudinary.com/practicaldev/image/fetch/s--A-3k59Ec--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images- 1.medium.com/max/1024/1%2AeLQPHGOVDArlDDRvPmnLXQ.png)
Github 会告诉我们构建正在等待中。
...
工作正在过去
[](https://res.cloudinary.com/practicaldev/image/fetch/s--hVJ4xke---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1 .medium.com/max/1024/1%2AvZwLnuEbj8egwCvUQ8cAIg.png)
并且 Github 被告知:
[](https://res.cloudinary.com/practicaldev/image/fetch/s--vLW_KEQk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/1%2AW6y5kHRubVrYJgFeACdRIA.png)
现在我们已经启动并运行了动态环境
[](https://res.cloudinary.com/practicaldev/image/fetch/s--XFVL0HSf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/1%2AM_n7-CK4mquX42LkpM_pmQ.png)
我可以在合并之前将此 URL 发送给 QA 或 PO 审查它。如果出现问题,可以在它出现在 master 之前对其进行验证。
就这样
干杯🍻
更多推荐
所有评论(0)