使用 Docker 和 Gitlab CI 的开发和测试过程

我建议阅读 Inventos 的 Alexander Sigachev 的报告“使用 Docker + Gitlab CI 进行开发和测试过程”的文字记录

那些刚刚开始实施基于 Docker + Gitlab CI 的开发和测试流程的人经常会问一些基本的问题。 从哪里开始? 如何组织? 如何测试?

这份报告很好,因为它以结构化的方式讨论了使用 Docker 和 Gitlab CI 的开发和测试过程。 该报告本身是 2017 年的。 我认为从这个报告中你可以学到基础知识、方法论、思想、使用体验。

谁在乎,请在猫下。

我的名字是亚历山大·西加乔夫。 我在 Inventos 工作。 我给大家讲讲我使用Docker的经历以及我们是如何在公司的项目上逐步落地的。

演示主题:使用 Docker 和 Gitlab CI 的开发流程。

使用 Docker 和 Gitlab CI 的开发和测试过程

这是我第二次谈论 Docker。 在第一份报告发布时,我们仅在开发人员计算机上使用 Docker 进行开发。 使用Docker的员工人数约为2-3人。 渐渐地,我们积累了经验,又向前迈进了一步。 链接到我们的 第一次报告.

这份报告会包含哪些内容? 我们将分享我们收集了哪些耙子、解决了哪些问题的经验。 并非所有地方都是美丽的,但允许继续前进。

我们的座右铭是:对接我们能得到的一切。

使用 Docker 和 Gitlab CI 的开发和测试过程

我们正在解决什么问题?

当公司中有多个团队时,程序员是共享资源。 在某些阶段,程序员会退出一个项目,并在一段时间内投入到另一个项目中。

为了让程序员能够快速理解,他需要尽快下载项目的源代码并启动环境,这将让他能够进一步解决这个项目的问题。

通常,如果您从头开始,那么项目中的文档很少。 有关如何设置的信息仅适用于老用户。 员工在一两天内自行搭建工作场所。 为了加快速度,我们使用了 Docker。

下一个原因是开发中设置的标准化。 根据我的经验,开发人员总是采取主动。 每五个案例中都会输入一个自定义域,例如 vasya.dev。 坐在他旁边的是他的邻居 Petya,其域名是 petya.dev。 他们使用该域名开发网站或系统的某些组件。

当系统增长并且这些域名开始进入配置时,会出现开发环境冲突并重写站点路径。

数据库设置也会发生同样的情况。 有人不关心安全性并使用空的 root 密码。 在安装阶段,MySQL 要求某人输入密码,结果密码是 123。经常发生数据库配置根据开发人员的提交而不断变化的情况。 有人更正,有人没有更正配置。 当我们取出某种测试配置时,有一些技巧 .gitignore 每个开发人员都必须安装数据库。 这使得入门变得困难。 除其他事项外,有必要记住数据库。 必须初始化数据库、输入密码、注册用户、创建表等等。

另一个问题是库的版本不同。 开发人员经常会处理不同的项目。 有一个五年前开始的遗留项目(从 2017 年开始 - 编者注)。 在发布时,我们从 MySQL 5.5 开始。 还有一些现代项目,我们尝试实现更现代版本的 MySQL,例如 5.7 或更早版本(2017 年 - 编者注)

任何使用 MySQL 的人都知道这些库带来了依赖关系。 两个基地一起运行是相当有问题的。 至少,旧客户端连接新数据库是有问题的。 这反过来又产生了几个问题。

下一个问题是,当开发人员在本地计算机上工作时,他使用本地资源、本地文件、本地 RAM。 开发问题解决方案时的所有交互都是在它在一台机器上运行的事实框架内进行的。 一个例子是,当我们在 Production 3 中有后端服务器时,开发人员将文件保存到根目录,nginx 从那里获取文件来响应请求。 当这样的代码进入生产环境时,结果表明该文件存在于 3 台服务器之一上。

现在正在发展微服务的方向。 当我们将大型应用程序划分为一些相互交互的小组件时。 这允许您为特定的任务堆栈选择技术。 它还允许您在开发人员之间分担工作和责任。

Frondend-developer,在JS上开发,对Backend几乎没有影响。 在我们的例子中,后端开发人员反过来开发 Ruby on Rails,并且不会干扰 Frondend。 交互是使用 API 执行的。

