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

我建议您熟悉Inventos的Alexander Sigachev的报告“使用Docker + Gitlab CI进行开发和测试过程”的抄本。


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


该报告非常适合以结构化的方式告诉您使用Docker和Gitlab CI进行开发和测试的过程。 2017年报告本身。 我认为您可以从这份报告中得出使用的基础知识,方法论,想法和经验。



谁在乎,请在猫下。


我叫Alexander Sigachev。 我为Inventos工作。 我将向您介绍我使用Docker的经验以及我们如何逐步在公司项目中实施它。


主题:使用Docker和Gitlab CI的开发过程。



这是我第二次谈论Docker。 在发布第一份报告时,我们仅在开发机器上的Development中使用了Docker。 使用Docker的员工人数约为2-3人。 渐渐地,经验积累了起来,我们又往前走了一点。 链接到我们的第一份报告


该报告将是什么? 我们将分享有关收集了哪些耙子,解决了哪些问题的经验。 并非到处都是美丽的,但可以继续前进。


我们的座右铭是:Docker化我们手所能到达的一切。



我们要解决什么问题?


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


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


通常,如果您从头开始,那么项目中几乎没有文档。 只有老手才知道如何设置。 员工在一到两天内独立建立工作场所。 为了加快速度,我们使用了Docker。


下一个原因是开发中设置的标准化。 以我的经验,开发人员总是主动。 在第五种情况下,都会输入一个自定义域,例如vasya.dev。 附近的邻居是Petya,其域是petya.dev。 他们正在使用该域名开发网站或系统的某些组件。


当系统增长并且这些域名开始落入配置中时,开发环境就会发生冲突,并且站点路径将被重写。


数据库设置也会发生相同的情况。 有人不担心安全性,而是使用空的root密码。 在安装阶段,有人要求MySQL密码,而密码原来是123。通常会发生数据库配置根据开发人员提交而不断变化的情况。 有人纠正了,有人没有纠正了配置。 当我们在.gitignore进行某种测试配置时,有一些技巧,每个开发人员都必须安装数据库。 这使启动过程变得复杂。 除其他外,您需要记住有关数据库的信息。 必须初始化数据库,必须注册密码,必须注册用户,必须创建板,依此类推。


另一个问题是库的版本不同。 开发人员经常处理不同的项目。 有一个从5年前开始的旧项目(从2017年开始-编者注)。 首先,我们从MySQL 5.5开始。 在一些现代项目中,我们尝试引入更现代的MySQL版本,例如5.7或更早版本(在2017年-注意。Ed。)


使用MySQL的任何人都知道这些库正在提取依赖项。 一起运行两个基地是很成问题的。 至少,老客户连接到新数据库是有问题的。 这又引起了几个问题。


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


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


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


另外,借助Docker,我们能够在登台上利用资源。 每个项目由于其特殊性,都需要进行某些设置。 从物理上讲,有必要选择一个虚拟服务器并分别对其进行配置,或者共享某种环境变量,并且项目可能会相互影响,具体取决于库的版本。



工具类 我们用什么?


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


该报告分为两部分。


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


第二部分将讨论如何与GitLab交互,如何运行测试以及如何在Staging上推广。



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



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


我们从官方来源获取MySQL 5.7.14映像,而Docker Hub保持不变。 从当前目录收集的负责我们的Web应用程序的图像。 他在首次发射时为我们收集了图像。 然后它启动我们在这里执行的命令。 如果返回,将看到已经定义了通过Puma的启动命令。 Puma是用Ruby编写的服务。 在第二种情况下,我们重新定义。 该命令可以是任意命令,具体取决于我们的需求或任务。


我们还将描述将主机上的端口从3000端口容器转发到3000端口容器所需的内容。 这是使用iptables及其自身的机制自动完成的,该机制直接嵌入Docker中。


开发人员可以像以前一样,申请任何可用的IP地址,例如,机器的127.0.0.1本地或外部IP地址。


最后一行说Web容器依赖于db容器。 当我们调用Web容器的启动时,docker-compose将为我们启动数据库。 已经在数据库启动时(实际上,在启动容器之后!这不能保证数据库的就绪性),我们将启动一个应用程序,即后端。


