作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
安东尼Reversat
验证专家 在工程

Antoine是一名软件开发人员,在Meta和育碧等公司拥有超过18年的经验. 他在Linux系统管理方面具有深厚的专业知识, DevOps, 和Python, 并为几个组织开发了CI/CD管道.

阅读更多

以前在

Meta
分享

持续部署(CD)是将新代码自动部署到生产环境的实践. 大多数持续部署系统通过运行单元和功能测试来验证要部署的代码是可行的, 如果一切顺利的话, 展开部署. rollout本身通常分阶段进行,以便在代码没有按照预期运行时能够回滚.

关于如何使用各种工具(如AWS堆栈)实现自己的CD管道的博客文章比比皆是, 谷歌云堆栈, Bitbucket都管道, 等. 但是我发现它们中的大多数都不符合我对一个好的CD管道应该是什么样子的想法:首先构建的管道, 并且只测试和部署单个构建文件.

在本文中, 我将构建一个事件驱动的持续部署管道,它首先构建,然后在部署最终工件上运行测试. 这不仅使我们的测试结果更加可靠,而且使CD管道易于扩展. 它看起来是这样的:

  1. 向源存储库提交.
  2. 这将触发相关映像的构建.
  3. 测试在构建的工件上运行.
  4. 如果一切正常,则将映像部署到生产环境中.

本文假设您至少对Kubernetes和容器技术有一定的了解, 但是如果你不熟悉或者需要复习一下, 看到 Kubernetes是什么? 容器化和部署指南.

大多数CD设置的问题

这是我对大多数CD管道的问题:它们通常在构建文件中执行所有操作. 我读过的大多数关于这一点的博客文章都会在他们拥有的构建文件中有以下顺序的一些变化(cloudbuild.yaml 对于Google 云构建, bitbucket-pipeline.yaml Bitbucket都).

  1. 运行测试
  2. 构建图像
  3. 将映像推送到容器仓库
  4. 用新映像更新环境

你不能在最终的藏物上做测试.

通过按此顺序执行操作,您可以运行测试. 如果它们成功了,您将构建映像并继续进行管道的其余部分. 如果构建过程更改了映像,导致测试无法通过,会发生什么? 在我看来, 您应该从生成工件(最终的容器映像)开始,并且该工件在构建和部署到生产环境之间不应该更改. 这确保了您所拥有的关于工件的数据(测试结果、大小等)始终是有效的.

您的构建环境拥有“通往王国的钥匙”.”

通过使用构建环境将映像部署到生产堆栈, 您实际上允许它改变您的生产环境. 我认为这是一件非常糟糕的事情,因为任何对源存储库有写访问权限的人现在都可以对您的生产环境做任何他们想做的事情.

如果最后一步失败,您必须重新运行整个管道.