作为奖励,在 Docker 的帮助下,我们能够回收 Staging 上的资源。 每个项目由于其具体情况,都需要某些设置。 从物理上讲,有必要分配一个虚拟服务器并单独配置它们,或者共享某种可变环境,并且项目可能会相互影响,具体取决于库的版本。

使用 Docker 和 Gitlab CI 的开发和测试过程

工具。 我们用什么?

  • Docker 本身。 Dockerfile 描述了单个应用程序的依赖关系。
  • Docker-compose 是一个捆绑包,汇集了我们的一些 Docker 应用程序。
  • 我们使用GitLab来存储源代码。
  • 我们使用 GitLab-CI 进行系统集成。

使用 Docker 和 Gitlab CI 的开发和测试过程

报告由两部分组成。

第一部分将讨论 Docker 如何在开发人员的机器上运行。

第二部分将讨论如何与 GitLab 交互、如何运行测试以及如何推出 Staging。

使用 Docker 和 Gitlab CI 的开发和测试过程

Docker 是一种允许(使用声明性方法)描述必要组件的技术。 这是一个 Dockerfile 示例。 这里我们声明我们继承自官方的 Ruby:2.3.0 Docker 镜像。 它包含已安装的 Ruby 版本 2.3。 我们安装所需的构建库和 NodeJS。 我们描述我们创建一个目录 /app。 将应用程序目录设置为工作目录。 在此目录中,我们放置所需的最小 Gemfile 和 Gemfile.lock。 然后我们构建安装此依赖项映像的项目。 我们指示容器将准备好侦听外部端口 3000。最后一个命令是直接启动我们的应用程序的命令。 如果我们执行项目启动命令,应用程序将尝试运行并运行指定的命令。

使用 Docker 和 Gitlab CI 的开发和测试过程

这是 docker-compose 文件的最小示例。 在本例中,我们表明两个容器之间存在连接。 这是直接进入数据库服务和Web服务。 在大多数情况下,我们的 Web 应用程序需要某种数据库作为存储数据的后端。 由于我们使用的是 MySQL,所以示例是使用 MySQL - 但没有什么可以阻止我们使用其他数据库(PostgreSQL、Redis)。

我们从 Docker hub 的官方来源获取 MySQL 5.7.14 的镜像,没有进行任何更改。 我们从当前目录收集负责我们的 Web 应用程序的图像。 它在首次启动期间为我们收集图像。 然后它运行我们在这里执行的命令。 如果我们返回,我们将看到通过 Puma 的启动命令已被定义。 Puma 是一个用 Ruby 编写的服务。 在第二种情况下,我们重写。 根据我们的需要或任务,该命令可以是任意的。

我们还描述了我们需要将开发人员主机上的端口从容器端口上的 3000 转发到 3000。 这是使用直接嵌入在 Docker 中的 iptables 及其机制自动完成的。

开发者还可以像以前一样访问任何可用的IP地址,例如127.0.0.1是机器的本地或外部IP地址。

最后一行表示 Web 容器依赖于 db 容器。 当我们调用web容器的启动时,docker-compose会首先为我们启动数据库。 在数据库启动时(事实上,在容器启动之后!这并不能保证数据库的准备就绪)将启动应用程序,即我们的后端。

这可以避免数据库未启动时出现错误,并在我们停止数据库容器时节省资源,从而为其他项目释放资源。

使用 Docker 和 Gitlab CI 的开发和测试过程

是什么让我们在项目上使用数据库dockerization。 我们为所有开发人员修复了 MySQL 的版本。 这可以避免当版本不同、语法、配置、默认设置更改时可能出现的一些错误。 这允许您指定数据库的通用主机名、登录名、密码。 我们正在摆脱之前的配置文件中的名称和冲突。

我们有机会为开发环境使用更优化的配置,这将不同于默认配置。 MySQL默认配置为弱机器,其开箱即用的性能非常差。

使用 Docker 和 Gitlab CI 的开发和测试过程

Docker 允许您使用所需版本的 Python、Ruby、NodeJS、PHP 解释器。 我们不再需要使用某种版本管理器。 以前,Ruby 使用 rpm 包,允许您根据项目更改版本。 借助 Docker 容器,它还允许顺利迁移代码并将其与依赖项一起进行版本控制。 我们可以毫无问题地理解解释器和代码的版本。 要更新版本,请降低旧容器并升高新容器。 如果出现问题,我们可以降低新容器,升高旧容器。

构建镜像后,开发和生产中的容器将是相同的。 对于大型安装尤其如此。