这样可以避免在不引发数据库时发生错误,并可以在我们停止数据库容器时节省资源,从而为其他项目释放资源。



是什么使我们在项目上使用了dockerization数据库。 我们所有人都修复了MySQL的版本。 这样可以避免版本不同,语法,配置和默认设置更改时可能发生的某些错误。 这使您可以为数据库指定通用主机名,登录名和密码。 我们远离了动物园的名称和先前配置文件中的冲突。


我们可以为开发环境使用更优化的配置,这将与默认配置不同。 默认情况下,MySQL是在较弱的计算机上配置的,其开箱即用的性能非常低。



Docker允许您使用所需版本的Python,Ruby,NodeJS,PHP解释器。 我们摆脱了使用某种版本管理器的需要。 以前,Ruby使用rpm软件包,该软件包允许根据项目更改版本。 这也使Docker容器可以平滑地迁移代码并将其与依赖项一起版本化。 我们很容易理解解释器和代码的版本。 要升级版本,请降低旧容器并升高新容器。 如果出现问题,我们可以放下新的容器,抬高旧的容器。


组装完图像后,“开发”和“生产”中的容器将相同。 对于大型安装尤其如此。


在前端,我们使用JavaScipt和NodeJS。


现在我们有了ReacJS的最新项目。 开发人员运行了整个容器,并使用热重装进行了开发。


接下来,启动组装JavaScipt的任务,并通过nginx节省资源给出在静态变量中收集的代码。



在这里,我给出了最后一个项目的图表。


您解决了什么任务? 我们需要构建与移动设备进行交互的系统。 他们获取数据。 选项之一是向该设备发送推送通知。


我们为此做了什么?


我们分为应用程序组件,例如:JS中的admin部分,后端,它通过Ruby on Rails下的REST接口工作。 后端与数据库进行交互。 生成的结果将提供给客户端。 后端和数据库的管理员通过REST界面进行交互。


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


我们开发了这样一个方案:浏览器中的操作员与管理面板进行交互,管理面板与后端进行交互,任务是发送Push通知。


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


正在建立队列,然后按照其自身的机制发送通知。


这里绘制了两个数据库。 目前,在Docker的帮助下,我们使用了2个独立的数据库,它们之间没有任何联系。 此外,它们具有公共虚拟网络,并且物理数据存储在开发人员计算机上的不同目录中。



同样的事情,但数量上。 在这里重用代码很重要。


如果之前我们曾讨论过以库的形式重用代码,那么在此示例中,响应推送通知的服务将被完全用作服务器。 它提供了一个API。 并且已经与我们的新开发进行了交互。


当时,我们使用的是NodeJS版本4。 现在(2017年,请注意。)在最近的开发中,我们使用NodeJS的版本7。 新组件不会吸引新版本的库。


如有必要,您可以重构和升级推送通知服务的NodeJS版本。


而且,如果我们可以保持API兼容性,那么我们可以将其替换为之前使用的其他项目。



您需要添加什么Docker? 将Dockerfile添加到我们的存储库中,该文件描述了必要的依赖关系。 在此示例中,组件按逻辑细分。 这是最少的后端开发人员集。


创建新项目时,创建一个Dockerfile,描述所需的生态系统(Python,Ruby,NodeJS)。 docker-compose描述了必要的依赖关系-数据库。 我们描述了我们需要这样一个版本的数据库,以便将数据存储在某个地方。


我们将单独的第三个容器与nginx一起使用以呈现静态。 您可以上传图片。 后端将它们放置在预先准备好的卷中,该卷也已安装在具有nginx的容器中,该容器可提供静态效果。


为了存储nginx和mysql配置,我们添加了Docker文件夹,其中存储了必要的配置。 当开发人员在自己的机器上建立git clone存储库时,他已经准备好了一个项目,可以进行本地开发。 不会出现该问题,应应用哪个端口或哪个设置。



此外,我们有几个组件:admin,information-API,push-notifications。


为了运行所有内容,我们创建了另一个名为dockerized-app的存储库。 当前,我们在每个组件之前使用几个存储库。 它们在逻辑上只是有所不同-在GitLab中,它看起来像一个文件夹,而在开发人员的机器上,它是特定项目的文件夹。 下面的一层是将要组合的组件。



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