如果最后一步失败(例如, 由于凭证问题,您必须重新运行整个管道, 占用了本可以用在其他事情上的时间和其他资源.

这就引出了我的最后一点:

你的步骤不是独立的.

从更普遍的意义上说, 拥有独立的步骤可以让您在流程中拥有更大的灵活性. 假设您想要向管道中添加功能测试. 通过将您的步骤放在一个构建文件中, 您需要让您的构建环境启动一个功能测试环境,并在其中运行测试(很可能是顺序的)。. 如果你的步骤是独立的, 您可以让单元测试和功能测试都由“image built”事件启动. 然后它们将在自己的环境中并行运行.

我理想的CD设置

在我看来, 解决这个问题的一个更好的方法是通过事件机制将一系列独立的步骤连接在一起.

与之前的方法相比,这有几个优点:

您可以对不同的事件采取几个独立的操作.

如上所述, 新映像的成功构建只会发布一个“成功构建”事件. 反过来,我们可以在触发此事件时运行几个东西. 在我们的例子中,我们将启动单元和功能测试. 您还可以考虑在触发构建失败事件或测试未通过时提醒开发人员.

每个环境都有自己的一组权限.

通过让每一步都发生在自己的环境中, 我们消除了在单一环境中拥有所有权利的需要. 现在构建环境只能构建, 测试环境只能测试, 而部署环境只能部署. 这让你有信心,一旦你的形象建立起来,它就不会改变. 生成的工件将最终出现在您的生产堆栈中. 它还允许更容易地审计管道的哪个步骤正在做什么,因为您可以将一组凭据链接到一个步骤.

有更多的灵活性.

希望在每次成功构建时向某人发送电子邮件? 只需添加一些对该事件作出反应并发送电子邮件的内容. 这很容易——您不需要更改构建代码,也不需要在源存储库中硬编码某人的电子邮件.

重试更容易.

具有独立的步骤还意味着,如果一个步骤失败,您不必重新启动整个管道. 如果失败情况是临时的或已手动修复, 您可以重试失败的步骤. 这允许更高效的管道. 当构建步骤需要几分钟时, 不必仅仅因为忘记为部署环境提供对集群的写访问权限而重新构建映像,这是件好事.

实现Google云持续部署

谷歌云平台 在很短的时间内用很少的代码构建这样一个系统所需的所有工具都具备了吗.

我们的测试应用程序是一个简单的Flask应用程序,它只提供一段静态文本. 该应用程序被部署到Kubernetes集群中,该集群为更广泛的互联网提供服务.

我将实现前面介绍的管道的简化版本. 我基本上删除了测试步骤,所以现在看起来像这样:

  • 对源存储库进行新的提交
  • 这将触发映像构建. 如果成功,则将其推送到容器存储库,并将事件发布到Pub/Sub主题
  • 一个小脚本将订阅该主题并检查图像的参数—如果它们与我们所要求的匹配, 部署到Kubernetes集群.

这是我们管道的图形表示.

管道的图形表示

流程如下:

  1. 有人提交到我们的存储库.
  2. 这会触发一个基于源存储库构建码头工人镜像的云构建.
  3. 云构建将映像推送到容器存储库,并将消息发布到云pub/sub.
  4. 这将触发一个云函数,该函数检查已发布消息的参数(构建状态), 生成的映像的名称, 等.).
  5. 如果参数正确,云功能将使用新映像更新Kubernetes部署.
  6. Kubernetes使用新映像部署新容器.

源代码

我们的源代码 是一个非常简单的Flask应用程序,只是提供一些静态文本. 下面是我们项目的结构:

├──码头工人
│├──码头工人file
│├──├─├.ini
├── k8s
│├──├─├.yaml
│├──├─├.yaml
├──许可证
├──Pipfile
├──Pipfile.锁
└──src
    └──主要.py

码头工人目录包含构建码头工人镜像所需的所有内容. 这个图像是基于 uWSGI和Nginx镜像 然后安装依赖项并将应用复制到正确的路径上.

k8s目录包含Kubernetes配置. 它由一个服务和一个部署组成. 部署将基于从中构建的映像启动一个容器 码头工人file. 然后,该服务启动一个具有公共IP地址的负载均衡器,并重定向到应用程序容器。.

云构建

云构建配置本身可以通过云控制台或Google cloud命令行完成. 我选择使用云控制台.

云控制台的屏幕截图

在这里, 我们为任何分支上的任何提交构建映像, 但是你可以在开发和生产中使用不同的图像, 例如.

如果构建成功, 云构建将自己将映像发布到容器注册中心. 然后,它将向cloud-builds发布/订阅主题发布消息.

云构建还会在构建进行中和构建失败时发布消息, 所以你也可以让东西对这些信息做出反应.

云构建的发布/订阅通知的文档是 在这里 并且可以找到消息的格式 在这里

云Pub / Sub

如果您查看云控制台的云pub/sub选项卡, 您将看到云构建创建了一个名为云构建的主题. 这是云构建发布其状态更新的地方.

Pub/Sub项目截图

云计算功能

我们现在要做的是创建一个云函数,在发布到cloud-builds主题的任何消息上触发该函数. 同样,您可以使用云控制台或Google cloud命令行实用程序. 在我的例子中,我所做的是在每次有更改时使用云构建来部署云功能.

云功能的源代码是 在这里.

让我们首先看看部署这个云功能的代码:

步骤:
- name: 'gcr ..io / cloud-builders gcloud”
  id:“测试”
  参数:[“功能”,
  “部署”,
  “new-image-trigger”,
  “——运行时= python37”,
  “——trigger-topic = cloud-builds”,
  “——入口点= onNewImage”,
  “——地区= us-east1”,
  ”——源= http://source.开发人员.谷歌.com/projects/ PROJECT_ID /回购/美元REPO_NAME ']

这里,我们使用Google Cloud 码头工人镜像. 这样可以很容易地运行GCcloud命令. 我们正在执行的相当于直接从终端运行以下命令:

gcloud函数部署new-image-trigger——runtime=python37——trigger-topic=cloud-builds——entry-point=onNewImage——region=us-east1——source=http://source.开发人员.谷歌.com/projects/ PROJECT_ID /回购/ REPO_NAME美元

我们要求Google Cloud部署一个将使用Python 3的新的云函数(或者替换该区域中已经存在的同名函数).并将由cloud-builds主题中的新消息触发. 我们还告诉Google在哪里可以找到该函数的源代码(这里PROJECT_ID和REPO_NAME是由构建过程设置的环境变量). 我们还告诉它调用哪个函数作为入口点.

作为旁注, 为了使它工作, 您需要为cloudbuild服务帐户赋予“云功能开发人员”和“服务帐户用户”,以便它可以部署云功能.

下面是一些注释过的云函数代码片段

入口点数据将包含在发布/订阅主题上接收到的消息.

defonnewimage (data, context):

第一步是从环境中获取特定部署的变量(我们通过修改云控制台中的云功能来定义这些变量).

    项目= OS.环境.get(“项目”)
    Zone = OS.环境.get(“区域”)
    集群= OS.环境.get(集群)
    部署=操作系统.环境.get(部署)
    Deploy_image = OS.环境.get('图像')
    Target_container =操作系统.环境.get(容器)

我们将跳过检查消息结构是否符合预期的部分,并验证构建是否成功并生成了一个映像工件.

下一步是确保所构建的映像是我们想要部署的映像.

    形象= decoded_data['结果']['图像'][0](“名字”)

    Image_basename = image.split(“/”)[1].split(“:”)[0]
    如果image_basename != 部loy_image:
        日志记录.错误(f'{image_basename}与{部loy_image}不同')
        返回

现在,我们获得一个Kubernetes客户端并检索我们想要修改的部署

    V1 = get_kube_client(项目、分区、集群)
    Dep = v1.read_namespaced_部loyment(部署,“违约”)
    如果deep为None:
        日志记录.错误(“没有名为{部loyment}的部署”)
        返回

Finally, we patch the 部loyment with the new image; Kubernetes will take care of rolling it out.

    对于i,容器在enumerate(深度).规范.template.规范.容器):
        如果容器.名称== target_container:
            部.规范.template.规范.容器(我).Image =图像
    日志记录.info(f“更新到{图像}”)
    v1.Patch_namespaced_部loyment(部署,'default', 部)

结论

这是一个非常基本的例子,说明我喜欢如何在CD管道中构建东西. 您可以通过更改pub/sub事件触发的内容来执行更多步骤.

例如, 您可以运行在映像内运行测试的容器,在成功时发布一个事件,在失败时发布另一个事件,并通过更新部署或根据结果发出警报来对这些事件作出反应.

我们构建的管道非常简单, 但是您可以为其他部分编写其他云函数(例如, 一个云功能,它会向提交了破坏单元测试代码的开发人员发送电子邮件).

如你所见, 我们的构建环境不能改变Kubernetes集群中的任何东西, 而且我们的部署代码(云功能)不能修改所构建的映像. 我们的特权分离看起来不错, 我们可以睡个安稳觉,因为我们知道流氓开发者不会搞垮我们的生产集群. 我们也可以给予我们的更多 ops-oriented开发者 访问云功能代码,以便他们可以修复或改进它.

如果您有任何问题,评论或改进,请随时在下面的评论中与我们联系.

了解基本知识

  • 持续交付和持续部署之间的区别是什么?

    持续交付是一种实践,旨在能够在任何时间点交付稳定的软件. 持续部署则更进一步,自动部署上述软件(通常在软件即服务环境中)。.

  • 什么是部署管道?

    部署管道是一组软件,它允许开发人员从应用程序的源代码转到在生产环境中运行的应用程序. 这通常包括构建、测试和部署应用程序的软件.

  • 传统的持续部署管道有什么问题?

    大多数传统的持续部署管道依赖于将所有信息放在同一位置,包括将新工件推向生产环境的凭据.

  • 我们如何构建更好的持续部署管道?

    在理想的情况下, 管道的所有不同部分都应该是独立的,并且应该使用某种类型的事件机制在前一个步骤成功时启动一个步骤.

聘请Toptal这方面的专家.
现在雇佣
安东尼Reversat

安东尼Reversat

验证专家 在工程

加拿大蒙特利尔,QC

2018年9月10日成为会员

作者简介

Antoine是一名软件开发人员,在Meta和育碧等公司拥有超过18年的经验. 他在Linux系统管理方面具有深厚的专业知识, DevOps, 和Python, 并为几个组织开发了CI/CD管道.

阅读更多
作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

以前在

Meta

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.