使用 Docker 和 Gitlab CI 的开发和测试过程 在前端,我们使用 JavaScipt 和 NodeJS。

现在我们有了 ReacJS 上的最后一个项目。 开发人员在容器中运行所有内容并使用热重载进行开发。

接下来启动JavaScipt组装任务,通过nginx节省资源给出编译成静态的代码。

使用 Docker 和 Gitlab CI 的开发和测试过程

这里我给出了我们上一个项目的方案。

解决了哪些任务? 我们需要构建一个与移动设备交互的系统。 他们接收数据。 一种可能性是向该设备发送推送通知。

我们为此做了什么?

我们将应用程序分为以下组件:JS 上的管理部分,后端,通过 Ruby on Rails 下的 REST 接口工作。 后端与数据库交互。 生成的结果被提供给客户端。 管理面板通过 REST 接口与后端和数据库交互。

我们还需要发送推送通知。 在此之前,我们有一个项目实现了一种负责向移动平台传递通知的机制。

我们开发了以下方案:浏览器的操作员与管理面板交互,管理面板与后端交互,任务是发送推送通知。

推送通知与 NodeJS 中实现的另一个组件交互。

建立队列,然后根据其机制发送通知。

这里画了两个数据库。 目前,在 Docker 的帮助下,我们使用 2 个独立的数据库,彼此之间没有任何关系。 另外,他们有一个共同的虚拟网络,物理数据存储在开发者机器上的不同目录中。

使用 Docker 和 Gitlab CI 的开发和测试过程

相同,但数量不同。 这就是代码重用很重要的地方。

如果之前我们讨论过以库的形式重用代码,那么在这个例子中,我们响应推送通知的服务被重用为一个完整的服务器。 它提供了一个API。 我们的新开发已经与之互动。

当时,我们使用的是 NodeJS 第 4 版。 现在(2017 年 - 编者注)在最近的开发中我们使用 NodeJS 版本 7。 新组件涉及新版本的库是没有问题的。

如有必要,您可以通过推送通知服务重构并提升 NodeJS 版本。

如果我们能够保持 API 兼容性,那么就有可能用以前使用的其他项目来替换它。

使用 Docker 和 Gitlab CI 的开发和测试过程

Docker 需要添加什么? 我们将 Dockerfile 添加到存储库,其中描述了必要的依赖项。 在此示例中,组件按逻辑进行了分解。 这是一个后端开发人员最起码的一套。

创建新项目时,我们创建一个 Dockerfile,描述所需的生态系统(Python、Ruby、NodeJS)。 在docker-compose中,它描述了必要的依赖项——数据库。 我们描述我们需要一个这样那样版本的数据库,在那里存储数据。

我们使用带有 nginx 的单独的第三个容器来提供静态服务。 可以上传图片。 后端将它们放在预先准备好的卷中,该卷也安装在带有 nginx 的容器中,这提供了静态。

为了存储 nginx、mysql 配置,我们添加了一个 Docker 文件夹,在其中存储必要的配置。 当开发人员在他的计算机上对存储库进行 git 克隆时,他已经有了一个可供本地开发的项目。 毫无疑问要应用什么端口或什么设置。

使用 Docker 和 Gitlab CI 的开发和测试过程

接下来,我们有几个组件:管理、通知 API、推送通知。

为了开始这一切,我们创建了另一个存储库,我们将其称为 dockerized-app。 目前我们在每个组件之前使用多个存储库。 它们只是逻辑上不同 - 在 GitLab 中它看起来像一个文件夹,但在开发人员的机器上,它是特定项目的文件夹。 下一层是将要组合的组件。

使用 Docker 和 Gitlab CI 的开发和测试过程

这只是 dockerized-app 内容的示例。 我们还将 Docker 目录引入此处,在其中填写所有组件交互所需的配置。 有一个 README.md 简要描述了如何运行该项目。

这里我们应用了两个 docker-compose 文件。 这样做是为了能够逐步运行。 当开发人员使用核心时,他不需要推送通知,他只需启动一个 docker-compose 文件,相应地,资源就会被保存。

如果需要与推送通知集成,则启动 docker-compose.yaml 和 docker-compose-push.yaml。

由于 docker-compose.yaml 和 docker-compose-push.yaml 位于一个文件夹中,因此会自动创建单个虚拟网络。

使用 Docker 和 Gitlab CI 的开发和测试过程