在这里,我们使用了两个docker-compose文件。 这样做是为了能够逐步运行。 当开发人员使用内核时,他不需要Push通知,那么他只需启动docker-compose文件,并相应地节省了资源。


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


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



组件说明。 这是负责收集组件的更高级的文件。 这里有什么特别之处? 在这里,我们介绍平衡器组件。


这是一个现成的Docker映像,其中启动了nginx,还有一个监听Docker套接字的应用程序。 动态,随着容器的打开和关闭,nginx配置将重新生成。 我们通过第三级域名来分配对组件的处理。


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


然后将配置转移到每个项目,并同时启动所有项目。



如果以图形方式描绘,则表明客户端是我们的浏览器或一些我们执行平衡器请求的工具。


域名平衡器确定要访问的容器。


它可以是nginx,它提供了JS管理区域。 它可以是nginx,以加载图像的形式提供给nginx的API或静态文件。


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


在开发人员的机器上,您可以使用知道IP的容器,但是我们基本上不使用它。 实际上不需要直接治疗。



寻找什么样的示例来对应用程序进行docker化? 在我看来,一个很好的例子是MySQL的官方docker镜像。


这很复杂。 有很多版本。 但是它的功能使您能够满足在进一步开发过程中可能出现的许多需求。 如果您花费时间并弄清所有这些过程是如何相互作用的,那么我认为您的自我实现将没有问题。


在hub.docker.com中,通常存在指向github.com的链接,该链接直接提供原始数据,您可以从中自行组装映像。


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


同样在此示例中,也有可能使用环境变量进行配置。 通过在启动单个容器时或通过docker-compose定义环境变量,可以说我们需要为MySQL或我们想要的根目录上的docker设置一个空密码。


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


在我们的项目中,我们稍微统一了负责初始化的Dockerfile。 在那里,我们纠正了我们的需求,仅扩展了应用程序使用的用户权限。 这样一来,将来就可以从应用程序控制台简单地创建数据库。 Ruby应用程序具有创建,修改和删除数据库的命令。



这是一个特定版本的MySQL在github.com上的外观示例。 您可以打开dockerfile并查看安装过程。


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



我们转到第二部分。


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


Gitlab的组件之一是Gitlab CI。 它允许您描述后续命令,随后将使用这些命令来组织代码交付系统或运行自动测试。


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



现在,我们将考虑激活Gitlab CI所需的条件。 为了启动Gitlab CI,我们将.gitlab-ci.yml文件放在项目的根目录下就足够了。


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


我们执行直接调用docker的脚本,组成应用程序的程序集。 这只是一个后端的示例。


接下来,我们说有必要推动迁移以更改数据库并运行测试。


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


部署阶段目前已实现用于登台。 我们没有组织平稳的重启。


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


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


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


更改其他分支时不执行。


可以组织分支上的推广。



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


该实用程序是用Golang编写的。 它是Golang世界中惯用的单个文件,不需要任何依赖关系。


在启动时,我们注册了Gitlab Runner。


我们在Gitlab的Web界面中获得了密钥。


然后我们在命令行上调用init命令。


在对话框模式(Shell,Docker,VirtualBox,SSH)中配置Gitlab Runner


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



它在Web界面中在Gitlab中的外观。 连接GItlab CI之后,将出现一个标志,显示构建的当前状态。


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



我们可以更详细地查看构建。 在这里,我们看到两个状态已经通过。 在登台上测试状态和部署状态。


如果我们单击特定的构建,则将根据.gitlab-ci.yml在过程中启动命令的控制台输出。



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



引入docker时,我们在暂存阶段解决了哪些任务? , , , .


.


Docker-compose .


, Docker . Docker-compose .


, .


— staging .


production 80 443 , WEB.



? Gitlab Runner .


Gitlab Gitlab Runner, - , .


Gitlab Runner, .


nginx-proxy .


, . .


80 , .



? root. root root .


, root , root.


- , , , , .


? , .


, ?


ID (UID) ID (GID).


ID 1000.


Ubuntu. Ubuntu ID 1000.



?


Docker. , . , - , .


, .


.


Docker Docker Swarm, . - Docker Swarm.


. . . web-.


Source: https://habr.com/ru/post/zh-CN449742/


All Articles