组件描述。 这是一个更高级的文件,负责组件的收集。 这里有什么值得注意的地方? 这里我们介绍一下平衡器组件。

这是一个现成的 Docker 映像,运行 nginx 和侦听 Docker 套接字的应用程序。 动态的,当容器打开和关闭时,它会重新生成 nginx 配置。 我们通过三级域名来分发组件的处理。

对于开发环境,我们使用 .dev 域 - api.informer.dev。 具有 .dev 域的应用程序可在开发人员的本地计算机上使用。

此外,配置会传输到每个项目,并且所有项目都会同时启动。

使用 Docker 和 Gitlab CI 的开发和测试过程

从图形上看,客户端是我们的浏览器或我们用来向平衡器发出请求的某种工具。

域名平衡器决定联系哪个容器。

可以是nginx,它提供了admin JS。 这个可以是nginx,它给出API,也可以是静态文件,以图片上传的形式给nginx。

该图显示容器通过虚拟网络连接并隐藏在代理后面。

在开发者的机器上,知道IP就可以访问容器,但原则上我们不使用这个。 实际上不需要直接访问。

使用 Docker 和 Gitlab CI 的开发和测试过程

要查看哪个示例来对您的应用程序进行 docker 化? 在我看来,MySQL 的官方 docker 镜像就是一个很好的例子。

这是相当具有挑战性的。 有很多版本。 但它的功能可以让您满足进一步开发过程中可能出现的许多需求。 如果你花时间弄清楚它们是如何相互作用的,那么我认为你在自我实现上不会有任何问题。

Hub.docker.com 通常包含 github.com 的链接,其中直接包含原始数据,您可以从中自行构建镜像。

此外,在此存储库中还有一个 docker-endpoint.sh 脚本,该脚本负责初始初始化和应用程序启动的进一步处理。

此外,在此示例中,还可以使用环境变量进行配置。 通过在运行单个容器时或通过 docker-compose 定义环境变量,我们可以说我们需要为 docker 设置一个空密码以在 MySQL 上 root 或任何我们想要的东西。

有一个选项可以创建随机密码。 我们说我们需要一个用户,我们需要给用户设置一个密码,我们需要创建一个数据库。

在我们的项目中,我们稍微统一了Dockerfile,它负责初始化。 在那里,我们根据需要对其进行了更正,使其成为应用程序使用的用户权限的扩展。 这使我们稍后可以从应用程序控制台简单地创建数据库。 Ruby 应用程序具有用于创建、修改和删除数据库的命令。

使用 Docker 和 Gitlab CI 的开发和测试过程

这是 github.com 上特定版本的 MySQL 的示例。 您可以打开 Dockerfile 并查看安装情况。

docker-endpoint.sh 是负责入口点的脚本。 在初始初始化期间,需要一些准备步骤,并且所有这些操作都在初始化脚本中完成。

使用 Docker 和 Gitlab CI 的开发和测试过程

我们转到第二部分。

为了存储源代码,我们切换到了gitlab。 这是一个相当强大的系统,具有可视化界面。

Gitlab CI 是 Gitlab 的组件之一。 它允许您描述一系列命令,稍后将用于组织代码交付系统或运行自动测试。

Gitlab CI 2 演讲 https://goo.gl/uohKjI - 来自 Ruby Russia 俱乐部的报告 - 非常详细,也许您会感兴趣。

使用 Docker 和 Gitlab CI 的开发和测试过程

现在我们将了解激活 Gitlab CI 需要什么。 为了启动 Gitlab CI,我们只需要将 .gitlab-ci.yml 文件放在项目的根目录中。

在这里,我们描述了我们想要执行一系列状态,例如测试、部署。

我们执行直接调用 docker-compose 的脚本来构建我们的应用程序。 这只是一个后端示例。

接下来,我们说需要运行迁移来更改数据库并运行测试。

如果脚本正确执行并且未返回错误代码,则系统将继续进行部署的第二阶段。

部署阶段目前已实施暂存。 我们没有组织零停机重启。

我们强行熄灭所有容器,然后再次升起所有容器,这些容器是在测试过程中第一阶段收集的。

我们正在为当前环境变量运行开发人员编写的数据库迁移。

有一点需要注意的是,这仅适用于 master 分支。

当改变其他分支时不执行。

可以沿着分支组织部署。

使用 Docker 和 Gitlab CI 的开发和测试过程

为了进一步组织这个,我们需要安装 Gitlab Runner。

该实用程序是用 Golang 编写的。 它是一个单个文件,这在 Golang 世界中很常见,不需要任何依赖项。

启动时,我们注册 Gitlab Runner。

我们在 Gitlab Web 界面中获取密钥。

然后我们在命令行调用初始化命令。

交互式设置 Gitlab Runner(Shell、Docker、VirtualBox、SSH)

Gitlab Runner 上的代码将在每次提交时执行,具体取决于 .gitlab-ci.yml 设置。

使用 Docker 和 Gitlab CI 的开发和测试过程

它在 Gitlab 的 Web 界面中的视觉效果如何。 连接 GItlab CI 后,我们有一个标志显示当前构建的状态。

我们看到 4 分钟前进行了一次提交,它通过了所有测试并且没有造成任何问题。

使用 Docker 和 Gitlab CI 的开发和测试过程

我们可以仔细看看构建。 这里我们看到两个状态已经过去了。 测试状态和临时部署状态。

如果我们单击特定的构建,则会根据 .gitlab-ci.yml 输出该进程中运行的命令的控制台输出。

使用 Docker 和 Gitlab CI 的开发和测试过程

这就是我们的产品历史。 我们看到有一些成功的尝试。 提交测试后,不会继续进行下一步,并且暂存代码不会更新。

使用 Docker 和 Gitlab CI 的开发和测试过程

当我们实施 docker 时,我们在 staging 上解决了哪些任务? 我们的系统由组件组成,我们需要重新启动,只是存储库中更新的部分组件,而不是整个系统。

为此,我们必须将所有内容粉碎到单独的文件夹中。

完成此操作后,我们遇到了一个问题,Docker-compose 为每个爸爸创建了自己的网络空间,并且看不到邻居的组件。

为了解决这个问题,我们在 Docker 中手动创建了网络。 它是用 Docker-compose 编写的,您可以在这个项目中使用这样的网络。

因此,从此网格开始的每个组件都可以看到系统其他部分中的组件。

下一个问题是在多个项目中分割分期。

因为为了让这一切看起来漂亮并且尽可能接近生产,最好使用端口 80 或 443,这在 WEB 中随处可见。

使用 Docker 和 Gitlab CI 的开发和测试过程

我们是如何解决的? 我们为所有主要项目分配了一名 Gitlab Runner。

Gitlab 允许您运行多个分布式 Gitlab Runner,它将简单地以混乱的方式依次接收所有任务并运行它们。

由于我们没有房子,因此我们将项目组限制为一个 Gitlab Runner,它可以毫无问题地处理我们的工作量。

我们将 nginx-proxy 移至单独的启动脚本中,并为其中的所有项目添加了网格。

我们的项目有一个网格,平衡器按项目名称有多个网格。 它可以通过域名进一步代理。

我们的请求通过端口 80 上的域发出,并解析到为该域提供服务的容器组中。

使用 Docker 和 Gitlab CI 的开发和测试过程

还有哪些其他问题? 这就是所有容器默认以 root 身份运行的情况。 这与系统的根主机不同。

但是,如果你进入容器,它将是root,我们在这个容器中创建的文件将获得root权限。

如果开发人员进入容器并执行一些生成文件的命令,然后离开容器,那么他的工作目录中有一个他无权访问的文件。

怎么解决呢? 您可以添加将在容器中的用户。

添加用户时出现了什么问题?

在创建用户时,我们常常没有相同的组ID(UID)和用户ID(GID)。

为了在容器中解决这个问题,我们使用ID为1000的用户。

在我们的例子中,这与几乎所有开发人员都使用 Ubuntu 操作系统的事实相吻合。 在 Ubuntu 上,第一个用户的 ID 为 1000。

使用 Docker 和 Gitlab CI 的开发和测试过程

我们有计划吗?

阅读 Docker 文档。 该项目正在积极开发,文档正在更改。 两三个月前收到的数据已经慢慢过时了。

我们解决的一些问题很可能已经通过标准方法解决了。

所以我想更进一步,直接进入编排。

一个例子是 Docker 的内置机制,称为 Docker Swarm,它是开箱即用的。 我想在生产环境中运行一些基于 Docker Swarm 技术的东西。

生成容器使得处理日志变得不方便。 现在日志已被隔离。 它们分散在容器中。 任务之一是通过 Web 界面方便地访问日志。

使用 Docker 和 Gitlab CI 的开发和测试过程

来源: habr.com

添加评论