CircleCI-博客中文翻译-四-
CircleCI 博客中文翻译(四)
CircleCI API v2 简介
今天,我们宣布我们 API 的 v2,它开放了九个新的稳定端点,允许控制管道和工作流。v2 API 的预览版已经发布了几个月,随着时间的推移,我们将继续添加新的端点,包括一些目前作为不稳定预览版提供的端点。
在这篇文章中,我将概述 v2 API 中的第一个端点,它们现在对于您的生产使用是稳定的。
管道现在在 API 中是一流的
2019 年 3 月,我们宣布将“构建处理”简单更名为“管道”,并告诉你,在你再次考虑管道之前,还需要一段时间。从那时起,你们中的许多人已经看到管道出现在 v2 API 的预览版和我们新的 UI 预览版中。通过 v2 API,我们扩展了管道,为您提供了触发和检索管道的编程方式。
管道封装了你在 CircleCI 上运行的东西。当您通过对 git 存储库的提交或对项目的 API 调用来触发管道时,该管道表示您在 CircleCI 中执行工作的请求的开始,并持久保存一个稳定的管道 ID 以及关于触发原因、我们将把管道的运行归因于谁的参与者以及用于在管道中运行工作流的配置的信息。
管道是 CircleCI 要求的工作单元。每一个都是在给定的项目、给定的分支(您的默认分支作为默认值应用)上由具有特定配置的特定参与者触发的(或者以隐含的方式检索上游配置,就像我们历史上的 GitHub 集成一样)。
如果你在 2018 年 9 月之后添加你的项目,你一直在使用管道,甚至可能没有意识到这一点。
“项目 _slug”
CircleCI v2 API 为您的项目使用一个与其上游存储库相对应的标识符。例如,如果您想从 CircleCI 获取关于 GitHub 存储库https://github.com/CircleCI-Public/circleci-cli的信息,您可以在 CircleCI API 中将其称为gh/CircleCI-Public/circleci-cli
,它是项目类型、您的“组织”名称和存储库名称的“三元组”。对于项目类型,您可以使用github
或bitbucket
以及更短的形式gh
或 bb
,这些现在在 API v2 中都得到了支持。组织是您在版本控制系统中的用户名或组织名称。
在 API v2 中,我们引入了称为 project_slug 的三元组的字符串表示,其形式为:<project_type>/<org_name>/<repo_name>
。当提取有关项目的信息以及通过 ID 查找管道或工作流时,project_slug 包含在有效负载中。然后可以使用 project_slug 来获取关于项目的信息。将来我们有可能改变 project_slug 的形状,但是在任何情况下,它都可以作为给定项目的可读标识符。
管道参数
v2 API 现在允许您向管道触发器添加参数。
要使用参数触发管道,您需要在 POST 主体中传递 JSON 包中的parameters
键。对于上面的配置,这可能类似于:
curl -u ${CIRCLECI_TOKEN}: -X POST --header "Content-Type: application/json" -d '{
"parameters": {
"workingdir": "./myspecialdir",
"image-tag": "4.8.2"
}
}' https://circleci.com/api/v2/project/:project_slug/pipeline
与 1.1 版作业触发端点中直接注入到正在运行的作业环境中的参数不同,管道参数在配置处理过程中解析,使它们可用于配置的大多数部分。
在.circleci/config.yml
文件的顶层关键字中使用parameters
节声明管道参数。然后在pipeline.parameters
范围内引用参数的值。
下面的示例显示了一个带有两个管道参数的配置,image-tag
和workingdir
,这两个参数都用在后续的配置节中:
version: 2.1
parameters:
image-tag:
type: enum
default: "latest"
enum: ["latest","bleeding","stable201911"]
# NOTE use of enum allows only the options you want
# You could, instead, choose to make it an arbitrary string
workingdir:
type: string
default: "main-app"
jobs:
build:
docker:
- image: circleci/node:<< pipeline.parameters.image-tag >>
environment:
IMAGETAG: << pipeline.parameters.image-tag >>
working_directory: /Code/<< pipeline.parameters.workingdir >>
steps:
- run: echo "Image tag used was ${IMAGETAG}"
- run: echo "$(pwd) == << pipeline.parameters.workingdir >>"
请记住,如果您声明了一个管道参数,那么任何拥有触发凭证(例如,一个与拥有上游 repo 提交权限的用户相关联的 API 令牌)的人都能够有效地将该值注入到您的配置中。在上面的例子中,如果你不想允许 docker 标签的注入,你就不要使用这个技术。
使用管道参数的条件工作流
我们在一个工作流声明下添加了一个新的when
子句(我们也支持反向子句unless
),用一个布尔值来决定是否运行该工作流。
这种构造最常见的用途是使用管道参数作为值,允许 API 触发器传递该参数来确定要运行哪些工作流。
下面是使用两个不同管道参数的配置示例,一个用于驱动特定工作流是否运行,另一个用于确定特定步骤是否运行。
version: 2.1
parameters:
run_integration_tests:
type: boolean
default: false
deploy:
type: boolean
default: false
workflows:
version: 2
integration_tests:
when: << pipeline.parameters.run_integration_tests >>
jobs:
- mytestjob
jobs:
mytestjob:
steps:
- checkout
- when:
condition: << pipeline.parameters.deploy >>
steps:
- run: echo "deploying"
九个新端点
今天发布的 v2 中的明星是 CircleCI API v2(管道和工作流)中第一组端点的稳定性和普遍可用性。
以下管道端点分别允许您,
- 触发项目分支上的管道,并接收其 ID 供以后使用
- 检索给定项目的最新管道
- 筛选出请求者最近运行的管道
POST /project/{project-slug}/pipeline
GET /project/{project-slug}/pipeline
GET /project/{project-slug}/pipeline/mine
你也可以
- 按 ID 检索单个管道
- 检索管道的工作流
- 检索用于运行管道的源配置和已处理配置
GET /pipeline/{pipeline-id}
GET /pipeline/{pipeline-id}/workflow
GET /pipeline/{pipeline-id}/config
其他三个新的端点允许您
- 检索单个工作流
- 检索单个工作流的作业
- 还有一个取消工作流的端点
GET /workflow/{id}
GET /workflow/{id}/job
POST /workflow/{id}/cancel
CircleCI API v2 支持 OpenAPI
有了 API v2,我们现在完全支持 OpenAPI 3 。您可以从这里下载反映当前生产 API 的实时规范:
了解更多信息
通过设置工作流| CircleCI 介绍动态配置
原文:https://circleci.com/blog/introducing-dynamic-config-via-setup-workflows/
通过设置工作流的新版本动态配置,CircleCI 客户现在可以使用作业和工作流,不仅可以执行工作,还可以确定他们想要运行的工作。
我们构建了动态配置,因为我们知道我们的用户希望在 CircleCI 构建过程中有更多的动态性。从历史上看,我们的平台是非常确定的:配置是根据给定管道的版本预先设置在一个文件中的。然而,这种确定性导致用户在他们想要的时候运行他们想要的东西的灵活性降低。
过去,客户试图通过使用预提交挂钩中的脚本在提交前生成配置,或者通过 API 触发管道运行的作业来设置管道参数,从而解决 CircleCI 配置的静态特性。虽然这些解决方案有效,但它们经常涉及不容易解决的奇怪的变通办法或边缘情况。
我们如何启用动态配置
意识到作业和工作流是使我们的配置更加动态的最佳方式之一,我们创建了一个特殊的工作流,称为“设置工作流”此工作流可以包含通过计算管道参数或生成 YAML 配置来“设置”管道的作业。然后,可以在设置工作流结束时将这些参数和配置传递到 CircleCI 命令/作业中,并将该配置中定义的任何工作流或作业添加到同一管道中。
该功能最令人兴奋的部分之一是,因为设置工作流是普通的 CircleCI 配置,所以您可以使用 orb 来合并常见类型的逻辑。我们目前正在为这个用例开发几个球体。例如,我们已经创建了一个路径过滤 orb ,它运行 git 命令来分析哪些文件在提交中发生了更改,并使用正则表达式来设置可用于过滤工作流或步骤的管道参数。阅读我们的文档了解更多关于动态配置的具体功能。
下一步是什么
我们期待使用动态配置和 orbs 作为帮助客户解决不同用例的方法。我们正在考虑的一些想法:
- 一个 orb,允许用户组合配置,使得分解大型配置文件成为可能
- 解释构建工具输出的球体,如 Bazel 或 Lerna
- 一个能让你运行。特定分支的 circleci/config.yml(就像您的默认分支),而不管触发管道的分支或 PR。
- 定义 SDK 的 orb,这样您就可以使用 TypeScript 编写配置
前往我们的社区论坛,讨论,让我们知道您如何使用动态配置,并查看本教程以了解如何将动态配置整合到您的 CI/CD 管道中。
介绍我们重新设计的 UI | CircleCI
原文:https://circleci.com/blog/introducing-our-redesigned-UI-built-for-increased-user-productivity/
我们正在推出一个新的用户界面,它有一个以管道为中心的仪表板、一个改进的作业详细信息页面和一个更新的配置视图,以便于调试。在 2020 年春季面向所有用户推出之前,现在就选择试用新的用户界面!
在过去的一年里,我们的团队在广泛的用户反馈的帮助下彻底改造了我们的前端。到今年春天,我们计划让所有用户都使用我们的新 UI。
当我们构建新的 UI 时,我们不断地问自己,“这里要做的工作是什么?”或者说,我们用户的目标是什么,我们如何帮助他们实现这些目标?
为了寻找答案,我们进行了近 100 次采访,阅读了 12,174 份应用内调查回复(还在继续),参与了大量 CircleCI 讨论帖子和推文,并对我们的 MVP 替代方法WAFL——架构良好、功能有限的进行了测试。
接下来是对新用户界面主要变化领域的概述,以及对这些变化背后的思考过程的解释。
为什么我们要重建用户界面?
旧的 UI 是为 CircleCI 1.0 的基于作业的世界和基于容器的使用模型而设计的。运行层次结构的不同级别之间的导航对于许多用户来说是混乱的。
但也许更重要的是,我们的代码库已经经历了这么多代开发人员和框架,以至于高速迭代变得越来越困难。即使是很小的改变也需要大量的时间。
因此,我们组建了一个团队,同时从用户体验和代码基础架构两方面着手。
用户界面的主要变化是什么?
CircleCI 用户要做的主要工作是定位和修复应用程序中的错误,同时还要做一项相关的工作,使每一个好的、经过验证的更改都能够无缝地发送给用户。这是一个大的、广泛的目标,我们只能通过将每个目标分解成具体的功能性工作(主要工作的实际需求)来实现它们,同时在我们的后端和 API 的约束下工作。
为了更好地支持用户在应用中最常见的目标,我们对用户界面进行了三大改进。
1.创建了一个以管道为中心的仪表板
对于与 CircleCI 仪表板交互的用户来说,三个最常见的目标是:
1。从特定的 CircleCI 作业导航到该作业的最近一次运行。
2。从失败的运行导航到最近通过的管线运行。
3。快速检查手头的项目是否健康。
当用户试图完成这三个目标时,我们以前的以工作为中心的仪表板产生了不必要的问题。
例如,如果您在一个工作流中有 20 个作业,而您组织中的其他用户经常运行他们自己的作业,那么运行将会很快分散,并且很难找到您正在寻找的确切的作业(在确切的运行中)。
也很难判断这个项目是否健康。比方说,每次运行中有 9/10 的工作通过。在旧的 UI 中,如果你看一下仪表板,它可能看起来像是一个非常健康的项目,而实际上每个管道(围绕这些作业的信封)可能都失败了。
我们的新用户界面包含了一个以管道为中心的仪表板。管道表示在您触发项目工作时运行的整个配置,它可能包括多个工作流中包含的多个作业。
通过将单个管道中的所有作业打包在一起,可以更容易地导航到您最近的运行或最近经过的运行。在一个项目中,扫描大多数运行是通过还是失败也更容易。
2.设计了更高效的工作详情页面
我们几乎所有用户的核心目标是找出特定作业失败的原因并修复它。为此,用户通常会直接导航到作业详细信息页面,找出问题所在。
当我们设计新的 UI 时,我们希望让用户更容易阅读任务的步骤输出或测试摘要,以便更好地识别错误的来源。以下是我们简化从作业详细信息页面查找和修复错误的一些方法:
-
将标准输出或测试输出在页面上向上移动。我们已经减少了 STDOUT 之前页面顶部的屏幕空间,这通常是用户识别错误来源所需的唯一信息。
-
删除不太相关和很少使用的信息。这包括排队时间、参数选项卡、计时选项卡、页面顶部的横幅等等。虽然数量有限的用户认为信息很重要,但绝大多数用户并不这样认为。
-
使 STDOUT 框跳转到输出的底部。大多数时候,用户需要的信息在 STDOUT 的末尾或接近末尾。这有助于他们更快地找到它。
-
包含对长标准输出框的屏幕高度限制。之前,我们注意到在观察用户会话时,一些用户会深入到 a STDOUT 中,以至于忘记了他们正在进行的步骤。限制 STDOUT 的高度可以更快地加载页面,并有助于使页面更易于管理。
-
突出显示 Bash 命令和错误代码。 Bash 命令和错误代码现在可以在一个小灰框中找到,帮助用户快速区分。
-
增加了一个弹出标签来全屏查看标准输出。这有助于用户不受干扰地浏览,并在与他人分享时轻松链接到失败的步骤。
-
在作业详细信息页面中添加了从失败重新运行。以前,用户只能从作业页面重新运行整个工作流,而现在他们可以从失败的作业重新运行工作流。
-
将选择工具默认设置为仅显示失败的并行运行。用户很少查看通过的并行运行,因此我们决定让选择工具默认只显示失败的并行运行,以帮助用户更快地找到他们正在寻找的信息。
-
从文件夹中取出未嵌套的工件。我们看到许多用户点击多个文件夹来获得一个工件,有时当他们点击所有的工件时,忘记了他们需要哪个工件。开发人员喜欢 CNTL+F,通过解除构件嵌套,我们使他们能够直接使用它。
3.为调试重建了配置视图
我们在旧 UI 的 configuration 选项卡上确定了两个主要的用户目标:
- 配置选项卡上的主要用户目标是将最近通过的运行与最近失败的运行进行比较,以识别变化并调试错误。这通常在管道列表中完成。
- 第二个用户目标是在配置和输出之间创建一个思维图,并将预期结果与实际结果进行比较,以调试错误。这最好在工单详情页面完成。
因为用户依赖两个不同的位置来完成这些目标,所以我们将配置从 Job Details 页面上的选项卡更改为全屏、类似模块的视图,可以在新 UI 的任何级别打开。将来,我们还希望有一个“比较配置”功能,以便更直接地修复这些错误。
同样值得注意的是:用户几乎总是通过源配置(你写的)调试,很少通过编译配置(后处理)调试。
然而,Job Details 页面先前首先显示编译后的配置,下面的源配置是灰色的。我们在新的 UI 中完全改变了这一点。现在,在弹出的配置页面中,顶部有两个按钮可以将您的视图从源代码更改为已编译的,源代码设置为默认设置。这有助于用户轻松查看最相关的信息,并且无需滚动即可查看其源代码和编译后的配置。
准备好加入了吗?
在接下来的几个月里,我们将逐步向所有用户推出新的用户界面。
我们鼓励您选择测试新的用户界面,并在整个过程中与我们分享您的反馈。
您可以选择加入新的用户界面,方法是导航到任何支持管道处理的项目的作业详细信息页面,然后单击页面顶部的横幅:
—
在探索新的用户界面时,请向我们提供您的反馈!你可以发起 CircleCI 讨论帖子、tweet in me,或者注册一个长达一小时的用户会议。
CircleCI server 3.x 简介:针对自托管 CircleCI 安装的企业级 Kubernetes-circle ci
CircleCI server 3.x 现已面向所有 CircleCI 客户推出,旨在满足最严格的安全性、合规性和监管限制。这种自托管解决方案可以在繁重的工作负载下扩展,所有这些都在您的团队的私有网络上的 Kubernetes 集群中,但具有 CircleCI 的动态扩展云体验。
提供卓越的现场服务意味着通过最新版本保持服务的更新。在幕后,我们一直在开发流程,使我们能够快速地将新功能和创新从我们的云产品中引入服务器,以便所有服务器客户都能充分利用 CircleCI。
CircleCI server 3.x 的企业级安全性
软件开发中的网络安全比以往任何时候都更加重要。对一些人来说,这意味着保护云存储容器。对其他人来说,这意味着在防火墙后面加倍努力。Server 3.x 通过对 CircleCI 安装的端到端控制,使客户能够达到最严格的安全要求。客户在他们自己的控制平面中托管 CircleCI 服务和应用程序,将所有信息保存在他们的专用网络中。Server 3.x 还通过了顶级安全公司的第三方渗透测试。
强大的开发工具和功能
server 3.x 最令人兴奋的方面是,客户将能够比以前更快地访问最新的 CircleCI 功能:现在我们的自托管客户可以受益于与使用我们的云产品的客户相同的功能。这包括管道、orb、矩阵作业、预定工作流等等。这些只是已经可用的功能中的一部分——在未来的版本中,客户将可以使用其他增强功能,如适用于 macOS 的 runner 版本、observability dashboards、insights 和设置工作流。
行业领先的维护和监控
Server 3.x 允许客户使用 Grafana、Prometheus、Loki 等标准工具来监控他们的安装。团队还可以通过 Telegraf 集成他们自己的监控解决方案。
对于拥有内部 DevOps 和 Kubernetes 专业知识、拥有自己的数据中心或具有其他地方无法满足的高度定制需求的团队来说,最新的服务器版本是一个理想的解决方案。CircleCI 服务器可以在 Google Kubernetes 引擎(GKE)、Amazon Elastic Kubernetes 服务(EKS)或本地 Kubernetes 安装上运行。
要了解更多关于 server 3.x 的信息,请访问我们的文档。如果您目前是 CircleCI server 的客户,请联系您的客户成功经理以了解任何问题或安排一次指导迁移。
通过片状测试检测引入测试洞察力| CircleCI
原文:https://circleci.com/blog/introducing-test-insights-with-flaky-test-detection/
CircleCI Insights 仪表板旨在帮助您提高交付效率。一年前,我们推出了仪表板,为团队提供可操作的数据来优化您的渠道。
从那以后,我们一直在听取您的反馈。到目前为止,要求最多的功能是能够进一步了解测试性能。这就是为什么我们正式向所有 CircleCI 云客户发布测试见解,并计划在未来几个月内扩展到服务器客户。
测试洞察提供了给定测试套件中最失败、最慢和最脆弱的测试的可见性,以及:
- 最近工作流程的扩展回顾窗口
- 对测试套件性能的高层次总结,提供了关键知识的概览
- 跨开发分支测试性能的能力
您的团队可以在您最近的执行中分析测试性能,并确定优化的机会。通过 Insights 仪表板访问该功能,并直接通过测试视图访问正在运行或已完成的管道。
</blog/media/2021-10-25-Home-to-test.mp4>
Home to test view
片状测试检测
Test Insights 包括一个新的检测和分析层,专门用于不确定性失败的测试。这些类型的测试被标记为“古怪的”,使得它们易于在 Insights 仪表板上扫描。
</blog/media/2021-10-25-Test-Insights.mp4>
Test Insights
洞察力的下一步是什么
我们相信测试性能的可观察性对所有开发人员来说都是无价的,这也是我们在这一领域投入巨资的原因。
我们目前正在探索对 Insights 的这些改进:
- 扩展数据回顾以支持尽可能多的最近管道执行
- 面向服务器客户的测试洞察
- 通过 Insights API 导入所有指标和数据
- 片状检测改进,可能包括手动贴标和定制模型支持
- 测试文件聚合和优化建议
我们继续依靠您的反馈来改进。请通过 Canny 提交您的想法,让我们知道您如何使用 Insights 和您希望看到的新功能。
CircleCI 2.0 工作流程简介- CircleCI
原文:https://circleci.com/blog/introducing-workflows-on-circleci-2-0/
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
TL;DR: CircleCI 2.0 现在随着工作流的发布,将您的构建、测试和部署阶段识别为单独的作业。使用工作流的团队现在将能够控制他们开发过程的每个阶段,以从开始到结束匹配他们的理想过程。
当前的 2.0 用户可以访问我们的文档,立即开始使用工作流。
不基于 CircleCI 2.0 构建?立即注册获取访问权限。
CircleCI 2.0 现在随着工作流的发布,将构建-测试-部署识别为单独的作业。用户现在可以完全灵活地控制流程中每个步骤的执行方式。
在 CircleCI 1.0 上,用户受到以锁定方式执行作业的约束。当测试失败时,用户需要从第一步重新运行构建,导致时间浪费。工作流允许开发人员准确指定每个作业的运行时间、运行条件和运行方式,从而为他们提供了控制和灵活性。
为什么选择工作流?
分离和协调作业的能力是我们的用户最需要的功能。每个团队从代码到生产的理想流程都不一样。工作流使团队能够在他们的工作配置中反映他们的理想过程,因此您的更改如何从提交到测试再到试运行或部署完全在您的控制之下。我们知道,将代码转化为软件不是一个一刀切的过程,每个团队都应该有自己的软件开发工作流,他们可以根据自己的需求定义工作流。您可能已经看到了类似的概念,称为“管道”或“构建阶段”
我可以用工作流做什么?
工作流支持许多您现在就可以开始使用的配置:
顺序作业执行:作为单独的作业顺序运行构建、测试和部署,并从每个作业中获得更快的反馈。CircleCI 1.0 按顺序锁步运行构建-测试-部署任务。
并行作业执行:用于精细构建协调、更快的构建、并行运行多个作业的能力,以及在部署之前解决所有相关依赖关系的方法。并行作业执行还支持构建矩阵,这允许您同时针对一种语言的多个版本进行构建,以获得更快的反馈。
repo 中的扇入/扇出:非常适合更复杂的构建流程编排,以及希望针对提交运行不同测试套件以获得更快反馈的团队。允许您并行运行多个作业,从而产生一个单一作业(反之亦然,从一个单一作业到多个作业)。
针对作业执行的分支级过滤:允许您将代码部署到特定的分支。
从失败开始执行作业和从开始执行作业:选择重新运行构建的位置和方式,以保存重复的不必要作业。这允许您从上一个失败的作业开始作业,而不是重新运行每个作业或阶段。
开始构建工作流:
CircleCI 2.0 用户现在就可以开始使用工作流。点击此处阅读更多信息并查看示例配置。
更新:截至 2017 年 7 月 11 日,CircleCI 2.0 已上线并公开发布。点击了解更多信息。
IP 范围-更好的安全性,更大的信心| CircleCI
本文由 Sebastian Lerner 和 Radhika Gulati 共同撰写。
注意: 启用了 IP 范围功能的作业使用每 GB 数据需要 450 信用点。
今天,我们宣布我们的最受欢迎的功能请求之一,IP 范围,现已面向 CircleCI 云客户全面开放。此功能通过限制与其基础架构通信的连接,使团队能够满足法规遵从性要求。
没有公司愿意让整个互联网访问他们的工件库或其他敏感环境。有了 IP 范围,团队就可以只对 CircleCI 开放基于 IP 的防火墙。
借助 IP 范围,使用 Docker executor 的性能和规模计划中的客户可以通过与 CircleCI 可验证关联的 IP 地址路由作业流量。我们很自豪成为首批提供此功能的 CI/CD 平台之一。
更好的安全性,更大的信心
IP 范围功能允许 CircleCI 客户满足其基于 IP 的合规性要求,同时仍然受益于在 CircleCI 云上运行的强大计算和无缝作业编排。
当通过配置文件在 Docker 作业上启用 IP 范围时,CircleCI 会通过文档中列出的大约 30 个 IP 地址中的一个来重新路由任何传出的通信。这一特性让团队确信流向其私有环境的流量来自 CircleCI。
可能需要基于 IP 的受限访问的一些示例包括:
- 访问私有工件库
- 部署包含敏感数据的内部应用程序
- 从防火墙依赖性管理器中提取依赖性
- 在内部环境中运行测试用例
启用 IP 范围功能的作业所使用的每 GB 数据需要 450 个配额。要查看 IP 范围内的数据使用情况,请访问 CircleCI UI 中“计划使用情况”页面内的“IP 范围”选项卡。
了解有关 IP 范围的更多信息
查看我们的文档了解更多有关如何在作业中启用 IP 范围的信息。访问 Canny 浏览或提交新想法,并前往我们的社区论坛讨论,给我们关于 IP 范围的反馈。
Arm 是云计算的未来吗?圆环
Arm 架构可以追溯到 1981 年 Acorn Computers 的 BBC Micro。BBC Micro 是一台传统机器,但它的运行速度几乎是其同时代产品(如 Apple II)的两倍。大约在同一时间,对基于精简指令集计算机(RISC)的微处理器设计的研究表明,简单的芯片设计可以轻松超越当前的高端 32 位产品。
这个计算机历史上的关键时刻激发了 Acorn 在 1983 年发布了自己的 CPU 设计 Acorn RISC 机器。这些计算机最终被称为高级 RISC 机器,以缩写 ARM 推广。
Arm 架构仍然是一系列计算应用中的流行选择。在本文中,您将了解 Arm 架构的特性,将其与 x86 架构进行比较,并探索在云中使用 Arm 的优势和劣势。
什么是 Arm 架构?
Arm 是计算中使用最普遍的架构类型之一。虽然它被广泛使用,但理解它是什么以及为什么它如此受欢迎却是令人惊讶的困难。
Arm 架构属于 RISC 设计家族。RISC 微处理器架构设计使用高度优化的指令集,使小型处理器能够有效地处理复杂的任务。
Arm 已经成为移动设备这个世界上最大的计算机生态系统的基石,许多专家认为 Arm 是云计算的未来。它越来越受欢迎的原因包括其低功耗、灵活的许可和低成本。
低功耗
与其他微处理器架构相比,Arm 提供了出色的性能功耗比。其高级版本的 RISC 设计提供了出色的性能,同时保持了卓越的能效。这种效率对于智能手机、平板电脑和笔记本电脑等电池供电设备至关重要,使它们能够持续更长时间,同时最大限度地提高性能速度。
公平公开的许可
Arm 架构的许可提供了重要的用户控制。许可证是知识产权,而不是特定的代码或硬件。因此,最终用户在使用 Arm 架构时享有极大的灵活性。
例如,苹果使用 Arm 架构来开发针对其产品特定功能优化的处理器。苹果最近用新的 M1 CPU 革新了它的一个依赖 Arm 的处理器组。
低进入成本
与其他公司对其处理器和架构的收费相比,Arm 许可相当便宜。Arm 不制造物理芯片,因此开销较低。
任何公司都可以将自己的片上系统(SoC)技术添加到 Arm 架构中。这一过程允许用户根据自己的专有芯片定制 Arm 架构,针对特定用途进行优化。随着 Arm 的发展,生态系统中固有的制造成本降低使得成本效率不断提高。
低功耗、公平开放的许可和低入门成本相结合,为移动和云计算创造了一个理想的平台。不过 Arm 也不是没有竞争对手,尤其是在云计算市场。Arm 在这一领域的主要竞争对手是英特尔的 x86 架构。
Arm 对比 x86
英特尔于 1978 年首次推出 x86,并于 2003 年推出了现在标准的 64 位版本。x86 架构设计主导着个人电脑和游戏控制台等设备。
很长一段时间,x86 还主宰着高端计算密集型工作站和云计算市场。然而,随着亚马逊网络服务(AWS)等空间提供许多基于 Arm 的云计算选项,情况开始发生变化。即使是今天最快的超级计算机也是基于 Arm 的。
市场趋势正在转变,Arm 似乎正在打破领先地位。
Arm 和 x86 之间的差异
Arm 和 x86 处理器之间的主要区别在于它们执行指令的方式。x86 系统基于复杂指令集计算机(CISC)设计,而 Arm 基于 RISC 设计。简而言之,x86 处理器可以对每条指令执行多步操作,而 Arm 处理器使用有限但高度优化的指令集。
这种执行差异并不是 Arm 开始在重要市场胜过 x86 的唯一原因。可以说,这种变化同样归因于 Arm 诱人的性价比。
例如,考虑亚马逊的第一款 AWS Arm 产品 AWS Graviton 处理器。Arm 可以为 AWS 用户节省高达 40%的成本,性能相当于亚马逊之前的 x86 云处理器。
Arm 灵活的许可流程是 Arm 和 x86 之间的一个重要区别。英特尔的 x86 处理器仍然是一种专有芯片,这意味着英特尔是物理硬件的唯一创造者,也是芯片 SoC 的唯一决策者。与 Arm 许可方案相比,这是一个限制性的生态系统。依赖 x86 时,公司可能无法优化其处理器的功能和特定使用情形。
Arm 在云中的优势和劣势
除了 Amazon 之外,许多其他组织都选择 Arm 架构而不是 x86 架构来构建他们的云系统。Arm 可能也是你的正确选择,但是在云中使用 Arm 可能会有一些缺点。回顾 Arm 的正面和负面属性可能会有所帮助。
优越的性价比
对于选择 Arm 的公司来说,性价比是一个决定性因素,这是有充分理由的。Arm 在云计算方面与 x86 一样高效,同时价格也很有吸引力。
组织可以在处理各种工作负载的同时节省资金,真正将 Arm 的性能与 x86 架构相媲美。在这个领域,它的对手根本无法超越 Arm 的性价比。
增强的安全性
Arm 的许可使亚马逊等公司能够将该架构集成到他们专注于云的芯片中,包括 Graviton2 CPU。使用这种集成,亚马逊可以建立在 Nitro 安全芯片上,在云计算市场上提供无与伦比的安全性。
Nitro 芯片提供 256 位 DRAM 加密,每核心加密性能提高 50%。x86 芯片的专有性质限制太多,使亚马逊无法从这项创新中受益。
兼容性降低
基于 Arm 的云计算的一个缺点是某些应用程序或依赖关系的兼容性降低。历史上 x86 硬件在 PC 和云领域的主导地位意味着开发人员已经构建了大多数应用和容器来运行在 x86 机器上。对于基于 Arm 的系统,您可能需要重新编译和配置您团队的应用程序和依赖项。
可用性较低
x86 一直是云计算的基础。对于大多数云供应商来说,Arm 仍然是一个相对较新的产品。因此,虽然 Arm 实例可用于亚马逊弹性计算云(EC2)、甲骨文云、阿里云和其他一些空间,但它们尚未出现在谷歌云平台(GCP)或 Azure 上。如果你依赖谷歌云及其生态系统,或者如果你需要转换平台来继续你的产品开发,可用性可能会限制你对 Arm 的采用。
结论
Arm 是我们所知的云计算的未来吗?答案是视情况而定。
Arm 是未来的一部分。能否独家使用 Arm,取决于你的具体需求和工作负载。尽管 Arm 在性价比和安全性方面处于领先地位,但它在与某些应用程序或平台集成时可能会出现问题。
也就是说,在许多情况下,您可以将工作负载从 x86 迁移到 Arm,几乎不需要修改。总的来说,Arm 是一个非凡的平台,它正在扩展云计算的潜力,就像几十年前它对移动设备所做的那样。
如果您决定在下一个云项目中使用基于 Arm 的系统,请确保您能够获得有效的持续集成和持续部署(CI/CD)工具。良好的 CI/CD 可以简化 Arm 驱动的虚拟机(VM)的构建和部署,从而简化向 Arm 的过渡。要开始你的探索,请查看 CircleCI 的强大手臂支撑产品。如果你没有 CircleCI 账号, 在这里 注册一个免费的。
我的管道容易受到攻击吗?圆环
您的持续集成(CI)管道是您的应用程序的变更管理过程的核心。正确设置后,CI 管道可以自动执行许多手动任务,以确保您的应用程序及其运行环境的一致性和可重复性。如果您使用这个管道来扫描应用程序、容器和基础设施配置的漏洞,那么它可以成为您的安全策略的一个组成部分。
然而,管道本身可能包含潜在危害您所有系统的漏洞。幸运的是,通过对设计“文化”的一点关注和改变,您可以发现并减轻 CI 管道安全问题。
在本文中,我们将讨论一种与技术无关的方法来发现和缓解这些管道漏洞,以及确保从设计到部署等各个阶段的安全特性的方法。
发现漏洞
当您查看完整的 CI 管道时,通常会看到几个组件协同工作,为您的应用程序提供高效的集成和部署流程。这些组件需要相互信任,通过流程步骤传递正确的信息,以确保每次都能快速、一致地获得预期的结果。因为管道组件具有不同的依赖性和要求,所以可能存在攻击者可以利用的问题。
幸运的是,大多数漏洞都与您的管道的配置或管理有关。您的管道中可能存在的一些最常见的安全问题或风险包括:
- 共享、开放或受损的源代码库
- 构建服务器和容器配置错误
- 缺少对操作系统、运行时和工具的更新
- 代码库中公开的秘密、密钥和凭证
- 缺乏监控工具或定期维护
- 组件的访问控制和安全设置管理不善
解决漏洞
漏洞列表中的大多数问题也存在于您构建的应用程序中,因此查找和解决这些漏洞应该有些熟悉。就像应用程序一样,您必须研究和评估管道的攻击面,以提出一套保护管道的最佳实践。
使用上面的列表作为起点,以下步骤将有助于最小化与这些安全问题相关的风险:
- 锁定你的源代码库。尽量减少有权批准与您的管道直接相关的拉请求的人数,并使用严格的代码审查周期。
- 将基础设施作为代码(IaC) 实践用于您的构建服务器,并且在可能的情况下,尝试采用关注内置安全性的短命容器。
- 定期审计和更新管道中的操作系统、运行时和构建工具。定期安排安全补丁和错误修复有助于减少用于构建应用程序的库中的错误和缺陷。
- 从代码中移除秘密、凭证和密钥,并采用秘密管理器,例如 SecretHub 、 Fortanix 或 AWS 系统管理器参数存储。
- 采用审计工具,让你能够跟踪对你的管道所做的改变。使用代码分析工具来扫描代码中常见的漏洞,以确保在源代码中纠正这些漏洞。
- 在整个管道中实施访问控制和安全策略,以阻止未经授权或匿名的访问。
什么是 DevSecOps?
我所描述的补救措施依赖于采用一种开发人员心态。DevSecOps 是一组额外的实践,旨在通过“左移”将更常见的 DevOps 流程中的安全问题转移到更接近开发的地方
总体思路是大部分安全问题和风险可以在设计和开发过程中解决。在这些早期阶段,开发人员可以从一开始就对安全性进行规划和自动化,而不是依赖于部署后的安全性测试。正是这种文化上的转变,即参与软件交付的每个人都考虑应用程序的安全性,使得安全的应用程序开发得以快速进行。
你能做些什么来开始将你的 DevOps 实践扩展成 DevSecOps 实践?
您不一定需要添加昂贵的专业工具来开始这种实践。一些常见的策略是:
- 鼓励所有利益相关者开始了解应用程序开发中的现代安全威胁。
- 为开发周期中的不同利益相关者开发流程,以应对各种安全事件。
- 利用您现有的 DevOps 沟通渠道,允许人们快速分享安全问题和风险,以加快解决问题。作为正常软件交付过程的一部分,在整个开发生命周期中实施定期的安全性和合规性审计。
- 采用适当的安全工具,这些工具可以帮助您实施安全和遵从性策略,并且可以检查您的代码和管道中的安全漏洞。
代码漏洞
DevSecOps 理念的核心组成部分包括定期在编写时或在源代码中设计和测试安全代码。这个过程被称为静态应用程序安全测试(SAST)。有许多工具可以帮助自动化 SAST。
SAST 是对应用程序内部结构的一种测试形式,通常针对类似于在 OWASP 十大中发现的漏洞类型。像 Snyk 、 WhiteSource 和 NowSecure 这样的工具可以作为拉取请求的一部分或在开发过程中针对您的代码库运行,在安全问题进入您的管道之前就将其捕获。
更传统的动态应用程序安全测试(DAST)是一种在执行过程中针对应用程序运行的测试形式,没有应用程序内部结构的详细信息,以发现攻击者可以利用的漏洞。
理想情况下,你应该在你的开发生命周期中同时使用静态和动态应用安全工具。虽然静态测试不会像动态测试那样发现与基于环境的安全问题相关的漏洞,但它会在安全问题变得更加昂贵之前及早发现它们。结合这两种形式的测试,并将其自动化到您的管道中,将提供全面的漏洞扫描分层。
添加 SAST 工具成为长期“安全左移”思维模式的关键部分,在开发人员编码时或合并之前,会给他们带来安全问题和风险。这有助于开发人员更加意识到常见的安全问题,并允许他们养成编写代码的良好习惯,在第一次尝试时就通过安全检查。
采用 SAST 工具并将结果合并到代码审查中,允许开发人员快速上手,同时仍然在 QA 和生产中使用 DAST 工具来实现更严格的安全测试。
后续步骤
您的应用程序的消费者值得并期望它是高性能的、可用的和安全的,特别是如果您存储他们的数据,或者他们依赖它进行日常操作。通过保护构建应用程序的管道并最大限度地降低管道受损的风险,您可以提高部署的速度和可靠性。
将“左倾”的安全态度作为一种实践,并在开发过程中采用安全工具,可以消除与应用程序相关的许多安全风险。这种结合的方法允许您利用 DevOps 的自动化和可重复性原则。
首先,确保您的 CI 渠道得到保护和维护。如果你还没有做 DevSecOps,为什么不试一试呢?CircleCI 提供了大量的安全集成来对抗我们已经讨论过的漏洞,并且不需要花费任何成本就可以开始使用。
这真的是未来的 CircleCI
上周我写了 It's the Future ,一篇讽刺容器生态系统的文章,略微嘲讽了 Docker、Google、CoreOS 和一堆其他技术。很多 Docker 爱好者喜欢成为笑柄,但也有很多人喜欢和分享这一点,他们大喊“我告诉过你这都是废话”。
很容易理解为什么人们会认为容器生态系统是狗屎,正如我讽刺的那样。毕竟,乍一看并不清楚 Docker 是什么。这是容器化,类似于虚拟化,但又不完全是。它有一个 Dockerfile,有点像 Chef,但它与一个叫做 layers 的东西结合在一起,这个东西包含了一个奇怪的文件系统之类的东西。它解决了与 AWS、Heroku、VMware 和 vagger 类似的问题,但在每种情况下,它都略有不同,起初并不特别清楚,也不清楚为什么会这样。它有 27 个竞争版本的工具,你不能确切地说是什么,有着有趣的名字,如 machine 和 swarm 和法兰绒和 weave 和 etcd 和 rkt 和 kubernetes 以及 compose 和 flocker。这在某种程度上与新的、闪亮的微服务有关,但考虑到一开始保持单个服务运行是多么困难,这似乎是一个非常愚蠢的想法。在这一切之后,它有一种奇怪的邪教氛围,几十家初创公司和大公司都在竞争获得“开发者心智份额”,这可能有一天会以某种方式与金钱相关,这一切都非常 1999 年,肯定有某种 Koolaid 喝醉了。
看看整个 Docker 和 container 的事情,得出结论说都是扯淡,真的不是没有道理。
但事实并非如此。
这实际上是我们构建应用程序的未来。
为什么是仇恨?
许多对 It's the Future 做出反应的人都认为它是 100%准确的,一点也不讽刺,并质疑围绕这整个容器的炒作。为什么?
Docker 和容器生态系统(以下简称“Docker”)正在采用应用程序开发人员世界的一系列标准,如虚拟化、面向服务的架构和操作系统,并以不同的目标和优势重新交付它们。当它这样做的时候,它引起了很大一部分开发者社区的愤怒:讨厌任何新事物的坏脾气者。
与你可能期望的相反,软件行业绝对充满了讨厌进步的人。这种人会在米开朗基罗完成后走进西斯廷教堂,宣称他们已经有了一幅完美的上帝的画像,他们更喜欢他们的天花板是白色的,反正壁画也没那么酷。
与此同时,大多数软件行业像高中生一样做决定:他们痴迷地查看自己圈子里的酷东西,可能会四处看看 Instagram 和脸书上有什么,然后盲目地跟随他们被带到的地方。围绕这些技术,他们形成了从众的小团体,甚至走得更远,围绕他们适合的技术利基来塑造他们自己的身份——他们甚至用他们的帮派颜色覆盖他们的笔记本电脑——并且讨厌和抱怨奇怪或不同的事情。
Docker 进入了那个世界:一种做几乎所有事情的新方式。它抛弃了关于操作系统、部署、ops、打包、防火墙、PaaSes 以及其他一切的旧规则。一些开发人员立即喜欢上了它,有时是因为它解决的问题等正当理由,有时是因为它是一个闪亮的玩具,允许他们在其他孩子使用它之前变得很酷。其他开发者讨厌它——他们说这纯粹是炒作;他们说,这和之前发生的事情没什么两样,我不明白为什么每个人都在谈论它——通常是出于部落原因,而不是理性原因。
所以对 Docker 的反应不一定是基于技术本身。大多数憎恨者对 Docker 解决重要和复杂问题的方法没有真正的反应。这主要是因为,如果您没有花时间扩展大型系统,这些问题是您可能不会注意到的。如果你不能直观而深刻地理解“牛不是宠物”的含义以及为什么它很重要,那么 Docker 和相关工具所做的许多选择对你来说都将是怪异和可怕的。
融合世界
Docker 处于两个学科的结合点:web 应用程序和分布式系统。在过去的十年中,我们在网络社区中一直在假装我们可以通过知道如何编码来构建网络应用程序。我们写了一些 HTML,一些 JavaScript 和一些 Rails,我们有一个网站。我们添加一些表单和一些处理程序,也许还有一个 API,我们就完成了:这足以推出一个产品,它可以获得牵引力、客户和收入,并改变世界!
与此同时,在过去的二十年里,分布式系统人员一直在做一些相当无聊的事情。他们试验了 CORBA 和 SOAP 等复杂的协议,并学习了如何处理 CAP 定理、时钟同步是如何不可能的以及两个一般问题等问题,这些问题对大多数人来说似乎主要是理论性的。那些问题和它们的解决方案对于那些仅仅试图用他们的知识来编写和使用 web 应用程序的人来说是相当无趣的。
但是有趣的事情发生了。Web 应用程序变得足够大,以至于它们开始需要伸缩。足够多的人来到了互联网上,网络应用程序不能再停留在一个 VPS 上,或者只是纵向扩展。当我们开始扩展时,我们开始在我们的应用程序中看到所有这些错误,这些错误有着有趣的名字,如“竞争条件”、“网络分区”、“死锁”和“拜占庭失败”。这些问题是分布式系统人员已经研究了很长时间的问题,这些问题的解决方案不仅困难,而且在许多情况下理论上是不可能的。
在这种可扩展性危机的早期,Heroku 发生了。Heroku 让水平扩展基础设施变得非常容易,让我们再次假装我们真的只是在制作简单的 web 应用程序。作为一个行业,我们为自己赢得了 5 年的假装和自欺欺人。
我们现在已经达到了这种自我错觉的极限,当我们从这种错觉中走出来时,我们发现自己正在尝试尽早构建可伸缩性,重新设计破碎的东西以便它们可以伸缩,并了解整体架构的缺点以及为什么使用单一数据库不会继续为我们工作。我们想出了诸如不可变架构、“宠物对牛”、微服务以及一整套最佳和最差实践的短语,试图使其中一些变得更容易。
此时,在此轮班期间,Docker 进来并试图解决许多问题。但是 Docker 没有告诉我们,我们可以假装不存在扩展的问题,我们可以继续以基本相同的方式做事,就像 Heroku 所做的那样,而是告诉我们,分布式系统从根本上来说就是我们一直在做的事情,所以我们需要接受它,并开始在这种模式下工作。现在,我们面对的不再是简单的 web 框架、数据库和操作系统,而是 Swarm、Weave、Kubernetes 和 etcd 等工具,这些工具不会假装一切都很简单,而是要求我们不仅要解决问题,还要深入理解我们正在解决的问题。
好处是,我们获得了构建可伸缩架构的能力,只要我们不假装我们可以抽象它。我们现在需要知道什么是网络分区,如何处理它,如何在 AP 和 CP 系统之间进行选择,以及如何构建能够在真实网络和机器的压力下进行扩展的架构。有时弗吉尼亚会有一场暴风雨,有时东西会着火,有时鲨鱼会咬到海底电缆,有时会有延迟,传递失败,机器会死亡,抽象会泄露。
一切都需要更有弹性,更可靠,我们需要承认这些是我们在开发应用程序时需要考虑的事情。我们需要这样做,不是因为它闪闪发光,或者因为它是一些神话般的最佳实践,而是因为像亚马逊、网飞和谷歌这样的人已经投入了 15 年的血汗和行业经验来解决这个问题,并告诉我们如何建立真正规模的系统。
真正的问题解决了
那么 Docker 到底在为我们解决什么问题呢?我们在构建 web 应用程序时所做的一切都是极其脆弱的,Docker 正在迫使我们保持理智:
-
到目前为止,我们一直将机器(DevOps 的 ops 部分)与应用程序(dev 部分)分开部署。我们甚至有两个不同的团队来管理应用程序堆栈的这些部分。这是荒谬的,因为应用程序依赖于机器、操作系统和代码,单独考虑它们是没有意义的。容器在开发者的工具包中统一了操作系统和应用程序。
-
到目前为止,我们一直在 AWS 和 Heroku 以及其他缺乏管理面向服务架构的真正工具的 IaaSes 和 PaaSes 上运行我们的面向服务架构。Kubernetes 和 Swarm 管理和编排这些服务。
-
到目前为止,我们一直使用完整的操作系统来部署我们的应用程序,以及它们带来的所有安全影响,而不是我们可以部署的绝对最小的东西。容器允许您公开一个非常小的应用程序,只有您需要的端口,甚至可以小到一个静态二进制文件。
-
到目前为止,在机器上线后,我们一直在摆弄它们,要么使用“配置管理”工具,要么多次将应用程序重新部署到同一台机器上。由于容器是由编排框架按比例放大和缩小的,所以只启动不可变的映像,运行中的机器永远不会被重用,从而消除了潜在的故障点。
-
到目前为止,我们一直使用的语言和框架主要是为单台机器上的单个应用程序设计的。面向服务架构的 Rails 路线的等价物以前并不存在。现在 Kubernetes 和 Compose 允许您指定跨服务的拓扑。
-
到目前为止,我们一直在按照 AWS 提供的规模部署大型虚拟化服务器。我们不能说“我想要 0.1 的 CPU 和 200 兆的内存”。我们不仅浪费了虚拟化开销,还使用了超出应用需求的资源。容器的部署需求要小得多,并且在共享方面做得更好。
-
到目前为止,我们一直在使用多用户操作系统部署应用和服务。Unix 的构建是为了让几十个用户同时在其上运行,共享二进制文件、数据库、文件系统和服务。这与我们构建 web 服务时所做的完全不匹配。同样,容器只能保存简单的二进制文件,而不是整个操作系统,这导致在应用程序或服务中需要考虑的事情少了很多。
唯一不变的是变化
我们的行业如此迅速地神化新的和令人兴奋的技术,以至于它不会等待这些技术成熟。Docker 正以令人难以置信的速度前进,这意味着它还没有接近稳定或成熟。我们有多种容器运行时间、图像格式、编排工具和主机操作系统选项,每种选项都有不同级别的效用、范围、吸引力和社区支持。
环顾我们这个行业的其他部分,事情不会变得稳定,直到它们变得陈旧和乏味。举个例子,在我们得到休息之前,有多少协议必须终止?我们在 SOAP 和 CORBA 的尸体上构建了 REST、AJAX 和 JSON,利用了我们在构建它们时学到的经验。在大约 10 年的时间里,这是两次重大的技术转变。然而,我们仍然没有为基于 REST 的 API 提供与十年前 SOAP 相同水平的工具,尤其是 SOAP 还没有完全消亡。
同样的事情也发生在前端,事实上很多人将我对 Docker 生态系统的模仿与前端开发中的狗屎秀相提并论。自从我们十年前逃离 Java 以来,编程语言也发生了同样的事情。一直以来,直到问题得到很好的解决,开发人员会不断提出新的解决方案。Docker 生态系统有大量的问题需要解决。
所以我们可以预期 Docker 还没有那么成熟。当你尝试它的时候,仍然会有许多边缘情况和奇怪的事情发生,当我们几年后回顾它们的时候,它的一些决定是奇怪的,实际上可能是完全错误的。最佳实践仍然需要不断尝试、失败、再尝试、再失败,直到我们把它们做好。
这将需要几年的时间,直到我们弄清楚所有这些事情,并安顿下来。但这并不意味着容器是废话,或者我们可以忽略它们。我们总是面临着这样一个选择:是继续使用我们已知的技术,还是跨越一步尝试新事物,吸取教训,适应、迭代和改进我们周围的行业。
如果你在找我,我会在未来。
想动作快又不坏东西?免费注册软件团队喜欢使用的现代持续集成和交付平台。
你的防火墙后面需要牛逼的 CI/CD?给我们留言或注册 CircleCI 企业的30 天免费试用。
它是未来-英雄库坞|圆形坞站 vs 火箭,英雄库坞站 vs 坞站
嘿,我的老板说要和你谈谈-我听说你对网络应用很了解?
是的,我现在更喜欢分布式系统。我刚从 ContainerCamp 和 Gluecon 回来,下周要去 Dockercon。对行业的发展方式感到非常兴奋——让一切变得更简单、更可靠。是未来!
酷毙了。我目前只是在构建一个简单的 web 应用程序——一个使用 Rails 的普通 CRUD 应用程序,将部署到 Heroku。那还是要走的路吗?
哦,不。那是老方法了。Heroku 死了,没人再用它了。你现在需要使用 Docker。这是未来。
哦,好吧。那是什么?
码头工人是这种新的集装箱运输方式。它就像 LXC,但它也是一种打包格式,一个分发平台,以及让分布式系统变得非常简单的工具。
集装箱里..—现在怎么办?LXE 是什么?
我是 LXC。这就像类固醇激素!
cher-oot 是什么?
好吧,看。码头工人。集装箱化。这是未来。这就像虚拟化,但是更快更便宜。
哦,就像流浪汉一样。
不,流浪者已经死了。现在一切都将集装箱化,这是未来。
好的,那么我不需要了解任何关于虚拟化的知识?
不,你仍然需要虚拟化,因为容器还不能提供完整的安全故事。所以如果你想在多租户环境中运行任何东西,你需要确保你不能逃离沙箱。
好吧,我有点迷路了。我们来备份一下。所以有一种类似虚拟化的东西,叫做容器。我可以用这个对付 Heroku?
嗯,赫罗库有些支持多克,但是我告诉过你:赫罗库已经死了。你想在 CoreOS 上运行你的容器。
好吧,那是什么?
这个很酷的主机操作系统可以和 Docker 一起使用。见鬼,你甚至不需要 Docker,你可以使用 rkt。
火箭?
不,rkt。
好的,火箭。
-不,现在叫 rkt。完全不同。这是一种替代的容器化格式,不像 Docker 那样捆绑在一起,因此更具可组合性。
这样好吗?
当然是好的。可组合性是未来。
好的,你如何使用它?
我不知道。我觉得没人用。
叹气。你刚才说了些关于科瑞奥斯的事?
是的,所以这是你和 Docker 一起使用的主机操作系统。
什么是主机操作系统?
一个主机操作系统运行你所有的容器。
管理我的集装箱?
是的,你得有东西来管理你的集装箱。因此,您可以像设置 EC2 实例一样,将 CoreOS 放在上面,然后运行 Docker 守护进程,然后可以将 Docker 映像部署到它上面。
那是容器的哪一部分?
所有的一切。看,你拿着你的应用,写一个 Docker 文件,在本地把它变成一个图像,然后你可以把它推送到任何 Docker 主机。
啊,就像赫罗库?
不,不是 Heroku。我告诉过你。赫罗库死了。你现在使用 Docker 运行你自己的云。
什么?
是的,这很简单。查一下#gifee。
吉菲?
——“谷歌为其他所有人打造的基础设施”。你可以使用容器从现成的工具和堆栈中取出一些,你可以拥有和 Google 一样的基础设施。
我为什么不用谷歌的东西呢?
你认为这种情况会在 6 个月后出现吗?
好吧,难道没有其他人做这些东西的主机吗?我真的不想托管自己的东西。
嗯,亚马逊有 ECS,但是你必须写 XML 或者其他东西。
OpenStack 上的东西呢?
恶心。
Ew?
-恶。
听着,我真的不想托管我自己的东西。
不,这真的很简单。您刚刚建立了一个 Kubernetes 集群。
我需要一个集群?
-Kubernetes 星团。它将管理您所有服务的部署。
我只有一项服务。
你是什么意思?你有一个应用程序的权利,所以你至少有 8-12 个服务?
什么?没有,就一个 app。服务什么的。只是其中之一。
不,看看微服务。这是未来。这是我们现在做任何事情的方式。你把你的单片应用程序分成 12 个服务。你做的每一项工作都有一个。
这似乎太过分了。
这是确保它可靠的唯一方法。因此,如果您的认证服务出现故障…
认证服务?我正要用这块我以前用过几次的宝石。
太好了。使用宝石。把它放到它自己的项目里。在上面放一个 RESTful API。然后,您的其他服务使用该 API,并优雅地处理失败等问题。把它放在一个容器里,然后不断地把它送出去。
好了,现在我已经有了几十个无法管理的服务,接下来呢?
是的,我说的是 Kubernetes。让您能够协调所有服务。
策划他们?
是的,所以你有这些服务,它们必须可靠,所以你需要它们的多个副本。因此,Kubernetes 会确保您有足够的资源,并且这些资源分布在您的机群中的多个主机上,因此它总是可用的。
我现在需要一支舰队?
是的,为了可靠性。但是 Kubernetes 为你管理它。你知道 Kubernetes 的作品,因为谷歌建立了它,它运行在 etcd 上。
什么是 etcd?
这是 RAFT 的一个实现。
好吧,那么木筏是什么?
就像帕克斯一样。
天啊,我们要去多深的兔子洞?我只想推出一个应用程序。叹气。他妈的,好吧,深呼吸。上帝啊。好吧,Paxos 是什么?
Paxos 就像是 70 年代的分布式共识协议,没有人理解或使用。
太好了,谢谢你告诉我这件事。筏子是什么?
-因为没人了解帕克斯,这个家伙迭戈…
哦,你认识他?
不,他在 CoreOS 工作。不管怎样,迭戈为他的博士论文造了一个筏子,因为帕克斯太难了。聪明绝顶的家伙。然后他写了 etcd 作为实现,Aphyr 说不是屎。
Aphyr 是什么?
Aphyr 是那个写了“也许打电话给我”的人你知道,分布式系统和 BDSM 家伙?
什么?你是说 BDSM 吗?
是的,BDSM。这是旧金山。每个人都喜欢分布式系统和 BDSM。
呃,好吧。他写了凯蒂·佩里的歌?
不,他写了一系列关于每个数据库如何失败的博文。
CAP 是什么?
——上限定理。它说一致性、可用性和分区容忍度三者只能取其二。
好的,所有数据库都失败了吗?这到底是什么意思?
意思是他们是狗屎。比如 Mongo。
我以为 Mongo 是网络规模的?
其他人都没有。
好吧,那么等等?
是的,etcd 是一个分布式键值存储。
哦,就像里兹。
不,一点也不像雷迪斯。etcd 分布式。如果网络分区,Redis 会丢失一半的写入数据。
好了,这是一个分布式键值存储。这为什么有用?
-Kubernetes 使用 etcd 作为消息总线建立一个标准的 5 节点集群。它与 Kubernetes 自己的一些服务相结合,提供了一个非常有弹性的编排系统。
5 个节点?我有一个应用程序。所有这些我需要多少台机器?
嗯,你将有大约 12 个服务,当然你需要每个服务的一些冗余拷贝,一些负载平衡器,etcd 集群,你的数据库,和 kubernetes 集群。大概有 50 个流动集装箱。
WTF!
没什么大不了的!容器真的很高效,所以你应该能够把它们分布在 8 台机器上!是不是很神奇?
这是一种说法。有了这些,我就可以简单地部署我的应用程序了?
当然可以。我的意思是,对于 Docker 和 Kubernetes 来说,存储仍然是一个开放的问题,联网需要一些工作,但你基本上已经做到了!
我明白了。好的,我想我明白了。
太好了!
谢谢你的解释。
没问题。
让我重复一遍,看看我是否理解对了。
当然可以!
因此,我只需要将我的简单 CRUD 应用程序分成 12 个微服务,每个微服务都有自己的 API,这些 API 调用彼此的 API,但可以灵活地处理故障,将它们放入 Docker 容器中,启动一个由 8 台运行 CoreOS 的 Docker 主机组成的机群,使用一个运行 etcd 的小型 Kubernetes 集群对它们进行“编排”,找出网络和存储的“未决问题”,然后我不断向我的机群提供每个微服务的多个冗余副本。就这样吗?
是的!是不是很光荣?
我要回赫罗库。
想用 Docker,源源不断的送那狗屁?查看 CircleCI 的码头支持。
想动作快又不坏东西?免费注册软件团队喜欢使用的现代持续集成和交付平台。
你的防火墙后面需要牛逼的 CI/CD?给我们留言或注册 CircleCI 企业的30 天免费试用。
2023 年 1 月 4 日安全事件的 CircleCI 事件报告
2023 年 1 月 4 日,我们提醒客户发生了安全事故。今天,我们想与您分享发生了什么,我们学到了什么,我们的计划是什么,以不断改善我们未来的安全态势。
我们要感谢我们的客户对轮换和撤销机密的关注,并为此次事件可能对您的工作造成的任何中断表示歉意。我们鼓励尚未采取行动的客户采取行动,以防止未经授权访问第三方系统和商店。此外,我们要感谢我们的客户和我们的社区,感谢您在我们进行彻底调查期间的耐心。为了实现负责任的披露,我们已尽最大努力在信息共享速度和保持调查完整性之间取得平衡。
该报告将涵盖:
发生了什么事?
除非另有说明,所有日期和时间均以 UTC 表示。
2022 年 12 月 29 日,我们的一个客户提醒我们注意可疑的 GitHub OAuth 活动。这一通知引发了 CircleCI 的安全团队与 GitHub 的深入审查。
2022 年 12 月 30 日,我们得知该客户的 GitHub OAuth 令牌已被未经授权的第三方攻破。尽管该客户能够快速解决问题,但出于谨慎,我们在 2022 年 12 月 31 日代表我们的客户主动启动了所有 GitHub OAuth 令牌的轮换流程。尽管与 GitHub 合作提高了 API 速率限制,但轮换过程需要时间。虽然目前还不清楚其他客户是否受到影响,但我们继续扩大了分析范围。
到 2023 年 1 月 4 日,我们的内部调查已经确定了未经授权的第三方入侵的范围和攻击的进入路径。到目前为止,我们已经了解到,未经授权的第三方利用部署到 CircleCI 工程师笔记本电脑上的恶意软件来窃取有效的 2FA 支持的 SSO 会话。这台机器在 2022 年 12 月 16 日被入侵。我们的防病毒软件没有检测到恶意软件。我们的调查表明,该恶意软件能够执行会话 cookie 窃取,使他们能够在远程位置模拟目标员工,然后提升对我们生产系统子集的访问权限。
因为目标员工拥有生成生产访问令牌的特权,这是该员工日常职责的一部分,所以未经授权的第三方能够访问和泄露数据库和存储的子集的数据,包括客户环境变量、令牌和密钥。我们有理由相信,未经授权的第三方在 2022 年 12 月 19 日从事了侦察活动。2022 年 12 月 22 日,发生了泄漏,这是我们生产系统中未授权活动的最后记录。尽管所有泄漏的数据都是静态加密的,但第三方从正在运行的进程中提取加密密钥,使他们有可能访问加密的数据。
虽然我们对内部调查的结果充满信心,但我们已经聘请了第三方网络安全专家来协助我们的调查并验证我们的调查结果。迄今为止,我们的发现基于对我们的身份验证、网络和监控工具的分析,以及我们的合作伙伴提供的系统日志和日志分析。
针对这一事件,我们采取了以下措施:
- 世界协调时 2023 年 1 月 4 日 16:35,我们关闭了该员工的所有访问权限,因为他的帐户受到了威胁。
- 世界协调时 2023 年 1 月 4 日 18:30,我们关闭了几乎所有员工的生产访问权限,将运营问题的访问权限限制在一个极小的小组。在调查过程中,我们从未有任何证据表明任何其他员工或其设备的凭据遭到破坏,但我们采取了这一行动,以限制潜在的影响范围。
- 2023 年 1 月 4 日 22:30 UTC,我们轮换了所有潜在暴露的生产主机,以确保生产机器的清洁。
- 2023 年 1 月 5 日 03:26 UTC,我们撤销了所有项目 API 令牌。
- 2023 年 1 月 6 日 05:00 UTC,我们撤销了 2023 年 1 月 5 日 00:00 UTC 之前创建的所有个人 API 令牌。
- 2023 年 1 月 6 日 06:40 UTC,我们开始与 Atlassian 的合作伙伴合作,代表我们的客户轮换所有 Bitbucket 令牌。这项工作于 2023 年 1 月 6 日 10:15 UTC 完成。
- 2023 年 1 月 7 日 07:30 UTC,我们完成了我们于 2022 年 12 月 31 日 04:00 UTC 开始的 GitHub OAuth 令牌轮换。
- 2023 年 1 月 7 日 18:30 UTC,我们开始与我们在 AWS 的合作伙伴合作,通知客户可能受影响的 AWS 令牌。我们了解到,这些通知已于 2023 年 1 月 12 日 00:00 UTC 完成。
在此期间,我们在外部调查人员的支持下继续我们的取证调查,在我们的平台上推出了额外的安全层,并构建和分发了额外的工具(更多详细信息见下文)来支持我们的客户保护他们的秘密。
我们怎么知道这个攻击媒介是封闭的,并且是安全的呢?
我们相信客户可以安全地在 CircleCI 上进行构建。
自从意识到这种攻击后,我们已经采取了许多措施来消除攻击媒介并增加额外的安全层,包括:
- 通过我们的 MDM 和 A/V 解决方案,针对此次攻击中使用的恶意软件表现出的特定行为增加了检测和阻止功能。
- 在我们实施额外的安全措施时,限制数量非常有限的员工进入生产环境。我们对我们平台的安全性充满信心,没有迹象表明任何其他员工的设备受到了威胁。
- 对于保留生产访问权限的员工,我们增加了额外的逐步身份认证步骤和控制。这将帮助我们防止可能的未经授权的生产访问,即使是在被盗的 2FA 支持的 SSO 会话的情况下。
- 针对我们在此场景中确定的特定行为模式,跨多个触发器并通过各种第三方供应商实施监控和警报。
我们知道安全工作永远不会结束。除了关闭这一特定媒介,我们还进行了强化和持续的审查,以确保对潜在攻击的更强防御。
对客户的沟通和支持
在 2023 年 1 月 4 日 22:30 UTC 完成所有生产主机的轮换后,我们确信我们已经消除了攻击媒介和潜在的持续损坏主机。
太平洋标准时间 2023 年 1 月 4 日下午 6:30/世界标准时间 2023 年 1 月 5 日 02:30,我们发送了披露电子邮件,在我们的博客上发布了安全通知,通过我们的社交媒体账户和我们的讨论论坛通知了客户,并创建了一篇关于如何执行建议的安全步骤的支持文章。
我们建议所有客户轮换他们的秘密,包括 OAuth 令牌、项目 API 令牌、SSH 密钥等等(更多细节见博客文章或讨论文章)。
这一披露开启了一个与我们的客户进行积极和持续沟通的时期。我们要感谢我们的客户对这一事件的一致反应,以及帮助我们寻找机会为您提供额外的工具。我们采纳了这一反馈意见,作为回应,我们构建并发布了新工具,并修改了现有工具,以通过以下方式加快客户的补救:
- 用于秘密发现的脚本,以便创建秘密轮换的可操作列表。
- CircleCI API 的两个关键变化:
- 为了更好地匹配 GitHub 用户界面,新功能返回 SHA-256 签名。
- 将
updated_at
字段添加到上下文 API 中,以便客户可以验证这些变量的成功循环。
- 免费和付费计划的所有客户都可以访问审计日志,以帮助客户审查 CircleCI 平台的活动。
我们感谢客户对我们可以改进沟通的所有反馈,包括让事件在我们的渠道中更加明显的机会。
我如何知道我是否受到影响?
我的数据有风险吗?
在此事件中,未经授权的参与者于 2022 年 12 月 22 日泄露了客户信息,其中包括第三方系统的环境变量、密钥和令牌。如果您在此期间在我们的平台上存储了机密,请假设它们已被访问,并采取建议的缓解措施。我们建议您从 2022 年 12 月 16 日开始调查您系统中的可疑活动,并在 2023 年 1 月 4 日我们披露后结束您的秘密轮换。2023 年 1 月 5 日之后进入系统的任何东西都可以被认为是安全的。
是否有未经授权的参与者使用这些数据访问我的任何系统?
因为这一事件涉及第三方系统的密钥和令牌的泄露,我们无法知道您的秘密是否被用于对这些第三方系统的未授权访问。我们在下面提供了一些详细信息,以帮助客户进行调查。
在本文发布时,只有不到 5 家客户告知我们,此事件导致了对第三方系统的未授权访问。
可能有助于您的团队进行内部调查的详细信息
在第三方法医调查员的帮助下,我们最近确认了可能有助于客户审计和调查的其他细节。
影响日期:
- 我们在 2022 年 12 月 19 日看到未经授权的第三方访问,在 2022 年 12 月 22 日发生数据泄露。
- 在 2022 年 12 月 19 日之前,我们没有任何影响客户活动的证据。出于谨慎,我们建议您调查 2022 年 12 月 16 日的损坏日期到 2023 年 1 月 4 日之后您轮换您的机密的日期之间的这段时间,以发现您系统中的异常活动。
被识别为威胁参与者使用的 IP 地址:
- 178.249.214.10
- 89.36.78.75
- 89.36.78.109
- 89.36.78.135
- 178.249.214.25
- 72.18.132.58
- 188.68.229.52
- 111.90.149.55
被确定为威胁参与者使用的数据中心和 VPN 提供商:
- 数据营有限公司
- Globalaxs 魁北克 Noc
- 汉迪网络有限责任公司
- 马尔瓦德虚拟专用网
要搜索和删除的恶意文件:
- /private/tmp/.svx856.log
- /private/tmp/。ptslog
- PTX-player . dmg(sha 256:8913 e 38592228 ADC 067d 82 f 66 c 150d 87004 EC 946 e 579d 4 a 00 c 53 b 61444 ff 35 BF)
- PTX 应用程序
阻止以下域:
检查 GitHub 审计日志文件中的意外命令,例如:
我们从这次事件中学到了什么,我们下一步要做什么
我们了解到:虽然我们拥有阻止和检测攻击的工具,但仍然有机会加强我们的安全态势。
我们拥有的身份验证、安全和跟踪工具使我们能够全面诊断和修复问题。随着恶意行为者变得越来越狡猾,我们也在不断改进我们的安全标准,并推行最佳实践来应对未来的威胁。我们将越来越积极地使用安全工具。展望未来,为了支持更保守的立场,防止不良行为者不正当地访问我们的系统,我们将优化现有工具的配置,以创建额外的防御层。
我们的计划:
首先,我们将为所有客户启动定期自动 OAuth 令牌轮换。我们的计划还包括从 OAuth 到 GitHub 应用程序的转变,使我们能够在令牌内实施更细粒度的权限。我们还计划完成对我们所有工具配置的全面分析,包括第三方审查。我们将继续采取额外的措施,包括扩大警报范围、降低会话信任度、增加额外的身份认证因素,以及执行更常规的访问轮换。最后,我们将使我们的系统权限更加短暂,严格限制从类似事件中获得的任何令牌的目标值。
我们了解到:我们可以让客户更容易采用我们最先进的安全功能。
通过 CircleCI 的发展,我们不断推出新功能来提高客户构建管道的安全性。虽然客户可以使用高级安全功能,但我们可以做更多工作来提高这些功能的采用率。
我们的计划:
客户必须能够更轻松地无缝采用最新、最先进的安全功能,包括 OIDC 和 IP 范围。我们还在探索其他主动步骤,例如,自动令牌过期和未使用机密的通知。我们将让我们的客户更简单、更方便地创建和维护高度安全的管道,在智能管理风险的同时充分利用云的优势。
关于雇员责任与系统保护的说明
我们想搞清楚。虽然一名员工的笔记本电脑通过这种复杂的攻击被利用,但安全事故是系统故障。作为一个组织,我们的责任是构建多层防护措施来抵御所有攻击媒介。
安全最佳实践
鉴于日益增多的高度复杂和动机不良的行为者,我们致力于与我们的客户分享最佳做法,以加强我们对未来不可避免的企图的集体防御。以下是客户可以采纳的提高管道安全性的建议:
- 尽可能使用 OIDC 令牌以避免在 CircleCI 中存储长期有效的凭证。
- 利用 IP 范围将到您系统的入站连接限制为仅已知的 IP 地址。
- 使用上下文来合并共享的秘密,并限制特定项目对秘密的访问,然后可以通过 API 自动轮换。
- 对于特权访问和附加控制,您可以选择使用 runners ,它允许您将 CircleCI 平台连接到您自己的计算和环境,包括 IP 限制和 IAM 管理。
结束语
我们知道没有方便的时间来响应关键系统上的安全事件,我们要真诚感谢所有在事件发生后立即采取行动的客户。通过这一集体行动,我们将能够更有力地应对未来的威胁。我们也亲眼目睹了我们客户群体的力量和慷慨,许多人来到我们的讨论论坛分享知识并相互帮助。感谢您在我们努力解决此事件时给予的支持和耐心。
CircleCI 安全警报:轮换存储在 CircleCI 的任何秘密(1 月 13 日更新)
安全更新 01/13/2023 - 21:25 UTC
我们完整的事故报告
现已发布
安全更新 01/12/2023 - 00:30 UTC
我们已与 AWS 合作,帮助通知其 AWS 令牌可能在此次安全事件中受到影响的所有 CircleCI 客户。今天,AWS 开始通过电子邮件提醒客户潜在受影响令牌的列表。此电子邮件的主题行是[需要采取的措施] CircleCI 安全警报以循环访问密钥。
我们与 AWS 在这一额外沟通层面上合作的目标是帮助客户更轻松地识别、撤销或轮换任何可能受影响的密钥。如需帮助,请参见关于旋转访问键的 AWS 文档或联系亚马逊支持。
您可能有的其他问题:
-
如果我收到了电子邮件,这是否意味着有人未经授权访问了列出的 AWS 帐户? 此时,没有任何迹象表明您的 AWS 帐户被访问过,只有存储在 CircleCI 中的令牌有可能被泄露,因此应该从 AWS 中删除并轮换。
-
circle ci 1 月 4 日披露以来这里有什么新鲜事?还发生了什么事吗? 这是 CircleCI 在 2023 年 1 月 4 日所做的原始披露的一部分的额外警告。没有新的信息或新的发展出现。本说明旨在帮助客户识别和轮换 AWS 上的 AWS 令牌。
安全更新 01/10/2023 - 21:10 UTC
这是一个简短的更新,传达我们的事件报告的状态。我们预计在 1 月 17 日(太平洋标准时间)星期二向我们的客户提供事故报告。
我们对 CircleCI 平台的安全性有信心,客户可以继续构建。我们的支持工程、客户成功和安全团队将继续在袖手旁观协助您解决任何问题或顾虑。我们还将继续在我们的论坛上与我们的客户和社区保持联系。谢谢你。
安全更新 01/07/2023 - 07:30 UTC
昨天,我们让客户知道我们正在代表客户轮换 GitHub OAuth 令牌。这个过程现在已经完成,所有的 GitHub OAuth 令牌都已经被轮换。
希望轮换自己的 OAuth 令牌的客户仍然可以按照下面概述的说明进行。
在这一点上,我们不期望有更多的实质性更新来分享,直到我们完成了我们正在进行的调查与我们的第三方法医团队。我们对 CircleCI 平台的安全性有信心,客户可以继续构建。
我们想继续表达我们对顾客的感激和关心。我们知道秘密和可变的轮换可能是费时费力的工作。感谢您对保护所有系统安全的支持和关心。我们在 CircleCI 的团队夜以继日地工作,尽可能多地采取缓解措施来支持我们的客户。我们的支持工程、客户成功和安全团队可以提供帮助。我们还将继续在我们的论坛上与我们的客户和社区保持联系。谢谢你。
安全更新 01/06/2023 - 23:00 UTC
这是一个简短的更新,提供我们的 GitHub OAuth 令牌轮换的状态。截至 2023 年 1 月 6 日 23:00 UTC,我们完成了 99%的令牌轮换。我们预计将在未来几个小时内完成,并将在完成后提供额外的更新。
作为一个澄清点,对于处理旋转机密和密钥的客户,您应该在源(他们提供访问的系统)上旋转密钥,然后在 CircleCI 上存储新的机密。仅仅把它们从切尔莱西移走是不够的。
感谢您对缓解这一问题的支持和关心。我们的支持工程、客户成功和安全团队将继续在袖手旁观协助您解决任何问题或顾虑。我们还将继续在我们的论坛上与我们的客户和社区保持联系。谢谢你。
安全更新 01/06/2023 - 17:52 UTC
我们的团队正在努力采取一切可能的行动来帮助客户缓解这一事件。
自上次更新以来,我们的团队代表客户解决了以下问题:
- 个人和项目 API 令牌:我们已经移除了 2023 年 1 月 5 日 00:00 UTC 之前创建的所有个人和项目 API 令牌。
- Bitbucket OAuth :截至世界协调时 2023 年 1 月 6 日 10:00,我们在 Atlassian 的合作伙伴已终止 Bitbucket 用户的所有 OAuth 令牌。用户登录时将刷新位桶令牌,此处无需任何其他操作。Bitbucket 用户仍然需要替换 SSH 令牌。
- GitHub OAuth: 截至世界协调时 1 月 7 日 07:30,所有 GitHub OAuth 令牌已代表 CircleCI 客户进行轮换。希望轮换自己的 GitHub OAuth 令牌的客户可以遵循以下说明。
注意:该安全事件与使用 CircleCI 服务器的客户无关。
截至 1 月 10 日 2:00 UTC 的更新说明
- OAuth 令牌:
- 对于 GitHub:截至 1 月 7 日 07:30 UTC,所有 GitHub OAuth 令牌已经代表 CircleCI 客户进行了轮换。希望这样做的客户可以通过注销 CircleCI 应用程序,转到https://github.com/settings/applications,选择“授权的 OAuth 应用程序”,然后撤销 CircleCI 条目来轮换他们自己的 OAuth 令牌。完成后,重新登录 CircleCI 以触发重新授权。
- 对于 Bitbucket:截至 2023 年 1 月 6 日 10:00 UTC,我们在 Atlassian 的合作伙伴已终止 Bitbucket 用户的所有 OAuth 令牌。用户登录时将刷新位桶令牌,此处无需任何其他操作。Bitbucket 用户仍然需要替换 SSH 令牌。
- 对于 GitLab: GitLab 用户不需要重新授权他们的应用程序访问。作为预防措施,我们仍然建议 GitLab 用户轮换他们的环境变量、个人和项目 API 令牌以及所有 SSH 密钥。
- 项目 API 令牌:要旋转它们,进入项目设置> API 权限>添加 API 令牌。更新:CircleCI 已撤销 2023 年 1 月 5 日 00:00 UTC 之前创建的所有代币。
- 项目环境变量:进入项目设置>环境变量,然后创建一个同名的环境变量来替换现有的值。
- 上下文变量:转到组织设置>上下文,为每个上下文执行与项目环境变量相同的操作。
- 注意:截止到 1 月 9 日 22:50 UTC,我们已经更新了上下文 API,以包含最后的“updated_at”日期和时间戳。这为确定秘密轮换是否成功完成提供了必要的信息。我们将推出额外的更改,以确保除 API 之外,UI 中也包含 updated_at 日期。你可以在 API 文档上下文和环境变量中了解更多信息。
- 用户 API 令牌:进入用户设置>个人 API 令牌,然后删除并重新创建您可能正在使用的任何令牌。更新:CircleCI 已撤销 2023 年 1 月 5 日 00:00 UTC 之前创建的所有代币。
- 项目 SSH 密钥:
- 前往项目设置> SSH 密钥。
- 删除部署密钥并再次添加它。
- 如果您使用了任何额外的键,那么这些键需要被删除并重新创建。
- 注意:SSH 密钥也需要从目标环境中轮换。
- Runner 令牌:使用 CircleCI CLI,运行以下命令:
circleci runner token list <resource-class name>
circleci runner token delete "<token identifier>"
circleci runner token create <resource-class-name> "<nickname>"
- 按照这些命令,您需要将创建的令牌添加到您的 launch-agent-config.yml 中,并重新启动您的 runner 服务
注意:在 CircleCI 上还有一个工具可以发现你所有的秘密,可以用来找到一个可操作的物品列表进行轮换。
2023 年 1 月 9 日更新:我们已经添加了使用我们的get-checkout-key
API V1.1 端点返回结帐键的 SHA256 签名的功能。
请参见下面的 API 调用示例:
- curl-H " Circle-Token:
" https://Circle ci . com/API/v 1.1/project/:VCS-type/:username/:project/check out-key?digest=sha256 - 请注意这里的
sha256
查询参数
我们感谢您的适应性和保持系统安全的承诺。随着更多细节的出现,我们将继续在这里提供更新和信息。我们还将继续在我们的论坛上与我们的客户和社区保持联系。谢谢你。
安全更新 2023 年 5 月 1 日
我们希望向客户介绍我们昨天披露的安全事件的最新情况,并进一步澄清客户的常见问题。我们理解新年伊始就中断您的工作是不理想的。我们感谢您的耐心和理解,因为我们共同努力,以保持所有系统的安全。
CircleCI 客户可以构建
我们从客户那里收到的第一个问题是,“我能建造吗?”答案是肯定的。
我们相信我们已经消除了导致此次事故的风险。我们采取了以下措施来确保我们平台的完整性:
- 我们已经轮换了所有生产机器并循环使用了所有访问密钥。
- 我们已经完成了对所有系统访问的审计。
- 我们正积极与第三方调查人员和我们的合作伙伴合作,以验证我们调查的步骤和行动。
你现在应该继续做什么
我们还想为所有客户提供更多有关我们建议行动的详细信息。
请轮换 CircleCI 储存的所有秘密。有多种方法可以做到这一点,我们鼓励你和你的团队使用你喜欢的方法。这里有一个你可以遵循的方法:
- OAuth 令牌:
- 对于 GitHub,这意味着去https://github.com/settings/applications,选择“授权 OAuth 应用”,然后撤销 CircleCI 条目。完成后,注销并重新登录 CircleCI 以触发重新授权。
- 对于比特桶:https://bitbucket.org/account/settings/app-authorizations/。
- 对于 GitLab: GitLab 用户不需要重新授权他们的应用程序访问。作为预防措施,我们仍然建议 GitLab 用户轮换他们的环境变量、个人和项目 API 令牌以及所有 SSH 密钥。
- 项目 API 令牌:要旋转它们,进入项目设置> API 权限>添加 API 令牌。
- 项目环境变量:进入项目设置>环境变量,然后创建一个同名的环境变量来替换现有的值。
- 上下文变量:转到组织设置>上下文,为每个上下文执行与项目环境变量相同的操作。
- 用户 API 令牌:进入用户设置>个人 API 令牌,然后删除并重新创建您可能正在使用的任何令牌。
- 项目 SSH 按键:进入项目设置> SSH 按键。删除部署密钥并再次添加它。如果您使用了任何额外的键,那么这些键需要被删除并重新创建。
- Runner 令牌:使用 CircleCI CLI,运行以下命令:
circleci runner token list <resource-class name>
circleci runner token delete <token identifier>
circleci runner token create <resource-class-name> "<nickname>"
- 按照这些命令,您需要将创建的令牌添加到您的 launch-agent-config.yml 中,并重新启动您的 runner 服务
注意:我们建议对所有项目(转到“项目设置”)、组织(转到“组织设置”)和用户(转到“用户设置”)都这样做。
除了这些说明,今天,我们创造了一个工具,用于在 CircleCI 上发现你的所有秘密。这将有助于您创建一个可操作的物品轮换列表。
其他安全建议
当客户正在轮换密钥、机密和变量时,为您的 CI/CD 管道配置添加额外的保护层可能会有所帮助。
所有客户都可以利用以下几点来提高管道安全性:
- 尽可能使用 OIDC 令牌以避免在 CircleCI 中存储长期有效的凭证。
- 利用 IP 范围将到您系统的入站连接限制到已知的 IP 地址。
- 使用上下文实现跨项目的环境变量共享,然后可以通过 API 自动旋转。
- 对于特权访问和附加控制,您可以选择使用 runners ,它允许您将 CircleCI 平台连接到您自己的计算和环境,包括 IP 限制和 IAM 管理。
解决客户关注的问题
我们还想解决一些客户对事件处理方式的担忧。作为一家深度投资于迭代和改进的公司,我们始终欢迎并感谢客户对事件管理的反馈。
以下是客户分享的几个问题:
-
如何访问 CircleCI 的审计日志? 我们已将自助审计日志的访问权限扩展至所有客户,包括免费客户。客户可以通过我们的 UI 访问自助审计日志。客户最多可以查询 30 天的数据,并有 30 天的时间下载生成的日志。虽然我们理解访问 CircleCI 审计日志的要求,但我们对所有客户的建议是将您的审计和调查重点放在 CircleCI 中存储了机密的任何系统的日志上。
-
为什么周三这么晚才发出这个? 我们了解到,1 月 4 日(星期三)太平洋时间下午 6:30/美国东部时间晚上 9:30 发布我们的轮换机密指南后,我们的许多北美客户经历了深夜和随叫随到的轮换。我们错在尽可能快地获取信息,以尽量减少任何潜在的暴露时间。我们也知道,作为一家几乎在每个国家都有客户的全球性公司,除了“尽快”之外,没有披露安全事件的好时机
-
此事件与您在博客上发布的12 月 21 日可靠性更新有关吗? 不是。作为我们客户沟通的一部分,我们会定期提供可靠性更新。作为此次事件的一部分,我们建议在 12 月 21 日至 1 月 4 日之间查看日志,而我们在 12 月 21 日更新可靠性的时间只是一个简单的巧合。
结论
我们要感谢我们的客户和社区对我们的支持,因为我们都在努力保持我们系统的安全。我们知道安全事故会给所有相关人员带来压力和负担,为此,我们深表歉意。随着更多细节的出现,我们将继续在这里提供更新和信息。我们还将继续在我们的论坛上与我们的客户和社区保持联系。
2023 年 4 月 1 日安全警报
我们想让你知道,我们目前正在调查一个安全事件,我们的调查正在进行中。我们将向您提供有关此事件的最新消息,以及我们的回应。此时,我们确信在我们的系统中没有未授权的参与者;然而,出于谨慎,我们希望确保所有客户也采取某些预防措施来保护您的数据。
行动请求
出于谨慎,我们强烈建议所有客户采取以下措施:
- 立即轮换 CircleCI 中存储的所有秘密。这些可能存储在项目环境变量或上下文中。
- 我们还建议客户在 2022 年 12 月 21 日至 2023 年 1 月 4 日期间,或在您的机密轮换结束后,查看其系统的内部日志,查看是否有任何未经授权的访问。
此外,如果您的项目使用项目 API 令牌,我们已经使它们无效,您将需要替换它们。你可以在我们的文档中找到关于如何做的更多信息。
我们对您工作的任何中断表示歉意。我们非常重视我们系统和客户系统的安全性。虽然我们正在积极调查这一事件,但我们承诺在未来几天与客户分享更多细节。
感谢您紧急关注旋转秘笈。
乔尔连续分娩测验
原文:https://circleci.com/blog/joel-test-for-continuous-delivery/
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
围绕更新乔尔测试或生产更多特定用途的变体有一点家庭手工业的形成。如果你不熟悉 Joel 测试,这是 Joel Spolsky“不负责任地快速”测量开发团队质量的方法。我觉得这些读起来很有趣,并想为后一类贡献一个——专门针对持续交付的 Joel 测试(在短周期内交付可以随时发布的软件的实践)。
我真的应该注意到,这个列表甚至比那个更具体——它几乎完全专注于 web 应用程序的连续交付。手机是一个完全不同的游戏,我希望在未来写更多。尽管如此,将这个列表放在一起帮助我明确了我自己对我们在 LaunchDarkly 使用的流程的想法,更重要的是,我们为什么要做一些我们正在做的事情。
这是我对乔尔测试的看法,持续交付版。
1.你使用分布式版本控制系统吗?
正如 Joel 自己指出的,分布式部分并不有趣。无论如何,每个常用的 DVCS 工作流都使用一个集中的存储库。DVCS 的有趣之处在于,它让分支和合并变得廉价。这很有趣,因为…
2.你使用特征分支吗?
3.你做基于拉式请求的代码审查和与分支构建的持续集成吗?
4.你有一个总是可部署的主(或发布)分支吗?
…如果有一个持续交付的主要指令,那就是主服务器必须始终可部署。它如此重要的原因是,它消除了围绕部署建立任何类型的沟通或协调过程的需要。如果你不得不在你的 Slack 或 HipChat 房间中@all 提及你的团队,并等待每个人给出解除警报,你就不能一天部署多次。
如果您能够保持 master(或者您的发布分支名称)总是可部署的不变量,这意味着您已经有了许多正确的过程。这是实现持续交付的必要条件,但不是充分条件。
特性分支、基于拉请求的代码审查和分支构建是帮助保持主可部署性的技术。它们确保没有经过充分审查和测试的代码不会被合并到 master 中。任何在这些步骤完成之前合并代码的工作流都违反了 prime 指令。
5.您是否每天都进行生产部署?
在 2010 年,这可能每个月都会发生。现在,每周部署是标准的,许多团队努力实现每日部署。尖端团队每天部署多次(例如,Flickr 每天部署 30-50 次)。这是一个雄心勃勃的目标——需要重大的文化变革和(在撰写本文时)定制工具。
6.您是否有一个暂存环境?
您的暂存环境需要尽可能接近您的生产环境。生产运行在 https 下?舞台也应该如此。在生产中使用 PubNub,Fastly,或者其他云服务?舞台也应该如此。否则,由于环境的差异,bug 会溜到生产中。分期应该是(半)稳定的。它的正常运行时间应该有几个 9。多少由你决定,但至少应该有一个 9。最好是领先的九。
此外,请注意,一个暂存环境是最低要求。理想情况下,团队中的任何人都应该能够构建一个类似生产的环境,其他人只需点击一个按钮,就可以看到完整的生产或类似生产的数据。一旦代码被推送到一个分支,团队中的任何人(尤其是非开发人员)都应该能够在几分钟内看到该代码在类似生产环境中的行为。
在我之前的一个团队中,我们建立了一个名为 Rainicorn 的系统,帮助我们实现了这一目标,这绝对改变了游戏规则。这是我开始黑暗发射时希望能随身携带的系统之一。
7.你使用特征标志吗?
无论您的试运行环境和测试有多复杂,生产流量都是完全不同的东西。功能标志开启了黑暗启动的能力,允许您通过逐步推出功能来降低风险。功能标志将部署与展示分开。这些步骤通常在持续部署管道中合并在一起——最好的团队理解这些是不同的步骤,并使用特性标志来区分这两个步骤。
8.你有跨职能团队吗(开发、设计、产品)?
如果你想每天向你的用户交付新的代码和新的价值,你需要建立一个反馈回路,在这个回路中,设计、项目管理和开发尽快地将用户的反馈整合到产品中。除非你的团队坐在一起有效地合作,否则很难让反馈环足够紧密。
9.你有关于生产的用户分析吗?
这又回到了拥有一个有效的反馈回路。快速行动通常意味着押注于应该建造什么以及如何建造——而其中一些押注将是错误的。如果你没有足够的数据,就不可能对正在发生的事情进行提炼和调整。用户通过使用你的产品不断地告诉你什么是有效的,什么是无效的——但是如果你没有设置用户分析(例如 Mixpanel 或 KISSMetrics ),你就没有在倾听。
10.团队中的任何人都可以通过一个按钮部署到生产环境中吗?
请记住,一个团队应该是一个跨职能的团体——这意味着要达到这个目标,你的设计师,你的项目经理,每个人都应该能够部署到生产中。
很少有团队能做到这一点,根据我的经验,通常有三个因素的组合在起作用:
- 工具问题
- 质量问题
- 合规/法律限制
我在生产部署中看到的最常见的问题是,很难构建这样做所必需的工具。它通常需要大量的技术投资,通常是消除技术债务和支撑部署基础设施的某种组合。幸运的是,部署工具在过去几年中已经变得非常好了。我想过举出一些具体的例子,但它们都归结为 Docker。所以我就和 Docker 一起去。码头工人码头工人。
除了工具问题,最常见的反对让任何人轻松部署的理由是代码质量。质量问题通常归结为过程问题(例如,不保持主可部署性)或者(不太常见的)文化/信任问题。
我不能帮助或说太多关于(3),除了指出,你应该推回遵守限制,如果你可以的话。在某些情况下,过于保守的建议是在不了解它们对生产力的影响的情况下做出的。萨班斯-奥克斯利法案是否意味着只有您专门的发布工程师才能部署到生产环境中?我不知道,我也不是律师,但我打赌 Docker 能帮上忙。也许微服务也是。
约翰·科杜马尔是launch darky的首席技术官和联合创始人。LaunchDarkly 帮助软件团队启动、测量和控制他们的特性。此前,John 是 Atlassian 的开发经理和 Coverity 的架构师。他拥有加州大学伯克利分校的编程语言和类型系统博士学位,以及哈维马德学院的学士学位。他一直在努力改进 launch crystally 的持续交付工作流程。
Hacktoberfest 2019 -啤酒节| CircleCI
原文:https://circleci.com/blog/join-the-party-get-your-hacktoberfest-prs-with-circleci-orbs/
Hacktoberfest 是由 DigitalOcean 和 DEV 举办的为期一个月的开源软件庆典。它鼓励所有技能水平的个人通过在十月份完成开源项目的拉请求来参与开源软件。Hacktoberfest 对整个全球社区开放,关于活动的更多细节,如何参与,以及奖品,可以在这里找到。
今年,CircleCI 将以我们自己的活动参与 Hacktoberfest,我们称之为#Orbtoberfest!我们甚至在派对上加入了自己的特别 Hacktoberfest 礼品。通过向球体捐款获得您的 Hacktoberfest PRs,并获得双倍奖励!
阅读规则,看看你能赢得什么这里。
*CircleCI orbs 是开源的、可重用的配置包,可以帮助加快开发速度,并将您最喜欢的工具连接到您的 CI 管道。可重用的配置增加了一致性,减少了错误,并让开发人员轻松地将 CircleCI 与他们喜欢的工具集成:从通信到安全到部署。此外,创建 orb 既简单又有趣:任何能够编写 bash 脚本的人都可以构建 orb 。
从#啤酒节开始,在 Github 上找到标有orbtoberfest
的问题,并发送您的 PRs。直接在 GitHub 上找到orbtoberfest
问题或者询问水晶球你下一步应该尝试什么!你可以在我们的 Hacktoberfest 页面上浏览一些建议的问题,困难从更新文档到添加全新的功能。
您也可以在我们的社区论坛上加入讨论。
CircleCI 全体员工祝你黑客生涯愉快!
阅读更多关于 CircleCI orbs 的信息:*
用 k6 | CircleCI 调度负载测试和持久化输出
原文:https://circleci.com/blog/k6-results-output-and-scheduling-pipelines/
本教程涵盖:
- 导出 k6 测试结果
- 持续 k6 结果输出
- 为负载测试安排 CI/CD 管道
在这个 k6 系列中,我已经用 k6 介绍了 HTTP 请求测试,用 k6 介绍了性能测试。我设计这些教程是为了向你介绍 k6,并向你展示如何使用 k6 进行微服务的性能测试。作为 k6 系列的第三篇教程,这将涵盖如何在本地存储 k6 测试结果,以及如何使用 CircleCI 的调度管道特性来调度负载测试。
先决条件
要学习本教程,您需要:
- JavaScript 的基础知识
- HTTP 请求和测试的基本知识
- 您系统上安装的 Node.js (版本> = 10.13)
- 一个圆的账户
- GitHub 的一个账户
如果你还没有完成 k6 性能测试教程,我建议你在开始本教程之前完成。
本教程直接建立在“用 k6 进行性能测试”教程的基础上。你的第一步是为教程克隆的 GitHub 库。
您可以使用以下命令克隆 GitHub 存储库:
git clone https://github.com/CIRCLECI-GWP/api-performance-testing-with-k6.git
太好了!你已经准备好开始学习了!
持续 k6 结果输出
在使用 k6 进行性能测试的教程中,我介绍了如何将 k6 指标和日志输出到云中,以便整个团队进行分析和查看。在本教程中,我将引导您在运行测试的机器上本地持久化测试输出。
k6 提供了多个导出测试结果的选项,供您以后查看。支持 k6 导出的服务包括 Amazon CloudWatch、Apache Kafka、Influx DB、New Relic、Prometheus、Datadog 等。本教程重点介绍如何使用 JSON 输出获得相同的结果。
要在 k6 上运行外部测试结果输出,您需要定义--out
标志。您在之前的教程中以稍微不同的方式完成了这项工作。在这种情况下,您将在之前克隆的存储库中为 Heroku API 运行一个简单的负载测试。
负载测试在 30 秒内运行多个虚拟用户。在 30 秒内,测试:
- 使用 API 创建多个
todo
项目。 - 确保每个 Todo 项都是使用应用程序端点创建的。
下面是这个测试的代码片段:
// create-todo-http-requests.js file
import http from 'k6/http';
import { check, group } from 'k6';
import { Trend } from 'k6/metrics';
const uptimeTrendCheck = new Trend('/GET API uptime');
const todoCreationTrend = new Trend('/POST Create a todo');
export let options = {
stages: [
{ duration: '0.5m', target: 3 }, // simulate ramp-up of traffic from 0 to 3 users over 0.5 minutes.
],
};
export default function () {
group('API uptime check', () => {
const response = http.get('https://todo-app-barkend.herokuapp.com/todos/');
uptimeTrendCheck.add(response.timings.duration);
check(response, {
"status code should be 200": res => res.status === 200,
});
});
let todoID;
group('Create a Todo', () => {
const response = http.post('https://todo-app-barkend.herokuapp.com/todos/',
{ "task": "write k6 tests" }
);
todoCreationTrend.add(response.timings.duration);
todoID = response.json()._id;
check(response, {
"status code should be 200": res => res.status === 200,
});
check(response, {
"response should have created todo": res => res.json().completed === false,
});
})
}
create-todo-http-requests.js
文件中的第一个group()
块运行正常运行时间检查,以验证 API 正在响应。另一个块创建todo
项,并验证它们是否被正确创建。
您可以用命令k6 run test-file
运行这个测试。因为我们想从这个测试中得到更多,我们将使用一个不同的命令。除了验证正确执行之外,我们还希望看到 JSON 文件中的执行日志。
为此,调用--out outputFile.json
标志。这个标志确保您的测试响应以 JSON 格式记录。执行以下命令:
k6 run create-todo-http-request.js --out json=create-todo-http-request-load.json
该测试正常执行,但也会创建一个文件:create-todo-http-request-load.json
。这个新文件包含该运行中所有测试的执行信息。
一旦 k6 在定义了测试度量输出的情况下执行,它就会记录该输出,并在测试执行时显示出来。
测试执行后,您可以打开输出文件,并在 JSON 文件中查看结果输出。
JSON 输出文件包含 k6 用来生成数据的指标。您还可以查看:
- 负载测试运行时发出的所有请求
- 请求的时间戳
- 执行每个请求所花费的时间
使用这些数据,您可以确定最慢和最快的响应,并识别瓶颈。这不仅让您更好地理解您的测试,而且生成的结果输出文件是永久的。这使得数据导出成为一种有效的方式,可以持久化数据以供以后查看,并将该运行与其他测试运行进行比较。既然您已经尝试了导出 JSON,那么您可以尝试导出其他文件类型,比如 CSV 或 XML。
使用 CircleCI 调度管道调度负载测试
执行负载测试需要时间,并且会占用大量系统资源。只有当没有用户访问您的系统时,或者当您知道对应用程序用户的影响很小甚至没有影响时,您才可能希望运行负载测试。另一种方法是建立一个模拟生产环境的并行负载测试环境。
作为 CI/CD 从业者,您知道资源将永远是一个限制因素,质量将永远是一个优先事项。这使得设置调度管道来运行负载测试成为一个很好的选择。
使用调度管道,您可以自动化运行负载测试的过程,以便它们只在您预计系统中用户数量有限时发生。
在 CircleCI 上设置预定管道
要将您的管道配置为按计划运行,请转到 CircleCI 仪表板并选择项目设置。选择项目。在这种情况下,使用api-performance-testing-with-k6
项目。单击项目旁边的省略号(…)。
点击触发。
在触发页面,点击添加预定触发显示触发表单。
当您希望执行负载测试时,填写表单以添加执行负载测试的触发器。对于本教程,将触发器配置为每周在22:00 UTC
的Sunday
只执行一次。保存计划的管道。
您现在有了一个管道,它将每周运行一次,以确保您的负载测试正常工作。您可以根据需要编辑时间表。您可能会忘记执行您的负载测试,但是 CircleCI 总是会在您选择的准确时间为您执行。要了解更多关于计划管道如何帮助您优化开发时间和资源使用的信息,请阅读计划持续集成管道的好处。
结论
在本教程中,我们回顾了如何使用 k6 运行负载测试,并添加了将测试指标导出到外部目的地。在这种情况下,它是一个 JSON 文件,但是您也可以将其导出为 CSV 或 XML。我们还介绍了创建调度管道来执行负载测试的过程。我们能够具体管理 CircleCI 管道执行负载测试的条件和时间。我希望这些信息对您团队的工作有所帮助。
通过完成本系列中的其他两个教程,以您所学到的知识为基础:
Waweru Mwaura 是一名软件工程师,也是一名专门研究质量工程的终身学习者。他是 Packt 的作者,喜欢阅读工程、金融和技术方面的书籍。你可以在他的网页简介上了解更多关于他的信息。
Kubernetes 部署-使用 CloudFlare 的 Kubernetes | circle ci
原文:https://circleci.com/blog/k8s-deployments-with-cloudflare/
这篇文章是关于多云部署的系列文章的第二篇。第一篇文章“用 CircleCI 和 Jib 对 Java 应用程序进行 Dockerizing”,使用谷歌的 Jib 项目来简化在 Docker 中包装 Java 应用程序。这篇文章介绍了全局负载平衡器背后的多云部署。
在第一篇帖子中,我们讨论了构建容器化的应用程序,并发布了一个简单的带有 Google Jib 的 Java Spring Boot 应用程序。该过程在构建和组装我们的应用程序时是有效的,但是我们没有解决分发问题。在本帖中,我们将了解一种将 Docker 应用程序部署到 Kubernetes 并通过 Cloudflare 的域名系统(DNS)和代理服务实现全球访问的方法。
我们将在这个博客中使用几种技术,并且需要对 CircleCI 有一个基本的了解。如果你是 CircleCI 的新手,我们有介绍性内容帮助你入门。
本文中使用的代码位于 Github 上的一个示例库中。你可以参考https://github.com/eddiewebb/circleci-multi-cloud-k8s获得完整的代码样本,或者跟随。以下是我们将要构建的工作流:
凭证管理
我将凭证管理放在这篇关于部署的文章的最前面,因为它是成功的 CI/CD 管道的基础。在我们开始推送我们的配置之前,我们将设置我们对 Docker Hub(可选)、谷歌云平台(GCP)和 Cloudflare 的访问。
向 Docker Hub 认证
我的示例工作流使用 Docker Hub 作为容器化应用程序的注册中心。或者,你可以将它们推送到谷歌或亚马逊的注册中心。如何进行身份验证将取决于您的应用。这个示例应用程序使用 Maven 加密来验证 Google Jib 与 Docker Hub 的连接,正如我们在第一篇帖子中所描述的。
使用谷歌云平台进行认证
GCP 是我们选择托管 Kubernetes 集群的 IaaS 提供商。要开始使用 GCP 认证,你需要创建一个服务用户密钥,如谷歌文档中所述。当提示您下载密钥时,选择新的.json
格式。您还需要为服务帐户分配角色。要成功创建集群和部署,您的服务帐户需要“Kubernetes 引擎集群管理”和“Kubernetes 引擎开发人员”这些角色可以在 Google 控制台的主身份和访问管理(IAM)选项卡上授予。
密钥文件包含敏感信息。我们从不希望将敏感信息签入源代码。为了向我们的工作流公开它,我们将把该文件编码为一个字符串,并将其保存为一个环境变量:
base64 ~/Downloads/account-12345-abcdef1234.json
复制输出并将其保存为 CircleCI 项目配置中的环境变量:
稍后,当我们在部署作业中需要它时,我们可以再次使用 base64 将其解码回一个文件:
echo "${GCP_KEY_FILE}" | base64 --decode >> gcp_key.json
我觉得有必要发布标准预防措施:base64 不是加密,它没有增加安全性,也没有对敏感值进行真正的混淆。我们使用 base64 作为将文件转换成字符串的手段。对 base64 值的保护级别与密钥文件本身的保护级别相同,这一点非常重要。
使用 Cloudflare 进行认证
我们部署的最后一步是使用 Cloudflare 将 justademo.online 的流量路由回我们的各个集群。我们需要一个 Cloudflare API 密钥、一个 DNS 区域和一个电子邮件地址。因为这些都是简单的字符串,所以可以按原样添加。
CLOUDFLARE_API_KEY
-可在个人资料和域仪表板中找到CLOUDFLARE_DNS_ZONE
-可在域仪表板上找到CLOUDFLARE_EMAIL
-用于创建 Cloudflare 帐户的电子邮件
使用 CircleCI 将 Docker 容器部署到 Kubernetes
我发现谷歌云的 Kubernetes 支持直观且易于上手。他们的gcloud
命令行界面(CLI)甚至提供了将所需配置注入 Kubernetes kubectl
CLI 的方法。有很多提供商,尽管这篇博客文章中没有提到,但是样本代码确实包含了使用kops
部署到亚马逊网络服务(AWS)的内容。
配置命令行工具
CircleCI 更喜欢确定性和显式配置,而不是插件。这使我们能够控制在我们的配置中使用的环境和版本。我们要做的第一件事是安装我们的 CLI 工具:
记住:您可以从我们的示例项目的配置中复制文本。
因为上面的步骤在每次构建时都要安装 CLI,所以我们的工作会增加大约 10-15 秒。对于您经常使用的工具,值得花时间打包您自己的定制 Docker 图像。
安装了gcloud
和kubectl
工具后,我们可以开始创建集群和部署。GCP Kubernetes 环境中的一般流程是:
- 创建一个定义虚拟机数量和大小的集群来运行我们的工作负载。
- 创建一个部署,它定义了用作我们的工作负载的容器映像。
- 创建一个负载平衡器服务,将 IP 和端口映射到我们的集群。
在gcloud
做这些之前,我们需要向它提供凭证和项目信息。这是我们将 GCP 密钥“再水合”成可读文件的步骤:
运行您的首次部署
CLI 现在可以访问我们的服务帐户。我们将使用该访问权限来创建我们的集群:
gcloud container clusters create circleci-k8s-demo --num-nodes=2
该命令应该一直阻塞,直到集群创建成功。这允许我们将容器映像作为工作负载提供。gcloud
CLI 也会自动为我们将所需的权限保存到kubectl
中:
kubectl run circleci-k8s-demo --image=${DOCKER_IMAGE} --port 8080
变量DOCKER_IMAGE
以标准注册表格式定义了我们的容器的完整坐标。如果你关注了我们之前关于用 Jib 版本化 Java 容器的文章,那么DOCKER_IMAGE
将会是:eddiewebb/circleci-k8s-demo:0.0.1-b8
。
与 create cluster 命令不同,创建或更新部署的命令是异步的。我们需要对我们的集群进行一些基本的冒烟测试。我们使用 rollout status 命令等待成功部署。该命令非常完美,因为它等待最终状态,并且在成功部署时会以 0 退出:
kubectl rollout status deployment/circleci-k8s-demo
我们的容器现在运行在几个节点上,在端口 8080 上有不同的 IP。在我们离开 GCP 之前,要做的最后一件事是设置本地负载平衡器,将流量从单个 IP 发送到集群中的所有容器。为此,我们使用了kubectl
的expose
命令:
kubectl expose deployment circleci-k8s-demo --type=LoadBalancer --port 80 --target-port 8080
为了确保一切都已启动并且路由成功,我们可以运行一个基本的健康检查:
正如你在上面的冒烟测试工作中可能注意到的,我们不知道 Google 将提前分配的 IP 地址。在生产环境中,您可能会选择购买和分配保留 IP,而不是使用浮动 IP。你可以在我们的 AWS 部署中看到这样的例子,它是样本项目的一部分。
使用 Cloudflare 路由您的域
每个云提供商都公开了将动态集群 IP 分配给已知域名的方法。我想展示我们如何为来自多个云提供商的应用提供服务,并动态解决分布和负载问题。除了提供简单的 API 来管理我们的 DNS,Cloudflare 还允许我们通过他们的代理路由流量,以获得额外的缓存和保护。此外,由于这是在它自己的步骤中运行,我们需要再次从kubectl
中获取CLUSTER_IP
。然后,我们将在对 Cloudflare 的 DNS 的POST
调用中使用该 IP:
注意:因为我们稍后要在负载平衡器中使用这个子域,所以禁用“代理”很重要。
这将把 GCP 集群映射到一个子域。我们的全球负载平衡器将使用该子域在多个云提供商之间分配流量。
酷!成功了吗?如果您已经完成了,您将拥有一个类似于 k8sgcp.justademo.online 的活动子域,通过 Cloudflare 路由到您在 Google Cloud 中的 Kubernetes 集群。
使用 CircleCI 更新 GCP 上现有的 Kubernetes 部署
我们上面的例子创造了一切新鲜的东西。如果您尝试重新运行该部署,将会失败!Google 不会创建已经存在的集群、部署或服务。为了使我们的工作流正常运行,我们必须增加我们的原始解决方案来考虑当前的状态。
我们可以使用get
和describe
命令来确定我们的栈是否已经存在,并运行适当的更新命令。
创建群集或获取凭据:
运行部署或更新映像:
我们的示例项目使用的逻辑将根据当前状态创建或更新所有方面。
或者,对于更健壮的部署,您将希望探索可以定义和应用的 Kubernetes 部署定义的使用,将配置捕获为一个.yml
文件。更多信息可以在 Kubernetes 示例中找到。
添加 AWS 部署
AWS 为部署容器提供了几个选项,包括 Fargate、ECS 和从 EC2 实例构建自己的集群。您可以查看我们的示例项目中的deploy-AWS
作业来探索一种方法。我们使用kops
项目从 EC2 和弹性负载平衡器(ELB)提供一个集群,将流量路由到该集群。
与这篇博文的主题密切相关的是,我们现在有了跨多个云提供商运行的相同应用程序。接下来,我们将实现一个全局负载平衡器,在它们之间分配流量并设置我们的 DNS。由于kops
为我们创建了一个 ELB,设置 DNS 会有所不同。
用 Cloudflare 把它绑在一起
与 Cloudflare 的 DNS 服务一样,他们的流量服务公开了用于管理负载平衡器配置的完整 API。这使得我们可以在 CircleCI 中直接自动化这项任务。
在 Cloudflare 中创建负载平衡器
我选择不写这个部分,因为这真的是一次性的事情。创建一个负载平衡器,它与您希望位于所有云前面的主域相关联:
您需要创建至少一个源池。如果您已经运行了部署,您可以指定我们在上面为 GCP 集群创建的子域:
您还将被要求定义一个健康监视器。我选择了一个简单的GET
到应用程序上的/build-info
端点,期望得到 200 响应:
一旦创建了初始负载平衡器和 GCP 原始池,我们就可以继续编写 AWS 集成的脚本,使用在每个部署上创建的动态 AWS ELB 来更新第二个原始池。
在部署时更新我们的 Cloudflare 源池
如上所述,在 AWS 中使用kops
时,它会自动创建一个路由到 Kubernetes 集群的 ELB。我们不需要在 Cloudflare 中设置 AWS 域,而是使用 Amazon 提供的域。我们需要将它传递给 Cloudflare。使用kubectl get service
可以获得 ELB 的名称:
因为 ELB 可能不会立即准备好,所以我们将该逻辑放在一个循环中:
结果应该是两个健康的源池,在单独的云提供商中运行,都在单个域后面:
我们在顶级域名上使用代理和 SSL 强制,所以请继续查看https://justademo.online/。
摘要
这篇文章探讨了 Docker 应用程序的创建和跨多个云提供商的部署。我们使用 Google 对 Kubernetes 集群的原生支持,而在 AWS 中,我们从头开始创建集群。最后,我们使用 Cloudflare 作为全局负载平衡器和 DNS 提供商,使我们能够实现对最终用户透明的多云部署。请查看示例项目,其中包含这篇文章中涉及的所有内容的完整示例代码。
航运愉快!
私钥-秘密环境变量-秘密屏蔽| Circleci
原文:https://circleci.com/blog/keep-environment-variables-private-with-secret-masking/
在接下来的几周内,我们将推出一个名为秘密屏蔽的新功能。这将使项目和上下文中的环境变量变得模糊,并阻止它们在用户的构建中出现。我们刚刚开始对所有用户进行增量部署,在接下来的几周内,这个特性将在我们所有的资源/执行器类型上启用。
这对我有什么影响?
使用秘密屏蔽,如果你不小心echo
,或者打印了你的环境变量或上下文,我们用XXXXX
来代替它。在 UI 打印出构建日志之前,CircleCI 会扫描日志的输出,以确保没有打印出与项目或上下文环境变量名称相匹配的秘密。
你为什么建造它?
我们的客户要求针对意外秘密暴露的保护措施,我们很高兴现在能够提供这种额外的保护级别。我们认为这是采取主动和明智的安全措施的重要一步。例如,在有许多未知贡献者的开放源码项目中,或者在有许多人在库上工作的大公司中,无意中的秘密泄露可能是一个相当大的问题。在这两种情况下,意外泄露机密的成本都很高。
使用秘密屏蔽的安全提示
从 CircleCI 的权限角度来看,有权访问构建的用户被认为是可信的。由于这个原因,秘密屏蔽与受限上下文很好地配对,因为它限制了被允许授权访问受限上下文组的可信用户的集合。如果一个用户已经可以访问您的源代码,并且可以插入恶意的命令,那么秘密屏蔽将不会对那些用户正在运行的命令提供万无一失的防御。
秘密掩盖在对参与者有信任的模型中工作得很好。有时候,构建脚本很容易和意外地混淆在提交和输出构建秘密中。虽然秘密屏蔽可以有效地防止人们无意中打印出秘密,但它不会阻止任何人使用 SSH 并手动运行echo
。它也不会阻止某人编写或使用这个秘密,并将值上传到其他地方。
如果你处理的是不可信的参与者,那么切换到一个分叉模型并且不传递任何秘密,或者限制被允许运行注入了秘密的任务的参与者,这要安全得多。
通过结合使用受限上下文和秘密屏蔽,您可以通过防止有意和无意的秘密泄露来实现相当大的安全性改进。
需要注意的几个问题:
- 4 个字符以下的秘密不会被屏蔽。
- 值为
true
、TRUE
、false
、FALSE
的秘密不会被屏蔽。 - 这并不妨碍在您 SSH 到一个构建中时显示机密。
如果你没有看到你的团队已经可用的秘密掩蔽,你应该在接下来的几周内获得它。一旦启用,您将可以自动使用它。同时,如果你泄露了秘密,请尽快轮换你的秘密。
首次访问
如果你想让你的团队排在访问列表的第一位,请在这里注册:https://forms.gle/ncmgri9bWSHSLh2k9。
善良被低估了
事实是,人们需要知道我对事情的立场。我不能只说“请不要这样”,因为人们不会听。我说“在互联网上,没有人能听到你的微妙”,我是认真的。我也绝对不愿意欺骗别人。我也遇到过这种情况——没有足够明确地告诉人们我不喜欢他们的方法,他们继续重新设计一些东西,当我不愿意接受他们的工作时,他们变得非常沮丧。
- Linus Torvalds,在 2013 年 7 月 15 日发给 Linux 内核邮件列表的电子邮件中(来源)
我一直怀疑莱纳斯对事物的看法是科技行业的主导观点。有一个直言不讳的批评合唱(“这糟透了!”,“你做错了!”,“只有白痴才会……”)从黑客新闻评论线程,到酒吧里的对话,再到更正式的工作环境中的技术会议。可以肯定的是,它的流行是有正当理由的,其中最主要的是一个真实的事实,即不应该以礼貌的名义牺牲愿景或明确的目标。也就是说,我认为它如此普遍的真正原因是它很容易。
几年前,我有幸阅读了杰夫·贝索斯对普林斯顿大学 2010 届毕业生的演讲。如果你还没有读过,我强烈建议你花几分钟去读一读。在视频中,贝佐斯谈到了他的祖父在他们每年夏天都会进行的一次越野公路旅行中给他们上的一课:“杰夫,有一天你会明白,善良比聪明更难。”
他继续说道:
今天我想和你谈的是天赋和选择的区别。聪明是一种天赋,善良是一种选择。礼物很简单——毕竟是别人送的。选择可能很难。如果你不小心,你可能会用你的天赋诱惑自己,如果你这样做了,很可能会对你的选择不利。
善良的选择可能很难,尤其是当你沮丧的时候,或者由于你的天赋,你知道有人犯了你本可以轻易避免的错误。比较容易直接;不要太在意他人的情绪;想要明确拒绝。
让我们来谈谈做出善良的选择。
在过去的几年里,我一直在思考这个问题。如果我们诚实的话,Linus 可以承受他的感受(并相应地运行 Linux 内核邮件列表)。Linux 几乎在各个层面上都取得了巨大的成功,尽管其不可否认的杰出的首席架构师偶尔会以惊人的方式失败。
然而,对于我们大多数人来说,莱纳斯的观点是站不住脚的。作为合作努力的一般陈述,直言不讳的批评会伤害团队的凝聚力和士气;有时间和精力损失在伤害感情、损害控制、团队成员之间信任的丧失上——更不用说人们在一个从根本上来说不够人性化的环境中工作。直截了当似乎更快更容易,但作为一种策略,这是因小失大。
然而,仅仅知道直言不讳的批评从长远来看可能伤害更大并不会真的让善良的选择变得更容易。那么是什么呢?
信任。
信任有助于建立友善的文化,原因有二:
- 作为建议或批评的接受者,你认为即使是措辞最礼貌的评论或观察也值得调查和审视。
- 作为一个评论者,你不必说得很强烈就知道你的信息会被听到。
仅此而已。如果你真的相信并确信你的同事没有浪费你的时间,当他们说什么的时候,你就会注意——引用 Linus 的例子,“请不要这样做”,最终成为你可以得到的措辞更强烈的指导之一。
在思考和撰写这篇文章的过程中,我回顾了 CircleCI 的各种问题和请求中的评论,我想举一些例子来说明我的意思。
对丹尼尔来说,这可能需要想象一些东西是如何被扩展的
对于 David 来说,他经常关注更大的基础设施问题。
对艾伦来说,这通常是要求对清晰性和目的做出解释:
在 CircleCI,我们每个人在工作过程中都有不同的表达方式,但共同点是对同事的温柔和信任。这种信任的一部分是因为我们知道,我们的同行会倾听我们的意见,而我们不必诉诸强硬的语言,但这种信任的更大一部分来自于我们都犯错误这一基本认识。这些错误可大可小,但我们相信,温和地询问某件事是否是事故或遗漏细节的结果是可以的,而不是假设他们只是习惯性地犯错。
我必须指出,至少在这里,这种信任是有充分根据的。在 CircleCI 工作的每一个人,无论他们的经验水平或关注领域如何,都是聪明、勤奋和体贴的——简而言之,值得信任。我们不遗余力地确保我们只雇佣那些我们有信心可以信任的人,以及那些我们有信心能够回报我们的人。
在一天结束的时候,可能会有这样做不奏效的地方,但是根据经验,我们可以说它对我们来说非常有效,而且我们没有计划在短期内改变。而且,就在你我之间,你真的想在一个没有足够的信任来允许仁慈的地方工作吗?
…我不这么认为。
如果你是那种一想到在一个基于信任和友善的环境中工作就兴奋不已的人,那么我们正在招聘!
在黑客新闻上讨论这篇文章
Kubernetes 和 CircleCI orbs:开发您的项目,而不是您的部署管道
虽然微服务架构和容器的兴起加快了许多人的开发周期,但在生产中管理它们却带来了新的复杂性,因为团队需要考虑管理这些服务的负载平衡和分布。Kubernetes 是一个管理分布式容器化应用程序的强大工具,但是管理 Kubernetes 本身是一个复杂的过程,有一个陡峭的学习曲线。Kubernetes 解决方案不仅难以实现,而且难以更新和调试。
降低 Kubernetes 环境管理复杂性的一种方法是将其直接集成到 CI/CD 管道中。CircleCI orbs 只需几行代码,就可以轻松地将集成添加到 Kubernetes 和容器管理的其他工具和服务中。orb 是 CircleCI config 的可重用、可共享的开源包,支持这些服务的即时集成。使用 orbs,您可以获得一个现成的解决方案来管理管道中最重要的服务。
使用这些 orb 部署和管理 Kubernetes 服务和环境:
Google Kubernetes 引擎 新
部署 Kubernetes 集群,并在几秒钟内从您的 CI 渠道更新生产代码
亚马逊弹性容器服务(EKS) 新
使用 AWS 上的 Kubernetes 部署、管理和扩展容器化应用
Azure Kubernetes 服务 全新
借助 Azure Kubernetes 服务,运输速度更快、操作更轻松、扩展更自信
Red Hat open shiftnew
自动化 Kubernetes 应用程序的构建、部署和管理
Kublr 新
在您的所有环境中集中部署、运行和管理大型企业的多集群 Kubernetes 部署
赫尔姆 新
用赫尔姆图表查找、分享和使用为 Kubernetes 构建的软件
Nirmata
通过 Nirmata API 跨开发、测试、试运行和生产环境管理 Kubernetes 应用
VMware 代码流 新
发布更高质量的应用和更快的 IT 代码,同时降低运营风险
DeployHub
自动化您工作流程的发布步骤,为您管道中的每个状态创建 100%可重复的部署流程
使用这些 orb 存储、管理和保护容器映像:
Amazon EC2 Container Registry
使用 Amazon 轻松存储、管理和部署容器映像
Google Container Registry
使用 Google Cloud Registry 存储、管理和保护您的 Docker 容器映像
Docker Hub
创建、管理、构建和运送你团队的容器应用到任何地方
Azure Container Registrynew
通过轻松存储和管理 Azure 部署的容器映像,简化 Kubernetes 容器开发
我们的合作伙伴在说什么:
VMware CMBU 产品营销主管 Ken Lee 表示:“对于 CI/CD 而言, VMware 代码流和 VMware 云组装 orb 用于创建 PKS 群集和自动化应用程序部署是一个很好的起点。
“CircleCI 和 Nirmata 为客户提供了跨任何基础设施无缝自动化其从开发、部署到运营和优化的容器化应用生命周期管理的能力。CircleCI 专注于加速大规模应用程序开发,而 Nirmata 则为客户提供了单一管理平台,用于在基于 Kubernetes 的多云环境中部署和管理这些应用程序。CircleCI 和 Nirmata 携手实现了企业业务敏捷性的承诺,同时显著降低了云原生应用生命周期管理的总拥有成本,”Nirmata 客户成功副总裁 Anubhav Sharma 说道。
“随着开发团队转向 Kubernetes,他们将开始脱离单片 CD 管道。CircleCI 正在通过 DeployHub orb 进一步增强这一能力,以支持微服务共享、配置和发布,”DeployHub 首席执行官兼联合创始人特雷西·拉冈说。
“Kublr 创建了一个 orb,使 CircleCI 用户能够在 Kubernetes 集群上自动构建、测试和部署应用程序。CircleCI 用户可以自动向 Kublr 平台认证,以查询和利用 Kublr API。CircleCI 作业将能够创建 kubernetes 集群,检查集群的状态,并检索集群配置文件,以便在 Kubernetes 上部署应用程序,”Kublr 首席技术官 Oleg Chunikhin 说。
你能做什么
你还想用 Kubernetes 做些别的什么事情吗?orb 是开源的,所以在一个现有的 orb 上增加功能只是获得你的 PR 批准和合并的问题。查看 orbs 注册表中所有可用的 orbs。你有没有一个用例让你觉得与当前的 Kubernetes orbs 不同?你可以自己创作一个并贡献给社区。我们甚至发布了为 orb 创建自动化构建、测试和部署管道的最佳实践(第 1 部分和第 2 部分)来帮助您。
借助微服务,您的团队可以利用第三方服务和操作系统,无需在内部开发通用工具和资源。有了 orb,您的团队只需要知道如何使用这些服务,而不需要知道如何集成或管理它们。
如何在 CircleCI 版本中使用 LaunchDarkly 特性标志
原文:https://circleci.com/blog/launchdarkly-feature-flags-in-your-circleci-builds/
利用 CircleCI 来支持其持续集成和交付的组织了解尽早和经常部署的好处:降低风险、减少冲突和更快地为客户实现价值。将 CI/CD 与特性标志结合起来,允许现代开发团队和组织以他们自己的速度编写和发布代码,同时仍然确保客户和用户获得最佳的体验。
代码参考
我们构建了一个名为代码引用的新功能,它可以在您的代码中显示 LaunchDarkly 功能标志的所有实例。我们通过利用一个名为ld-find-code-refs
的作为 orb 可用的实用程序,以不允许对您的源代码进行启动加密访问的方式构建代码引用。这个ld-find-code-refs
实用程序扫描您部署的代码,并将代码片段发送回 LaunchDarkly,以轻松确保标记在您期望的位置,并在不再需要标记时确保它们被删除。
发射黑暗之球
每次进行部署时,launch crystally orb 都使用ld-find-code-refs
将更新的代码片段发送回 launch crystally。通过使用 orb 将代码引用嵌入到您的标准部署工作流中,您团队中的每个人都有信心在部署时将所有新代码包装在一个标志中,并且在向所有客户和用户发布新功能时安全地删除了所有功能标志。
易于集成
这是一个标准 orb 配置的示例:
version: 2.1
orbs:
launchdarkly: launchdarkly/ld-find-code-refs@1.0.0
workflows:
main:
jobs:
- launchdarkly/find-code-references:
debug: true
access_token: '${LD_ACCESS_TOKEN}'
proj_key: YOUR_LAUNCHDARKLY_PROJECT_KEY
repo_type: github
repo_url: YOUR_REPO_URL
context_lines: 3
在这个例子中,我们有一个配置,将repo_type
设置为 GitHub,将repo_url
设置为 GitHub URL。我们建议配置这些参数,以便 launch crystally 能够生成指向您的源代码的引用链接。
在这里了解更多关于 launch crystally 的代码引用,在这里获取宝珠。
包扎
在 launch crystally,我们使用 CircleCI 一天多次部署到生产中。随着我们的成长,我们希望让开发团队更容易确保他们部署的代码在部署时安全地包装在功能标志中,并帮助团队在功能向所有用户和客户公开后删除功能标志。有了代码引用,就很容易确定 Git 存储库中的哪些文件引用了您的特性标志,这使得清理和移除技术债务变得容易。
如果您想了解更多信息,请务必注册参加 4 月 3 日的特别launch blackly+circle ci 网络研讨会。
2.0 -持续集成工作流| CircleCI
原文:https://circleci.com/blog/launching-today-circleci-2-0-reaches-general-availability/
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
我们的使命是让团队尽最大努力。今天发布的 CircleCI 2.0 代表着在这条道路上前进了一大步。经过 5 年多的时间和 6500 多万次构建,我们对最有效的工程团队的工作方式有了更多的了解。他们很早就做出承诺,而且经常这样做,新想法会立即得到认可。他们可以迅速发现问题,甚至更快地解决问题。实现 CI/CD 可以让团队更快地发布更好的软件,让个人开发人员无所畏惧地开发代码和创新。随着 CircleCI 2.0 的发布,我们吸取了多年来与开发人员合作的经验,并为团队提供了他们一直要求的帮助他们更好地工作的东西:
强大:先进的缓存策略和灵活的资源分配加快了构建速度。通过使用 SSH 访问和本地构建快速排除故障和修复,加快构建周期并确保代码质量。
灵活性:无限制运行 CI/CD:在环境更新时不暂停工作。语言包容性让您的团队可以自由使用任何工具链或框架。
控制:使用工作流让团队将构建-测试-部署阶段作为单独的工作运行,允许团队完全定制他们的开发过程。
我们重新设计了我们的构建引擎,在速度和控制上都取得了很大的进步。在 CircleCI 2.0 测试版发布以来的 7 个月里,超过 4000 家组织已经在这个新平台上运行了 100 多万个版本。他们非常积极地宣传这极大地改进了他们的构建流程:
“工作流+ Docker 缓存在 CircleCI 2.0 中表现出色。我对我们能用这个做什么感到兴奋!在 CircleCI 上,我们的 Docker 构建时间已经从大约 12 分钟缩短到不到 2 分钟。太棒了。"——乔希·多佛,高级软件工程师,CrateJoy
你可以在这里阅读更多关于早期 2.0 用户所说的。
但是我们还没有开发完。在这个基于云的软件产品的时代,一个好的产品是永远做不完的。CircleCI 2.0 也不例外。下一步是什么?对我们新定价的深入解释(现有用户注意:除非您升级您的计划,否则您的定价不会改变),以及针对我们的移动和防火墙后客户的 CircleCI 2.0。
所以,在这里。本着持续发布、反馈和迭代的精神,我们渴望听到您对 CircleCI 2.0 的看法!请在此与我们分享您的想法。
最后,我们要特别感谢所有使用我们的测试版、提供了令人难以置信的反馈并帮助我们将 CircleCI 2.0 发展到现在的水平以及它将继续发展的团队。
什么是 py torch-py torch | circle ci 简介
原文:https://circleci.com/blog/leading-open-source-ml-advancements-an-introduction-to-pytorch/
PyTorch 是由脸书人工智能研究小组创建的开源深度学习平台。像 NumPy 一样,PyTorch 是一个张量运算库,但增加了对 GPU 和其他硬件加速的支持,以及为人工智能研究人员探索不同领域提供的有效工具。虽然 PyTorch 最初是 Lua Torch 框架的基于 Python 的后继者,但它的范围已经扩大,不仅是一个研究平台,还是一个部署平台。像斯坦福和 T2 这样的组织依靠 PyTorch 来推动他们的机器学习研究和产品。PyTorch 依靠 CircleCI 为其众多开源贡献者创建了一个平稳可靠的 PR 流程,并且比以前的解决方案更快地交付代码,开销也更少。
PyTorch 团队有近 100 名核心成员(脸书内部和外部),此外还有 900 多名开源贡献者和 6 名普通维护者。
“我们接触的东西都是活跃的研究领域,如量子化和低精度神经网络。”
他们的图书馆还支持数百个下游学术和商业项目。“我们接触的东西都是积极研究的领域,如量化和低精度神经网络,”在 PyTorch 工作的脸书产品经理 Joe Spisak 说。“我们认为这些是理所当然的,因为我们今天对翻译和计算机视觉等事物进行了低精度的万亿次预测。但在这个领域,人们仍然发表大量论文,他们正在寻找新的做事方式。”团队在 PyTorch 上的工作为那些进行最新研究的人提供了重要的工具。
为社区优先考虑开源
对于大多数人来说,在盈利性公司中管理开源社区的需求可能是一个挑战,但对于 PyTorch 来说,这是他们是谁的核心。“开源从一开始就存在于我们的 DNA 中,”Spisak 说。“当脸书在 2004 年问世时,它是建立在一个开源堆栈上的,包括 Linux、Apache、MySQL 和 PHP,因此,开源一直是脸书工程部门核心文化的一部分。”脸书的开源努力与公司一起成长,使 PyTorch 团队和人工智能部门的其他人能够为世界各地的研究人员提供重要的工具,并建立一个庞大的互联项目网络。
“我们致力于开源我们所做的大部分事情,”Joe 继续说道,“当我们觉得项目是稳定的,并且我们认为它们可以有益于社区或研究时,它就是开源的。我们维护它们,并从 PyTorch 等许多项目中创建社区。”
大规模运行操作系统的 CI/CD
超过 900 名 PyTorch 贡献者依靠 CircleCI 来管理他们的软件交付工作流。该团队的堆栈包括 CircleCI、Netlify 和 GitHub 等 CDN 服务。这给了他们一个一致的过程来处理任何提交或拉取请求,不管这个项目有多少贡献者。
“这种类型的系统可以从小项目扩展到大项目,”脸书 OSS 项目的开发者倡导者 Joel Marcey 说。“为这个项目做出贡献的每个人都知道他们将获得一致的体验,并且可以看到他们的公关是通过还是失败。”
“为这个项目做出贡献的每个人都知道他们将获得一致的体验,并且可以看到他们的公关是通过还是失败。”
“CircleCI 帮助我们以更灵活的方式做事,”脸书开源开发者倡导者 Eric Nakagawa 说。“我们团队直接面对的最大挑战之一就是在网站上创建教程。这个过程以前需要四到六个小时,当你试图在这里或那里进行小的调整时,可能会非常沮丧。”使用 CircleCI,Eric 的团队能够在多个实例类型上并行运行他们的构建,这使得他们的构建时间减少了 75%。更重要的是,通过将自己从保持机器运转的日常顾虑中解放出来,他们能够专注于项目跳动的心脏:他们的社区。
“通过允许我们不关注基础设施,我们能够将[我们的努力]瞄准我们认为可以提供更多价值的地方,这是我从与 CircleCI 的合作中获得的最大收获之一。”
跟上 PyTorch 和脸书·艾
要了解更多关于 PyTorch 和脸书其他人工智能的发展,请访问 https://ai.facebook.com/的 T2。
了解我们的新用户界面-现在在公共测试版- CircleCI
原文:https://circleci.com/blog/learn-about-our-new-ui-now-in-public-beta/
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
在 CircleCI,迭代是我们公司的核心原则之一。最近,我们将注意力转向内部,并自豪地推出了我们的新用户界面,现已推出公测版。从今天开始,我们所有的用户都可以通过点击一个按钮来选择加入新的设计。
随着我们的成长,我们面临着在保持设计简单和不中断的同时增加功能的挑战。我们一直在与我们的用户合作,他们是内部圈子(我们的私人测试用户组)的一部分,我们直接根据他们的反馈添加功能并做出改变。
最新消息:亮点
我们知道您非常依赖构建列表和构建细节页面,我们的努力和改进在我们的设计过程中给予了这些页面更高的优先级。以下是我们新设计的主要亮点:
在图像上移动滑动条,对比构建列表页面的新旧用户界面。
构建列表
新的构建列表显示用户 repo/分支名称,GitHub 头像,拉请求,SHA,开始时间,构建持续时间和日志。用户现在可以点击 PR 或者 SHA 直接在 GitHub 上看到变化。构建列表页面增加的功能是能够随时取消任何排队或运行项目的构建,即使它没有显示完整的队列。
在图像上移动滑动条,对比构建详细信息页面的新旧用户界面。
构建详细信息
我们的构建详细信息页面得到了改进,具有更突出的上下文和围绕测试失败的操作,因此您可以轻松地看到什么被破坏,并将您的宝贵时间花在重要的事情上——获得绿色构建。
树枝采摘者
新的重新设计的分支选择器适应长回购和分支名称。用户可以在“我的和所有的”、“最近的”或“按回购”过滤器之间进行选择,以查看他们感兴趣的回购或分支机构。
构建输出
新的构建输出页面是全幅的,便于阅读。提交 ID 位于顶部,帮助用户查看当前构建中的更改。
上下文栏
为了便于导航,增加了一个新的粘性上下文栏。用户可以查看他们当前所在的页面,并点击上下文栏来浏览站点。
重复,重复
这是我们新用户界面的首次展示。这一重新设计的目标是提供更好的、信息丰富的、一致的体验,以及提供视觉上更愉悦的东西。我们将继续致力于我们的设计,以改善整体 CircleCI 体验。我们希望你喜欢这个新流程,并期待听到你的想法,根据你的反馈做出新的改变。
我们的新设计提供了更好、更丰富和一致的用户体验。试试我们的新造型,然后给我们发邮件,sayhi@circleci.coml 让我们知道你的想法!
PS:有兴趣加入内圈,第一时间了解新功能?您可以选择加入我们的新测试计划,并在向公众发布功能和设置之前获得独家访问权。
了解如何建立一个有弹性的软件团队
原文:https://circleci.com/blog/learn-how-to-build-a-resilient-software-team/
2020 年的过山车突出了运行良好的软件交付团队提供的竞争优势。在新冠肺炎来袭的那一刻,每个人都必须变得不仅是远程优先,而是仅远程优先,许多工程团队被迫考虑他们现有的手动流程的数量。突然间,他们不再依赖于某人的桌子下有一台构建机器的事实,如果那台机器有问题,他们可以重启它。突然间,他们需要自动化一切。
这种自动化的想法,即能够快速可靠地移动,不再是“拥有就好”,而是当今软件交付团队的核心职责。我有幸不仅领导了一家专注于向工程师交付价值的开发人员至上的公司,还目睹了成千上万世界上最好的团队如何将他们的代码从想法转移到交付。虽然我对如何最好地将自动化引入工程团队有很多强烈的意见,但很明显,最先进、最强大的自动化工具需要一个关键因素来取得成功:人。
对于企业来说,疫情的第一阶段是关于系统:更新和建立你的技术栈,这样你就可以完全远程操作。我相信下一个阶段将是团队和人。
(听我们的播客第一集,有目的的软件工程,Brad Henrickson 在自信提交。)
经验教训:建立弹性软件团队
拥有一个足够大的团队来处理日常事务,同时能够创新,这是至关重要的。在一个用户期望和需求急剧增加的世界里,尤其如此。您需要足够多的个人贡献者来处理不断增加的维护和升级开销。如果你的团队太小,创新的步伐就会变慢。
保持软管中的压力——前进动力的感觉——对你的团队和你的客户来说都是至关重要的。你需要保持稳定的创新心跳。通过不断地回答社区的问题,您提供了一个一致的节奏和信心,相信正在取得进展。
弹性软件交付团队的分解应该是这样的:
- 你所做的 50%是针对以用户为中心的特性——你正在做的事情将会改善你的用户的生活。
- 25%集中在技术投资上—您需要做什么来维护您的系统?它会在哪里翻倒?
- 25%集中在升级和缺陷上——系统是否没有按照预期的方式运行?系统崩溃了,阻止用户构建吗?
随着您的系统变得越来越大,专门用于维护系统的时间自然也会增加。你必须扩大你的团队规模,以保持以用户为中心的工作比例。最终,团队变得太大,这时你就要把他们分成更小的小组。想想亚马逊的两个披萨法则。
虽然理想的团队规模将取决于经验、职责的总范围、随叫随到的负担等等,但 5 到 20 名代码贡献者是合适的目标。当然,在沟通和协调成本变高的情况下,有一个规模上限,但是您总是希望有足够多的开发人员来维护服务并继续创新它。
我认为,尤其是现在,更大的团队更好的另一个原因是,2020 年向我们展示了生活是如何阻碍我们最好的计划的。如果你的团队太小,有人去长期休假,有紧急情况,或者只是需要休息,你就没有足够的火种来让火继续燃烧。
你想让你的团队有足够的人手来应对生活的冲击。
分布式团队呢?
如果构建弹性团队总体上很难,那么构建弹性分布式团队就更难了。当我们分布在不同的地点和时区时,所有团队面临的许多挑战都会加剧。建立和领导团队的最有效的方法就是不容易应用。这意味着作为领导者,我们经常需要更有创造力。
根据我的经验,成功的领导者专注于建立结构和支持联系、沟通和协作。
作为人类,我们努力与一个更大的目标联系在一起,但我们也想感觉与周围的人联系在一起。当我们在视频通话中以像素的形式或在聊天应用程序中以图标的形式与人互动时,我们很容易忘记屏幕的另一端也有人。首先也是最重要的,我们需要对我们的队友是谁感到好奇,并知道是什么驱使他们。
作为领导者,我们也有责任确保团队中的每个人都清楚自己的期望。当您的组织处于高速增长模式时尤其如此。几年前,CircleCI 的工程团队逐年增加一倍,并变得更加全球化。相反,管理团队小得令人难以置信。在所有这些增长之后,我们在发展我们的工程文化方面遇到了挑战。我们的知识非常孤立,我们需要找到一条前进的道路,让我们能够扩展业务。
因此,我们创建了一个工程能力矩阵,它融入了我们所做的一切。从招聘到结构化反馈,再到绩效评估,它帮助我们让每个人都遵循相同的标准,并在我们扩展时明确预期。
协作也很重要。使用分布式团队,很容易意外地开发出按时区划分的“子团队”。避免这种情况的一种方法是鼓励跨时区工作,即所谓的“乒乓”配对。乒乓配对在原理上类似于传统的配对编程,但却是异步的。
为了便于乒乓操作,我们发现以下方法很有用:
- 将我们正在进行的工作限制为 3 张卡。这鼓励我们在开始任何新的事情之前寻找已经在进行中的票。
- 尽快发布进行中的拉请求(PRs)。很自然地,除非有人正在积极地处理某项任务,否则任何人都应该能够通读 PR(和其他相关的沟通)并继续处理它。
- 移交正在进行的工作。无论是在空闲时间异步进行,还是在人们的工作时间发生重叠时进行视频通话,尤其是在一天的边缘,一个人的一天即将结束,而另一个人的一天刚刚开始。
归根结底,您的平台或服务以及技术的弹性当然依赖于良好的 CI/CD 实践、基于云的监控、测试和其他 DevOps 流程。但是人的因素是必不可少的。
当你开始看到性能滞后时,那就是疲劳。作为商业领袖,我们有责任创建有弹性的系统和团队,无论是现在还是将来。创建一个高效的软件交付团队的最重要的组成部分之一是优先考虑运行它的人。
如果你想了解更多关于建立一个弹性软件团队的信息,请查看我们的 2020 年软件交付报告。
本文最初发表于DevProJournalT3
基础设施即代码,第 2 部分:使用 Terraform | CircleCI 构建 Docker 映像并部署到 Kubernetes 集群
本系列向您展示如何开始使用基础设施即代码(IaC)。目标是通过教程和代码示例帮助开发人员加深对 IaC 的理解。
在这篇文章中,我将演示如何为应用程序创建一个 Docker 映像,然后将该映像推送到 Docker Hub。我还将讨论如何使用 HashiCorp 的 Terraform 创建 Docker 映像并将其部署到 Google Kubernetes Engine (GKE)集群。
下面是我们将在这篇文章中完成的事情的快速列表:
- 构建新的 Docker 映像
- 将新的 Docker 映像推送到 Docker Hub 注册表
- 使用地形创建一个新的 GKE 集群
- 使用 Terraform Kubernetes 提供者创建一个新的 Terraform Kubernetes 部署
- 摧毁所有使用地形创造的资源
注意: 在你开始学习这部分教程之前,确保你已经完成了第 1 部分的先决条件部分中的所有操作。
我们的第一个任务是学习如何基于包含在代码报告中的 Node.js 应用程序构建一个 Docker 镜像。
建立码头工人形象
在之前的帖子中,我们使用 Terraform 创建了一个新的 GKE 集群,但该集群不可用,因为没有部署任何应用程序或服务。因为 Kubernetes (K8s)是一个容器编排器,应用和服务必须打包到 Docker 镜像中,然后这些镜像可以衍生出 Docker 容器来执行应用或服务。
Docker 镜像是使用 docker build
命令创建的,你将需要一个Docker 文件来指定如何构建你的 Docker 镜像。我将讨论 Dockerfiles,但首先我想解决。dockerignore 文件。
是什么?dockerignore 文件?
.dockerignore
文件排除了与其中声明的模式相匹配的文件和目录。使用该文件有助于避免不必要地将大型或敏感的文件和目录发送到守护程序,并有可能将它们添加到公共映像中。在这个项目中,.dockerignore
文件排除了与 Terraform 和 Node.js 本地依赖相关的不必要的文件。
了解 Dockerfile 文件
Docker 文件对于构建 Docker 映像至关重要。它指定了如何构建和配置映像,以及向其中导入什么文件。Dockerfile 文件是动态的,因此您可以用不同的方式完成许多目标。对 Dockerfile 功能有一个扎实的了解是很重要的,这样您就可以构建功能性映像。这是这个项目的代码报告中包含的 docker 文件的细目分类。
FROM node:12
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
COPY package*.json ./
RUN npm install --only=production
# Bundle app source
COPY . .
EXPOSE 5000
CMD [ "npm", "start" ]
FROM node:12
行定义了一个要继承的图像。构建映像时,Docker 从父映像继承。在这种情况下,它是从 Docker Hub 获取的node:12
图像,如果它不在本地的话。
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
COPY package*.json ./
RUN npm install --only=production
这个代码块定义了WORKDIR
参数,它指定了 Docker 映像中的工作目录。COPY package*.json ./
行将任何与包相关的文件复制到 Docker 映像中。RUN npm install
行安装在package.json
文件中列出的应用程序依赖项。
COPY . .
EXPOSE 5000
CMD [ "npm", "start" ]
这个代码块将所有文件复制到 Docker 映像中,除了在.dockerignore
文件中列出的文件和目录。EXPOSE 5000
行指定为这个 Docker 映像公开的端口。CMD [ "npm", "start" ]
行定义了如何开始这个图像。在这种情况下,它正在执行该项目的package.json
文件中指定的start
部分。该CMD
参数是默认的执行命令。现在您已经理解了 Dockerfile 文件,您可以使用它在本地构建一个映像。
使用 Docker 构建命令
使用 docker build
命令,Dockerfile 根据其中定义的指令构建一个新的映像。在构建 Docker 映像时,需要记住一些命名约定。如果您计划共享图像,命名约定尤其重要。
在我们开始构建图像之前,我将花点时间来描述如何给它们命名。Docker 图像使用由斜杠分隔的名称组件组成的标签。因为我们将把图像推送到 Docker Hub ,所以我们需要在图像名称前加上我们的 Docker Hub 用户名。对我来说,那就是ariv3ra/
。我通常会在后面加上项目的名称,或者图像的有用描述。这个 Docker 镜像的全名将会是ariv3ra/learniac:0.0.1
。:0.0.1
是应用程序的版本标签,但是您也可以用它来描述图像的其他细节。
一旦你有了一个好的、描述性的名字,你就可以建立一个形象。以下命令必须从项目 repo 的根目录中执行(确保将ariv3ra
替换为您的 Docker Hub 名称):
docker build -t ariv3ra/learniac -t ariv3ra/learniac:0.0.1 .
接下来,运行以下命令查看机器上的 Docker 映像列表:
docker images
这是我的输出。
REPOSITORY TAG IMAGE ID CREATED SIZE
ariv3ra/learniac 0.0.1 ba7a22c461ee 24 seconds ago 994MB
ariv3ra/learniac latest ba7a22c461ee 24 seconds ago 994MB
Docker 推送命令
现在我们已经准备好将这张图片推送到 Docker Hub 并公开发布。Docker Hub 需要授权才能访问服务,所以我们需要使用 login
命令来认证。运行以下命令登录:
docker login
在提示中输入您的 Docker Hub 凭据以授权您的帐户。每台机器只需要登录一次。现在你可以推送图片了。
使用您的docker images
命令中列出的图像名称,运行以下命令:
docker push ariv3ra/learniac
这是我的输出。
The push refers to repository [docker.io/ariv3ra/learniac]
2109cf96cc5e: Pushed
94ce89a4d236: Pushed
e16b71ca42ab: Pushed
8271ac5bc1ac: Pushed
a0dec5cb284e: Mounted from library/node
03d91b28d371: Mounted from library/node
4d8e964e233a: Mounted from library/node
现在,您在 Docker Hub 中有了一个 Docker 映像,并准备好部署到 GKE 集群。将您的应用程序部署到新的 Kubernetes 集群的所有工作都已就绪。下一步是使用 Terraform 构建 Kubernetes 部署。
使用 Terraform 部署 Kubernetes
在本系列的第 1 部分中,我们学习了如何使用 Terraform 创建一个新的 Google Kubernetes 引擎(GKE)集群。正如我前面提到的,该集群没有提供任何应用程序或服务,因为我们没有向它部署任何应用程序或服务。在这一节中,我将描述使用 Terraform 部署 Kubernetes 部署需要什么。
Terraform 有一个 Kubernetes 部署资源,允许您定义一个并执行一个 Kubernetes 部署到您的 GKE 集群。在第 1 部分中,我们使用part01/iac_gke_cluster/
目录中的 Terraform 代码创建了一个新的 GKE 集群。在本文中,我们将分别使用part02/iac_gke_cluster/
和part02/iac_kubernetes_app/
目录。iac_gke_cluster/
是我们在第 1 部分中使用的相同代码。我们将在这里结合iac_kubernetes_app/
目录再次使用它。
Terraform Kubernetes 提供商
我们之前使用 Terraform Google 云平台提供商创建了一个新的 GKE 集群。Terraform 提供者是特定于 Google 云平台的,但它仍然是引擎盖下的 Kubernetes。因为 GKE 本质上是一个 Kubernetes 集群,我们需要使用 Terraform Kubernetes 提供者和 Kubernetes 部署资源来配置和部署我们的应用到 GKE 集群。
地形代码文件
part02/iac_kubernetes_app/
目录包含这些文件:
- providers.tf
- 变量. tf
- main.tf
- 部署. tf
- services.tf
- 输出. tf
这些文件维护我们用来定义、创建和配置 Kubernetes 集群应用程序部署的所有代码。接下来,我将分解这些文件,让您更好地理解它们的作用。
细分:providers.tf
在provider.tf
文件中,我们定义了将要使用的 Terraform 提供者: Terraform Kubernetes 提供者。provider.tf
:
provider "kubernetes" {
}
此代码块定义了将在此 Terraform 项目中使用的提供程序。{ }
块是空的,因为我们将用不同的进程处理认证需求。
细分:变量. tf
这个文件应该看起来很熟悉,并且类似于第 1 部分variables.tf
文件。这个特殊的文件只指定了这个 Terraform Kubernetes 项目使用的输入变量。
variable "cluster" {
default = "cicd-workshops"
}
variable "app" {
type = string
description = "Name of application"
default = "cicd-101"
}
variable "zone" {
default = "us-east1-d"
}
variable "docker-image" {
type = string
description = "name of the docker image to deploy"
default = "ariv3ra/learniac:latest"
}
此文件中定义的变量将在项目文件的代码块中用于整个 Terraform 项目。所有这些变量都有default
值,可以在执行代码时通过在 CLI 中定义它们来更改。这些变量为 Terraform 代码增加了急需的灵活性,并允许重用有价值的代码。这里需要注意的一点是,variable "docker-image"
默认参数被设置为我的 Docker 图像名称。将该值替换为您的 Docker 图像的名称。
细分:main.tf
main.tf
文件的元素以terraform
块开始,它指定了 Terraform 后端的类型。Terraform 中的一个“后端”决定了如何加载状态,以及如何执行一个操作,如apply
。这种抽象支持非本地文件状态存储和远程执行等。在这个代码块中,我们使用了remote
后端。它使用 Terraform 云,并连接到您在第 1 部分文章的先决条件部分创建的iac_kubernetes_app
工作空间。
terraform {
required_version = "~>0.12"
backend "remote" {
organization = "datapunks"
workspaces {
name = "iac_kubernetes_app"
}
}
}
细分:deployments.tf
接下来是对deployments.tf
文件中语法的描述。该文件使用 Terraform Kubernetes 部署资源来定义、配置和创建将我们的应用程序发布到 GKE 集群所需的所有 Kubernetes 资源。
resource "kubernetes_deployment" "app" {
metadata {
name = var.app
labels = {
app = var.app
}
}
spec {
replicas = 3
selector {
match_labels = {
app = var.app
}
}
template {
metadata {
labels = {
app = var.app
}
}
spec {
container {
image = var.docker-image
name = var.app
port {
name = "port-5000"
container_port = 5000
}
}
}
}
}
}
是时候回顾一下代码元素了,以便更好地理解正在发生的事情。
resource "kubernetes_deployment" "app" {
metadata {
name = var.app
labels = {
app = var.app
}
}
这个代码块指定了 Terraform Kubernetes 部署资源的使用,它为 Kubernetes 定义了我们的部署对象。metadata
块用于为 Kubernetes 服务中使用的参数赋值。
spec {
replicas = 3
selector {
match_labels = {
app = var.app
}
}
template {
metadata {
labels = {
app = var.app
}
}
spec {
container {
image = var.docker-image
name = var.app
port {
name = "port-5000"
container_port = 5000
}
}
}
}
}
在 resources spec{...}
块中,我们指定想要三个 Kubernetes pods 在集群中运行我们的应用程序。selector{...}
块代表标签选择器。这是 Kubernetes 的一个核心分组原语,允许用户选择一组对象。
资源template{...}
块中有一个spec{...}
块,它有一个container{...}
属性块。该块具有定义和配置部署中使用的容器的参数。从代码中可以看出,这是我们定义 pod 的 Docker image
(我们想要使用的图像)和容器的name
的地方,它应该出现在 Kubernetes 中。这也是我们定义在容器上公开的port
的地方,它将允许对正在运行的应用程序的入口访问。这些值来自同一文件夹中的variables.tf
文件。Terraform Kubernetes 部署资源能够执行非常健壮的配置。我鼓励您和您的团队尝试其他一些属性,以便更广泛地熟悉这个工具。
细目:services.tf
我们已经创建了一个 Terraform Kubernetes 部署资源文件,并为这个应用程序定义了我们的 Kubernetes 部署。剩下一个细节来完成我们的应用程序的部署。我们正在部署的应用程序是一个基本的网站。和所有的网站一样,它需要是可访问的才是有用的。此时,我们的deployments.tf
文件指定了使用 Docker 映像部署 Kubernetes pod 的指令以及所需的 pod 数量。我们的部署缺少一个关键要素:Kubernetes 服务。这是一种将运行在一组 pod 上的应用程序作为网络服务公开的抽象方式。使用 Kubernetes,您不需要修改应用程序来使用不熟悉的服务发现机制。Kubernetes 为一组 pods 提供它们自己的 IP 地址和一个 DNS 名称,并且可以在它们之间进行负载平衡。
在services.tf
文件中,我们定义了一个 Terraform Kubernetes 服务。它将连接 Kubernetes 元素,以提供对集群中 pods 上运行的应用程序的入口访问。这里是services.tf
文件。
resource "kubernetes_service" "app" {
metadata {
name = var.app
}
spec {
selector = {
app = kubernetes_deployment.app.metadata.0.labels.app
}
port {
port = 80
target_port = 5000
}
type = "LoadBalancer"
}
}
此时,描述一下spec{...}
块和其中的元素可能会有所帮助。selector{ app...}
块指定了一个在deployments.tf
文件中定义的名称,并表示部署资源中元数据块的label
属性中的app
值。这是一个重用相关资源中已经分配的值的例子。它还提供了一种机制来简化重要的值,并为像这样的重要数据建立一种形式的引用完整性。
port{...}
块有两个属性:port
和target_port
。这些参数定义了服务将侦听应用程序请求的外部端口。在本例中,它是端口 80。target_port
是我们的 pod 正在监听的内部端口,即端口 5000。该服务将所有流量从端口 80 路由到端口 5000。
这里要回顾的最后一个元素是type
参数,它指定了我们正在创建的服务的类型。Kubernetes 有三种服务。在这个例子中,我们使用的是LoadBalancer
类型,它使用云提供商的负载均衡器对外公开服务。外部负载平衡器路由到的 NodePort 和 ClusterIP 服务是自动创建的。在这种情况下,GCP 将创建并配置一个LoadBalancer
,它将控制流量并将其路由到我们的 GKE 集群。
细分:output.tf
Terraform 使用输出值返回 Terraform 模块的值,该模块在运行terraform apply
后向子模块提供输出。这些输出用于向父模块公开其资源属性的子集,或者在 CLI 输出中打印某些值。output.tf
块我们使用输出值来读出集群名称和我们新创建的负载平衡器服务的入口 IP 地址等值。这个地址是我们可以访问托管在 GKE 集群上的应用程序的地方。
output "gke_cluster" {
value = var.cluster
}
output "endpoint" {
value = kubernetes_service.app.load_balancer_ingress.0.ip
}
初始化地形零件 02/iac_gke_cluster
现在您已经对我们的 Terraform 项目和语法有了更好的理解,您可以开始使用 Terraform 配置我们的 GKE 集群了。将目录更改为part02/iac_gke_cluster
目录:
cd part02/iac_gke_cluster
在part02/iac_gke_cluster
中,运行以下命令:
terrform init
这是我的输出。
Initializing the backend...
Successfully configured the backend "remote"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "google" (hashicorp/google) 3.31.0...
The following providers do not have any version constraints in configuration,
so the latest version was installed.
To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.
* provider.google: version = "~> 3.31"
Terraform has been successfully initialized!
这太棒了!现在我们可以创建 GKE 集群了。
地形应用零件 02/iac_gke_cluster
Terraform 有一个命令,允许您在不实际执行任何操作的情况下,模拟运行和验证您的 Terraform 代码。该命令名为terraform plan
,它还绘制了 Terraform 将针对您现有的基础设施执行的所有操作和更改。在终端中,运行:
terraform plan
这是我的输出。
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# google_container_cluster.primary will be created
+ resource "google_container_cluster" "primary" {
+ additional_zones = (known after apply)
+ cluster_ipv4_cidr = (known after apply)
+ default_max_pods_per_node = (known after apply)
+ enable_binary_authorization = false
...
Terraform 将根据main.tf
文件中的代码为您创建新的 GCP 资源。现在,您已经准备好创建新的基础设施并部署应用程序了。在终端中运行以下命令:
terraform apply
Terraform 将提示您确认您的命令。键入yes
并按回车键。
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
Terraform 将在 GCP 建立新的 Google Kubernetes 引擎集群。
注意 : 集群需要 3-5 分钟才能完成。这不是一个即时的过程,因为后端系统正在进行配置并使其上线。
在我的集群完成之后,这就是我的输出。
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
cluster = cicd-workshops
cluster_ca_certificate = <sensitive>
host = <sensitive>
password = <sensitive>
username = <sensitive>
新的 GKE 集群已经创建,并显示了Outputs
结果。请注意,标记为敏感的输出值在结果中用<sensitive>
标记屏蔽了。这确保敏感数据受到保护,但在需要时可用。
接下来,我们将使用part02/iac_kubernetes_app/
目录中的代码来创建一个 Kubernetes 部署和附带的LoadBalancer
服务。
Terraform 初始化 part02/iac_kubernetes_app/
我们现在可以使用part02/iac_kubernetes_app/
目录中的代码将我们的应用程序部署到这个 GKE 集群。使用以下命令将目录更改为目录:
cd part02/iac_kubernetes_app/
在part02/iac_kubernetes_app/
中,运行此命令初始化 Terraform 项目:
terrform init
这是我的输出。
Initializing the backend...
Successfully configured the backend "remote"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "kubernetes" (hashicorp/kubernetes) 1.11.3...
The following providers do not have any version constraints in configuration,
so the latest version was installed.
To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.
* provider.kubernetes: version = "~> 1.11"
Terraform has been successfully initialized!
GKE 集群凭据
使用 Terraform 创建google_container_cluster
后,需要对集群进行身份验证。您可以使用 Google Cloud CLI 来配置集群访问,并生成一个 kubeconfig 文件。执行以下命令:
gcloud container clusters get-credentials cicd-workshops --zone="us-east1-d"
使用这个命令,gcloud
将生成一个使用gcloud
作为认证机制的 kubeconfig 条目。该命令使用cicd-workshops
值作为集群名称,该名称也在variables.tf
中指定。
Terraform 应用 part02/iac_kubernetes_app/
最后,我们准备使用 Terraform 将我们的应用程序部署到 GKE 集群。执行以下命令:
terraform plan
这是我的输出。
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# kubernetes_deployment.app will be created
+ resource "kubernetes_deployment" "app" {
+ id = (known after apply)
+ metadata {
+ generation = (known after apply)
+ labels = {
+ "app" = "cicd-101"
}
+ name = "cicd-101"
+ namespace = "default"
+ resource_version = (known after apply)
+ self_link = (known after apply)
+ uid = (known after apply)
}
...
Terraform 将根据deployment.tf
和services.tf
文件中的代码为您创建新的 GCP 资源。现在,您可以创建新的基础设施并部署应用程序。在终端中运行以下命令:
terraform apply
Terraform 将提示您确认您的命令。键入yes
并按回车键。
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
Terraform 将为您构建新的 Kubernetes 应用程序部署和相关的负载平衡器。
注意 : 集群需要 3-5 分钟才能完成。这不是一个即时的过程,因为后端系统正在进行配置并使其上线。
在我的集群完成之后,这就是我的输出。
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
endpoint = 104.196.222.238
gke_cluster = cicd-workshops
该应用程序现在已经部署。endpoint
值和输出是集群LoadBalancer
的公共入口的 IP 地址。它还表示您可以访问应用程序的地址。打开网络浏览器,使用output
值访问应用程序。将会出现一个网页,内容为“欢迎使用 CircleCI 学习 CI/CD 101!”。
使用地形摧毁
您已经证明您的 Kubernetes 部署工作正常,并且将应用程序部署到 GKE 集群已经过成功测试。你可以让它继续运行,但要知道,在谷歌云平台上运行任何资产都是有成本的,你要为这些成本负责。谷歌为其免费试用注册提供了慷慨的 300 美元信用,但如果你让资产运行,你可以很容易地吃掉它。
运行terraform destroy
将终止您在本教程中创建的任何正在运行的资源。
运行以下命令销毁 GKE 集群。
terraform destroy
请记住,上面的命令只会销毁part02/iac_kubeernetes_app/
部署,您需要运行以下命令来销毁在本教程中创建的所有资源。
cd ../iac_gke-cluster/
terraform destroy
这将破坏我们之前创建的 GKE 集群。
结论
恭喜你!您已经完成了本系列的第 2 部分,并且通过构建和发布新的 Docker 映像,以及使用基础设施作为代码和平台将应用程序配置和部署到 Kubernetes 集群,提升了您的体验。
继续本教程的第 3 部分,您将学习如何使用 CircleCI 将所有这些令人惊叹的知识自动化到 CI/CD 管道中。
以下资源将帮助您扩展知识:
基础设施代码第 1 部分:用 Terraform | CircleCI 创建一个 Kubernetes 集群
本系列向您展示如何开始使用基础设施即代码(IaC)。目标是通过教程和代码示例帮助开发人员加深对 IaC 的理解。
基础设施即代码(IaC)是现代持续集成管道不可或缺的一部分。它是使用机器可读的定义文件管理和调配云和 It 资源的过程。IaC 通过在代码中静态定义和声明计算资源,为组织提供了创建、管理和销毁这些资源的工具。
以下是本系列将涵盖的主题:
在本帖中,我将讨论如何使用 HashiCorp 的 Terraform 来供应、部署和销毁基础设施资源。在我们开始之前,您需要在目标云提供商和服务中创建帐户,如谷歌云和 Terraform 云。然后你可以开始学习如何使用 Terraform 创建一个新的 Google Kubernetes 引擎(GKE)集群
先决条件
在开始之前,您需要准备好这些东西:
这篇文章使用了这个回购的part01
文件夹中的代码。不过首先,你需要创建 GCP 凭证,然后 Terraform。
创建 GCP 项目凭据
GCP 凭证将允许您使用 IaC 工具执行管理操作。要创建它们:
- 转到创建服务帐户密钥页面
- 选择默认服务帐户或创建一个新帐户
- 选择 JSON 作为键类型
- 点击创建
- 将这个 JSON 文件保存在
~/.config/gcloud/
目录中(可以重命名)
HashiCorp Terraform 是如何工作的?
HashiCorp Terraform 是一款开源工具,用于安全高效地构建、更改和管理基础设施。Terraform 可以管理现有的服务提供商以及定制的内部解决方案。
Terraform 使用配置文件来描述运行单个应用程序或整个数据中心所需的组件。它生成一个执行计划,描述它将做什么来达到期望的状态,然后执行它来构建该计划所描述的基础结构。随着配置的变化,Terraform 会确定发生了哪些变化,并创建可用于更新基础架构资源的增量执行计划。
Terraform 用于创建、管理和更新基础设施资源,如物理机、虚拟机、网络交换机、容器等。Terraform 可以管理的组件包括低级基础架构组件,如计算实例、存储和网络,以及高级组件,如 DNS 条目和 SaaS 功能。
几乎任何基础设施类型都可以在 Terraform 中表示为资源。
什么是 Terraform 提供商?
提供者负责理解 API 交互和公开资源。提供商可以是 IaaS(阿里云、AWS、GCP、微软 Azure、OpenStack)、PaaS(比如 Heroku)或者 SaaS 服务(Terraform Cloud、DNSimple、Cloudflare)。
在这一步中,我们将使用 Terraform 代码在 GCP 提供一些资源。我们想编写 Terraform 代码,定义并创建一个新的 GKE 集群,我们可以在本系列的第 2 部分中使用它。
为了创建一个新的 GKE 集群,我们需要依靠 GCP 提供商与 GCP 进行交互。一旦定义和配置了提供者,我们就可以在 GCP 上构建和控制地形资源。
什么是 Terraform 资源?
资源是地形语言中最重要的元素。每个资源块描述一个或多个基础设施对象。基础设施对象可以是虚拟网络、计算实例或更高级别的组件,如 DNS 记录。一个资源块声明了一个给定类型(google_container_cluster
)的资源,它有一个给定的本地名称,比如“web”。该名称用于从同一 Terraform 模块的其他地方引用该资源,但它在模块范围之外没有任何意义。
理解 Terraform 代码
既然您对 Terraform 提供者和资源有了更好的理解,那么是时候开始深入研究代码了。Terraform 代码保存在目录中。因为我们使用的是 CLI 工具,所以您必须从代码所在的根目录中执行命令。对于本教程,我们使用的 Terraform 代码位于part01/iac_gke_cluster
文件夹这里。该目录包含这些文件:
- providers.tf
- 变量. tf
- main.tf
- 输出. tf
这些文件代表了我们将要创建的 GCP 资源基础结构。这就是地形的过程。您可以将所有的 Terraform 代码放在一个文件中,但是一旦语法变得越来越多,管理起来就会变得越来越困难。大多数 Terraform 开发人员为每个元素创建一个单独的文件。这里是每个文件的快速分解,并讨论每个文件的关键元素。
细分:providers.tf
provider.tf
文件是我们定义将要使用的云提供商的地方。我们将使用 google_container_cluster 提供者。这是provider.tf
文件的内容:
provider "google" {
# version = "2.7.0"
credentials = file(var.credentials)
project = var.project
region = var.region
}
该代码块在闭包{ }
块中有参数。credentials
块指定您之前创建的 GCP 凭证的 JSON 文件的文件路径。注意,参数值以var
为前缀。var
前缀定义了 Terraform 输入变量的用法,用作 Terraform 模块的参数。这允许在不改变模块自己的源代码的情况下定制模块的各个方面,并且允许在不同的配置之间共享模块。在配置的根模块中声明变量时,可以使用 CLI 选项和环境变量来设置它们的值。当您在子模块中声明它们时,调用模块将在模块块中传递值。
细分:变量. tf
variables.tf
文件指定了这个 Terraform 项目使用的所有输入变量。
variable "project" {
default = "cicd-workshops"
}
variable "region" {
default = "us-east1"
}
variable "zone" {
default = "us-east1-d"
}
variable "cluster" {
default = "cicd-workshops"
}
variable "credentials" {
default = "~/.ssh/cicd_demo_gcp_creds.json"
}
variable "kubernetes_min_ver" {
default = "latest"
}
variable "kubernetes_max_ver" {
default = "latest"
}
该文件中定义的变量将在整个项目中使用。所有这些变量都有default
值,但是这些值可以在执行 Terraform 代码时通过 CLI 定义来更改。这些变量为代码增加了非常必要的灵活性,并使重用有价值的代码成为可能。
细分:main.tf
main.tf
文件定义了我们的大部分 GKE 集群参数。
terraform {
required_version = "~>0.12"
backend "remote" {
organization = "datapunks"
workspaces {
name = "iac_gke_cluster"
}
}
}
resource "google_container_cluster" "primary" {
name = var.cluster
location = var.zone
initial_node_count = 3
master_auth {
username = ""
password = ""
client_certificate_config {
issue_client_certificate = false
}
}
node_config {
machine_type = var.machine_type
oauth_scopes = [
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring",
]
metadata = {
disable-legacy-endpoints = "true"
}
labels = {
app = var.app_name
}
tags = ["app", var.app_name]
}
timeouts {
create = "30m"
update = "40m"
}
}
以下是对从terraform
块开始的main.tf
文件的每个元素的描述。该块指定了地形后端的类型。Terraform 中的一个“后端”决定了如何加载状态,以及如何执行一个操作,如apply
。这种抽象支持非本地文件状态存储和远程执行。在这个代码块中,我们使用的是使用 Terraform Cloud 的remote
后端,它连接到您在先决条件部分创建的iac_gke_cluster
工作区。
terraform {
required_version = "~>0.12"
backend "remote" {
organization = "datapunks"
workspaces {
name = "iac_gke_cluster"
}
}
}
下一个代码块定义了我们将要创建的 GKE 集群。我们还使用了variables.tf
中定义的一些变量。resource
块有许多用于在 GCP 上供应和配置 GKE 集群的参数。这里的重要参数是name
、location
和Initial_node_count
,它们指定了将组成这个新集群的计算资源或虚拟机的初始总数。我们将从该集群的三个计算节点开始。
resource "google_container_cluster" "primary" {
name = var.cluster
location = var.zone
initial_node_count = 3
master_auth {
username = ""
password = ""
client_certificate_config {
issue_client_certificate = false
}
}
node_config {
machine_type = var.machine_type
oauth_scopes = [
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring",
]
metadata = {
disable-legacy-endpoints = "true"
}
labels = {
app = var.app_name
}
tags = ["app", var.app_name]
}
timeouts {
create = "30m"
update = "40m"
}
}
细分:output.tf
Terraform 使用一种叫做的输出值。这些函数返回 Terraform 模块的值,并为子模块提供输出。子模块输出向父模块公开其资源属性的子集,或者在运行terraform apply
后在 CLI 输出中打印某些值。以下代码示例中显示的output.tf
块输出值,以读出集群名称、集群端点以及敏感数据等值,这些值由sensitive
参数指定。
output "cluster" {
value = google_container_cluster.primary.name
}
output "host" {
value = google_container_cluster.primary.endpoint
sensitive = true
}
output "cluster_ca_certificate" {
value = base64decode(google_container_cluster.primary.master_auth.0.cluster_ca_certificate)
sensitive = true
}
output "username" {
value = google_container_cluster.primary.master_auth.0.username
sensitive = true
}
output "password" {
value = google_container_cluster.primary.master_auth.0.password
sensitive = true
}
初始化地形
现在我们已经介绍了 Terraform 项目和语法,您可以开始使用 Terraform 配置 GKE 集群了。将目录切换到part01/iac_gke_cluster
文件夹:
cd part01/iac_gke_cluster
在part01/iac_gke_cluster
中,运行以下命令:
terraform init
您的输出应该如下所示:
root@d9ce721293e2:~/project/terraform/gcp/compute# terraform init
Initializing the backend...
Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "google" (hashicorp/google) 3.10.0...
* provider.google: version = "~> 3.10"
Terraform has been successfully initialized!
使用地形预览
Terraform 有一个命令,允许您在不实际执行任何操作的情况下,模拟运行和验证您的 Terraform 代码。这个命令叫做terraform plan
。该命令还显示了 Terraform 将针对您现有的基础设施执行的所有操作和更改。在终端中,运行:
terraform plan
输出:
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# google_container_cluster.primary will be created
+ resource "google_container_cluster" "primary" {
+ additional_zones = (known after apply)
+ cluster_ipv4_cidr = (known after apply)
+ default_max_pods_per_node = (known after apply)
+ enable_binary_authorization = false
+ enable_intranode_visibility = (known after apply)
+ enable_kubernetes_alpha = false
+ enable_legacy_abac = false
+ enable_shielded_nodes = false
+ enable_tpu = (known after apply)
+ endpoint = (known after apply)
+ id = (known after apply)
+ initial_node_count = 3
+ instance_group_urls = (known after apply)
+ label_fingerprint = (known after apply)
+ location = "us-east1-d"
}....
Plan: 1 to add, 0 to change, 0 to destroy.
Terraform 将根据main.tf
文件中的代码为您创建新的 GCP 资源。
地形应用
现在,您可以创建新的基础设施并部署应用程序。在终端中运行以下命令:
terraform apply
Terraform 将提示您确认您的命令。键入yes
并按回车键。
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
Terraform 将在 GCP 建立新的 GKE 集群。
注意 : 集群需要 3-5 分钟才能完成。这不是一个即时的过程,因为后端系统正在进行资源调配并使其上线。
在我的集群完成之后,这是我的输出:
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
cluster = cicd-workshops
cluster_ca_certificate = <sensitive>
host = <sensitive>
password = <sensitive>
username = <sensitive>
新的 GKE 集群已经创建,并显示了Outputs
结果。请注意,标记为敏感的输出值在结果中用<sensitive>
标记屏蔽了。这确保敏感数据受到保护,但在需要时可用。
使用地形摧毁
现在您已经证明您的 GKE 集群已经成功创建,运行terraform destroy
命令来销毁您在本教程中创建的资产。你可以让它继续运行,但是要知道,在 GCP 上运行任何资产都是有成本的,你要对这些成本负责。谷歌为其免费试用注册提供了慷慨的 300 美元信用,但如果你让资产运行,你可以很容易地吃掉它。这取决于你,但是运行terraform destroy
将会终止任何正在运行的资产。
运行以下命令销毁 GKE 集群:
terraform destroy
结论
恭喜你!您刚刚完成了本系列的第 1 部分,并通过使用 IaC 和 Terraform 将 Kubernetes 集群配置和部署到 GCP,提升了您的体验。
继续本教程的第 2 部分。在第 2 部分中,您将学习如何为应用程序构建 Docker 映像,将该映像推送到存储库,然后使用 Terraform 将该映像作为容器部署到使用 Terraform 的 GKE。
这里有一些资源可以帮助你扩大知识面:
基础设施即代码,第 3 部分:使用 CI/CD 和 Terraform | CircleCI 自动化 Kubernetes 部署
本系列向您展示如何开始使用基础设施即代码(IaC)。目标是通过教程和代码示例帮助开发人员加深对 IaC 的理解。
在这篇文章中,我将演示如何创建持续集成和部署(CI/CD) 管道,这些管道自动化了本系列的第 1 部分和第 2 部分中涉及的地形 IaC 部署。下面是我们将在这篇文章中完成的事情的快速列表:
- 为项目构建一个新的 CircleCI .config.yml 文件
- 配置新的作业和工作流
- 自动执行 Terraform 代码以创建 Google Kubernetes 引擎(GKE)集群并部署应用程序
注意: 在你开始学习这部分教程之前,确保你已经完成了第 1 部分的先决条件部分中的所有操作。
我们将从快速解释什么是 CI/CD 开始,并回顾本系列教程的前两部分。然后你就可以开始了解这个代码报告中包含的 CircleCI .config.yml 文件。
持续集成和持续部署
CI/CD 管道帮助开发人员和团队自动化他们的构建和测试过程。CI/CD 创建了有价值的反馈循环,提供了软件开发过程的接近实时的状态。CI/CD 自动化还提供了一致的流程执行和准确的结果。这有助于优化这些流程,并有助于提高速度。用 CI/CD 简化开发实践正在成为团队中的普遍实践。理解如何集成和自动化重复的任务对于构建有价值的 CI/CD 管道至关重要。
在第 1 部分和第 2 部分中,我们使用 Terraform 创建了一个新的 GKE 集群和相关的 Kubernetes 对象,用于部署、执行和服务应用程序。这些地形命令是从我们的终端手动执行的。这在你开发 Terraform 代码或者修改它的时候很有效,但是我们希望自动执行这些命令。有许多方法可以使它们自动化,但是我们将把重点放在如何从 CI/CD 管道中完成。
什么是 CircleCI 管道?
CircleCI pipelines 是在您启动项目工作时运行的全套流程。管道包含您的工作流程,进而协调您的工作。这都是在项目配置文件中定义的。在本教程的下一节中,我们将定义一个 CI/CD 管道来构建我们的项目。
在 CircleCI 建立项目
在我们开始为这个项目构建一个config.yml
文件之前,我们需要将这个项目添加到 CircleCI 中。如果你不熟悉这个过程,你可以在这里使用设置 CircleCI 指南。一旦您完成了设置 CircleCI 部分,就此停止,以便我们可以配置项目级环境变量。
项目级环境变量
此管道中的某些作业将需要访问身份验证凭据,以便在目标服务上执行命令。在本节中,我们将定义一些作业所需的凭证,并演示如何将它们作为项目级环境变量输入到 CircleCI 中。对于每个变量,在名称字段中输入EnVar Name:
值,在值字段中输入凭证。以下是我们的管道将需要的凭证及其值的列表:
一旦上面所有的环境变量都就位,我们就可以开始在config.yml file
中构建我们的管道。
CircleCI config.yml
config.yml 文件是您定义要处理和执行的 CI/CD 相关作业的地方。在本节中,我们将为我们的管道定义作业和工作流。
在编辑器中打开.circleci/.config.yml
文件,删除其内容并粘贴以下代码:
version: 2.1
jobs:
version:
键指定运行该管道时要使用的平台特性。jobs:
键代表我们将为该管道定义的单个作业的列表。接下来,我们将创建管道将要执行的作业。
作业运行测试:
我鼓励您熟悉本 CircleCI 参考文档中的特殊按键、功能和特性,这将有助于您获得平台经验。下面是我们将要讨论的工作中每个关键点的概述和解释。
- docker: 是一个键,代表我们的作业将要执行的运行时
- 图像: 是一个键,表示 Docker 容器要用于这个作业
- 步骤: 是一个键,代表一个作业期间运行的可执行命令的列表或集合
- 签出: 是一个键,是一个特殊的步骤,用来将源代码签出到配置好的路径
- 运行: 是用来调用所有命令行程序的一个键
- store _ test _ results:是一个键,表示一个特殊的步骤,用于上传和存储一个构建的测试结果
- path: 是指向包含 JUnit XML 或 Cucumber JSON 测试元数据文件子目录的目录的路径(绝对路径,或相对于您的
working_directory
)
- path: 是指向包含 JUnit XML 或 Cucumber JSON 测试元数据文件子目录的目录的路径(绝对路径,或相对于您的
- store_artifacts: 是一个键,表示在 web 应用程序中或通过 API 存储可用性工件(例如日志、二进制文件等)的步骤
- 路径: 是用于保存作业工件的主容器中目录的路径
CI/CD 的一个有价值的好处是能够对新编写的代码执行自动化测试。它通过在每次修改代码时对代码执行测试来帮助识别代码中已知和未知的错误。
我们的下一步是在config.yml
文件中定义一个新任务。将以下内容粘贴到文件中:
run_tests:
docker:
- image: circleci/node:12
steps:
- checkout
- run:
name: Install npm dependencies
command: |
npm install --save
- run:
name: Run Unit Tests
command: |
./node_modules/mocha/bin/mocha test/ --reporter mochawesome --reporter-options reportDir=test-results,reportFilename=test-results
- store_test_results:
path: test-results
这是我们刚刚添加的内容的明细。
- docker: 和 image: 键指定了我们在这项工作中使用的执行者和 docker 图像
- 命令:
npm install --save
键安装应用程序中使用的应用程序依赖项 - 名称:运行单元测试执行自动化测试,并将它们保存到名为
test-results/
的本地目录中 - store_test_results: 是一个特殊的命令,它将
test-results/
目录结果保存并固定到 CircleCI 中的构建中
这项工作作为一个单元测试功能。它有助于识别代码中的错误。如果这些测试中的任何一个失败,整个管道构建都将失败,并提示开发人员修复错误。目标是通过所有的测试和作业。接下来,我们将创建一个构建 Docker 映像的作业,并将其推送到 Docker Hub registry 。
工单-构建 _docker_image
在本系列的第 2 部分中,我们手工创建了一个 Docker 映像,并将其推送到 Docker Hub 注册中心。在这项工作中,我们将使用自动化来完成这项任务。将该代码块追加到config.yml
文件中:
build_docker_image:
docker:
- image: circleci/node:12
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build Docker image
command: |
export TAG=0.2.<< pipeline.number >>
export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME
docker build -t $DOCKER_LOGIN/$IMAGE_NAME -t $DOCKER_LOGIN/$IMAGE_NAME:$TAG .
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
docker push $DOCKER_LOGIN/$IMAGE_NAME
这项工作相当简单。你已经遇到了它使用的大多数 CircleCI YAML 键,所以我将直接跳到name: Build Docker Image
命令块。
export TAG=0.2.<< pipeline.number >>
行定义了一个本地环境变量,该变量使用 pipeline.number 值将 Docker 标签值与正在执行的管道号相关联export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME
定义了我们将在命名 Docker 图像时使用的变量docker build -t $DOCKER_LOGIN/$IMAGE_NAME -t $DOCKER_LOGIN/$IMAGE_NAME:$TAG .
使用我们之前设置的项目级变量和我们指定的本地环境变量的组合来执行 Docker 构建命令echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
验证我们的 Docker Hub 证书以访问平台docker push $DOCKER_LOGIN/$IMAGE_NAME
将新的 docker 映像上传到 Docker Hub 注册表
这些应该看起来和感觉起来都很熟悉,因为这些命令与您在第 2 部分中手动运行的命令完全相同。在本例中,我们添加了环境变量命名位。接下来,我们将构建一个作业来执行构建 GKE 集群的 Terraform 代码。
作业- gke_create_cluster
在这项工作中,我们将自动执行在part03/iac_gke_cluster/
目录中找到的 Terraform 代码。将这个代码块附加到config.yml
文件,然后保存它:
gke_create_cluster:
docker:
- image: ariv3ra/terraform-gcp:latest
environment:
CLOUDSDK_CORE_PROJECT: cicd-workshops
steps:
- checkout
- run:
name: Create GKE Cluster
command: |
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
cd part03/iac_gke_cluster/
terraform init
terraform plan -var credentials=$HOME/gcloud_keys -out=plan.txt
terraform apply plan.txt
在这个代码块中需要注意的一点是执行器 Docker 图像image: ariv3ra/terraform-gcp:latest
。这是我创建的一个安装了 Google SDK 和 Terraform CLI 的图像。如果我们不使用它,我们将需要在这个作业中添加安装步骤,以便每次安装和配置工具。environment: CLOUDSDK_CORE_PROJECT: cicd-workshops
键也是一个重要的元素。这设置了我们稍后将执行的gcloud cli
命令所需的环境变量值。
代码块中使用的其他元素:
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
是解码$TF_CLOUD_TOKEN
值的命令,它创建 Terraform 访问相应 Terraform 云工作空间上的状态数据所需的./terraformrc
文件echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
是解码$GOOGLE_CLOUD_KEYS
值的命令,它创建了glcoud cli
访问 GCP 所需的gcloud_keys
文件gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
是一个命令,它授权使用我们之前解码并生成的gcloud_keys
文件访问 GCP
其余的命令是带有-var
参数的terraform cli
命令,这些命令指定并覆盖在各自 Terraform variables.tf
文件中定义的变量的default
值。一旦terraform apply plan.txt
执行,这个作业将创建一个新的 GKE 集群。
Job - gke_deploy_app
在这项工作中,我们将自动执行在part03/iac_kubernetes_app/
目录中找到的 Terraform 代码。将此代码块追加到 config.yml 文件,然后保存它:
gke_deploy_app:
docker:
- image: ariv3ra/terraform-gcp:latest
environment:
CLOUDSDK_CORE_PROJECT: cicd-workshops
steps:
- checkout
- run:
name: Deploy App to GKE
command: |
export CLUSTER_NAME="cicd-workshops"
export TAG=0.2.<< pipeline.number >>
export DOCKER_IMAGE="docker-image=${DOCKER_LOGIN}/${CIRCLE_PROJECT_REPONAME}:$TAG"
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
gcloud container clusters get-credentials $CLUSTER_NAME --zone="us-east1-d"
cd part03/iac_kubernetes_app
terraform init
terraform plan -var $DOCKER_IMAGE -out=plan.txt
terraform apply plan.txt
export ENDPOINT="$(terraform output endpoint)"
mkdir -p /tmp/gke/ && echo 'export ENDPOINT='${ENDPOINT} > /tmp/gke/gke-endpoint
- persist_to_workspace:
root: /tmp/gke
paths:
- "*"
下面是这个职务代码块的重要元素和一些我们以前没有讨论过的新元素。
- 定义一个变量,该变量保存我们将要部署到的 GCP 项目的名称。
gcloud container clusters get-credentials $CLUSTER_NAME --zone="us-east1-d"
是一个从我们在上一个作业中创建的 GKE 集群中检索kubeconfig
数据的命令。terraform plan -var $DOCKER_IMAGE -out=plan.txt
是一个命令,覆盖在各个地形variables.tf
文件中定义的各个变量的default
值。export ENDPOINT="$(terraform output endpoint)"
将 Terraform 命令生成的输出endpoint
值赋给一个本地环境变量,该变量将被保存到一个文件中,并持久保存到 CircleCI 工作区。然后可以从附属的 CircleCI 工作区取回、附加并用于后续工作。
作业- gke_destroy_cluster
这项工作是我们将为此管道建立的最后一个。这将基本上摧毁我们在以前的 CI/CD 工作中建立的所有资源和基础设施。作为测试的一部分,短暂的资源被用于冒烟测试、集成测试、性能测试和其他类型的测试。当不再需要这些构造时,执行 destroy 命令的作业对于删除它们非常有用。
在这项工作中,我们将自动执行在part03/iac_kubernetes_app/
目录中找到的 Terraform 代码。将此代码块追加到 config.yml 文件,然后保存它:
gke_destroy_cluster:
docker:
- image: ariv3ra/terraform-gcp:latest
environment:
CLOUDSDK_CORE_PROJECT: cicd-workshops
steps:
- checkout
- run:
name: Destroy GKE Cluster
command: |
export CLUSTER_NAME="cicd-workshops"
export TAG=0.2.<< pipeline.number >>
export DOCKER_IMAGE="docker-image=${DOCKER_LOGIN}/${CIRCLE_PROJECT_REPONAME}:$TAG"
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
cd part03/iac_kubernetes_app
terraform init
gcloud container clusters get-credentials $CLUSTER_NAME --zone="us-east1-d"
terraform destroy -var $DOCKER_IMAGE --auto-approve
cd ../iac_gke_cluster/
terraform init
terraform destroy -var credentials=$HOME/gcloud_keys --auto-approve
该作业代码块的重要元素是terraform destroy -var credentials=$HOME/gcloud_keys --auto-approve
命令。该命令执行 Terraform 命令,该命令销毁分别在part03/iac_gke_cluster
和part03/iac_kubernetes_app/
目录中使用 Terraform 代码创建的所有资源。
现在,我们已经定义了管道中的所有作业,我们准备创建 CircleCI 工作流,它将协调作业在管道中的执行和处理方式。
创建 CircleCI 工作流
我们的下一步是创建工作流,它定义了如何执行和处理作业。将工作流视为作业的有序列表。您可以使用工作流指定何时以及如何执行这些作业。将此工作流代码块附加到config.yml
文件:
workflows:
build_test:
jobs:
- run_tests
- build_docker_image
- gke_create_cluster
- gke_deploy_app:
requires:
- run_tests
- build_docker_image
- gke_create_cluster
- approve-destroy:
type: approval
requires:
- gke_create_cluster
- gke_deploy_app
- gke_destroy_cluster:
requires:
- approve-destroy
这个代码块代表我们管道的工作流定义。这个街区的情况是这样的:
workflows:
键指定一个工作流元素build_test:
表示该工作流的名称/标识符jobs:
键代表在config.yml
文件中定义的要执行的任务列表
在此列表中,您可以指定要在此管道中执行的作业。这是我们的工作清单:
- run_tests
- build_docker_image
- gke_create_cluster
- gke_deploy_app:
requires:
- run_tests
- build_docker_image
- gke_create_cluster
- approve-destroy:
type: approval
requires:
- gke_create_cluster
- gke_deploy_app
- gke_destroy_cluster:
requires:
- approve-destroy
run_tests
、build_docker_image
和gke_create_cluster
工作流作业在中并行或并发运行,不像gke_deploy_app:
项目有一个requires:
键。默认情况下,作业是并行运行的,因此您必须使用一个requires:
键和一个在开始指定的作业之前必须完成的作业列表,通过它们的作业名称明确要求任何依赖项。把requires:
键想象成建立在其他工作成功的基础上。这些键允许您分割和控制管道的执行。
approve-destroy:
项指定具有手动批准步骤的作业。它需要人工干预,必须有人批准执行工作流作业列表中的下一个作业。下一个作业gke_destroy_cluster:
依赖于执行前正在完成的approval-destroy:
作业。它会破坏管道中先前执行的作业所创建的所有资源。
完整的. config.yml 文件
完整的config.yml
基于这个帖子,可以在.circleci/
目录下的项目代码报告中找到。它在这里供您回顾:
version: 2.1
jobs:
run_tests:
docker:
- image: circleci/node:12
steps:
- checkout
- run:
name: Install npm dependencies
command: |
npm install --save
- run:
name: Run Unit Tests
command: |
./node_modules/mocha/bin/mocha test/ --reporter mochawesome --reporter-options reportDir=test-results,reportFilename=test-results
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
build_docker_image:
docker:
- image: circleci/node:12
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build Docker image
command: |
export TAG=0.2.<< pipeline.number >>
export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME
docker build -t $DOCKER_LOGIN/$IMAGE_NAME -t $DOCKER_LOGIN/$IMAGE_NAME:$TAG .
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
docker push $DOCKER_LOGIN/$IMAGE_NAME
gke_create_cluster:
docker:
- image: ariv3ra/terraform-gcp:latest
environment:
CLOUDSDK_CORE_PROJECT: cicd-workshops
steps:
- checkout
- run:
name: Create GKE Cluster
command: |
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
cd part03/iac_gke_cluster/
terraform init
terraform plan -var credentials=$HOME/gcloud_keys -out=plan.txt
terraform apply plan.txt
gke_deploy_app:
docker:
- image: ariv3ra/terraform-gcp:latest
environment:
CLOUDSDK_CORE_PROJECT: cicd-workshops
steps:
- checkout
- run:
name: Deploy App to GKE
command: |
export CLUSTER_NAME="cicd-workshops"
export TAG=0.2.<< pipeline.number >>
export DOCKER_IMAGE="docker-image=${DOCKER_LOGIN}/${CIRCLE_PROJECT_REPONAME}:$TAG"
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
gcloud container clusters get-credentials $CLUSTER_NAME --zone="us-east1-d"
cd part03/iac_kubernetes_app
terraform init
terraform plan -var $DOCKER_IMAGE -out=plan.txt
terraform apply plan.txt
export ENDPOINT="$(terraform output endpoint)"
mkdir -p /tmp/gke/
echo 'export ENDPOINT='${ENDPOINT} > /tmp/gke/gke-endpoint
- persist_to_workspace:
root: /tmp/gke
paths:
- "*"
gke_destroy_cluster:
docker:
- image: ariv3ra/terraform-gcp:latest
environment:
CLOUDSDK_CORE_PROJECT: cicd-workshops
steps:
- checkout
- run:
name: Destroy GKE Cluster
command: |
export CLUSTER_NAME="cicd-workshops"
export TAG=0.2.<< pipeline.number >>
export DOCKER_IMAGE="docker-image=${DOCKER_LOGIN}/${CIRCLE_PROJECT_REPONAME}:$TAG"
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
cd part03/iac_kubernetes_app
terraform init
gcloud container clusters get-credentials $CLUSTER_NAME --zone="us-east1-d"
terraform destroy -var $DOCKER_IMAGE --auto-approve
cd ../iac_gke_cluster/
terraform init
terraform destroy -var credentials=$HOME/gcloud_keys --auto-approve
workflows:
build_test:
jobs:
- run_tests
- build_docker_image
- gke_create_cluster
- gke_deploy_app:
requires:
- run_tests
- build_docker_image
- gke_create_cluster
- approve-destroy:
type: approval
requires:
- gke_create_cluster
- gke_deploy_app
- gke_destroy_cluster:
requires:
- approve-destroy
结论
恭喜你!您刚刚完成了本系列的第 3 部分,并通过构建一个新的使用 Terraform 执行 IaC 资源的config.yml
文件提升了您的体验。这篇文章解释并演示了config.yml
文件中的一些关键元素以及与 CircleCI 平台相关的内部概念。
在这个系列中,我们涵盖了许多概念和技术,如 Docker、GCP、Kubernetes、Terraform 和 CircleCI,并包含了一些使用它们的实践经验。我们还介绍了如何连接您的项目以使用 CircleCI,并利用 Terraform 代码在目标部署环境中测试您的应用程序。本系列旨在增加您对重要的 DevOps 概念、技术以及它们如何协同工作的了解。
我鼓励你自己和你的团队去尝试;更改代码,添加新的 Terraform 提供程序,以及重新配置 CI/CD 作业和管道。使用您所学到的知识和团队提出的其他想法的组合,互相挑战以完成发布目标。通过实验,你会学到比任何博客文章都多的东西。
感谢您关注本系列。希望你觉得有用。请随时在 Twitter @punkdata 上寻求反馈。
以下资源将帮助您扩展知识:
先说工程;通过构建社区来构建软件
在过去的三年里,我一直在经营和促进一个社区,CircleCI 各个级别和部门的人可以聚在一起讨论各种各样的话题。我们称之为“让我们谈谈工程”我们讨论的一些主题本质上是技术性的,而另一些则更侧重于领导力:不同团队如何运作、个人成长和写作等等。让我们谈谈工程学庆祝跨学科和多学科。人们不一定要成为工程师才能参与其中;毕竟,来自所有学科的人们一起工作来实现工程——所以我们关注它的美妙之处。
对我来说,这是一个文化试金石——LTE 是我认为工程师应该如何学习和合作的一种表达方式:透明地,感同身受地,着眼于更广泛的背景,而不仅仅是解决下一个问题。钱璐·辛诺特,工程学
我们有一个# let-talk-engineering slack 频道,欢迎任何人加入并分享他们的想法和创意。今天,“让我们谈谈工程”是一个由近 200 人组成的社区。在任何给定的会议上,与会者可以在任何地方从 10 - 30+,但总是有很大的参与!
我发现这是一种令人耳目一新的方式,可以在没有任何期望或感觉像是“工作”的情况下重新构建业务挑战产品营销阿诗玲·康罗伊
会议是什么样的?
我尽力为“让我们谈谈工程”创造一个随意、非正式的环境。我设定的期望是,人们可以加入进来,只是简单地倾听,并根据自己的意愿参与。我每两周选择一个主题(通常是一个新的主题,但有时是上一次会议的延续),每次讨论都以一系列问题开始。
我想通过“让我们谈谈工程”实现的最重要的事情是:
- 创建一个任何人都可以加入的社区
- 让人们能够相互联系和了解
- 展示我们之间的联系
- 说明跨学科的经验和研究如何使工程变得更好
- 用一种不会让人感到排斥或害怕的方式谈论工程
- 创造一个可以自由分享和培养想法、经验和知识的空间
我们鼓励每个人通过我们的 Slack 频道分享他们对未来主题的任何想法。如果人们感兴趣,也欢迎他们主持或出席会议。在我们的会议中,我鼓励人们提出问题,如果他们愿意的话,引导讨论。他们也可以互相问问题。随着时间的推移,这种感觉越来越像和一群朋友坐在咖啡桌旁,聊着我们都关心的事情。
我们已经有将近两年的时间没有见面了,我们中的许多人根本没有见过我们的同事。这些非常接近于复制我们错过的与工作相关的“晚餐聊天”。希瑟·维恩科,工程
为什么我创造了“让我们谈谈工程”
我的导师 Olu Ayandosu 鼓励我成立这个小组,他是我们团队的高级工程师。当时,我们正在讨论《干净的代码》这本书。他鼓励我把我们关于代码的小讨论转变成对我们关于这本书的一对一谈话有影响的事情。当时,我非常害怕托管任何东西。我刚到切尔莱西,在我的职业生涯中还是个新手,所以我能提供什么呢?
经过一番思考,我突然明白了。几年前,我在旧金山参加了一个由美国首席技术官梅根·史密斯主讲的关于技术的社会影响的讲座。梅根在一个舞台上发表了讲话,随后是一个晚宴,人们可以在那里交流、交谈和提问。我要坐在她旁边!
当时作为一名计算机科学学生,我非常沮丧,觉得自己不属于技术领域。我问她是什么帮助她保持动力,她是否有给我的建议。她告诉我“继续。你属于。需要你。我们需要更多科学和技术领域的女性。”这件事和她的话让我深受鼓舞,那段记忆一直伴随着我。这是我第一次参加跨学科讨论处于前沿的活动,而“普通人”能够与身居高级领导职位的人打成一片,交流思想。我记得,人们是多么自愿和兴奋地走到一起见证和参与讨论,这让我感到震惊。
回想那次经历,我决定试一试。我想创造一个空间,让人们可以聚在一起分享他们的经验和知识,不仅仅是作为专业人士,而是作为普通人。毕竟都是有联系的。
建立你自己的跨学科社区
社区可以极大地改变人们的感受和公司内部的思想流动。我想我们都喜欢被倾听的感觉,就像我们可以分享我们的经历,并且有一个其他人也可以做同样事情的空间。当焦点放在分享上时,谈论我们的经历变得不那么关于谁对谁错,而是更多的关于“你和 X 的经历是什么?”
建立一个社区并不像拥有一个专用频道或在日历上重复会议那样简单。这是关于人和经历,以及这个渠道或会议给人的感觉。他人的包容程度在很大程度上取决于你创造和促进的环境。
如果你想从“让我们谈谈工程”开始,你可能会发现这 10 个问题很有帮助:
- 你想达到什么目的?你的目标是什么?
- 你希望人们在每节课结束时带走什么?
- 你希望人们在每次会议中如何感受和互动?
- 这是一个开放的论坛/讨论吗?您是否在为每次会议提供便利?还是会有志愿者演讲者?
- 谁负责每次会议都有一个明确的主题和目标?这是你吗?如果是这样的话,一定要腾出时间来做计划,这样当该聚在一起的时候,一切都感觉很顺利,很有条理。
- 如果你想创建一个定期会议,节奏应该是什么?
- 如果人们不能参加这个会议会怎么样?有他们能跟随的笔记吗?会有录音吗?
- 你将如何推广这个社区?谁应该来加入?
- 您如何让它变得包容和受欢迎?
- 你怎么能让别人对此感到兴奋呢?
为了充分利用你的会话,你可以像我一样使用模板。
使用变化驱动的设计构建弹性架构
原文:https://circleci.com/blog/letting-change-and-uncertainty-advance-your-software-architecture/
CircleCI 代码库的第一行是在近九年前编写的。事后清楚地回顾我们迄今为止所建立的东西,揭示了一些有趣的主题,这些主题对于确定应对变化的方法是有指导意义的。虽然它们并非都是有意的,但这并不会降低它们的价值。三个这样的主题是:推迟处理变化的需要,像产品经理一样思考,保持头脑清醒。
变化是技术的永恒
在工程师的职业生涯中,变化可能是唯一需要处理的常量之一。这无疑推动了我们许多最有趣的挑战。市场在变化,我们的业务在发展,我们的客户群在增长,我们的团队也在壮大。我们需要构建一个既能满足当前需求,又能满足未来需求的解决方案。当我们开始构建系统时,我们如何设计我们的系统来适应和改变甚至不存在的事物?
最近几年,有很多关于架构的讨论,这些架构是专门为发展或更容易适应变化而设计的。然后,我们开始讨论这些架构的优点,使用一种思路,表明您正在选择“微服务架构”、“事件驱动架构”或“无服务器架构”。我认为这种类型的描述创造了一种虚假的终结感,这种感觉在大多数现实世界的系统中并不存在。
除了最年轻的项目,你不太可能用任何简单的一行程序来描述你的架构。它更有可能只是“你的架构”,如果你已经在它上面工作了很长时间,你可能已经应用了多种不同的模式来解决特定的问题。某处可能有一个整体、一些微服务、一些事件和一两个无服务器元素。最重要的是,您可能正在将这些部分从一种模式转换到另一种模式。
设计架构来解决问题
人们经常问我他们应该选择哪种架构,或者何时应该从一种架构切换到另一种架构。我很容易对类似“你想解决什么问题?”这样的回答感到失望
那么,随着产品的发展和需求的变化,你应该考虑什么呢?
我们可以使用很多方法来设计软件和系统,使其更能适应变化。不必进行复杂的重构或重新构建的诱惑让我们想要设计变更成本低的系统。另一方面,虽然通用抽象和可重用组件是减少变更影响的核心,但它们很难一开始就正确处理。
很难预测未来
在技术领域不断变化的同时,您的业务和组织也在不断发展。这两者对您的架构决策都有重大影响。您正在经历的特定类型的变更将影响最适合吸收这些变更的软件架构。
在一个项目或一家公司的早期,有可能对目标进行彻底的根本性转变。极端但并不罕见的例子包括 Tiny Speck 变得 Slack 和 Odeo 变成 Twitter。当你的业务领域发生如此剧烈的变化时,你的 DDD 模式的清晰界限就不复存在了。在一组微服务中,这些边界将被编码,所以如果你必须穿过它们,你可能会感谢一个更柔韧的整体。
一旦你找到合适的产品/市场,你的重点将转向支持你的快速增长。随着系统规模成为变革的驱动力,响应变革的能力通常与基于其操作特征的系统独立性联系在一起。
在这个扩展阶段之后,你将拥有一个更大的组织和更多的团队。如果你幸运的话,你将回到可持续的产品进化。那么变更的成本将与进行变更所需的跨团队协调的数量成比例。
推迟对变更的响应
当个人或团队试图解决一个问题时,他们通常会从一个非常简单的指令开始——让我们来解决这个问题。我们可以用什么最有效的架构来以最低的成本构建可行的产品?无论是初创公司还是庞大的跨国公司,一般都是这样。一个新的项目将建立在一个相对较新的系统上,并且不太可能适合产品市场,因为公司还没有完全理解其业务领域,也不知道变化的主要来源将来自哪里。
在这种情况下,为变化而设计变得更加困难。如果使事物适应变化的成本要高得多,并且您还不能告诉您系统的哪些部分将会改变,那么您如何决定在哪里进行投资呢?
尽管听起来有些违反直觉,但答案可能是不存在的。至少现在还没有。相反,密切关注变化,并以最小化犯错成本的方式进行构建。
在“每个软件架构师都应该知道的 97 件事中,Kevlin Henney 描述了一种考虑不确定性的重要方法:
“两个选项的存在表明你需要在设计中考虑不确定性。将不确定性作为一个驱动因素来决定在哪里可以推迟对细节的承诺,在哪里可以划分和抽象以降低设计决策的重要性。如果你把想到的第一件事硬连线,你更有可能被它困住——偶然的决定变得重要,软件的柔软变得僵硬。”
这种框架对于明确的决策非常有用,但是如果你不知道你正在做一个选择呢?
很多时候,选择甚至还不可见,但以后会自己显现出来。在这些情况下,答案是不要过度概括,到处构建抽象以防万一。相反,让事情尽可能简单,这样你就可以在以后需要做出改变的时候理解它们。
简单使它更容易适应变化
在早期,CircleCI 应用程序是一个整体,它将客户的构建及其相关数据放入几个 LXC 容器中的一个。我们构建的每个容器都是从同一个映像实例化而来的,该映像包含了我们认为任何人在他们的测试环境中都想要的所有东西。事后看来,这听起来像是一个可怕的想法,但在当时,这是梦幻般的。维护和支持我们早期客户的需求很简单,他们中的许多人都在构建 Rails monoliths。
随着时间的推移和我们客户群的增长,他们在测试环境方面的需求也越来越多样化。他们的底层数据库的升级版本、新颖的新开发框架、甚至新的操作系统版本变得必要。
CircleCI 中最初的容器管理不是以允许我们容易地适应这些变化的需求的方式设计的。但这很简单。因此,当我们着手解决这些问题时,我们知道在新的方法中何处拼接,并且实现拼接是最简单的工作。我们也不需要解释一个不支持我们新问题的糟糕的概括。
值得注意的是,虽然看起来很简单,但在我们的第一批客户测试其替代品之前,我们已经成功发展了五年,在这个简单的系统上满足了客户的需求。在那五年里,Docker 和 HashiCorp 的 Nomad 都被创建了。结合起来,这些工具消除了获得我们今天为客户支持的灵活且可扩展的环境所需的大量工作。
此外,当我们重组系统以适应变化的市场时,我们可以问“我们如何才能更好地适应增量变化?”在设计解决方案时,很难夸大五年的经验所提供的价值。
这么说听起来很奇怪,也非常不公平,但是大多数时候我们甚至不知道我们正在做一个重大的设计决策,因为备选路径还没有出现。你如何防范这种情况?
推迟,推迟,推迟
如果你能找到一种等待足够长时间的方法,正确的解决方案往往会自己显现出来。技术要么获得牵引力,要么消亡。如果你在 2016 年选择了一个容器编排引擎,你有大约 20%的机会选择 Kubernetes。这意味着到 2018 年初,几乎 80%的公司都在转换。这种可能性不大。
所以推迟可能是好的。推迟到制造危机的时候就不是了。
像产品经理一样思考
你的架构是一个有漏洞的抽象。虽然 CircleCI 可能是这种情况的一个极端例子,因为我们的客户可以访问我们平台中的系统,但客户在定义您的架构时所做的决策总会有一些影响。认识到这种影响并能够连贯地讨论方法和备选方案是工程师可以带给他们的 PM 和团队的巨大财富。
当我们目睹客户构建软件方式的所有这些变化时,有一件事我们忽略了太久,那就是 Docker 的崛起。当 CircleCI 在 2011 年成立时,Docker 甚至还不存在,但到了 2014 年,它已经无处不在。我们的许多客户已经开始构建 Docker 映像,作为准备部署的一部分。
Docker 最初构建于 LXC 之上,这意味着在早期,我们可以支持使用 Docker 命令来构建映像,并将它们推送到我们的一个 LXC 容器内的存储库。2014 年,Docker 推出了一种名为 libcontainer 的东西,他们剥离了对底层系统的访问,并创建了执行驱动程序,这很快导致了 LXC 驱动程序的过时。令人失望,但它仍然有效。然后 Docker 删除了所有 LXC 支持。那很糟糕。
永远为用户着想
作为工程师和架构师,我们看到了这一点,坦率地说,这是在我们产品团队的雷达下。我们的非技术产品人员不一定理解其中的含义,但我们的工程师每天都在谈论它。因此,作为一名工程师、建筑师或技术领导者,你需要思考产品的方向,并与产品经理合作,确保每个人都理解技术选择的含义。
将这种讨论与技术投资分开是很重要的。这种类型的投资绝对有一席之地,而且,有一种观点认为,用设计产品投资的同样方式来设计这些投资,可以更容易地做出权衡决策。然而,确定体系结构选择对客户直接可见的特性的影响不同于通常讨论的成本、性能、安全性等的影响。
更好地理解您的架构和您的客户所实现的价值之间的关系,将使您能够随着业务的发展,就构建什么和如何构建做出更明智的决策。然后你就可以专注于走在进化的前面。
抬起你的头
我是加拿大人,不出所料,我是打冰球长大的。在我的童年,我花了无数的时间练习滑冰:向前,向后,转弯,停下,绕着圆锥体做 8 字形。然后是棍子处理。我的想法是让冰上的运动成为第二天性,这样我就可以专注于真正重要的事情:看比赛进展,进入空间,并试图得分。让基础变得几乎毫不费力,这样你就可以把你的精力放在新的东西上。
在软件领域,我们有一个不好的倾向,那就是通过采用我们认为将会改变游戏规则的新技术,使基础变得更加困难,但最终却只有适度的上升。更糟糕的是,当我们让我们的团队沿着学习曲线前进,在生产事件中发现未经测试的边缘案例,并发明我们自己的“最佳实践”时,负面影响往往是无限的堆栈溢出没有答案的世界。
当我们将这种努力投入到我们的新技术中时,我们并没有关注我们的系统需要如何发展来满足我们客户的需求。
一个简单的时代会有所帮助
CircleCI 是一家 Clojure 商店,在早期,我们决定如果我们使用与后端相同的语言,我们会在前端开发方面做得更好。所以我们采用了 Om,一个围绕 React 的 ClojureScript 包装器。几年后,Om 的创始人大卫·诺伦(David Nolen)认为他不喜欢这种模式,于是他用一种不兼容的叫做 Om Next 的东西取而代之。我们试图逐步迁移到 Om Next,最终得到了一个过于复杂的前端,有两个状态模型和巨大的开销。
我们最终在 React 中重写了它。回想起来,Om 的变革风险和成本都很高,我们为此付出了代价。我们把时间花在了脚下,而不是前方。
没有一家公司因为一个令人惊讶的新奇或深奥的技术选择而获胜。然而,成功的公司不胜枚举,因为它们能够快速灵活地行动,适应变化。技术应该是帮助我们满足客户需求的加速器。充分理解的、经过生产测试的工具更有可能符合这个要求。正如我的同事 Bear 所说,“如果一个工具没有帮助,它就不是工具,而是一件苦差事。放下它。”
结论
不可预见的变化是我们许多人现在最关心的问题,而且比以往任何时候都更明显的是,在商业中,没有人能够预测未来。这是一个安全的赌注,通用汽车公司的人没有开始考虑如何利用他们的工厂来制造通风机。
准备好适应变化需要将变化视为你所做一切的驱动力。观察您的市场是如何变化的,并思考它是如何影响您的体系结构的,以便您可以有针对性地逐步提高其适应性。这将有助于你专注于进化,而不会在不需要的领域浪费时间或金钱。
黑天鹅事件是如此不寻常,以至于研究细节可能没有什么价值,但它们提醒我们现实离我们的计划有多远。
用 gotestsum - CircleCI 升级 go 测试
原文:https://circleci.com/blog/level-up-go-test-with-gotestsum/
嘿地鼠们!
Go (Golang)的众多优点之一是运行测试非常简单。有了我们在 Go v1.11 中看到的变化,运行测试就像:
# To test only your current module
go test ./...
# To test your module as well as its direct and indirect dependencies
go test all
在这篇文章中,我将向你展示如何让go test
的输出对本地开发和 CircleCI 更有用。
gotestsum
gotestsum
是一个用 Go 编写的 CLI 工具,为你运行go test
。它提高了产量,使它更可口。正如其 GitHub 库上所描述的,它“运行测试,并打印友好的测试输出和测试运行的摘要。”
它看起来是这样的:
这对于本地开发来说很酷,但是将它用于持续集成怎么样呢?
CircleCI / JUnit 支持
大多数 CI 提供者支持以 JUnit 格式保存测试结果。CI 提供者用它做什么可以有所不同,但是这通常意味着访问历史测试数据、很酷的图表等。CircleCI 支持收集测试元数据,只要它是 JUnit 格式的。由于go test
本身没有以 JUnit 格式输出,我们将使用gotestsum
来完成这项工作。
gotestsum --junitfile unit-tests.xml
上面的命令将以 JUnit 格式输出您的测试结果。然后,您可以通过您的 CircleCI config.xml
文件告诉 CircleCI 使用该文件作为测试元数据。
- store_test_results:
path: /tmp/test-results
安装 gotestsum
要求
本地安装
与大多数 Go 应用程序一样,运行gotestsum
可以像在$PATH
上放置二进制文件一样简单:
curl -sSL "https://github.com/gotestyourself/gotestsum/releases/download/v0.3.1/gotestsum_0.3.1_linux_amd64.tar.gz" | sudo tar -xz -C /usr/local/bin gotestsum
上面的命令安装了gotestsum
的 v0.3.1,这是本文撰写时的最新版本。如果有新版本,请更新 URL 中的版本号。对于 macOS 用户,您也可以将 URL 中的linux
替换为darwin
。可用的二进制包适用于 64 位 x86 系统。请参阅下一节来编译您自己的代码。
编译二进制文件
如果您有 32 位系统、ARM 或其他需求,以下是编译和安装自定义二进制文件的方法:
go get -u gotest.tools/gotestsum
安装在 CircleCI 上
如果你使用的是 Golang CircleCI Docker 便利镜像 ( circleci/golang
),那么恭喜你,gotestsum
已经为你预装了!如果没有,我建议遵循本地安装指令,并将其应用到您的 CircleCI config.yml
文件中,或者对于自定义映像,应用到 docker 文件中。
gotestsum
是由 CircleCI 的工程师丹尼尔·内芬维护的项目。
当地 CI 渠道开发| CircleCI
CircleCI 命令行界面(CLI)工具使开发人员能够在其本地开发环境中构建、验证和测试他们的管道作业。本教程演示了这个过程,并讨论了这种开发模式的一些好处。我将讨论在使用 CLI 工具构建管道配置时需要理解的关键管道概念和元素。
先决条件
在开始之前,您可以通读 CircleCI CLI 入门指南。然后,您需要在您的本地开发环境中执行一些事情:
完成先决条件后,您就可以开始在本地构建管道作业了。在下一节中,我将讨论一些重要的 CircleCI 概念。然后,我们将开始在本地构建管道作业,并用 CLI 工具验证它们。
CircleCI 概念
在我们开始在本地构建管道作业之前,我想分解 CircleCI 管道配置的主要元素。
概念 | 描述 |
---|---|
管道 | CircleCI 管道包含使用 CircleCI 触发项目工作时运行的全套工作流。 |
工作流程 | 工作流协调项目配置中定义的作业。 |
遗嘱执行人 | 在您的配置中定义的每个单独的作业将在一个唯一的执行器/运行时中运行,比如 Docker 容器和虚拟机。 |
工作岗位 | 作业是您的配置的构造块。作业是步骤的集合,根据需要运行命令/脚本。 |
步骤 | 步骤是完成工作需要采取的行动。步骤通常是可执行命令的集合。 |
熟悉这些概念有助于理解 CI 渠道配置及其构成方式。
CircleCI 是一个为开发人员执行连续集成和连续交付相关任务提供自动化的平台,这些任务在 CircleCI 配置文件中定义。配置文件是代码和 CircleCI 平台之间的主要接口。它基本上告诉 CircleCI 何时、如何以及对代码执行什么操作。该平台非常健壮,可以执行诸如运行自动化测试、编译工件、构建 docker 映像、将代码部署到目标环境等过程,以及许多其他高级过程。
CLI 限制
在您开始学习如何使用 CLI 工具之前,我想对 CLI 工具的功能和局限性提出一些期望。CLI 工具可以执行以下操作:
- 调试和验证您的 CI 配置
- 在本地运行作业
- 查询 CircleCI 的 API
- 创建、发布、查看和管理球体
- 管理上下文
我在下面列出了的限制:
- 只有作业可以在本地运行。管道和工作流不在本地运行。
- 您不能在本地作业中使用机器执行器。这是因为机器执行器需要额外的 VM 来运行它的作业。
- 目前无法添加 SSH 密钥。
- CLI 工具不支持运行工作流,因为工作流会在多台计算机上同时运行作业,从而使您能够实现更快、更复杂的构建。因为 CLI 仅在您的计算机上运行,所以它只能运行单个作业。
- 本地作业目前不支持缓存。当您的配置中有 save_cache 或 restore_cache 步骤时,CircleCI 会跳过它们并显示警告。
- 出于安全原因,UI 中配置的加密环境变量不会导入到本地版本中。或者,您可以使用
-e
标志指定环境变量来与 CLI 一起使用。如果有多个环境变量,则必须为每个变量使用标志。比如circleci build -e VAR1=FOO -e VAR2=BAR
。
使用 CircleCI CLI
如前所述,管道配置文件是 CircleCI 自动化的接口。它定义并控制您的 CI/CD 流程。CircleCI 平台的默认行为是在每次代码变更/git 提交被推送到您的版本控制系统(VCS)上的共享存储库时,触发基于各自配置文件的构建。一旦构建被触发,平台就执行配置文件中的指令,并在管道运行时产生通过或失败的结果。这个结果对开发者来说是很好的反馈。它向他们提供了关于他们的代码/管道哪里有问题的详细信息,以便他们可以快速理解并解决它们。我在这里描述的是现有管道配置文件的典型过程,该文件已经通过了配置开发的初始阶段。
为项目开发初始管道配置文件可能会耗费大量时间和资源。尤其是如果你是这种范式的新手。通常,管道配置开发流程遵循以下模式:
- 编辑配置文件中的一些代码
- 在本地提交这些更改
- 将这些变化推给你的上游回购
- CircleCI 执行您的配置文件
- 管道构建通过或失败
- 如果构建失败,调试代码并重复这些步骤,直到它通过
上述模式很常见。可以想象,开发一个有意义的配置文件需要多次提交。这种配置文件开发模式也使用平台资源。这可能会导致一些不必要的自动化周期消耗。使用 CLI 工具,您可以减少不必要的代码提交量和浪费的资源周期。您可以简化配置文件开发模式,如下所示:
- 在本地编辑代码
- 验证配置文件语法
- 执行/运行您修改的特定作业
- 作业通过或失败
- 如果作业失败,调试代码,然后重复这些步骤,直到它通过
这里的主要区别在于,您减少了提交污染,并且您专注于特定的工作元素,而不是整个管道配置。这使您能够快速进行更改,并在本地测试和调试它们,而不必消耗宝贵的平台资源。它还使您能够实验和优化您的作业和命令,而不会污染您的 VCS 的版本历史。
让我们了解如何使用 CLI 工具构建和运行管道作业。
使用 CLI 运行作业
在这一节中,我将演示如何在本地构建一个简单的配置文件,验证语法,并使用 CLI 工具运行一个作业。
先决条件一节中的代码报告有一个示例应用程序,我们将使用它在本地创建工作。确保您位于项目的根目录,并运行以下命令来创建所需的目录和一个空的配置文件:
mkdir .circleci/ && touch config.yml
接下来,用您最喜欢的文本编辑器打开config.yml
文件,并添加以下内容:
version: 2.1
jobs:
workflows:
上面的语法是我们的配置文件的开始,其中定义了作业和工作流的元素。现在,让我们向配置中添加一个实际的作业。用以下代码更新您的配置文件:
version: 2.1
jobs:
run_tests:
docker:
- image: circleci/node:12
steps:
- checkout
- run:
name: Install npm dependencies
command: |
sudo npm install -g
- run:
name: Run Unit Tests
command: |
./node_modules/mocha/bin/mocha test/ --reporter mochawesome --reporter-options reportDir=test-results,reportFilename=test-results
workflows:
build_test:
jobs:
- run_tests
我们在管道配置文件中定义了一个新的作业和工作流。该作业被命名为run_tests
。它对应用程序运行测试,并将结果保存到文件中。可以使用 CLI 工具在本地修改和测试run_tests
作业,而无需提交更改或使用宝贵的平台计算周期。
在build_test
工作流中,我们定义在这个管道中执行哪些作业。工作流可以被视为作业编排器。它们定义了各个作业在管道中运行的方式和时间。由于我们目前只有一个作业,我们将指示我们的工作流在被触发时执行我们的run_tests
作业。
注意: 工作流不在本地执行。只有在配置文件中定义的作业才会从 CLI 工具中执行。
验证配置文件语法
CLI 工具有一个很棒的功能,它可以验证配置文件中的 YAML,以确保语法是兼容的和有效的。验证功能应该在每次更改后运行,以确保您没有引入格式问题。每当我对我的配置文件进行重大修改时,我都会使用它。这样,我可以及早发现错误,并专注于我的配置中更紧迫的问题。使用以下命令验证您的配置文件:
circleci config validate
运行此命令会产生以下结果:
Config file at .circleci/config.yml is valid.
如果您的配置文件中有语法问题,validate 进程会用大量的细节为您标记出来。
在本地测试作业
现在您已经有了一个有效的配置文件,您可以在本地执行和测试run_tests
作业。在终端中运行以下命令:
circleci local execute --job run_tests
接下来,CLI 将处理run_tests
作业。它将开始下载所需的指定 Docker 映像(第一次将比后续运行时间长)。一旦 Docker 执行器/容器启动并运行,就会处理run
块并执行它们的命令。
====>> Run Unit Tests
#!/bin/bash -eo pipefail
./node_modules/mocha/bin/mocha test/ --reporter mochawesome --reporter-options reportDir=test-results,reportFilename=test-results
Node server is running..
Welcome to CI/CD Server
GET /
✓ returns status code 200 (39ms)
welcomeMessage
✓ Validate Message
2 passing (48ms)
[mochawesome] Report JSON saved to /home/circleci/project/test-results/test-results.json
[mochawesome] Report HTML saved to /home/circleci/project/test-results/test-results.html
Success!
run_tests
作业已经成功完成,并且您已经验证了它将在 CircleCI 平台上按设计运行。这是一场了不起的胜利!正如我前面提到的,您能够在本地构建和测试管道作业,而不必向 repo 提交代码或在平台上消耗宝贵的计算。使用 CLI 是在本地环境中维护通用开发实践的一种很好的方式。
通过 CLI 工具使用环境变量
因此,我们定义、验证并测试了一个在我们的应用程序上运行自动化单元测试的作业。既然这些测试通过了,我们应该构建一个新的任务来对我们的应用程序执行漏洞扫描,这样我们就可以很容易地识别代码中任何危险的漏洞。在这一节中,我将演示如何构建一个新的作业,该作业将使用您在先决条件一节中创建的 Snyk API 令牌。
在我们进入代码之前,我想讨论一下如何在本地处理环境变量。在作业中使用环境变量是很常见的,CircleCI 平台的特性使您能够安全地定义、保护和使用配置文件中的敏感数据。因为我们在本地执行作业,所以我们无法访问存储在平台上的环境变量及其值。幸运的是,有一个影响最小的解决方法。
您可以在本地开发环境中指定在 CircleCI 中配置的相同环境变量。这提供了无缝体验。根据您本地环境的操作系统,定义环境变量可能与我在这里演示的不同。在这个例子中,我使用的是 Linux。我将在我的$HOME/.bashrc
文件中为这个新任务定义一个环境变量:
export SNYK_TOKEN="<Replace this with your Snyk API Token>"
一旦本地定义了这个环境变量,我就可以创建使用它们的新管道作业,并模拟从平台访问它们。现在,让我们构建新的漏洞扫描作业。
在本地测试漏洞扫描作业
我们准备创建一个新的管道作业,该作业将扫描应用程序代码以发现安全漏洞。首先,用下面的代码更新您的config.yml
文件:
version: 2.1
orbs:
snyk: snyk/snyk@0.0.11
jobs:
run_tests:
docker:
- image: circleci/node:12
steps:
- checkout
- run:
name: Install npm dependencies
command: |
sudo npm install -g
- run:
name: Run Unit Tests
command: |
./node_modules/mocha/bin/mocha test/ --reporter mochawesome --reporter-options reportDir=test-results,reportFilename=test-results
vulnerability_scan:
docker:
- image: circleci/node:12
steps:
- checkout
- run:
name: Install npm dependencies
command: |
sudo npm install -g
- snyk/scan
workflows:
build_test:
jobs:
- run_tests
- vulnerability_scan
在我们执行新作业之前,让我们先了解一下这个配置中的一些新语法和特性。文件的顶部是新行:
orbs:
snyk: snyk/snyk@0.0.11
这些代码行代表了 CircleCI orbs 的一个实现,它们是可重用的代码片段,有助于自动化重复的过程,加快项目设置,并使其易于与第三方工具集成。因为我们在管道工作中使用 Snyk 扫描工具,所以实现 Snyk orb 对我们的管道来说是完美的。
vulnerability_scan:
作业中的- snyk/scan
行演示了如何实现 Snyk orb 的scan
函数。这将触发扫描,并提供将识别任何漏洞并建议缓解步骤(如果存在)的结果。让我们使用 CircleCI CLI 来执行这个新任务。在终端中运行以下命令:
circleci local execute -e SNYK_TOKEN=$SNYK_TOKEN --job vulnerability_scan
上面命令中的-e
标志指定了一个名为SNYK_TOKEN
的环境变量。它被赋予我们在上一节中定义的$SNYK_TOKEN
环境变量的值。这个环境变量的值非常敏感。在管道中使用时,必须对其进行保护。
在使用 CLI 执行此作业后,它失败了,因为我的安全扫描失败,输出如下:
====>> Run Snyk test to scan app for vulnerabilities
#!/bin/bash -eo pipefail
snyk test --severity-threshold=low
Testing /home/circleci/project...
Tested 168 dependencies for known issues, found 2 issues, 2 vulnerable paths.
Issues to fix by upgrading:
Upgrade mocha@5.2.0 to mocha@6.2.3 to fix
✗ Prototype Pollution [Medium Severity][https://snyk.io/vuln/SNYK-JS-MINIMIST-559764] in minimist@0.0.8
introduced by mocha@5.2.0 > mkdirp@0.5.1 > minimist@0.0.8
✗ Regular Expression Denial of Service (ReDoS) [High Severity][https://snyk.io/vuln/SNYK-JS-MOCHA-561476] in mocha@5.2.0
Organization: datapunkz
Package manager: npm
Target file: package.json
Project name: nodejs-circleci
Open source: no
Project path: /home/circleci/project
Licenses: enabled
Run `snyk wizard` to address these issues.
Error: Exited with code 1
Step failed
Error: runner failed
Task failed
Mocha 版本有一个明显的问题,Snyk 工具已经识别了易受攻击的依赖项,并提供了缓解解决方案。在这种情况下,解决方案是升级package.json
文件中定义的摩卡版本。事实上,漏洞有时是可以接受的。这是例外,不是规则。我强烈鼓励每个人尽可能修复所有已知的漏洞,但在极少数情况下这是不可能的。在这种情况下,您的管道不应该因为“可接受”的漏洞扫描失败而失败。幸运的是,Snyk 有一个标志,即使扫描失败,也可以让管道继续运行。Snyk orb 有一个名为fail-on-issues
的参数,默认为真,如果扫描失败,作业将失败。更新您的snyk/scan
线路以匹配以下内容:
- snyk/scan:
fail-on-issues: false
现在再次执行作业:
circleci local execute -e SNYK_TOKEN=$SNYK_TOKEN --job vulnerability_scan
这产生了以下结果:
Issues to fix by upgrading:
Upgrade mocha@5.2.0 to mocha@6.2.3 to fix
✗ Prototype Pollution [Medium Severity][https://snyk.io/vuln/SNYK-JS-MINIMIST-559764] in minimist@0.0.8
introduced by mocha@5.2.0 > mkdirp@0.5.1 > minimist@0.0.8
✗ Regular Expression Denial of Service (ReDoS) [High Severity][https://snyk.io/vuln/SNYK-JS-MOCHA-561476] in mocha@5.2.0
Explore this snapshot at https://app.snyk.io/org/datapunkz/project/6a7762c9-1447-4162-a776-de26d34ef418/history/88186524-4993-46a0-b67f-f4d4bd2331f3
Notifications about newly disclosed issues related to these dependencies will be emailed to you.
Success!
Snyk 扫描结果与前一次运行相同,但这一次管道作业没有失败,因为我们将fail-on-issues
参数值设置为false
。我想再次强调的是,所有已发现的安全漏洞都应该尽快得到解决和缓解,将标志设置为false
是一个危险的行为,在实施之前应该彻底考虑和审查。
下面是将fail-on-issues:
参数设置为false
的完整配置文件:
version: 2.1
orbs:
snyk: snyk/snyk@0.0.11
jobs:
run_tests:
docker:
- image: circleci/node:12
steps:
- checkout
- run:
name: Install npm dependencies
command: |
sudo npm install -g
- run:
name: Run Unit Tests
command: |
./node_modules/mocha/bin/mocha test/ --reporter mochawesome --reporter-options reportDir=test-results,reportFilename=test-results
vulnerability_scan:
docker:
- image: circleci/node:12
steps:
- checkout
- run:
name: Install npm dependencies
command: |
sudo npm install -g
- snyk/scan:
fail-on-issues: false
workflows:
build_test:
jobs:
- run_tests
- vulnerability_scan
摘要
恭喜你!您刚刚学习了如何使用 CircleCI CLI 在本地开发环境中开发和测试管道作业。在这篇文章中,我重点介绍了在本地开发和测试作业的各种方法,但是我强烈建议您继续使用 CLI 工具来探索其他开发模式,以简化您的管道开发和管理工作。CLI 工具还使开发人员能够管理和执行其他 CircleCI 产品和功能上的命令,如 orbs 、 CircleCI API 和上下文。
感谢你关注这篇文章,希望你觉得有用。请随时在 Twitter @punkdata 上寻求反馈。
地方政府,全球关注- CircleCI
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
CircleCI 最近的开源前端是使用 Om 在 ClojureScript 中构建的。将 Clojure 的功能原语和 React 的编程模型结合起来,可以产生一种独特的强大的用户界面方法。以前复杂的功能,如高效的撤销,实现起来变得非常简单。简单的版本变得更加强大。您不仅获得了高效的撤销,还获得了序列化应用程序的整个状态以进行检查、调试或重新加载的能力!虽然快照应用状态的承诺从一开始就是 Om 故事的一部分,但我们一直在努力将这个概念从想法变成现实。
新优势,新成本
和任何新技术一样,也有一些问题需要解决。使用 Om 拍摄快照需要避免使用组件本地状态。CircleCI 的前端设计时就考虑到了这一约束,精心安排所有组件将它们的状态存储在一个全局数据结构中。作为这种努力的回报,我们得到了强大的调试工具:保存和恢复完整应用程序状态的能力。但是代价是什么呢?组件本地状态是 React.js 的一个重要特性,支持对单个组件进行本地推理。在共享应用程序状态数据结构上敲打的组件可以呈现与全局变量相同的风格。幸运的是,我们可以恢复 React 的本地状态设计的好处,而不牺牲我们强大的新全局调试工具。请继续阅读,找出方法。
组件本地状态
著名的 reactdiffs virtual DOM为真实的浏览器 DOM 产生一个最小的批量变化集,它把它当作一个“外部”服务。很少有人理解,从概念上讲,React 也将一组变化应用于另一个外部服务:组件状态图。这个状态图是一个全局可变字典,它保持了组件局部状态的假象。正如我们说一个组件被“装载”到浏览器的 DOM 上,它的状态也被装载到状态图上。类似地,当一个组件从浏览器 DOM 中卸载时,它的状态必须从状态图中卸载。我们的目标是将组件状态装载到我们的全局应用程序状态中,而不是 React 的私有状态映射。要做到这一点,我们必须更多地了解 React 是如何工作的。
当 React 呈现函数返回 DOM 树时,这些节点表示潜在的有状态组件。为了将返回的无状态树与有状态后备存储相关联,我们需要通过组件 id 连接这两个数据集。为了减轻程序员手动分配和管理 id 的负担,React 通过组件树的路径计算 id。考虑这个例子 DOM 和相应的状态图:
DOM 中的每个节点都标有子索引和组件类型。“Bar”类型的节点也标有区别键(A、B 和 C)。假设只有 Bar 类型具有组件本地状态,右边的状态表将组件 id(路径)映射到它们的私有状态图。默认情况下,在路径中使用子索引,但是程序员可以用键覆盖它,这样当子索引来来去去或者简单地重新排序时,路径就稳定了。想象一个 todo 任务列表,其中服务器提供的 task-id 作为显式键:重新排序任务不应该混淆它们的完成状态。例如,让我们重新渲染没有中间的酒吧节点。
颜色表示计算的差异:ID 为“/1/B”的“Bar:B”节点及其对应的状态表条目已被删除。请注意,“Bar:C”已被重新编号,但显式键意味着“/1/C”ID 是稳定的。与传统的面向对象的小部件库不同,我们可以从根重新呈现,并依靠框架机器来为我们管理状态图。添加一个新的有状态子节点的工作方式类似,但是当一个 ID 被新添加到状态图中时,组件类型可以提供默认的状态值。从这个角度来说,传统的 addChild 和 removeChild 方法无异于用 malloc 和 free 进行手动内存管理!如果我们放弃 React 的组件本地状态,除了与不协调的全局变量相关的典型问题之外,我们还会承受手动 ID 管理的负担。
从工具到调解
组件状态的封装支持对单个组件进行有用的本地推理。不幸的是,封装经常会阻碍诸如自省和检测之类的全局关注。Om 的许多妙招,包括快照调试,都依赖于应用程序状态的统一外部表示。
React.js 正确地封装了状态,既有利于本地推理,也保护了 diff 引擎的不变量。与所有优秀的调试工具一样,React 开发工具打破了这种封装,让你窥视原本封闭的组件内部。检测对象以观察封装的数据通常是无害的,但是改变封装的数据是非常危险的。我们 Clojure 程序员知道我们可以用不可变的值做得更好。别名状态值不可能与 diff 引擎的不变量相冲突:不可变的值不会改变!这意味着我们可以安全地将组件本地状态挂载到我们的全局应用程序状态中。
带着不变的价值观和对强大调试工具的渴望,Om 的创造者大卫·诺伦写道摘下眼罩。Om 为检测每个组件提供了面向方面的钩子。David 没有提到的是,他的钩子支持调解和插装。通过拦截组件接口上的所有操作,无论是公共的还是私有的,仲裁使我们能够全局地增强所有组件的行为。通过重写 get-state 和 set-state!,我们可以重用 React 的 ID 管理和抽象状态接口,但是我们可以将状态值重定向到我们的全局应用程序状态!
诉讼中的国家调解
这是 CircleCI 前端的样子:
通过对 React diff 引擎和它的状态服务之间的所有请求进行仲裁,我们有效地替代了我们自己的状态服务。我们的状态服务只是将组件本地状态装载到应用程序状态数据库中,这只是存储在普通 Clojure 原子中的一个值。CircleCI 前端在 frontend.state-graft 名称空间中实现这个状态仲裁。因此,我们可以拥有组件状态封装蛋糕,也可以吃掉全局状态值。
摘要
Om 撤销演示风格的快照在开发和调试中非常有用。我们可以保存和恢复应用程序状态,以便更快地迭代和重现错误。这种能力以前与组件本地状态不一致。既然组件本地状态被安装到了全局应用程序状态上,我们就可以普遍地依赖这些调试工具,而不会牺牲组件本地状态的独立推理优势。
这项技术解决了 Om 模型中的一个难题。Circle 团队已经解决了该解决方案实施中的问题。David Nolen 致力于在不久的将来向所有 Om 用户提供这些改进。请继续关注他的推特消息,了解这方面的最新进展。
感谢 Sean Grove、David Nolan 和 Cheng Lou 审阅本文的草稿。
CTO 对 2020 年 DevOps 预测做出回应| CircleCI
原文:https://circleci.com/blog/Looking-back-on-2020-predictions-what-I-got-right-and-what-I-got-wrong/
反思我对 2020 年的软件预测
每年的这个时候,我们中的许多人都会花时间反思过去 365 天的经历。世界不会随着时间的推移而演变,但就我们所面临的挑战而言,这个世界几乎是边对边的。然而,我们在 2020 年经历的许多事情都是我们社会更大弧线的一部分。退一步说,这肯定是不可预测的。
在这篇博客中,我回顾了我对 2020 年软件开发的预测,并反思了事情的结果。
2020 年预测:DevOps 的渐进式发展将证明比全有或全无的方法更成功
“我们将看到团队对 DevOps 变得非常实际,并专注于被证明是成功的特定策略,例如,持续集成和持续交付。
全力以赴的团队将不得不解构他们的方法,以确定到底是什么在推动他们自己的需求。重要的是采取一种更渐进的方法:从小处着手,赢得胜利,获得成功,扩大规模。"
Rob: 回想起来,老实说有点不好意思。我想如果你问我过去五年中的任何一年,我都会给你同样的预测。采用增量方法总是会更成功。虽然我可能认为 2020 年是这样,但我也认为 2019 年和之前的每一年都是这样。
或者,我现在回想起来,2020 年的一些变化是如此之大和巨大,以至于我认为我们做出了一些真正大的变化,而不是一些小的渐进变化,因为我们周围的事情变化如此之快。
2020 年预测:开发商在决策中将更加适应风险
“鉴于开发人员比以往任何时候都更具创新性和生产力的压力,他们需要有助于更快地向用户交付价值、更快地找到适合产品市场的工具,并专注于商业价值。
面对市场上的各种噪音,他们正在寻找适合其情况的解决方案和做法。过度工程化和一刀切解决方案的时代已经结束,因此开发人员需要根据他们正在构建的项目的风险状况做出明智的选择。"
我想当我预言这件事的时候,我真正想要的是它成为现实。并不是说我真的相信它会发生。因为我认为作为软件开发人员和软件工程师,我们倾向于绝对化地思考,而不是能够理解不同方法的风险。
这是不可预测的一年。在 2020 年,我认为人们必须对什么对他们真正重要做出更深思熟虑的决定,因为事情变化如此之快。
很难理解你的企业在 2020 年将走向何方。风险评估成为成功的关键。
了解您的团队如何在 2021 年更好、更快地成长,并牢记风险评估
下载 2020 年软件交付状态:工程团队的数据支持基准,了解您和您的团队如何在未来扩大您的软件交付。
编码 Bootcamp - Dev Bootcamp | CircleCI
原文:https://circleci.com/blog/looking-for-a-job-after-bootcamp-5-things-you-should-know/
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
CircleCI 大约有 100 名员工,我们中至少有 10 人是因为参加了编码训练营才来到这里的。不管你喜不喜欢,训练营已经存在,并且在招聘渠道中变得越来越根深蒂固。课程报告统计了 2013 年 30 个以技术为中心的训练营。今天,他们列出了 300 多个。
“在美国/加拿大超过 69 个城市有训练营,”该网站说。“预计 2016 年编码训练营将从毕业 18,000 名学生,总学费收入为 2 亿美元。”
我们在 CircleCI 的工程师参加了 Hack Reactor 、大会、 Dev 训练营、格蕾丝·赫柏学院和 Recurse Center ,这更像是一个静修营,但有着类似的使命。我参加了黑客反应堆,我是 CircleCI 的安全工程师。其他 bootcamp 毕业生是前端工程师、支持工程师、站点可靠性工程师、数据科学家和产品设计师。我们的一个开发者传道者也参加了一个。
众所周知,在训练营结束后找到第一份工作非常困难。在对训练营的校友进行内部调查后,新的训练营毕业生应该记住以下 5 件事:
1。找工作需要时间。三个月 100 份申请是我 2015 年毕业时的基准。有趣的是,今天需要更长的时间,需要更多的坚持,因为有更多的训练营毕业生。虽然这听起来令人沮丧,但训练营毕业生的接受程度比 2015 年好得多。苹果、谷歌、亚马逊、推特和脸书被认为是禁区。今天,他们都雇佣新兵训练营的毕业生,并将新兵训练营视为管道的另一部分。
2。不要执着于一个固定的结果。仅仅因为你的训练营专注于 Javascript 并不意味着你只会找一份做前端工作的工作。你越喜欢产品、技术和公司文化,未来的雇主就越想雇佣你。快乐不是构建 API 端点。它和一些有趣的人一起开发一个有趣的产品。出于你自己的原因去学习——而不是为了取悦别人——你很快就会成为受欢迎的专家。
3。练习屏幕共享面试。虽然大多数程序练习白板如罚球,但没有人能做到这一点,除非首先坐在屏幕共享的另一边,与期待你解决 N 皇后的工程师一起。你将使用他们的工具,而不是你的。在工作面临危险之前,要习惯这样做。因此,与其使用更多的白板或尝试最新的前端框架,不如在与他人合作的同时,真正擅长用一种语言解决问题。
4。建立关系网并开始回馈社会。信不信由你,你已经有很多东西可以教人了。在聚会上做志愿者,不要害怕在那里寻求帮助。提交开源项目的 pull 请求,即使只是文档中的一个打印错误。帮助其他对编码好奇的新手。雇主希望雇佣那些赋予他人权力的人。
5。开始申请工作。现在。不要再花一个月的时间去润色那个高级项目或学习你今天早上在黑客新闻上读到的一些新东西。不管你认为在接下来的几个月里你可以专注于什么,这都不会让你成为一个有几年实际经验的工程师。开始求职,继续求职,不要停止。
寻找完美的第一份(或下一份)工作?我们正在招聘!
DevOps 对 2021 年的预测
原文:https://circleci.com/blog/looking-forward-to-2021-devops-predictions-from-circleci-cto-rob-zuber/
2020 年怎么学?
我们都从 2020 中学到了什么,我希望我们能把这些知识带在身边。我们已经学会了如何更快地做出反应,以及如何适应充满不确定性的快速变化的环境。这将是我们想要继续带来的东西,不要变得自满,退回到关于软件应该如何交付的旧观念中。
说了这么多,还是说说我对 2021 的预测吧。
全球的不确定性将导致软件开发的不断变化
我的第一个预测是,全球的不确定性将导致软件开发的不断变化。我想我们很多人都希望 2021 年会回归正常,但现实情况是,不会的。这不一定是回到我们之前的状态,这将是一种不同的常态,因为我们已经从今年的情况中走出来,并继续学习新的东西。
我们知道很难预测未来。现在我们所有人都有了一个真正的核心具体例子,我认为我们应该发扬光大,至少使用我们已经开发的工具和我们已经学到的技能来发挥我们的最大优势。
2021 年将是快速创新的一年
我的第二个预测围绕着 2021 年的快速创新。原因是,不可预测的市场趋势混合着高度的不安,这意味着将会有问题需要解决,也需要精力去解决。
形势在继续快速演变,我们中的许多人已经结束了与我们进入 2020 年时非常不同的地方。我认为人们会(无论是出于选择还是出于需要)带着新发现的技能、新发现的欲望和能量去从事激情项目。
这些新情况的结果将带来积极的创新。这将是一个我们看到许多新思想产生的时期,将会有许多人快速行动。我认为我们可以抓住巨大的能量和机会,所以让我们充分利用它。
专注于激光的领导者将占上风
我的第三个预测可能有点显而易见,但今天的一些伟大公司诞生于昨天的不确定时期。现在是机会真正成熟的时候,我们在过去的一年里已经看到了很多。发生了这么多事,很容易让人分心。
在充满挑战的时代,专注于目标比以往任何时候都更加重要。由此,我想,有这么多有趣的新发现,我们都有机会变得更好。那些看到机会、全力以赴、快速驾驶的人将会看到一些真正的好机会在他们面前展现。
不要忘记你从 2020 年学到了什么
世界不会随着时间的推移而演变,但就我们所面临的挑战而言,这个世界几乎是边对边的。然而,我们在 2020 年经历的许多真实发生的事情是我们社会更大范围的一部分。
我将亲自尝试利用我在过去一年中学到的一切,并在我思考如何继续时使用它。让坏事过去吧。我们所有人都学到了很多,如果我们试图忽视我们的学习或认为我们会回到以前的状态,那么我们就会失去过去一年中从我们的状况中得到的所有好的东西。
了解您的团队如何借助行业标准工程基准在 2021 年更好更快地发展
下载 2020 年软件交付状态:工程团队的数据支持基准,了解您和您的团队如何在未来扩大您的软件交付。
基于苹果芯片构建,M1 支持 CI/CD 管道
全球的苹果开发者利用 CircleCI 的大量 macOS 资源来快速构建、测试和部署软件。今天,CircleCI 推出了其首个 M1 资源,为客户提供破纪录的构建速度,将他们的软件交付提升到一个新的水平。最新增加的 M1 大型资源满足了对方便、云托管的苹果芯片支持的快速增长的需求,并帮助开发团队跟上苹果硬件创新的步伐。
作为一流的 CI/CD 平台,CircleCI 致力于为 Apple 社区配备顶级工具,帮助更快地将产品交付给最终用户。请继续阅读,了解 CircleCI 上 M1 的更多细节,以及如何在 M1 的基础上推动项目进入苹果开发的未来。
加速 iOS 与 M1 建立管道
2020 年,苹果推出了一套 Mac 笔记本电脑,里面有一个特别的惊喜——一个为实现最佳效率而构建的 M1 芯片。自首次亮相以来,Apple silicon 就兑现了其提供卓越速度和性能的承诺。
M1 的效率源于其标志性的“片上系统”,该系统将各种组件(包括 CPU、GPU、RAM 和 Secure Enclave)统一到一个处理器中。M1 的简化内存架构允许处理器组件利用中央数据池,消除了在每个组件之间复制数据所用的时间。随着效率的飞跃,可以有把握地说未来是以硅为基础的。
M1 给 iOS 开发者带来的好处
更方便的端到端测试,包括 GPU 测试:自动化测试是持续集成/持续交付管道不可或缺的一部分,因为能够快速自信地构建和部署有效的代码至关重要。测试 macOS 和 iOS 平台的最大挑战之一是针对游戏和虚拟现实应用等 GPU 密集型应用的图形处理单元(GPU)测试。
以前,在 macOS 平台上有效运行自动化 GPU 测试的唯一方法是使用一个专用主机。一些开发人员通过在本地 GPU 设备上进行测试来解决这一限制,但在 CI 管道中执行自动化 GPU 测试可以实现更频繁、一致的测试,从而更好地了解最终用户体验。
CircleCI 的 M1 资源包括硬件加速,将通常由运行在 CPU 上的软件完成的任务转移到 GPU,以加快处理时间。例如,由 CPU 处理时可能看起来不稳定的 3D 渲染任务在由 GPU 执行时会看起来不稳定。这有助于在管道内进行快速、有效和流畅的 GPU 测试,提供关于应用程序内视觉效果的快速反馈,并有助于在发布更新之前最大限度地减少破坏性更改。
提高构建速度以获得更好的产品体验:说到提供高质量的产品体验,速度就是游戏的名字。为了建立忠诚度和信任度,工程团队必须在几分钟之内向客户交付增强功能和错误修复。在基准测试中,M1 将 macOS 的工作速度提高了 2 倍,为构建速度设立了新的标准。这对 iOS 团队尤其有用,因为它加快了应用商店的审核过程,有助于应用更快地获得批准和发布。向应用商店提交和重新提交花费的时间越少,就有越多的时间可以用来编写重要的代码。
M1 计算如何提高构建速度
下面是一个作业示例,该作业将一个大型存储库(约 4GB)的克隆下载下来,然后保存并检索缓存。
在英特尔硬件上,这项工作需要将近 24 分钟。克隆存储库和保存缓存的步骤占了总构建时间的大部分。下面是在我们的新 M1 资源上运行的相同作业的示例。
在 M1 上,同样的工作不到 7 分钟就完成了,几乎是在英特尔硬件上运行所需时间的四分之一!请注意,性能结果将因工作流程而异。
如何在 CircleCI 管道中使用 M1 资源类
使用 M1 构建需要对 CircleCI 上的配置文件进行简单的更新。下面是一个配置示例供参考:
# .circleci/config.yml
version: 2.1
jobs: # a basic unit of work in a run
build-and-test: # your job name
macos:
xcode: 13.4.1 # indicate your selected version of Xcode
resource_class: macos.m1.large.gen1
steps: # a series of commands to run
- checkout # pull down code from your VCS
- run: bundle install
- run:
name: Fastlane
command: bundle exec fastlane $FASTLANE_LANE
- store_artifacts:
path: output
- store_test_results:
path: output/scan
workflows:
build-test:
jobs:
- build-and-test
这个配置使用resource_class
键来指定build-and-test
作业应该使用macos
执行程序的 M1 大型实例。注意,您还可以决定在您的构建中使用哪个版本的 Xcode 。
与 M1 合作对我的 iOS 渠道意味着什么?
苹果在从基于英特尔的 M1 芯片转向基于 Arm 的芯片方面的大力投资表明,最终苹果的所有开发都将在苹果芯片上运行。在 CircleCI 为 Mac 建造的团队可以通过在他们的管道中建造 M1 来保持领先。下面,我们将回答一些从 Mac 社区听到的常见问题。
我能在 M1 计算平台上为英特尔构建吗?在 M1 上测试英特尔应用可以通过添加一个安装 Rosetta 的步骤来轻松完成,Rosetta 是一个通用软件工具,它使 M1 Mac 能够使用为英特尔 Mac 构建的应用。关于这样做的例子,你可以参考我们 macOS orb 的 PR。
M1 将在 CircleCI 上计算多少成本?你可以在我们的定价页面上找到 M1 的定价,以及 CircleCI 的所有其他 macOS 资源。
结论
在 CircleCI 上构建和测试苹果应用程序的下一代已经到来。从简化端到端测试到加快上市时间,M1 可以帮助世界各地的工程团队交付高质量的应用,并以最高的效率扩展开发人员的运营。
M1 目前在规模计划中可用。在我们的文档中了解更多关于 CircleCI 对 macOS 的全面支持。
使用 Codecov CircleCI orb 轻松覆盖代码
原文:https://circleci.com/blog/making-code-coverage-easy-to-see-with-the-codecov-orb/
我们在 Codecov 的目标是提供相关的、及时的数据来帮助开发人员和团队理解并提高他们代码的质量。我们通过提供高度集成的工具来分组、合并、归档和比较覆盖率报告来做到这一点。使用 Codecov,工程团队可以创建良好的测试文化,同时看到每个新提交对整体覆盖度量的影响。
Codecov 最初是一个项目,旨在解决一个开发人员无法快速、轻松地看到正在测试的代码的个人挫折。从那时起,它已经发展成为一种工具,从独立开发人员到开源项目,从小机构到大型企业,每个人都可以使用它。无论项目或团队规模如何,每个人都可以从更好地理解他们的代码质量中受益,我们相信代码覆盖率是这种理解的重要部分。
我们的球体是做什么的?
将 Codecov 与 CI/CD 提供者集成在一起,可以确保代码覆盖率成为管理和部署软件的基础。它将代码覆盖从单个开发人员可以在他们的终端上运行的活动,扩展到团队级别的共享活动,该活动可以由团队中的每个开发人员发起、查看和评论。团队还可以依靠代码覆盖率来对每个 PR 执行检查,确保没有 PR 会被合并到您的主分支中,除非覆盖率需求得到满足。
在将 Codecov 集成到 CI/CD 工作流中之后,团队可以依赖代码覆盖率作为工具来帮助提高他们的测试覆盖率和整体代码库的质量。Codecov 的特性提供了对一个项目的测试覆盖和可操作路径的深入了解(拉请求注释,检查,等等)。)来提高代码质量。此外,当团队将 Codecov 合并到他们的 CI/CD 管道中时,很自然地会看到代码覆盖率随着时间的推移而提高。
Codecov 的主要目的是获取您的覆盖率报告,并提供易于查看、易于理解的指标。CircleCI 用户现在有了 Codecov orb ,这使得将覆盖报告上传到 Codecov 变得简单,从而让您更快地获得您的指标。
向 CircleCI 工作流添加您的覆盖率报告的上传
使用 Codecov orb 就像在 CircleCI 配置文件(.circleci/config.yml
)中添加几行代码一样简单。
下面是一个将代码覆盖率报告上传到 Codecov 的例子:
version: 2.1
orbs:
codecov: codecov/codecov@1.0.2
jobs:
build:
steps:
- codecov/upload:
file: {{ coverage_report_filepath }}
请注意,如果您有多个文件,您将需要为每个文件创建一个步骤。您还可以指定以下其他可选参数:
- conf -用于指定
.codecov.yml
配置文件的位置 - flags -标记上传到组覆盖度量(例如,单元测试、集成、ui、chrome)
- 令牌——设置私有存储库令牌(默认为环境变量
$CODECOV_TOKEN
) - 上传名称 -自定义上传的名称
包扎
我们非常高兴能够与 CircleCI 的合作伙伴,并快速方便地为 CircleCI 用户带来代码覆盖。在 Codecov,我们相信开发人员日常使用的工具之间的集成可以让开发人员专注于真正重要的事情,而不是试图争论不同的平台。为此,我们非常自豪能与 CircleCI 合作并成为这一新计划的一部分。
在 2019 年 1 月 17 日上午 11:00 PST/19:00 UTC 举行的我们的网络研讨会“Codecov 和 CircleCI Orbs:使代码覆盖变得简单”中,了解更多关于 CircleCI 和 Codecov 的信息。在这里报名。
Tom Hu 与开发人员一起构建和维护高质量的代码,并在他们的组织和网络中成为代码覆盖率的捍卫者。在 Codecov 之前,Tom 在各种公司担任了 7 年的软件工程师,从预融资的初创公司到财富 500 强公司。在他的个人生活中,汤姆是一个狂热的攀岩者和鸡尾酒爱好者。
使用 Axios | CircleCI 发出 HTTP 请求
原文:https://circleci.com/blog/making-http-requests-with-axios/
本教程涵盖:
- 为什么要用 Axios?
- 生成 Axios POST 和 GET 请求
- 测试请求
Axios 是一个基于 promise 的 HTTP 库,开发者可以向自己或第三方服务器请求获取数据。它提供了不同的请求方式,如GET
、POST
、PUT/PATCH
和DELETE
。在本教程中,我将解释 Axios 如何与应用程序交互,描述 Axios 请求和响应的结构,如何向 API 发出请求,以及如何使用 CircleCI 为您的请求编写测试。
先决条件
要跟随教程,请确保您已经:
Axios 是如何工作的?
Axios 的工作原理是在浏览器上用 NodeJS 和 XMLHttpRequests 发出 HTTP 请求。如果请求成功,您将收到一个包含所请求数据的response
。如果request
失败,你会得到一个错误。您还可以intercept
请求和响应,并转换或修改它们。在本教程的后面部分,我将对此进行更详细的介绍。
此图展示了 Axios 如何与应用程序交互。
Axios 决定是向浏览器还是向 NodeJS 发出请求。然后,它确定发出 API 请求的正确方式,并将转换后的响应返回给发出服务器请求的客户机。
Axios 请求和响应配置
在 Axios 中发出一个基本请求很容易,因为唯一需要的选项是url
。但是,您可以根据想要发出的请求类型配置其他选项。
下面是一个请求示例:
const axios = require('axios');
const res = await axios.get(url, {
//We can add more configurations in this object
params: {
//This is one of the many options we can configure
}
});
// This is the second configuration option
const res = await axios({
method: 'get',
url://Endpoint goes here,
params:{
}
});
Axios 为配置您的请求提供了极大的灵活性。您可以决定用 JavaScript 点符号格式调用 Axios。或者您可以使用对象文字格式将所有的 Axios 请求属性捆绑到一个对象中,用作发出 Axios 请求的属性。
Axios 支持几种方法,并且允许它们发出请求。它们包括:
request
get
delete
head
options
post
put
patch
下一个代码片段展示了如何使用 Axios 将一个示例GET
请求发送给一个Todos
示例 API。
axios({
method: "get",
url: "https://jsonplaceholder.typicode.com/todos",
params: {
_limit: 5,
},
});
Axios 响应
一旦你用 Axios 发送了一个请求,你期望得到一个响应。这个片段显示了 Axios 响应的数据结构:
{
// `data` is the response that was provided by the server
data: {},
// `status` is the HTTP status code from the response
status: 200,
// `statusText` is the status message from the response
statusText: 'OK',
// `headers` are the HTTP headers that the server responded with
headers: {},
// `config` is the config that was provided to `axios` for the request
config: {},
// `request` is the request that generated the response
request: {}
}
在这个响应中,您会得到:
- 您期望的数据的对象
- 服务器发回的状态代码
- 状态文本
- 响应标题
- Axios 设置的配置对象
- 用于生成响应的请求对象
然后,您可以根据需要的数据在客户端应用程序上使用响应。
这是不是感觉很多理论知识?如果是这样,继续下一节,我将向您展示如何使用 Axios 发出 HTTP 请求。
发出 Axios HTTP 请求
在本节中,您将发出GET
和PUT
请求,并观察并发请求。您将使用一个免费的“假”API: JSONPlaceholder 。
您还将使用一个应用程序来帮助您提出请求,并更好地了解正在发生的事情。
但是在开始之前,您需要为教程克隆 GitHub 存储库。运行这些命令:
git clone https://github.com/CIRCLECI-GWP/making-http-requests-axios
cd making-http-requests-axios
现在您应该已经有了本教程所需的文件。下一步是安装 Axios 和 Jest 作为依赖项。运行以下命令:
npm install
在浏览器上打开index.html
文件,查看您的 Axios 演示网页。
目前,这些按钮没有任何作用。我们将在本教程的后面构建该功能。
每个请求有三个按钮。在发出 Axios 请求并将数据返回到浏览器后,单击按钮应该会显示响应。
在您克隆的存储库的users.js
文件中,有一个事件监听器显示 Axios 返回的数据。这个文件还有一些函数,您可以使用它们来提出请求。从提出第一个GET
请求开始。
发出 GET 请求
这个片段显示了您的请求应该是什么样子:
axios
.get("https://jsonplaceholder.typicode.com/users/1")
.then((response) => {
displayOutput(response);
})
.catch((err) => console.log(err));
这个代码片段向 JSON API 发送一个GET
请求。因为请求返回一个承诺,所以您将使用.then()
块来处理响应。您还需要使用.catch()
方法将任何错误记录到控制台。
将前面的代码片段添加到users.js
文件中的getUser()
函数并保存。
接下来,进入浏览器并点击GET
按钮。在其下方,应该会出现新的内容,显示响应的详细信息。
造型和陈列已经完成。收到的 Axios 响应部分包括:
status
部分,显示响应的状态代码。在本例中是200
,这意味着请求成功了。headers
部分,包含服务器响应的所有 HTTP 头。data
部分,包含有效负载或从服务器请求的信息。在这种情况下,它是关于user 1
的所有信息。config
部分,它包含为请求传递给 Axios 的所有配置。
如请求所示,Axios 的行为类似于传统的fetch-API
库。考虑到这是一个GET
请求,您不需要传递带有请求的主体。接下来,我将向您展示如何使用 Axios 在POST
请求中做到这一点。
提出发布请求
post 请求略有不同,因为您将在请求中向服务器传递一些数据。在请求中,您将创建一个用户,并为该用户传递详细信息。该请求的代码片段将如下所示:
axios
.post("https://jsonplaceholder.typicode.com/users", {
id: 11,
name: "Tom Brady",
username: "Brad",
email: "tombrad@asd.com",
})
.then((response) => displayOutput(response))
.catch((err) => console.log(err));
Axios POST 请求在请求 URL 后使用一个对象来定义要为用户创建的属性。一旦操作完成,将会有来自服务器的响应。为了验证你的代码正在工作,回到应用浏览器窗口,点击 POST 按钮。这个片段是users.js
文件中postUser()
函数的一部分。
POST
响应与GET
请求略有不同:
status
部分有一个状态代码201
,这意味着一个资源已经被创建。在本例中,创建了一个新用户。- 对于我们发送的数据的长度,
headers
部分有一个‘content-length’
属性。它还指定了数据存储的位置:location
。 data
部分包含发送到服务器的信息。config
部分包含随请求一起发送的配置。这涉及到method
、url
和data
被发送。
您可以验证在您的POST
请求中定义的数据是作为创建的资源从服务器接收的准确响应。
在本节中,您学习了如何发出 Axios 请求,我描述了 Axios 请求的基本结构及其预期响应。在下一节中,我将解释如何在数据作为请求发送到 Axios 之前请求拦截来验证数据。
Axios 请求和响应拦截器
Axios 中的拦截发生在请求在被then()
或catch()
代码块处理之前被拦截。例如,假设您想要检查通过客户端的所有请求是否都具有有效的 JWT 令牌。您将设置一个请求拦截器,以确保对服务器的所有调用都有有效的令牌。如果呼叫没有有效的令牌,系统的用户将不得不返回到登录页面并被重新认证。为了节省时间,我将带领您完成一个不太复杂的用例,为您当前的应用程序编写一个日志记录器。
记录器将记录何时发出请求、请求的 URL 以及触发请求的时间。使用以下代码片段:
axios.interceptors.request.use(
(config) => {
const today = new Date();
console.log(
`${config.method.toUpperCase()} request sent to ${
config.url
} at ${today.getHours()} : ${today.getMinutes()}`
);
return config;
},
(error) => {
console.log(error);
}
);
Axios 将访问您的请求的config
部分,以显示请求方法、URL 和时间。这个代码片段是示例项目的interceptRequests()
函数中的users.js
文件的一部分。为了观察 Axios 拦截器的行为,打开 Chrome 浏览器控制台。单击获取 API 中第一个用户的GET
方法
为了让拦截器工作,它们需要作为对jsonplaceholder
API 的并发请求被调用。下一个代码片段展示了实现请求和响应的 Axios 方法:
const concurrentRequests = () => {
interceptRequests();
axios
.all([
axios.get("https://jsonplaceholder.typicode.com/users?_limit=5"),
axios.get("https://jsonplaceholder.typicode.com/albums?_limit=5"),
])
.then(
axios.spread((users, albums) => {
displayOutput(albums);
})
)
.catch((err) => console.log(err));
};
Chrome DevTools 图像显示,当您发出任何请求时,您可以在 Axios 将其发送到 API 服务器之前对其进行修改或检查。Axios 拦截器不仅功能强大,还让开发人员能够控制请求及其响应的行为。
现在您已经学习了如何使用拦截器来修改或检查您的 Axios 请求和响应。干得好!您的下一个任务是为您的 Axios 实现编写测试。
测试 Axios 实现
测试是开发任何应用程序不可或缺的过程。测试有助于确保您的应用程序按预期工作,并且质量始终一致。在本节教程中,您将使用 Axios 的node.js
版本。因为您已经在安装依赖项时安装了 Jest,所以您可以立即开始编写测试。
在根目录下,创建两个文件,分别命名为app.js
和app.test.js
。使用app.js
进行请求,使用app.test.js
进行测试。您需要重新创建app.js
文件,以确保您可以访问使用Node.js
Axios 的方法——而不是在您的index.html
文件的script
部分中定义的浏览器 Axios。
在app.js
文件中,您可以使用axios
在Node.js
中发出请求。此方法使用与浏览器方法相同的请求结构:
const axios = require("axios");
const getUser = async () => {
const getResponse = await axios
.get("https://jsonplaceholder.typicode.com/users/1")
.then((response) => response)
.catch((err) => console.log(err));
return getResponse;
module.export = { getUser };
};
这个代码片段向前面使用的同一个 URL 发出请求,然后保存对getResponse
变量的响应。然后,它返回响应,使其他可能需要它的方法或函数可以访问它——比如我们的测试。该代码片段导出该方法,使其可以在app.js
文件之外访问。
要为此编写一个测试,将getUser
方法导入到您的app.test.js
测试文件中。然后如下所示编写您的测试:
const { getUser, postUser, concurrentRequests } = require("./app.js");
describe("Axios requests suite", () => {
test("should get a single user", async () => {
const response = await getUser();
expect(response).not.toBeNull();
expect(response.status).toBe(200);
expect(response.data.address.street).toContain("Kulas");
});
}
使用这个测试和样本响应对象,您可以验证从 Axios 端点调用的数据是否被返回,以及您的 API 调用是否成功。要运行这个测试,请到您的终端并使用 Jest 命令npm test
:
PASS ./app.test.js
Axios requests suite
✓ should get a single user (144 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.266 s, estimated 1 s
Ran all test suites.
成功!现在您可以使用 Axios 为POST
请求和并发请求编写更多的测试。
在同一个文件中,添加这些测试:
test("should post a new user", async () => {
const response = await postUser();
expect(response).not.toBeNull();
expect(response.status).toBe(201);
expect(response.data.username).toBe("Brad");
});
test("should make simultaneous axios requests", async () => {
const response = await concurrentRequests();
expect(response).not.toBeNull();
expect(response.status).toBe(200);
});
完成后,重新运行您的测试。
npm run test
再次,成功!
既然您已经编写了 Axios 请求并对它们进行了测试,那么您就可以为它们构建 CI 管道了。使用 CircleCI 这样的 CI/CD 工具可以让每个人都知道什么时候变更会破坏管道并导致测试失败。这种反馈循环提供了成功软件开发过程中所需的洞察力和透明度。
与 CircleCI 集成
创建一个.circleci
文件夹,并在其中创建一个名为config.yml
的文件。这是 CircleCI 的配置文件。在文件中,添加以下配置:
version: 2.1
jobs:
build:
working_directory: ~/repo
docker:
- image: cimg/node:19.0.1
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: install dependencies
command: npm install
- save_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
paths:
- ./node_modules
- run:
name: Axios request tests
command: npm run test
- store_artifacts:
path: ~/repo/axios-http-requests
这个配置定义了您的working_directory
,然后告诉 CircleCI 使用一个节点映像来执行您的测试。一旦设置好,它就检查项目的任何存储的缓存。如果有,需要在安装新的依赖项之前将其恢复。在安装新的依赖项、执行测试以及保存生成的任何工件之后,缓存被保存。
现在您需要保存配置文件,提交,然后将更改推送到您的 GitHub 库。然后,登录 CirclecI 仪表盘。在项目部分找到教程的 GitHub 存储库。在我们的例子中,它被称为making-http-requests-axios
。点击设置项目。
出现提示时,输入包含您的config.yml
文件的分支的名称。对于本教程,它是main
分支。然后,点击设置项目。
您已经成功构建了管道!点击build
工作流程查看详细信息。
单击您想要了解更多详细信息的步骤;比如说Axios requests tests
步。
CircleCI 会在您每次推送到main
分支中的 GitHub 存储库时检测到变化。CircleCI 管道将再次执行,并确保您的测试套件运行,确保成功的持续集成过程。
我很高兴为你创建这个教程,我希望你觉得它很有价值。直到下次,继续编码!
Waweru Mwaura 是一名软件工程师,也是一名专门研究质量工程的终身学习者。他是 Packt 的作者,喜欢阅读工程、金融和技术方面的书籍。你可以在他的网页简介上了解更多关于他的信息。
技术债务:如何用 DevOps | CircleCI 衡量和管理它
原文:https://circleci.com/blog/manage-and-measure-technical-debt/
软件行业的每个技术团队都熟悉技术债。这是因为每个软件团队在每个软件开发周期都会产生技术债务。
这篇文章回答了一些关于技术债务的关键问题。它回顾了什么是技术债务及其原因,为什么必须解决它,以及这种债务是如何积累的。它还质疑技术债务是否总是一件坏事,探索如何度量它,并强调如何使用 DevOps 管理它。
什么是技术债?
当一个技术团队实现一个问题的次优解决方案时,他们是在现在支付实现最优解决方案的成本和以后支付成本之间进行权衡。在这样做的时候,这个团队正在招致技术上的“债务”
https://www.youtube.com/embed/itAAGQclIF0
视频
项目越大,团队承担的技术债务就越多。此外,与金融债务一样,技术债务会产生“利息”。随着项目的增长,由于知识的丢失和处理债务所需的变更规模,解决问题变得更加困难和昂贵。越早处理债务越便宜。
虽然许多技术债务可以被一个有经验的技术团队发现,但是有些仍然没有被注意到。不幸的是,逃避检测的问题往往比可检测的问题修复成本更高。
拥有一个 DevOps 过程可以通过早期检测、质量控制和建立解决技术缺陷的组织支持来减缓技术债务的积累。
技术债是什么原因造成的?
有必要检查技术债务的原因,以了解 DevOps 过程如何有所帮助。技术债务有两个主要来源:
- 业务需求包括时间压力和不断变化的条件
- 技术领导力诸如糟糕的技术设计或有缺陷的评审过程、工具、文档和测试套件等问题。
一些债务也可能来自业务对解决技术债务本身的支持不足。
为什么解决技术债务很重要?
简而言之,如果不去解决技术债务,成本会很高。成本有多种形式,例如:
- 开发功能需要更长的时间
- 产品质量受损
- 公司声誉受到打击
为了展示技术债务是如何增长的,想象一个开发团队正在开发一个没有 DevOps 的产品。可能是他们没有时间介绍 DevOps,也可能是他们没有预算。可能是他们认为这不会产生足够的影响。
当他们引入新代码时,团队中的开发人员在他们的计算机上测试它,并将其推送到 QA 环境。这是一个简单但容易出错的过程:有人可能将过时的代码或正确的代码部署到错误的服务器上,或者有人可能推送未提交的代码。当团队没有 DevOps 过程时,花费在诊断这些问题上的时间就是“兴趣”。
有时,中断是最小的,组织的其他部分几乎没有意识到。其他时候,此类错误会导致整个环境瘫痪,影响到所有相关人员。至少,有人浪费时间重新执行他们的测试,通过延长开发时间来增加公司的成本。
实际的美元价值与技术债务相关,企业需要了解技术债务是如何累积的。只有这样,他们才能支持需要解决债务的技术团队。
技术债是如何积累的?
一般来说,技术债务的积累是时间压力的直接结果。当为了满足交付日期而竞争时,将一些优先级较低的 bug 留到最后通常是一个容易的决定。对于许多团队来说,这组 bug 是“没有人会去处理的 bug 墓地”
同时,使用快速简单的修复来解决更高优先级的问题是很有诱惑力的。目的是在将来回过头来充分解决它们(只需检查代码中“TODOs”的数量)。实际上,他们将在很长一段时间内仍然有事情“要做”,因为一个已经处于时间紧张中的团队不太可能找到更多的时间。而且,随着时间的推移,开发人员会失去问题的背景,变得不太倾向于解决它们。
早期做出的技术决策可能不再适合软件当前的需求。但是为了节省时间,开发团队通常会寻找快速解决方案来加速交付,而不是花时间进行重构。随着时间的推移,他们发现维护这些临时解决方案的成本远远超过偿还债务的成本。然后他们可能会决定重构代码。在某些极端情况下,这可能会导致整个产品重写。
有时,团队可能会不知不觉地招致技术债务,因为他们缺乏编写更好代码的知识或技能。因此,随着代码库的增长,代码变得更难维护。
无论团队的构成还是公司,技术债都是不可避免的。定期管理和偿还债务至关重要。DevOps 可以在实现这一目标方面发挥作用。
技术债总是不好的吗?
虽然技术债务可能会很昂贵,但你不必马上偿还所有技术债务也是事实。就像一个企业为了发展而承担金融债务一样,承担适量的技术债务也是健康的。不要让完美成为好的敌人。
如果解决未来问题的成本低于承担债务的价值,那么承担债务是有意义的。然而,如果不管,技术债务会变得难以管理,侵蚀代码库的基础。因此,管理债务至关重要。
技术债务是如何衡量的?
衡量技术债务可能很复杂,需要考虑许多变量。团队的构成变了,技术变了,甚至需求也变了。使用比率最小化这些变量的影响。
技术负债比率
技术负债比率(TDR)是修复代码库的成本与构建代码库的成本之比。组织可以用时间或货币价值来衡量价格。从企业获得支持时,使用比率会很有帮助。这种支持可能是放宽时间表、增加预算、减少“必须具备的”特性的数量,或者为团队提供培训和工具。
您可以使用 TDR 向高管和利益相关者展示,偿还特定的技术债务比让它增长更有价值。将 TDR 保持在阈值以下可能是组织的目标。
用于分析代码中技术债务的自动化工具
手动评估可能不一致且主观,因此从业务角度来看不太可信。使用 SonarQube 和 Kiuwan 这样的代码扫描仪可以消除这种担忧。例如,SonarQube 扫描整个代码库,并客观地估计解决它发现的任何问题需要多长时间。
将这些工具与 DevOps 结合起来是有益的。
如何使用 DevOps 管理技术债务
虽然 DevOps 不能解决技术债务的所有问题,但它可以通过早期检测减缓债务的积累,从而减少 bug 的数量并改善沟通。
通过在您的 CI/CD 管道中整合 SonarQube 和 Kiuwan 等工具,您可以持续计算技术债务并将其传达给业务部门。DevOps 可以作为技术团队和公司之间的沟通工具。它还可以作为一种教育工具,告知开发人员将来哪种代码会引入 bug。这些知识应该会提高代码质量,减少 bug 的数量。
缺乏 DevOps 标准是公司必须支付的技术债务。有了定义良好的 DevOps 标准,就有可能在每次代码检入、运行测试、然后部署时创建质量关卡。这种方法将开发人员从重复和容易出错的手动操作中解救出来,并优化了开发工作。通过保持高昂的士气和高水平的生产力,这间接地为公司节省了资金。
最后,DevOps 还支持持续交付。通过持续交付,特性在开发时就被推出。持续交付通过在更频繁的发布时更容易快速发现问题,从而最小化重构的障碍。
结论
技术债务是由为了节省时间而实现次优解决方案的软件团队产生的。这种债务会产生利息或不利的后果,直到团队通过实现一个优化的、可持续的解决方案来偿还它。并不总是需要马上偿还技术债务。然而,管理技术债务并确保它保持在可维护的阈值以下是至关重要的。
DevOps 通过整合 SonarQube 和 Kiuwan 等工具,可以在管理技术债务方面发挥重要作用。这些工具在早期检测到次优代码,减少了 bug 的数量,并将最新的结果传达给每个相关人员。
要了解 CircleCI 如何帮助开发团队平衡技术债务和开发速度,探索 CircleCI
使用 PractiTest orb | CircleCI 管理自动化测试数据
原文:https://circleci.com/blog/manage-test-data-with-practitest/
CI/CD 工具提供的软件测试数据是有价值的,但是它并不总是足够全面,不足以给管理者提供他们需要进行改进的洞察力。为了做出有效的商业决策,管理人员需要了解整个测试过程,这将有助于他们理解需要做什么以及如何做。
一些可视化工具提供了编辑自动化脚本和添加 REST API 调用来报告运行和结果的选项。这些工具可以在以后以更全面的方式可视化您的测试数据和结果。但是添加这些 API 调用可能需要大量的工作,这些工作可能不适合您的组织工作流和优先级。此外,这并不能满足对你的总体 QA 工作的整体观点的需求。
在本教程中,您将学习如何通过将测试数据从您的 CI/CD 管道导出到practice test来获得对您的整个测试操作的更大可见性,包括您的自动化测试。
直接向 PractiTest 获取有价值的测试数据
PractiTest 是一个端到端的测试管理工具,能够向您展示您想要了解的关于整个测试操作的一切,包括:
- 执行进度运行图
- 测试运行时报告
- 基于运行的报告
- 更多
对于 CI/CD 用户,PractiTest 创建了鞭炮工具。based 自动将 XML 测试结果转换成 PractiTest 中的测试,让您动态地组织数据,并基于您的测试数据创建可定制的仪表板图形和报告。
有了practist 鞭炮球,CircleCI 用户可以轻松地将 practist 集成到他们的管道中。通过将 orb 添加到您的 CircleCI 配置中,所有运行结果都将被填充到 PractiTest 中,您将获得整个测试过程的完整动态视图。这不需要对自动化脚本进行任何代码更改。
先决条件
要学习本教程,您需要:
入门指南
在 PractiTest 中,创建一个 API 令牌。我们建议使用个人 API 令牌,该令牌可以由帐户所有者启用,并且可以在个人设置页面上找到。
在您的 CircleCI 设置中,创建一个上下文,并将其命名为PT API Token
(确保使用与此处显示的名称完全相同的名称)。添加一个名为FIRE_EMAIL
的环境变量,并将您的电子邮件作为值(这将是测试人员的电子邮件,它将在稍后的 PractiTest 运行中出现)。然后添加另一个名为FIRE_API_TOKEN
的环境变量,并将您之前检索到的 PractiTest API 令牌作为值。
将 PractiTest 添加到您的持续集成工作流
在这个例子中,我们将在 GitHub 中使用一个非常基本的测试项目,我们将把它添加到 CircleCI 管道中。鞭炮 orb 将向 PractiTest 报告所有的运行和结果,在这里您可以创建高级的、可定制的测试结果显示。
首先,分叉示例 GitHub 项目。接下来,登录您的 CircleCI 帐户。在项目页面上,找到名为circleci.test
的新分叉的 PractiTest 项目,并按设置项目。选择项目报告中提供的现有配置。如果您收到一条错误消息,指出您需要为您的帐户启用第三方 orb,您可以在组织安全设置中这样做。
接下来,从下拉列表中选择主分支,并按下右上角的编辑配置。
如果因为 orb 中的参数尚未定义而收到错误消息,可以忽略它。在配置文件中,您可以找到用于修改 PractiTest 集成的参数。
让我们从所需的参数开始:
firecracker_version
:确保你使用的是最新的鞭炮罐版本号。author_id
:您可以在个人设置>下的网址中找到测试运行人员的 PractiTest 用户 ID,点击此处更改您的个人信息。复制 URL 中的四个数字并粘贴为author_id
。api_token
和email
:这些将由您在先决条件部分创建的上下文自动定义。project_id
:要找到你想要结果报告的项目的 ID,进入 PractiTest 中的需求、测试库、测试集&运行或发布模块。/p/
后面的四个数字是项目 ID。api_uri
:如果您的 PractiTest 账户在欧盟数据中心,请在此参数中添加https://eu1-prod-api.practitest.app/
。
其他参数,如testset_name
,将定义结果在 PractiTest 中的显示方式,但现在我们将保持它们不变。
按下保存并运行。CircleCI 将自动构建示例项目,并用测试结果更新 PractiTest。要在 PractiTest 中找到结果,请转到测试集&运行模块。您将看到一个名为Circleci_Testset
的新测试集,如配置中所定义的。
在测试集中,您将看到从这个构建中报告的测试以及每个测试和步骤的结果。
万岁!你已经完成了运行爆竹球的基本步骤。在 PractiTest 中,您可以使用仪表板和报告以各种方式显示您的测试数据。在本教程的后面部分,您将会看到一些这样的例子。首先,让我们看看在 CircleCI 测试失败时会发生什么。
在 PractiTest 中记录测试结果
一旦您将鞭炮 orb 添加到您的管道中,对项目代码的每一个更改都会自动触发 CircleCI 创建一个新的构建并用结果更新 PractiTest。
例如,转到 GitHub 中的项目,打开测试文件夹,通过将 end is
语句更改为{:pass 1, :fail 1, :error 3}
来编辑名为test_report
的 CLJ 文件,使其失败。然后提交您的更改。
如果你回到 CircleCI,你会看到它正在运行,但是这一次构建会因为测试失败而失败。在 PractiTest 中,您将看到相同的测试集现在处于“失败”状态。
您可以打开测试集来查看失败的确切实例。
如果打开实例,您将看到两次运行:第一次通过了,第二次失败了。通过单击失败的运行,您可以查看有关此特定运行的结果以及失败原因的更多信息。
现在,您已经熟悉了基本的流程和配置,可以开始进行更高级的设置了。
为详细结果设置高级参数
使用鞭炮 orb,您可以定义额外的参数来获得更有见地的结果,这将让您创建广泛的仪表板图形和报告。
对于一些高级参数,我们建议您在 PractiTest 中创建目标自定义字段,以匹配将从 CircleCI 接收的测试结果。您可以将这些字段用于下面解释的附加参数。
下面是一些可以在配置文件中定义的额外参数。这些也可以在 CircleCI 文档中找到。
如果你不想以机器用户的身份运行 CircleCI,你可以在 Docker 容器中运行鞭炮。通过在运行测试的步骤之前插入下面的参数(这样,如果测试失败,CircleCI 仍将执行鞭炮并将结果上传到 PractiTest):
setup_remote_docker:
version: 19.03.13
如果您想要将更多的测试集字段从您的构建映射到 PractiTest,使用additional_testset_fields
参数。例如,如果您在 PractiTest 中创建了一个名为“分支”的测试集字段,并希望向其中添加分支值,请按如下方式添加行:
additional_testset_fields: |
\"custom-fields\":{\"---f-field_id\":\"${CIRCLE_BRANCH}\"},\"system-fields\":{}
将上面的field_id
替换为项目设置>字段>分支字段中可用的字段 ID。字段 ID 是 URL 末尾的五个数字。一旦完成,CircleCI 中的分支值将自动填充到 PractiTest 中的分支字段中。
如果您想要从您的构建映射更多的测试字段到 PractiTest,使用additional_test_fields
参数。例如,您可以使用 XML 测试结果中的一个名为full-class-name
的字段(可以在 CircleCI 以前版本的工件中找到),并将其映射到 PractiTest 中的 prerequisites test 系统字段:
additional_test_fields: |
\"custom-fields\":{},\"system-fields\":{\"preconditions\":\"?full-class-name\"}
这样,full-class-name
字段中的值将自动填充到每个构建的前提条件字段中。
如果你想添加更多的内嵌参数,你可以使用extra_params
参数。例如,如果您想将命令行display action log
添加到您的构建中,您可以添加extra_params: --display-action-logs
。
在命令行中独立运行鞭炮时,鞭炮帮助中提供了不同的内联参数选项。有关更多信息,请联系 PractiTest 支持人员。
默认情况下,bracket 将为 PractiTest 中的所有测试结果创建一个测试集。如果您希望基于Suite-Name
属性创建多个测试集,使用multitestset: true
并删除原始配置文件中的testset_name
参数。
默认情况下,构建中的所有 XML 测试用例都作为一个多步骤测试报告给 PractiTest。如果您希望每个 XML 测试用例被报告为一个单步测试,那么添加test_case_as_pt_test_step: false
参数。
如果您没有使用multitestset
参数,或者如果您将它设置为false
,您可以使用testset_name: CallMeAnything
来定义接收结果的单个测试集的名称。
可视化您的测试数据
在 PractiTest 中,您可以对数据进行切片和切块,以创建有洞察力的仪表板和报告。例如,您可以基于任何 PractiTest 过滤器和测试字段创建实例管状聚合报告。
您还可以为您的实例创建一个基本的饼图,来呈现您的任何测试字段,比如测试区域、分支、模型或者阶段。
如果您想要跟踪执行进度,您可以创建一个实例执行进度图,并过滤结果以准确显示您想要跟踪的内容。
结论
将 PractiTest 与您的 CircleCI 集成是集中您所有测试工作的另一个步骤,无需使用 API 的开销。PractiTest 让您整合所有正在使用的工具,使您的工作流程无缝高效。
拥有一个可以动态组织数据和可视化数据的地方,可以让您更好地管理测试,并根据所有相关信息做出更明智的业务决策。访问 PractiTest 帮助中心,了解更多关于如何使用鞭炮以及如何将鞭炮工具与 CircleCI 集成。你也可以在 CircleCI 开发者中心找到更多关于爆竹球的信息。
使用 Arm 计算资源类管理 CI/CD 管道| CircleCI
原文:https://circleci.com/blog/managing-ci-cd-pipelines-with-arm-compute-resource-classes/
Arm 处理器和架构正变得广泛可用,因为开发团队将它们作为许多应用基础设施中的计算节点。需要以经济高效的方式运行微服务、应用服务器、数据库和其他工作负载的组织将继续转向 Arm 架构。需要基于 Arm 的计算的 CircleCI 客户已经可以使用自托管的 runners,但是我们认识到更多的客户将需要使用基于 Arm 的计算来构建、测试和部署他们的应用。为了让开发人员能够在 CI/CD 管道中基于 Arm 的实例上运行代码,而无需自行维护基础设施,我们为所有 CircleCI 用户添加了新的基于 Arm 的资源类。在本教程中,我将介绍新的 Arm 资源类,并演示如何在您的管道中使用它们来构建、测试和部署 Arm 应用程序。
先决条件
在开始学习本教程之前,您需要完成一些任务:
Arm 计算资源类
本教程的以下部分将演示如何在基于 Arm 的执行器上配置和执行 CI/CD 管道,以及如何使用 Terraform an 基础架构作为代码,基于 AWS Graviton2 计算节点创建、部署和销毁 AWS ECS 集群。
在 config.yml 中实现 Arm 计算
下面的管道配置示例显示了如何定义 Arm 资源类。
version: 2.1
orbs:
node: circleci/node@4.2.0
jobs:
run-tests:
machine:
image: ubuntu-2004:202101-01
resource_class: arm.medium
steps:
- checkout
- node/install-packages:
override-ci-command: npm install
cache-path: ~/project/node_modules
- run:
name: Run Unit Tests
command: |
./node_modules/mocha/bin/mocha test/ --reporter mochawesome --reporter-options reportDir=test-results,reportFilename=test-results
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
build_docker_image:
machine:
image: ubuntu-2004:202101-01
resource_class: arm.medium
steps:
- checkout
- run:
name: "Build Docker Image ARM V8"
command: |
export TAG='0.1.<< pipeline.number >>'
export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME
docker build -t $DOCKER_LOGIN/$IMAGE_NAME -t $DOCKER_LOGIN/$IMAGE_NAME:$TAG .
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
docker push -a $DOCKER_LOGIN/$IMAGE_NAME
workflows:
build:
jobs:
- run-tests
- build_docker_image
在这个代码示例中,run-tests:
作业展示了如何指定一个机器执行器,并为它分配一个 Arm 计算节点资源类。image:
键指定分配给执行器的操作系统。resource_class:
指定使用哪个 CircleCI 资源类。在这种情况下,我们使用的是arm.medium
资源类类型,它支持管道在 Arm 架构和资源上执行和构建代码。build_docker_image:
作业是使用arm.medium
资源类构建支持 Arm64 的 Docker 映像的好方法,可以放心地部署到 Arm 计算基础设施,如 AWS Graviton2 。
version: 2.1
orbs:
node: circleci/node@4.2.0
commands:
install_terraform:
description: "specify terraform version & architecture to use [amd64 or arm64]"
parameters:
version:
type: string
default: "0.13.5"
arch:
type: string
default: "arm64"
steps:
- run:
name: Install Terraform client
command: |
cd /tmp
wget https://releases.hashicorp.com/terraform/<<
parameters.version >>/terraform_<<
parameters.version >>_linux_<<
parameters.arch >>.zip
unzip terraform_<< parameters.version >>_linux_<<
parameters.arch >>.zip
sudo mv terraform /usr/local/bin
jobs:
run-tests:
machine:
image: ubuntu-2004:202101-01
resource_class: arm.medium
steps:
- checkout
- node/install-packages:
override-ci-command: npm install
cache-path: ~/project/node_modules
- run:
name: Run Unit Tests
command: |
./node_modules/mocha/bin/mocha test/ --reporter mochawesome --reporter-options reportDir=test-results,reportFilename=test-results
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
build_docker_image:
machine:
image: ubuntu-2004:202101-01
resource_class: arm.medium
steps:
- checkout
- run:
name: "Build Docker Image ARM V8"
command: |
export TAG='0.1.<< pipeline.number >>'
export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME
docker build -t $DOCKER_LOGIN/$IMAGE_NAME -t $DOCKER_LOGIN/$IMAGE_NAME:$TAG .
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
docker push -a $DOCKER_LOGIN/$IMAGE_NAME
deploy_aws_ecs:
machine:
image: ubuntu-2004:202101-01
resource_class: arm.medium
steps:
- checkout
- run:
name: Create .terraformrc file locally
command: echo "credentials \"app.terraform.io\" {token = \"$TERRAFORM_TOKEN\"}" > $HOME/.terraformrc
- install_terraform:
version: 0.14.2
arch: arm64
- run:
name: Deploy Application to AWS ECS Cluster
command: |
export TAG=0.1.<< pipeline.number >>
export DOCKER_IMAGE_NAME="${DOCKER_LOGIN}/${CIRCLE_PROJECT_REPONAME}"
cd terraform/aws/ecs
terraform init
terraform apply \
-var docker_img_name=$DOCKER_IMAGE_NAME \
-var docker_img_tag=$TAG \
--auto-approve
destroy_aws_ecs:
machine:
image: ubuntu-2004:202101-01
resource_class: arm.medium
steps:
- checkout
- run:
name: Create .terraformrc file locally
command: echo "credentials \"app.terraform.io\" {token = \"$TERRAFORM_TOKEN\"}" > $HOME/.terraformrc
- install_terraform:
version: 0.14.2
arch: arm64
- run:
name: Destroy the AWS ECS Cluster
command: |
cd terraform/aws/ecs
terraform init
terraform destroy --auto-approve
workflows:
build:
jobs:
- run-tests
- build_docker_image
- deploy_aws_ecs
- approve_destroy:
type: approval
requires:
- deploy_aws_ecs
- destroy_aws_ecs:
requires:
- approve_destroy
部署到 AWS ECS
上一节中的代码示例显示了如何在管道中利用 Arm 资源类。在这一节中,我将向您展示如何扩展代码来创建 AWS 资源,例如 ECS 集群。我将用底层的 AWS Graviton2 EC2 计算节点创建这些资源,使用 Terraform 和基础设施作为代码。
version: 2.1
orbs:
node: circleci/node@4.2.0
commands:
install_terraform:
description: "specify terraform version & architecture to use [amd64 or arm64]"
parameters:
version:
type: string
default: "0.13.5"
arch:
type: string
default: "arm64"
steps:
- run:
name: Install Terraform client
command: |
cd /tmp
wget https://releases.hashicorp.com/terraform/<<
parameters.version >>/terraform_<<
parameters.version >>_linux_<<
parameters.arch >>.zip
unzip terraform_<< parameters.version >>_linux_<<
parameters.arch >>.zip
sudo mv terraform /usr/local/bin
jobs:
run-tests:
machine:
image: ubuntu-2004:202101-01
resource_class: arm.medium
steps:
- checkout
- node/install-packages:
override-ci-command: npm install
cache-path: ~/project/node_modules
- run:
name: Run Unit Tests
command: |
./node_modules/mocha/bin/mocha test/ --reporter mochawesome --reporter-options reportDir=test-results,reportFilename=test-results
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
build_docker_image:
machine:
image: ubuntu-2004:202101-01
resource_class: arm.medium
steps:
- checkout
- run:
name: "Build Docker Image ARM V8"
command: |
export TAG='0.1.<< pipeline.number >>'
export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME
docker build -t $DOCKER_LOGIN/$IMAGE_NAME -t $DOCKER_LOGIN/$IMAGE_NAME:$TAG .
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
docker push -a $DOCKER_LOGIN/$IMAGE_NAME
deploy_aws_ecs:
machine:
image: ubuntu-2004:202101-01
resource_class: arm.medium
steps:
- checkout
- run:
name: Create .terraformrc file locally
command: echo "credentials \"app.terraform.io\" {token = \"$TERRAFORM_TOKEN\"}" > $HOME/.terraformrc
- install_terraform:
version: 0.14.2
arch: arm64
- run:
name: Deploy Application to AWS ECS Cluster
command: |
export TAG=0.1.<< pipeline.number >>
export DOCKER_IMAGE_NAME="${DOCKER_LOGIN}/${CIRCLE_PROJECT_REPONAME}"
cd terraform/aws/ecs
terraform init
terraform apply \
-var docker_img_name=$DOCKER_IMAGE_NAME \
-var docker_img_tag=$TAG \
--auto-approve
destroy_aws_ecs:
machine:
image: ubuntu-2004:202101-01
resource_class: arm.medium
steps:
- checkout
- run:
name: Create .terraformrc file locally
command: echo "credentials \"app.terraform.io\" {token = \"$TERRAFORM_TOKEN\"}" > $HOME/.terraformrc
- install_terraform:
version: 0.14.2
arch: arm64
- run:
name: Destroy the AWS ECS Cluster
command: |
cd terraform/aws/ecs
terraform init
terraform destroy --auto-approve
workflows:
build:
jobs:
- run-tests
- build_docker_image
- deploy_aws_ecs
- approve_destroy:
type: approval
requires:
- deploy_aws_ecs
- destroy_aws_ecs:
requires:
- approve_destroy
这段代码扩展了原始的管道配置示例。您可能已经注意到,已经定义了一些新的工作。deploy_aws_ecs:
、approve_destroy:
和destroy_aws_ecs:
作业是这个扩展配置中的新元素。在我深入研究它们之前,我将描述一下commands:
和install_terraform:
元素。
install_terraform:命令
CircleCI 能够使用管道参数封装和重用配置代码。install_terraform:
命令是定义可重用管道代码的一个例子。如果您的管道重复执行特定的命令,我建议定义可重用的command:
元素,以提供可扩展和集中管理的管道配置。deploy_aws_ecs:
和destroy_aws_ecs:
作业都执行 Terraform 代码,因此管道需要多次下载并安装 Terraform cli 。install_terraform:
命令提供了宝贵的可重用性。
commands:
install_terraform:
description: "specify terraform version & architecture to use [amd64 or arm64]"
parameters:
version:
type: string
default: "0.13.5"
arch:
type: string
default: "arm64"
steps:
- run:
name: Install Terraform client
command: |
cd /tmp
wget https://releases.hashicorp.com/terraform/<<
parameters.version >>/terraform_<<
parameters.version >>_linux_<<
parameters.arch >>.zip
unzip terraform_<< parameters.version >>_linux_<<
parameters.arch >>.zip
sudo mv terraform /usr/local/bin
该代码块定义了install_terraform:
可重用命令。parameters:
键维护一个参数列表。参数version:
和arch:
分别定义了 Terraform CLI 版本和 CPU 架构。这些参数下载并在执行器中安装客户机。因为这个代码块代表一个command:
元素,所以必须定义一个命令steps:
键。在前面的例子中,run:
元素执行相应的command:
键。该键使用<< parameter.version >>
和<< parameter.arch >>
变量下载特定的 Terraform 客户端,以指定客户端版本号和 CPU 架构。管道参数对于优化和集中管理管道配置中的功能非常有用。如果你想了解更多,你可以在这里得到所有的细节。
部署 _aws_ecs 作业
管道中定义的deploy_aws_ecs:
作业利用基础设施作为代码来创建新的 Amazon ECS 集群。它包括所有必需的资源,如虚拟专用网络(VPC)、子网、路由表、应用程序负载平衡器和 EC2 自动扩展组。这项工作创建和调配部署和运行应用程序所需的所有基础架构。因为目标架构是 Arm,所以 AWS ECS 集群必须由 AWS Gravtion2 ECS 计算节点组成。这些节点将在之前的流水线作业中执行基于 Arm 的 Docker 应用映像构建。
deploy_aws_ecs:
machine:
image: ubuntu-2004:202101-01
resource_class: arm.medium
steps:
- checkout
- run:
name: Create .terraformrc file locally
command: echo "credentials \"app.terraform.io\" {token = \"$TERRAFORM_TOKEN\"}" > $HOME/.terraformrc
- install_terraform:
version: 0.14.2
arch: arm64
- run:
name: Deploy Application to AWS ECS Cluster
command: |
export TAG=0.1.<< pipeline.number >>
export DOCKER_IMAGE_NAME="${DOCKER_LOGIN}/${CIRCLE_PROJECT_REPONAME}"
cd terraform/aws/ecs
terraform init
terraform apply \
-var docker_img_name=$DOCKER_IMAGE_NAME \
-var docker_img_tag=$TAG \
--auto-approve
这个代码块演示了如何使用我之前描述的install_terraform:
命令。我们已经将version:
参数设置为 0.14.2,将arch:
参数设置为 arm64。最后的run:
元素初始化 terraform,然后代码使用相应的参数执行一个terraform apply
命令,这些参数传递在管道运行中创建的 Docker 图像名称和标记的值。完成后,该作业将创建应用程序,并将其部署到基于 AWS ECS Graviton2 的全功能集群中。
销毁 _aws_ecs 作业
我们在deploy_aws_ecs
中创建了 AWS ECS 基础架构。destroy_aws_ecs
作业显然执行相反的操作,以编程方式破坏所有创建的基础设施和资源。这是终止不必要的基础设施的最干净的方法。
destroy_aws_ecs:
machine:
image: ubuntu-2004:202101-01
resource_class: arm.medium
steps:
- checkout
- run:
name: Create .terraformrc file locally
command: echo "credentials \"app.terraform.io\" {token = \"$TERRAFORM_TOKEN\"}" > $HOME/.terraformrc
- install_terraform:
version: 0.14.2
arch: arm64
- run:
name: Destroy the AWS ECS Cluster
command: |
cd terraform/aws/ecs
terraform init
terraform destroy --auto-approve
在这个代码块中,除了最后一个run:
元素之外,大部分作业定义与前一个相同。在这个元素中,我们发出了一个 Terraform 初始化和terraform destroy
命令,正如所料,这个命令将销毁在前面的步骤中创建的所有资源。
工作流:批准 _ 销毁作业
我将讨论的最后一项是在配置示例的workflows:
元素中找到的approve_destroy:
。该作业是一种手动批准类型,其中工作流将被有意停止并保持暂停状态,直到手动交互完成。在这种情况下,必须按下 CircleCI 仪表板上的按钮,以便执行destroy-aws-ecs:
。如果没有这个批准作业,管道将自动触发销毁作业,并终止在以前的作业中创建的所有资源。在管道执行中需要手动干预或批准的情况下,批准类型的作业非常有用。
结论
CircleCI 以 Arm 计算节点的形式推出了支持 Arm 的执行器,让开发人员能够访问 Arm 架构的管道。在本教程中,我演示了如何将 CircleCI Arm 计算节点实现为管道执行器。我还展示了如何使用 Terraform 和 infrastructure 作为代码将应用程序部署到由 AWS Graviton2 EC2 节点支持的 AWS ECS 集群。本教程中的所有代码示例都可以在 GitHub 的 arm-executors repo 中找到,我强烈建议你去看看。我很想听到你的反馈、想法和意见,所以请发推特给我 @punkdata 加入讨论。
感谢阅读!
GitHub pull 请求-管理机密| CircleCI
原文:https://circleci.com/blog/managing-secrets-when-you-have-pull-requests-from-outside-contributors/
Mozilla 喜欢尽可能开放地工作,这意味着我们主要在可公开访问的代码库中进行开发,无论我们是否期望外部合作者。然而,这些存储库仍然需要连接到其他系统,这有时涉及到管理敏感凭证。我们如何让这些连接为维护人员提供丰富的工作流,同时也为外部贡献者提供良好的体验?
我们将在 GitHub 中构建一个示例 Java 项目,该项目使用 CircleCI 对所有 pull 请求(PRs)进行测试,无论是来自主存储库的分支还是 fork。然后,我们将添加条件逻辑,当可信提交者将代码推送到主存储库时,该条件逻辑将构建并部署一个 java jar 工件到亚马逊 S3。
创建项目
让我们使用 Apache Maven 作为构建工具来生成一个小的 Java 项目:
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=managing-secrets -DarchetypeArtifactId=maven-archetype-quickstart -Dversion=1.3 -DinteractiveMode=false
现在,我们将创建一个简单的 CircleCI 工作流,运行单个test
步骤:
version: 2.0
jobs:
test:
docker:
- image: circleci/openjdk:8-jdk
steps:
- checkout
- run: mvn clean test
workflows:
version: 2
build:
jobs:
- test
一旦我们将它提交到 GitHub 并在 CircleCI 中启用它作为一个项目,每次推送都会触发 CircleCI 中的build
工作流的运行。从主存储库的任何分支发出的 PRs 将显示test
作业的状态。
为分叉 PRs 启用 CircleCI
现在,我们希望为来自分叉存储库的拉请求启用相同的工作流。这不仅允许没有提交权限的贡献者提出变更,而且对于喜欢从自己的分支工作的提交者也很有帮助。
为了启用 CircleCI 的分叉拉取请求,我们在 CircleCI 中进入我们项目的设置页面,选择构建设置 > 高级设置,并启用 构建分叉拉取请求 选项。
当我们在那里时,注意下一个选项,将秘密从分叉的拉请求传递到构建。这是默认禁用的,这正是我们在这里想要的。在下一步中,我们将上传 AWS 凭证,我们不希望意外地将它们暴露给我们组织之外的用户。
添加秘密
我们通过将 AWS 凭证设置为特定于项目的环境变量,使它们对可信构建可用。注意,创建一个由多个项目共享的上下文也是可能的。
我们现在将移动到 CircleCI 中项目配置的构建设置 > 环境变量部分,并添加AWS_ACCESS_KEY_ID
和AWS_SECRET_ACCESS_KEY
变量,这些变量包含允许写入亚马逊 S3 中我们将存放[工件][14]的选定位置的凭证。这些变量不会为从分叉的 pull 请求触发的 CircleCI 作业设置,而只会为由具有 commit 访问权限的人发起的主存储库上的分支设置。
构建和部署工件
此时,我们已经准备好向我们的[持续集成(CI)][15]工作流添加逻辑,以构建一个 jar 并将其部署到 S3。为了让我们的 CI 作业尽可能快地运行,我们将把 jar 工件与test
作业并行打包。一旦测试和打包成功完成,我们将把工件部署到 S3。
我们将以下作业定义添加到我们的config.yml
中,使用工作区在package
和deploy
步骤之间共享数据:
jobs:
test:
...
package:
docker:
- image: circleci/openjdk:8-jdk
steps:
- checkout
- run: mvn clean package
- persist_to_workspace:
root: target
paths:
- managing-secrets-1.3.jar
deploy:
docker:
- image: python:3.7
steps:
- checkout
- attach_workspace:
at: target
- run: pip install awscli
- run: aws s3 cp target/managing-secrets-1.3.jar s3://mybucket/managing-secrets/$CIRCLE_BRANCH/managing-secrets-1.3.jar
我们希望将这些新任务添加到我们的工作流中,并将deploy
定义为依赖于test
和package
步骤。我们的工作流现在在配置中表示为:
workflows:
version: 2
build:
jobs:
- test
- package
- deploy:
requires:
- test
- package
我们将这些更改提交给 master,并且我们有了第一次成功的部署!🎉Alice 是我们在另一家公司的朋友,她对项目的进展很兴奋,她想提出一个改进方案,所以她分叉这个项目并发出第一个拉动请求。不幸的是,Alice 的公关没有通过我们的 CI 测试。deploy
步骤返回:
upload failed: ... Unable to locate credentials
这里有好有坏。好的一面是,CircleCI 完全按照我们的要求做了;它运行分叉公关的工作流程,没有暴露任何秘密。从坏的方面来说,这对爱丽丝来说是一次令人困惑的经历;在她打开 PR 之前,她确保她的新代码和测试在本地正常工作,所以她有理由期望她的 PR 应该通过我们的 CI 测试。
我们需要引入更多的逻辑来检测分叉的 PRs,并延迟部署,直到可信的提交者批准并合并代码。
定义在分叉 PRs 上提前返回的命令
CircleCI 2.1 配置引入了可重用的用户定义命令,我们将利用这个概念来制作一个early_return_for_forked_prs
命令。调用它将使我们知道分叉 PRs 不需要的工作或我们知道会失败的工作短路。确保参考上的文档,启用配置重用。
首先,我们如何在一个工作运行中辨别这是否是一个分叉的 PR?我们可以直接检查传入的特定环境变量是否存在,比如AWS_ACCESS_KEY_ID
,但是我们希望实现一个更通用的解决方案,它可以被复制到任何项目中,而不考虑我们已经定义的特定秘密集。相反,我们将使用由 CircleCI 的内置环境变量提供的关于作业的丰富上下文。我们感兴趣的特定变量是CIRCLE_PR_NUMBER
,记录为“相关联的[GitHub 或 Bitbucket][16] pull 请求的编号。仅在分叉的 PRs 上可用。如果CIRCLE_PR_NUMBER
存在,那么我们知道我们正在运行一个无法访问秘密的分叉 PR 的构建。
为了在 shell 语法中表达这个条件,我们使用了-n
(非零长度)测试。条件将是这样的:
if [ -n "$CIRCLE_PR_NUMBER" ]; then
# mark this job successful and stop processing
fi
CircleCI 上的大多数执行器都有一个可用的本地 circleci-agent
命令行界面,它提供了我们填写这个条件表达式所需的命令:
circleci-agent step halt
现在,我们准备将所有这些放入配置的一个新的顶级commands
部分:
commands:
early_return_for_forked_pull_requests:
description: >-
If this build is from a fork, stop executing the current job and return success.
This is useful to avoid steps that will fail due to missing credentials.
steps:
- run:
name: Early return if this build is from a forked PR
command: |
if [ -n "$CIRCLE_PR_NUMBER" ]; then
echo "Nothing to do for forked PRs, so marking this step successful"
circleci step halt
fi
我们添加自定义命令作为部署作业的第一步:
jobs:
deploy:
docker:
- image: python:3.7
steps:
- early_return_for_forked_pull_requests
- checkout
- attach_workspace:
at: target
- run: pip install awscli
- run: aws s3 cp target/managing-secrets-1.3.jar s3://mybucket/managing-secrets/$CIRCLE_BRANCH/managing-secrets-1.3.jar
此时,我们可以为package
作业添加相同的命令,因为它的唯一目的是为我们跳过的deploy
作业暂存工件。我们还不如不要浪费计算时间去建造一个我们从来不用的工件。
如果爱丽丝在这些配置改变的基础上重新设置她的 PR,CircleCI 现在会运行得更快,并由于早期的返回而显示所有绿色。当她的变更被批准和合并时,包括秘密在内的完整工作流将在主分支运行,构建一个批准的工件并部署到 S3。
进一步阅读
这里讨论的演示项目的完整代码可以在 GitHub 上的 jklukas/managing-secrets 获得。
了解更多关于 CircleCI 上下文 REST API 的信息。
要查看这种方法在实际生产环境中的应用,请参见[mozilla/telemetry-batch-view][12]和[Mozilla/telemetry-streaming][13],Mozilla 数据平台团队在这些存储库中定义了 Spark 转换作业,用于从 Firefox 遥测数据创建派生数据集。对这些存储库的每次推送都会触发一次构建,并将一个 jar 工件交付给 S3;我们通过旋转指向一个已部署 jar 的 Amazon EMR 集群来运行转换。默认情况下,夜间运行引用 S3 的master/
路径中的工件,因此我们的 CircleCI 配置确保了白天合并到 master 的代码将在第二天晚上运行。
https://github . com/Mozilla/telemetry-batch-view/blob/33 D1 BF 1 cafd 29098 a 989d 08770358361 a 93 D7 BC 3/。circle ci/config . yml[12]:https://github . com/Mozilla/telemetry-streaming/blob/b 3318 acdfeae 5e 0 f 7d 5a 484 BFA be 809355 F3 ad C5/。circle ci/config . yml[14]:https://circleci.com/docs/artifacts/[15]:https://circleci.com/continuous-integration/[16]:https://circleci.com/docs/gh-bb-integration/
杰夫·克鲁卡斯有实验粒子物理学的背景,他既是教师又是帮助发现希格斯玻色子的研究人员。他现在在俄亥俄州哥伦布市的 Mozilla 公司的 Firefox 数据平台上远程工作,之前是 Simple 公司数据平台的技术负责人,Simple 是一家云端无分支银行。
手动作业审批和计划工作流运行- CircleCI
原文:https://circleci.com/blog/manual-job-approval-and-scheduled-workflow-runs/
在 CircleCI 2.0 中,团队在选择如何运行工作流方面比以往任何时候都更加灵活。您的作业可能很复杂(或者如您所愿很简单),并且作业不一定按顺序运行。
当我们交付工作流时,我们希望为您提供一种方法来分解您的工作,并选择您想要协调配置的时间和方式。但是你仍然被卡住了——这仍然取决于你去找出一种不破坏任何东西的方式来运行你的工作,或者你没有浪费时间等待它们运行。您被迫就如何以及何时运行作业做出许多单独的决定,甚至是您每天运行的作业。有充分的理由选择手动批准作业,也有同样多的理由您可能希望提前安排工作流:
人工批准 | 计划作业 |
---|---|
为需要某种程度监控的工作启用控制和风险降低层 | 资源密集型工作流 |
确保您团队中的某个人可以在部署到生产环境之前检查和确认工作的细节 | 您希望自动执行的定期运行的任务,例如:生成按计划运行的报告,而不是在每次提交时运行 |
自动运行作业,无需人工监督 |
因此,现在我们引入了要求手动批准和安排工作流运行的功能。这允许您为您的团队创建最合适的时间表。现在,您可以自定义您的工作流程,使其以最佳方式为您服务。
设置计划
按计划运行工作流是自动执行日常任务中重复任务的简单方法。要开始,只需添加一个带有schedule
触发器的工作流。欲了解更多详细信息,查看文档。
示例配置:
version: 2
commit-workflow:
jobs:
- build
scheduled-workflow:
triggers:
- schedule:
cron: "0 1 * * *"
filters:
branches:
only: try-schedule-workflow
jobs:
- build
设置人工审批
当您希望保留手动批准时,可以通过向您的工作流程添加一个包含type: approval
条目的特殊作业来轻松配置手动批准流程。参见下面的示例,并查看文档了解更多详情。
示例配置:
version: 2
release-branch-workflow:
jobs:
- build
- request-testing:
type: approval
requires:
- build
- deploy-aws:
requires:
- request-testing
看不到您的具体用例,或者已经使用这些选项以不同的方式编排您的工作流?在讨论上与我们和 CircleCI 社区的其他人分享吧!
我们如何使用新的 markdown proofer - CircleCI 发现并修复文档中的 11 个错误
静态站点生成器(SSG)如雨果和杰基尔如今风靡一时。这些后端的静态和前端网站的 JavaScript 被称为 JAMStacks。通常,我们用两种方式测试它们:
- 经由 SSG 成功地建立了网站
- 和 HTMLProofer
如果我们想做得更多呢?让我们来看看我为测试 markdown 文件制作的新工具,以及它如何提高 CircleCI 文档示例的准确性。
作为 CircleCI 的开发人员,Hugo 社区的成员,以及 CircleCI docs ( 用 Jekyll 构建)的经常撰稿人,我一直在思考改进静态站点测试的方法。拥有一个定制的小型 Docker 映像可以缩短构建时间,如果可能的话,并行化作业也会有所帮助。更多的原始测试来确保页面看起来和工作正常是最重要的,但有时也是最难做到的。CircleCI 客户对 CircleCI Docs 的频繁反馈给了我一个想法。
问题是
像大多数技术文档一样,CircleCI 文档是用 Markdown 编写的,包含数百个示例。这些例子是用 GitHub 风格的 Markdown fenced 代码块编写的。我们的大多数例子都是使用 YAML 语法的片段或完整的 CircleCI 配置文件。许多用户在阅读我们的文档时,会看到一些对他们有用的 YAML 示例,自然会将&复制粘贴到他们自己的配置中。然后我们破坏了他们的配置。
怎么会?
YAML 非常严格。契约意味着一切。缩进量告诉 YAML 解释器如何解组所有数据。错误的缩进可以创建或破坏配置文件。此外,YAML 规范很久以前就做出了——在笔者看来——只通过空格缩进的糟糕决定。YAML 行开头的制表符是语法错误。
回到 docs,当 CircleCI docs 团队、其他员工甚至社区成员为 repo 添加或更新示例时,有时会出现这些错误。这可能是一个简单的人为错误——我们都会犯错——或者有时可能是由文本编辑器在不应该的时候假设了一些事情造成的。无论哪种方式,人眼可能看不到的语法错误(制表符看起来像空格)被合并进来,稍后用户出现,复制它,然后出现错误。可以理解的是,这可能会导致挫败感,而 HTMLProofer 这个伟大的工具却无法捕捉到这一点。
解决方案
除了 HTMLProofer,还有其他工具可以测试网站,其中一些可以覆盖 markdown 文件。为了帮助提高 CircleCI 文档、我的个人项目和其他静态网站的质量,我写了一个新工具,叫做 Markdown Proofer 。这是一个用 Go 编写的小型开源命令行界面(CLI ),旨在测试 markdown 文件。受 CircleCI docs 的启发,Markdown Proofer 的第一个也是迄今为止唯一的功能是在 Markdown 文件中寻找 YAML 防护代码块,然后验证 YAML 以确保其语法正确。这允许一些人在他们的 markdown 中自动测试 YAML 代码块,有希望在他们的站点中产生更少的充满错误的例子。让我们用 CircleCI docs 测试这个新工具。
使用降价检验器测试 CircleCI 文档
在推出 Markdown Proofer 的第一个版本 v0.1.0 后,我在 CircleCI docs 中创建了一个 PR,将其添加到构建过程中,看看我们会发现什么。正如所料,由于 Markdown Proofer 发现了几个错误,PR 构建失败。你可以在 GitHub 这里看到这个 PR,在这里看到失败的 CircleCI build 。
所以,太好了!该工具完成了它的工作,并发现了一些错误。现在我需要修复它们,这样构建就可以通过,我们就可以正式将 Markdown Proofer 放入 CircleCI docs 的master
分支。我开发了一个新的 PR 来修复所有被发现的 YAML 错误,并且,不算任何表面上的改变,能够修复我们 YAML 例子中的 11 个错误。这个 PR 可以在 GitHub 这里找到。
随着所有 PRs 的合并,CircleCI docs 的配置示例中修复了 11 个 YAML 错误,未来的 YAML 错误将被捕获,而无需手动检查所有内容,并且仅增加了 1 秒或更少的总构建时间。虽然不是开创性的,但我能够在 CircleCI 文档构建过程中添加一个小工具,以提高我们提供给用户的文档的质量。听起来我赢了。
下一步是什么?
用于减价打样机
Markdown Proofer 仍然是一个全新的工具,只检查 YAML 围栏代码块。在不久的将来,我计划:
- 添加对 JSON 代码块的支持。
- 添加对 JavaScript 代码块的支持。
- 使用 CircleCI 本地 CLI 验证 CircleCI 配置示例(一个完整的文件),不仅验证 YAML 语法,还验证 CircleCI 配置方案。
- 对 CLI 输出和 README.md 进行更多润色。
- 改进错误消息(Go YAML 库没有提供很好的消息传递)。
由于 Markdown Proofer 是开源的,您可以跟踪项目并公开问题或在 GitHub 上提交 PRs。在这里找到它。
对于静态网站测试
许多网站只是使用 HTMLProofer 进行测试,我们可以做得更多。也许 Markdown Proofer 是你的事,也许不是。用 Selenium 测试你网站的 UI 是另一种测试大多数网站的方法。了解需要什么来获得测试过程的更好日志以帮助解决挂起和失败的构建。还有针对您所拥有的内容类型的独特测试。例如,如果您有一个普通的 RSS 提要、一个 sitemap.xml 文件、一个播客提要等等。有一些聪明的方法来测试这些页面,以确保它们不只是返回一个 HTTP 200“Status OK”响应,而是实际工作并有意义。
如果你有自己的想法或任何问题,请在circle ci discuse上继续讨论。
马斯洛的远程员工需求层次| CircleCI
原文:https://circleci.com/blog/maslow-s-hierarchy-of-remote-worker-needs/
《远程生活》是《独居》的工作版。每天分享你的空间会给你带来惊喜,无论是好是坏。你可以自由地即时实验和改变事物,而且你不需要请求许可。你是你一天生活的主人:你可以监控你的能量水平,控制零食,设置恒温器,如果你想出去走走。伴随着这种自由而来的是巨大的责任:你可以做任何你想做的事情,但坏处是…你可以做任何你想做的事情。
我已经远程工作了三年,在一家远程友好型公司的办公室工作了四年,我看到了很多关于如何发展的好(和坏)建议。很多人都倾向于提出声明性的建议,或者给各种选择赋予特定的积极或消极的价值,例如,“手头不要有零食,因为你会吃掉它们”——但我喜欢零食,当我有零食时,我工作得最好。
远程工作是一门艺术,也是一门科学:部分自省(我认为什么对我最合适?),以及部分实验(现在我已经尝试过了,实际上什么对我有效呢?).我们能做的最好的事情就是帮助彼此发现和尝试不同的选择。本着这种精神,在我重复自己的远程工作生活时,我学会了考虑以下几个关键方面:
1。工作环境
首先也是最重要的:你打算在哪里度过你的工作日?你的环境对你的感觉和你能完成的事情有着巨大的影响,所以值得花时间去思考这个最基本的难题。什么样的环境有助于你把工作做到最好?
如果你习惯在办公室工作,你可能以前没有太多机会玩这个作品!你想要每天在同一个地方工作的稳定性,还是四处移动的新鲜感让你保持灵感?你是一朵向日葵,寻找尽可能多的自然光吗?或者如果可以的话,你会住在一个山洞里吗?白天你如何对待自己的身体:你喜欢站立式办公桌、坐式办公桌、瑜伽球、地板、躺椅,还是以上所有的?
许多远程办公的人在家办公:考虑一下这是否适合你,兼职还是全职。也许这对于一种工作(通过视频会议召开团队会议)来说很棒,但对于其他工作(创造性工作、编码、低头专注时间)来说就不是了。午餐时放入大量衣物的自由会让你的生活更轻松吗,还是会让你被家里所有可能的杂务分散注意力?
你可能不会马上猜出所有的正确答案;它们甚至可能会随着时间而改变。保持好奇心,并对变化持开放态度:做好工作需要你有一个坚实的基础,而你的物理空间是拼图中至关重要的一块。
保持好奇心,并乐于接受改变。
第一年我在地下室远程工作。很好,然后就不好了。接下来的一年,我在家工作,在一个有落地窗的房间里。感觉很棒,然后感觉很孤立。我最终搬到一个共享的工作/社交空间住了一年,然后去了一个更传统的共同工作空间。我喜欢在家工作的自由,但我也喜欢一点咖啡店时间,加上我的共同工作空间里熟悉的面孔。
2。照顾好你的身心
如果你想做好工作,你需要能够清晰地思考,而心理健康的很大一部分是身体健康。如果你不能满足最基本的需求,比如睡眠和食物,你就不能在精神上表现出让你做好工作的状态。好消息是:这些因素中的大部分都很简单(这并不是说它们很容易),而且它们是你有能力独立改变的事情(而且比一张办公桌便宜)。
一些想法需要考虑:
- 你喝了足够的水吗?
- 你的饮食方式适合你吗?
- 你有充足的睡眠吗?睡眠时间是否稳定?
- 你正在进行某种体育活动吗,不管那对你意味着什么?
- 你白天有没有一点户外时间,尤其是在早上?
3。朋友和同事,归属感和爱
我们在生活中都需要一些联系,但在这个部门没有放之四海而皆准的解决方案。一个办公室每天提供内置的人际联系;偏远地区的人们需要为我们自己微调这种平衡。从好的方面来看,这给了你很多自由、灵活性和控制力。如果你以前陷入了只和同事交往的陷阱,那可能不再是一个选择。作为一名远程工作者,你可能需要更加积极主动地为你的生活配备适量的社交活动。
社交活动不会偶然发生:虽然没有人会强迫你出来参加欢乐时光,但你也不会在厨房里碰到他们惊喜的生日蛋糕。
作为一名远程工作者,你有机会也有责任对你要做什么和见谁做出积极的选择。否则,你的视频会议同事/同桌邻居/咖啡师可能是你唯一交谈过的人,或者知道你的猫怎么样的人(即使这些人真的很可爱,非常关心猫的健康)。
社交活动不会偶然发生:虽然没有人会强迫你出来参加欢乐时光,但你也不会在厨房里碰到他们惊喜的生日蛋糕。但这可能是一个建立长期系统的好机会,这些系统将比你当前的远程角色和情况更持久。也许有一个你想加入的体育运动队,或者一个几乎不加掩饰的定期聚会朋友的借口。我的一个朋友每周晚上举行“办公时间”,在那里他会提供坏建议和好拥抱;另一个人哀叹她缺少女性朋友,于是开始邀请她认识的酷女人聚一聚,并带上她们的朋友。
4。识别
许多组织现在意识到,工作换来的金钱并不是人们获得满足感的全部。别人对你每天工作的关心和质量的认可对工作满意度和成就感有很大的影响。对我来说,认可是人际关系和自我实现之间的粘合剂。
和你的同事在同一个物理空间工作提供了充分的击掌机会,特别的大喊大叫,以及当你无意中听到厨房里的人们谈论你有多棒时的美妙感觉。但作为一名远程工作者,有时意味着你会错过偶然的积极反馈或积极的肢体语言。有些经理会主动找出你需要什么来获得认可和欣赏。如果你有这样的经理,那太好了!如果没有,你可能需要扮演更积极的角色来获得你需要的和应得的认可。
做一些思考:你喜欢如何被认可?认可可以是公开的(团队或公司会议上的口头表扬),也可以是私下的(一对一),其中一种可能会让你感觉更好。不管你的偏好是什么,先问清楚你需要什么。不要有隐秘的欲望;即使是善意的管理者也无法读懂你的心思。如果你不习惯像这样直接提出要求,你可能想和朋友练习一下。刚开始可能会不舒服,但是通过以一种让你感觉良好的方式被认可,你会获得更多的工作满足感,这是非常值得的。
不要有隐秘的欲望;即使是善意的管理者也无法读懂你的心思。
5。自我实现
远程工作梦想的一部分是它提供的灵活性。如果不是为了过上你梦想的生活,灵活性又有什么用呢?你可能有很多梦想,你总是说,如果你有时间或灵活性,你会实现这些梦想,其中许多梦想现在可能比你意识到的更可行。远程工作给了你无数的选择来完成你需要做的事情:你如何定义家?自由对你意味着什么?除了工作之外,你还想在生活中完成什么?
这些都是大胆的问题——人生的重大哲学问题。远程工作给你自由、灵活性和能动性去完成对你来说重要的事情。
是的,大量的可能性可能会让人感到害怕,但定期检查自己以确保你对生活做出的选择会帮助你感到满足,而不仅仅是做眼前的事情,这是值得的。
在构建理想的远程工作生活时,请考虑这份绝对不详尽的列表:
- 你想要多大的灵活性和变化?你喜欢经常搬家还是喜欢找到一个完美的地方?
- 你有没有一个爱好是你想要围绕着它生活的?在某个地方(海边)或保持某个时间表(夜间或白天)支持你的课外活动吗?
- 你喜欢旅行的灵活性,而不限制自己的假期天数吗?
- 你参与的社区(宗教/文化/健身/生活方式)对你的幸福和健康很重要吗?
远程工作可能是令人满意的、适应性强的生活方式的一个关键部分,但像任何复杂的系统一样,很多东西可以用来优化它。选择听起来合理的选项是一个很好的开始,定期检查自己可以帮助你发现这些是否仍然是你的最佳选择。随着时间的推移,让“远程”成为我最喜欢的工作环境之一的因素已经发生了变化。
我过去喜欢独自拥有我们的房子(或者整天呆在咖啡店里),定期装饰我的桌子,在家做午餐。现在,我喜欢我的共同工作空间(有着大窗户和友好的人们),有我为自己设计的通勤,在我们餐厅的角落里有一张站立的桌子(下面有我们学步的孩子的桌子),并且能够在我需要的时候调整我的一天和我的环境。保持好奇,敢于犯错,不断尝试帮助我发展我的方法。我的偏远生活让我有机会比以往任何时候都更加探索和热爱我的家乡,每年,我也能从我出色的同事那里学到东西,不管他们是在几十(或几千)英里之外。
有兴趣探索 CircleCI 的远程工作吗?了解更多关于该团队的信息,并点击查看空缺职位。
模拟自动扩展构建集群第 1 部分:不让构建队列循环的数学证明
原文:https://circleci.com/blog/mathematical-justification-for-not-letting-builds-queue/
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
注意:这篇文章是系列文章的一部分。 Part 2 和 Part 3 现在也上线了,你可以点击这里在本系列有新帖子加入时得到通知。
在过去的 5 年里,CircleCI 在我们快速增长的构建容器舰队上运行了数百万次构建。我们调整了许多参数,以使我们庞大的自动扩展服务器集群始终有能力运行构建,同时最大限度地减少未使用的资源。
一个新来的孩子
然而最近,在推出 circle ci Enterprise T1 之后,我们有很多客户询问我们如何在 AWS 自动扩展组或类似服务中运行他们自己的构建。自动缩放对他们有意义吗?哪些参数可以保持 CircleCI 实例的响应速度,同时最大限度地减少服务器的空闲时间?问题是,这些客户不希望一个有几千个容器的集群每小时处理来自全球分布的开发人员的几千个构建。他们想要几百个容器来每小时处理几十个开发者的构建。他们还使用不同的机器类型,不同的集装箱规格,与 circleci.com 有不同的运输模式。我们是运行单个巨型集群的专家,但现在我们需要学习成为运行数十个异构中型集群的顾问。思考这个问题让我想起了《心学数学》书架上的一本书,我突然想到,也许有一种方法可以用排队论来模拟这个问题。单个服务器运行在时间上随机发生的固定持续时间的作业(遵循泊松分布)的简单情况被称为 M/D/1 队列,并且在这样的队列中有简单的期望等待时间的封闭形式的解决方案。多服务器(M/D/c)的情况有更复杂的封闭形式的解决方案,但是快速添加自动伸缩使问题变得棘手。或者至少我在几分钟的激烈搜索后没有找到答案,这实际上是一样的。
实验方法
在这里,我可以建立一些自动缩放的精细计算模拟,建立集群,用成千上万的可能参数运行蒙特卡罗实验的数百万次迭代,所以这当然就是我所做的。它位于 bellkev/asg-sim ,我将在本文的剩余部分使用该模型的数据。我还应该在这里指出,虽然我关注的是在 CircleCI Enterprise 中使用的构建集群建模,但我所涉及的大部分内容也将适用于在 circleci.com 上选择计划规模,甚至管理运行任意作业的服务器集群。
在讨论自动扩展之前,我花了一点时间讨论固定车队规模的情况,因为这是所有自动扩展解决方案的比较基础。在验证了我的模型与简单的 M/D/1 情况的理论预测相匹配之后,我开始研究平衡排队时间和资源利用的问题。只做好其中一件事,显然很容易。您可以供应不足并保持队列满且机器忙,也可以供应过度并保持队列空且机器空闲。下图清楚地说明了这一点。
此图显示了在不同大小的固定大小集群上运行相同流量模式时的情况。显然,较小的集群具有较高的机器利用率和较长的排队时间,而较大的集群具有较低的利用率和较短的排队时间。
成本函数
为了选择某种平衡点,我们需要做一点数学计算。开发人员的时间是宝贵的,所以他们在队列中等待构建的时间应该有某种价值。大型服务器也很昂贵,而且维持它们的运行也需要成本。您可以对总成本建模,如下所示:
cost = idle machine hours × cost per machine hour +
queued build hours × cost per developer hour
你可能在哲学上反对将人的工作时间等同于机器的工作时间,或者你可能会认为等待一个构建五分钟实际上会导致 30 分钟的生产力损失,因为这会导致中断。您甚至可能认为排队时间并不重要,因为开发人员可以在他们的构建运行时去喝咖啡或开发一些其他功能或其他东西。我认为这是荒谬的,因为如果开发人员在每次推进 CI 后都不得不进行任意长时间的休息或上下文切换,那么你在鼓励什么样的不可测量的坏结果呢?我们只需要将成本参数精确到十倍左右,所以不用太担心。我将使用每小时 200 美元(可能有点高,但中断是昂贵的)的开发人员时间和 AWS m4。除非另有说明,机器时间的大量点播率。
在我们继续之前值得指出的最后一点是,无论如何,缓慢的构建仍然是一样糟糕的。使用上面的参数,实际运行构建的成本如下所示:
build running cost = build run time ×
(cost per machine hour + cost per developer hour)
显然这是很糟糕的,因为它占用了计算资源和开发人员的时间,但这个数字只取决于构建运行时间、构建数量和其他成本参数。您应该尽最大努力降低这一成本,但这与优化配置不足/过度配置构建集群的浪费成本是另一个问题。
计算数字
好了,现在我们已经为我们的模型定义了一个合理的成本函数,我们可以从成本曲线的角度来看之前的车队规模实验。(美元数额基本上是任意的。它们很大,因为我用数百台服务器为每个数据点运行了相当于几个月的模拟,以使图形漂亮而平滑。)
这条曲线显然是不对称的,但如果你仔细想想,这是有道理的。在排队论中,队列增加的速度超过它处理作业的速度被认为是“不稳定的”,并且会迅速增长到无穷大。即使是稳定的队列也会由于流量的随机峰值而偶尔增长一点,导致一些平均队列长度,但是当系统接近不稳定极限时,该平均队列长度会逐渐增大。在曲线的另一端,随着我们向车队中添加建筑机械,成本会出现简单的线性增长。
如果我们为各种流量模式绘制其中一条曲线,我们就可以通过取最小值来得出这些工作负载的最佳车队规模。这是我们在不同频率下触发的 5 分钟构建的结果。
现在我们已经有了成本方面的最佳车队规模,让我们看看对于固定规模的车队,什么样的排队时间和机器利用率水平被认为是最佳的。
看那个!排队时间几乎为零,即使对于较小的车队规模,这需要将机器的利用率保持在 20%以下。请记住,即使在我们将开发人员视为机器中简单的、可交换的、固定价格的齿轮的模型中也是如此。保持较低的排队时间显然是值得的。还要注意,随着流量和车队规模的增长,您会获得一些规模经济。
如果我们以每小时固定的构建次数来看不同的构建时间,情况是相似的。
只是这一次,对于较慢的构建,我们获得了更高的利用率。这是缓慢构建的乌云周围的一线希望,但显然不是努力的目标。
这是真的吗?
到目前为止,对这一结论肯定有合理的可能的反对意见。例如,一个团队在昂贵的硬件上运行构建,生活在开发人员稍微便宜的经济环境中,并且不认为等待构建是“那么糟糕”呢?即使我的假设相差几个数量级,每次构建都需要相当于两台 m 4.10 x 大型机器的按需价格,并且开发人员赚取饥饿工资,结果几乎是一样的:
即使有超级昂贵的硬件,让构建排队超过 10 秒钟也是不划算的,除非开发人员每小时赚 10 美元。让构建排队超过一分钟就相当于把开发人员的时间估价为每小时不到一美元,而让构建排队超过十分钟只有在开发人员每小时赚几分钱的情况下才有意义。
让构建排队超过一分钟就像把你的开发人员的时间估价为每小时不到一美元。”
我的理论是,为什么这种排队仍然很常见,是因为大多数组织没有将他们构建基础设施的成本与他们开发人员的成本放在一起考虑。相反,建造机器很可能是一项支出,一些团队领导受到激励来减少这一支出,而工资则更加不透明,由更高的层面决定。由于排队构建造成的生产力损失很难测量或量化,所以它被掩盖起来,成为懒惰、抱怨的开发人员的又一个借口。
结论
幸运的是,迄今为止的实验结果可以归结为一些非常简单的建议。如果您需要决定将固定大小的构建集群(或 circleci.com 计划,在逻辑上是等价的)设置为多大,那么将它设置为导致零排队的任何大小。当我说零时,这意味着对于大多数工作负载来说平均不到一秒,即使您在昂贵的硬件(或 circleci.com 上的高并行性)上运行构建,平均也不到 10 秒。随着流量(或构建运行时间)的增加,您将享受到更高的机器利用率,但您不应该为此担心。总是以接近零的排队时间为目标。
我还应该强调,这个模拟确实假设构建之间的时间是随机的,不受队列时间本身的影响。在长期排队构建的团队中,当队列很长时,开发人员避免推送或跳过 CI 可能是很常见的。但是,这种行为是你希望在你的团队中发生的吗?大概不会!
等等,自动缩放怎么样?难道不是应该力挽狂澜,让我们实现网络规模吗?嗯,我的手指现在已经厌倦了写博客,所以我将不得不把所有激动人心的数据留到下一期。敬请期待!
注意:这篇文章是系列文章的一部分。 Part 2 和 Part 3 现在也上线了,你可以点击这里在本系列有新帖子加入时得到通知。
构建工程文化:微晋升和导师
在过去的十年里,我一直在男性主导的环境中工作。虽然在这段时间里,我遇到了许多傻瓜和批评者,但我也很幸运地遇到了许多支持者和倡导者。虽然没有一家公司是完美的,但在 CircleCI,我有机会在我喜欢的环境中发展我的职业生涯。这一部分要归功于我,很大一部分要归功于我的同事。他们通过一系列真实的、实质性的、微推广来指导、赞助和支持我。他们的行动产生了影响。不管你是谁或你在组织中的角色是什么,你的行动也是如此。一个包容的团队要么被团队中每个人的姿态所加强,要么被削弱。从小处着手。
注意:如果你是一名领导者,你有责任努力创造让每个人都感到受欢迎的环境。自我导向的员工行动并不构成解决技术多样性和包容性问题的全面解决方案。
——
按照大致的时间顺序,我的团队做了一些具体的事情,这些事情对我的职业生涯产生了影响,值得效仿:
贾斯汀赞助了我。他向公司推荐了我,并把我介绍给我们的工程副总裁杰夫。他给了我一只脚。
杰夫在我意料之外的时候采取了行动。在初步的信息访谈午餐后,他:
- 他后悔在最近一轮招聘结束后才遇到我。
- 主动让我联系他的一些专业人士。
- 实际上,是他帮我联系了他的一些联系人。
那时,我刚刚从编码训练营毕业,很少有机会进入科技世界。杰夫给了我更多,最终给了我一份工作。剧透:我接受了。
在 CircleCI 的第一年,我的许多同事为我提供了技术指导。Rishi 让我们的看板板上有适合小型技能的 JIRAs。我的同事安教我如何深入研究一个问题,如何确定在哪里测试它,以及不要害怕打破东西来解决它们。马特用深思熟虑的解释回答了天真的问题。Phil 用完整的历史、阐明上下文、代码库历史和各种流行的编程信念系统的进化来回答问题。他还使用 Emacs,这很糟糕,但人无完人。
在令人沮丧的日子里(CircleCI 很棒,不是乌托邦),像 Tad、Eugene 和 Kyle 这样的人邀请我去喝咖啡,把事情说清楚。他们提供了可靠的建议和新的观点。
在第一年,我的同事们给了我支持,挑战性的工作,以及成长的空间,让我可以舒服地称自己为“软件工程师”,而不用做内部的双重考虑。
那一年,我的同事对我很有耐心(我承认,这种宽容是通过回避问题来分散负担培养起来的)。重要的是,他们也承认了我的贡献。致谢不需要游行。这些是人们做出的对我有意义的小手势:
- 在 pull request 注释中使用👏 emoji 来突出我代码中的一个小亮点。
- 在意识到他们正从我对之前粗糙的代码进行的重构中积极受益时,发送一封直接的感谢邮件。
- 在 Slack 上我们的#感恩频道上发布一条消息,承认我写的一些文档对他们有多么大的帮助,并鼓励其他人使用它。
尽管有这些积极的互动,但也有少数时候,人们说的或做的事情是不专业的,可以说是出格的,让我感到不舒服。在每种情况下,当我向我的经理表达我的不舒服时(甚至当他们意识到我变得不舒服时),他们会毫无疑问地介入。有时候成为盟友意味着支持某人。
有时这意味着当你越界时,要倾听和学习。在大多数情况下,对抗者幸福地不知道他们引起的不适,并试图改变他们的触发行为。在情况恶化之前,及早设定期望,纠正不良行为。
获得了发展所需要的尊重和自由,我进入了 CircleCI 的第二年。那一年,我面临了新的挑战。新的同事,新的团队动态,新的代码库,以及我自己对提升和做好它的期望。在同龄人中我又一次幸运了。
当我试图理解更多的技术概念时,比如系统如何关联以及何时使用什么库,约翰通过定期配对帮助我成长。他纠正我错误的假设,每天向我介绍新的概念和工具,同时疯狂地打字,在屏幕上扔 tmux 会话,并奇迹般地设法不吵醒绑在胸前的一岁大的孩子(他一岁大)。约翰也使用 Emacs,这很糟糕,但人无完人。
那年晚些时候,在 Eric 的推荐下,我开始担任我的团队的交付审查领导,不久之后又成为我们的代理团队领导。又一次,我对某样东西是陌生的,并且在这方面没有特别的天赋。我的同事再一次为我提供了成长和获得自信所必需的支持、空间和挑战。
内森在会议中有目的地向我传递技术问题,并在棘手的情况下提供指导。当我尴尬地开玩笑说远程会议需要一个笑声音轨时(在这种情况下,与会者保持沉默,观众的反馈很难衡量),布雷迪在特别长的停顿时间里开始播放一个。Marc 给我上了新代码库的速成班,用他的话说,他很喜欢和我一起工作,感谢我给我们团队带来的秩序。
在更大的组织范围的会议上,Nate 对我的意见进行了补充,对我的想法给予了肯定,并在此基础上提出了自己的观点并改进了可交付成果。贝尔提名我是为了表彰我在跨团队协作方面的工作。帕特感谢我做了胶水工作。
在我在这里的时候,我把事情搞砸了,丢了球,制造了一些事故。与我的同事一起,我们已经纠正了这些事件,并无可指责地制定了更好的前进道路。这些事件被视为独立事件,任何人都可能犯的错误。我并不是唯一擅长制造失败的人,也没有人让我觉得我是。
相反,我觉得自己得到了指导、赞助和支持。在我们的行业中,多样性是一个持续的挑战。平衡失衡让人感觉像是一项永无止境的任务。从小处着手。对你在下一次代码评审中发现的聪明之处进行评论,在会议中询问一个安静的同事的意见,对那些给你灵感的人给予肯定。感觉野心勃勃?处理这场宾果游戏。你的行动有所作为,它们营造了一个包容的环境,我和其他人都想在这里工作。
如果你愿意和我一起改变现状,和一些非常聪明的人一起写一些非常酷的代码,请查看我们的公开招聘信息!
阅读更多信息:
从 buddybuild 迁移到 CircleCI - CircleCI
原文:https://circleci.com/blog/migrating-from-buddybuild-to-circleci/
在被苹果收购后,昨天 buddybuild 宣布他们将停止为 Android 版本和免费 iOS 计划提供服务。祝贺 buddybuild 团队,尽管这个消息让一些团队为他们的移动构建寻找一个替代的 CI 系统。我们将这种比较放在一起,以帮助团队决定 CircleCI 是否是适合他们的工具。
CircleCI 和 buddybuild 如何比较?
| | 绕圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈圈 | buddybuild |
| 移动支持(macOS、Android) | ✅ | ✅ |
| Non-mobile project support | ✅ | ❌ |
| 为 macOS 和 Android 构建隔离 | ✅ | ✅ |
| 支持开源项目 | ✅ | ✅ |
| 自动上传到 iTunes Connect | ✅ | ✅ |
| 在同一工作流程中运行 macOS 和 Linux 作业 | ✅ | ❌ |
| 工作流的手动批准步骤 | ✅ | ❌ |
| UI 中的配置 | ❌ | ✅ |
| 没有 GitHub / Bitbucket 访问权限的合作者 | ❌ | ✅ |
CircleCI 和 buddybuild 的主要区别是什么?
git 中存储的配置文件
buddybuild 的配置完全通过他们的 web 用户界面完成,而 CircleCI 将配置存储在 git 存储库中的配置文件中。将配置作为文件存储在存储库中的好处是:
- 更好的审计跟踪。查看谁对您的配置进行了更改,并在 GitHub 和 Bitbucket 上并排比较新旧配置。
- 轻松恢复以前的配置版本。既然配置存储在 git 中,就很容易恢复以前版本的配置文件。
- 将配置复制到新的存储库。如果您有多个存储库需要配置,您可以在它们之间复制配置文件,并在每个存储库中调整细节。
当您注册 CircleCI 时,我们会为您提供一个配置文件模板,您可以在您的项目中使用它。我们在本文档中提供了当前最佳实践配置以供参考。
你可以在 CircleCI 这里找到一个使用 fastlane 的示例 iOS 应用程序。
使用 fastlane 进行代码签名、测试和部署
buddybuild 为代码签名、运行测试命令和部署应用程序提供了内置机制。CircleCI 不提供任何内置机制,相反我们依赖于使用 fastlane ,这是一个用于构建、测试和发布 iOS 和 Android 应用的开源工具。
我们选择依赖 fastlane,因为 fastlane 和相关工具背后的社区在跟踪苹果和其他供应商实现的改进和变化方面做得很好。这确保您在与 Apple 构建工具和第三方服务进行交互以进行部署时,能够获得最一致的构建体验。
我们建议使用 fastlane 运行您的测试,导出您的应用程序,并部署到 TestFlight(以及其他测试服务)和 iTunes Connect。请查看此文档部分,了解在 CircleCI 上使用 fastlane 的示例。你可以在这里找到关于通过快速通道匹配设置代码签名的说明。你可以在这里找到更详细的快车道文件。
相同配置的 iOS 和 Android
使用 CircleCI 工作流,可以在一个配置文件中包含 iOS 和 Android 配置。这在构建 React 原生项目时尤其有用。
使用像 Danger 和 swiftlint 这样的 iOS 开发工具的项目也可以利用这一点:例如,如果 swiftlint 没有通过,您可以提前使工作流失败,或者并行运行 iOS 测试和 Danger。
您将在这里找到一个在工作流中将 swiftlint 和 Danger 作为单独作业运行的示例。请看一下在 CircleCI 上构建 iOS 和 Android 应用的示例 React Native 项目。工作流程文档显示了所有可用的工作流程选项。
试试 CircleCI
阅读我们的 iOS 文档以了解更多信息或查看我们的circle ci 示例 iOS 项目。准备好开始建造了吗?报名并立即开始。
使用最小特权原则和 AWS IAM 权限将风险降至最低
在评估一个人的风险因素时,拥有适当的安全控制不再是唯一的考虑因素。即使实现了安全控制,它们也经常没有得到适当的许可。例如,关键服务级别帐户被授予过多的系统权限并不罕见。如果帐户受到威胁,这将增加系统内的攻击媒介。然后,这个被侵入的帐户可能会执行恶意代码或访问非常敏感的数据,这可能会给组织带来许多问题。防止这类安全事件的一种方法是实现最小特权原则的概念。最小特权原则意味着只给一个帐户那些执行其预定功能所必需的特权。例如,仅用于从亚马逊 S3 存储桶读取文件的服务帐户不需要将文件写入存储桶。任何其他权限,如列出、更新或写入文件都会被阻止。拥有最低权限的帐户可以保护系统不被拥有过多权限的受损帐户利用。
在这篇文章中,我将向你展示如何收紧你的 AWS IAM CircleCI 服务帐户的权限,以便它只对一个特定的 AWS S3 桶中的/build
文件夹具有访问和权限。这些权限将限制该帐户只能操作/build
文件夹,并阻止访问其他资源。
这篇文章假设你有:
确定\创建一个 AWS S3 存储桶来存放您的构建
您需要确定一个现有的 S3 存储桶或为您的 CircleCI 服务帐户创建一个新的存储桶。我举的例子 S3 的斗名是devops.datapunks.org
。您可以随意命名您的 S3 存储桶,但是一个常见的、最好的做法是使用熟悉的完全限定域名(FQDN) 格式来命名存储桶,这将有助于随着您的基础架构的增长而保持原样。我的 bucket 的devops.
部分表示这个 bucket 将与我的datapunks.org
域的 devops 进程相关。
现在您已经有了一个 bucket,接下来创建一个名为builds
的文件夹,它将保存 CircleCI 构建生成的所有构建工件。
创建新的 IAM 组
AWS IAM 有一个组的概念,它是 IAM 用户的集合,使您能够为多个用户设置权限。这些组减轻了用户&对他们权限的管理。
从 IAM 控制台:
- 点击
Groups
>Create New Group
按钮。 - 指定新的组名。使用有意义的描述性名称。我将使用
circleci_devops
。 - 在
Attach Policy
屏幕>点击Next Step
。 - 点击
Create Group
按钮。
现在,您应该可以在列表中看到新创建的组。
创建 IAM 策略
IAM 策略是与定义其权限的身份或资源相关联的 AWS 实体。当主体(如用户)发出请求时,AWS 会评估这些策略。策略中的权限决定是允许还是拒绝请求。策略作为 JSON 文档存储在 AWS 中,作为基于身份的策略附加到主体,或者作为基于资源的策略附加到资源。
AWS 基本上有两类策略:托管策略和内联策略。
托管策略是基于身份的独立策略,您可以将其附加到 AWS 帐户中的多个用户、组和角色。您可以使用两种类型的托管策略:
- AWS 托管策略:这些是由 AWS 创建和管理的托管策略。
- 客户管理的策略:您在 AWS 帐户中创建和管理的管理策略。与 AWS 托管策略相比,客户托管策略可以更精确地控制您的策略。
内联策略是您创建和管理的策略,直接嵌入到单个用户、组或角色中。
在这篇文章中,您将创建一个新的customer managed
策略,因为我们想要限制我们的服务帐户用户对我们的 bucket 中的build
文件夹的访问。
在 IAM 控制台中:
- 点击
Policies
>点击Create Policy
- 点击
JSON
选项卡 - 将以下 JSON AWS 策略粘贴到文本字段>单击
Review Policy
重要
您必须将以下 JSON 中的 bucket 名称替换为您之前创建的 bucket 的名称。因此,在文本的所有实例中,桶名devops.dpunks.org
必须替换为您的桶名,否则帐户将无法访问 S3 桶。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets"
],
"Resource": "arn:aws:s3:::*"
},
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": "arn:aws:s3:::devops.dpunks.org"
},
{
"Effect": "Allow",
"Action": "*",
"Resource": "arn:aws:s3:::devops.dpunks.org/builds/*"
}
]
}
- 输入策略的名称。我的例子使用了
s3_full_access_devops_builds
。 - 输入策略的描述。
- 点击>
Create Policy
。
您刚刚创建的策略基本上允许对我们的 S3 存储桶中的build
文件夹的完全访问,这相当于只允许对这个文件夹的读/写权限。
将新策略附加到 IAM 组
新的s3_full_access_devops_builds
策略已经准备好附加到我们之前创建的circleci_devops
IAM 组。在 AWS IAM 控制台中:
- 点击
Groups
>点击Permissions
标签。 - 点击
Attach Policy
按钮。 - 在过滤器字段中键入
s3_full_access_devops_builds
或您策略的名称。 - 在列表中选中要附加的策略。
- 点击
Attach Policy
按钮。
新策略现已附加到circleci_devops
组,并将应用于分配到该组的任何用户。
创建新的 IAM 用户
您已经创建了一个新的 IAM 组 IAM 客户管理策略,并且还将该策略附加到了该组。接下来,您将创建一个新用户,并将其分配到circleci_devops
组,该组将授予该帐户在 bucket 的 S3 build
文件夹中的读/写权限。
在 AWS IAM 控制台中:
- 点击
Users
>点击Add User
按钮。 - 在
User Name
文本框中输入一个名称。我的示例用户是circleci_service
。 - 检查访问类型部分的
Programmatic access
选项。 - 点击
Next Permissions
按钮。 - 选中将用户添加到组部分中的
circleci_devops
选项。 - 点击
Next: Review
按钮>点击Create User
按钮。
您现在应该会看到一条Success! Account created
…消息,在这里您必须获取帐户的访问密钥 ID 和秘密访问密钥。
重要
访问密钥 ID 和秘密访问密钥值非常敏感,必须加以保护。你必须能够以. csv 格式下载这些凭证,但请注意,这将是你能够访问密钥的唯一时间。如果您此时不保存密钥或丢失密钥,您将不得不创建新密钥并管理先前创建的密钥。
在 CircleCI 项目中配置 AWS IAM 密钥
您创建了一个新的编程用户,该用户对您的 S3 存储桶中的build
文件夹只有读/写权限,现在您必须将该帐户与您的 CircleCI 帐户中的项目相关联。登录 CircleCI 控制台仪表板。下面是关联 AWS IAM 证书的简要说明,您还可以在这里看到更详细的说明
- 点击您想要链接 IAM 账户的项目的
Settings
(cog 按钮) - 找到
Permissions
章节>点击AWS Permissions
- 输入您的
Access Key ID
和Secret Access Key
>点击Save AWS Keys
按钮
您的 IAM 帐户密钥现在保存在 CircleCI 项目中,您现在可以对 S3 存储桶中的build\
文件夹进行读/写访问。你的 CircleCI 版本现在可以安全地上传版本包到你的 S3 桶了。
现在,您可以安全地管理和使用这些 AWS 凭证来对严格定义的资源执行特定的操作。
结论
这篇文章简要解释了最小特权原则的概念,并向您展示了如何在您的 CI\CD 管道中实现这一实践。这些概念将极大地减少 IAM 凭证受损时的安全风险和攻击媒介。这些做法对用户管理、事件管理和安全审计也很有帮助。
既然您已经对最小特权原则有了基本的了解,那么就开始负责任地计算吧!
移动和浏览器测试:设置简单、管理方便、信心增强| CircleCI
测试是开发生命周期中最重要的部分之一,但是对于拥有在许多不同设备或浏览器上运行的应用程序的公司来说,这是非常棘手的。此外,编写测试可能需要大量的时间投入,这对试图快速进入市场的团队来说是一个挑战。
这就是为什么许多团队转向 CI/CD 来自动化和简化测试。尽管如此,编写大量有价值的测试可能很困难。为了帮助解决这一挑战,我们与各种测试工具合作,为 CircleCI 用户创建预构建的集成。这些浏览器和移动测试集成允许用户在测试创建和管理上节省时间和精力。
我们广泛的列表orb帮助团队以更少的努力建立可靠的测试。如今市场上有如此多的测试工具和理念,我们希望给予团队选择最适合他们的工具所需的灵活性。最终,这些预先构建的集成是为了帮助团队快速、轻松地建立复杂的测试,这样他们就可以专注于开发。
查看我们的 web 和移动测试平台,直接从您的 CI/CD 渠道帮助促进和管理浏览器和移动环境测试:
浏览器测试集成
Orb 链接 | Orb 描述 |
---|---|
柏树 | 运行 Cypress.io 端到端浏览器测试,无需花费时间配置 CircleCI。这个 orb 在 Cypress 仪表板上记录结果,并以并行模式进行负载平衡测试。 |
酱实验室 | 自动设置内置的 http 代理,允许轻松访问防火墙后的开发/qa 站点。代理在构建开始时作为后台任务启动,在构建结束时结束连接。 |
雨林 QA | 在每次提交时集成并运行 RainforestQA 测试用例。 |
LambdaTest | 通过在 CI/CD 管道中集成 LambdaTest,在 2000 多种浏览器和操作系统上执行自动跨浏览器测试。 |
目录 | 利用 Katalon 和 CircleCI 之间的无缝集成,在 CI/CD 中为任何浏览器运行自动化测试。Katalon orb 允许使用强大的端到端测试框架进行跨浏览器和跨平台测试。 |
酸 | 通过 Happo.io 将截图测试直接集成到您的 CI/CD 管道中。 |
氧气 | 使用 Oxygen 作为 CircleCI 构建流程的一部分,运行集成和端到端测试。 |
幽灵探长 | 在 CircleCI 项目上执行 Ghost Inspector 浏览器测试。 |
Testim | 在代码提交或合并时运行 Testim 端到端、跨浏览器 UI 测试,无需花费时间配置 CircleCI。Testim orb 允许团队从人工智能稳定的测试中受益,而无需离开他们的 CircleCI 工作流。 |
PreFlight | 在您的 CircleCI 版本中,跨不同的环境、浏览器和屏幕大小执行印前检查的自动化 UI 测试。 |
移动测试集成
Orb 链接 | Orb 描述 |
---|---|
酱实验室 | 自动设置内置的 http 代理,允许轻松访问防火墙后的开发/qa 站点。代理在构建开始时作为后台任务启动,在构建结束时结束连接。 |
雨林 QA | 在每次提交时集成并运行 RainforestQA 测试用例。 |
目录 | 通过 Katalon 和 CircleCI 之间的无缝集成,在 CI/CD 中创建和运行移动测试。Katalon orb 支持在所有操作系统和环境上使用 Appium 兼容的测试框架进行测试。 |
现在安全 | 将 CircleCI 工作流与 NowSecure orb 集成,以自动启动每个移动应用版本的安全性和隐私测试。 |
元宵节 | 启动 Genymotion Android 虚拟设备,通过 ADB 连接,并在 Genymotion 云 SaaS 上停止设备,以进行移动自动化测试(web 和本地应用程序)。 |
氧气 | 使用 Oxygen 作为 CircleCI 构建流程的一部分,运行集成和端到端测试。 |
Testfairy | 将 Android 和 iOS 应用程序上传到 TestFairy,并轻松分发给用户。 |
阅读更多关于移动应用开发持续集成的信息。
“CircleCI 非常适合移动开发团队的独特移动需求。它们提供了关键功能,不仅能够实现高效的开发生命周期,还能完成 Apple AppStore 和 Google Play 提交流程。我们的移动客户在其 DevSecOps 流程中利用 CircleCI 和我们的 NowSecure orb 来确保内置安全性。”Brian C. Reed,NowSecure 首席移动官
“CircleCI 的 orb 概念使我们能够轻松地为用户提供无缝的 CI/CD 执行流程。通过最低的设置要求,Katalon 用户能够扩展他们的测试范围,实现整体 CI/CD 管道,并为他们的项目利用强大的测试框架。”Dzung Ngo,产品副总裁,Katalon。
“Testim 很高兴与 CircleCI 合作,帮助现代开发团队更快地进行测试和发布。这种集成消除了开发和测试过程之间的摩擦,从而提高了软件质量并加快了交付速度。”Testim 创始人兼首席执行官柳文欢·鲁宾
好奇想了解更多?请访问 Orb 注册表或查看这些点播网络研讨会:
移动应用安全测试:工具和最佳实践
为了最大限度地降低应用程序的安全风险,开发人员需要他们的应用程序经得起严格的安全测试。幸运的是,有一些工具可以简化甚至自动化这些安全测试。还有最佳实践来指导和通知测试过程。
在本文中,我将介绍移动应用程序最常见的安全问题,并重点介绍流行的安全测试。我还将讨论移动应用安全测试的最佳实践,并回顾在 CI/CD 管道中保护移动应用的工具。
移动应用安全测试的重要性
为了理解为什么安全测试很重要,我将描述这些常见问题:
- 数据存储保护不当
- 使用本机代码引起的内存问题
- 使用开源/第三方工具
数据存储保护不当
如果您没有为您的数据库设置适当的数据库凭证,或者如果您的 cookie 存储加密不良,攻击者可以很容易地读取这些数据存储的内容。
以根设备或逆向工程应用程序为例。如果由于安全措施薄弱,攻击者可以轻松地访问您的数据库,那么您的信息就有被泄露的风险。
使用本机代码引起的内存问题
尽管用 C、C++和 Objective-C 编写的应用程序要快得多,但是用这些语言编写的糟糕的代码会导致内存泄漏和缓冲区溢出。这些内存陷阱会导致 RAM 的问题以及内核-陆地进程的系统稳定性问题。攻击者可能利用这些问题来执行其他攻击,甚至通过触发内存泄漏和缓冲区溢出来导致拒绝服务(DoS)攻击。
使用通用 C 编程和 Objective-C 中的最佳实践来避免内存泄漏。静态代码测试(在运行代码之前检查应用程序中的安全漏洞)有助于更早地识别此类威胁。静态代码测试工具可以查明哪里可能发生内存泄漏和缓冲区溢出。
使用开源/第三方工具
开发人员使用开源库和框架来简化代码生产是很常见的。攻击者可以使用这些工具对您的系统发起攻击。更糟糕的是,当在应用程序中使用时,它们可能会启动恶意代码。
导致客户数据泄露的开源漏洞的一个例子是 ParkMobile 漏洞。一个第三方软件漏洞泄露了这个流行的北美停车应用程序的 2100 万用户的个人信息。
第三方服务漏洞通常是错误配置的结果。Check Point Research 发现 1 亿用户的私人数据因集成使用不当而暴露。
左移测试方法是避免第三方风险的最有效方法。这种方法强调在应用程序开发生命周期的开始就设置测试。Shift-left 允许测试您打算使用的开源和第三方工具的漏洞。这将有助于你及时发现危险信号。
安全性测试的重要性
对你的应用的攻击可能对你的组织不利。安全性测试对开发生命周期非常重要,因为它:
- 使您的应用符合行业标准。
- 让你的终端用户对你的产品产生信任感(例如,当你的应用通过 ISO 27001 认证时)。
- 帮助您检测和了解弱点,以便您能够消除和防范安全漏洞等风险。
- 降低与安全事故相关的成本,包括财务成本和声誉成本。
- 帮助您了解应用生态系统中需要调整的内容:第三方代码、您的代码或您的安全人员。
安全测试的类型
在本节中,我将探讨几种类型的移动应用程序安全性测试:
- 漏洞扫描
- 渗透测试
- 风险评估
- 姿势评估
漏洞扫描
这种方法使用自动化工具来检查应用生态系统中可能在攻击过程中受到危害的区域。漏洞扫描器寻找已知的漏洞,尤其是软件依赖关系中的漏洞。
漏洞扫描还可以检测应用程序中容易遗漏的漏洞,检查常见漏洞及其特征的记录。然后将匹配情况报告给开发人员或质量保证(QA)团队。您可以将漏洞扫描集成到 CI 管道中,我将在本文后面介绍这一点。
渗透测试
渗透测试模拟攻击来测试应用程序的安全性并找出其弱点。这不同于漏洞扫描,因为它涉及到人工输入(在这种情况下,是一个有道德的黑客)。他们使用多种技术侵入应用程序,检查攻击者可能利用的地方。
与漏洞扫描不同,漏洞扫描会导致误报,而渗透测试识别的威胁是真实的。这些测试通常可以提供漏洞精确位置的更多细节。
风险评估
风险评估包括列出应用生态系统中的所有组件和人员,以确定他们在遭受网络攻击时的个人风险。这有助于对组织内的某些资产实施措施,例如,如果 IT 部门的某人决定帮助或煽动攻击。
姿势评估
状态评估确定应用程序的当前安全状态,帮助开发人员确定需要改进的地方。它可以告诉您在攻击过程中哪些信息可能会受到损害,它将如何中断业务,需要多长时间才能恢复,以及需要采取哪些预防措施。
状态和风险评估一起工作,它们也可能包含其他类型的安全测试。所有这些都有一个共同的目标,那就是帮助您识别安全漏洞、防止攻击并减轻攻击。
移动应用安全测试的最佳实践
在本节中,我们将探讨保护和测试移动应用安全性的最佳实践的优势。这些是
- 供应链测试
- 使用 SAST、DAST 和 IAST 的技术
- 认证和认证测试
- 加密测试
供应链测试
攻击者可能不会直接攻击你的应用程序的主要代码,但他们可能会使用第三方代码。安全问题一节中讨论的开源和不可信的第三方工具就属于这一类。防止这些攻击的一种方法是左移测试,前面也讨论过。更具体地说,您可以执行静态代码测试,这可以通过静态应用程序安全测试(SAST)工具轻松实现。正如我们将在下一节中看到的,这些工具可以帮助检测安全风险。
供应链测试可以防止最终用户开始使用你的应用时出现的安全风险。使用其他方法进行测试时,供应链风险很容易被遗漏或忽略。
使用 SAST、DAST 和 IAST 的技术
SAST 指的是在应用程序运行之前测试应用程序代码的漏洞。Klocwork 和 Checkmarx 等工具对于实现 SAST 非常有用。
动态应用安全测试(DAST)主要针对运行中的应用。DAST 扫描应用程序以检查任何可能导致安全风险的漏洞。用于移动设备的 DAST 工具的一个例子是 HCL AppScan。
交互式应用安全测试(IAST)融合了 SAST 和 DAST 的特点,从而最大限度地发挥优势,最小化权衡。IAST 有助于捕捉源代码中和运行时的漏洞。
您可以使用这三种技术来帮助您轻松地识别可能发生诸如内存泄漏和缓冲区溢出、不正确的输入验证等问题的地方。查看 SAST vs DAST:它们是什么以及何时使用它们了解这些技术的更多信息。
认证和认证测试
薄弱的身份验证和授权允许攻击者获得更高的权限,并做出可能导致系统崩溃或收集用户信用数据的事情。DAST 有助于确保用户不会在不该登录或不该访问的时候登录应用程序。
以共享目录为例。拥有学生权限的用户能否访问只有拥有教师权限的用户才能访问的答案文件?用户可以绕过安全问题检查吗?在做测试的时候,这些问题应该在你的脑海中。
加密测试
强大的加密算法将使攻击者很难访问应用程序并获得重要信息。注意,仅在授权上设置加密是不够的。作为开发人员,我们可能会忘记或忽略在我们的应用程序使用的层中设置它,并且可能包含敏感信息。例如,OSI 模型的传输层。攻击者可以使用传输层进行窃听、泄露通信信息等。
为了确保您的应用程序遵循加密的最佳实践,请使用 SAST 来确保您设置了强大的加密机制。
在您的测试中使用持续集成
尽管安全测试很重要,但在许多开发团队中并不总是优先考虑。许多开发者更关注于实现应用的主要目标。应用程序中有许多需要测试的漏洞,您可能无法手动发现。如果开发人员发现安全测试浪费他们的时间,他们倾向于跳过它。
为了防止这种情况,您可以通过在 CI/CD 管道中设置安全测试工具来使用测试自动化。这些工具可用于向开发人员反馈关于应用程序漏洞的有意义的数据,开发人员反过来处理这些数据。开发人员可以专注于应用程序的交付,同时修复漏洞。
用于保护 CI/CD 管道中的移动应用的工具
要将测试集成到移动应用程序的 CI/CD 管道中,您可以使用 CircleCI 的移动测试工具。
多亏了 orbs,在这个平台上设置和管理测试很容易。orb 是一种可重用的 YAML 配置,有助于自动化重复的过程。使用 orbs 可以简化项目设置。您可以在 CircleCI 管道中轻松使用可信的第三方安全测试提供者。
一些有用的 orb,可以共享 CircleCI 配置的包,包括 NowSecure 和 Genymotion 。
结论
移动应用程序的广泛用户基础使它们对攻击者更具吸引力。此外,第三方应用程序的不当配置等安全问题会使它们更容易受到攻击。
现在,您已经了解了漏洞扫描和状态评估等安全测试,以及遵循最佳实践的重要性,您可以确保您的应用和用户的个人数据得到保护。
联系 CircleCI 了解有关将安全测试添加到您的移动应用的 CI/CD 管道的更多信息。
联邦风险和授权管理计划持续整合| CircleCI
CircleCI 现在获得了 FedRAMP (联邦政府评估和授权技术供应商的计划)的授权,使 CircleCI 成为第一个符合政府机构要求的严格安全和隐私标准的 CI/CD 工具。
以前,政府开发人员被迫依赖笨重、遗留的持续集成 (CI)选项,这些选项虽然免费,但需要太多的开销,维护成本高昂,并且降低了工作效率。现在,政府的开发者社区可以获得私营部门长期以来获得的竞争优势,确保联邦机构要求的安全和隐私控制。
“联邦管理者需要开始考虑他们开发工具的总拥有成本。如果你的团队花时间对一个“免费”的构建工具进行故障诊断,那么它就不是免费的。在尝试了几种不同的构建工具之后,我们选择了 CircleCI,从完全免费的开源工具到托管服务。CircleCI 运作良好,这意味着我们的团队只需专注于建设更好的政府服务。对于 CircleCI 这样的服务,总拥有成本要低得多。”
- Ryan Hillard,小型企业管理局 IT 专家/系统开发人员
小型企业管理局、总务管理局和退伍军人事务部等联邦机构已经在利用 CircleCI 每天自动构建、测试和部署软件。有了这一新的授权,整个联邦开发人员社区现在都可以访问 CI/CD 工具,该工具每月为世界上一些最具创新性的公司(如 Docker、GoPro 和 Zenefits)运行超过 1200 万次构建。
FedRAMP 是什么?
联邦风险和授权管理计划(FedRAMP)是美国政府的一项官方计划,旨在简化政府机构购买云技术的过程。白宫管理和预算办公室在 2010 年初开发了 FedRAMP,作为向云优先计划转变的标准化安全审查流程的一种方式。在此之前,每个机构都有自己的标准和指导方针,这为团队获取技术创造了困难和漫长的时间表。
FedRAMP 简化了安全控制,以此作为一种根据一套通用的基本准则来衡量公司安全和隐私状况的方法。虽然像 ISO27001 和 SOC 2 Type II 这样的认证是由私人审计公司开发和评估的,但政府官员使用 NIST 安全控制作为公共基线来开发 FedRAMP。
政府机构可以通过使用 FedRAMP 市场来识别已经通过严格的安全和隐私要求的工具,从而管理风险。
“保护联邦 IT 系统的传统方法主要侧重于在‘上线’前评估和响应安全问题。这种模式成本高,负担重,因为发现的安全问题可能导致返工和重做。使用 CircleCI 等自动化持续集成和持续部署工具,我们能够将网络安全要求“向左”移动到流程中,以实现真正的“内置安全”模式。”美国小企业管理局首席信息安全官 Beau Houser
涉及到什么?
FedRAMP 对 CircleCI 的低影响 SaaS LI-SaaS 类别的评估包括从网络图到第三方集成表的完整库存审计和评估、所有基础设施的主库存以及对严格控制的书面回应。这些控制措施涵盖了广泛的安全协议,并检查了 CircleCI 的用户授权、漏洞扫描流程和事件响应。本质上,联邦首席信息安全官应该评估新云供应商的所有方面。
在 6 月份成功完成初步审查后,FedRAMP 官员聘请了一名第三方审计员(一名拥有绝密安全许可的前联邦雇员)访问 CircleCI 的旧金山总部。审计员在 7 月份花了 4 天时间与 CircleCI 的员工在一起,彻底审查了每项控制措施的处理方式。一个联邦安全小组对他们的调查结果进行了审查分析,CircleCI 由此获得授权。
CircleCI 现在可以在 fed ramp market placehttps://marketplace.fedramp.gov买到,联邦软件团队可以下载第三方评估的副本。
要设置演示或与我们的政府团队交流,请联系我们 government@circleci.com。要了解 CircleCI 与顶级安全组织合作的更多信息,请查看政府安全页面。
借助 CircleCI | CircleCI 的见解,监控和优化您的 CI/CD 渠道
原文:https://circleci.com/blog/monitor-and-optimize-your-ci-cd-pipeline-with-insights-from-circleci/
今天,我们很高兴地宣布在 CircleCI 推出 Insights dashboard。我们的用户要求提供有助于他们在 CI/CD 部门提高效率的数据,我们正在回答。新的 Insights 仪表板为工程团队提供了可操作的数据,用于优化管道并从 CircleCI 中获得更多信息。
我们将从 10 月 1 日开始推广,并在未来几天扩展到我们所有基于云的客户。一旦可用,就可以通过 UI 中的左侧导航、Pipelines 页面或任何工作流页面来访问仪表板。
更多数据意味着更明智的决策
insights 仪表板允许用户:
- 跟踪状态:查看哪些工作失败,哪些工作流测试失败,并确定流水线改进工作的优先级。
- 监控持续时间:找出哪些工作流或作业花费的时间最长,并找出缓存、并行化和我们新的便利映像可以帮助加快速度的机会。
- 优化渠道:深入了解信用支出以及吞吐量、成功率和平均恢复时间,充分利用您的 CircleCI 体验。
“使用 CircleCI 的洞察能力,我们能够更快地查明失败的构建,并看到成功率攀升。CircleCI 的见解也有助于在为我们的团队确定最佳 CI/CD 实践时了解长期趋势。”Jon Anderson,高级软件工程师,外联
展望未来
在 CircleCI,我们的使命是让技术驱动的组织能够做最好的工作,Insights 是这一使命的延续。通过提供可操作的指标,洞察使公司能够做出更好的工程决策。有了这个特性,工程团队将能够更好地理解瓶颈并识别优化机会。
在未来几周,我们将继续增强和更新仪表板,增加趋势和测试洞察等功能。我们致力于通过释放组织的数据潜力来支持组织。更多的数据,更好的决策。
在这篇文章中,了解更多关于如何发现和监控团队成功的工程指标。
使用 DevSecOps 监控管道安全性
原文:https://circleci.com/blog/monitoring-pipeline-security-with-devsecops/
这个博客是为那些已经实施了安全最佳实践,并且正在寻找如何继续监控安全的进一步信息的团队而写的。如果您正在寻找有关如何实施自动化安全实践的信息,请阅读我们的电子书《漏洞管理和开发与 CI/CD 合作》。
DevSecOps 继续生产
如果您的团队已经将安全实践作为您的默认开发过程的一部分,那么保持监控是至关重要的。今天非常安全的代码明天可能会包含已知的安全漏洞。监控已经运行的软件,以及正在开发的代码。
你可以使用类似于 Splunk 或者 Prisma Cloud 这样的工具来完成这项工作。以所有相关方都能理解的格式自动生成您的报告。像 Honeybadger 、 Honeycomb 或 LogDNA 这样的监控和日志工具可以提供很大的帮助——并且有 CircleCI orbs 可以让你快速地将它们集成到你的管道中。
当您在云环境中托管时,请确保检查该环境的监控工具。Azure 有应用洞察,AWS 有 CloudWatch 应用洞察。好好利用它们。它们可以跟踪恶意登录尝试、未经授权的访问以及来自应用程序的错误。
第三方工具通常通过使开始变得更容易、使其他团队可以访问监控、生成报告以及监控其他指标来增加价值。
修补软件
当您的工具报告漏洞时,尽快修补您的软件非常重要。更新可能会破坏软件,这包括来自开源项目和第三方供应商的更新。
为了限制打补丁的任何风险,请确保在打补丁过程中遵循合理的开发实践,包括 DevOps 原则,如自动化单元测试和集成测试。尤其是集成测试,可以让您充满信心地修补软件,确信修复不会导致额外的问题。
自动化集成测试也将大大减少发布补丁的人力。如果您在团队之间共享标准化资产,您将确保所有团队都获得更新。
下一关:CircleCI 球体
如果你使用 CircleCI 作为你的 CI/CD 管道,你应该考虑使用 CircleCI orbs 。orb 是 CircleCI 配置的可重用、可共享的开源包,支持许多第三方服务的即时集成,包括扫描仪服务等有价值的安全工具。
CircleCI 提供了许多漏洞扫描 orb,可以轻松地将漏洞扫描集成到您的管道中,只需花费最少的设置时间。借助 orbs,您可以获得一个保护管道的开箱即用的解决方案。
您将找到我们已经提到的工具的扫描器,如 Alcide、Snyk 和 Stackhawk,以及更多的扫描器,如:
- 锚点(用于图像)
- AWS 参数存储(用于管理和加载环境机密)
- Checkmarx (用于静态和交互式应用程序安全测试)
- 可能是(用于扫描您的 web 应用程序的漏洞)
- 秘密中枢(为应用程序提供密码和密钥)
- SonarCloud (用于连续的代码质量扫描)
如果你想使用一个还没有 orb 的安全扫描器,你可以创建一个并把它推到开源的 CircleCI Orb 注册表中,贡献给社区。
与 DevSecOps 一起前进
将安全意识纳入 DevOps 实践的 DevSecOps 方法提供了一种利用 CI/CD 将漏洞扫描和管理添加到现有部署管道的战略方法。
随着时间的推移,您可以通过首先引入基本扫描来让开发团队习惯于 DevSecOps,然后随着时间的推移增加您扫描的漏洞的数量和类型来建立这一点。
漏洞管理只是 CI/CD 作为开发团队力量倍增器的一个领域。构建弹性系统允许团队以更少的时间和更低的风险交付高质量的代码。通过让您的 CI 渠道为您服务,您可以获得公司的关键优势和杠杆点。如果您准备好尝试一下,您可以在 CircleCI 上创建一个完整的 DevSecOps 管道。
只是想了解更多关于安全的信息?阅读我们的电子书漏洞管理和使用 CI/CD 的开发工具。
monorepo 开发实践的优势和挑战| CircleCI
在一个单一的整体存储库中,也称为 monorepo,您将所有应用程序和微服务代码保存在同一个源代码存储库中(通常是 Git)。通常,团队将各种应用程序组件的代码拆分到子文件夹中,并使用 Git 工作流来获得新功能或错误修复。这种方法对于大多数使用整体架构开发的应用程序或系统来说是很自然的。
这种 monorepo 代码通常只有一个生成应用程序可执行文件的构建管道。虽然维护很简单,但是这种方法降低了整体速度,因为一些难以修复的 bug 会阻止团队将候选发布部署到产品中。
本文概述了 monorepos 和 polyrepos 之间的区别,权衡了 monorepos 的利弊,并帮助您确定 monorepo 是否是您团队的最佳选择。
面向微服务的 Monorepo 与 polyrepo
随着微服务架构变得越来越流行,团队倾向于将他们的代码分成许多存储库(polyrepos)。团队独立开发微服务,使用不同的、特定问题的工具和编程语言。例如,一些开发人员可能使用像 Python 这样的开源项目进行人工智能(AI),而其他人则使用 Java 或。NET 来实现 API。
polyrepos 的优势显而易见。一个小团队可以快速实现并独立部署高速软件开发的微服务。
然而,使用独立的存储库会带来风险。系统知识分布在由不同团队维护的多个回购协议中。在某些时候,你意识到没有人知道如何构建和部署整个系统。
虽然 polyrepos 似乎是微服务的自然选择,但具有统一和自动化构建和部署管道的 monorepo 可以缓解许多问题。仔细看看 monorepo 的好处以及一些误解,可能有助于您决定 mono repo 环境是否最适合您的团队。
monrepos 的好处
单一回购方式有几个优点:
- 易见。如果你正在做一个调用其他微服务的微服务,你可以看看代码,了解它是如何工作的,确定 bug 是发生在你自己的代码中,还是发生在另一个团队的微服务中。
- 代码共享。团队为各种微服务复制代码会导致额外的工程开销。如果公共模型、共享库和助手代码都存储在一个存储库中,团队可以在许多微服务之间共享它们。
- 改善协作。mono repo 消除了团队之间的障碍和孤岛,使设计和维护协同工作的多组微服务变得更加容易。
- 标准化。使用 monorepos,跨团队标准化代码和工具变得更加容易。您可以实施分支策略来保持主分支的整洁,限制对特定分支的访问,强制实施命名准则,包括代码审查人员,以及强制实施最佳实践。分支策略将进行中的工作与已完成的工作隔离开来。
- 可发现性。monorepo 提供了整个代码的单一视图。您可以在 monorepos 中查看整个存储库的状态,筛选所有分支,并跟踪修改,这比在 polyrepos 中容易得多。
- 发布管理。mono repo 保留关于如何部署整个系统的所有信息。自动化的构建和部署管道不会像在 polyrepo 中那样隐藏每个团队内部的部署知识。
- 更简单的重构。直接访问所有微服务使得在 monorepo 中重构代码更加容易。此外,您可以更改代码结构。在文件夹和子文件夹之间移动源代码要比在多个存储库之间移动源代码容易得多。
蒙雷波斯的挑战
尽管有这些好处,monorepos 也带来了一些挑战。更改公共代码会影响许多应用程序组件,并且源冲突可能很难合并。您的部署过程可能更具挑战性,并且您需要扩展您的源代码控制管理系统。
根据你的情况,monorepos 的好处可能会超过它们的挑战。
对 monrepos 的误解
如果你一直在微服务架构中开发应用,你可能对 monorepos 有一些误解。一些开发人员认为,由于难以创建统一的构建过程,多种编程语言和工具阻止了使用单个 repo。您可以通过使用容器来缓解这一问题,将每个微服务构建到容器映像中,然后作为一个单独的单元进行部署。
一旦你容器化了微服务,你也可以单独测试它们。因此,您可以将所有构建阶段保存在 monorepo 中,而不是保存在许多存储库中。唯一的区别是您的构建目标被设置为容器。
通常,开发人员认为 monorepos 会导致紧密耦合的代码。不尽然,但是您必须运用良好的判断力来防止代码陷入混乱。
开发微服务的时候,你让他们独立,这样他们就不依赖其他微服务了。当您的团队遵循微服务开发的最佳实践和指南时,您可以在 monorepo 中实现这一点。
这个想法是将一个大系统分成可独立部署的、松散耦合的单元,与组件不同,这些单元通过流程边界相互通信(通常使用 REST APIs)。尽管 monorepo 使得打破微服务架构模式变得很容易,但它本身不会导致紧密耦合的代码。
你应该独立地更新微服务,但是你可能认为这对于 monorepo 是不可能的。通过用更高级的部署策略(如蓝绿色或金丝雀色)取代滚动更新来应对这一挑战。您可以将新版本与旧版本并行部署,同时确保新的微服务版本按预期工作。如果您检测到一个 bug,您可以快速将流量重定向到以前的版本。
自动化持续集成和持续部署(CI/CD)管道有助于应对所有这些 monorepo 挑战。每个开发团队可以独立工作微服务,构建其容器映像,并在不影响其他团队的情况下部署它。在将微服务投入生产之前,他们可以在测试环境中对其进行验证,并保持新旧版本的可用性。容器化让你可以独立地部署和测试微服务,而不用担心它们不同的工具和编程语言。
CI/CD 工具可以自动扩展并帮助您管理复杂的部署,以便您可以在更大的 monorepo 中构建、测试甚至部署单个微服务。
决定适合你的团队的策略
您如何决定在微服务开发中使用 monorepo 还是 polyrepo?首先,评估你的团队文化:它适合 monorepo 鼓励的协作开发吗?
其次,评估你团队的纪律:他们能够避免创建紧密耦合的代码吗?他们需要避免让自己的微服务依赖 monorepo 中的其他微服务。请记住,您可以通过分支策略和权限限制来实施这一原则,控制谁可以将微服务部署到生产环境中。权限决定了哪些团队和团队成员可以部署每项服务。
您可以通过一个统一的自动化 CI/CD 管道来实施所有这些实践,您的团队可以在一个更大的 monorepo 中构建、测试和部署各个微服务。自动化管道使管理 monorepo 变得更加容易,同时保持快速的部署速度。
结论
Monorepos 有很多优势,比如可见性、协作和代码共享,但它们并不适合每个团队。了解你的团队的优势和劣势,以确定单一回购是否是正确的选择。
如果您决定使用 monorepo,请保持您的高速软件部署,并通过统一的自动化 CI/CD 管道减少常见的微服务缺陷。您可以马上开始,今天就注册您的 CircleCI 免费试用。
从复杂到简单,容易,CI/CD - CircleCI
原文:https://circleci.com/blog/moving-from-complex-to-simple-easy-ci-cd/
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
PromptWorks 为从初创公司到财富 50 强的杰出公司开发定制的网络和移动应用程序,这些公司希望提高他们用来与客户互动的软件的质量和完善程度。我们帮助客户了解什么是可能的,我们的灵活性和洞察力为他们省钱。他们来找我们是因为,出于各种原因,他们需要找到一个拥有专业知识和资源的合作伙伴来帮助他们完成产品开发过程。我们开发的软件使公司做他们需要做的事情成为可能。
为什么是 CircleCI
我们依靠 CircleCI 对所有项目进行持续集成和持续部署。自动化测试对我们来说非常重要,持续交付也是如此,这样产品所有者(客户)可以尽快亲身体验我们正在构建的东西。我们的工作流要求我们在每个分支上运行测试,并在合并到 master 之前进行绿色构建,然而令人惊讶的是,许多 CI 工具并没有将功能分支视为一等公民。
CircleCI 之前的生活
在使用 CircleCI 之前,我们有一个复杂的 Jenkins 设置来为分支机构创建工作,在 matrix 构建时并行运行测试,并通过电子邮件和 Slack 通知团队。这种拼凑的插件和脚本没有激发信心,需要大量的设置和维护!当詹金斯情绪低落时,我们会在黑暗中承诺一段时间,但结果总是不好。
一流的可定制功能支持
有了 CircleCI,我们找到了对主要语言的简单支持:Ruby、Python 和 Javascript。并行化一直是小菜一碟。我们已经找到了重新运行不可靠测试的特定语言解决方案,尽管我们已经在提高测试套件的可靠性方面取得了很大进展,但我们已经接受了基于浏览器的验收测试的现实。我们虚拟化了我们更复杂的应用程序的许多依赖项,并使用 CircleCI API 在虚拟化关闭的情况下触发特定的夜间构建。这样,我们就能感觉到我们的应用程序是否能成功应对真实的基础设施和服务,而不会在它们停机时中断日常开发。
我们有自己的 circle.yml 文件和测试运行程序,这些文件和程序经过了大量定制,可以在测试失败时截屏,注意 FIXMEs 和其他不应该提交的单词,运行 linters 等等。我们发现用户界面简洁易用,最近的功能如构建时间和洞察力也很有帮助。
时间就是金钱
我们所做的任何一个脚本或事情都没有节省大量成本。CircleCI 让我们朝着我们的理想前进了一大步,并让我们定制剩下的路。我们可以更快地运行构建,缩短周期时间,以便更快地与客户迭代。与他们的旧 Jenkins 设置相比,它为我们的一个客户每月节省了大约 1500 美元的维护时间。对于我们的其他客户来说,他们为我们在另一个提供商上设置 CI 支付的时间更少,每小时 200 美元,这可以很快增加。
CircleCI 是我们开发过程的一个重要部分,没有它我们不会部署。
杰森·加伯是软件工程师,也是 T2 prompt works 公司的联合创始人。你可以在 Twitter 和 LinkedIn 上关注 Promptworks。
dock file-dock build-dock build name | circles-dock 样式-dock build-dock build name | circles
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
Docker 在 2017 年 5 月推出了多阶段构建。用最简单的话来说,这些是包含不止一个FROM
语句的 docker 文件。稍加调整,您就可以在 CircleCI 2.0 上构建多级 Dockerfiles。
什么是多阶段 Docker 构建?
Docker 新的多阶段构建允许 docker 文件变得更加强大,用一个 docker 文件提供复杂的构建。一个用例可能是,您通常有一个 docker 文件用于构建应用程序的源代码,然后有另一个文件用于运行和测试应用程序。这里有一个例子Dockerfile
用多阶段构建来解决这个问题,这个例子是从docs.docker.com那里借来的:
FROM golang:1.8.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]
这个 Docker 文件的第一个“阶段”是使用 Docker 库中的官方 Go 映像构建 Go 应用程序。然后,第二阶段从第一阶段(用COPY --from=0
)复制新构建的二进制文件,并将其放在一个定制的 Alpine 映像中。结果是一个基于 Alpine 的 Docker 映像,任何人都可以使用它来运行这个 Go 应用程序,而不需要任何最初用于构建该应用程序的额外软件,例如 Go 工具链。
命名阶段
为了将我们构建的二进制文件复制到第二个阶段,我们使用了COPY --from=0
,因为我们没有命名阶段,所以我们使用了它的索引号。相反,我们可以在FROM
语句中命名一个阶段,然后在以后使用这个名称。所以我们可以从FROM golang:1.8.3 as compile-stage
开始,在COPY
语句中我们将使用COPY --from=compile-stage
。
基于 CircleCI 2.0 构建
上面的 Dockerfile 示例可以用 CircleCI 2.0 构建,但是需要根据您使用的 executor
进行配置更改。这是因为 CircleCI 上默认使用的 Docker 版本是 Docker 17 . 03 . 0 Community Edition(CE)。Docker v17.05.0-ce 及更新版本支持多阶段构建。machine
和docker
执行者都允许我们请求更新的 Docker 版本。
docker
Executor
我们可以在setup_remote_docker
下设置 Docker 版本用于远程引擎:
- setup_remote_docker:
version: 17.05.0-ce
记住,当使用docker
执行器和setup_remote_docker
时,这只是为您提供了一个远程 Docker 引擎来连接。您的构建所使用的基本 Docker 映像仍然需要安装 Docker 客户端。更多关于这个以及 Docker 支持哪个版本的信息可以在这里找到。
machine
Executor
machine
执行器现在支持指定一个映像,类似于docker
执行器的工作方式,但是使用我们的基本 VM 映像。默认图像不足以满足我们的用例,但“边缘”图像是:
jobs:
build:
machine:
image: circleci/classic:edge
这为我们提供了 Docker v17.06.0-ce 的机器 VM。更多关于machine
的执行者这里。
使用 Docker v17.05.0-ce 和更新版本,您现在可以在 CircleCI 2.0 上构建多级 Docker 文件。
多云策略适合您的组织吗?圆环
在过去的几年里,许多开发团队已经用云托管的基础设施取代了传统的数据中心。云的采用持续增长,团队正在更新应用程序以利用基于云的服务。但是对于许多组织来说,采用单一的云提供商来托管他们所有的应用程序和数据会使他们的业务面临风险。为了降低这些风险,一些组织正在以一种特别设计的方式在多个云提供商之间分配资源。
本文探讨了采用多云策略的一些驱动因素、好处和缺点。在跨云提供商工作时,您还将获得一些关于构建环境的最佳方式的建议。
什么是多云策略?
多云战略不仅仅是将工作负载放在多个云提供商处。正确的多云策略意味着要具体设计和部署资源,包括规划跨多个提供商部署服务时采取的方法。这种策略可能需要使用纯粹基于消费者的提供商(如 AWS、Azure 和 GCP),在同一提供商内使用不同的云,或者包括混合的私有云提供商。重要的是使用多个云提供商时的意图和要求。
多云服务的常见架构包括:
- 跨提供商和负载平衡器部署容器化的应用程序或服务,以实现“永不停机”的环境。
- 按业务功能分组的服务,分布在云提供商之间,每个服务都有冗余的冷启动组件。
- 面向每个云提供商的同类最佳或技术一致的应用程序或服务组件。
这些架构需要一些战略思维来确保模式被一致地实现,并且与业务目标一致。多云战略还必须与受影响的应用和服务的架构和设计保持一致。
多云策略的优势
组织采用多云策略的原因有很多。大多数组织迁移到多云环境,以降低单一云环境的风险。其他团队可以选择多云策略,以便利用不同提供商的优势和技术堆栈。根据这些驱动因素,采用多云策略有三大优势:
- 弹性
- 灵活性
- 服从
弹性
许多组织为世界各地要求应用程序和系统全天候运行的客户提供服务。拥有弹性环境对于防止服务中断(包括云供应商环境中断)至关重要。即使是最大的云提供商也有中断的时候,比如谷歌、T2、Azure 和 T4 的 AWS。
确保关键服务不会随云一起关闭是多云方法的一个重要优势。有时,甚至在不同的云提供商上提供冷备份服务也可以补救这种情况,以便组织可以实施长期修复。例如,让 web 应用程序节点分布在多个供应商的云上,可以在某个供应商出现故障时保持您的服务正常运行——尽管资源减少了。即使将服务部署到处于空闲状态的辅助云中,但您可以在需要时打开它,这也提供了一种简单、快速的修复方法,直到问题得到解决。
灵活性
当第一次将工作负载迁移到云环境中时,大多数组织会选择最符合他们大多数标准的单一提供商。但是,随着这些工作负载的增加,对该单一供应商服务的依赖也在增加。供应商经常改变他们的策略、服务、协议和定价模式。局限于单一供应商可能意味着不一致、成本增加、错过更好的产品或被迫更新应用和服务。在云环境中移动和创建工作负载很容易,但是如果移动是无计划的,则移动工作负载会很困难且成本很高。
采用多云策略来设计应用和服务可以最大限度地减少供应商锁定。使用相同的技术堆栈和特性,如容器化,可以轻松有效地跨云环境设计和部署相同的服务。它还使组织能够选择最专业的云服务,从而提供显著的优势。
尽管如此,在使用多云策略时,确保所选选项的灵活性是至关重要的。例如,Azure Functions 是一个事件驱动的、无服务器计算平台,您的团队可以使用它来构建和部署服务,而无需调整规模来满足需求。您的团队还可以将这些功能部署到容器中,并在 Azure 之外托管它们,从而在需要时提供更改服务的选项。
服从
与避免供应商锁定一样,许多组织对 IT 系统合规性有特定的要求。这些合规性要求可以涵盖数据隐私或主权问题,包括灾难恢复和缓解规范。
通过采用多云策略并结合评估服务和数据的合规性要求,您的组织可以为关键组件建立特定的环境或架构。个人身份或财务信息等敏感数据集可能需要存储在强化的私有云环境中。数据安全性要求允许公共云环境中托管的应用程序以特定方式查询数据。
多云的缺点
虽然采用多云策略有一些重要的好处,但您应该意识到两个潜在的缺点:增加成本和环境复杂性。这些缺点可以通过本文前面描述的优点来克服和抵消,但是对于没有做好准备的组织来说,它们也可能成为问题。
复杂性
考虑到可用服务的广度,采用单一云提供商可能会增加员工的学习曲线。采用第二个云提供商可能会使 IT 人员必须学习的服务、其他多云系统和流程的数量翻倍。对于资源有限的组织来说,确保人们了解多云环境是一项挑战。这些类型的环境通常需要专门的平台工程或跨职能 IT 运营团队来保护、管理和优化多个部署目标和具有各种集成的技术堆栈。
费用
采用多云策略的第二个潜在障碍是总体成本。即使当前的资源基本上在提供商之间分割,环境之间的额外流量和管理层也会增加一些成本。不了解提供商之间的成本差异,尤其是在采用技术时,也会导致不必要的支出。
在计算转向多云的价格时,请确保将雇佣或培训员工的成本计算在内,以涵盖这两种环境。此外,一定要考虑未使用资源的成本,当环境的复杂性阻碍了所有资源的可见性时,可能会出现这种情况。采用多云可以节省一些成本,但这通常不是采用多云策略的主要原因。
你应该采用多云策略吗?
什么时候是考虑采用多云策略的合适时机?这个问题很难回答。由于采用多云策略会增加复杂性和成本,因此应该有一个重要的业务驱动因素可以解决。如果没有这种合理的、当前的业务需求,组织可以承担大量的复杂性和成本,而没有显著的优势。
这些业务驱动因素的例子包括:
- 与不符合行业或法律要求相关的重大风险或成本
- 需要工具、服务和技术的更大灵活性,以提高开发速度
- 希望减少跨多个供应商的集装箱化服务套件的停机时间
这些例子仍然需要显著的提升和坚实的技术资源,以确保您的团队能够有效地采用这种策略。
如何在 CI/CD 工作流中管理多个云部署
采用多云策略需要在多个不同的环境中一致地部署应用和服务。以下是设置 CI/CD 工作流以从多云环境中获得最佳效果的一些提示:
- 开发一个所有团队都尽可能遵循的标准化管道部署策略。
- 确保部署尽可能模块化,以便在团队将组件部署到不同供应商时可以互换组件。
- 采用提供多云支持的基础设施即代码模板,如 Terraform 和 Ansible ,以确保基础设施的最小重新配置。
- 使用发布管理工具来确保正确的构建到达合适的环境。
摘要
采用多个云供应商有几个好处,但可能相当具有挑战性且成本高昂。如果没有明确定义的策略,这种复杂性和成本会很快失控。有意采用多云并客观评估组织的技术成熟度是确保成功的关键。
CircleCI 支持构建 CI/CD 管道,将应用程序和服务部署到多个云。立即注册免费账户并了解更多关于如何为多云环境准备管道的。
使用多个 CircleCI orbs | CircleCI 配置管道
本教程涵盖:
- 什么是球体以及如何使用它们
- 为什么你可能想要使用多个球体
- 使用多个 orb 设置配置
持续集成/持续交付(CI/CD)工具为开发人员提供了自动化软件开发过程的能力。一旦开发人员将代码推送到 git,您的 CI/CD 系统就可以进行构建、测试、试运行、集成测试、部署和伸缩。太棒了!
在本教程中,我们将看看 CircleCI orbs 以及它们如何支持您的 CI/CD 练习。我们将了解如何使用多个 orb,以及 orb 如何帮助各种应用程序类型的多重构建。
先决条件
开始之前,请确保您具备以下要求:
- 一个圆的账户
- GitHub 的一个账户
- 基本 CI/CD 知识
- 对管道工作原理的基本理解
在 GitHub 上可以获得演示应用的克隆版本。每个项目目录中都有设置和安装说明。
什么是球体?
orb 是可重用的 YAML 配置包,它将配置减少到几行代码,允许开发人员轻松地打包、发布和重用配置。
球体非常有用,因为它们:
- 节省花费在项目设置上的时间
- 提高组织效率
- 简化第三方集成
球体可以成为你的配置文件中的英雄。
要使用 orb,请进入 CircleCI orb 注册表。在那里,您将找到适合您的技术堆栈的 orb 以及开始使用的说明。如果你不能为你的技术栈找到一个 orb,你可以按照 CircleCI 最佳实践和入门指南创建一个专门的 orb。
使用多个球体
CircleCI 支持在同一个配置文件中使用多个 orbs 这一节将向您展示如何使用您之前克隆的项目。
克隆的项目结合了一个 Flask API 和一个节点。JS 命令行应用程序。我们已经对这两个应用程序进行了测试;它们需要使用 Python 和节点 orbs 在 CircleCI 上运行。
为什么是多个球体?
就采用哪种技术以及何时采用而言,开发人员具有很强的适应性。大多数当前的应用程序都是由 API 组成的,带有一个前端插件来使用 API。假设 API 是用 Python 构建的。开发人员决定使用像 React 这样的 JavaScript 框架作为前端。在这种情况下,我们有一个 Python 和 JavaScript 应用程序。
为这种类型的应用程序配置 CI/CD 管道时,您应该将所有配置放在一个配置文件中。这是因为 CircleCI 在.circleci/config
中需要一个配置文件。
因为 orb 是灵活的,所以我们可以将 orb 组合在一个单独的config.yml
文件中,在 CircleCI 上运行我们的测试。多个 orb 将导致更快的项目执行,更健壮的 CI/CD 管道,以及优秀的可重用性。
设置多个球体
配置多个 orb 的过程类似于配置单个 orb 的过程。
首先,请按照下列步骤操作:
-
在您的
.circleci/config.yml
文件的顶部,添加下面一行来指定 CircleCI 版本version: 2.1
-
Invoke the orbs you wish to use. For example:
orbs: node: circleci/node@5.0.2 python: circleci/python@2.0.3
这个代码块在 CircleCI 版本之后列出了 orb。然后我们将在整个脚本中使用这两个。因为 orb 使用不同的名称引用,所以它们彼此完全独立。
-
将节点元素引入到现有的工作流和作业中。此时,orb 的元素可以用作
<orb-name>/<element>
。
现在您已经了解了配置多个 orb 的基础知识,您可以开始使用 Python 和节点 orb 配置您的克隆应用程序了。这些 orb 是独一无二的,正确的配置可以在 orb文档指南中找到。
下面是将配置文件分解成几个包含version
、orbs
、jobs
和workflow
的块。
CircleCI 版本
version: 2.1
球
这些是在管道中使用的特定 orb 版本:
orbs:
node: circleci/node@5.0.2
python: circleci/python@2.0.3
乔布斯
您可以考虑让每个应用程序有两个单独的作业,因为您要测试两个。每个 orb 都有自己的执行任务。
作业 A
角色:测试 Python 应用程序
使用的宝珠:Python 宝珠
jobs:
test-python-app:
executor: python/default
steps:
- checkout
- python/install-packages:
app-dir: ~/project/flask-api
pip-dependency-file: requirements.txt
pkg-manager: pip
- run:
command: |
pytest -v
name: Test
作业 B
角色:测试 JavaScript 应用程序
使用的 Orb:节点 orb
test-node-app:
executor: node/default
steps:
- checkout
- node/install-packages:
app-dir: ~/project/nodejs-cli
cache-path: ~/project/nodejs-cli/node_modules
override-ci-command: npm install
- run:
command: |
npm run test
working_directory: ~/project/nodejs-cli
工作流程
工作流决定了已定义作业的执行顺序。
workflows:
test:
jobs:
- test-python-app
- test-node-app
比较有和没有多个 orb 的配置
在示例 GitHub 库中可以找到config.yml
的完整打包版本以供参考。典型配置不使用 CircleCI orbs。
当您查看配置文件时,请注意不同之处,并使用不同的工作流运行每个工作流。例如,运行一个使用多个 orb 的、和一个使用标准配置的、而没有 orb。
除了 orb 提供的组织和可读性,回顾工作流运行时揭示了 orb 提供的更多好处。下面的屏幕截图显示了两个工作流的成功运行:一个使用标准配置,另一个使用 orbs。
此图显示,使用多个 orb 的配置运行的工作流比使用标准配置运行的工作流执行速度稍快。两种工作流中完成每项作业所需的时间略有不同。
球体又一次胜利了!
结论
本教程展示了在 CI 管道中使用多个 orb 的好处。您能够基于不同应用程序的架构配置 CI 管道来使用多个 orb。您已经体验了多个 orb 如何加速工作流运行、提高可重用性以及简化与第三方应用程序的集成。直到下一次,继续建设!
Waweru Mwaura 是一名软件工程师,也是一名专门研究质量工程的终身学习者。他是 Packt 的作者,喜欢阅读工程、金融和技术方面的书籍。你可以在他的网页简介上了解更多关于他的信息。
原生与跨平台移动应用开发| CircleCI
原文:https://circleci.com/blog/native-vs-cross-platform-mobile-dev/
短短十年间,智能手机变得无处不在。它们通过发短信和打电话来促进交流,提供娱乐,支持管理,并以应用程序的形式为用户提供实用程序。
用户通过他们的应用商店访问这些移动应用,无论是苹果的应用商店还是谷歌 Play 商店。开发人员在设计它们时考虑到了智能手机的操作系统。针对的两个主流操作系统是 Android 和 iOS。无论你的目标是 Android 还是 iOS 操作系统,都会影响你开发应用的策略。
开发移动应用有两种方式:原生开发和跨平台开发。在本地或跨平台开发之间做出选择是任何移动应用项目中最重要的决策之一。这个单一的决定对应用程序的设计、用于创建它的技术以及最终能够访问它的用户都有巨大的影响。
如何在原生和跨平台之间选择
本机开发和跨平台开发之间的根本区别源于您正在开发的操作系统。原生移动开发允许您为特定的操作系统(Android 或 iOS)构建应用程序。相比之下,跨平台移动开发允许您构建针对多个操作系统的应用程序。
在转向在确定使用哪种开发方法时应该考虑什么之前,让我们来探讨一下本机应用程序和跨平台应用程序之间的差异、它们是如何创建的以及它们的优缺点。
什么是原生移动应用?
原生移动应用是针对 Android 或 iOS 开发的应用。根据您正在开发的操作系统,您的应用程序通常是用特定的编程语言编写的。
使用 Java 或 Kotlin 编程语言编写原生 Android 应用程序。Java 是最初用来创建 Android 应用程序的语言。谷歌于 2017 年在 Android 上推出了对 Kotlin 的支持。Kotlin 支持面向对象和函数式编程,而 Java 仅限于面向对象编程。
如果您正在开发原生 iOS 应用程序,您可以使用 Objective-C 或 Swift 编程语言。C 是 C 编程语言的一个超集。它是用来编写在 iOS 上运行的软件的初始语言。苹果在 2014 年的全球开发者大会上推出了 Swift。对于 Apple 生态系统来说,它是一种强大的通用高级编程语言。苹果声明 Swift 比 Objective-C 快 2.6 倍,语法更容易学习。
本地移动应用的一些众所周知的例子包括:
- 谷歌地图
- 拼趣
- Spotify
原生应用工具
除了拥有特定于操作系统的软件开发工具包(SDK)之外,原生移动开发还需要集成开发环境(IDE)。
对于 Android 应用,需要使用 Android Studio 或者 IntelliJ IDEA。这些工具可以在 Windows、macOS 或 Linux 上运行。对于 iOS 应用,你需要使用 Xcode 或者 AppCode 作为你的 IDE。这些工具只能在 macOS 上使用。
原生移动应用的优势
构建原生移动应用程序有一系列好处。其中包括:
- 更好的性能:您可以为特定平台创建和优化原生应用。您还可以使用平台的核心编程语言和 API 来编译它们。这使得它们非常快,更有效,并对用户的行动作出反应。
- 严密的安全性:原生应用可以增强用户数据的安全性。他们可以访问特定于平台的内置安全特性。
- 质量 UX: 本地应用在用户输入和输出方面有更流畅的体验。外观和感觉是一致的,因为它们继承了设备的操作系统界面。他们遵循特定操作系统的设计指南,让应用程序的流程感觉更加自然。
- 完整功能集访问:原生移动应用可以访问设备的所有功能,提供更丰富、更集成的用户体验。他们还可以获得推送通知等服务,这是推动用户参与的关键。
- 最小 bug:原生应用开发者一发布就能获得更新的 SDK。这些更新总是包含改进和新功能。
原生移动应用的缺点
原生移动应用程序有很多好处,但也有一些缺点。一些缺点包括:
- 成本:原生移动应用的部署成本通常很高。对于特定的平台,你需要有独立的开发团队。例如,你需要精通 Java 或 Kotlin 的开发人员来创建一个原生 Android 应用程序。你还需要额外的本地 iOS Swift/Objective-C 开发者。
- 开发时间:为不同平台开发类似应用的不同团队需要时间。维护和增强也需要大量的工作。
- 缺乏代码可重用性:你必须在不同的项目中创建和保存代码,以适应不同的移动操作系统。
什么是跨平台移动应用?
您可以从单个代码库创建跨平台的移动应用程序。跨平台 app 开发的目标是用一个项目针对不同的操作系统。您使用跨平台框架创建这些应用程序,这些框架使用来自统一 API 的特定于平台的 SDK(Android SDK 和 iOS SDKs)。这使您能够轻松访问不同平台的 SDK 和库。
私营公司创造了这些框架。流行的跨平台框架的例子包括:
- 通过 Meta 反应 Native。它使用 JavaScript 作为编程语言。
- 被谷歌扑。它使用 Dart 作为编程语言。
- 微软的 Xamarin(正在向毛伊岛迁移)。它使用 C#和 XAML 作为编程语言。
跨平台的移动应用程序通常被编译为使用原生 UI 元素,使应用程序感觉像是原生的。如前所述,它们提供了底层平台 SDK 的抽象。暴露的传感器包括 GPS、电池电量、摄像头和麦克风。
跨平台移动应用程序的众所周知的例子包括:
- Instagram、Skype、沃尔玛和 Airbnb (React Native)
- 谷歌广告、我的宝马应用、易贝汽车和纽约时报
- 世界银行、福克斯体育、阿拉斯加航空和 BBC 美食频道(Xamarin)
跨平台工具
要在 Android 上部署您的应用程序,您仍然需要安装 Android SDK 以及 Windows、macOS 或 Linux 机器。iOS 的话,还是需要有 iOS SDK,Xcode,还有专属的一台 macOS 机器。下面是支持前面提到的跨平台框架的 ide 列表:
- React 原生移动应用: VS Code、Android Studio、WebStorm、Xcode、Atom
- Flutter 移动应用: VS Code、Android Studio、IntelliJ
- Xamarin 移动应用: Visual Studio
跨平台移动应用的优势
创建跨平台移动应用程序可以在以下几个方面为您带来好处:
- 成本低:跨平台移动开发只需要一个开发人员团队。他们需要了解所选择的框架。该应用覆盖了更广泛的受众,开始获得牵引力并测试市场。
- 代码可重用性:跨平台框架允许你使用单一代码库。这确保了所使用的业务逻辑的一致性。
- 快速开发:通过重用代码和提高生产率,你可以更快地进入市场。跨平台框架也优化了开发过程中的应用程序测试流程。
- 更容易维护:更新跨平台 app 更容易。这是因为您正在处理一个单一的代码库。所有针对不同平台的构建都使用相同的代码,以确保一致性。
跨平台移动应用的缺点
跨平台移动开发解决了原生应用开发面临的一些缺点。然而,它也有相当多的缺点:
- 更大的数字足迹:跨平台应用通常更大。你需要注意可用的移动优化选项。
- 困难的集成:跨平台框架不能集成所有特定于平台的特性。一些特定于硬件的集成,如使用 GPU,可能需要本地应用程序开发技能。
- 性能降低:跨平台框架通常会捆绑一个定制的运行时来运行你的应用。它负责与特定于平台的服务进行通信。这又增加了一层计算,导致性能下降。
- 延迟的平台特性:新的 SDK 版本通常包括新的特性和更新。对于跨平台框架,您必须等待单独的更新才能访问这些较新的特性。
原生应用与跨平台应用:哪个最好?
开发原生或跨平台应用的决定至关重要。有各种各样的场景让原生应用受益于跨平台应用,反之亦然。仔细考虑您的受众、预算、开发时间表、性能和安全需求等将有助于决策过程。
让我们看看在决定使用哪种开发方法时要考虑的几个方面。
上市时间
这是初创公司和新产品线普遍关心的问题。您希望尽早发布产品,以便从用户那里获得高质量的反馈。跨平台应用程序开发最适合这种情况,因为它可以快速构建和迭代。原生移动开发既耗时又难以维护,最终会导致上市时间变慢。
安全性
你需要考虑企业的声誉以及失去用户信任会带来的影响。对于某些类型的移动应用程序,如移动银行应用程序,风险可能非常高。在这些场景中,原生移动开发将是更好的途径。它提供了许多内置的安全功能,包括文件加密和使用特定操作系统库的智能欺诈检测。虽然跨平台移动开发是让应用更快上市的理想选择,但原生应用提供了卓越的安全性、稳定性和可伸缩性。
表演
移动应用程序可能有很高的计算要求,尤其是游戏。对于需要加速性能的应用,原生移动开发通常是最合适的。在这种情况下使用跨平台框架需要额外的努力和本地应用程序专业知识。针对特定操作系统优化性能使您能够确保应用程序尽可能高效地运行。
开发成本
一些公司开发移动应用的预算比其他公司高。为了降低预算,选择跨平台应用,因为你只需要一个小团队。此外,跨平台开发允许您通过重用代码和项目来控制成本。
结论
要创建一个成功的、稳定的、广受欢迎的移动应用程序,您需要确定您的应用程序要兼容哪个(或哪些)操作系统。虽然原生和跨平台开发方法都可以用于 Android 和 iOS 操作系统,但是在开发移动应用程序时,应该考虑每种方法所伴随的工具、方法、优点和缺点。
无论您的用户使用的是 Android 还是 iOS 操作系统,您都需要确保在创建应用程序时考虑安全性、性能和可伸缩性。开发应用程序没有通用的解决方案。无论是跨平台开发还是原生开发都不会最适合每一个移动应用程序和每一个用例。您应该不断评估每个移动应用程序项目的独特需求,以确定它更适合本机还是跨平台,并考虑应用程序的未来计划,即使您刚刚开始。
要了解如何通过在云中构建、测试和部署您的应用程序来加速您的移动开发,请阅读有关移动应用程序开发持续集成的更多信息并立即开始使用免费 CircleCI 帐户。
数据分析师的四堂课| CircleCI
原文:https://circleci.com/blog/navigating-the-sea-of-data-four-lessons-for-data-analysts/
我大学毕业,获得了运筹学和管理学的学位。虽然用两秒钟以上的时间说出你的专业听起来令人印象深刻,但这让很多人感到困惑,包括我自己。数据科学专业在我大四的时候刚刚开始提供,但通过我的专业,我最终参加了所有必要的课程,以开始我的数据职业生涯。
一开始,我在卡内基梅隆大学实验室兼职做研究数据分析师。这是我第一次在枯燥的学术界之外体验数据。在学校时,教授们尽力让我们接触原始数据,但你总是知道有一个答案,一条前进的道路。这不现实。在进行研究的过程中,我学到的技能成为我职业生涯的基础。像我在学校时那样,仅仅能够提取和清理数据是不够的。我需要数据来讲述一个故事。我每周展示的发现都有影响。这是我第一次尝试帮助构思故事。它帮助我将数据付诸实践,学习什么是噪音,什么是故事。
一年后,我在 CircleCI 找到了第一份全职工作。从一开始,即使是新人,我也被问了很多难题。“我对此没有答案”不是一个有效的答案。让我描述一下 CircleCI 数据的真实情况。每个公司都有一种叫做技术债务的东西,但我想把它作为技术历史呈现给你。当我第一次加入 CircleCI 时,我们正在从 1.0 向 2.0 过渡。与任何重大更新一样,旧的东西不会直接转化为新的东西。为了满足公司的需求,我们必须以度量标准的形式延续一些历史。作为数据的保管者,我有责任成为一名历史学家,并建立桥梁来帮助旅途中的人们。
那是什么感觉?嗯,在早期,产品副总裁问我的第一个问题是,“你的待办事项是什么?”我差点被冒犯了。对我来说,仍然处于学术状态,不能按时完成作业是一种嘲弄。我以为他在质疑我的表现。但对他来说,这是一个正常的和预期的现实。在一家快速成长的初创企业中,问题比需要回答的人还要多。加入数据团队后,团队有效地增长了 25%(从 3 人增加到 4 人)。能够回答这些问题的人肯定很少。这是长期存在的稀缺问题。
我有运筹学和管理学的学位,我看到了它的本质,一个最优化问题。我不得不学会用一些工具费力地处理积压的工作。这些工具不仅帮助我导航优先级,还帮助我导航我试图驯服的数据海洋。
以下是帮助数据分析师保持运转的四个经验:
-
建立信任
让你的过程和决策透明。作为数据分析师,我认为自己是数据的使者之一。我们的命运系在一起。当事情发生时,我成为坏消息的预兆,不管我是否能控制事情。由于数据触及平台的每个部分,了解影响是一项跨部门协作的工作。当你是一个人的时候,弹性是很难的,但是如果你有一群你信任的人,他们信任你,你就能成功。 -
数据不会说谎
畅所欲言。当我需要畅所欲言时,我会这样做,我发现我的声音很重要。因为我不是在谈论观点。我在用数据说话,我对数据告诉我的事情很有信心。这就是我在这里要做的。通过数据,我可以集中声音,整理意见,并引导我们做有价值的和可行的事情。有了数据,它改变了会议的气氛,从人们想做什么,到数据告诉我们应该做什么。它变成了技巧和讲故事。 -
乐于学习
数据的世界是崭新而令人兴奋的。在创业公司工作是新鲜和令人兴奋的。与一个不断改进的产品一起工作是一件新奇而令人兴奋的事情。当你周围的一切都在变化时,你必须积极主动地学习所有的新事物,这有助于每天都这样做。了解你的公司和它的产品会让解释数据的工作不那么令人畏惧。 -
休息一下
咻!我可能喜欢数据,但有时我只是需要离开工作一步,呼吸新鲜空气。为此,我一直在利用我的 PTO 来追寻我指导大学生和旅行的个人激情。我在加州大学伯克利分校(我的母校)和华盛顿大学做志愿者,在 CircleCI 工作期间还去过韩国和日本。无论何时我回来,我的个人动力水平都在充电,所以我已经准备好处理下一个数据问题。
归根结底,数据是关于人的——你如何解释行动和事件,你如何量化行为,以及你如何使用数字分享故事和趋势。记住,作为一名分析师,你扮演着历史学家和故事讲述者的角色,这其中有一定程度的主观性。有了这四条经验,在讨论中确定数据的优先顺序、边走边学、处理大型数据集而不精疲力尽,以及为您的团队和公司提供价值变得更加容易。
通过 CircleCI 了解哪些流程可用于使数据科学、机器学习和 AI 代码更加可靠。
通过对资源选项卡| CircleCI 的新增强功能,预测 IP 范围的成本
我们请求最多、最受欢迎的功能之一是 Docker executor 的 IP 范围,该功能最近对所有执行或规模计划的客户开放。使用 IP 范围,您可以通过与 CircleCI 可验证关联的 IP 地址路由作业流量。这使您的团队能够通过限制与基础架构通信的连接来满足法规遵从性要求。
对于任何新特性,你都想知道它会给你的团队带来多少成本。今天,我们在 CircleCI web 应用程序的工作详细信息页面的资源选项卡中引入了一个简单(但功能强大)的附加功能,以帮助您更好地预测使用新 IP 范围功能的成本。
在用户界面中查看网络传输数据
当作业启用 IP 范围功能时,每传输 GB 数据,该功能会消耗 450 个配额。我们已经将网络传输数据(进出容器的所有流量)添加到资源选项卡(显示在下面的绿框中),以便让您更加了解您的工作正在执行时发生的情况,并帮助 IP 范围用户做出明智的决策。
这些数据使您能够:
- 得出在给定作业上启用 IP 范围的估计成本
- 优化具有已启用 IP 范围的作业的项目的信用消耗
如果您考虑在作业上启用 IP 范围,您可以通过获取网络传输数据(转换为 GB)并乘以 450 配额来估算成本。如果您已经在项目中的几个作业上启用了 IP 范围,此特定于作业的数据将允许您了解项目中每个作业产生的成本,并相应地进行优化。
我们从客户那里听到了大量的反馈,资源选项卡中的资源利用率图表帮助他们无缝地调整计算规模,以利用我们平台上的多种资源规模。我们致力于发展这一势头,并将继续增强“资源”选项卡,让您进一步了解在您的工作执行过程中会发生什么,同时对 IP 范围等新功能进行无摩擦的成本预测。
您对资源选项卡中还包括什么有什么想法吗?通过 Canny 分享它们,让我们知道您在工作执行过程中发现了哪些有价值的资源可见性。
资源选项卡上的数据使用量是近似值。出于计费目的,计划使用页面应始终用作事实的来源。
开发者从最慷慨的免费计划中获得的开箱即用的东西
原文:https://circleci.com/blog/new-cicd-free-plan-what-devs-get/
免费增值计划是公司向开发者介绍他们的产品的好方法,并提供了他们所提供的价值的实际演示。但是,当一个自由层限制了对关键特性的访问,或者没有提供足够的能力来评估产品在现实世界开发场景中的表现时,这对开发人员来说是非常令人沮丧的。这不仅令人沮丧,而且还降低了开发人员的整体体验,导致对产品的负面的、有时是不准确的理解。
许多公司努力在免费层中包含哪些功能以及在何种使用水平下需要付费账户之间取得平衡。CircleCI 也不例外。直到最近,我们的免费计划还不足以提供最好的开发人员体验,并展示 CircleCI 可以为您的持续交付和发布流程带来的价值和力量。这就是为什么我们决定彻底改革我们的服务,提供市场上最全面的免费计划。
在这篇文章中,我将讨论新发布的 CircleCI 免费计划,强调一些最具影响力的变化以及它们将如何改善开发者体验。
免费计划详情
开发人员不断采用创新的策略和概念,比如持续交付和发布管理过程,目标是更快更有效地构建和发布软件。CircleCI 通过自动化软件构建和发布实践,使您的团队能够最大化开发速度。借助我们新推出的免费层产品,您可以获得充分利用构建时间所需的所有特性和功能。以下是新的免费计划给您带来的好处:
- 无限用户
- 每月 30,000 积分,足以支持多达 6,000 分钟的构建时间,具体取决于您的计算类型
- 访问多种执行环境,包括 Docker、Linux、Arm 和 Windows,以及更大的资源类
- 30 个并发作业在任何可用的计算选项上运行
- 5 个自托管运行程序在您自己的机器上运行作业
- 1 GB 的网络数据传输到自托管的运行者,2 GB 的数据存储用于保存缓存和工作区,上传测试结果,以及构建工件
- Docker 层缓存加速您的 Docker 构建
- 在我们的 Insights 仪表盘上检测多达 5 项测试
- 能够创建私有 orb,以便与团队的其他成员共享配置代码
既然我已经简要描述了新的免费计划中包含的令人敬畏的产品和特性,那么让我们仔细看看对开发者和他们的团队最重要的一些特性。
更大的资源类别
发布周期变得越来越短,这样团队就可以尽快向用户发布变更和新特性。对于开发人员来说,要满足他们的发布周期需求,关键是他们要对要发布的变更执行高效的 CI/CD 管道。有许多方法可以优化您的 CI/CD 管道,最简单和最有效的方法之一是通过一个称为资源类的特性来控制管道的底层计算节点容量。
资源类功能使团队能够配置和管理计算节点资源(如 CPU 和 RAM)的容量,确保管道有适当的马力来成功和方便地完成管道作业。通常,管道作业资源类配置的资源容量不足,导致运行速度大大降低。这些较慢的运行逐渐延长了管道完井的持续时间,这可能导致释放过程的延迟。新的免费计划提供了对更广泛的资源类的访问,使团队能够拨入正确的资源来优化他们的管道作业性能。
30 倍并发
除了为管道作业提供适当的计算能力,团队可以用来加速管道的另一个功能是并发性,即跨多个执行环境同时运行多个作业的能力。在新的免费计划下,您现在可以同时运行多达 30 个作业,这可以通过允许您在给定的执行环境中避免由资源限制引起的排队来节省大量时间。
管道内并发的一个很好的用例是并行测试执行。项目中的测试越多,在单台机器上完成测试所需的时间就越长。为了减少这个时间,您可以通过在您的作业配置中指定一个parallelism
级别来并行运行测试。然后,您的测试将在多个独立的执行器上同时运行,从而允许您缩短验证和向您的用户发布更改所需的时间。有关并行性和并发性的更多信息,请查看文档。
Docker 层缓存
Docker 是 CircleCI 平台中可用的容器化技术。Docker 映像允许团队构建容器环境来运行作业,许多团队构建自己的 Docker 映像来定制测试和部署应用程序的环境。Docker 映像是从 docker 文件构建的,docker 文件中的每个命令都会在映像中产生一个层。构建 Docker 映像可能是 CI/CD 工作流程中最耗时的任务之一。
CircleCI Free 计划现在提供 Docker 层缓存,您可以通过在每次作业运行时保存 Docker 映像的各个层来减少重复构建 Docker 所花费的时间。下次运行工作流时,CircleCI 将从缓存中检索任何未更改的图层,而不是从头开始重建整个图像。这使得团队能够高效地打包他们的应用程序并构建相关的 Docker 映像,而不会降低管道执行速度。有关 Docker 层缓存的更多信息,请查看文档。
私人球体
orb 是 YAML 的可重用包,使开发者更容易自动化流程,将第三方工具整合到他们的管道中,以及跨项目共享配置。虽然在我们的公共 orb 注册表中有许多有用的 orb,但在医疗保健、金融或公共部门等高监管行业工作的团队通常需要更高级别的安全性和合规性。
有了私有 orb,您的团队将获得 orb 的所有协作和效率优势,以及通过限制组织内经过身份验证的用户的访问而增加的隐私和安全性。您的团队可以使用 CLI 工具创建和发布新的私有 orb,经过身份验证的用户可以通过访问 CircleCI web 应用程序的组织设置选项卡中的 orb 页面来查看和管理您组织的私有 orb。
通过不稳定的测试检测获得测试洞察力
自动测试代码变更是持续集成的基础,也是最小化软件发布风险的重要一步。但现实是大多数测试并不完美。它们并不总是像预期的那样执行,有时还会不稳定,这意味着它们会不确定地失败。使用 CircleCI Insights 仪表板,您可以监控多个工作流和开发分支的测试性能,以自动识别速度慢、不可靠或最常失败的测试。
Insights 为开发人员提供了对测试执行和性能数据的有价值的可见性。提高对测试套件的健康和性能的认识可以通过消除花费在追踪未识别的 bug 上的时间和增加您对代码质量的信心来节省您的团队的时间和金钱。此外,借助 Insights,您还可以监控其他关键指标,包括信用额度使用情况、成功率和管道持续时间,这样您就可以一目了然地全面了解您的工作流程绩效。要了解更多信息,请访问洞察文档。
结论
在这篇文章中,我讨论了新发布的 CircleCI 免费计划,并强调了一些将为开发者体验提供最大改进的变化。通过访问 CircleCI 强大的持续集成和交付平台上的完整功能集,您的团队可以实施快速、灵活和高效的 CI/CD 管道,从而大大缩短从提交到部署的时间。
对于希望响应用户需求并在竞争中保持领先的软件生产商来说,通过持续交付来自动化开发过程不再是可选的。要了解更多关于 CircleCI 的定价以及它与市场上其他计划的比较,请访问我们的定价页面。如果你的团队还没有从一个强大的 CI/CD 解决方案所带来的时间节省和信心提升中获益,那么注册一个免费 CircleCI 账户,今天就开始吧。
用于安全机密管理的新 CircleCI 功能| CircleCI
原文:https://circleci.com/blog/new-features-secure-secrets-management/
当安全事故发生时,软件提供商和用户采取迅速有效的行动至关重要。为了应对我们最近的安全事件,我们亲眼目睹了我们的客户、技术合作伙伴和工程团队如何通过开放和协作来帮助遏制威胁并降低未经授权访问客户系统的风险。
在本帖中,我们想重点介绍一些我们为应对 2023 年 1 月 4 日的安全事件而推出的新功能和工具。其中包括:
- 一个交互式 CLI 工具,用于识别 CircleCI 中为您的组织项目存储的所有机密
- 对 CircleCI UI 和 API 进行了更改,以简化机密审计
- 全面访问自助审核日志,以查看 30 天内 CircleCI 环境的变化。
总的来说,这些更新使客户更容易通过我们的 UI 或 API 轮换机密,并验证所有受影响的管道和凭据都已得到保护。在许多情况下,这些变化是由我们的社区成员的问题、请求和积极响应激发的,因为他们致力于重置凭据并监控其项目和相关服务的活动。我们感谢您对此事件的贡献和持续支持。
使用 CircleCI-Env-Inspector 识别秘密
CircleCI-Env-Inspector 是一个命令行界面(CLI)工具,您可以使用它来生成存储在 CircleCI 上的所有机密的 JSON 报告。此工具对于在项目环境变量和组织范围的上下文中拥有大量机密的组织特别有用,因为它提供了需要手动轮换的项目的完整列表。请注意,该工具将只返回姓名和其他识别信息,但不会返回秘密值,这意味着没有意外暴露敏感信息的风险。
Environment inspector
要使用 Env-Inspector 工具,请访问并克隆项目存储库,然后生成一个 CircleCI API 令牌,它可以访问您想要检查的所有项目。要列出整个组织的所有项目中使用的秘密,您需要从一个组织管理员帐户生成 API 令牌。
一旦有了 API 令牌,导航到 Env-Inspector 存储库的根目录并运行命令run.sh
。在提示符下输入您的 API 令牌,工具将返回存储在项目环境变量和上下文中的所有秘密的列表。下面是一个输出示例:
{
"contexts": [
{
"name": "CONTEXT_NAME",
"url": "https://app.circleci.com/settings/organization/<VCS>/<ORG>/contexts/<CONTEXT-ID>",
"id": "xxx",
"variables": [
{
"variable": "GITHUB_TOKEN",
"context_id": "xxx",
"created_at": "yyy"
}
]
}
],
"projects": [
{
"name": "ORG/REPO",
"url": "https://app.circleci.com/settings/project/<VCS>/<ORG>/<REPO>/environment-variables",
"variables": [{ "name": "VAR", "value": "xxxx" }],
"project_keys": [
{
"type": "deploy-key",
"preferred": true,
"created_at": "xxx",
"public_key": "yyy",
"fingerprint": "zzz"
}
]
}
]
}
输出包括几条信息,您可以使用这些信息来识别存储在项目中的机密,包括上下文或项目的名称、URL、ID 号以及凭据创建和上次更新的日期和时间。
CircleCI Env-Inspector 是一个开源工具,我们欢迎社区以问题或项目存储库请求的形式做出贡献。
确认机密已经使用updated_at
时间戳进行了轮换
为了让客户放心,他们的长期秘密已经被更改,我们在 web 应用程序的上下文 API、环境变量和上下文管理页面中引入了updated_at
时间戳。此信息允许您验证机密是否已成功轮换,并识别任何需要更新的机密。
在 API 中,updated_at
数据在对列出上下文环境变量的 GET
请求和添加或更新现有上下文变量的 PUT
请求的响应中提供。以下是对上下文 API 的更新请求的示例响应:
{
"variable" : "a_variable",
"updated_at" : "2023-01-09T20:32:18.568Z",
"context_id" : "b5a5561a-7159-48f1-854f-1e53a8f315e0",
"created_at" : "2023-01-09T20:26:57.020Z"
}
响应包括变量的名称、更新的日期和时间、存储变量的上下文的 ID 号以及变量更新的原始日期和时间。
对于喜欢通过 web 应用程序更新秘密的用户,我们还更新了我们的上下文 UI,以提供关于变量何时创建和上次更新的信息。
要查看贵组织的上下文环境变量,请访问组织设置>上下文。
将 SSH 检出密钥与 SHA256 指纹进行比较
作为我们对客户的补救指导的一部分,我们建议在 CircleCI 和您的目标环境中轮换所有 SSH 密钥。为了让您更容易地跨系统比较签出密钥,我们添加了从我们的 v1.1 API 返回 SHA-256 公钥指纹的能力。这是对返回 MD5 指纹的现有能力的补充。
下面是检索阿沙-256 标识符的 API 调用示例:
curl -H “Circle-Token: " https://circleci.com/api/v1.1/project/:vcs-type/:username/:project/checkout-key?digest=sha256
?digest=sha256
查询参数指定响应将返回结帐键的 SHA-256 散列。
有关旋转 SSH 密钥的更多信息,请访问我们的支持文章。
使用自助审计日志审查 CircleCI 环境中的活动
为了帮助客户审计和分析其组织下的活动,CircleCI 在审计日志中记录重要的系统事件。客户可以通过两种方式访问这些日志:
- 通过请求客户支持
- 直接从 web 应用程序中的自助服务门户
传统上,自助审核日志仅向我们的规模、性能和服务器计划的客户提供。为了帮助所有客户保护他们的系统,我们暂时扩展了自助服务功能,将客户纳入我们的免费计划。
用户可以通过访问组织设置>安全并选择一个日期范围来请求在之前 365 天中任何 30 天期间的系统活动日志。免费计划的客户每天可以请求一个审计日志,而付费计划的客户每天最多可以请求三个日志。
CircleCI 审计日志记录重要的系统活动,比如创建或删除上下文、项目环境变量、SSH 密钥或 API 令牌;工作流作业开始、完成或批准的时间;和对预定流水线的改变。这些日志包括以下数据:采取操作的时间、启动操作的用户、受操作影响的实体(组织、项目、帐户或版本)以及操作是否成功。
请注意,虽然查看您的 CircleCI 审计日志是确保 CI/CD 环境中没有发生未授权活动的重要步骤,但我们强烈建议您也查看 2022 年 12 月 21 日至 2023 年 1 月 4 日期间您的构建流程中使用的所有连接服务的审计日志。
结论
这篇文章中强调的新功能和更新是由我们的工程团队制作的,以立即帮助客户应对 1 月 4 日的安全事件。但是,定期轮换静态凭据是一种安全最佳实践,我们鼓励您继续将这些功能作为日常安全工作的一部分。保护管道安全的其他步骤包括:
我们感谢我们的客户在保持管道安全方面的持续合作和警惕,我们的团队将随时为您提供支持。如果您对事件、我们的持续响应或我们对客户的建议有任何疑问,请访问我们完整的事件报告或联系客户支持。
如何在 CircleCI 上导入项目环境变量
原文:https://circleci.com/blog/new-on-circleci-import-project-environment-variables/
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
共享项目环境变量的能力是我们最需要的功能之一,现在所有 CircleCI 用户都可以使用。
以前,每个新项目都需要手动添加环境变量,如 API 密钥。现在,组织管理员用户可以从组织内的其他项目一次性导入环境变量。这应该会减少建立新项目时的痛苦。
以下是如何从构建仪表板中查找和导入项目环境变量:
</blog/media/2016-07-09-env-variables-screencast.mp4>
你可以在这里阅读更多关于环境变量和如何在 CircleCI 1.0 上使用它们,在这里阅读 CircleCI 2.0 上的。
如今,导入指的是简单的一次性复制粘贴功能。我们正在努力进行迭代,以尊重与用户和组相关的更多权限,允许环境变量的作业级更改,并保持组织级同步。如果您对此类功能感兴趣,您的反馈将是无价的- 请访问讨论!
新视频系列:与罗布·祖贝尔·切尔莱西的对话
原文:https://circleci.com/blog/new-video-series-drawn-out-conversations-with-rob-zuber/
如果你听过 CircleCI 首席技术官 Rob Zuber 的演讲,你就会知道他有几条最喜欢的规则:永远不要做任何现成的东西,不要把精力投入到你的核心业务能力之外的事情上。
事实证明,物联网公司 Particle 的联合创始人兼首席技术官扎卡里·克罗克特(Zachary Crockett)打破了罗布的两条规则,并在此过程中建立和扩大了一家成功的公司。
这就是为什么我们邀请扎卡里作为 CircleCI 视频系列的嘉宾,引出对话。在这个系列中,Rob 邀请来自整个技术世界的工程领导者到 CircleCI 总部,打开一包新的 Sharpies,让他们尽情地绘制图表、架构模型,并解释他们如何处理最棘手的技术挑战。
在这一集中,了解 Particle 如何开发自己的移动网络和操作系统来应对分布式 loT 设备的独特挑战。罗布会修改他珍视的规则吗?立即观看并了解:
</blog/media/2019-09-27-drawn-out-particle.mp4>
Next.js 应用程序的持续集成| CircleCI
Next.js 被标榜为生产的 React 框架。它使 React.js 开发人员能够通过提供开箱即用的功能,如路由、代码分割、捆绑、类型脚本和内置 CSS 支持,来开发生产级应用程序。这些是生产应用程序开发所必需的功能。在本教程中,我将向您展示如何通过建立一个测试框架来自动测试添加到我们的应用程序中的特性,并确保它不会在这个过程中中断,从而不断地将特性集成到您的 Next.js 应用程序中。
先决条件
要遵循本教程,需要做一些事情:
安装并设置好所有这些之后,让我们开始本教程。
创建新的 Next.js 项目
首先,通过运行以下命令创建一个新的 Next.js 项目:
npx create-next-app next-testing
注意 : 如果你使用的是 Node.js 版本 13,你需要你的版本是> =13.7 才能成功运行这个命令。
这将在一个next-testing
文件夹中自动创建一个 Next.js 应用程序(你可以给这个文件夹取任何你选择的名字)。一旦搭建过程完成,进入项目的根目录并运行应用程序:
cd next-testing
npm run dev
这将在http://localhost:3000
启动一个服务于应用程序的开发服务器。在您的浏览器中加载此 URL。
安装和设置 Jest 进行测试
下一步是建立测试框架和运行测试所需的工具。我们将使用 Jest 作为我们的测试框架,并安装一些实用程序来确保我们的测试顺利运行。下面是我们将要安装的软件包列表:
jest
:测试框架@testing-library/jest-dom
:通过提供自定义的matchers
来测试 DOM 的状态,从而扩展了jest
- React 测试库提供了简单完整的 React DOM 测试工具
@testing-library/dom
:测试 DOM 节点的基础测试库babel-jest
:用于在我们的测试套件中传输 Javascript
使用以下命令立即安装这些软件包:
npm install -D jest @testing-library/react @testing-library/jest-dom @testing-library/dom babel-jest
一旦安装了这些,下一步就是创建一个.babelrc
配置文件来指示babel-jest
使用 Next.js 的定制预置。在你的项目的根目录下创建.babelrc
文件并输入以下配置:
{
"presets": ["next/babel"]
}
接下来,我们需要配置 jest 来完成以下任务:
- 忽略
.next
构建文件夹和node_modules
文件夹 - 在我们的测试中使用
babel-jest
来传输 Javascript - 在我们的测试中模拟静态文件(CSS 导入和文件)
在package.json
文件中,为jest
添加以下部分:
...
"jest": {
"testPathIgnorePatterns": [
"<rootDir>/.next/",
"<rootDir>/node_modules/"
],
"transform": {
"^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest"
},
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js"
}
}
...
仍然在package.json
文件中,将test
脚本添加到scripts
部分:
...
"scripts": {
...
"test": "jest"
},
...
为 Jest 测试模仿静态资产
在上一节中,我们添加了一个jest
配置来指示jest
在我们的测试中模拟 CSS 文件导入和其他静态文件。这样做是因为这些类型的文件在测试中没有用,所以我们可以安全地模拟它们。在编写设置时,你会注意到我们指向了两个模拟文件fileMock.js
和styleMock.js
,它们分别用于模拟静态文件和 CSS 文件。
让我们创建这些文件。在项目的根目录下创建一个__mocks__
文件夹。然后创建fileMock.js
文件并输入以下代码:
module.exports = "placeholder-file";
在这个__mocks__
文件夹中创建styleMock.js
文件,并输入以下代码:
module.exports = {};
有了这些文件,您就能够安全地模拟静态资产和 CSS 导入。
呈现 React.js 组件
现在我们可以开始编写测试了。该项目包含运行它们所需的一切。在项目的根目录下创建一个__tests__
文件夹(这是一个特殊的文件夹,jest
在其中寻找测试文件),并添加一个名为HomeTest.js
的测试文件。在文件中输入以下代码:
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import Home from "../pages/index";
test("Check for Getting Started Text", () => {
const { getByText } = render(<Home />);
expect(getByText("Get started by editing")).toBeInTheDocument();
});
在上面的代码中,React 测试库中的render
对象被导入来呈现我们的 React.js 组件。screen
对象也被导入,这使我们可以访问页面文档。对于我们的断言,extend-expect
模块是从@testing-library/jest-dom
导入的。最后,我们将要测试的组件,我们在/pages/index.js
中找到的索引页面组件,被导入。
然后包含一个测试来呈现在/pages/index.js
文件中导出的主页组件(<Home />
),并检查页面上显示的Get started by editing
文本是否确实存在于组件中。
通过运行以下命令来运行此测试:
npm run test
继续添加另一个测试,将下面的代码添加到__tests__/HomeTest.js
:
...
it("Renders appropriately", () => {
render(<Home />);
expect(
screen.getByRole("heading", { name: "Welcome to Next.js!" })
).toBeInTheDocument();
});
上面的测试使用screen
对象来访问 React.js DOM,并断言一个包含文本Welcome to Next.js!
的heading
。这是我们主页上的第一项。
再次运行测试套件
npm run test
持续集成的自动化测试
现在您已经运行了您的测试,一切都很好。但是我们在这里不仅仅是为了“好”让我们使用持续集成来自动运行我们的测试。有了自动化,我们的测试将在每次我们向我们的存储库推送更新时运行。
从将你的项目推送到 GitHub 开始。
接下来,转到 CircleCI 仪表板上的添加项目页面。
点击设置项目。
在设置页面上,点击使用现有配置。接下来,您将得到一个提示,要么为 CI 管道下载一个配置文件,要么开始构建。
点击开始构建开始构建。这个构建将会失败,因为我们还没有设置配置文件。这是我们的下一步。
在项目的根目录下创建一个名为.circleci
的文件夹,并添加一个名为config.yml
的配置文件。在该文件中,输入以下代码:
version: 2.1
jobs:
build:
working_directory: ~/repo
docker:
- image: circleci/node:10.16.3
steps:
- checkout
- run:
name: Update NPM
command: "sudo npm install -g npm@5"
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: Install Dependencies
command: npm install
- save_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
paths:
- ./node_modules
- run:
name: Run tests
command: npm run test
在上面的管道配置代码中,我们首先导入一个 Docker 映像,其中包含运行 Next.js 应用程序所需的 Node.js 版本。然后,npm
被更新,依赖项被安装,缓存它们以便后续的构建更快。
最后,项目中包含的所有测试都用npm run test
命令运行。
保存该文件,并将所有更改提交到远程存储库。这将触发管道再次运行我们的构建脚本。
要查看测试详情,请点击构建。
太棒了,不是吗?
结论:减轻锅炉板块代码负担
完整的项目可以在 GitHub 上的这里看到。
对于构建生产应用程序来说,Next.js 是一个令人印象深刻的框架,因为它提供了减轻样板代码负担的特性。你不希望在产品中出现的一件事就是代码损坏。在本教程中,您了解了如何测试您的特性,并确保测试在您每次推送新代码时自动运行。这对于确保有缺陷的代码不会被推送到任何部署环境中有很大的帮助。
编码快乐!
Fikayo Adepoju 是 LinkedIn Learning(Lynda.com)的作者、全栈开发人员、技术作者和技术内容创建者,精通 Web 和移动技术以及 DevOps,拥有 10 多年开发可扩展分布式应用程序的经验。他为 CircleCI、Twilio、Auth0 和 New Stack 博客撰写了 40 多篇文章,并且在他的个人媒体页面上,他喜欢与尽可能多的从中受益的开发人员分享他的知识。你也可以在 Udemy 上查看他的视频课程。
Python 中用棉花糖进行对象验证和转换
原文:https://circleci.com/blog/object-validation-and-conversion-with-marshamallow/
Marshmallow 是一个 Python 库,它将复杂数据类型与 Python 数据类型相互转换。它是验证和转换数据的强大工具。在本教程中,我将使用 Marshmallow 来验证一个简单的书签 API,用户可以在其中保存他们喜欢的 URL 以及每个站点的简短描述。
本教程将涵盖:
- 用棉花糖序列化和反序列化对象
- 测试序列化和反序列化的对象
- 用棉花糖验证对象
先决条件
要充分利用本教程,您需要:
- 我们机器上安装的 Python 版本> = 3.5
- GitHub 账户。你可以在这里创建一个。
- CircleCI 账户。你可以在这里创建一个。
- 对 SQLite 数据库有基本的了解。
- 对 Flask 框架的基本理解。
我们的教程是平台无关的,但是使用 CircleCI 作为例子。如果你没有 CircleCI 账号,请在 注册一个免费的 。
克隆存储库并创建虚拟环境
从克隆来自这个 GitHub 链接的库开始。一旦你克隆了库,下一步就是创建一个虚拟环境,并激活它来安装我们的 Python 包。使用此命令:
pip3 install -r requirements.txt
注意: 为了避免全局安装包,使用 Python 3.5 默认捆绑的虚拟环境。默认的虚拟环境允许轻松管理 Python 项目中的依赖关系。
要创建默认虚拟环境,请运行以下命令:
virtualenv api-venv
创建后还需要激活。更多关于 Python 虚拟环境的信息可以在这里找到。
为什么是棉花糖?
通常在处理数据时,需要将数据从一种数据结构转换成另一种数据结构。Marshmallow 是一个 Python 库,它可以将复杂的数据类型转换成本地的 Python 数据类型,反之亦然。
Python 解释器支持一些内置的数据类型,包括整数、布尔、元组、列表、字典、浮点、集合和数组。对于想要创建能够处理不同类型操作的复杂程序的开发人员来说,这些是必不可少的。
Marshmallow 的一个优点是它可以与任何数据库技术一起工作。它是平台无关的,这对开发者来说总是一个胜利。
为了进一步推广棉花糖,我们将使用这些技术:
- Marshmallow-sqlalchemy 是 SQL 对象关系映射器 sqlalchemy 的扩展。
- Flask-marshmallow 是一个针对棉花糖的 Flask 扩展,可以很容易地将棉花糖与 Flask 一起使用。它还为棉花糖对象生成 URL 和超链接。
理解棉花糖模式
理解棉花糖模式是如何工作的对于使用它是必不可少的。模式作为 Marshmallow 的核心,通过声明的模式跟踪数据。模式定义了数据的结构以及数据的验证。
我们书签应用程序的模式示例如下:
class BookMarkSchema(ma.Schema):
title = fields.Str(required=True, allow_none=False)
url = fields.URL(
relative=True, require_tld=True, error="invalid url representation"
)
description = fields.String(required=False, allow_none=True)
created_at = fields.DateTime(required=False, allow_none=True)
updated_at = fields.DateTime(required=False, allow_none=True)
这个模式为我们的模式中的字段创建验证并定义数据类型。模式已经过时,是时候序列化和反序列化数据了。
在烧瓶应用程序中实现棉花糖
为了构建我们的书签 API,我们将首先构建一个BookMarkModel
类。这个类将在我们的表、关系和字段的结构上连接到数据库引擎。我们还将添加一个BookMarkSchema
类来序列化和反序列化模型中的数据。这些类可以在/src/app.py
文件中的克隆存储库中找到。
为了展示 Marshmallow 如何将数据从 Python 类型解析为序列化对象,我们使用了 SQLAlchemy。序列化的对象可以存储在数据库中,以后可以从数据库中反序列化为可接受的 Python 数据类型。
首先为模型和模式定义类创建一个结构。
# Adding SQLAlchemy
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(BASE_DIR, 'db.sqlite3')
db = SQLAlchemy(app)
# Add Marshmallow
ma = Marshmallow(app)
# Create the API model (SQLAlchemy)
class BookMarkModel(db.Model):
pass
# Create schema (marshmallow)
class BookMarkSchema(ma.Schema):
class Meta:
pass
bookMarkSchema = BookMarkSchema()
bookMarksScehma = BookMarkSchema(many = True)
这个代码片段首先将SQLAlchemy
连接到我们的应用程序,默认情况下使用 SQLite。当 URL 被配置时,它连接到那个 SQL 数据库。然后,当数据从我们的模型发送和接收时,snipped 实例化 Marshmallow 到serialize
和deserialize
。
当与单个书签交互时,bookMark = BookMarkSchema()
模式负责反序列化单个数据集(即POST
、READ
和UPDATE
路线)。相反,bookMarks = BookMarkSchema(many =True)
用于反序列化数据集中的项目列表,例如获取所有请求的书签。
在 Marshmallow 中序列化和反序列化数据
在前面的代码片段中,我们基于我们的BookMarkModel
创建了一个棉花糖模式。在本节中,我们将使用 b Marshmallow 在保存到数据库时序列化数据,在从数据库检索时反序列化数据。
序列化 Python 数据
序列化是将 Python 对象转换成可以存储在数据库中或传输的格式的过程。在 Flask 中,我们使用 SQLAlchemy 连接到我们的数据库。我们需要将 SQLAlchemy 对象转换成 JSON 数据,然后可以与我们的 API 进行交互。棉花糖是这个过程中一个很好的工具。在本节中,我们将在创建书签后使用 Marshmallow 返回一个 JSON 对象。我们将通过向 SQLite 数据库添加一个新书签来实现这一点。
# CREATE a bookmark
@app.route("/bookmark/", methods=["POST"])
def create_bookmark():
title = request.json["title"]
description = request.json["description"]
url = request.json["url"]
book_mark = BookMarkModel(
title=title,
description=description,
url=url,
created_at=datetime.datetime.now(),
updated_at=datetime.datetime.now(),
)
result = bookMarkSchema.dump(book_mark)
db.session.add(book_mark)
db.session.commit()
return result, 201
这个代码片段使用BookMarkModel
类创建一个新的书签。它使用db.session.add
和db.session.commit
方法向数据库连续添加和保存书签。为了序列化对象,代码片段使用了BookMarkSchema
类的dump
方法,该方法返回一个格式化的 JSON 对象。
为了验证这一点,我们可以用 Postman 向数据库添加一个书签,并检索它。首先使用以下命令运行 Flask 应用程序:
FLASK_APP=src/app.py flask run
一旦应用程序开始运行,我们现在可以使用 Postman 和POST
route /bookmark
向我们的 API 请求创建一个新的书签。
该请求返回的响应是一个 JSON 对象。成功!现在已经创建了一个书签并用 Marshmallow 序列化了,您可以从数据库中检索它并反序列化它。
将 JSON 数据反序列化回 SQLite
反序列化与序列化相反。为了序列化,我们将数据从 Python 转换为 JSON。为了反序列化,我们将 JSON 数据转换为 SQLAlchemy 对象。当从 SQLite 数据库中反序列化对象时,Marshmallow 会自动将序列化的数据转换为 Python 对象。Marshmallow 为此使用了load()
函数。
book_mark = BookMarkModel(
title=title,
description=description,
url=url,
created_at=datetime.datetime.now(),
updated_at=datetime.datetime.now(),
)
try:
json_input = request.get_json()
result = bookMarkSchema.load(json_input)
except ValidationError as err:
return {"errors": err.messages}, 422
对于反序列化,这个代码片段返回一个 SQLAlchemy 对象,该对象是从我们的 API 的 JSON 响应转换而来的。
既然已经序列化和反序列化了一些数据,下一步就是编写测试。测试将确保端点返回正确的数据。为了完全确保一切正常,我们还将在 CircleCI 上运行这些测试。
测试序列化
测试通过验证您的代码是否按预期运行来激发您对应用程序的信心。在本节中,我们将创建一个测试来确保我们的序列化按预期工作。
# Test if one can add data to the database
def test_add_bookmark():
my_data = {
"title": 'a unique title',
"description": 'a bookmark description',
"url": 'unique bookmark url',
}
res = app.test_client().post(
"/bookmark/",
data=json.dumps(my_data),
content_type="application/json",
)
assert res.status_code == 201
该测试验证了我们可以成功创建一个新书签。它还测试响应是否是我们在创建方法时定义的 201 状态代码。现在,我们可以通过将测试添加到 CircleCI 管道中来进一步验证成功。
设置 Git 并推送到 CircleCI
要设置 CircleCI,通过运行以下命令初始化项目中的 Git 存储库:
git init
然后,在根目录下创建一个.gitignore
文件。在文件中添加任何您不想添加到远程存储库中的模块。下一步是添加一个提交,然后将你的项目推送到 GitHub 。
登录 CircleCI 并进入 Projects,在这里您应该会看到与您的 GitHub 用户名或您的组织相关联的所有 GitHub 存储库。您希望为本教程设置的特定存储库是object-validation-and-conversion-with-marshmallow
。在“项目”面板上,选择设置选定项目的选项,然后将该选项用于现有配置。
注意: 在启动构建之后,预计您的管道会失败。您仍然需要将定制的.circleci/config.yml
配置文件添加到 GitHub 中,以便正确构建项目。
设置 CircleCI
首先,在您的根目录中创建一个.circleci
目录。为每个项目的 CircleCI 配置添加一个config.yml
文件。在这个设置中,我们将使用 CircleCI Python orb
。使用此配置来执行您的测试。
version: 2.1
orbs:
python: circleci/python@1.2
workflows:
sample:
jobs:
- build-and-test
jobs:
build-and-test:
docker:
- image: cimg/python:3.8
steps:
- checkout
- python/install-packages:
pkg-manager: pip
- run:
name: Run tests
command: python -m pytest
使用第三方球体
CircleCI orbs 是可重用 yaml 配置的可重用包,它将多行代码压缩成一行。要允许使用像python@1.2
这样的第三方球体,您可能需要:
- 如果您是管理员,请启用组织设置,或者
- 向您组织的 CircleCI 管理员请求权限。
设置好配置后,将配置推送到 Github。CircleCI 将开始建设这个项目。
瞧啊。转到 CircleCI 仪表板,展开构建详细信息。验证测试运行成功,并集成到 CircleCI 中。
现在您已经设置了 CI 管道,您可以继续使用 Marshmallow 来验证数据。
使用棉花糖的对象验证
Marshmallow 提供了一种在将对象数据发送到数据库之前验证对象数据的简单方法。Marshmallow 模式使用模式中的validate()
方法来创建书签。在这一步中,我们将添加验证以确保书签标题只允许字符串,而不允许其他类型。
class BookMarkSchema(ma.Schema):
title = fields.String(required=True, allow_none=False)
...
当规则被传递给模式时,我们可以使用validate()
方法来验证创建新书签的方法上的数据:
def create_bookmark():
title = request.json["title"]
description = request.json["description"]
url = request.json["url"]
# Validate the data from request before serialization
error = bookMarkSchema.validate({"title": title, "description": description, "url": url})
if error:
return jsonify(error)
在上面的代码片段中,我们使用validate()
方法来检查返回的数据是否与我们描述的模式验证相匹配,如果出现错误,我们将把错误返回给用户。
为了验证这是否有效,向 Postman 发出一个POST
请求,在title
中输入一个整数值。您的 API 应该抛出一个错误。
当与请求一起发送的无效title
导致错误时,您将知道您的验证工作正常。
为更多端点添加测试
本教程没有涵盖用于克隆存储库的所有端点。如果您想自己继续,可以为端点添加测试,比如获取所有书签或获取单个书签。使用此代码:
# Test if all bookmarks are returned
def test_get_all_bookmarks_route():
res = app.test_client().get("/bookmarks/")
assert res.headers["Content-Type"] == "application/json"
assert res.status_code == 200
# Test if a single bookmark is returned
def test_get_one_bookmark_route():
res = app.test_client().get("/bookmark/1/")
assert res.headers["Content-Type"] == "application/json"
assert res.status_code == 200
这些测试验证了我们可以检索我们创建的书签,无论是所有书签还是一个书签。测试还验证了接收到的数据是一个JSON
对象,与 Marshmallow 的序列化过程一致。
在我们称之为聚会之前,我们需要保存并提交我们的测试,并将它们推送到 GitHub。成功的管道运行意味着一切顺利。
结论
在本文中,我们探索了使用 Marshmallow 反序列化和序列化数据以及执行验证的强大功能。通过这篇文章,我们已经经历了创建模型、创建模式以及连接它们的过程。我们还学习了如何使用验证来只允许特定类型的响应。我希望本教程对您有所帮助,并且希望您能够更好地理解如何使用 Marshmallow 进行序列化和反序列化。通过为更多的端点添加测试,让团队的其他成员参与进来,并将您所学到的知识应用到您自己的项目中。
Waweru Mwaura 是一名软件工程师,也是一名专门研究质量工程的终身学习者。他是 Packt 的作者,喜欢阅读工程、金融和技术方面的书籍。你可以在他的网页简介上了解更多关于他的信息。
软件开发中的可观察性与监控
为了监控分布式应用程序的行为并跟踪服务故障和停机时间的来源,开发人员通常使用传统的监控技术和工具。然而,这种方法可能无法衡量现代云原生架构的整体健康状况,这种架构可能跨越多个托管环境,并包含数百个微服务。今天,团队可以利用可观察性和监控来发现他们的应用程序架构中的问题,并确保他们的软件按预期执行。
但是理解应用程序健康和性能的这两种方法之间到底有什么区别呢?本文探讨了可观察性和监控的区别,以及如何将它们添加到您的开发工作流中。它还解释了可观察性和监控如何适应软件开发过程,以及它们如何帮助提高您的工程团队的生产力。
要听 CircleCI 首席技术官 Rob Zuber 与 Lightstep 首席执行官 Ben Sigelman 谈论可观察性如何与自信地交付变革联系起来,请查看自信提交播客的第 10 集。
什么是可观测性?
可观测性的概念起源于工程领域,用于描述通过研究系统的外部输出来理解系统内部状态的能力。最近,开发人员采用了可观察性的概念来描述从一系列组件中捕获和综合数据的能力,以得出关于应用程序状态的有根据的结论。
换句话说,可观察性是一个应用程序的质量,它允许团队监督和推理整个系统的健康状况。在设计分布式系统时,促进可观察性是至关重要的,因为它不仅提供了故障发生在哪里的可操作的见解,还提供了故障发生的原因和方式。
可观察性包括软件组件本身和它们之间传输的数据。它通过提供软件系统如何作为一个整体执行的可见性,而不仅仅是作为单独的服务或功能,来帮助建立对云原生应用的信心。为了实现这种整体视角,它依赖于三种类型的遥测数据:日志(事件记录)、度量(随时间测量的性能数据)和跟踪(关于整个系统中数据流的信息)。
可观察性使得跨网络边界收集、存储和分析大量信息成为可能,为开发人员提供了一个环境中正在发生的事情的完整画面——即使涉及多种技术。
什么是监控?
监控是可观察性的一个子集,包括跟踪重要事件和指标,以便异常、错误和停机时间可以立即被注意到。度量监控包括识别成功的特定标准,并测量与这些目标相关的应用程序的性能。典型的例子是队列深度,但是指标可能包括内存使用、每秒请求数、活动连接、流量或错误。指标有利于报告系统的总体运行状况、触发警报,并提供对应用程序性能的深入了解。
通过监控运行系统的事件和指标,开发人员可以检测到这些系统何时开始偏离正常行为。监控使开发人员能够在问题还很小的时候就迅速发现问题。它还帮助团队理解他们的系统是如何工作的,以及他们的局限性是什么。最后,监控让每个依赖服务或工具的人相信他们的服务水平协议得到了满足。
能够访问监控数据是非常强大的。例如,如果某个微服务的行为不符合预期,了解其底层指标有助于快速诊断问题。然而,为了正确地解决问题的根本原因,这可能源于受影响的服务之外,团队通常需要求助于可观察性工具。
可观察性和监控在软件开发中的作用
可观察性和监控在维护基于云的应用程序的弹性方面都起着重要的作用。监控鼓励团队思考哪些度量标准或性能指标对他们的用户是重要的,并使系统就位以确保应用程序满足这些目标。最简单地说,这可能涉及到将有关系统状态的数据输出到存储介质,如标准输出或数据库。在最复杂的情况下,监控可以整合专门用于确保系统保持正常运行或优化延迟或吞吐量等指标的整个框架。
虽然监控有助于开发人员描述系统应该如何运行,并识别何时偏离预期,但可观测性允许他们检测无法通过已建立的度量标准来度量的风险,并且在没有附加上下文的情况下无法理解这些风险——通常称为未知的未知。
可观察性包含有针对性的性能测量,监控提供了更丰富的遥测数据,可以揭示更大的模式和事件之间的关系。这有助于团队快速有效地找到性能下降的根本原因,从而加快分布式系统和多云环境的故障排除、调试和调优。
如何将可观察性和监控添加到开发工作流中
在运行基于容器的微服务部署或负载平衡器时,捕获遥测数据对于企业获得所需的可见性至关重要。开发人员可以使用来自日志、指标和跟踪的几个数据点来增加对生产环境的可观察性和监控。
许多云提供商提供内置的应用和基础设施监控工具。例如,亚马逊 CloudWatch 、谷歌云监控、 Azure Monitor 为 AWS、GCP、Azure 上托管的应用和服务提供应用和基础设施监控。
还有许多第三方工具可以帮助您在应用程序中增加可观察性。集中式日志管理解决方案,如 Graylog 和 ELK stack (Elasticsearch、Logstash 和 Kibana)可以帮助团队组织日志文件,并可视化跨多云部署的重要趋势。像 Prometheus 、 Nagios 和 Zabbix 这样的度量捕获工具可以聚合和分析随时间收集的应用程序健康数据。包括 Jaeger和 Zipkin 在内的分布式跟踪工具允许团队跨越服务边界跟踪用户请求。
一些平台,包括 Datadog 、蜂巢、相扑逻辑、 Lightstep 、 New Relic 和 Splunk 结合了度量、日志和痕迹,提供一体化的可观测性解决方案。
选择适合您组织需求的可观察性和监控平台至关重要。监控软件系统因公司而异。
如果您的公司、系统和资源允许,实现完全的可观察性是最好的。但是监控是至关重要的——尤其是当某个指标第一次失去控制时。这种情况对于投资新技术的公司来说非常具有挑战性,比如容器、微服务和 Kubernetes。
结论
监控不能代替可观察性。它是可观测性工具包中的一个重要工具。当您的公司完全了解监控的工作原理、如何使用收集的数据制作警报,以及何时针对流程行为和应用程序行为发出警报时,您的开发团队就能很好地交付新功能、观察应用程序的运行状况和性能,并快速自信地修复中断。
为了将这种自信扩展到开发过程的所有阶段,可以考虑采用持续集成和持续交付(CI/CD)解决方案。自动化您的构建、测试、部署和发布流程,并在错误和漏洞进入生产之前快速诊断和解决它们。借助 CircleCI,您可以集成第三方监控工具以在开发流程的最早期阶段增加可观察性,并获得对团队绩效的可操作性洞察。首先,今天就注册一个免费的 CircleCI 账户。
在不稳定的云提供商上实现稳定的计算| CircleCI
原文:https://circleci.com/blog/offering-stable-compute-despite-unstable-cloud-providers/
对于 CircleCI 来说,云提供商停机是一个常见且不可避免的问题。对于我们的客户来说,启动失败的最常见原因之一是可用性区域降级,这可能是由多种原因造成的,包括网络错误、操作失败以及区域达到其容量限制。
对于使用我们的机器执行器或远程 Docker 功能的客户,我们使用我们的云提供商为构建中的每个执行器旋转一个全新的虚拟机(VM ),然后在构建完成后将其丢弃。由于这些虚拟机需要几分钟的时间来启动,因此我们维护了一个预启动虚拟机池,以便构建可以在很少甚至没有等待时间的情况下开始。当我们的云提供商全力以赴时,这很方便,但当他们遇到故障,从而影响客户的等待时间时,这就不那么方便了。
尽管我们尽最大努力将区域故障对客户和工程师的影响降至最低,但再多的指标、警报、日志记录和文档也无法完全解决问题。虽然云提供商通常不会认为某个区域宕机违反了他们各自的 SLA,但我们在向客户提供计算时会寻求超越这一责任级别。因此,为了提高我们在面对这些不可避免的问题时的容错能力,我们决定提出一个内部解决方案。
挑战
现在是上午 11 点 23 分,我已经进入状态,正在追踪一个已经困扰我们两个多小时的 bug。突然,我的电话响了。这是传呼机的责任。"任务等待时间超过资源类型:中等图像:ubuntu-1604:201903-01 "。我叹了口气,点击“确认”,并进一步阅读提醒信息。
我打开仪表板,将症状与操作手册进行比对。然后,我评估客户影响——这次停机看起来很严重。我前往#incident Slack 频道向团队汇报最新情况。我努力缓解问题并监控恢复情况。最终,等待时间缩短,事件得以解决。太好了,现在我在做什么?
不幸的是,当你依赖云提供商时,这种类型的中断是不可避免的。这种中断不仅会增加客户的等待时间并占用工程资源进行修复,还会导致我们落后于对虚拟机的需求,客户的构建将在队列中等待更长时间。
在我们解决这个问题的早期尝试中,我们为客户等待时间建立了一个阈值。一旦等待时间超过阈值,就会触发数据狗警报,并呼叫工程师。从那时起,诊断和修复问题的手动过程是有效的,但效率低下。工程师需要查阅操作手册,找出哪个区域降级了,配置更改,然后部署它们。移除不健康区域后,他们还需要手动启动虚拟机,以跟上构建的进度。最后,一旦团队陷入了积压,工程师还必须记住在区域恢复后重新启用它。
尽管我们的系统能够容忍一些故障(比如机器无法启动或者 API 不合作),但我们收到的大多数警报都需要工程师来诊断问题。尽管我们努力提前触发警报和寻呼,按数据中心和区域隔离故障,甚至跟踪提供商机器的生命周期以帮助我们提前预测停机,但一旦我们知道有问题,我们仍然必须手动将流量从降级区域重新路由。这让客户排队等候,工程师们争先恐后地抢在问题前面。
解决方案
我们没有对区域故障和缓慢的构建时间做出反应,而是决定寻找一种方法来主动减轻对客户的影响,即使我们无法控制云提供商区域的健康状况。我们需要一种解决方案,能够自动检测某个区域何时不正常,停止该特定区域中的虚拟机运行,并在该区域恢复正常时自动将其添加回我们的循环中。
我们选择了断路器设计模式作为这个问题的最佳解决方案。在这个模式中,断路器用于控制对外部资源的访问——在本例中是提供者可用性区域。当与提供者区域交互的成功率低于某个阈值时,断路器“打开”,对资源的访问被切断,将流量重新路由到另一个区域。经过一段时间后,允许少量的交互来确定成功率是否已经恢复正常。如果是这样,断路器“闭合”,允许再次访问资源。否则,断路器返回到“打开”状态,并重复测试,直到该区域恢复正常。
通常,断路器在内存中实现。但是,内存模式对我们来说并不可行。在我们的系统中,在进行 API 调用以启动 provider-zone 中的 VM 之后,我们将一条消息放在一个队列中,以便另一个工作器(在不同的进程中运行)可以等待 VM 启动,SSH 进入,并准备运行客户构建。这意味着需要知道哪些区域运行正常的过程不同于需要知道虚拟机是否在这些区域成功启动的过程,这使得内存中的断路器不适合我们的问题。
我们意识到,我们需要一个集中式断路器实现。我们建立了断路器服务,以跟踪区域成功率并管理每个断路器的状态。这使得在我们的虚拟机配置服务集群中的所有节点上汇总提供商区域成功率成为可能。通过将我们的断路器集中到服务中,我们还能够在一个地方手动设置断路器状态,如果需要的话。
结论
自动化断路器系统显著缩短了客户的构建时间,为我们的工程师腾出了时间来处理其他重要项目,并通过减少设置预启动虚拟机以应对区域中断的需求,最终节省了我们的资源。让工程团队松了一口气的是,新系统消除了因该问题而产生的页面需求,并将每次事故对区域中断的总体响应时间缩短了约 20 分钟。
自从实施以来,断路器一直工作得非常顺利,以至于当一个区域在我们的 Windows 发布的那个星期降级时,我们甚至没有注意到。断路器会根据需要自动重新路由流量,从而减少客户可能会经历的任何额外等待时间,并腾出团队的时间专注于产品发布。断路器不仅为我们的客户提高了性能,而且它还起到了故障保护的作用,让团队更加确信等待时间不会因为降级区域中缺少虚拟机而增加。
我们对构建映像的更新策略- CircleCI
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
以下是我们将在 CircleCI 2.0 和 CircleCI 1.0 上更新构建映像的时间和原因。
CircleCI 图像
Docker 便利图片
文档页面-48 小时内更新 - CircleCI 2.0 支持使用 Docker 映像作为 CI 构建的环境。有些用户不想制作自定义映像,或者他们使用的映像缺少在 CI 环境中有用的工具。这就是便利图片的用武之地。这些图片基于官方的 Docker 库图片。由于我们获取这些图像并添加常用工具(如git
和tar
)以及浏览器工具(chrome、selenium 等),更新频率基于上游图像。我们通常会在 Docker 库版本发布 24-48 小时后进行图像更新。
Ubuntu 14.04
文档页面 -每季度更新 - CircleCI 1.0 使用超大 LXC 图片。这是因为我们在一个图像中有许多语言和所有相关的工具。如果我们删除用户正在使用的软件版本,更新此映像有时会导致用户的构建中断。这就是为什么我们建议切换到 CircleCI 2.0,在那里这种情况不会再发生。为了最大限度地减少损坏,CircleCI 1.0 Ubuntu 14.04 映像大约每季度(每 3 个月)更新一次。不会添加映像中不存在的软件。只会添加现有软件的新的主要版本。
Ubuntu 12.04
“文档”页面——从不更新——这张图片是 CircleCI 1.0 的大型 LXC 图片,类似于 Ubuntu 14.04 的图片。生产 Ubuntu 的 Canonical 公司已经停止生产 Ubuntu 12.04。这意味着将不会有更多的错误修复或安全更新。反过来,我们也停止了对该图像的支持。我们建议改用 CircleCI 2.0。如果你因为任何原因不得不继续使用 CircleCI 1.0,你可以切换到 Ubuntu 14.04 镜像来继续更新。Canonical 的停产公告可以在这里找到。
反馈和贡献
CircleCI 2.0 Docker 便利图片随着 2.0 的发布而开花结果。他们仍然是相当新的和开源的。如果你想浏览 docker 文件的源代码,报告错误,或者修复问题,你可以在 GitHub 项目上这样做。
如果您不熟悉 CircleCI 2.0、Docker 或我们的图像,您可以在circle ci discuse上提问、查看示例并讨论工作流程。
最后,你也可以使用 CircleCI 讨论来分享你的公共 Docker 图片或 CircleCI 配置片段,你认为这些可以帮助其他人。我们很想看看你在做什么。
新站点可靠性工程师入职- CircleCI
原文:https://circleci.com/blog/onboarding-new-site-reliability-engineers/
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
我最近参加了#SRECon 的一个小组,在那里我分享了我对如何让现场可靠性工程师(sre)参与进来的想法。对于那些错过它的人,我将分享我为那些正在考虑加入 SRE 团队或正在积极加入/管理 sre 的人所讨论的内容。
背景
我毕业于一个名为“数字未来”的项目,在那里我学习了游戏设计、编程装置和可穿戴技术。在设计行业工作了一段时间后,我决定改做编程。当我开始在 CircleCI 工作时,我是一名初级 DevOps 工程师,负责扩展和日常服务器维护。
这份工作不需要深入的技术知识或编程技能,但它需要快速学习和适应不断变化的环境。事实上,有必要列出一个潜在 SRE 的非技术性品质:
在潜在的 SRE 身上寻找什么
- 好奇:戳东西,拆开。研究主题并提出问题。
- 果断:不顾一切困难完成任务。
- 自学:看书/文档,用工具实验。
- 适应性强:对变化持积极态度。这份工作经常变化。
- 自动化:创建系统(不一定是技术性的)来解决重复性问题,提高效率。
- 冷静:在压力或紧急情况下工作良好。
所有这些品质使 SRE 在他们的环境中茁壮成长,并迅速适应环境。
像 SRE 一样快速前进
给你的
阅读文档并修改它们。通过编辑它们并发现错误,你将填补自己知识的空白。有时候这些文件就不存在了。在这种情况下,让他们。如果不是什么都懂也不用担心;这需要时间。
参加会议,倾听人们如何谈论系统和概念。把行话记下来,这样你就不会被术语绊倒。记笔记来巩固你的学习。
四处打探,问问题。不要害怕问脚本是做什么的或者为什么要运行它。不要在不好的时候问问题,比如在事故发生时,或者当所有人都回家了或者放假了。
给你的经理
将程序与你的新 SRE 配对,鼓励他们跟随有经验的工程师。一个新工程师在真实的项目中与真人一起学习要比阅读文件快得多。
确保您的团队保持文档最新。你不可能总是抽出时间给工程师们进行配对,所以从别处获得背景和细节是很重要的。
给可管理的项目设定明确的目标。对于不了解代码库的新工程师来说,开放式项目可能会令人生畏。结构是你的朋友。
逐步介绍 SREs 随叫随到。让他们在灭火的时候跟着其他人。接下来,让他们自己做,其他 sre 待命。最后,让他们承担全部责任,必要时给予指导。
为了你的队友
不要给新 SRE 人答案。问一些引导性的问题,让他们自己得出结论。这些记忆和教训持续的时间更长。
告诉他们去哪里找。如果有不明显的有用文档,指出来。向他们展示在你的组织中查找信息的过程也是有帮助的。
让你的新简历浏览一下文件。新鲜的眼睛意味着新鲜的发现,他们会在纠正错误和修补漏洞的同时了解产品。
不要假设他们知道你在说什么。定义组织特有的概念、术语或行话。通过询问他们事情是否有意义来确认他们的理解。如果他们不提问,那就太容易或太难了。找出是哪一个。
不要倾倒知识。谈谈你为什么要分享一些东西,以及你是如何学会的。不要指望你的新 SRE 第一次就能记住所有的东西。
解释您的升级策略。通过确保你有冗余和多个备份联系人来保持他们的密闭性。
分享战争故事。对于一个新 SRE 来说,知道首席技术官停止所有生产超过 24 小时仍有工作是非常令人欣慰的。
更详细的是可取的。这给了新 SRE 一个机会说,“我已经知道了”,而不是“你刚才说的我一个字也不懂,你能重新开始吗?”这是给他们信心和保持动力的一个小方法。
思考失败
由于风险总是存在,它有助于定义您的组织如何考虑它。我们认为:
验尸应该是无可指责的。大多数事件涉及许多因素;指责无助于找到他们。
错误应该得到祝贺。这意味着您的新 SREs 正在学习!
战争故事是荣誉的徽章,应该自豪地佩戴。自由分享这些故事。这将帮助新的 sre 了解:
- 失败发生了。
- 失败会发生在你的眼皮底下。
- 当失败发生时,你需要做好应对的准备。
- 当你失败的时候,你仍然有一份工作。
最后的想法
入职 SRE 和入职一名普通的软件工程师并没有什么不同。风险越来越大,恐惧也就越来越多。希望这些最佳实践和信息能帮助你,无论你是在寻找潜在的雇员,开始一个 SRE 的角色,还是帮助其他人开始他们的角色。
非常感谢 Ruth Wong 组织了这个小组,也感谢@lizthegrey 和@jennski 在推特上发布了我们讨论的内容。
CircleCI onboarding toolkit:帮助您更快发货的用户友好型工具
持续集成和交付(CI/CD)通过自动化构建、测试和部署过程来优化团队开发流程,以确保快速可靠地交付高质量的软件。采用一流的 CI/CD 工具是您的组织能够做出的最佳投资之一以缩短交付周期、消除痛苦的手动流程并控制基础设施成本。
尽管有这些好处,将新技术引入您的工具链有时会涉及一个漫长而令人沮丧的学习过程。幸运的是,CircleCI 提供了几个工具,您可以使用它们来简化入职流程,并在几分钟内启动和运行一个快速安全的管道,无论您的经验水平如何。
在这篇博文中,我们将重点介绍一些流行的特性,这些特性使得设置、管理和共享 CircleCI 管道变得更快、更直观。我们将了解一个典型的入职流程,并指出您可以在何处以及如何使用以下工具来快速上手:
我们还将探讨诸如 CircleCI CLI 、 Insights dashboard 和私有和公共 orbs 等功能如何帮助您优化管道,以实现更轻松、更安全、更高效的交付。我们开始吧!
步骤 0:将项目连接到 CircleCI
使用 CircleCI 自动化开发工作流的第一步是连接您的代码库,以监控代码库中的更改。CircleCI 支持所有主流版本控制系统,包括 GitHub、GitLab、Bitbucket。按照我们的快速启动指南连接您的回购并建立一个项目。
如果你是一个 VS 代码用户,现在也是安装我们官方 VS 代码扩展的好时机,它提供了几个特性来帮助你在你的 IDE 中设置和管理你的管道。您可以从“设置”窗格中选择要跟随的项目、分支和状态。
您可以直接在 VS 代码中安装扩展,或者从 Visual Studio marketplace 下载。
步骤 1:设置管道配置文件
CircleCI 使用 YAML 配置文件存储在您的项目报告的.circleci
目录中,以定义您的管道中要执行的各种作业。如果您的项目报告中还没有配置文件,您可以在项目设置期间为您的首选编程语言添加一个示例配置,方法是在项目设置对话框中选择“快速”选项,或者复制我们文档中的示例配置之一。
虽然可以在 CircleCI web 应用程序中直接编辑您的config.yml
文件,但 CircleCI VS 代码扩展提供了一个配置助手,通过在您的开发环境中直接使用实时反馈来帮助您创建、修改和排除配置文件故障。
扩展中的一些配置管理功能包括:
- 语法验证
- 语法突出显示
- 转到定义和转到引用
- 悬停文档和使用提示
- 自动完成
在您的 IDE 中提供这些特性可以缩短您的配置学习曲线,并且更容易根据您团队的独特需求来定制您的管道。
喜欢用 JavaScript 或 TypeScript 编写配置的更有经验的开发人员也可以使用我们的开源配置 SDK。通过 SDK,您可以利用完整的 JavaScript 生态系统,并将您的配置编写为更高级的编排逻辑的代码。
</blog/media/2022-09-19-config-sdk.mp4>
您可以在介绍 CircleCI 配置 SDK 中了解更多关于配置 SDK 的信息。有关在配置文件中编排工作流的所有可能方式的更多信息,请访问我们文档中的配置介绍和配置参考。
步骤 2:验证您的配置并运行工作流
现在您已经配置了您的管道,您可以在 CircleCI 上触发工作流运行之前在本地验证您的配置文件。这可以通过捕捉错误并防止错误配置的工作流在云中运行来节省您的时间和金钱。
在触发管道之前,有两种方法可以验证 CircleCI 配置:
- 从命令行使用 CircleCI CLI
- 通过命令中的 VS 代码扩展
CircleCI CLI 是从本地环境与配置文件和管道进行交互的有用工具。要使用它来验证您的配置并排除故障,首先安装 CLI ,然后使用命令circleci config validate
。CLI 将在终端中显示配置文件中任何错误的详细信息。
同样的命令也可以通过 VS 代码扩展获得。您可以输入 cmd+shift+P 打开命令面板,然后选择命令“CircleCI: Validate Configuration”。
任何识别出的错误都会显示在 VS 代码输出面板中。
一旦您确认您的配置文件没有错误,您就可以通过提交对项目存储库中的应用程序代码的更改来触发管道。您可以使用管道管理器在 VS 代码扩展中监控您的工作流状态。
从“管道”面板,您可以快速识别成功或失败的工作流,取消正在运行的作业,从头重新运行失败或取消的工作流,为具有手动审查要求的作业提供批准,下载构建工件,等等。
步骤 3:扩展和优化您的配置
现在,您已经有了一个工作流程,您可以寻找在范围和效率方面从您的工作流中获得更多的方法。扩大管道中执行的工作范围的一些选项包括:
好消息是,您不需要从头开始实现这些类型的作业。CircleCI 提供了许多orb,或可重用的配置代码包,专为使第三方工具更容易、更安全地集成到您的工作流中而构建。在我们的博客上了解更多关于使用球体的信息。
随着您的管道变得越来越复杂,您可能还会发现使用以下优化之一使其运行得更快的机会:
寻找优化机会的最佳方式之一是咨询 CircleCI web 应用程序中的 Insights 仪表盘。在此面板中,您可以查看有关工作流持续时间、吞吐量、成功率和恢复时间的历史数据,以及每个工作流运行消耗的配额。
花时间了解更多关于优化技术以及如何使用 Insights dashboard 来评估您的管道性能,可以在 CI/CD 管道上花费大量时间和金钱。
VS 代码扩展通过提供关于未使用的作业、废弃的键或映像的警告,以及标记新的可用版本的 orb 或映像,使得发现未优化的配置变得容易。它还根据我们在新用户中发现的模式提供了一些使用提示。
步骤 4(高级):共享并保护您的配置
一旦您有了一个复杂的、快速运行的管道,就该与其他团队和项目分享您的工作,以便他们可以达到相同的性能水平。CircleCI 的 orbs 和 config SDK 允许您打包并跨团队共享配置,以便于重用重要的工具和流程。
使用 orb,您可以抽象出配置的关键部分,以便可以将它们导入到整个组织的其他项目中。球体可以是公共的,也可以是私有的。私有 orb 对于与团队共享内部工具和框架特别有用。你可以在我们的文档中了解更多关于 orb 创作的信息,或者跟随我们的教程为你的组织创建私有 orb。
Config SDK 用户还可以将配置片段作为可导入的 JavaScript 包共享。您可以将包发布到 NPM、GitHub 或其他存储库,您的团队可以访问这些存储库来跨项目重用公共工作流或作业。在 config SDK wiki 中了解更多关于发布配置包的信息。
最后,随着 CI/CD 的使用在整个组织中扩展,您可能希望实现一些规则来确保管道的一致性和安全性。配置策略是我们的规模计划中提供的一项功能,可让您对组织管道中使用的工作负载和工具实施全局要求和限制。任何违反策略的管道都将自动失败。
配置策略对于大规模管理 CI/CD 渠道的平台团队尤其有用。您可以将它与本指南中的其他功能结合使用,不仅可以帮助新用户快速上手,还可以帮助他们遵循组织的最佳实践。
结论
现代 CI/CD 技术的用途极其广泛,但有时会让新用户感到不知所措。如果您刚刚开始使用 CircleCI,或者如果您正在将新的团队成员带到该平台上,请利用我们用户友好的工具来帮助释放自动化开发流程的所有好处。
除了这些工具,CircleCI 还提供广泛的定制支持和按需学习资源,帮助您迈向 DevOps 成熟:
在 CircleCI,我们相信软件应该是一个想法问题,而不是交付问题。我们很高兴你成为我们社区的一员,我们迫不及待地想看看你下一步会做什么。请务必通过 Twitter 或 LinkedIn 联系我们,让我们知道您是如何使用 CI/CD 来提升您组织的软件交付实践的。
还有一点:苹果开发者现在可以在 CircleCI 2.0 - CircleCI 上为 macOS、iOS、tvOS 和 watchOS 构建
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
从今天开始,CircleCI 2.0 可供为 iOS、macOS、tvOS 或 watchOS 构建应用程序的客户使用。为苹果设备构建的团队现在可以访问工作流来定制他们的应用交付管道,使用单个配置文件为苹果平台、Android 和 Linux 构建,并完全控制他们的 Xcode 版本。
开发人员现在还可以安排作业,使得在 App Store 和谷歌 Play 商店中跨所有操作系统同时更新应用等任务变得容易。
推一次,处处测试
CircleCI 2.0 是为希望获得快速反馈的团队而构建的:一旦完成拉取请求,最新版本的应用程序将自动部署到试飞中。这使得在 iPhones、Apple Watches、MAC 和 Apple TV 上一次性更新和测试您的应用程序变得非常容易。因为构建现在可以通过一个配置文件在 macOS 和 Linux 上运行,团队现在也可以使用他们自己的定制 Docker 映像来在他们的 pull 请求中运行 Danger 和 SwiftLint 等流行工具,而不必等待这些工具安装在 Mac 机器上。对于那些想要为他们构建的所有操作系统整合他们的工具链的团队来说,这个更新也应该是令人兴奋的。拥有为不同操作系统构建的多个团队的组织现在可以在一个工具上简化他们的流程,有助于确保一致性、顺利扩展并节省时间。
选取您自己的 Xcode 版本
有了 CircleCI 2.0,跟上苹果的更新和 Xcode 版本就容易多了。我们已经添加了对跨所有作业容纳多个 Xcode 版本的支持。这意味着您可以完全控制所使用的 Xcode 版本,确保作业在符合每个应用程序要求的版本上运行,并且依赖关系始终是最新的。想在并行作业中运行 Xcode 9.0 和 9.0.1 的测试吗?没问题。
加入 React Native、Envoy、Lyft 和许多其他应用程序——在 CircleCI 2.0 上灵活地构建您的应用程序
" Envoy 旨在通过简化微服务架构的扩展来帮助团队避免重复和无差别的繁重工作。我们一直在寻找一个共享这一理念的解决方案,并允许我们在相同的配置中构建 macOS 和 Linux。我们在切尔莱西找到的。通过使用工作流,我们能够在超大机器上运行我们的 Linux 作业,同时运行我们的 macOS 构建,这使得一次为多个操作系统进行构建、测试和部署变得非常容易。”
–马特·克莱恩,Lyft 软件工程师兼 Envoy 维护人员
“ React Native 项目最近迁移到 CircleCI 2.0,以利用更快的构建时间和工作流。由于能够同时构建 iOS、Android 和 Linux 作业,我们能够从多个 CI 平台中辞职,并将一切(包括自动化部署)与我们新的 CircleCI 设置相集成。”
——迈克·格拉博斯基,React Native 的撰稿人
“我非常喜欢 macOS 的 CircleCI 2.0!我喜欢作业强大的可配置性和可定制性,绝对的速度,以及跨平台 macOS/Linux 作业并发运行的能力。”Lyft 的 iOS 开发人员 JP Simard
查看我们关于 circle ci 2.0iOS 项目的文档,今天就开始构建。
如果您还没有在 CircleCI 上构建 iOS,请从这个针对 iOS 项目的入门指南开始。
使用 OpenID Connect 身份令牌向云提供商认证作业| CircleCI
原文:https://circleci.com/blog/openid-connect-identity-tokens/
在 CircleCI 作业中引入 OpenID Connect 身份令牌!此令牌使您的 CircleCI 作业能够向支持 OpenID Connect 的云提供商(如 AWS、Google 云平台和 HashiCorp Vault)进行身份验证。在这篇博文中,我们将向您介绍 OpenID Connect,解释它在 CI/CD 系统中的用途,并展示如何使用它通过 AWS 和 GCP 进行身份验证,让您的 CircleCI 作业安全地与您的帐户交互,而无需任何静态凭据。
如果您对使用 OIDC 向第三方凭证管理器进行认证感兴趣,您还可以查看我们关于使用 OIDC 将 CircleCI 与 HashiCorp Vault 集成的教程。
什么是 OpenID Connect?
OpenID Connect (OIDC)是一种认证协议,允许云服务验证最终用户的身份。它向 OAuth2.0 添加了一个身份层,oauth 2.0 是一个授权协议,用于提供对云资源的单点登录(SSO)访问。自 2014 年推出以来,云提供商已经广泛采用 OpenID Connect 作为提供对受保护资源的安全访问的标准。
根据 OpenID Connect Foundation , OpenID “允许客户端基于授权服务器执行的认证来验证最终用户的身份。”当您使用 CircleCI 的 OpenID 连接令牌时,客户端是 AWS 等云提供商。最终用户是你在 CircleCI 运行的作业。授权服务器本身就是 CircleCI。
综上所述,这意味着像 AWS 这样的云提供商可以验证您的作业的身份,并允许它像经过身份验证的用户一样采取行动。这意味着您的工作可以安全地与 AWS 交互。
为什么 OpenID Connect 在 CI/CD 中有用?
在您的 CI 工作流中,您可能想要将二进制文件、测试工件或者日志上传到云存储中。也许您想要将一个包提交给一个工件存储库。或者您可能想要部署到您的生产环境中。如果您想要安全地访问这些云资源中的任何一个,您的作业需要拥有凭据以通过云提供商的身份验证。CircleCI 的上下文让您安全地存储凭证,并在整个组织的工作中使用它们。
但是,这些凭据是静态的。物理密钥授予对空间的访问权,直到锁被更改,类似地,这些静态凭证在您轮换它们之前是有效的,这可能是您想要做的安全最佳实践。您可以使用 CircleCI CLI 或 CircleCI API for Contexts 在 CircleCI web UI 中旋转按键。
轮换需要时间投入,这会降低您的工程能力。OpenID 连接令牌避免了这个缺点。当您将云提供商配置为信任来自 CircleCI 作业的 OpenID Connect 令牌时,您的云提供商将识别 CircleCI 的签名,并将您的作业请求视为可信。您的作业可以安全地与您的云提供商进行交互,以便您可以上传到云存储,更改生产状态,或者执行您决定允许的任何操作。
当您的 CircleCI 作业启动时,CircleCI 会对 OpenID 连接令牌进行签名,并使其可供您的作业使用。您的作业可以将此令牌提交给您的云提供商,云提供商将验证其真实性,授予您的作业临时凭据,并允许它采取一些操作。
你可以在我们的文档中读到 CircleCI 的 OIDC 令牌的结构。
使用 OpenID 连接令牌从 CircleCI 作业与 AWS 进行交互
下面是一个如何使用 CircleCI 的 OpenID 连接令牌与 AWS 交互的示例。首先,我们将进行一次性设置,将您的 AWS 帐户配置为信任 CircleCI 的 OpenID Connect 令牌。然后,我们将运行一个作业,该作业使用令牌与 AWS 进行交互,并将图像上传到 ECR。
设置 AWS
首先,在 AWS 中创建 IAM 身份提供者和 IAM 角色。这允许您的 AWS 帐户信任 CircleCI 的 OpenID 连接令牌。你只需要做一次。
在 IAM 中,创建一个 OpenID Connect 身份提供者。您将需要您的组织 ID,您可以通过转到您的 CircleCI 组织设置并从概览页面复制组织 ID 来找到它。
对于提供者 URL,提供https://oidc.circleci.com/org/<organization-id>
,其中<organization-id>
是您的 CircleCI 组织的 ID。
对于受众,请提供相同的组织 ID。
在 IAM 中,创建一个角色。对于受信任的实体,选择 Web 身份,然后选择您之前创建的身份提供者。对于观众,选择唯一选项,即添加权限。这允许您指定 CircleCI 作业可以做什么和不可以做什么。只选择您的工作需要的权限,这是一个 AWS 最佳实践。
注: 你可能会发现创建自己的策略 很有用。
从 CircleCI 作业与 AWS 交互
现在您已经设置了一个 IAM 角色,您已经准备好编写一个与 AWS 交互的 CircleCI 作业了。最简单的方法是使用 CircleCI 的 AWS CLI orb 来生成临时密钥,并配置一个使用 OIDC 的配置文件。
orb 是可重用的 YAML 配置包,它将重复的配置压缩成一行代码。在这种情况下,AWS CLI orb 使您能够在配置中仅用六行或更少的代码生成临时会话令牌、AWS 访问密钥 ID 和 AWS 秘密访问密钥。CircleCI 有几个支持多种 AWS 服务的 orb。你可以在我们的开发者中心找到它们。
首先,在您想要使用 OIDC 的工作流程中选择 CircleCI 作业。确保这些作业中的每一个都使用有效的 CircleCI 上下文。OpenID 连接令牌仅适用于至少使用一个上下文的作业。上下文可能不包含环境变量。你可以在我们的 OIDC 代币文档中读到 CircleCI 的 OIDC 代币。
在你的配置中,一定要导入aws-cli
orb。接下来,在与任何 AWS 服务交互之前,在您的作业中运行aws-cli/setup
命令。您需要为aws-cli/setup
命令提供与您在上一步中创建的角色相关联的role-arn
以及您的aws-region
。您可以选择提供一个profile-name
、role-session-name
和session-duration
。
如果您提供一个profile-name
,临时密钥和令牌将被配置到指定的配置文件。您必须对其余的 AWS 命令使用相同的profile-name
。否则,如果没有提供密钥和令牌,它们将被配置为默认配置文件。此外,如果您不提供role-session-name
或session-duration
,它们的默认值分别是${CIRCLE_JOB}
(您的作业名称)和3600
秒。
下面是一个完整的配置文件示例,其中包含一个使用 OIDC 配置概要文件并使用它登录 AWS ECR 的作业。您可以使用这个配置文件来运行其他 AWS 命令,比如 S3、EKS、ECS 等等,只要已经为role-arn
配置了适当的权限。
version: '2.1'
orbs:
# import CircleCI's aws-cli orb
aws-cli: circleci/aws-cli@3.1
jobs:
aws-example:
docker:
- image: cimg/aws:2022.06
steps:
- checkout
# run the aws-cli/setup command from the orb
- aws-cli/setup:
role-arn: 'arn:aws:iam::123456789012:role/OIDC-ROLE'
aws-region: "us-west-1"
# optional parameters
profile-name: "OIDC-PROFILE"
role-session-name: “example-session”
session-duration: 1800
- run:
name: Log-into-AWS-ECR
command: |
# must use same profile specified in the step above
aws ecr get-login-password --profile "OIDC-PROFILE"
workflows:
OIDC-with-AWS:
jobs:
- aws-example:
# must use a valid CircleCI context
context: aws
高级用法
你可以利用 CircleCI 的 OIDC 令牌中声明的格式来限制你的 CircleCI 作业在 AWS 中可以做什么。例如,如果某些项目应该只能访问某些 AWS 资源,您可以限制您的 IAM 角色,以便只有特定项目中的 CircleCI 作业可以担任该角色。
为此,请编辑您的 IAM 角色的信任策略,以便只有您选择的项目中的 OIDC 令牌才能担任该角色。信任策略决定了在什么条件下可以担任该角色。
首先,在项目设置>概述下找到你项目的项目 ID。
然后将以下条件添加到您的角色的信任策略中,以便只有您选择的项目中的作业可以担任该角色:
"StringLike": {
"oidc.circleci.com/org/<your organization ID>:sub": "org/<your organization ID>/project/<your project ID>/user/*"
}
它使用 StringLike 来匹配 CircleCI 的 OIDC 令牌在您选择的项目中的子声明。现在,您的其他项目中的工作不能承担这个角色。
使用 CircleCI OIDC 代币向 GCP 认证
您需要部署一些 GCP 基础设施,以便 GCP 能够识别 CircleCI 发送的令牌。需要创建三种资源:
- 工作负载身份池
- 工作负载身份池提供程序
- 服务帐户
您可以手动或使用 Terraform 以编程方式完成此操作。我们将在本文中讨论这两个问题。开始之前,您需要检索您的组织 ID。登录 CircleCI 点击组织设置即可找到。您的 ID 将显示在页面的顶部。记下您的 ID,以便在后面的步骤中使用。
手动创建 GCP 资源
要创建工作负载标识池及其提供者,请导航到工作负载标识池页面,然后单击创建池。命名您的池并选择 Open ID Connect (OIDC) 作为提供商。选择提供商名称和提供商 ID。接下来,将发行者 URL 设置为https://oidc.circleci.com/org/<YOUR ORGANIZATION ID>
。点击允许的观众并输入您的组织 ID。在属性映射下,配置以下属性:
google.subject = assertion.sub
attribute.org_id = assertion.aud
单击“保存”创建新的工作量标识池和工作量标识池提供程序。
下一步是将服务帐户绑定到工作负载标识池。创建一个服务帐户或使用一个现有的帐户,该帐户有权执行管道作业所需的 GCP 操作。接下来,从 Workload Identity Pools 页面中选择您新创建的工作量身份池。点击页面顶部的授予访问权限。从下拉菜单中选择服务帐户。接下来,单击【仅 匹配过滤器的身份,选择 org_id 作为属性名称,输入您的 CircleCI 组织 id 作为属性值,然后单击保存。关闭“配置您的应用程序”提示。
此时,所有必需的 GCP 资源都应该准备就绪。
使用 Terraform 以编程方式创建资源
此 GitHub repo 包含一个 Terraform 计划,该计划将部署使用 CircleCI OIDC 令牌进行身份验证所需的 GCP 基础架构。如果您之前没有在 GCP 使用过 Terraform,您需要安装 Terraform 并配置它以与 GCP 一起使用。
要使用该计划,请填写示例文件terraform.tfvars
,然后运行terraform validate
和terraform plan
。如果输出看起来不错,您可以使用terraform apply
来部署资源。有关 Terraform 计划如何运作的更多详细信息,请查看 repo 的自述文件。
使用 oidc 令牌从 circleci 作业使用 gcp 进行身份验证
这个 GitHub repo 包含了如何在 CircleCI 作业中使用 gcloud 向 GCP 认证的示例。相关的配置部分如下所示。此配置中的命令需要四个环境变量:
GCP_PROJECT_ID
GCP_WIP_ID
GCP_WIP_PROVIDER_ID
GCP_SERVICE_ACCOUNT_EMAIL
您可以在上下文中或在项目级别配置它们。如果您需要使用多个 GCP 项目,我们建议您创建一个上下文来保存每个项目的变量。如果您需要在一个 GCP 项目中使用多个服务帐户,您可以在作业级别使用环境键来覆盖GCP_SERVICE_ACCOUNT_EMAIL
的值,如所示,例如。这个示例作业中的验证步骤假设服务帐户拥有执行gcloud iam service-accounts get-iam-policy
的权限。
version: "2.1"
orbs:
gcp-cli: circleci/gcp-cli@2.4.1
commands:
gcp-oidc-generate-cred-config-file:
description: "Authenticate with GCP using a CircleCI OIDC token."
parameters:
project_id:
type: env_var_name
default: GCP_PROJECT_ID
workload_identity_pool_id:
type: env_var_name
default: GCP_WIP_ID
workload_identity_pool_provider_id:
type: env_var_name
default: GCP_WIP_PROVIDER_ID
service_account_email:
type: env_var_name
default: GCP_SERVICE_ACCOUNT_EMAIL
gcp_cred_config_file_path:
type: string
default: /home/circleci/gcp_cred_config.json
oidc_token_file_path:
type: string
default: /home/circleci/oidc_token.json
steps:
- run:
command: |
# Store OIDC token in temp file
echo $CIRCLE_OIDC_TOKEN > << parameters.oidc_token_file_path >>
# Create a credential configuration for the generated OIDC ID Token
gcloud iam workload-identity-pools create-cred-config \
"projects/${<< parameters.project_id >>}/locations/global/workloadIdentityPools/${<< parameters.workload_identity_pool_id >>}/providers/${<< parameters.workload_identity_pool_provider_id >>}"\
--output-file="<< parameters.gcp_cred_config_file_path >>" \
--service-account="${<< parameters.service_account_email >>}" \
--credential-source-file=<< parameters.oidc_token_file_path >>
gcp-oidc-authenticate:
description: "Authenticate with GCP using a GCP credentials file."
parameters:
gcp_cred_config_file_path:
type: string
default: /home/circleci/gcp_cred_config.json
steps:
- run:
command: |
# Configure gcloud to leverage the generated credential configuration
gcloud auth login --brief --cred-file "<< parameters.gcp_cred_config_file_path >>"
# Configure ADC
echo "export GOOGLE_APPLICATION_CREDENTIALS='<< parameters.gcp_cred_config_file_path >>'" | tee -a $BASH_ENV
jobs:
gcp-oidc-defaults:
executor: gcp-cli/default
steps:
- gcp-cli/install
- gcp-oidc-generate-cred-config-file
- gcp-oidc-authenticate
- run:
name: Verify that gcloud is authenticated
environment:
GCP_SERVICE_ACCOUNT_EMAIL: jennings-oidc-test@makoto-workbench.iam.gserviceaccount.com
command: gcloud iam service-accounts get-iam-policy "${GCP_SERVICE_ACCOUNT_EMAIL}"
- run:
name: Verify that ADC works
command: |
ACCESS_TOKEN=$(gcloud auth application-default print-access-token)
curl -f -i -H "Content-Type: application/x-www-form-urlencoded" -d "access_token=${ACCESS_TOKEN}" https://www.googleapis.com/oauth2/v1/tokeninfo
workflows:
main:
jobs:
- gcp-oidc-defaults:
name: Generate Creds File and Authenticate
context:
- gcp-oidc-dev
GCP 的高级 OIDC 用法
到目前为止,我们已经完成了使用 OIDC 令牌向 GCP 进行身份验证所需的最低配置。根据最小特权的原则,我们建议配置您的工作负载身份池提供者,根据 OIDC 令牌的声明限制访问。这可以通过以 CEL 表达式的形式向提供者添加属性条件来实现。
CEL 表达式可以使用 CircleCI 标记中包含的声明来限制谁能够模拟服务帐户。除了一些常见的标准声明之外,令牌还包含对项目 ID 和上下文的声明。
以下是限制特定组织和用户访问的表达式示例:
attribute.org_id=='01234567-89ab-cdef-0123-4567890abcde' &&
google.subject.matches('org/([\da-f]{4,12}-?){5}/project/([\da-f]{4,12}-?){5}/user/76543210-ba98-fedc-3210-edcba0987654')
下面是另一个将访问权限限制在能够访问特定上下文的用户的示例(参见我们的文档以了解更多关于如何限制对上下文的访问的信息):
attribute.org_id=='01234567-89ab-cdef-0123-4567890abcde' &&
attribute.context_id=='76543210-ba98-fedc-3210-edcba0987654'
以下示例将限制特定项目的任何用户的访问权限:
attribute.org_id=='01234567-89ab-cdef-0123-4567890abcde' &&
attribute.project_id=='76543210-ba98-fedc-3210-edcba0987654'
您可以使用这些声明来分别限制对单个项目中的作业或对特定上下文具有访问权限的用户的访问。有关 CircleCI OIDC 代币索赔的更多详细信息,请参见我们的文档。
结论
干得好!过去使用上下文的作业现在包含 CircleCI 的 OpenID 连接令牌。使用这些令牌在您的工作中安全地与云提供商交互,而没有密钥轮换的负担。
使用 CircleCI 资源仪表板| CircleCI 优化您的资源类
本文由史蒂夫·阿尔米和拉迪卡·古拉提共同撰写。
CircleCI cloud 在多个执行环境中提供了 20 多个资源类(不同的 CPU 和 RAM)。为您的工作找到最佳的资源类大小——不要太大也不要太小——有时可能是一个挑战。
为了帮助解决这个问题,您可以在 UI 中查看 Docker、Linux、Windows、macOS、Arm 和 GPU 执行器的 CPU 和 RAM 使用情况。
仪表板位于作业详细信息页面的资源选项卡中,显示作业中所有并行运行的 CPU 和 RAM。
了解您工作中利用的资源将有助于您的团队通过以下方式优化您的 CircleCI 体验:
- 更大的资源类,以最大限度地减少等待验证更改所花费的时间
- 有机会将较轻的工作负载转移到较小的资源类别,并将配额重新分配给更密集的工作负载
- CircleCI 平台上优化的信用消费
在使用 CI/CD 平台时,更深入地了解您如何使用资源非常重要。我们的工程团队将致力于改进和扩展资源利用率仪表板,以支持更多的资源类类型。
我们继续依赖您的反馈。请通过 Canny 分享您的想法,让我们知道资源仪表板如何帮助您的团队以及您希望看到的新功能。
使用 BuildGraph 和 CircleCI | CircleCI 优化虚幻引擎构建
本教程涵盖:
- 创建构建图脚本来打包 Windows 和 Linux 的虚幻引擎项目
- 将构建图脚本转换为 CircleCI 的动态配置
- 使用 CircleCI 的自托管运行程序在 AWS 上并行执行虚幻引擎构建
在 Vela Games ,我们使用 CircleCI 构建 Project-V,这是我们新的多人在线合作(MOCO)游戏,融合了多人在线战斗竞技场(MOBA)游戏的团队合作和技能与大型多人在线(MMO)地牢运行的冒险。
在下面的教程中,我们将演示如何使用虚幻引擎的 BuildGraph 技术以及 CircleCI 的动态配置和自托管运行程序并行执行构建步骤,从而显著加快虚幻引擎游戏的构建时间。
以下项目超出了本教程的范围:
本教程中显示的所有代码都可以在这个 GitHub 资源库中获得。
背景
随着 Project-V 的开发在 2022 年快速发展,越来越多的人加入我们的团队,由于提交量和我们的管道结构,我们开始在 Jenkins CI 基础设施中遇到瓶颈。每个步骤(构建、烹饪、打包和游戏服务器部署)都是按顺序进行的,在某些情况下需要 2.5 个小时才能完成。
顺便提一下,我们也在用不同的技术开发游戏支持项目。Jenkins 的一些项目实现自动化的门槛比我们预期的要高,我们希望探索其他选项来降低工程师的复杂性,从而使他们能够更多地专注于开发产品,而不是构建流程自动化。
我们决定将詹金斯管道迁移到 CircleCI,以改善这些问题。我们首先关注 Project-V 的构建过程。我们的目标是通过将不同的阶段并行化并分散到不同的代理上,尽可能地减少游戏构建时间。
这导致我们利用虚幻引擎的构建图技术以及 CircleCI 的动态配置和自托管跑步者。有了这些,在最好的情况下,我们能够将构建时间减少 85%。
本教程在很大程度上基于我们所做的工作,并将向您展示如何利用动态配置和自托管运行器,通过使用 BuildGraph 在多个运行器之间分配过程来加速您的虚幻引擎 5 项目的构建、烹饪和打包阶段。
先决条件
要完成本教程,您需要准备好以下物品:
- 一个 CircleCI 账户。
- AWS 客户以及如何部署 EC2 实例的知识。
- Linux 和 Windows AWS AMIs 用虚幻引擎 5 源代码。
- 所有自助跑步者之间的快速共享储物空间。对于 OpenZFS ,我们使用了 AWS FSx。
- 一个虚幻的 Engine 5 项目。我们将使用虚幻引擎中包含的第一人称射击游戏示例。
什么是 BuildGraph?
BuildGraph 是包含在 Unreal Engine 中的基于脚本的构建自动化系统,它以 UE 项目中常见的构建块的图形为特色。
构建图脚本是用 XML 编写的,其中定义了相互依赖的节点。
每个节点由按顺序执行的任务组成,这些任务期待(取决于配置)输入并产生输出。使用标签定义输入和输出,其形式为#MyTag
。
通过定义输入期望,BuildGraph 形成一个依赖图,它使用该依赖图来确定每个节点必须具有哪种依赖关系才能执行配置的任务;这些在使用共享存储的作业之间共享,因为每个节点可能运行在不同的物理节点上。
构建图脚本由以下元素创建:
- 任务是作为构建过程(编译、烹饪等)的一部分执行的动作。).
- 节点是被命名的有序任务序列,被执行以产生输出。在执行任务之前,节点可能依赖于其他节点先执行它们的任务。
- 代理是在同一台机器上执行的节点组(如果作为构建系统的一部分运行)。代理在本地构建时没有效果。
- 触发器是组的容器,只能在手动干预后执行。
- 集合是一组节点和命名输出,可以用一个名称引用。
在本教程中,我们将只使用任务、节点和代理。还有像ForEach
这样的流控制节点和条件,我们将使用它们来使我们的脚本更加动态,并根据输入做不同的事情。
BuildGraph 与 UnrealBuildTool、AutomationTool 和编辑器深度集成,允许您跨不同平台编排游戏的编译、制作和打包。
在撰写本文时,有一个使用 BuildGraph 时的免责声明:
- XML 脚本当前只能位于虚幻引擎目录中。
- 有一个 bug,BuildGraph 失败是因为产生的工件被假定为改变了,而实际上它们并没有改变。
由于这些限制,我们修补了 BuildGraph,以便在我们的游戏报告中使用 XML 脚本,并允许文件的“突变”。红点工作室的 June Rhodes 发现并修补了这些问题。
您可以在这个 GitHub 库中找到 git 补丁。
动态配置
如果你已经使用了 CircleCI,但你不知道动态配置,那么你习惯于纯粹在 YAML 定义你的工作流程。
动态配置允许您在已经运行的工作流setup
中定义您的完整工作流,您可以使用任何您想要的编程语言来完成,只要输出是一个可接受的 CircleCI YAML 定义。
一旦有了 YAML,就可以将它传递给 CircleCI API 的/api/v2/pipeline/continue
端点来创建动态工作流。对于这一步,我们将使用延续球,因为它大大简化了过程。
我们将利用动态配置,根据 BuildGraph 在 XML 脚本中定义的内容来定义整个工作流。
注意: 要使用此功能,请通过选择项目设置 > 高级 > 使用设置工作流启用动态配置来启用它。
自主跑步者
我们的另一个重要特性是自托管运行器,它允许您在自己的基础设施上运行 CircleCI 作业(在我们的例子中是 AWS 上的 EC2 实例)。我们的构建过程需要 CircleCI 的云平台上没有的非常大的资源类型,自托管运行程序允许我们访问我们需要的定制计算资源。
当使用自托管 runners 时,您有责任使用任何您需要的软件来构建您自己的基础架构,以使您的工作流成功运行。在我们的场景中,我们需要将虚幻引擎源代码打包到 AMI 中,我们将使用它来部署这些运行器。
如何用 UE 构建 AMI 不属于本教程的范围,但是我推荐使用 Packer 来构建 AMI。你可以在 Epic Games 的网站上获取 UE 源码。有关于如何在虚幻引擎 GitHub 库上构建引擎的文档。
我们开始吧
对于本教程,我们将使用虚幻引擎附带的第一人称射击游戏示例。
我们将创建一个 BuildGraph 脚本来编译、烹饪和打包 Windows 和 Linux 上的项目;我们将利用它创建一个 Python 脚本,将 BuildGraph 转换为 CircleCI 动态配置。
构建图脚本将是所有核心管道逻辑的所有者,并将决定运行什么以及在哪里运行。我们将在动态配置的setup
阶段运行 BuildGraph,使用一个特殊的标志来指示 BuildGraph 只将图形输出为 JSON。然后,我们将 JSON 作为参数传递给我们的 Python 层,并在 YAML 中输出最终的工作流。
最终结果将是一个包含游戏二进制文件的 ZIP 文件,它将作为工件上传到 CircleCI 上。
部署自托管运行程序
在本节中,我们将配置和部署自托管运行程序,我们的工作流将在这些运行程序上运行。这些人将负责构建、烹饪和打包我们的示例游戏。
首先,使用 CircleCI CLI 或自托管 runner web UI 为 Linux 和 Windows 实例分别创建一个资源类。我们将使用命令行界面:
$ circleci runner resource-class create vela-games/linux-runner-test "For CircleCI Tutorial" --generate-token
api:
auth_token: f5dfdc41d7973864e9f625a897b755fea5ac17ecdf519732b81b87a309467bf20698fbdbc04a8b94
+------------------------------+-----------------------+
| RESOURCE CLASS | DESCRIPTION |
+------------------------------+-----------------------+
| vela-games/linux-runner-test | For CircleCI Tutorial |
+------------------------------+-----------------------+
无论您选择哪个选项,在创建资源类之后,都会提供一个auth_token
。在实例上配置代理时,您需要这样做。
接下来,部署配置资源类和auth_token
的实例。
在 Vela,我们坚信基础设施即代码和 GitOps 驱动的工作流以及同行评审的变更。Terraform 与我们的意识形态完全一致,我们用它来部署我们的自托管跑步者。
我们正在开源一个例子,说明如何使用 Terraform 为这个场景部署自托管运行器。这在很大程度上基于我们在 Vela 内部使用的方法。你可以在 GitHub 上找到库,欢迎你叉出来使用。
这个模块为每个 runner 资源类使用一个自动缩放组。这使得在需要时伸缩实例变得非常简单,因为只需定义一次启动配置,就可以在所有需要的 EC2 实例中重用它。如果您愿意,这也为您将来基于作业队列自动缩放运行程序奠定了基础。
BuildGraph 需要一个共享存储来在运行的作业之间共享工件。为此,我们选择将 AWS FSx 用于 OpenZFS ,因为它为我们提供了:
- 能够通过 NFS 协议挂载文件系统,实现与操作系统无关的可访问性。这对我们很重要,因为我们将在 Windows 和 Linux 机器上构建
- 访问文件时的延迟非常低。这对于几乎即时的元数据操作非常有用,Unreal 执行这些操作是为了检查构建所需的文件。
当应用 Terraform 模块时,它将:
- 为您配置的每个资源类创建一个自动缩放组
- 默认情况下,部署自动扩展组,根据一个 cron 表达式执行一个预定的操作来扩大或缩小。我们这样做是为了在不希望管道运行时不运行实例,从而节省一些资金。
- 为 OpenZFS 文件系统部署一个 FSx,我们将使用它来拥有一个共享 DDC ,并为 BuildGraph 在运行的作业之间共享工件。
- 使用用户数据脚本部署每个 EC2 实例,该脚本将安装 CircleCI 代理并配置 FSx 挂载。在 Windows 的情况下,当我们运行管道时,它将创建一个 PowerShell 脚本来挂载 FSx。对于 Linux,FSx 将作为用户数据执行的一部分被挂载。
要使用 Terraform 模块,请确保创建一个tfvars
文件,为您的部署和您的 vpc_id 配置subnet _ id。
每个资源类的auth_tokens
必须被配置为 circleci_auth_tokens 映射中的一个映射。该值必须采用以下格式:
{
"namespace/resource-class": "your-token"
}
将标记视为敏感值。在 Vela,我们使用 Terraform Cloud,并将auth_token
地图配置为工作区的敏感变量。
在 runners.auto.tfvars 文件中,您可以配置您想要部署的运行程序的列表。这是一个如何使用它的例子:
runners = [{
name = "namespace/linux-resource-class"
instance_type = "c6a.8xlarge"
os = "windows"
ami = "ami-id"
root_volume_size = 2000
spot_instance = true
asg = {
min_size = 0
max_size = 10
desired_capacity = 0
}
key_name = "key-name"
scale_out_recurrence = "0 6 * * MON-FRI"
scale_in_recurrence = "0 20 * * MON-FRI"
},
{
name = "namespace/windows-resource-class"
instance_type = "c6a.8xlarge"
os = "linux"
root_volume_size = 2000
spot_instance = true
ami = "ami-id"
asg = {
min_size = 0
max_size = 10
desired_capacity = 0
}
key_name = "key-name"
scale_out_recurrence = "0 6 * * MON-FRI"
scale_in_recurrence = "0 20 * * MON-FRI"
}]
确保name
等于您之前创建的资源类名,因为这用于从映射中获取auth_token
。
如果您决定不使用我们的 TF 模块来部署代理,查看文档了解如何安装。确保你有一个快速共享的存储解决方案,所有的运行者都可以连接到这个解决方案,在任务之间共享中间文件。
创建构建图脚本
如果你对这一部分不感兴趣,你可以在这里找到完整的脚本,然后进入教程的下一部分。
如前所述,我们将使用 UE 5.0 附带的第一人称射击游戏示例。您可以使用 UE 编辑器从头开始创建一个新项目,或者从 GitHub 中的示例中获取。
现在我们已经运行了自托管的 runner 基础设施,我们可以继续创建 BuildGraph 脚本,它将处理我们管道上的所有工作。
该脚本的目标是:
- 为烹饪编译编辑器
- 使用编辑器二进制文件做饭
- 编译游戏
- 包装游戏
Linux 和 Windows 都会出现这种情况。我们将创建一些参数,我们可以传递到构建图表
在您的项目上创建一个名为Tools
的新文件夹,并在其中创建一个BuildGraph.xml
文件。
创建顶层节点
首先定义root/top
节点:
<?xml version='1.0' ?>
<BuildGraph xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.epicgames.com/BuildGraph ./Schema.xsd" >
<BuildGraph/>
在<BuildGraph>
里面是我们所有的脚本将居住的地方。
创建一些输入选项
我们首先要包含一些<Option>
节点,这些节点可以从 CLI 传入,可以用来在我们的脚本中做不同的事情。
这些选项的值可以使用一个正则表达式来限制,它们可以有一个默认值。该值也可以是分号分隔的列表,您可以在<ForEach>
节点中使用它来动态创建新节点。
<?xml version='1.0' ?>
<BuildGraph xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.epicgames.com/BuildGraph ./Schema.xsd" >
<Option Name="ProjectRoot" Restrict=".*" DefaultValue="" Description="Path to the directory that contains the .uproject" />
<Option Name="UProjectPath" Restrict=".*" DefaultValue="" Description="Path to the .uproject file" />
<Option Name="ExecuteBuild" Restrict="true|false" DefaultValue="true" Description="Whether the build steps should be executed" />
<Option Name="EditorTarget" Restrict="[^ ]+" DefaultValue="FirstPersonGameEditor" Description="Name of the editor target to be built, to be used for cooking" />
<Option Name="GameTargets" Restrict="[^ ]*" DefaultValue="FirstPersonGame" Description="List of game targets to build, e.g. UE4Game" />
<Option Name="GameTargetPlatformsCookedOnWin" Restrict="[^ ]*" DefaultValue="Win64" Description="List of the game target platforms to cook on win64 for, separated by semicolons, eg. Win64;Win32;Android"/>
<Option Name="GameTargetPlatformsCookedOnLinux" Restrict="[^ ]*" DefaultValue="Linux" Description="List of the game target platforms to cook on linux for, separated by semicolons, eg. Win64;Win32;Android"/>
<Option Name="GameTargetPlatformsBuiltOnWin" Restrict="[^ ]*" DefaultValue="Win64" Description="List of the game target platforms to build on win64 for, separated by semicolons, eg. Win64;Win32;Android"/>
<Option Name="GameTargetPlatformsBuiltOnLinux" Restrict="[^ ]*" DefaultValue="Linux" Description="List of the game target platforms to build on linux for, separated by semicolons, eg. Win64;Win32;Android"/>
<Option Name="GameConfigurations" Restrict="[^ ]*" DefaultValue="Development" Description="List of configurations to build the game targets for, e.g. Development;Shipping" />
<Option Name="StageDirectory" Restrict=".+" DefaultValue="dist" Description="The path under which to place all of the staged builds" />
<BuildGraph/>
我为每个选项添加了一个Description
,这样你就可以对每个选项有一些了解。
正如您可能从创建的<Option>
节点中看到的,在这个例子中,我们将为每个平台保持独立的编译-烹饪-打包过程。对于某些平台,UE 支持交叉编译,但这将演示我们如何为不同的自托管 runner 操作系统选择我们的作业应该在哪里运行。
创建属性
接下来我们将添加一些<Property>
节点。这些就像变量,我们可以在脚本的不同阶段读取和写入。因为我们将使用<ForEach>
来遍历我们创建的一些<Option>
节点,我们将使用这些来聚合所有创建的标签。
<Property Name="GameBinaries" Value="" />
<Property Name="GameCookedContent" Value="" />
<Property Name="GameStaged" Value="" />
<Property Name="GamePatched" Value="" />
创建代理节点
一个<Agent>
将定义一组将在特定类型的实例(Linux 或 Windows)上运行的节点
定义如下:
<Agent Name="Windows Build" Type="UEWindowsRunner">
<Agent/>
Name
和Type
都可以是任意值。对于Name
,我们将为它内部将要运行的内容设置一些解释性的东西。Type
是这里最重要的东西,因为我们将使用它来确定这个<Agent>
中的所有作业将在哪个自托管运行器资源类上运行。这将发生在我们的 Python 转换层上。
我们将把类型限制为:
- 意味着它可以在 Windows 上运行
- 意味着它运行在 Linux 上
我们将创建七个这样的<Agent>
节点:
- 三个用于 Linux 构建(在 Linux 上编译、烹饪和打包)
- 三个用于 Windows 构建(在 Windows 上编译、烹饪和打包)
- 一个作为聚合,能够告诉 BuildGraph 运行所有作业
从 Windows 代理开始(构建、烹饪和打包)
在这一节中,我们将首先定义一个节点,该节点将编译我们的编辑器并将生成的输出标记为#EditorBinaries
,这样我们就可以在以后使用它们来烹饪我们的资产。
在第二部分中,我们将使用<ForEach>
遍历我们允许在 Windows 节点上构建的所有目标平台,并遍历我们针对游戏的每个配置(这可能是开发和/或发布)。
这些将(取决于我们的<Option>
输入)创建多个节点,这些节点将为 windows 上的多种配置和平台并行编译游戏,并标记生成的输出以供依赖它们的后续作业共享。
由于我们的<Agent>
中没有节点相互依赖,它们将并行运行。
<!-- Targets that we will execute on a Windows machine. -->
<Agent Name="Windows Build" Type="UEWindowsRunner">
<!-- Compile the editor for Windows (necessary for cook later) -->
<Node Name="Compile $(EditorTarget) Win64" Produces="#EditorBinaries">
<Compile Target="$(EditorTarget)" Platform="Win64" Configuration="Development" Tag="#EditorBinaries" Arguments="-Project="$(UProjectPath)""/>
</Node>
<!-- Compile the game (targeting the Game target, not Client) -->
<ForEach Name="TargetPlatform" Values="$(GameTargetPlatformsBuiltOnWin)">
<ForEach Name="TargetConfiguration" Values="$(GameConfigurations)">
<Node Name="Compile $(GameTargets) $(TargetPlatform) $(TargetConfiguration)" Produces="#GameBinaries_$(GameTargets)_$(TargetPlatform)_$(TargetConfiguration)">
<Compile Target="$(GameTargets)" Platform="$(TargetPlatform)" Configuration="$(TargetConfiguration)" Tag="#GameBinaries_$(GameTargets)_$(TargetPlatform)_$(TargetConfiguration)" Arguments="-Project="$(UProjectPath)""/>
<Tag Files="#GameBinaries_$(GameTargets)_$(TargetPlatform)_$(TargetConfiguration)" Filter="*.target" With="#GameReceipts_$(GameTargets)_$(TargetPlatform)_$(TargetConfiguration)"/>
<SanitizeReceipt Files="#GameReceipts_$(GameTargets)_$(TargetPlatform)_$(TargetConfiguration)" />
</Node>
<Property Name="GameBinaries" Value="$(GameBinaries)#GameBinaries_$(GameTargets)_$(TargetPlatform)_$(TargetConfiguration);"/>
</ForEach>
</ForEach>
</Agent>
我想强调的一点是,节点属性中的变量插值是允许的。
我们定义了第二个代理来在 Windows 上烹饪我们的资产:
<!-- Targets that we will execute on a Windows machine. -->
<Agent Name="Windows Cook" Type="UEWindowsRunner">
<!-- Cook for game platforms (targeting the Game target, not Client) -->
<ForEach Name="TargetPlatform" Values="$(GameTargetPlatformsCookedOnWin)">
<Node Name="Cook Game $(TargetPlatform) Win64" Requires="#EditorBinaries" Produces="#GameCookedContent_$(TargetPlatform)">
<Property Name="CookPlatform" Value="$(TargetPlatform)" />
<Property Name="CookPlatform" Value="Windows" If="'$(CookPlatform)' == 'Win64'" />
<Property Name="CookPlatform" Value="$(CookPlatform)" If="(('$(CookPlatform)' == 'Windows') or ('$(CookPlatform)' == 'Mac') or ('$(CookPlatform)' == 'Linux'))" />
<Cook Project="$(UProjectPath)" Platform="$(CookPlatform)" Arguments="-Compressed" Tag="#GameCookedContent_$(TargetPlatform)" />
</Node>
<Property Name="GameCookedContent" Value="$(GameCookedContent)#GameCookedContent_$(TargetPlatform);"/>
</ForEach>
</Agent>
这里我们遍历了允许在 Windows 上烹饪的每一个平台,Requires="#EditorBinaries"
意味着这个节点依赖于首先编译的 Windows 的编辑器。
对于其中的每一个,我们使用<Cook>
任务来烹饪资产,并标记最终的输出以用于以后的打包。
我们定义了在 Windows 上打包的第三个也是最后一个代理:
<!-- Targets that we will execute on a Windows machine. -->
<Agent Name="Windows Pak and Stage" Type="UEWindowsRunner">
<!-- Pak and stage the game (targeting the Game target, not Client) -->
<ForEach Name="TargetPlatform" Values="$(GameTargetPlatformsBuiltOnWin)">
<ForEach Name="TargetConfiguration" Values="$(GameConfigurations)">
<Node Name="Pak and Stage $(GameTarget) $(TargetPlatform) $(TargetConfiguration)" Requires="#GameBinaries_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration);#GameCookedContent_$(TargetPlatform)" Produces="#GameStaged_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration)" >
<Property Name="StagePlatform" Value="$(TargetPlatform)" />
<Property Name="StagePlatform" Value="Windows" If="'$(StagePlatform)' == 'Win64'" />
<Property Name="DisableCodeSign" Value="" />
<Property Name="DisableCodeSign" Value="-NoCodeSign" If="('$(TargetPlatform)' == 'Win64') or ('$(TargetPlatform)' == 'Mac') or ('$(TargetPlatform)' == 'Linux')" />
<Spawn Exe="c:\UnrealEngine\Engine\Build\BatchFiles\RunUAT.bat" Arguments="BuildCookRun -project=$(UProjectPath) -nop4 $(DisableCodeSign) -platform=$(TargetPlatform) -clientconfig=$(TargetConfiguration) -SkipCook -cook -pak -stage -stagingdirectory=$(StageDirectory) -compressed -unattended -stdlog" />
<Zip FromDir="$(StageDirectory)\$(StagePlatform)" ZipFile="$(ProjectRoot)\dist_win64.zip" />
<Tag BaseDir="$(StageDirectory)\$(StagePlatform)" Files="..." With="#GameStaged_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration)" />
</Node>
<Property Name="GameStaged" Value="$(GameStaged)#GameStaged_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration);" />
</ForEach>
</ForEach>
</Agent>
以类似于我们在编译阶段所做的方式,我们迭代通过我们允许在 Windows 上打包的每个平台,并且对于我们正在编译的每个配置,当我们需要编译的游戏时,我们打包熟的资产用于分发。
在编写的时候,没有 BuildGraph-native 任务来打包游戏,所以我们从RunUAT
调用经典的BuildCookRun
命令。
之后,我们使用<Zip>
任务来压缩我们的最终结果,这就是我们将作为工件存储的内容。
我想在这里指出,使用<Property>
来操纵依赖条件集的值:
<Property Name="StagePlatform" Value="$(TargetPlatform)" />
<Property Name="StagePlatform" Value="Windows" If="'$(StagePlatform)' == 'Win64'" />
在这个场景中,我们使用它来从Win64
值转换到 ue 支持的Windows,
。
Linux 代理(构建、烹饪和打包)
为我们的 Linux 构建定义步骤的过程基本上是相同的。我们将更改Tags
的名称、代理Types,
以及我们在 Linux 代理上放置虚幻引擎的路径:
<!-- Targets that we will execute on a Linux machine. -->
<Agent Name="Linux Build" Type="UELinuxRunner">
<!-- Compile the editor for Linux (necessary for cook later) -->
<Node Name="Compile $(EditorTarget) Linux" Produces="#LinuxEditorBinaries">
<Compile Target="$(EditorTarget)" Platform="Linux" Configuration="Development" Tag="#LinuxEditorBinaries" Arguments="-Project="$(UProjectPath)""/>
</Node>
<!-- Compile the game (targeting the Game target, not Client) -->
<ForEach Name="TargetPlatform" Values="$(GameTargetPlatformsBuiltOnLinux)">
<ForEach Name="TargetConfiguration" Values="$(GameConfigurations)">
<Node Name="Compile $(GameTarget) $(TargetPlatform) $(TargetConfiguration)" Produces="#GameBinaries_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration)">
<Compile Target="$(GameTarget)" Platform="$(TargetPlatform)" Configuration="$(TargetConfiguration)" Tag="#GameBinaries_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration)" Arguments="-Project="$(UProjectPath)""/>
<Tag Files="#GameBinaries_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration)" Filter="*.target" With="#GameReceipts_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration)"/>
<SanitizeReceipt Files="#GameReceipts_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration)" />
</Node>
<Property Name="GameBinaries" Value="$(GameBinaries)#GameBinaries_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration);"/>
</ForEach>
</ForEach>
</Agent>
<Agent Name="Linux Cook" Type="UELinuxRunner">
<ForEach Name="TargetPlatform" Values="$(GameTargetPlatformsCookedOnLinux)">
<Node Name="Cook Game $(TargetPlatform) Linux" Requires="#LinuxEditorBinaries" Produces="#GameCookedContent_$(TargetPlatform)">
<Property Name="CookPlatform" Value="$(TargetPlatform)" />
<Property Name="CookPlatform" Value="Windows" If="'$(CookPlatform)' == 'Win64'" />
<Property Name="CookPlatform" Value="$(CookPlatform)" If="(('$(CookPlatform)' == 'Windows') or ('$(CookPlatform)' == 'Mac') or ('$(CookPlatform)' == 'Linux'))" />
<Cook Project="$(UProjectPath)" Platform="$(CookPlatform)" Arguments="-Compressed" Tag="#GameCookedContent_$(TargetPlatform)" />
</Node>
<Property Name="GameCookedContent" Value="$(GameCookedContent)#GameCookedContent_$(TargetPlatform);"/>
</ForEach>
</Agent>
<Agent Name="Linux Pak and Stage" Type="UELinuxRunner">
<!-- Pak and stage the dedicated server -->
<ForEach Name="TargetPlatform" Values="$(GameTargetPlatformsBuiltOnLinux)">
<ForEach Name="TargetConfiguration" Values="$(GameConfigurations)">
<Node Name="Pak and Stage $(GameTarget) $(TargetPlatform) $(TargetConfiguration)" Requires="#GameBinaries_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration);#GameCookedContent_$(TargetPlatform)" Produces="#GameStaged_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration)">
<Property Name="StagePlatform" Value="$(TargetPlatform)"/>
<Property Name="DisableCodeSign" Value="" />
<Property Name="DisableCodeSign" Value="-NoCodeSign" If="('$(TargetPlatform)' == 'Win64') or ('$(TargetPlatform)' == 'Mac') or ('$(TargetPlatform)' == 'Linux')" />
<Spawn Exe="/home/ubuntu/UnrealEngine/Engine/Build/BatchFiles/RunUAT.sh" Arguments="BuildCookRun -project=$(UProjectPath) -nop4 $(DisableCodeSign) -platform=$(TargetPlatform) -clientconfig=$(TargetConfiguration) -SkipCook -cook -pak -stage -stagingdirectory=$(StageDirectory) -compressed -unattended -stdlog" />
<Zip FromDir="$(StageDirectory)/$(StagePlatform)" ZipFile="$(ProjectRoot)/dist_linux.zip" />
<Tag BaseDir="$(StageDirectory)/$(StagePlatform)" Files="..." With="#GameStaged_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration)" />
</Node>
<Property Name="GameStaged" Value="$(GameStaged)#GameStaged_$(GameTarget)_$(TargetPlatform)_$(TargetConfiguration);" />
</ForEach>
</ForEach>
</Agent>
正如你所看到的,我们主要是把代理的Type
改成了UELinuxRunner
,并且把所有的路径都改成了它们的 Linux 对应物。
聚集剂
我们最后创建一个虚拟代理,我们将使用它来聚合我们前面定义的所有任务。我们将在我们的setup
工作流中使用它来指示 BuildGraph 我们想要执行所有节点。
<Agent Name="All" Type="UEWindowsRunner">
<!-- Node that we just use to easily execute all required nodes -->
<Node Name="End" Requires="$(GameStaged)">
</Node>
</Agent>
完整的构建图脚本
概括地说,我们创建了一个构建图脚本,该脚本将生成以下步骤:
- 编译用于烹饪的 UE 编辑器
- 伪造游戏资产
- 编译游戏
- 将资产和二进制文件打包到一个可分发的
所有这些都适用于 Windows 和 Linux 平台。
您可以在我们的库中找到完整的构建图脚本。
翻译层
如果你更喜欢阅读代码而不是遵循本节教程,你可以在这里找到翻译层的所有代码。
现在我们有了 BuildGraph 脚本,我们需要找到一种方法将这个 XML 脚本转换成 CircleCI YAML 工作流。
为此,我们将创建一个 Python 转换层。在这里,我们将创建这些类:
- 一个基类
Runner
,包含我们所有的流水线步骤和操作系统无关的代码。由于我们将在 Linux 和 Windows 上运行 BuildGraph,我们需要考虑到这两个操作系统使用不同的 shells,并且某些步骤会有所不同,比如设置和读取环境变量。 - 一个继承自
Runner
的UEWindowsRunner
类,将包含所有特定于 Windows 的代码 - 一个继承自
Runner
的UELinuxRunner
类,将包含所有特定于 Linux 的代码
如您所见,我们的类名与我们在构建图脚本中定义的Agent Types
相同。这不是巧合;我们将使用Reflection
来实例化这些类。
在Tools
中创建一个buildgraph-to-circleci
目录,并在其中创建:
- 一个
buildgraph-to-circleci.py
剧本 - 一个
common.py
剧本 - 一个
runners
目录和内部:__init__.py
-公开 runners 目录中的类。- 这是我们的基类,包含了所有的 CircleCI 工作流逻辑
ue_linux_runner.py
-具有特定于操作系统的代码的 Linux 类ue_windows_runner.py
-带有操作系统特定代码的 Windows 类
首先,在common.py:
中创建一些常用函数
# common.py
# This will add support for multiline string in PyYAML
def yaml_multiline_string_pipe(dumper, data):
text_list = [line.lstrip().rstrip() for line in data.splitlines()]
fixed_data = "\n".join(text_list)
if len(text_list) > 1:
return dumper.represent_scalar('tag:yaml.org,2002:str', fixed_data, style="|")
return dumper.represent_scalar('tag:yaml.org,2002:str', fixed_data)
# Will use this to sanitize job names, removing spaces with hyphens.
def sanitize_job_name(name):
return name.replace(' ', '-').lower()
这些是我们将在其他几个 Python 文件中使用的一些函数。一个是在 YAML 为最终的 CircleCI 工作流添加更好的多行字符串支持,另一个是通过用连字符替换空格来净化作业名称。
buildgraph-to-circleci.py
是我们将在setup
工作流中执行的主要脚本。这将接收 BuildGraph 导出的 JSON,遍历它,并使用反射实例化正确的 runner 类,最终生成build-game
工作流的步骤:
#!/usr/bin/env python3
import json
import yaml
import argparse
import os
import sys
import importlib
from common import yaml_multiline_string_pipe, sanitize_job_name
# We create some arguments that we need to execute the script.
parser = argparse.ArgumentParser()
parser.add_argument("--json-graph", required=True, help="Path to Graph in JSON format")
parser.add_argument("--git-branch", default=os.getenv("CIRCLE_BRANCH", ""), help="Branch that triggered the pipeline")
parser.add_argument("--git-commit", default=os.getenv("CIRCLE_SHA1", ""), help="Commit that triggered the pipeline")
args = parser.parse_args()
if not args.git_branch or not args.git_commit:
print("--git-branch and --git-commit are required. Or CIRCLE_BRANCH and CIRCLE_SHA1 variables should be defined")
parser.print_help(sys.stderr)
parser.exit(1)
graph = None
# Create an empty boilerplate object that we will fill with jobs for CircleCI
# We create a workflow named build-game
circleci_manifest = {
'version': 2.1,
'parameters': {
},
'jobs': {
},
'workflows': {
'build-game': {
'jobs': [
]
}
}
}
# Load the Exported JSON BuildGraph
with open(args.json_graph, "r") as stream:
try:
graph = json.load(stream)
except Exception as exc:
print(exc)
## Loop over the generated jobs
for group in graph["Groups"]:
for node in group["Nodes"]:
# Sanitize the node name BuildGraph gives us. Replacing spaces with hyphens and lowercase.
sanitized_name = sanitize_job_name(node['Name'])
# The End node is an additional node that buildgraph creates that depends on all nodes being executing, there's no actual logic here to execute
# so we check if we should skip.
if "end" in sanitized_name:
continue
# Using reflection load the runner class defined in the 'Agent Type' field on BuildGraph
Class = getattr(importlib.import_module("runners"), group['Agent Types'][0])
# The git branch is the only required parameter for our constructor.
runner = Class(args.git_branch)
# Generate the job for this Node
steps = runner.generate_steps(node['Name'])
# Add the job to our manifest
circleci_manifest['jobs'][sanitized_name] = {
'shell': runner.shell,
'machine': True,
'working_directory': runner.working_directory,
'resource_class': runner.resource_class,
'environment': runner.environment,
'steps': steps
}
job = {
sanitized_name: {
}
}
# Set the dependencies for each job
if node["DependsOn"] != "":
if not 'requires' in job[sanitized_name]:
job[sanitized_name]['requires'] = []
# The depdencies are in a semicolon-separated list.
depends_on = [sanitize_job_name(d) for d in node["DependsOn"].split(";")]
job[sanitized_name]['requires'].extend(depends_on)
# Add the job to the build-game workflow
circleci_manifest['workflows']['build-game']['jobs'].append(job)
## Print the final YAML
yaml.add_representer(str, yaml_multiline_string_pipe)
yaml.representer.SafeRepresenter.add_representer(str, yaml_multiline_string_pipe)
print(yaml.dump(circleci_manifest))
我们的主脚本接收 JSON 格式的 BuildGraph 导出图的路径,并遍历它。图中的每个节点都有作业必须运行的Agent Type
。我们获取它并实例化同名的 Python 类,然后调用generate_steps
方法来获取我们将在工作流中执行的作业的步骤。
最后,它设置工作流中哪些作业相互依赖。
现在我们在runners/runner.py
中创建我们的基础Runner
类:
from common import sanitize_job_name
class Runner:
def __init__(self):
# This will determine the resource class the runner will use
self.resource_class = ''
# The location of the RunUAT script
self.run_uat_subpath = ''
# The prefix to read an environment variable in the OS
self.env_prefix = ''
# The prefix to assign a value to an environment variable
self.env_assignment_prefix = ''
# The shell the runner will be using
self.shell = ''
# Any environment variables we want to include in our job execution
self.environment = {}
# The path to unreal engine
self.ue_path = ''
# The working directory for the jobs
self.working_directory = ''
# The shared storage BuildGraph will use
self.shared_storage_volume = ''
# The name of the branch we are running
self.branch_name = ''
# These methods have to be overloaded/overriden by the classes that inherit Runner
# they have to return the OS-specific way to execute these actions.
def mount_shared_storage(self):
raise NotImplementedError("Runners have to implement this")
def create_dir(self, directory):
raise NotImplementedError("Runners have to implement this")
def pre_cleanup(self):
raise NotImplementedError("Runners have to implement this")
def self_patch_buildgraph(self):
return f"git -C {self.ue_path} apply {self.env_prefix}CIRCLE_WORKING_DIRECTORY/Tools/BuildGraph.patch"
def patch_buildgraph(self):
self.self_patch_buildgraph()
def generate_steps(self, job_name):
return self.generate_buildgraph_steps(job_name)
# These return all the steps to run for the job
def generate_buildgraph_steps(self, job_name):
sanitized_job_name = sanitize_job_name(job_name)
filesafe_branch_name = self.branch_name.replace("/", "_")
steps = [
# Checkout our code
"checkout",
# Mount our shared storage
{
'run': {
'name': 'Mount Shared Storage (FSx)',
'command': self.mount_shared_storage()
}
},
# Create a directory within our shared storage specific for our branch/commit combination.
{
'run': {
'name': "Create shared directory for workflow",
'command': f"""{self.create_dir(f"{self.shared_storage_volume}{filesafe_branch_name}/{self.env_prefix}CIRCLE_SHA1")}
"""
}
},
# We do some cleanup previous to running BuildGraph
{
'run': {
'name': "Cleanup old build",
'command': self.pre_cleanup()
}
},
# We patch BuildGraph
{
'run': {
'name': "Apply BuildGraph patch",
'command': self.patch_buildgraph()
}
},
# Here we run our current BuildGraph node.
# Environment variables used:
# BUILD_GRAPH_ALLOW_MUTATION - To allow for file mutations
# uebp_UATMutexNoWait - Allows UE5 to execute multiple instances of RunUAT
# uebp_LOCAL_ROOT - The location of our Unreal Engine Build
# BUILD_GRAPH_PROJECT_ROOT - The working location of our project
#
# We always run the same BuildGraph command calling the specific Node we have to run for the step. Then BuildGraph takes care of the rest.
{
'run': {
'name': job_name,
'command': f"""
{self.env_assignment_prefix}BUILD_GRAPH_ALLOW_MUTATION=\"true\"
{self.env_assignment_prefix}uebp_UATMutexNoWait=\"1\"
{self.env_assignment_prefix}uebp_LOCAL_ROOT=\"{self.ue_path}\"
{self.env_assignment_prefix}BUILD_GRAPH_PROJECT_ROOT=\"{self.env_prefix}CIRCLE_WORKING_DIRECTORY\"
{self.ue_path}{self.run_uat_subpath} BuildGraph -Script=\"{self.env_prefix}CIRCLE_WORKING_DIRECTORY/Tools/BuildGraph.xml\" -SingleNode=\"{job_name}\" -set:ProjectRoot=\"{self.env_prefix}CIRCLE_WORKING_DIRECTORY\" -set:UProjectPath=\"{self.env_prefix}CIRCLE_WORKING_DIRECTORY/FirstPersonGame.uproject\" -set:StageDirectory=\"{self.env_prefix}CIRCLE_WORKING_DIRECTORY/dist\" -SharedStorageDir=\"{self.shared_storage_volume}{filesafe_branch_name}/{self.env_prefix}CIRCLE_SHA1\" -NoP4 -WriteToSharedStorage -BuildMachine
"""
}
}
]
pak_steps = []
# We check if we have the word 'pak' on our job name (this will come from our buildgraph script)
# To know if we have to upload an artifact after running BuildGraph
if "pak" in sanitized_job_name:
suffix = ""
if "win64" in sanitized_job_name:
suffix = "_win64"
elif "linux" in sanitized_job_name:
suffix = "_linux"
# We upload the produced artifact
pak_steps.extend([
{
'store_artifacts': {
'path': f'dist{suffix}.zip'
}
}
])
steps.extend(pak_steps)
return steps
这个基类定义了所有需要传递的属性,比如虚幻引擎目录的位置,以及如何分配环境变量。它还定义了一些必须由继承的类覆盖的方法,例如,创建目录,Linux 和 Windows 中的命令略有不同。然后,generate_buildgraph_steps
方法将输出相应的 CircleCI 工作流,该工作流将与 OS 无关。
现在我们有了与操作系统无关的代码,我们分别在runners/ue_linux_runner.py
和runners/ue_windows_runner.py
中创建 Linux 和 Windows 类。
# runners/ue_linux_runner.py
from .runner import Runner
class UELinuxRunner(Runner):
def __init__(self, branch_name):
Runner.__init__(self)
self.branch_name = branch_name
self.env_prefix = '$'
self.env_assignment_prefix = 'export '
self.environment = {
'UE_SharedDataCachePath': '/data_fsx/SharedDDCUE5Test'
}
self.resource_class = 'vela-games/linux-runner-ue5'
self.run_uat_subpath = '/Engine/Build/BatchFiles/RunUAT.sh'
self.shell = '/usr/bin/env bash'
self.shared_storage_volume = '/data_fsx/'
self.ue_path = '/home/ubuntu/UnrealEngine'
self.working_directory = f'/home/ubuntu/workspace/{branch_name.replace("/","-").replace("_", "-").lower()}'
def patch_buildgraph(self):
return f'{self.self_patch_buildgraph()} || true'
def mount_shared_storage(self):
return 'echo "Linux already mounted"'
def create_dir(self, directory):
return f"mkdir -p {directory}"
def pre_cleanup(self):
return """rm -rf *.zip
rm -rf /home/ubuntu/UnrealEngine/Engine/Saved/BuildGraph/
rm -rf $CIRCLE_WORKING_DIRECTORY/Engine/Saved/*
rm -rf $CIRCLE_WORKING_DIRECTORY/dist
"""
# runners/ue_windows_runner.py
from .runner import Runner
class UEWindowsRunner(Runner):
def __init__(self, branch_name):
Runner.__init__(self)
self.branch_name = branch_name
self.env_prefix = '$Env:'
self.env_assignment_prefix = '$Env:'
self.environment = {
'UE-SharedDataCachePath': 'Z:\\SharedDDCUE5Test'
}
self.resource_class = 'vela-games/windows-runner-ue5'
self.run_uat_subpath = '\\Engine\\Build\\BatchFiles\\RunUAT.bat'
self.shell = 'powershell.exe'
self.shared_storage_volume = 'Z:\\'
self.ue_path = 'C:\\UnrealEngine'
self.working_directory = f'C:\\workspace\\{branch_name.replace("/","-").replace("_", "-").lower()}'
def patch_buildgraph(self):
return f"""{self.self_patch_buildgraph()}
[Environment]::Exit(0)
"""
# This script we are creating in the user-data on our TF module. See above in the tutorial
def mount_shared_storage(self):
return "C:\\mount_fsx.ps1"
def create_dir(self, directory):
return f"New-Item -ItemType 'directory' -Path \"{directory}\" -Force -ErrorAction SilentlyContinue"
def pre_cleanup(self):
return """Remove-Item -Force *.zip -ErrorAction SilentlyContinue
Remove-Item -Force -Recurse \"C:\\UnrealEngine\\Engine\\Saved\\BuildGraph\\\" -ErrorAction SilentlyContinue
Remove-Item -Force -Recurse \"$Env:CIRCLE_WORKING_DIRECTORY\\Engine\\Saved\\*\" -ErrorAction SilentlyContinue
Remove-Item -Force -Recurse \"$Env:CIRCLE_WORKING_DIRECTORY\\dist\\\" -ErrorAction SilentlyContinue
[Environment]::Exit(0)
"""
正如您所看到的,对于这两者,我们只使用特定于操作系统的操作来设置类属性和方法。基本的Runner
级将负责重物的提升。
在runners/__init__.py
内添加:
# runners/__init__.py
from .ue_linux_runner import UELinuxRunner
from .ue_windows_runner import UEWindowsRunner
这是必需的,这样我们就可以在主脚本中导入类。
设置工作流程
现在我们有了翻译层,我们可以创建 CircleCI 配置文件来执行我们的管道!
在您的项目中创建.circleci/config.yml
文件,并添加以下内容:
version: 2.1
setup: true
orbs:
continuation: circleci/continuation@0.1.2
jobs:
setup:
machine: true
resource_class: vela-games/your-linux-class
steps:
- checkout
# Here we run BuildGraph only to "Compile" our BuildGraph script and export the JSON Graph
# We then pass it down to our translation layer and redirect the output to a yml file that the continuation orb will send to CircleCI's API
- run: |
/home/ubuntu/UnrealEngine/Engine/Build/BatchFiles/RunUAT.sh BuildGraph -Target=All -Script=$PWD/Tools/BuildGraph.xml -Export=$PWD/ExportedGraph.json
./Tools/buildgraph-to-circleci/buildgraph-to-circleci.py --json-graph $PWD/ExportedGraph.json > /tmp/generated_config.yml
- continuation/continue:
configuration_path: /tmp/generated_config.yml
workflows:
setup:
jobs:
- setup
这将在 Linux 自托管运行程序上运行setup
作业。在其中,我们:
- 运行 BuildGraph,以我们的聚合节点为目标,使用
-Export
参数,我们指示它只将结果图作为 JSON 文件输出。 - 将导出的图形传递给
buildgraph-to-circleci.py
翻译脚本,我们将输出重定向到一个临时文件。 - 使用延续 orb 传递生成的 YAML 工作流。
当工作流程完成时,您将能够从pak
任务的 Artifacts 选项卡下载构建的游戏:
概述
在本教程中,我们演示了如何通过创建一个从 BuildGraph 的 JSON 图形转换到 CircleCI 的 YAML 定义的层来集成 Unreal Engine 的 BuildGraph 自动化系统和 CircleCI,以及如何使用 CircleCI 的自托管 runner 产品作为作业执行者。
正如我们在简介中提到的,与常规的BuildCookRun
脚本相比,这可以实现更快的并行构建。
为此,我们:
- 使用 Terraform 在 AWS 上部署自托管跑步者和 FSx(共享存储解决方案)
- 创建了一个 BuildGraph 脚本来编译、制作和打包 Windows 和 Linux 平台上的游戏
- 创建了一个 Python 转换层,将 BuildGraph JSON 图转换为 CircleCI 工作流定义
- 使用动态配置来动态创建我们的构建工作流
我们希望你可以使用同样的技术,它允许我们减少高达 85%的构建时间,作为你的下一个虚幻引擎项目的构建自动化的基础,或者加速一个现有项目的开发。
我们是谁?
Vela Games 是一个独立的娱乐软件工作室,创造原创 IP 和引人入胜的合作游戏,将玩家放在第一位。总部设在都柏林,由世界一流的人才组成的国际团队,我们正在为世界各地的玩家开发我们的第一个流派定义的多人游戏。
我们正在寻找有才华、有抱负的个人加入我们的团队,帮助我们为即将发布的多人合作竞技游戏 Project-V 构建基础设施。这是一个在一个有趣的协作环境中从事前沿项目和磨练技能的绝佳机会。
优化 OSS 版本| CircleCI
原文:https://circleci.com/blog/optimizing-open-source-projects-on-circleci/
在之前的一篇博客文章中,我写了我如何在 CircleCI 上优化 React Native Camera 的构建。我们从这项工作中了解到,维护人员甚至没有意识到这些优化。我所做的一切都可以在我们的文档中参考,但正如那句老话所说,“你不知道你不知道的。”现在,我们想借此机会推广 CircleCI 上所有项目可用的大量优化。
构建时间非常宝贵,尤其是当你是一个预算有限的 OSS 项目时。我们的文档中有一页是专门针对优化的,但是我们决定写一篇博客文章来强调和介绍使我们的平台与众不同的各种功能。
以下几乎所有的特性都是免费的,任何用户都可以使用,无论是私人项目还是公共项目。高级功能将被如此标记。尽管我很想为更多的项目开放 PRs,但这并不实际。我能做的下一件最好的事情是给每个人配备知识和资源,让他们自己去做。
CircleCI 的优化特性
CircleCI 有许多特性,开发人员可以使用它们来加速构建和部署。
依赖缓存
我们系统中的每项工作都在一个全新的环境中运转。这是为了防止以前的工作和意外行为造成的污染。但这意味着同一作业的后续运行将再次下载相同的依赖项。
为了解决这个问题,我们提供了依赖缓存。您可以缓存项目的依赖项,从而大大加快构建过程。
依赖关系缓存的示例
例如,如果构建一个 Node.js 应用程序,您应该缓存node_modules
。你将缓存存储为一个键,我们有个模板可以用于这个键。
在下面的例子中,我将我的node_modules
文件夹缓存到一个包含package.json
的 SHA256 的键中。这样,如果我的依赖项发生变化,缓存键也会发生变化,随后会为新的依赖项创建一个新的缓存。有些人可能会选择使用他们的package.lock
或yarn.lock
文件的校验和。
version: 2.1
jobs:
my-job:
executor: my-executor
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
- v1-dependencies- # Fallback cache
- run: yarn
- save_cache:
key: v1-dependencies-{{ checksum "package.json" }}
paths:
- node_modules
有关更多信息和示例,请访问我们的 awesome 文档:
与工作区共享文件和工件
正如上一节提到的,我们系统中的每个作业都在一个全新的环境中旋转。为了在工作流中的作业之间共享文件和工件,您可以使用工作区。工作区允许您轻松地将工作从一项工作转移到下一项工作,以避免重复劳动。
跨工作区共享文件的示例
在下面的例子中,我将我的 Go 二进制构建和 Docker 映像构建分离到不同的作业中。为了避免再次克隆存储库和/或重做构建,我已经通过工作区转移了所需的文件。
version: 2.1
jobs:
my-build-job:
executor: my-executor
steps:
- checkout
- run: go build -o APP_NAME cmd/**/*.go
- persist_to_workspace:
root: .
paths:
- Dockerfile
- cmd
- internal
- go.mod
- go.sum
my-docker-job:
executor: my-other-executor
steps:
- attach_workspace:
at: .
- run: docker build -t TAG_NAME .
有关更多信息和示例,请访问我们的 awesome 文档:
重用以前构建的 Docker 映像
特别是,工作区对于在作业之间传输构建的 Docker 图像非常有用。我们的许多客户都在我们的平台上进行 Docker 构建,在某些情况下,他们将构建 Docker 映像和测试分开进行。
您可以通过使用docker save
和docker load
命令传输保存的图像。
重复使用先前构建的 Docker 映像的示例
在本例中,我在第一个作业中构建了 Docker 映像,并在将其保存到工作空间之前将其写入docker-cache/image.tar
。下一个作业连接到那个工作区,然后在运行它和测试套件之前加载映像。
version: 2.1
jobs:
my-build:
executor: my-executor
steps:
- checkout
- setup_remote_docker
- run:
name: Build and Save Docker Image
command: |
docker build -t MY_APP:MY_TAG .
mkdir docker-cache
docker save -o docker-cache/image.tar MY_APP:MY_TAG
- persist_to_workspace:
root: .
paths:
- docker-cache
my-test:
machine: true
steps:
- attach_workspace:
at: .
- run:
name: Load, Run Docker Image and Tests
command: |
docker load < docker-cache/image.tar
docker run -d -p 8080:80 MY_APP:MY_TAG
mvn test -Dhost=http://localhost:8080
创建自定义 Docker 图像执行器
我们的平台提供了各种执行环境,其中之一就是 Docker。我们有针对各种框架和语言的便利图片,对于大多数用例,我们建议使用这些图片作为起点。然而,对于非常高级或复杂的用例,您可以轻松地制作自己的定制 Docker 图像来用作执行者。
通过使用自定义映像,您可以使用所有必需的依赖项和所需的任何其他设置来预烤环境。默认情况下,我们从 Docker Hub 获取图像,但是如果您需要从其他来源获取图像(使用 auth ),您可以在配置中轻松更改。请记住指定完整的 URL:
version: 2.1
jobs:
build:
docker:
- image: quay.io/project/IMAGE:TAG
auth:
username: $QUAY_USER
password: $QUAY_PASS
有关更多信息和示例,请访问我们的 awesome 文档:
测试拆分和并行性
您还可以在我们的平台中使用测试分割和并行。通过拆分测试并并行运行不同的工作负载,您可以极大地减少测试套件的挂钟时间。我们的免费计划中的开源组织在 Linux、Docker、Arm 和 Windows 版本上享有高达 30 倍的并发性,而我们的性能计划客户在所有执行者上享有高达 80 倍的并发性。如果您对您团队的更多资源感兴趣,今天就联系我们。
有关更多信息和示例,请访问我们的 awesome 文档:
可配置资源
默认情况下,所有执行环境都在中等大小上运行,这取决于执行器的类型。然而,我们的平台允许用户灵活地为他们的构建配置不同的资源(vcpu 和 RAM)。
有些工作不需要很多资源,所以你可以选择一个small
Docker 容器来节省计算和信用。但是还有其他工作会受益于一个2xlarge+
集装箱。无论如何,CircleCI 可以配置为满足您的工作负载需求。
使用可配置资源的示例
在这个例子中,我已经指定我的构建使用xlarge
资源类,这意味着我将拥有 8 个 vCPUs 和 16GB RAM。这只是对配置的一行修改。
version: 2.1
jobs:
build:
docker:
- image: circleci/node:10.15.1-browsers
resource_class: xlarge
steps:
# ... steps here
Docker 层缓存
对于构建 Docker 图像的客户,他们可以利用一个叫做 Docker 层缓存的功能。正如您可以缓存应用程序的依赖关系一样,您也可以缓存 Docker 映像层,以加快 Docker 映像的后续构建。
为了最好地利用这个特性,你会希望把不断变化的项目(例如,复制或添加源代码等。)在 Dockerfile 文件的底部附近,最不频繁改变的项目在顶部。
Docker 层缓存示例
使用它就像给 YAML 添加一个额外的键一样简单。如果将 Docker 执行器与远程 Docker 一起使用:
jobs:
build:
executor: my-executor
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: true
# ... other steps here
或者,如果使用我们的machine
执行者:
jobs:
build:
machine: true
docker_layer_caching: true
steps:
# ... steps here
有关更多信息和示例,请访问我们的 awesome 文档:
便利功能
除了优化构建时间的特性之外,我们还有许多便利的特性,有助于提高生产率和改善整体开发人员体验。
可重复使用的配置和 orb(带参数)
</blog/media/2021-01-31-orbs-browser-example.mp4>
一年前,我们推出了 orbs 和可重用配置。使用新的 2.1 配置,您可以在项目内以及跨项目和组织定义可重用的作业、执行者和命令,并进行参数化。这对您来说意味着几件事:
- 您可以定义一次配置并重用它:这对于想要定义或建立共享的执行器、作业和命令,并在 orb 中维护它们作为单一事实来源的团队来说非常好。这些参数允许您将不同的输入插入到相同的作业或命令逻辑中,以获得最大的可重用性。
- 你不必重新发明轮子:举个例子,AWS 是一个广泛使用的云服务提供商。如果你正在部署,比如说, AWS ECS ,你可能会意识到其他人也在做同样的事情。每个人都重写相同的配置是没有意义的,事实证明你不需要。
- 你可以像维护代码一样维护它们:orb 只是打包的 YAML,所以它们可以像其他代码一样被版本化、测试和发布。这意味着整个过程可以自动化和跟踪,作为开发人员,您不必学习任何新的语言或框架来使用 orb。
orb 用法示例
在下面的例子中,我将我的文件部署到 AWS S3。不必编写许多行配置来更新 AWS CLI,只需这样配置,那样做,等等。,我必须只写五行:两行“导入”orb,然后三行使用它(带参数)。
orbs: # "Import" the orb
aws-s3: circleci/aws-s3@2.0.0
jobs:
deploy:
docker:
- image: circleci/python:2.7
steps:
- attach_workspace:
at: .
- aws-s3/sync: # Using said orb
from: .
to: my_awesome_bucket
有关更多信息和示例,请访问我们的 awesome 文档:
数据库和服务
你知道你可以为你的测试旋转数据库吗?还是其他服务形象?在第一个docker
之后指定的任何图像都被视为服务图像。它将在与主执行容器相同的网络中运行。
启动多个服务的示例
在下面的例子中,我正在构建一个 Postgres 数据库,用于在主容器中测试我的应用程序。默认情况下,如果不指定名称,可以通过localhost
访问容器,但是如果指定了名称,也可以使用它。
jobs:
build:
docker:
# Primary Executor
- image: cimg/openjdk:11.0.9
# Dependency Service(s)
- image: postgres:10.8
name: postgres # Can access via postgres:5432
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
如果您正在使用一个完整的machine
执行器,您可以像对待任何其他虚拟机一样简单地对待它,并构建一个 Docker 容器网络。
jobs:
build:
machine: true
steps:
- run: docker-compose up
# OR
- run: docker run -d -p 5432:5432 -e POSTGRES_USER=postgres postgres
- run: docker run -d -p 8080:80 MY_APP:MY_TAG
# ...etc.
有关更多信息和示例,请访问我们的 awesome 文档:
测试总结
</blog/media/2021-01-31-failed-test.mp4>
你在运行测试套件吗?我们有一个测试总结特性,它解析 JUnit XML 格式的文件并存储数据。这使您能够看到您运行了多少测试的摘要,以及它们是否都通过了。如果它们中的任何一个失败了,您将会看到是哪一个,并且它们的输出将会在那里被访问。方便快速地访问重要信息。
测试总结的使用示例
要实现这一点,您需要配置您的测试套件,将结果以 JUnit XML 格式输出到一个文件中。然后你将文件存储在一个文件夹中,并使用store_test_results
YAML 键:
jobs:
build:
executor: my-executor
steps:
# Other build steps
- run:
name: Run TestCafe tests
command: |
yarn runTests -- ${TESTFILES} # Outputs > /tmp/test-results/results.xml
- store_test_results:
path: /tmp/test-results
# ...etc.
有关更多信息和示例,请访问我们的 awesome 文档:
洞察力
当我们收集您所有项目、管道、工作流和作业的所有构建信息和测试摘要时,我们开始跟踪它们每一个运行的时间、频率和成功率。这样,您可以很容易地看到项目管道的哪些部分最有问题,应该首先改进。您可以在 CircleCI 仪表盘的 Insights 选项卡中找到这些信息。
有关 Insights 的更多信息,您可以阅读初始版本和文档:
如果洞察力让你兴奋,你可能也会对我们的 2020 年软件交付状态报告感兴趣,其中我们探索了来自超过 160,000 个软件项目、构建时间以及团队如何合作的信息。
人工制造
CircleCI 的作业每次都在新的、干净的、隔离的执行环境中运行。随后,如果您想让任何文件或构建工件可供外部访问,您可以使用我们的加工特性。工件文件使它们在您的作业详情页面的工件选项卡中可用——顺便提一下,因为我们为此使用 AWS S3,只要引用路径正确,您就可以预览文件,就好像它们是静态托管的一样。
示例工件创建
这个例子和上面的一样:我已经运行了一个测试套件,并将结果输出到/tmp/test-results/results.xml
。然后,我使用store_artifacts
键构建这些文件,它们现在可以在构件选项卡上查看。
jobs:
build:
executor: my-executor
steps:
# Other build steps
- run:
name: Run TestCafe tests
command: |
yarn runTests -- ${TESTFILES}
- store_artifacts:
path: /tmp/test-results
destination: .
# ...etc.
有关更多信息和示例,请访问我们的 awesome 文档:
加入构建
</blog/media/2021-01-31-ssh-into-build.mp4>
测试输出不明显?构建失败的原因你无法确定?你可以在我们的平台上用 SSH 重新运行。这将重新运行相同的构建,但是这次 SSH 端口是打开的。使用与 GitHub 或 Bitbucket 帐户相同的 SSH 密钥,您可以在运行时登录并验证执行环境,以实时调试构建。通过直接访问环境,您可以实际检查环境、tail
日志,或者查看其他项目来研究构建。
相对于在环境之外的反复试验,这对于高效调试来说是非常有用的特性。
有关更多信息和示例,请访问我们的 awesome 文档:
构建分叉的拉请求和传递秘密
开源项目通常需要来自社区开发人员的跨项目分支的协作。默认情况下,CircleCI 只会在 GitHub 上你自己的项目上构建分支,而不会在外部分叉上构建。如果你希望允许跨分支构建拉请求,你可以在你的项目设置中切换——在高级设置标签下。
您的 CircleCI 管道可能还需要秘密来执行某些操作,比如签署二进制包,或者与外部服务交互,例如在部署到云环境时。您也可以在同一个选项卡下切换。
有关更多信息,请参见相关文档页面:
结论
在上面提到的所有功能中,只有三个是高级或半高级的。对于使用 CircleCI 的开发人员来说,有很多功能可以提高构建速度和生产力。我们的用户一次又一次地告诉我们他们有多爱我们的产品,喜欢我们提供的功能;从现在开始,我们只会对体验进行改进。
优化 React 原生相机的构建| CircleCI
原文:https://circleci.com/blog/optimizing-react-native-camera/
上个月,我的一个拉取请求被合并到流行的社区项目 React Native Camera 中,以回应一个维护者的推文。React Native Camera 的构建被阻止,因为他们试图使用自由/OSS 计划中不可用的资源类。除了解除阻塞,我还做了一些额外的优化。
我在这篇 PR 中使用的策略可以被推广并用于优化许多其他项目。在这篇文章中,我将分解我对 React 原生相机项目所做的一些更改,解释它们如何提高性能,并建议您可以使用类似的技术来改进自己的项目。
缓存步骤放置
一般来说,人们习惯于将他们的save_cache
语句放在构建的末尾。React Native Camera 也是这样做的,就像这样:
jobs:
build-app:
# ...environment setup here
steps:
- checkout
- restore_cache:
keys:
- v1-npm-{{ .Branch }}-{{ checksum "yarn.lock" }}
- v1-npm
- run:
name: Install Dependencies
command: yarn install --ignore-engines
# ...other steps, etc.
- save_cache:
key: v1-npm
paths:
- node_modules/
- save_cache:
key: v1-npm-{{ .Branch }}-{{ checksum "yarn.lock" }}
paths:
- node_modules/
# ...rest of config
这不是最佳选择,因为如果作业中的任何其他步骤在save_cache
步骤有机会执行之前失败,将不会保存缓存。最好将save_cache
步骤放在依赖步骤之后——这保证了只要依赖步骤工作,缓存就会被保存:
jobs:
build-app:
# ...environment setup
steps:
- checkout
- restore_cache:
keys:
- v1-npm-{{ .Branch }}-{{ checksum "yarn.lock" }}
- v1-npm
- run:
name: Install Dependencies
command: yarn install --ignore-engines
#### MOVED
- save_cache:
key: v1-npm
paths:
- node_modules/
- save_cache:
key: v1-npm-{{ .Branch }}-{{ checksum "yarn.lock" }}
paths:
- node_modules/
#### /MOVED
# ...rest of config
您可以找到更多关于缓存的信息,并在我们的文档中查看示例。
为 Gradle 添加缓存
我做的另一个改变是为 Gradle 依赖项添加缓存。他们在 CircleCI 中的Run Checks
和Build Sample App
步骤是 Gradle 包装器执行,它们占用了大部分构建时间。在下面的配置中,我存储了 Gradle 缓存和包装器。
jobs:
build-app:
# ...environment setup
steps:
# ...previous build steps
#### ADDED
- restore_cache:
keys:
- v1-gradle-{{ checksum "android/gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "examples/basic/android/gradle/wrapper/gradle-wrapper.properties" }}
- v1-gradle-wrapper
- restore_cache:
keys:
- v1-gradle-cache-{{ checksum "android/build.gradle" }}-{{ checksum "examples/basic/android/build.gradle" }}
- v1-gradle-cache
#### /ADDED
- run:
name: Run Checks
command: |
cd android
chmod +x ./gradlew && ./gradlew check
# ...other step(s)
- run:
name: Run Yarn to Generate react.gradle
command: cd examples/basic/android && yarn
- run:
name: Build Sample App
command: |
cd examples/basic/android && chmod +x ./gradlew && ./gradlew build
#### ADDED
- save_cache:
key: v1-gradle-wrapper-{{ checksum "examples/basic/android/gradle/wrapper/gradle-wrapper.properties" }}
paths:
- ~/.gradle/wrapper
- save_cache:
key: v1-gradle-cache-{{ checksum "examples/basic/android/build.gradle" }}
paths:
- ~/.gradle/caches
#### /ADDED
# ...rest of config
从报表制作工件
您知道我们的工件就像一个静态文件服务器吗?这对于存储测试套件的 HTML 报告和像访问网页一样访问它们是很有用的。我添加了一个工件步骤来存储林挺命令生成的输出。这允许你在工件标签上浏览工件,并像访问网页一样访问那些文件。
jobs:
build-app:
# ...environment setup
steps:
# ...previous build steps
- run:
name: Run Checks
command: |
cd android
chmod +x ./gradlew && ./gradlew check
#### ADDED
- store_artifacts:
path: android/build/reports
#### /ADDED
# ...other step(s)
- run:
name: Build Sample App
command: |
cd examples/basic/android && chmod +x ./gradlew && ./gradlew build
#### ADDED
- store_artifacts:
path: examples/basic/android/app/build/reports
destination: app
#### /ADDED
# ...rest of config
你可以在我们的文档中找到更多关于创建工件的信息。
结论
在这个 pull 请求中所做的小改动只是您可以用来优化 CircleCI 构建的一些特性的例子。我们平台的文档内容丰富,包含各种示例和教程。T2 的 CircleCI 博客也是一个很好的资源。如果你被困住了,试着发微博给我们 @CircleCI !
要了解更多优化特性和技术,请阅读我们在 CircleCI 上发布的优化开源项目。
感谢阅读,并快乐建设!
使用 CircleCI AWS ECR orb 构建 CI/CD 管道
CircleCI 最近发布了一款名为 orbs 的新产品,旨在让你在 CircleCI 上快速启动并运行。现在,您可以轻松地将您的 DevOps 工具与我们的技术合作伙伴提供的可信 orb 相集成。
在这篇文章中,我将演示和解释 AWS ECR Orb 及其在 CircleCI 配置文件中的用法,利用 CircleCI 工作流,并将图像推送到 Docker Hub 和指定的 AWS ECR。
什么是 CircleCI 球体?
orb 是 CircleCI 配置的包,可以跨项目共享。orb 允许您创建一个作业、命令和执行器的捆绑包,它们可以相互引用,可以导入到 CircleCI 构建配置中,并在它们自己的名称空间中调用。orb 在 CircleCI 注册,使用 semver 模式表示修订。CircleCI orbs 由 CircleCI Orbs Registry 托管和维护,该注册中心是 CircleCI Orbs 的集中存储库。
先决条件
在你开始之前,你需要有这些东西:
完成所有先决条件后,就可以继续下一部分了。
配置 CircleCI 项目环境变量
我们项目的 CI/CD 管道将 Docker 映像部署到多个容器存储库,因此您需要在 CircleCI 项目设置中设置一些环境变量:
- 点击左侧菜单中 CircleCI 仪表板上的添加项目
- 在项目列表中找到并点击项目名称,点击右侧的设置项目
- 点击右上方 CircleCI 仪表盘中的项目重心按钮
- 在构建设置部分,点击环境变量
- 点击添加变量
在Add an Environment Variable
对话框中,你将定义这个构建所需的几个环境变量。下面是必须定义的环境变量列表:
- 名称:
AWS_ECR_ACCOUNT_URL
值:您的 AWS ECR 注册表 URL - 名称:
AWS_ACCESS_KEY_ID
值:Your AWS IAM Account's Access Key ID
- 名称:
AWS_SECRET_ACCESS_KEY
值:Your AWS IAM Account's Secret Access Key
- 名称:
DOCKER_LOGIN
值:Your Docker Hub User Name
- 名称:
Docker_PWD
值:Your Docker Hub Password
正确设置这些环境变量对于构建成功完成至关重要。在继续下一节之前,请确保正确设置了这些变量及其值。
使用 CircleCI AWS ECR Orb
下面是定义这个项目管道的config.yml
文件。在接下来的部分中,我将解释这个config.yml
文件的各种元素。
version: 2.1
orbs:
aws-ecr: circleci/aws-ecr@0.0.4
workflows:
build_test_deploy:
jobs:
- build_test
- docker_hub_build_push_image:
requires:
- build_test
- aws-ecr/build_and_push_image:
region: us-east-1
account-url: ${AWS_ECR_ACCOUNT_URL}
repo: ${CIRCLE_PROJECT_REPONAME}
tag: ${CIRCLE_BUILD_NUM}
requires:
- build_test
jobs:
build_test:
docker:
- image: circleci/python:2.7.14
steps:
- checkout
- run:
name: Setup VirtualEnv
command: |
virtualenv helloworld
. helloworld/bin/activate
pip install --no-cache-dir -r requirements.txt
- run:
name: Run Tests
command: |
. helloworld/bin/activate
python test_hello_world.py
docker_hub_build_push_image:
docker:
- image: circleci/python:2.7.14
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build and push Docker image to Docker Hub
command: |
echo 'export TAG=0.1.${CIRCLE_BUILD_NUM}' >> ${BASH_ENV}
echo 'export IMAGE_NAME=${CIRCLE_PROJECT_REPONAME}' >> ${BASH_ENV}
source ${BASH_ENV}
docker build -t ${DOCKER_LOGIN}/${IMAGE_NAME} -t ${DOCKER_LOGIN}/${IMAGE_NAME}:${TAG} .
echo ${DOCKER_PWD} | docker login -u ${DOCKER_LOGIN} --password-stdin
docker push ${DOCKER_LOGIN}/${IMAGE_NAME}
指定工作流和 orb
orbs:
aws-ecr: circleci/aws-ecr@0.0.4
orbs:
键指定一个 orb 将在这个管道中使用。aws-ecr:
键定义了配置中使用的内部名称。circleci/aws-ecr@0.0.4
值指定并关联由aws-ecr:
键使用和引用的实际 orb。这些 orb 语句可以被认为是其他语言和框架中的 import 语句。
workflows:
build_test_deploy:
jobs:
- build_test
- docker_hub_build_push_image:
requires:
- build_test
- aws-ecr/build_and_push_image:
region: us-east-1
account-url: ${AWS_ECR_ACCOUNT_URL}
repo: ${CIRCLE_PROJECT_REPONAME}
tag: ${CIRCLE_BUILD_NUM}
requires:
- build_test
工作流定义
workflows:
键指定了由构建作业和 orb 组成的工作流列表。该段指定了一个称为build_test_deploy:
的工作流
jobs:
- build_test
- docker_hub_build_push_image:
requires:
- build_test
在这个片段中,指定了一个jobs:
键,列出了在这个管道中执行的所有作业和 orb。该列表中的第一个作业是build_test
,它没有作业依赖性。
Orb 定义
工作流列表中的下一个任务docker_hub_build_push_image:
指的是稍后将讨论的任务,但是请注意requires:
键,它指定docker_hub_build_push_image:
任务依赖于build_test
任务的通过。否则,整个构建将会失败,并且工作流中的剩余作业将不会执行。
- aws-ecr/build_and_push_image:
region: us-east-1
account-url: ${AWS_ECR_ACCOUNT_URL}
repo: ${CIRCLE_PROJECT_REPONAME}
tag: ${CIRCLE_BUILD_NUM}
requires:
- build_test
上面的部分显示了指定 AWS ECR Orb 执行的aws-ecr/build_and_push_image:
键。在本例中,AWS ECR Orb 的参数需要分配给内置环境变量的值。有关此 Orb 的更多详细信息,请参见 AWS ECR Orb 文档。这个特定的 AWS ERC Orb 依赖于在执行这个 Orb 的build_and_push_image
方法之前成功完成的build_test:
作业。
作业定义
我已经讨论了配置中的workflows:
和orbs:
元素,它们是对该配置的主jobs:
元素中定义的作业的引用。以下代码段显示了在此配置语法中定义的所有作业。
jobs:
build_test:
docker:
- image: circleci/python:2.7.14
steps:
- checkout
- run:
name: Setup VirtualEnv
command: |
virtualenv helloworld
. helloworld/bin/activate
pip install --no-cache-dir -r requirements.txt
- run:
name: Run Tests
command: |
. helloworld/bin/activate
python test_hello_world.py
docker_hub_build_push_image:
docker:
- image: circleci/python:2.7.14
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build and push Docker image to Docker Hub
command: |
echo 'export TAG=0.1.${CIRCLE_BUILD_NUM}' >> ${BASH_ENV}
echo 'export IMAGE_NAME=${CIRCLE_PROJECT_REPONAME}' >> ${BASH_ENV}
source ${BASH_ENV}
docker build -t ${DOCKER_LOGIN}/${IMAGE_NAME} -t ${DOCKER_LOGIN}/${IMAGE_NAME}:${TAG} .
echo ${DOCKER_PWD} | docker login -u ${DOCKER_LOGIN} --password-stdin
docker push ${DOCKER_LOGIN}/${IMAGE_NAME}
在上面的片段中,定义了两个作业build_test:
和docker_hub_build_push-image:
,演示了原始配置语法。build_test:
作业实例化一个 Python 容器,安装应用程序依赖项,并运行应用程序的单元测试。
docker_hub_build_push_image:
作业设置用于命名和标记图像的环境变量,基于 Docker 文件构建 Docker 图像,并将构建的图像推送到 Docker Hub。
这整个配置演示了如何定义和实现工作流、orb 和作业,它们在配置语法上提供了健壮和强大的功能。成功执行这个管道后,您应该在 Docker Hub 和 AWS ECR 存储库中得到一个经过测试的 Docker 映像。
摘要
如您所见,与 AWS ECR Orb 定义相比,jobs:
段更加冗长。orb 旨在封装功能,提供一致性并减少管道配置中的重复代码量,从而减少冗长的配置语法。
使用 orbs,您可以在团队和项目之间共享您首选的 CI/CD 设置,并且只需几行代码就可以轻松集成工具和第三方解决方案。开发社区的成员可以编写 orb 来解决常见问题并帮助管理配置。在常见用例中共享和重用 orb 使团队能够解决有趣的、独特的问题,使他们的业务与众不同,同时为社区中的其他人提供加速 CI/CD 工作流的解决方案。
我期待 orb 的未来发展,以及它们给 CI/CD 管道带来的令人敬畏的流线型功能。如果您浏览 orb 注册表,没有找到符合您需要的功能的 orb,那么我鼓励您创作您自己的定制 orb 。请注意,目前 CircleCI Orb 注册表上托管的所有 Orb 都是开放的,可供公众使用。注册中心目前还不支持私有 orb,但是这个特性已经在 orb 的路线图上了,将来会提供。
参考
要了解有关 orb、工作流和 CircleCI 的更多信息,请使用下列参考资料:
我们的云平台,您的计算:CircleCI runner 简介
原文:https://circleci.com/blog/our-cloud-platform-your-compute-introducing-the-circleci-runner/
从今天开始,circle ci runner将让我们新规模计划的客户能够选择哪些作业在云中运行,哪些作业在他们自己的基础设施上运行。
我们为什么要造转轮?
我们的目标一直是建立一个平台,让开发者做他们最擅长的事情:创造伟大的东西。首先是减轻为持续集成和部署而维护基础设施的负担。
在 CircleCI 之前,工程团队必须担心保持构建机器运行、清理依赖关系、更新插件、维护映像等等。在过去的九年里,我们一直在不断改进我们的云托管计算选项,消除了所有这些开销。
尽管在云中使用我们的机队速度很快,也很容易,但在一些边缘情况下,它不能满足我们客户的所有需求。例如,我们在金融和医疗保健等监管更严格的行业中的一些大客户必须满足合规性要求,以防止他们在云中运行某些工作负载。其他从事嵌入式系统或物联网工作的人需要在云中根本不存在的硬件上构建。我们构建了 runner 来满足这些要求,因此即使是具有最严格的安全性和合规性要求的客户也可以使用 CircleCI 来满足他们 100%的软件交付需求。
跑步者如何工作
我们对 runner 的目标是为我们的用户提供最大的灵活性,因此我们将其构建为可以在任何云或内部的任何环境中运行,无论您的网络是如何设计的。runner 软件本身是一个简单的应用程序,您可以将其安装在自托管的机器上。一旦设置好了,你给跑步者分配一个名字,称为resource_class
,它就在 CircleCI 服务中轮询工作。当一个作业正在进行时,runner 报告状态并记录回 CircleCI,这些显示在 UI 中,就像其他任何作业一样。
通过我们的云托管计算无缝使用 runner
runner 提供了额外的灵活性和控制,但它也带来了管理基础架构的额外责任,这是我们努力为客户消除的。尽管在使用 runner 时会有一些维护开销,但是我们希望确保您的团队只在绝对必要的时候管理基础设施。我们确保使用 runner 的作业与使用我们的云托管计算的作业无缝协作。您可以像现在一样继续使用我们的托管环境,但现在您可以使用 runner 将之前因安全限制或计算要求而受阻的工作负载添加到您现有的管道和工作流中。您甚至可以使用工作区在自托管作业和云托管作业之间共享数据。
规模计划
自从我们推出性能计划(我们的第一个基于使用的定价计划)以来,已经过去了一年多。在这段时间里,我们看到了平台使用的巨大增长,我们认识到使用 CircleCI 的最大和最复杂的组织需要一个比性能更先进和更灵活的计划。
对于这些组织,我们很高兴推出规模计划。Scale 提供对我们的云托管平台的无限制访问,但具有额外的控制,有助于应对大规模运营 CI/CD 的独特挑战。该计划为客户提供自托管运行程序、无限并发机器、包括 GPU 在内的我们最大的资源类别,并包括金牌支持,以确保客户在 CircleCI 的投资获得最大回报。
下一步是什么?
CircleCI runner 和我们的新规模计划是我们领先的云托管 CI/CD 平台的最新成员,也是我们满足企业软件组织独特需求的第一步。在接下来的几个月中,我们将推出更多功能,使使用 CircleCI 大规模操作 CI/CD 管道变得更加容易。我们最近还收到了 SOC 2 Type II 报告,让我们的企业客户对 CircleCI 平台更有信心。
如果使用 CircleCI 跑步者听起来像是你的团队的正确解决方案,你可以在这里找到更多信息。
Clojure Web 框架使用 Docker - Clojure web 开发| CircleCI
原文:https://circleci.com/blog/package-a-clojure-web-application-using-docker/
这是关于构建、测试和部署 Clojure web 应用程序的系列文章的第二篇。你可以在这里找到第一帖,在这里找到第三帖。
在这篇文章中,我们将关注如何向应用程序添加生产数据库(在本例中是 PostgreSQL ),如何将应用程序打包为 Docker 实例,以及如何在 Docker 中运行应用程序和数据库。为了跟进,我建议浏览第一篇文章,并按照步骤创建应用程序。否则,您可以通过分叉这个存储库并检查主分支来获得源代码。如果你选择这种方法,你还需要按照第一篇文章中的描述设置你的 CircleCI 账户。
尽管我们正在构建一个 Clojure 应用程序,但是本系列的这一部分并不需要太多 Clojure 知识。
先决条件
为了构建这个 web 应用程序,您需要安装以下软件:
- Java JDK 8 或更高版本——clo jure 运行在 Java 虚拟机上,实际上只是一个 Java 库(JAR)。我用版本 8 构建了这个,但是一个更好的版本应该也可以。
- Leiningen - Leiningen,通常被称为 lein(读作‘line’)是最常用的 Clojure 构建工具。
- Git -无处不在的分布式版本控制工具。
- Docker——一个工具,旨在通过使用容器来简化应用程序的创建、部署和运行。
- Docker Compose -一个定义和运行多容器 Docker 应用程序的工具。
您还需要注册:
- CircleCI 账号 - CircleCI 是一个持续集成和交付平台。
- GitHub 账户 - GitHub 是一个基于网络的托管服务,使用 Git 进行版本控制。
- Docker Hub 帐户 - Docker Hub 是一个基于云的存储库,Docker 用户和合作伙伴可以在其中创建、测试、存储和分发容器映像。
运行 PostgreSQL 数据库
在本节中,我们将介绍如何运行 PostgreSQL 数据库,我们将从本博客系列的第一部分中构建的 web 应用程序连接到该数据库。
我们将使用 Docker 来“打包”我们的应用程序以进行部署。已经有很多关于为什么以及如何使用 Docker 的文章,比我计划在这里讨论的还要详细。我之所以决定使用它,是为了提供与我们要部署到的物理机器的一定程度的隔离,我认为,更重要的是,为了确保应用程序在本地或远程环境之间运行时的运行时行为的一致性。
出于这个原因,最终的游戏将是运行我们在上一篇博客中构建的 web 应用程序和 Docker 中的 PostgreSQL 数据库,并让两者进行通信。
该应用程序当前在开发模式下运行时使用 SQLite。在博客系列的第一部分中,我们只在开发模式下运行,要么使用lein repl
从 REPL 运行服务器,要么使用lein test
运行单元测试。如果我们通过从我们的项目目录中发出lein run
命令,尝试在生产模式下运行应用程序,我们将得到一个错误,因为没有指定生产数据库连接。
$ lein run
Exception in thread "main" clojure.lang.ExceptionInfo: Error on key :duct.database.sql/hikaricp when building system {:reason :integrant.core/build-threw-exception ...
我们将使用官方的 postgres Docker 映像 (alpine 版本)在 Docker 容器中运行数据库。为此,我们可以发出以下命令:
$ docker run -p 5432:5432 -e POSTGRES_USER=filmuser -e POSTGRES_DB=filmdb -e POSTGRES_PASSWORD=password postgres:alpine
...
2019-01-20 10:08:32.064 UTC [1] LOG: database system is ready to accept connections
这个命令运行 postgres Docker 镜像(如果需要,从 Docker Hub 中下载),数据库监听 TCP 端口 5432,设置一个名为filmuser
的默认用户,将该用户的密码设置为password
,并创建一个名为filmdb
的空数据库。如果您已经将 PostgreSQL 作为一项服务安装在您的计算机上,您可能会收到一条关于端口 5432 正在使用的消息。如果发生这种情况,要么停止本地 PostgreSQL 服务,要么更改-p 5432:5432
条目以公开不同的端口,例如端口 5500 -p 5500:5432
。
为了检查您是否可以连接到数据库,请在不同的终端窗口中发出以下命令:
psql -h localhost -p 5432 -U filmuser filmdb
Password for user filmuser:
psql (11.1 (Ubuntu 11.1-1.pgdg16.04+1))
Type "help" for help.
filmdb=#
虽然您现在已经连接到了数据库,但是此时您还不能做很多事情,因为我们还没有创建任何表、视图等(关系)。
filmdb=# \d
Did not find any relations.
所以让我们关闭 psql 实用程序。
filmdb=# exit
接下来,让 postgres 的 Docker 容器保持运行,并更改我们的应用程序,使其具有可以连接到数据库的生产配置。
打开电影分级项目目录中的resources/film_ratings/config.edn
文件。然后找到:duct.module/sql
条目,并在它下面添加以下内容:
:duct.database.sql/hikaricp {:adapter "postgresql"
:port-number #duct/env [ "DB_PORT" :or "5432" ]
:server-name #duct/env [ "DB_HOST" ]
:database-name "filmdb"
:username "filmuser"
:password #duct/env [ "DB_PASSWORD" ]}
此条目使用 PostgreSQL 定义了光连接池的配置。注意,我们从环境变量 DB_HOST
和DB_PASSWORD
中获取服务器名称和密码。我们还考虑了一个可选的环境变量DB_PORT
,但是如果需要的话,可以用来设置应用程序连接到不同的端口而不是5432
。
您还需要在project.clj
文件中添加 PostgreSQL 数据库驱动程序和 hikaricp 库的依赖项,因此依赖项部分如下所示:
:dependencies [[org.clojure/clojure "1.9.0"]
[duct/core "0.6.2"]
[duct/module.logging "0.3.1"]
[duct/module.web "0.6.4"]
[duct/module.ataraxy "0.2.0"]
[duct/module.sql "0.4.2"]
[org.xerial/sqlite-jdbc "3.21.0.1"]
[org.postgresql/postgresql "42.1.4"]
[duct/database.sql.hikaricp "0.3.3"]
[hiccup "1.0.5"]]
此外,我们希望在插入一部新电影时,id
列被自动分配一个唯一的编号,因此我们需要稍微改变一下迁移,以便id
列的类型不再是整数(适用于 SQLite ),而是 PostgreSQL 中的类型serial
。这意味着您需要将resources/film_ratings/config.edn
中的 migrator ragtime 条目更改为:
[:duct.migrator.ragtime/sql :film-ratings.migrations/create-film]
{:up ["CREATE TABLE film (id SERIAL PRIMARY KEY, name TEXT UNIQUE, description TEXT, rating INTEGER)"]
:down ["DROP TABLE film"]}
为了测试这个配置,首先需要设置环境变量。因此,从运行 Docker postgres 实例的终端窗口中打开一个单独的终端窗口,并像这样设置两个环境变量:
$ export DB_HOST=localhost
$ export DB_PASSWORD=password
注意 : 如果您必须更改 postgres Docker 实例正在使用的端口号,您还需要将DB_PORT
环境变量设置为相同的端口号。
一旦您设置了这些环境变量,您就可以在生产配置文件中运行应用程序,如下所示(首先将目录更改为您的项目根目录):
$ lein run
lein run
19-01-21 07:19:51 chris-XPS-13-9370 REPORT [duct.server.http.jetty:13] - :duct.server.http.jetty/starting-server {:port 3000}
正如您从输出中看到的,我们的迁移(在博客的第一部分中定义)并不是为了插入film
表而运行的。默认情况下,迁移不是通过生产配置文件中的管道运行的,但是我们将在以后解决这个问题。为了创建film
表,我们可以通过打开另一个终端会话并执行以下命令来手动运行我们的迁移(在设置环境变量并将目录更改为项目根目录之后):
$ lein run :duct/migrator
19-01-21 07:48:59 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["CREATE TABLE ragtime_migrations (id varchar(255), created_at varchar(32))"], :elapsed 4}
19-01-21 07:48:59 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["SELECT id FROM ragtime_migrations ORDER BY created_at"], :elapsed 6}
19-01-21 07:48:59 chris-XPS-13-9370 REPORT [duct.migrator.ragtime:14] - :duct.migrator.ragtime/applying :film-ratings.migrations/create-film#11693a5d
19-01-21 07:48:59 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["CREATE TABLE film (id SERIAL PRIMARY KEY, name TEXT UNIQUE, description TEXT, rating INTEGER)"], :elapsed 10}
19-01-21 07:48:59 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["INSERT INTO ragtime_migrations ( id, created_at ) VALUES ( ?, ? )" ":film-ratings.migrations/create-film#11693a5d" "2019-01-21T07:48:59.960"], :elapsed 4}
19-01-21 07:48:59 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:31] - :duct.database.sql/batch-query {:queries [], :elapsed 0}
您现在可以通过打开浏览器并指向http://localhost:3000
来测试正在运行的应用程序。
如果您尝试添加电影,您将看到一条错误消息,指出列rating
是整数类型,但提供的值是字符类型。
这是因为 add film 表单中的所有值都是字符串,但是film
表中的ratings
列需要一个INTEGER
。在我们的开发模式下不会发生这种情况,因为 SQLite 数据库驱动程序将评级的字符串值强制转换为整数,但 PostgreSQL 驱动程序不会。这说明使用不同的数据库进行生产和开发可能会有问题!
现在,让我们修复处理程序中的这个错误。编辑src/film_ratings/handler/film.clj
文件,将处理电影形式的代码重构为一个单独的函数,该函数还处理将ratings
强制为整数的操作:
(defn- film-form->film
[film-form]
(as-> film-form film
(dissoc film "__anti-forgery-token")
(reduce-kv (fn [m k v] (assoc m (keyword k) v))
{}
film)
(update film :rating #(Integer/parseInt %))))
(defmethod ig/init-key :film-ratings.handler.film/create [_ {:keys [db]}]
(fn [{[_ film-form] :ataraxy/result :as request}]
(let [film (film-form->film film-form)
result (boundary.film/create-film db film)
alerts (if (:id result)
{:messages ["Film added"]}
result)]
[::response/ok (views.film/film-view film alerts)])))
为了测试这一点,停止运行应用程序的服务器并重新启动它(CTRL-C 停止,lein run
重新启动)。然后将您的浏览器指向http://localhost:3000
并试用该应用程序。
一旦您对一切正常感到满意,现在您可以使用 CTRL-C 关闭服务器,并通过在运行 Docker 实例的终端会话中发出 CTRL-C 来关闭 Docker postgres 实例。
为了确保我们没有破坏任何东西,我们可以尝试在开发模式下运行服务器,这应该使用 SQLite 数据库实例。
$ lein repl
lein repl
nREPL server started on port 38553 on host 127.0.0.1 - nrepl://127.0.0.1:38553
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.9.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_191-b12
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
user=> (dev)
:loaded
dev=> (go)
Jan 20, 2019 12:16:20 PM org.postgresql.Driver connect
SEVERE: Connection error:
org.postgresql.util.PSQLException: Connection to localhost:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
我们的配置应用方式有问题。这里不使用dev/resources/dev.edn
中的配置:
:duct.module/sql
{:database-url "jdbc:sqlite:db/dev.sqlite"}
我们似乎在resources/config.edn
中至少获得了一些 postgres 配置。实际发生的是开发配置和生产配置映射被合并。因此,postgres 适配器配置和其余的键-值对被添加到:database-url
中。我们需要解决这个问题,所以让我们在dev/src/dev.clj
文件中添加一些代码来删除这些生产数据库属性。让我们添加一个函数来完成这项工作,并从对set-prep!
的调用中调用这个新函数:
(defn remove-prod-database-attributes
"The prepared config is a merge of dev and prod config and the prod attributes for
everything except :jdbc-url need to be dropped or the sqlite db is
configured with postgres attributes"
[config]
(update config :duct.database.sql/hikaricp
(fn [db-config] (->> (find db-config :jdbc-url) (apply hash-map)))))
(integrant.repl/set-prep!
(comp remove-prod-database-attributes duct/prep read-config))
现在,如果我们像这样在开发模式下运行服务器(使用quit
退出之前运行的 repl):
$ lein repl
nREPL server started on port 44721 on host 127.0.0.1 - nrepl://127.0.0.1:44721
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.9.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_191-b12
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
user=> (dev)
:loaded
dev=> (go)
:duct.server.http.jetty/starting-server {:port 3000}
:initiated
dev=>
注意: 如果你克隆了我的回购协议,而不是按照之前的博客,你可能会看到这样的错误:SQLException path to 'db/dev.sqlite': '.../blog-film-ratings/db' does not
exist org.sqlite.core.CoreConnection.open (CoreConnection.java:192)
。在这种情况下,使用mkdir db
在项目根目录下手动创建一个空的‘db’目录,然后重试 repl 命令。
我们可以看到服务器现在成功启动,并连接到 SQLite 数据库。现在,让我们提交我们的更改。
$ git add --all .
$ git commit -m "Add production database config, dependencies & fix dev db config"
$ git push
在 Docker 中运行我们的应用程序
学习了如何在 Docker 中运行 PostgreSQL 数据库,以及如何从我们的应用程序连接到它,下一步是在 Docker 中运行我们的应用程序。
我们将添加一个 Docker 文件以在 Docker 中构建我们的应用程序,并且我们将使用 Docker Compose 在它们自己的网络中同时运行我们的应用程序 Docker 实例和 postgres Docker 实例。
在我们创建 docker 文件之前,让我们检查一下我们的 web 应用程序将如何在 docker 文件中实际运行。到目前为止,我们一直使用 repl 在开发模式下运行我们的应用程序,或者使用 lein 在生产模式下运行我们的应用程序。
通常,Clojure 应用程序在生产中的运行方式与 Java 应用程序相同。最常见的是将应用程序编译成。类文件,然后将它们打包在一个 Uberjar 中,这是一个包含所有。我们的应用程序所需的类文件和所有依赖库(jar 文件)以及任何资源文件(例如,我们的配置文件)。然后,这个 Uberjar 将使用 JVM (Java 虚拟机)运行。
在我们必须在 Docker 内部运行应用程序之前,让我们尝试在本地以这种方式运行我们的应用程序。首先,我们可以使用以下命令在 Uberjar 中编译和打包我们的应用程序:
$ lein uberjar
Compiling film-ratings.main
Compiling film-ratings.views.template
Compiling film-ratings.views.film
Compiling film-ratings.views.index
Compiling film-ratings.boundary.film
Compiling film-ratings.handler.film
Compiling film-ratings.handler.index
Created /home/chris/circleciblogs/repos/film-ratings/target/film-ratings-0.1.0-SNAPSHOT.jar
Created /home/chris/circleciblogs/repos/film-ratings/target/film-ratings.jar
这将创建两个 JAR 文件。标记为快照的那个只包含没有库依赖关系的应用程序,而名为film-ratings.jar
的那个是我们的 Uberjar,包含所有的依赖关系。我们现在可以从这个 Uberjar 运行我们的应用程序,但是首先要确保 postgres 的 Docker 实例正在运行,并且在发出这个命令之前,已经在终端会话中设置了DB_HOST
和DB_PASSWORD
环境变量:
$ java -jar target/film-ratings.jar
19-01-22 07:26:20 chris-XPS-13-9370 REPORT [duct.server.http.jetty:13] - :duct.server.http.jetty/starting-server {:port 3000}
我们现在需要做的是编写一个 docker 文件来执行这个命令。这意味着我们需要 Docker 实例拥有一个 Java 运行时环境(在这种情况下,我们实际上使用的是 Java 开发工具包环境)。在项目基本目录中创建 Dockerfile 文件,并添加以下内容:
FROM openjdk:8u181-alpine3.8
WORKDIR /
COPY target/film-ratings.jar film-ratings.jar
EXPOSE 3000
CMD java -jar film-ratings.jar
您现在可以像这样构建 Docker 实例:
$ docker build . -t film-ratings-app
Sending build context to Docker daemon 23.71MB
Step 1/5 : FROM openjdk:8u181-alpine3.8
---> 04060a9dfc39
Step 2/5 : WORKDIR /
---> Using cache
---> 2752489e606e
Step 3/5 : COPY target/film-ratings.jar film-ratings.jar
---> Using cache
---> b282e93eff39
Step 4/5 : EXPOSE 3000
---> Using cache
---> 15d2e1b9197e
Step 5/5 : CMD java -jar film-ratings.jar
---> Using cache
---> 2fe0b1e058e5
Successfully built 2fe0b1e058e5
Successfully tagged film-ratings-app:latest
这创建了一个新的 Docker 图像,并将其标记为film-ratings-app:latest
。我们现在可以像这样运行我们的 dockerized 应用程序:
$ docker run --network host -e DB_HOST=localhost -e DB_PASSWORD=password film-ratings-app
19-01-22 09:12:20 chris-XPS-13-9370 REPORT [duct.server.http.jetty:13] - :duct.server.http.jetty/starting-server {:port 3000}
但是,我们仍然存在之前遇到的问题,即迁移没有运行。如果您打开浏览器进入http://localhost:3000/list-films
,您可以演示这一点。您会看到内部服务器错误,并且会在 Docker 中运行应用程序的终端会话中看到一个巨大的堆栈跟踪,该跟踪以以下内容结束:
serverErrorMessage: #object[org.postgresql.util.ServerErrorMessage 0x5661cc86 "ERROR: relation \"film\" does not exist\n Position: 15"]
为了解决这个问题,我们将做一些实际上不建议可伸缩生产服务器做的事情,让迁移在应用程序启动时运行。更好的方法是在生产环境中有一个单独的 Docker 实例,它可以按需启动,可能是通过 CI 管道,在发生变化时运行迁移。为了这篇博客的目的,让我们采用更简单的方法,将我们的主函数改为调用迁移器。打开并编辑src/film_ratings/main.clj
文件,如下所示:
(defn -main [& args]
(let [keys (or (duct/parse-keys args) [:duct/migrator :duct/daemon])]
(-> (duct/read-config (io/resource "film_ratings/config.edn"))
(duct/prep keys)
(duct/exec keys))))
这确保了在守护程序启动服务器之前,调用:duct\migrator
Integrant 键来运行迁移。为了将这一变化加入到我们的 Docker 映像中,我们需要重新运行lein uberjar
和docker build . -t film-ratings-app
。然后我们可以在 Docker 中运行我们的应用程序:
$ docker run --network=host -e DB_HOST=localhost -e DB_PASSWORD=password film-ratings-app
19-01-22 18:08:23 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["CREATE TABLE ragtime_migrations (id varchar(255), created_at varchar(32))"], :elapsed 2}
19-01-22 18:08:23 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["SELECT id FROM ragtime_migrations ORDER BY created_at"], :elapsed 11}
19-01-22 18:08:23 chris-XPS-13-9370 REPORT [duct.migrator.ragtime:14] - :duct.migrator.ragtime/applying :film-ratings.migrations/create-film#11693a5d
19-01-22 18:08:23 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["CREATE TABLE film (id SERIAL PRIMARY KEY, name TEXT UNIQUE, description TEXT, rating INTEGER)"], :elapsed 13}
19-01-22 18:08:23 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["INSERT INTO ragtime_migrations ( id, created_at ) VALUES ( ?, ? )" ":film-ratings.migrations/create-film#11693a5d" "2019-01-22T18:08:23.146"], :elapsed 3}
19-01-22 18:08:23 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:31] - :duct.database.sql/batch-query {:queries [], :elapsed 0}
19-01-22 18:08:23 chris-XPS-13-9370 REPORT [duct.server.http.jetty:13] - :duct.server.http.jetty/starting-server {:port 3000}
这一次,我们可以看到在服务器开始添加film
表之前,迁移正在运行。现在,如果我们打开指向http://localhost:3000/list-films
的浏览器,我们会看到“找不到电影”消息。您可以通过添加一些电影来试用该应用程序。
让我们在继续之前提交这些更改。
$ git add --all .
$ git commit -m "Add Dockerfile & call migrations on startup."
$ git push
持久数据
我们还有一些问题。目前,如果我们停止 postgres Docker 容器进程并重新启动它,我们将丢失所有的电影。此外,我们通过主机网络在两个 Docker 容器之间进行通信。这意味着如果您在本地运行 PostgreSQL 服务器,您将会遇到端口冲突。
让我们通过创建 Docker 合成文件来解决这两个问题。我们的 Docker Compose 文件将构建我们的应用程序 Docker 文件,设置适当的环境变量并启动 postgres 实例。Docker Compose 将通过桥接网络促进应用程序和数据库之间的通信,这样我们就不会在本地主机上发生端口冲突,除了应用程序暴露的端口 3000。
在根项目目录中创建一个docker-compose.yml
文件,并添加以下内容:
version: '3.0'
services:
postgres:
restart: 'always'
environment:
- "POSTGRES_USER=filmuser"
- "POSTGRES_DB=filmdb"
- "POSTGRES_PASSWORD=${DB_PASSWORD}"
volumes:
- /tmp/postgresdata:/var/lib/postgresql/data
image: 'postgres:alpine'
filmapp:
restart: 'always'
ports:
- '3000:3000'
environment:
- "DB_PASSWORD=${DB_PASSWORD}"
- "DB_HOST=postgres"
build:
context: .
dockerfile: Dockerfile
该文件在 docker-compose 中注册了两个服务:一个名为 postgres,它使用postgres:alpine
映像并设置适当的 postgres 环境变量;另一个名为filmapp
,它构建我们的 Dockerfile,运行它并公开端口 3000 并设置它的环境变量。
您还可以看到,我们已经定义了一个卷,该卷将本地机器上的目录/tmp/postgresdata
映射到容器上的/var/lib/postgresql/data
,该容器是 postgres 的数据目录。
这意味着,当我们运行 docker-compose 进程时,存储在数据库中的任何数据都将被写入本地的/tmp/postgresdata
,甚至在我们重新启动 docker-compose 进程后,这些数据也将持续存在。
让我们试试这个。首先,我们构建 docker-compose 图像(确保您已经首先设置了DB_PASSWORD
环境变量)。
$ docker-compose build
postgres uses an image, skipping
Building filmapp
Step 1/5 : FROM openjdk:8u181-alpine3.8
---> 04060a9dfc39
Step 2/5 : WORKDIR /
---> Using cache
---> 2752489e606e
Step 3/5 : COPY target/film-ratings.jar film-ratings.jar
---> b855626e4a45
Step 4/5 : EXPOSE 3000
---> Running in 7721d74eee62
Removing intermediate container 7721d74eee62
---> f7caccf63c3b
Step 5/5 : CMD java -jar film-ratings.jar
---> Running in 89b75d045897
Removing intermediate container 89b75d045897
---> 48303637af01
Successfully built 48303637af01
Successfully tagged film-ratings_filmapp:latest
现在,让我们启动 docker-compose(首先退出任何正在运行的 Docker postgres 或 filmapp 实例)。
$ docker-compose up
Starting film-ratings_filmapp_1 ... done
Starting film-ratings_postgres_1 ... done
Attaching to film-ratings_filmapp_1, film-ratings_postgres_1
...
postgres_1 | 2019-01-22 18:35:58.117 UTC [1] LOG: database system is ready to accept connections
filmapp_1 | 19-01-22 18:36:06 5d9729ccfdd0 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["CREATE TABLE ragtime_migrations (id varchar(255), created_at varchar(32))"], :elapsed 4}
filmapp_1 | 19-01-22 18:36:06 5d9729ccfdd0 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["SELECT id FROM ragtime_migrations ORDER BY created_at"], :elapsed 11}
filmapp_1 | 19-01-22 18:36:06 5d9729ccfdd0 REPORT [duct.migrator.ragtime:14] - :duct.migrator.ragtime/applying :film-ratings.migrations/create-film#11693a5d
filmapp_1 | 19-01-22 18:36:06 5d9729ccfdd0 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["CREATE TABLE film (id SERIAL PRIMARY KEY, name TEXT UNIQUE, description TEXT, rating INTEGER)"], :elapsed 12}
filmapp_1 | 19-01-22 18:36:06 5d9729ccfdd0 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["INSERT INTO ragtime_migrations ( id, created_at ) VALUES ( ?, ? )" ":film-ratings.migrations/create-film#11693a5d" "2019-01-22T18:36:06.120"], :elapsed 3}
filmapp_1 | 19-01-22 18:36:06 5d9729ccfdd0 INFO [duct.database.sql.hikaricp:31] - :duct.database.sql/batch-query {:queries [], :elapsed 0}
filmapp_1 | 19-01-22 18:36:06 5d9729ccfdd0 REPORT [duct.server.http.jetty:13] - :duct.server.http.jetty/starting-server {:port 3000}
从消息中可以看出,我们已经启动了 postgres 服务,然后是 filmapp 服务。filmapp 服务已经连接到 postgres 服务(通过在config.edn
中拾取的DB_HOST=postgres
环境变量)。
filmapp 服务已经运行了迁移以添加film
表,并且现在正在侦听端口 3000。
您现在可以尝试添加一些电影。然后,通过在另一个终端会话中运行项目根目录中的docker-compose down
,或者在运行的会话中按 CTRL-C 键,停止 docker-compose 进程。
如果您随后使用docker-compose up -d
重新启动 docker-compose 服务,您应该会发现您添加的任何电影数据都被保存了下来。
注意:-d
标志运行 docker-compose detached,因此您必须运行docker-compose logs
来查看日志输出,运行docker-compose down
来关闭服务。
如果您想再次从一个空数据库开始,只需删除/tmp/postgresdata
目录并再次调用 docker-compose 服务。
同样,让我们在继续之前提交 docker-compose 文件。
$ git add --all .
$ git commit -m "Added docker-compose"
$ git push
将 Docker 映像发布到 Docker Hub
我们几乎已经完成了我们在这个博客中设定的目标。我们想做的最后一件事是将我们的 Docker 映像推送到 Docker Hub,最好作为我们持续集成系统的一部分。
首先,让我们手动操作。
手动发布到 Docker Hub
如果你还没有这样做,在 Docker Hub 上创建一个账户。然后,在您的帐户中创建一个名为film-ratings-app
的新存储库。
我们只想发布应用程序的 Docker 图像,因为我们不会将 docker-compose 文件用于生产。首先,让我们重新构建 Docker 映像,并用 Docker Hub 存储库 id(在我的例子中为chrishowejones/film-ratings-app
)标记它:
$ docker build . -t chrishowejones/film-ratings-app
Sending build context to Docker daemon 23.72MB
Step 1/5 : FROM openjdk:8u181-alpine3.8
---> 04060a9dfc39
Step 2/5 : WORKDIR /
---> Using cache
---> 2752489e606e
Step 3/5 : COPY target/film-ratings.jar film-ratings.jar
---> Using cache
---> 60fe31dc32e4
Step 4/5 : EXPOSE 3000
---> Using cache
---> 672aa852b89a
Step 5/5 : CMD java -jar film-ratings.jar
---> Using cache
---> 1fdfcd0dc843
Successfully built 1fdfcd0dc843
然后,我们需要像这样将该映像推送到 Docker Hub(记住使用您的 Docker Hub 存储库,而不是chrishowejones
!):
$ docker push chrishowejones/film-ratings-app:latest
The push refers to repository [docker.io/chrishowejones/film-ratings-app]
25a31ca3ed23: Pushed
...
latest: digest: sha256:bcd2d24f7cdb927b4f1bc79c403a33beb43ab5b2395cbb389fb04ea4fa701db2 size: 1159
添加 CircleCI 作业以构建 Docker 映像
好了,我们已经证明了可以手动推送。现在,让我们看看如何设置 CircleCI,每当我们标记一个版本的存储库时,circle ci 就会为我们做这件事。
首先,我们想使用 CircleCI 的一个名为 executors 的特性来减少重复。由于这个特性只在 2.1 版本中引入,我们需要打开我们的.circleci/config.yml
文件,并将版本参考从2
更改为2.1
。
version: 2.1
jobs:
...
我们需要添加一个作业来为应用程序构建 Docker 图像,并添加另一个作业来发布 Docker 图像。我们将添加两个工作流来控制各个构建步骤的运行时间。
让我们首先在文件的顶部添加一个 executor,它将声明一些我们希望在两个新任务中重用的有用内容。
version: 2.1
executors:
Docker-publisher:
working_directory: ~/cci-film-ratings # directory where steps will run
environment:
IMAGE_NAME: chrishowejones/film-ratings-app
Docker:
- image: circleci/buildpack-deps:stretch
jobs:
...
这个执行器声明了工作目录、本地环境变量IMAGE_NAME
,以及一个 Docker 映像,该映像具有我们支持执行 Docker 命令所需的 buildpack 依赖关系。和以前一样,您需要更改图像名称值,以您的 Docker Hub 用户而不是我的用户作为前缀。
在我们开始添加作业来构建 Docker 映像之前,我们需要确保在build
作业中构建的应用程序的 Uberjar 持久保存到我们将要编写的新作业中。
所以在build
作业的底部,我们需要添加 CircleCI 命令来持久化工作空间。
- run: lein do test, uberjar
- persist_to_workspace:
root: ~/cci-film-ratings
paths:
- target
这将持久保存target
目录,以便我们可以在新作业中重新附加该目录。现在,让我们将该作业添加到我们的build
作业之后:
build-docker:
executor: docker-publisher
steps:
- checkout
- attach_workspace:
at: .
- setup_remote_docker
- run:
name: Build latest Docker image
command: docker build . -t $IMAGE_NAME:latest
- run:
name: Build tagged Docker image
command: docker build . -t $IMAGE_NAME:${CIRCLE_TAG}
- run:
name: Archive Docker image
command: docker save -o image.tar $IMAGE_NAME
- persist_to_workspace:
root: ~/cci-film-ratings
paths:
- ./image.tar
好了,让我们来看看这个build-docker
任务在做什么。首先,我们引用我们的docker-publisher
执行器,这样我们就有了工作目录、IMAGE_NAME
变量和正确的 Docker 映像。
接下来,我们检验我们的项目,以便我们有可用的应用程序的Dockerfile
。之后,我们将保存的工作空间附加到我们当前的工作目录中,这样我们就可以在 Dockerfile 的正确路径上获得包含 jar 的target
目录。
setup_remote_docker
命令创建了一个远离主容器的环境,这是一个 Docker 引擎,我们可以用它来执行 Docker 构建/发布事件(尽管其他 Docker 事件也发生在我们的主容器中)。
接下来的两个 run 命令在 Docker 文件上执行 Docker 构建,并将结果图像分别标记为chrishowejones/film-ratings-app:latest
和chrishowejones/film-ratings-app:${CIRCLE_TAG}
。在您的情况下,您应该将IMAGE_NAME
更改为适合您的 Docker Hub 帐户的前缀,而不是chrishowejones
。
${CIRCLE_TAG}
变量将被插入到与这个构建相关的 GitHub 标签中。这里的想法是当我们标记一个 commit 并将其推送到 GitHub 时触发这个作业。出于论证的目的,如果我们将提交标记为0.1.0
并将其推送到 GitHub,当我们的build-docker
作业运行时,它将构建一个标记为latest
的 Docker 映像,但也会构建相同的映像并将其标记为0.1.0
。
docker save
命令将IMAGE_NAME
的所有 Docker 图像保存到一个 tar 存档文件中,然后我们在persist-to-workspace
命令中保存该文件,这样我们可以在下一个任务中使用它。
添加 CircleCI 作业以发布到 Docker Hub
我们现在有一个构建两个 Docker 映像并持久化它们的作业,这样我们就可以在下一个作业中使用它们,下一个作业会将这两个映像推送到 Docker Hub。
让我们将该作业添加到config.yml
中的build-docker
作业之后:
publish-docker:
executor: docker-publisher
steps:
- attach_workspace:
at: .
- setup_remote_docker
- run:
name: Load archived Docker image
command: docker load -i image.tar
- run:
name: Publish Docker Image to Docker Hub
command: |
echo "${DOCKERHUB_PASS}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin
docker push $IMAGE_NAME:latest
docker push $IMAGE_NAME:${CIRCLE_TAG}
让我们来看看这份工作是做什么的。首先,它重用了带有工作区、环境变量和图像的执行器。接下来,它将持久化的工作空间附加到工作目录。然后,我们使用setup_remote_docker
命令获取远程 Docker 引擎,这样我们就可以推送图像。
之后,我们运行 Docker 命令从持久化的 tar 文件中加载之前存储的 Docker 图像。下一个 run 命令使用两个尚未设置的环境变量DOCKERHUB_USER
和DOCKERHUB_PASS
登录到 Docker Hub,然后将前一个作业中构建的两个 Docker 映像推送到 Docker Hub。
在我们继续之前,让我们在 CircleCI 中设置这两个环境变量,这样我们就不会忘记。登录 https://circleci.com/,进入你的仪表板 https://circleci.com/dashboard.为你的项目选择项目设置(点击工作侧边栏中所列项目的图标。)
接下来,从BUILD SETTINGS
部分选择Environment Variables
,并添加两个新变量DOCKERHUB_USER
和DOCKERHUB_PASS
,分别为您的 Docker Hub 用户名和密码设置适当的值。
添加工作流,以便在发布新版本时运行作业
我们现在有了构建和发布 Docker 映像所需的作业,但是我们还没有指定如何执行这些作业。默认情况下,CircleCI 将在每次推送 GitHub 时运行一个名为build
的作业,但是额外的作业将被忽略,除非我们设置工作流来运行它们。让我们将工作流添加到.circleci/config.yml
文件的底部。
workflows:
version: 2.1
main:
jobs:
- build
build_and_deploy:
jobs:
- build:
filters:
branches:
ignore: /.*/
tags:
only: /^\d+\.\d+\.\d+$/
- build-docker:
requires:
- build
filters:
branches:
ignore: /.*/
tags:
only: /^\d+\.\d+\.\d+$/
- publish-docker:
requires:
- build-docker
filters:
branches:
ignore: /.*/
tags:
only: /^\d+\.\d+\.\d+$/
main
工作流指定build
作业应该执行,并且当这发生时没有给出任何特殊的过滤器,因此build
作业在每次推送到 GitHub 时执行。
build_and_deploy
工作流程有点复杂。在这个工作流中,我们指定当我们将任何符合指定正则表达式/^\d+\.\d+\.\d+$/
的标签推送到 GitHub 时,将运行build
作业。这个正则表达式是符合语义版本的标签的简单匹配,例如1.0.1
。因此,如果我们向 GitHub 推送一个像1.0.1
这样的标签,构建工作就会运行。
该工作流接下来指定build-docker
作业将在相同的条件下运行,但是它要求build
作业首先成功完成(这就是requires
命令所做的)。
工作流的最后一步是在将标签推送到 GitHub 的相同条件下运行publish-docker
作业,但前提是前一个build-docker
作业成功完成。
我们完成的.circleci/config.yml
文件应该是这样的(记住将对chrishowejones
的引用改为您的 Docker Hub 帐户)。
version: 2.1
executors:
docker-publisher:
working_directory: ~/cci-film-ratings # directory where steps will run
environment:
IMAGE_NAME: chrishowejones/film-ratings-app
docker:
- image: circleci/buildpack-deps:stretch
jobs:
build:
working_directory: ~/cci-film-ratings # directory where steps will run
docker:
- image: circleci/clojure:lein-2.8.1
environment:
LEIN_ROOT: nbd
JVM_OPTS: -Xmx3200m # limit the maximum heap size to prevent out of memory errors
steps:
- checkout
- restore_cache:
key: film-ratings-{{ checksum "project.clj" }}
- run: lein deps
- save_cache:
paths:
- ~/.m2
key: film-ratings-{{ checksum "project.clj" }}
- run: lein do test, uberjar
- persist_to_workspace:
root: ~/cci-film-ratings
paths:
- target
build-docker:
executor: docker-publisher
steps:
- checkout
- attach_workspace:
at: .
- setup_remote_docker
- run:
name: Build latest Docker image
command: docker build . -t $IMAGE_NAME:latest
- run:
name: Build tagged Docker image
command: docker build . -t $IMAGE_NAME:${CIRCLE_TAG}
- run:
name: Archive Docker images
command: docker save -o image.tar $IMAGE_NAME
- persist_to_workspace:
root: ~/cci-film-ratings
paths:
- ./image.tar
publish-docker:
executor: docker-publisher
steps:
- attach_workspace:
at: .
- setup_remote_docker
- run:
name: Load archived Docker image
command: docker load -i image.tar
- run:
name: Publish Docker Image to Docker Hub
command: |
echo "${DOCKERHUB_PASS}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin
docker push $IMAGE_NAME:latest
docker push $IMAGE_NAME:${CIRCLE_TAG}
workflows:
version: 2.1
main:
jobs:
- build
build_and_deploy:
jobs:
- build:
filters:
branches:
ignore: /.*/
tags:
only: /^\d+\.\d+\.\d+$/
- build-docker:
requires:
- build
filters:
branches:
ignore: /.*/
tags:
only: /^\d+\.\d+\.\d+$/
- publish-docker:
requires:
- build-docker
filters:
branches:
ignore: /.*/
tags:
only: /^\d+\.\d+\.\d+$/
现在可以将这个配置提交给 GitHub 了。
$ git add --all .
$ git commit -m "Added workflow & jobs to publish to Docker Hub."
$ git push
现在,您可以标记当前构建并检查新工作流是否运行以构建 Docker 映像并将其推送到 Docker Hub:
$ git tag -a 0.1.0 -m "v0.1.0"
$ git push --tags
这将创建一个带注释的标签,并将其推送到您的 GitHub 存储库中。这将触发build_and_deploy
工作流来构建应用程序 Uberjar,构建 Docker 映像(latest
和0.1.0
),并将这两个映像推送到您的 Docker Hub 存储库用于film-ratings-app
。你可以在 CircleCI 仪表盘中查看,也可以在 Docker Hub 中浏览你的film-ratings-app
库。
摘要
恭喜你!如果您已经阅读了本系列,那么您已经创建了一个简单的 Clojure web 应用程序,并使用 Docker 和 Docker Compose 对其进行了打包,这样您就可以在类似生产的环境中本地运行它。您还了解了如何让 CircleCI 构建、测试、打包和发布您的应用程序作为 Docker 映像。
在本系列的下一篇博文中,我将逐步介绍如何设置一个相当复杂的 AWS 环境,以便使用 Terraform 在云中运行我们的应用程序。
Chris Howe-Jones 是顾问 CTO、软件架构师、精益/敏捷蔻驰、开发人员和 DevCycle 的技术导航员。他主要从事 Clojure/ClojureScript、Java 和 Scala 方面的工作,客户从跨国组织到小型创业公司。
使用对象参数| CircleCI 管理可重用管道配置
CircleCI 管道是使用 YAML 语法定义的,它已经被许多软件工具和解决方案广泛采用。YAML 是一种人类可读的声明性数据结构,用于配置文件(类似于 CircleCI 管道的配置文件)以及存储或传输数据的应用程序中。管道配置文件中的数据指定并控制工作流和作业在平台上触发时的执行方式。配置文件中的这些管道指令往往会变得重复,这可能会导致配置语法量增加。随着时间的推移,这种数量的增加使得配置更难维护。因为 YAML 是一种数据结构,所以只有最低限度的语法可重用性能力(锚和别名)可用于处理增加的数量。锚和别名太有限,不能用于定义 CI/CD 管道。幸运的是,CircleCI 配置参数特性为封装和重用冗余的功能数据提供了强大的功能。
在这篇文章中,我将介绍管道配置参数,并解释在您的管道配置中采用它们的一些好处。
什么是配置参数?
执行者、作业和命令被认为是流水线配置文件中的对象。像面向对象编程(OOP) 范例中的对象一样,管道对象可以被扩展以提供定制的功能。CircleCI 配置参数通过提供创建、封装和重用管道配置语法的方法,让开发人员扩展执行器、作业和命令的功能。
执行器、作业和命令是具有各自属性的对象。参数也有自己独特的属性,可以与对象交互。参数的组成和属性包括:
parameters:
|_ parameter name: (Specify a name the parameter)
|_ description: (Optional. Describes the parameter)
|_ type: (Required. datatype string, boolean, integer, enum)
|_ default: (The default value for the parameter)
何时应该在管道配置中使用参数?
当数据和功能在管道中重复时,使用参数。换句话说,如果您的管道中有任何执行器、作业或命令在您的管道中被多次定义或执行,我建议识别这些模式或元素,并在您的配置文件语法中将它们定义为参数。使用参数使您能够集中管理和维护功能,并极大地减少配置文件中的冗余数据和语法行数。提供可变参数的能力也是一个好处,管道语法的整体可读性也得到了提高。
如何创建参数?
如前所述,执行者、作业、命令是可以用参数扩展的配置元素。决定扩展这些元素中的哪一个将取决于您的特定用例。
注意: 参数功能仅在 CircleCI 及以上版本中可用。必须在配置文件的顶部定义版本,如下所示: version: 2.1
,以便平台识别参数。
让我给你举一个在jobs:
对象中为并行度量定义参数的例子:
version: 2.1
jobs:
build_artifact:
parameters:
parallelism_qty:
type: integer
default: 1
parallelism: << parameters.parallelism_qty >>
machine: true
steps:
- checkout
workflows:
build_workflow:
jobs:
- build_artifact:
parallelism_qty: 2
在这个例子中,build-artifact:
作业有一个用名称parallelism_qty:
定义的parameters:
键。此参数的数据类型为整数,默认值为 1。 parallelism: 键是jobs:
对象的一个属性,它定义了在steps:
列表中产生和执行命令的执行器数量。在这种情况下,特殊的checkout
命令将在所有产生的执行器上执行。作业的parallelism:
键已被赋予值 parameters . parallelism _ qty
, which references the` parallelism _ qty:上面定义的参数定义。此示例显示了参数如何为管道构造增加灵活性,并提供了一种方便的方式来集中管理管道语法中重复出现的功能。
在作业对象中使用参数
使用前面的例子,工作流块中的parallelism_qty:
参数演示了如何在配置语法中使用参数。因为parallelism_qty:
是在作业对象中定义的,所以它可以作为工作流中指定的作业来执行。
workflows:
块有一个指定了build_artifact:
的jobs:
列表。它还为parallelism_qty:
赋值 2 个执行器,这将产生 2 个执行器并执行steps:
列表中的命令。如果该值为 3,那么 build_artifact 作业将产生 3 个执行器,并运行命令 3 次。
执行器、作业和命令是具有属性的对象,可以通过管道配置语法进行定义、自定义和重用。
在管道配置中重用执行器对象
上一节演示了如何在 jobs 对象中定义和使用参数。在这一节中,我将描述如何在执行器中使用参数。执行器定义用于执行管道作业和命令的运行时或环境。执行器对象有一组它们自己的独特属性,参数可以与之交互。这是一个定义和实现可重用执行器的例子:
version: 2.1
executors:
docker-executor:
docker:
- image: cimg/ruby:3.0.2-browsers
ubuntu_20-04-executor:
machine:
image: 'ubuntu-2004:202010-01'
jobs:
run-tests-on-docker:
executor: docker-executor
steps:
- checkout
- run: ruby unit_test.rb
run-tests-on-ubuntu-2004:
executor: ubuntu_20-04
steps:
- checkout
- run: ruby unit_test.rb
workflows:
test-app-on-diff-os:
jobs:
- run-tests-on-docker
- run-tests-on-ubuntu-2004
这个例子展示了如何在你的管道配置中定义和实现可重用的执行器。我在文件开头使用了executors:
键来定义 2 个执行者,一个名为docker-executor:
,一个名为ubuntu_20-04-executor:
。第一个指定使用 Docker 执行器,第二个指定使用 Ubuntu 20.04 操作系统映像的机器执行器。通过这种方式预定义执行器,开发人员可以创建一个要在管道中使用的执行器资源列表,并集中管理与执行器类型相关的各种属性。例如,码头执行器具有不属于机器执行器且不可用的属性,因为机器执行器不属于docker
类型。
重用对象将语法量保持在最低限度,同时提供简洁的对象实现,优化代码可读性和功能的集中管理。
jobs:
块定义了run-tests-on-docker:
和run-tests-on-ubuntu-2004:
;两者都有一个指定了值的executor:
键,作为该作业的适当执行者。run-tests-on-docker:
作业使用docker-executor
定义执行其步骤,而run-tests-on-ubuntu-2004:
作业根据ubuntu_20-04
定义执行。正如您所看到的,在它们自己的节中预定义这些执行器使得 config 语法更容易阅读,这将使它更容易使用和维护。对执行器的任何更改都可以在各自的定义中进行,并将传播到实现它们的任何作业。这种对定义的执行器的集中管理也可以应用于以类似方式定义的作业和命令对象。
在workflows:
块中,test-app-on-diff-os:
工作流并行触发两个作业,这两个作业在各自的执行器环境中执行单元测试。当您想了解应用程序在不同操作系统中的行为时,使用不同的执行器运行这些测试是很有帮助的。这种测试是常见的做法。这里的要点是,我只定义了一次执行者,并在多个作业中轻松地实现了它们。
可重复使用的命令对象
命令也可以在配置语法中定义和实现,就像执行器和作业一样。尽管命令对象属性不同于执行器和作业,但是它们的定义和实现是相似的。以下是显示可重复使用的命令的示例:
version: 2.1
commands:
install-wget:
description: "Install the wget client"
parameters:
version:
type: string
default: "1.20.3-1ubuntu1"
steps:
- run: sudo apt install -y wget=<< parameters.version >>
jobs:
test-web-site:
docker:
- image: "cimg/base:stable"
auth:
username: $DOCKERHUB_USER
password: $DOCKERHUB_PASSWORD
steps:
- checkout
- install-wget:
version: "1.17.0-1ubuntu1"
- run: wget --spider https://www.circleci.com
workflows:
run-tests:
jobs:
- test-web-site
在这个例子中,一个可重用的命令已经由一个作业定义和实现。配置顶部的command:
键定义了一个名为install-wget:
的命令,该命令安装特定版本的 wget 客户端。在这种情况下,定义一个参数来指定要安装哪个 wget 版本号。如果没有指定值,default:
键安装1.20.3-1ubuntu1
的默认值。steps:
键列出了 1 个run:
命令,用于安装在versions:
参数中指定的 wget 版本。versions:
参数被<< parameters.version >>
变量引用。
如示例所示,作业对象可以实现和使用定义的命令。test-web-site:
作业中的步骤节实现了- install-wget:
命令。它的version:
参数被设置为 wget 的早期版本,而不是默认的版本值。作业中的最后一个run:
命令使用 wget 测试来自给定 URL 的响应。这个例子运行一个简单的测试来检查一个网站是否响应请求。
像往常一样,workflows:
块触发- test-web-site
作业,该作业执行可重用的install-wget
命令。就像执行器和作业一样,命令带来了重用代码、集中管理更改以及提高管道配置文件中语法可读性的能力。
结论
在这篇文章中,我描述了使用管道参数和可重用管道对象的基础:执行器、作业和命令。关键要点:
- 执行器、作业和命令被视为具有属性的对象,可以在整个管道配置语法中进行定义、自定义和重用
- 重用这些对象有助于保持最少的语法量,同时提供简洁的对象实现,优化代码可读性和功能的集中管理
虽然我写这篇文章是为了向您介绍参数和可重用对象的概念,但我还是想鼓励您阅读一下可重用配置参考指南。它将帮助您更深入地了解这些功能,以便您能够充分利用这些出色的功能。
我很想知道你的想法和观点,所以请发推特给我 @punkdata 加入讨论。
感谢阅读!
如何以及在哪里隔离测试环境
原文:https://circleci.com/blog/path-to-production-how-and-where-to-segregate-test-environments/
学习如何以及在哪里隔离测试环境
将一个新工具引入一个组织不是一件简单的事情。采用 CI/CD 工具或任何其他工具应该在您的组织内进行一段时间的研究、分析和调整。
在我的上一篇文章中,我解释了任何成功的工具采用的先驱是如何与人相关的:有目的地一致,获得一些“之前”的度量来支持你的评估,以及适当地设置期望。我建议您重新阅读这篇文章,以便在进行任何工具更改之前为您的团队做好充分准备。
下一步是关于分析:准确推断出你最需要解决的管道问题。通过检查你的开发过程(我将称之为你的“生产之路”),你可以找出你最大的问题来自哪里。只有当您知道您试图集中解决什么问题时,您才能做出合理的工具决策。
检查生产路径的目标是创建清晰的阶段,作为检查点。为了从一个阶段进入下一个阶段,构建必须通过这些质量关。将使用各种测试来分离这些门。一旦测试通过,质量就有了保证,构建就可以继续了。
更喜欢看它的实际操作?观看视频,了解如何以及在哪里隔离测试环境。
这些管道阶段或执行环境被开发人员承担的越来越大的责任范围所分隔,从他们的本地笔记本电脑到团队空间,再到应用程序的整个代码库。随着责任范围的扩大,犯错的成本也就越高。这就是为什么我们在将一个构建传递到每一个连续的级别之前进行增量测试。
更重要的是,每个阶段需要不同类型的测试。当您从试运行阶段转移到生产阶段时,测试会从轻量级转移到重载级。资源成本也随着每个阶段而增加。重型测试只能发生在更像生产的环境中,包括完整的技术堆栈或外部依赖。为了正确地进行这些测试,还有更多的东西需要旋转,它们需要更昂贵的机器。因此,如果您能够在早期环境中进行尽可能多的测试,这是非常有益的,因为早期环境的成本要低得多。
在我们继续之前总结一下,职责分离的生产方式的好处是:
1。通过将开发和测试分成几个阶段,调试更快更容易。您可以更早地发现问题,反馈循环也更快。
2。通过保持尽可能少的类似生产的环境(一到两个就好)来节省资源。
我想提到的是,如果你的过程看起来不完全像我下面描述的那样,那也没关系。也许现在不会,或者永远不会。你可能有不同的目的,需要不同的流程。这里的目标是让您了解您的管道可能包含哪些阶段、测试和质量关。看到可能性应该有助于发现您的开发和测试工作流程中缺少了什么。此外,您的组织可能会使用与我在这里使用的不同的术语,这也没关系。这里重要的是考虑责任的级别,以及如何用测试来区分这些级别。
不同类型的测试环境
-单元/组件测试:这些测试涵盖了最小可能的组件、单元或功能。它们是运行起来最便宜和最快的测试,因为它们不需要大量的依赖或模仿。这些应该早做,让他们不碍事。
-集成测试:这些测试检查前一阶段的每个单元与其他组件、单元和功能的工作情况。从更广泛的意义上来说,它可以测试服务(比如 API)如何相互集成。
-UI 层测试:这是基于浏览器的自动化测试,测试基本的用户流程。它建立起来很昂贵,运行起来也很慢,所以应该在管道的后期进行。
现在让我们来谈谈这些测试如何适应软件开发管道。每个测试都有特定的角色和位置。
注意:这个例子使用了一个微服务应用程序,它允许我们分别测试和部署每个服务。
局部环境
这个环境是私有的,仅限于单个开发人员和他们的笔记本电脑。这种环境是最容易进行更改和测试您自己的实现的环境。
我们这里说的“本地”,其实只是一个“个人环境”。它可以在您的笔记本电脑上运行,但是如果应用程序太大而无法在您的本地笔记本电脑上运行,您也可以轻松地使用云环境。这里的关键是,它是一个较小规模的实例,是您自己的,您可以在开发过程中测试和调试您的实现,而不会干扰团队中的其他开发人员。因为您的本地环境中没有任何东西对其他人可见,所以您的团队可以同意将 Git 预推送挂钩集成到您的 repo 中,以确保使用本地环境,并在代码被推送到远程或共享的存储库之前运行自动化测试。
测试:在从本地环境迁移之前,我推荐的测试是单元测试、与模拟组件的集成测试,以及尽可能的 UI 测试。您在这个环境中可以做的测试越多,您在集成环境中取得成功的空间就越大。
职责范围:这里的范围只包括您正在构建的东西的实现或功能。你的组件能自己工作吗?表面积相对较小。但是,这是合并到共享环境之前的最后一个环境。因此,您需要负责任地进行测试,这样您就不会破坏共享的环境并阻碍其他人的积极开发。
CI 环境
这是最短命的环境;它与建筑同在。它是在构建被触发时创建的,并在构建完成后被拆除。也是最不稳定的环境。作为一名开发人员,我将代码签入 CI 环境。由于其他开发人员可能会同时进行部署,CI 环境中有许多并发的部署活动。因此,配置项可能并且经常会中断。没关系——它注定会坏掉的。关键是当它发生时要修复它。
如果您不能在本地环境中启动整个应用程序(这是非常可能的),CI 环境将是您能够进行浏览器驱动测试的第一个环境。您还可以进行在本地环境中无法进行的任何 UI 测试。
在这种环境中,您应该使用模拟外部服务和数据库来保持快速运行。
我建议像这样自动化 CI 环境的生命周期:当您合并代码时,CI 环境会自动加速,运行该代码,告诉您它是否安全,然后自行关闭。使用 Docker 自动启动环境将节省时间,自动化构建和环境创建的整个过程使团队成员更容易更频繁地提交。
从 CI 环境中获得的最大收益取决于您可以提高它的速度,因为这将决定您的反馈循环的长度。如果您的整个应用程序需要大约 30 分钟来启动和部署,那么您可能需要考虑进行部分部署。由于 CI 环境变化很快,您应该嘲笑大多数外部服务。请记住,您只是在测试核心组件的集成,而不是任何外部服务。
测试:单元测试,模拟组件的集成测试,模拟数据的 UI 测试
职责范围:在这里,您关心的是您要将代码合并到的共享代码库。您的组件与您的服务的其余部分一起工作吗?
发展环境
开发环境是与其他开发人员共享的环境。在这种环境下,应用程序中的每项服务每次都会得到部署。这些环境非常不稳定,因为每时每刻都有来自不同团队的不断变化。这里需要注意的是,您的集成和基于浏览器的测试现在可能会失败,即使它们在 CI 环境中通过了。这是因为它们现在已经与外部服务完全集成,其他服务也正在开发中。
尽管 CI 环境只是为您的团队(或者您产品的一个服务)准备的,并且可以在构建之间被拆除,但是这个开发环境是为您的整个产品代码库准备的。如果您选择合并到一个只属于您团队的开发环境中,那么与合并到所有团队共享的完整开发环境中相比,影响范围会更小。在开发环境中,现在需要系统健康检查监控,因为这是一个快速移动的环境,有许多不同的组件。由于它位于生产路径的中间,当这个环境中断时,它会对随后的环境产生不利影响——如果开发环境中断,这里的失败会阻止所有的更改,并且没有代码可以继续前进。
在开发环境中,有些外部服务会被模仿,有些不会,这取决于每个服务对您测试的内容有多重要,以及连接到那个外部服务的成本。如果您在这里使用模拟数据,请确保有足够的测试数据可用。
组织中的每个人都可以看到这个环境;任何开发人员都可以登录并将其作为应用程序运行。所有开发人员都可以使用这个环境进行测试和调试。
测试:单元测试,模拟组件集成测试,模拟数据 UI 测试,系统健康检查。
职责范围:范围扩大到与其他服务的集成。您的服务与它需要连接的其他服务一起工作吗?
质量保证环境
这是该场景中第一个手动部署的环境。它是手工部署的,因为 QA 团队需要根据变更的结构决定哪些特性值得他们自己测试。他们可能会选择一个堆叠的变更(变更 A、B 和 C ),并分别测试它们。在这种情况下,他们测试变更 A,一旦他们确定变更 A 按预期工作,他们就可以继续测试变更 B,当它抛出错误时,他们知道问题是由变更 B 引起的。否则,在只测试变更 C 的情况下,如果它抛出错误,就会阻碍 C、B 和 A 的进度。
QA 环境是一个受控的集成环境。在这里,QA 团队控制着即将到来的变化;相比之下,在开发环境中,任何变化都可能随时发生。构建现在与它将在应用程序中与之交互的服务集成在一起。
在此环境中,我们现在拥有与生产环境中相同的基础架构和应用程序。我们使用生产数据的一个代表性子集,足够接近生产数据来进行测试。QA 工程师或测试人员知道他们应该把测试的重点放在什么地方。我建议在这个阶段进行手动部署,这样可以在一个隔离的环境中测试小的变化,以帮助识别 bug。
如果在测试中不使用生产或类似生产的数据,QA 测试可能会错过很多。如果可能的话,使用真实的外部服务,这样 QA 可以捕捉到可能发生的真实问题。QA 环境越接近生产,您对测试结果的信心就越高。和往常一样,你必须权衡这种保证和成本。根据应用程序的功能,您仍然可以模仿一些外部服务,而不会产生不良影响。
测试:这个阶段是开放式探索性测试和安全性测试的适当时机。在这个阶段,你不仅希望发现技术问题,还希望发现潜在的用户流问题。
现在,您已经完成了所有种类的充分测试。是否存在任何潜在的用户流量或安全问题?在迁移到试运行环境之前,您还有什么需要解决的吗?
暂存环境
这是生产前的最后一个环境。试运行的目的是拥有一个与生产环境几乎完全相同的环境。当您将某些东西部署到产品化阶段,并且它工作正常时,您可以合理地确信该版本不会在生产中失败并导致停机。所有环境都有助于您发现潜在问题;筹备是信心的最后检查。在这种情况下,拥有与生产中几乎相同的数据量非常重要。这使您能够进行负载测试,并在生产中测试应用程序的可伸缩性。
应该将生产就绪代码部署到此环境中;同样,几乎与生产中相同。基础设施、数据库和外部服务集成应该与生产中的完全相同。维护和建设的成本将会很高——唯一更高的成本是不去做,以及中断生产。规模可以更小,但您的设置和配置必须相同。
这是生产前测试实施、迁移、配置和业务需求的最后一道关口。我想强烈建议您在进入试运行阶段之前获得业务签署。否则,做出改变将会非常昂贵。当你进行开发时,请确保拥有产品或所请求特性的人完全理解你在构建什么。从头到尾都保持一致。
测试:这个环境可以用来完成迄今为止我们所有测试中最繁重的工作:系统测试、负载测试、性能测试和安全性测试。
考虑到环境的重要性,如果在以前的环境中已经完成了其他核心应用程序功能和内部集成测试,那将是最有利的。
此时,要问的问题是:您的实现满足所有业务目标吗?
如何充分利用你的生产之路
- 使路径对每个人可见。
- 使用生成监控
- 允许所有开发人员访问预生产环境
- 构建和环境可能会中断。快速修复它,并尽可能保持绿色。
- 如果你知道如何修复构建,就修复它。
- 如果由于调试和实现的原因,某个构建修复需要一段时间,请先恢复该构建以取消阻止其他开发。
- 当一个构建在早期阶段被破坏时,不要将其提升到后期环境。
- 编写可靠的,自动化测试。
- 如果你的测试毫无理由地失败了,你不能信任他们,这条管道也不会为你工作。当这个管道可见时,并且当测试结果被准确地表示时,它是值得信任的。这就是为整个团队提供价值的方式。
创建您自己的集成测试环境
我刚刚与你分享了一个理想的生产途径。但是这里的理想可能对您或您的组织来说并不理想:您可能能够利用一个更轻量级的过程,这取决于您的组织和应用程序。但是我分享这个的意图是给你一个新的视角,通过它来看你自己的生产和测试实践之路。通过将您的质量关口与我概述的进行比较,您可以看到您可能遗漏了什么,因此您可以添加必要的控制,交付高质量的产品,并节省成本。
最后一点:你的整个生产路径,不管看起来像什么,都应该对组织中从事开发的每个人可见。每个在本地环境中实现代码的开发人员都应该能够了解代码将如何部署到生产环境中。在我之前的文章中,我谈到了 DevOps 意味着关心你的代码如何通过管道到达客户。其中一部分意味着要知道它将要通过的质量关。只有意识到他们的代码是如何被部署的,开发人员才能在进入下一阶段之前对减少错误产生最大的影响。
阅读更多信息:
CircleCI 的渗透测试- CircleCI
原文:https://circleci.com/blog/penetration-testing-at-circleci/
在过去的五年里,CircleCI 聘请了第三方渗透测试公司,至少每 90 天对我们的产品发布或基础设施进行一次测试。到目前为止,我们已经做了超过 25 个。有时他们涵盖新产品功能,有时他们涵盖基础设施。
CircleCI Server 3.0 的渗透测试
我们最近对我们的 server 3.0 主要版本进行了第三方渗透测试。这一版本使 Kubernetes 成为我们企业产品的一等公民。随着向 Kubernetes 的重大转变,所有后续版本都将建立在 Kubernetes 的基础设施之上,我们找到了最佳的合作伙伴来测试我们新基础的弹性。回车:位尾。
与比特轨迹合作
Trail of Bits 是云原生计算基金会(Cloud Native Computing Foundation)这两家第三方渗透测试公司之一,该基金会负责监督 Kubernetes 的开发,受雇于 2019 年对 Kubernetes 本身进行为期四个月的测试。将我们的工作尽可能紧密地联系起来似乎是最好的前进方式。
CircleCI 向 Trail Bits 提供了我们服务器产品的测试版,并要求他们按照我们向新客户或现有客户提供的说明随意敲打。三周后,他们发现了一些惊人的漏洞,这些漏洞推迟了产品的发布。为此,我们非常感激。我们非常乐意推迟发布(并对等待发布的客户无限感激),因为我们最不想做的事情就是将不安全的软件发送给任何人。
主要服务器版本的 Bits 测试不是唯一的。每隔 90 天,我们的一个开发团队就会进行一次类似的演练。其他测试包括我们的 Mac 机队、新的用户认证工作流程以及我们最近的技术收购。
CircleCI 的渗透测试政策
因为我们将客户的安全放在第一位,所以 CircleCI 有一个长期的渗透测试政策,可以立即修复所有关键和重要的发现,然后在测试结束之前验证这些修复。安全工程师与开发团队领导一起评估风险,并在适当的时候添加到团队的待办事项列表中。一个非常好的中等可能是一个中等,也需要立即修复,但是有了更多的上下文,我们可能有信心将其降低到一个较低的水平,并将其添加到团队的一般待办事项中。我们所有产品(云、运行器和服务器)的渗透测试遵循与基础设施测试相同的流程。
客户经常要求我们提供渗透测试结果的副本。出于安全原因,我们无法提供原始调查结果,但我们会提供一份面向客户的摘要,在不暴露任何潜在秘密的情况下提供尽可能多的细节。要请求渗透测试摘要,请联系您的客户成功经理。
权限 101:了解 CircleCI - CircleCI 上的权限
原文:https://circleci.com/blog/permissions-101-understanding-permissions-on-circleci/
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
我们最近为所有 CircleCI 用户推出了许多新功能。请求最多的功能之一集中在权限设置上。这篇博客将深入探讨这个新特性,它是如何工作的,以及它对使用 CircleCI 的你和你的团队意味着什么。
什么是权限?
权限是 CircleCI 上的一个新特性,它可以让您更好地控制项目的设置。使用权限,组织可以限制谁可以在 CircleCI 上更改“项目设置”。此设置在组织级别或组织内的项目级别可用。为此,CircleCI 利用 GitHub 的权限来确定用户的访问权限。
用户或组织为什么要启用权限??
为您的组织启用权限后,只有 GitHub repo 管理员或 GitHub 所有者才能在 CircleCI 上更改“项目设置”。这对于大型团队来说特别有用,可以确保您的项目设置只能由具有管理员权限的团队成员更改。这对于大型团队来说尤其有用,可以确保您的项目设置不会被团队中的非 GitHub 管理员/所有者意外更新。
这将如何影响我的团队?
为您的组织启用权限后,GitHub 成员和贡献者将无法对 CircleCI 的“项目设置”页面进行任何更改。这包括为您的组织设置容器、项目级并行化设置、构建设置等。GitHub 成员或合作者仍然可以使用 CircleCI 的功能,如重建、使用缓存重建或通过 SSH 重建。
如果我改变主意了怎么办?我可以关闭这个功能吗?
如果您发现权限不适合您,您可以随时创建支持请求(在应用程序中,点按“帮助?”按钮并选择“支持”),我们将为您的组织禁用权限。
入门:如何在 GitHub 上设置用户权限?
要设置您的团队在 GitHub 上的访问权限,请导航到您所在组织的以下链接:https://github.com/orgs/org-name/teams.
您可以使用以下链接设置 repo 级别的用户访问:https://github . com/org-name/repo-name/settings/collaboration。
阅读更多关于在 GitHub 上设置团队权限的信息。
如何在 CircleCI 上启用权限?
如果您希望为您的组织或组织内的特定项目启用此功能,请创建支持请求(在应用程序中,单击“?”当你滚动鼠标时,按钮上写着“救命?”并选择“支持”)
你希望 CircleCI 增加什么功能?您可以在我们社区网站的功能请求类别上提交一个新主题。你也可以在我们的开发者选择奖中投票,在这里我们收集了九个最常被请求的功能,我们很想构建这些功能,但由于各种原因,它们从未出现在我们的待办事项列表中。这是您直接对这些功能进行投票的机会,我们将构建获得最多投票的功能。投票将于太平洋时间 4 月 22 日星期一下午 5:00 结束。请访问开发者的选择,为您希望我们下一步开发的功能投票。
在工作流中持久化数据:何时使用缓存、工件和工作区
原文:https://circleci.com/blog/persisting-data-in-workflows-when-to-use-caching-artifacts-and-workspaces/
CircleCI 提供了许多不同的方法来将数据移入和移出作业,持久化数据,以及使用工作区在作业之间移动数据。为正确的任务使用正确的特性将有助于加速您的构建,提高可重复性,并提高效率。
对于任何曾经等待 CI 测试套件变绿的人来说,更快的 CI 运行的好处是显而易见的。
重复性也很重要。可重复的 CI 流程意味着,如果您对您的回购中的相同 SHA 再次运行相同的流程,您将获得相同的结果。当 CI 流程不可重复时,您会发现自己在浪费时间重新运行作业来实现环保。
数据如何在 CircleCI 作业之间流动
数据可以以不同的方式在 CircleCI 作业之间流动。工作空间在单个工作流中的作业之间保存数据。缓存在不同工作流版本中的相同作业之间保存数据。工件在工作流完成后保存数据。它们之间的用例、实现和数据停留的时间各不相同。
CircleCI 工作区
工作区在工作流程中的连续作业之间移动数据。
当在作业中声明工作空间时,可以添加一个或多个文件或目录。每次添加都会在工作区文件系统中创建一个新层。然后,下游作业可以根据自己的需要使用该工作空间,或者在其上添加更多层。
一种常见的方法是使用工作区将生成的版本号从构建作业传递到部署作业。它们也可以用来传递编译好的二进制文件——但是由于它们需要在每个作业中被上传和下载,这比仅仅传递元数据要慢。
与缓存不同,工作空间不会在运行之间共享,因为一旦工作流完成,它们就不再存在。有一个例外,即重新运行工作流。关于这一点的更多信息以及对工作空间的全面深入了解,可以在明天的博客文章中找到:深入 CircleCI 工作空间。
CircleCI 中的缓存
缓存在多个工作流运行中的同一作业之间持久保存数据。
缓存使您可以重用以前作业中昂贵的提取操作的数据。初始作业运行后,通过不重做工作,作业的未来实例将运行得更快。一个主要的例子是包依赖管理器,如 Yarn、Bundler 或 Pip。通过从缓存中恢复依赖关系,像yarn install
这样的命令将只需要下载新的依赖关系,如果有的话,而不需要在每次构建时重新下载所有内容。
缓存在一个项目中是全局的,保存在一个分支上的缓存将被其他分支使用,因此它们应该只用于可以跨分支共享的数据。要获得更多类似的技巧和对 CircleCI 缓存的更深入了解,您可以阅读 CircleCI 2.0 缓存文档。
CircleCI 文物
工件在一个工作流完成并消失后持久保存数据。
工件用于构建过程输出的长期存储。例如,如果你有一个 Java 项目,你的构建很可能会产生一个代码的.jar
文件。这些代码将通过您的测试来验证。如果整个构建/测试过程通过了,那么过程的输出(.jar
)可以被存储为一个工件。.jar
文件在创建它的工作流程结束后很久还可以从我们的工件系统下载。
如果您的项目需要以某种形式或方式打包,比如说一个 Android 应用程序。apk 文件被上传到 Google Play,这是一个很好的例子。许多用户将他们的工件上传到公司范围内的存储位置,如亚马逊 S3 或 Artifactory。更多提示和最新信息可以在 CircleCI 工件文档中找到。
想深入了解工作区以及如何最好地使用它们?阅读我们的后续博文深入 CircleCI 工作区。
有关工作流可以做的所有事情的概述,包括 OSS 配置,请参见工作流系列的广阔天地:
如何使用工件获得构建和测试结果
原文:https://circleci.com/blog/piecing-together-build-and-test-results-with-artifacts/
想象一下:你在一个偏远的沙漠地区,那里发现了一个历史性的考古定居点。通过最初的调查,这一群人似乎没有幸存下来的好运气。这就是你在那里的原因。你在寻找线索,看看你是否能拼凑出一幅准确的图像,来解释为什么他们的运气用完了。
通过你的搜索和搜索,你没有发现多少有用的东西,突然-砰!你发现了一个似乎是解释这里发生的事情的关键的物体。你找到的这件艺术品正是你所需要的,它能告诉你并帮助你理解发生了什么。
想象一下,对于您的测试运行、构建等等,有这些类型的关键线索!CircleCI 已经涵盖了你,虽然因为我们不能给系统像陶器的沙片,剑,或银猴之类的人工制品,让我们来看看我们可以创造什么,以及我们可以使用这些物品的一些方法。
让我们回顾一下什么是人工制品
神器到底是什么?摘自文档中的,“工件在一项工作完成后保存数据,并可用于长期存储您的构建过程的输出。”如果这对你来说仍然模糊不清,让我们来分解它。
在整个工作流程中,每个作业都有自己的数据和文件,每次运行后都会被擦除。为了跨作业传送数据,可以将数据保存到工作区,工作区是特定于每个工作流的临时数据存储区。与作业一样,工作流完成后,工作空间数据也将消失。怎么回事?我们如何在工作流之后保留可能对我们有用的项目?
我们将这些物品作为艺术品保存。如下图所示,您有了一个额外的步骤来将文件移动到存储,即使在您的工作流完成后,这些文件也不会丢失。
我们如何使用工件,它们在哪里配置?
为了让工件有用,它们需要是可访问的。简单地把它们埋在土里,然后希望一千年后有人会碰巧发现它们,这并没有多大帮助。幸运的是,存储工件文件是一个非常简单的过程,它发生在 config.yml 文件中,通过恰当命名的内置步骤:store_artifacts
。假设你有一个在测试你的网站的视觉回归时拍摄的截图目录,假设这些截图存储在/tmp/screenshots
。
将它们保存为配置中的工件的最简单方法如下所示:
- store_artifacts:
path: /tmp/screenshots
这是它在完整配置中的样子:
version: 2
jobs:
build_and_save:
docker:
- image: circleci/golang:latest
steps:
- checkout
- run: make build
- store_artifacts:
path: /bin/final_build
workflows:
version: 2
main:
jobs:
- build_and_save
就是这样!真的就这么简单。🎉
如果您想指定一个在通过工件 API 访问工件时使用的前缀,您可以通过添加如下代码行来指定:
- store_artifacts:
path: /tmp/screenshots
destination: screenshot
关于通过 API 下载工件的更多信息可以在文档中找到。
有哪些真实世界的人工制品的例子?
我们现在知道如何保存和访问我们的工件,我们甚至想保存什么?以下是一些可以保存下来长期使用的物品的简单列表:
- 测试步骤或失败的截图
- 试验结果
- APKs 二进制文件
- 压力测试的性能输出
- 日志文件
- 核心转储
- 覆盖率文件
基于什么对你的需求有意义,你可以保存这些项目中的一些或者一个都不保存,这完全没问题。CircleCI 不在乎,重要的是什么对你的需求最有用、最有帮助。(如果你保存了上面没有列出的神器,请留下评论,让我们知道是什么!)
保存文件固然很好,但如果不使用它们又有什么用呢?说得好!
- 如果你有正在保存的截图,在一次失败的运行后,你可以在一个作业的工件选项卡中调出截图,并查看在可视化回归中到底有什么不同,或者当 Selenium 找不到一个按钮时你的站点在哪里。如果您想更进一步,您甚至可以设置一个与您最喜欢的消息平台的集成,并使用完全相同的屏幕截图发送失败消息,从而节省您的时间!
- 如果你正在创建应用程序,你可以保存一个版本的 Android 代码,然后再分发。
- 如果您的日志文件输出非常大,并且您希望某些软件可以访问它们进行解析,您也可以这样做。
最后
天空是你选择使用神器的极限。它们是一个易于学习和实施的简单概念,但是它们的威力在于使您和您的团队能够以更深层次的方式进一步微调和补充您的 CI/CD 流程,而不仅仅是通过/失败。
感谢阅读!
Gemini Smith 是一名不断学习的软件工程师,对测试、交流和改进软件开发生命周期充满热情。主要用 Go 写作,她还通过公开演讲、宣传、咨询和指导对软件和测试社区做出贡献。
采用 circleback 模式| CircleCI 的高级管道编排
原文:https://circleci.com/blog/pipeline-orchestration-circleback/
本教程涵盖:
- 为依赖管道编排触发器
- circleback 模式介绍
- 为 circleback 管道创建配置
随着多个团队在许多项目上工作,为您的软件拥有一个单一的管道是不够的。在测试和发布之前,需要构建和集成这些项目。那么开发团队如何处理这种情况呢?许多团队通过将软件分解成更小的部分来解决问题,这些部分做得更少,更容易维护和构建。这种方法导致微服务架构在我们的行业中越来越普遍。
不利的一面是将软件分成更小的部分会增加复杂性。更多部件和更高复杂性的结合使得在生产中测试和运行应用程序更加困难。
CI/CD 管道在这里也不例外。在本系列的前一篇文章中,我们研究了从其他管道触发管道。那篇文章描述了如何在 CircleCI 中将多个项目链接在一起,以便一个部署启动另一个。
在这篇文章中,我们将更进一步。我将提供一个管道编排的例子,其中第一个管道不仅触发另一个管道,而且在继续之前等待另一个管道完成。我称之为 circleback 模式,因为它“循环返回”到第二个管道,在它完成工作时发出信号。使用这种模式允许更好的集成测试和更复杂的部署和发布场景。
先决条件
- 本教程涵盖了高级管道编排技术,因此必须具备 CircleCI 和 DevOps 的经验。
- 本教程也建立在之前的教程之上:从其他管道触发管道。
资源和示例代码
本例的源代码位于两个存储库中:
介绍 circleback 模式
在这些多管道的 circleback 编排中有三个步骤:
- 第一个管道等待
- 第二管道终止
- 从第一个管道中检索结果。另一种描述方式是,第二个管道调用或“循环回”第一个管道,而第一个管道暂停并等待信号继续。
另一种方法是保持第一个管道运行,定期检查第二个管道是否已经完成。这种方法的主要缺点是正在运行的作业的连续轮询成本增加。
根据第二个管道的状态,第一个管道将继续执行,直到成功终止或失败。
为什么管道要等待其他项目完成?
正如在简介中提到的,这是一种高级技术,旨在降低处理多个项目所带来的复杂性。它可以被在多个相互依赖的项目上工作的团队使用,这些项目不希望像 monorepo 一样放在一个存储库中。另一个用途是拥有一个集中的测试库,例如在一个硬件公司。这项技术对于微服务应用程序的集成测试或者编排复杂的部署场景也很有用。有很多种可能。
实现管道触发器
我们有 2 个管道,我们想编排
- 管道 A,它执行触发
- 管道 B 被触发,并循环回到管道 A
管道 B 依赖于 A,可用于验证 A。
两个管道都需要设置 API 密钥并使其可用。您可以在管道 A 的作业中使用 API 键集作为环境变量(CIRCLECI_API_KEY
),在管道 B 回调时也可以在管道 B 中使用。您可以在两个项目中设置它,也可以在组织级别作为上下文设置它。对于本教程,我在组织级别将其设置为circleci_api
上下文,这样两个项目可以使用相同的 API 键。
触发管道
触发过程在本教程的第一部分从其他管道触发管道中进行了深入解释。在接下来的教程中,我将只讲述重要的区别。
- 若要从管道返回,请将原始管道的 ID 传递给它。然后可以通过 API 检索和到达它。
- 您还需要存储触发的管道的 ID。你需要稍后得到它的结果。
在示例代码中,该参数称为triggering-pipeline-id
:
curl --request POST \
--url https://circleci.com/api/v2/project/gh/zmarkan-demos/circleback-cicd-project-b/pipeline \
--header "Circle-Token: $CIRCLECI_API_KEY" \
--header "content-type: application/json" \
--data '{"branch":"main","parameters":{"triggering-pipeline-id":"<< pipeline.id >>"}}'
为了存储管道 ID,将您的curl
调用包装在$()
中,并将其分配给变量CREATED_PIPELINE
。要从响应体中提取 ID,使用jq
工具,并将其写入文件pipeline.txt
:
CREATED_PIPELINE=$(curl --request POST \
--url https://circleci.com/api/v2/project/gh/zmarkan-demos/semaphore_demo_project_b/pipeline \
--header "Circle-Token: $CIRCLECI_API_KEY" \
--header "content-type: application/json" \
--data '{"branch":"main","parameters":{"triggering-pipeline-id":"<< pipeline.id >>"}}' \
| jq -r '.id'
)
echo "my created pipeline"
echo $CREATED_PIPELINE
mkdir ~/workspace
echo $CREATED_PIPELINE > pipeline.txt
现在您已经创建了文件pipeline.txt
,使用persist_to_workspace
来存储它并在后续的作业中使用它:
- persist_to_workspace:
root: .
paths:
- pipeline.txt
整个作业配置如下:
...
jobs:
trigger-project-b-pipeline:
docker:
- image: cimg/base:2021.11
resource_class: small
steps:
- run:
name: Ping another pipeline
command: |
CREATED_PIPELINE=$(curl --request POST \
--url https://circleci.com/api/v2/project/gh/zmarkan-demos/semaphore_demo_project_b/pipeline \
--header "Circle-Token: $CIRCLECI_API_KEY" \
--header "content-type: application/json" \
--data '{"branch":"main","parameters":{"triggering-pipeline-id":"<< pipeline.id >>"}}' \
| jq -r '.id'
)
echo "my created pipeline"
echo $CREATED_PIPELINE
mkdir ~/workspace
echo $CREATED_PIPELINE > pipeline.txt
- persist_to_workspace:
root: .
paths:
- pipeline.txt
...
精心安排等待
前一个作业将触发管道 B,它需要在循环回到管道 a 之前完成。您可以像这样使用 CircleCI 中的approval
作业:
...
workflows:
node-test-and-deploy:
jobs:
...
- trigger-project-b-pipeline:
context:
- circleci-api
requires:
- build-and-test
filters:
branches:
only: main
- wait-for-triggered-pipeline:
type: approval
requires:
- trigger-project-b-pipeline
- check-status-of-triggered-pipeline:
requires:
- wait-for-triggered-pipeline
context:
- circleci-api
...
作业trigger-project-b-pipeline
结束后,进入wait-for-triggered-pipeline
。因为作业类型是approval
,所以它将一直等到有人(在本例中是 API)手动批准它。(更多细节在下一节。)在它被批准后,添加一个requires
节,以便它继续后续的作业。
使用 CircleCI API 的两个作业都指定了context
,因此 API 令牌作为环境变量对两者都可用。
绕回管道 A
现在我们已经完成了管道 A,是管道 B 大放异彩的时候了。CircleCI 的approval
工作是一种特殊的工作,一直等到被接受。它通常用于将管道保持在挂起状态,直到它被人工交付主管或 infosec 工程师批准。
此时,管道 B 知道管道 A 的 ID,因此您可以使用批准 API 来获取它。您只有正在运行的管道的 ID,而没有需要批准的实际作业,因此您将需要不止一个 API 调用:
- 获取管道中的所有工作
- 按名称查找审批作业
- 发送批准作业的请求
批准该作业将允许管道 A 继续运行。
如果管道 B 中的测试失败,那么该作业将自动失败。在这种情况下,包含所需作业的工作流将不会继续。您可以通过在管道中使用post-steps
来解决问题,它总是会执行。整个工作流显示在下一个示例代码块中。
参数:
parameters:
triggering-pipeline-id:
type: string
default: ""
...
workflows:
node-test-and-deploy:
jobs:
- build-and-test:
post-steps:
- approve-job-in-triggering-pipeline
context:
- circleci-api
执行批准 API 调用的脚本可以这样实现。对于本教程,我使用了一个command
。
...
commands:
approve-job-in-triggering-pipeline:
steps:
- run:
name: Ping CircleCI API and approve the pending job
command: |
echo << pipeline.parameters.triggering-pipeline-id >>
if ! [ -z "<< pipeline.parameters.triggering-pipeline-id >>" ]
then
workflow_id=$(curl --request GET \
--url https://circleci.com/api/v2/pipeline/<< pipeline.parameters.triggering-pipeline-id >>/workflow \
--header "Circle-Token: $CIRCLECI_API_KEY" \
--header "content-type: application/json" \
| jq -r '.items[0].id')
echo $workflow_id
waiting_job_id=$(curl --request GET \
--url https://circleci.com/api/v2/workflow/$workflow_id/job \
--header "Circle-Token: $CIRCLECI_API_KEY" \
--header "content-type: application/json" \
| jq -r '.items[] | select(.name == "wait-for-triggered-pipeline").id')
echo $waiting_job_id
curl --request POST \
--url https://circleci.com/api/v2/workflow/$workflow_id/approve/$waiting_job_id \
--header "Circle-Token: $CIRCLECI_API_KEY" \
--header "content-type: application/json"
fi
when: always
...
该脚本首先检查是否存在triggering-pipeline-id
管道参数。仅当该参数存在时,它才会继续。命令中的when: always
行确保不管终止状态如何都会执行。
然后,它进行 3 次 API 调用:
- 获取管道中的工作流 ID。对于这个示例项目,只有一个工作流正在进行中。
- 获取该工作流中的作业,使用
jq
选择与批准作业名称(wait-for-triggered-pipeline
)匹配的作业,并提取批准作业的 ID。 - 向具有等待作业 ID 的审批端点发出请求。
对于本教程,我们将像工作流 ID 和作业 ID 这样的结果存储在本地 bash 变量中,并在对 API 的后续调用中使用它们。
注意 : 如果您的任务比单个响应所能发送的要多,那么您可能还需要处理分页。
现在您已经发出了批准请求,管道 B 已经完成,管道 A 应该可以再次运行了。
用 B 的结果更新管道 A
管道 A 获得批准后,工作流中的下一个作业将开始。如果工作流图需要,管道 A 可以触发多个作业。
我们仍然不知道先前工作流程的结果。要获得这些信息,您可以再次使用 API 从管道 a 获得 B 的状态。
首先,您需要检索被触发的管道(管道 b)的 ID,这与在前面的步骤中保存在工作区中的 ID 相同。使用cat
检索它:
- attach_workspace:
at: workspace
- run:
name: Check triggered workflow status
command: |
triggered_pipeline_id=$(cat workspace/pipeline.txt)
然后使用 API 来检索工作流。使用jq
获取返回的工作流数组中第一个项目的状态:
created_workflow_status=$(curl --request GET \
--url "https://circleci.com/api/v2/pipeline/${triggered_pipeline_id}/workflow" \
--header "Circle-Token: $CIRCLECI_API_KEY" \
--header "content-type: application/json" \
| jq -r '.items[0].status'
)
检查状态是否不是success
。如果不是,使用exit
终止作业,退出代码-1。如果工作流成功,它将终止:
if [[ "$created_workflow_status" != "success" ]]; then
echo "Workflow not successful - ${created_workflow_status}"
(exit -1)
fi
echo "Created workflow successful"
以下是作业check-status-of-triggered-pipeline
的完整配置:
check-status-of-triggered-pipeline:
docker:
- image: cimg/base:2021.11
resource_class: small
steps:
- attach_workspace:
at: workspace
- run:
name: Check triggered workflow status
command: |
triggered_pipeline_id=$(cat workspace/pipeline.txt)
created_workflow_status=$(curl --request GET \
--url "https://circleci.com/api/v2/pipeline/${triggered_pipeline_id}/workflow" \
--header "Circle-Token: $CIRCLECI_API_KEY" \
--header "content-type: application/json" \
| jq -r '.items[0].status'
)
echo $created_workflow_status
if [[ "$created_workflow_status" != "success" ]]; then
echo "Workflow not successful - ${created_workflow_status}"
(exit -1)
fi
echo "Created workflow successful"
结论
在本文中,我们回顾了一个复杂管道编排模式的例子,我将其命名为“circleback”。circleback 模式创建一个依赖管道,并允许您在完成之前等待它终止。它涉及到从两个项目中生成几个 API 键,使用一个批准作业,以及 CircleCI 的 workspace 特性来存储和传递值,例如工作流中作业间的管道 ID。样本项目位于不同的存储库中:项目 A ,以及项目 B 。
如果这篇文章在某种程度上对你有所帮助,我很想知道,如果你对它有任何问题或建议,或者对未来的文章和指南有任何想法,请通过 Twitter - @zmarkan 或电子邮件给我联系我。
关于 CircleCI 的新用户界面,你需要知道什么
经过几个月的努力,我们今天正式推出了我们的新 UI。
我们要感谢所有 CircleCI 的用户,他们在我们开发界面时发布了帖子,发了推文,并完成了 UX 会议。您的反馈使我们能够确保团队构建、测试和部署高质量代码的最佳体验。
管道
新 UI 最大的升级是围绕管道而不是作业的定位。基于作业的视图是为 CircleCI 1.0 构建的,非常适合配置简单的用户。然而,从事复杂项目的团队需要更多的可见性,而新的 UI 提供了这一点。新的基于管道的视图将所有作业组合到一个工作流中,并将所有工作流组合到一个管道中,这使得参与项目的每个人都可以尽快轻松找到与其运行相关的信息。
最近,我们使用户能够从管道页面取消和重新运行工作流,这是我们特别优先考虑的改进,因为我们的用户要求这样做!
速度
领导这个项目的高级产品经理 Kate Catlin 对用户在新的用户界面上体验更快的页面加载时间感到非常兴奋。“开发人员很忙,他们在我们的平台上完成特定的任务,然后回去写代码,”卡特林说。“通过减少每个屏幕加载信息所需的时间,并重新安排信息以优先考虑最重要的内容,我们可以让开发人员更快地提供新功能。”
快速导航的其他改进
您将在新用户界面上看到的其他一些激动人心的改进包括:
- 跨项目管道视图,提高跨团队的可见性
- 在分支和项目选择器上键入搜索,轻松过滤管道视图
- 在仪表板上自动打开失败的和最新的管线,以便于导航
- 失败时测试摘要选项卡的默认视图,因此最重要的信息总是在您的指尖
接下来会发生什么
在不久的将来,您可以看到更多特定于管道的信息,比如查看每个管道中使用了哪些orb的能力。CircleCI orbs 是 YAML 配置的可重用片段,它将重复的配置压缩成一行代码。它们有助于自动化流程,并且易于与开发人员已经使用的第三方工具集成。
我们将进行的最令人兴奋的更新之一是专门为新的基于管道的 UI 构建的 Insights UI。请继续关注 Insights 的更多信息。
即将推出的其他一些激动人心的 UI 改进包括:
- 通过每页顶部改进的面包屑更快地导航
- 失败测试的更好总结
- 能够通过单击按钮关闭“管道”页面上所有打开的管道
在不断变化的世界中支持开发人员
对于 Catlin 来说,改进 circle ci UI 的动力一直是帮助开发人员更好地完成他们的工作——现在比以往任何时候都要多。
“现在世界上有很多不确定性,但我个人认为,软件开发商在推动我们走向可以克服这些挑战的未来方面发挥着关键的、不可替代的作用,”卡特林说。“帮助软件开发人员更好、更快、更少地完成工作有助于我们战胜世界面临的新挑战。”
要了解更多关于我们新用户界面的信息,请查看我们的文档或阅读更多关于为什么新用户界面是为提高用户工作效率而构建的。
最佳性能计划:为什么 CircleCI 改变我们的定价模式
原文:https://circleci.com/blog/plans-for-optimal-performance-why-circleci-is-changing-our-pricing-model/
当 CircleCI 在 2011 年成立时,持续集成和持续部署(CI/CD)对一些人来说是白日梦,对几乎所有人来说都不是惯例。当时我们所有的客户都在构建我们的 1.0 系统,我们引入了一个基于容器的定价计划,客户将为他们想要访问的每个容器支付月费。在过去的七年里,我们一直在提供同样的计划。今天,我们介绍一个更好的选择。
逐步淘汰集装箱
当我们开始的时候,容器模型很容易计划和考虑。七年前,团队使用 CircleCI 的简单性,容器模型反映了这一点。小团队?拿四个容器。大型团队?拿 40。这是最简单不过的了。
容器模型限制了优化
在过去的几年里,我们一直在 CircleCI 上发布一些功能来帮助客户优化他们的管道:工作流来编排作业,上下文来管理跨项目的秘密,以及 orbs 来使配置可重用和集成更容易。有一点保持不变:容器。团队总是受到他们购买的容器数量的限制,许多团队要么资源闲置,要么开发人员排队等待开放的机器。
一个更好的选择:基于使用的循环定价
今天,我们将推出一种新的基于使用的定价模式。我们的新付费计划的客户将能够同时运行他们想要的任意多的资源,而不是被限制在一定数量的容器中。
它是如何工作的
CircleCI 的绩效计划有每个座位的基本费用(前 3 个用户 15 美元,然后每个额外用户 15 美元),然后根据信用包扩展使用。然后,客户在 CircleCI compute 上花费信用来运行他们的工作。CircleCI 提供多种类型的计算(Linux、Windows、macOS、Docker 等。),并且每种类型的计算每分钟花费给定数量的信用。例如,中型 Docker 计算资源(Docker 的默认值)每分钟花费 10 个信用点(0.006 美元),因此在中型 Docker 资源上运行 3 分钟的作业花费 30 个信用点(0.018 美元)。
根据您使用的计算机类型和您使用计算机的分钟数,您消耗的点数只有个;同时运行多少个作业并不重要。这意味着,如果您的管道中有 10 个可以独立运行的作业,它们将同时运行。
主要优势
与基于容器的计划相比,基于使用的计划有许多优势:
-
消除排队
使用任何付费使用计划(性能或定制)的团队几乎不会经历作业排队,因为他们不受一定数量容器的限制。CircleCI 会随着需求的增加而增加团队可用资源的容量,这样开发人员就不会在高峰时段排队。 -
同一计划中的 Linux、Windows 和 macOS
我们付费使用计划中的客户可以在同一计划中使用 Linux、Windows 和 MAC OS 计算。(免费层的客户可以访问 Linux 和 Windows) -
优化计算能力
基于使用的定价使 CircleCI 能够提供大量不同规格的不同计算类型。性能计划中的客户可以在其配置文件中指定资源类,以更改单个作业的计算资源能力。在小型 Docker 资源(1 个 CPU、2GB RAM)、大型 macOS 资源(8 个 CPU、16GB RAM)、中型 Linux GPU 资源(32 个 CPU、244GiB RAM、2 个 GPU)或两者之间的任何资源上运行作业。 -
按需付费
除了为满足高峰需求而支付 100 个集装箱的费用,性能计划中的客户还可以在高峰时段扩展到同时运行 100 个作业,在无人推送代码时则减少到零。团队只为他们使用的东西付费。
这对我意味着什么?
基于容器的计划将不再可供购买。如果您想要升级您的 CircleCI 计划,您可以从您组织的帐户设置页面将您的免费帐户升级到绩效计划,或者您可以与我们的销售团队讨论定制计划。
如果你已经在 CircleCI 支付了集装箱的费用…
你目前的计划将保持不变。您仍将有权访问所有付费的容器,并且管理员仍将能够在组织的计划设置页面中添加和移除容器。但是,如果您降级为免费帐户并希望在以后再次升级,您将只能注册绩效计划或定制计划。此外,容器计划中的客户将无法访问为使用计划保留的功能,如高级资源类、Windows 构建和 Docker 层缓存。
如果你目前免费使用 CircleCI,或者你是一个新用户…
CircleCI 的免费层现在每周将为每个组织提供 2500 个免费信用点,用于私有存储库(见下文关于开源存储库的信息)。自由计划的组织将继续使用我们的中型 Docker 计算,但现在也可以使用我们的小型 Linux 、中型 Windows 和中型 macOS 计算(即将推出)。
如果你在 OSS 项目中使用 CircleCI
CircleCI 将向我们的免费计划中的组织每月提供 400,000 信用点,用于开源存储库的中型 Docker 计算,但它们只能用于 Linux 计算。构建 OSS Windows 项目的组织仍然可以使用每周 2500 的免费配额,所有项目都可以在这些项目上使用,构建 OSS macOS 项目的组织可以通过联系 billing@circleci.com 请求免费的 macOS 访问。
如果您正在构建一个大型开源项目,并且您的项目需要更多的学分或更多的并发性,请与我们讨论为您的组织定制计划。
升级到性能
如今,CI/CD 不仅仅是关于自动化构建,它还关于实现软件开发生命周期的最大优化,以便您的开发人员可以花更多的时间做他们最擅长的事情:编写代码。CircleCI 引入性能计划是因为开发人员的生产力不应该被并发限制所限制。
目前使用容器计划的 CircleCI 客户可以通过向我们的支持团队提交一张票来升级性能。我们免费计划的客户可以通过访问其组织的帐户设置页面进行升级。如果您想了解更多关于绩效计划的信息,请查看我们的定价。
有问题吗?
阅读我们的账单常见问题解答文档或联系我们的销售团队。
开发运维没有死:平台工程如何实现大规模开发运维
原文:https://circleci.com/blog/platform-engineering-devops-at-scale/
DevOps 已经成为软件开发中事实上的方法论。有了它,软件工程师可以将操作掌握在自己手中,使用诸如基础设施作为代码 (IaC)之类的技术来自动化基础设施部署。
然而,随着 DevOps 的流行,现代应用程序开发的复杂性也在增加。开发人员必须学习新工具并维护基础设施,同时在功能开发的同时对 ops 任务进行编码和优先级排序。这些需求的压力不仅会导致生产力和效率的降低,还会表现为压力、倦怠和工作满意度的降低。
平台工程是成熟的 DevOps 实践的逻辑延伸,旨在与现有的 DevOps 概念一起工作,同时减轻相关的认知负荷。大前提是把自助服务带到 DevOps。平台工程师将常见 DevOps 流程的复杂性抽象到一个内部开发人员平台(IDP)中,该平台提供了一套用于构建和部署应用的标准工具。
本文将回顾大规模开发运维面临的挑战,以及为什么开发运维从业者希望通过平台工程来解决这些核心问题。
利用平台工程增强 DevOps
平台工程旨在增强 DevOps,而不是取代它。平台工程消除了开发人员学习、开发和维护基础设施工具和流程的需要。一些常见的例子包括使用 Helm 管理 Kubernetes 集群或编写 Terraform 代码来自动部署基础设施。相反,这些过程被抽象成一个 IDP,这样开发人员可以专注于构建软件。
DevOps 的认知负荷
尽管 DevOps 旨在使软件开发更加高效,但 DevOps 工程师经常面临许多增加他们认知工作量的高要求任务。让我们看几个常见的例子。
首先,DevOps 工程师经常需要为新的软件项目设计新的流程,比如持续集成和持续交付(CI/CD) 流程。DevOps 工程师可能还需要加快开发环境的基础设施,同时确保与生产环境的兼容性。这包括管理 Docker 文件、舵图和 Terraform 代码,这些都需要随着项目的进展进行定期维护和支持。CI/CD 管道也需要构建,尽管工程师可以从以前的项目中获取一些部件,但新项目的新测试或构建要求增加了额外的复杂性。
必须扩大或缩小现有流程,以满足当前需求。这些流程包括扩展基础设施以满足新的处理或存储要求,以及修改为一个小型工程师团队设计的现有工作流,该团队已经壮大。
DevOps 从业者也管理软件工程生命周期的许多方面的所有权。这包括在内部代码库和基础设施之上管理第三方工具和产品。其他开发人员也需要代码评审和会议来讨论潜在的解决方案选项,这需要专门的 DevOps 知识。
最后,DevOps 工程师必须确保系统记录正确,并且可以收集和分析指标。掌握不同软件应用程序的性能对于确保它们顺利运行至关重要。它还有助于工程师了解需要扩展的潜在领域。
除了满足服务级别协议(SLA)和其他内部业务目标之外,所有这些都给 DevOps 从业者带来了巨大的认知压力。每个任务和流程都会产生额外的开销,DevOps 实践者必须处理这些开销,通常会将资源从他们的创新和优化的主要目标上移走。
随着认知负荷的增加,相关的复杂性和压力也会增加,从而导致倦怠、错误和熵增加。这大大降低了团队的生产力,并最终扼杀了创新。
那么,平台工程如何降低负载,使工程师实现他们的首要目标呢?
平台工程师是做什么的?
平台工程师开发解决日常开发任务的 IDP,减少或完全消除开销。这使得 DevOps 能够更快地专注于提供真正商业价值的任务。
IDPs 的主要目标是为所有团队提供一个标准的、可重用的方法。设置存储库、分配正确的团队成员、启动环境以及配置部署管道已经内置于 IDP 工具和服务中。这些可以分为四个关键的管理领域:基础设施、部署、配置和用户访问控制。
无论是使用现成的 IDP 还是开发自己的 IDP,大多数 IDP 都为开发人员提供了用户界面(有些只有命令行界面)。通常,API 层请求不同的服务执行自动操作。这个操作可以是创建 git 存储库,使用 terraform 脚本启动数据库,或者任何其他常见的 DevOps 任务。然后,开发人员使用 IDP 自动初始化新项目的核心部分。
抽象出执行这些操作的底层细节可以防止开发人员在他们的方法上产生分歧。基础架构以标准方式配置和部署,使维护变得更加简单。
IDP 还通过自助工具促进开发人员自治。这可以防止因从头设计和构建基础设施而导致部署项目资源的时间过长。现在,开发人员可以使用 IDP 来完成他们的工作。IDP 还消除了为建立新环境而在待办事项中创建标签并等待它们被优先化和完成的需要。相反,开发人员可以通过 IDP 界面执行这些操作。
最后,境内流离失所者可以从一开始就做到安全和合规。这意味着平台工程师将安全性和合规性特性融入 IDP 服务,以便基础设施在部署时遵循安全性最佳实践。他们还可以确保 Git 管道被配置为在构建和发布之前自动扫描漏洞的代码。因此,减少了开发人员的负担和数据泄露的风险。
将平台工程融入 DevOps
现在,我们将检查一个假设的用例,平台工程方法如何改进 DevOps 团队的工作流程。
想象一下,一家企业设计的 web 应用程序都遵循相似的架构模式。有一个数据库,一个 API,和一个基于网络的前端。有一些预建的 Docker 映像和 CI/CD 模板。然而,一切都是手动完成的。DevOps 工程师必须为每个项目创建 Docker 文件,实现 Terraform 脚本,构建 Git 管道。然后,他们将与开发人员合作,确保环境满足他们的需求,并将监控和警报添加到生产基础设施中。这些任务与响应现有基础架构的 SLA 和执行定期维护一起发生。
这给 DevOps 工程师带来了巨大的压力。主要的问题是所有的任务都指向 DevOps 团队,不管它们有多复杂。因此,工作栈最终会增加开发人员开发项目的时间。
平台工程师可以通过将其构建到 IDP 中来自动化几乎所有这些工作。例如,开发人员可以从 IDP 请求一个存储库,然后由 IDP 创建它,而不是手动设置 Git 存储库。然后,IDP 将分配正确的用户组,并自动集成正确的 CI/CD 模板。
同样的模式也适用于创建开发环境和部署核心基础设施。IDP 充当开发人员请求服务和应用配置的自助服务平台,知道安全最佳实践和监控是默认内置的。IDPs 还可以在项目跟踪软件和文档模板中自动建立项目。
如您所见,平台工程师不会取代 DevOps 流程。他们通过将一组标准化模式构建到一个自助式内部开发平台中来增强它们。这消除了项目初始化的负担,因此团队可以立即开始提供业务价值,而不是花费项目的前几周来设置和解决初期问题。
此外,由于开发人员将更加自主,平台工程师可以专注于解决更大的架构挑战,并将其反馈给 IDP。通过这种方式,他们可以改善现有的服务,并加强未来的新系统。
结论
DevOps 越来越受欢迎,因为它为开发人员提供了管理应用程序端到端生命周期的自由。然而,这种强大的力量伴随着巨大的责任。随着时间的推移,它增加了大规模管理和操作这些流程所需的认知负荷。
因此,平台工程是每个拥有 DevOps 团队的组织都应该考虑的事情。将核心运营流程整合到一个 IDP 中极大地减少了 DevOps 从业者所需的固有认知负荷。将常见的 DevOps 流程转变为一组自助式 API 还可以缩短项目交付时间,因此开发人员可以迅速提供真正的业务价值。
如果您准备好开始利用平台工程扩展您的 DevOps 实践,请从注册一个免费 CircleCI 帐户开始,了解同类最佳的持续集成如何帮助您自动化关键流程,以获得更快、更一致的交付结果。
微服务的多语言持久性与多模型数据库
原文:https://circleci.com/blog/polyglot-vs-multi-model-databases/
微服务架构是一种应用系统设计模式,在这种模式下,整个业务应用程序由单个功能范围内的服务组成,这些服务可以按需伸缩。每个团队专注于一个单独的服务,并根据他们的技能或选择的语言来构建它。
除了灵活性之外,这种模式还提供:
- 由于松散耦合的服务模式,引入变更的风险更小
- 每项服务的独立扩展能力
- 集装箱化部署选项
这些特性使得微服务架构成为企业的热门选择。
在微服务时代之前,企业使用单片设计模式构建他们的应用程序,这些应用程序运行在现场的高容量服务器(数据中心)上。无论您的应用程序使用单片还是微服务设计模式,您都必须有一个存储解决方案来持久化它所管理的数据。
您的团队应该选择哪种数据存储解决方案来服务您的微服务架构?在本文中,我们将研究两种流行的方法,多语言持久性和多模型数据库,以及您可以用来确定哪个解决方案最适合您的应用程序的标准。
微服务的数据库管理挑战
单体应用通常只有一个数据库,而微服务则有一个更加分散的模型。这意味着在大多数现实部署中,每个微服务都有自己的数据库。
尽管每个团队都可以选择自己的编程语言和数据库,但是这种方法也带来了挑战。一个关键的挑战是为跨多个服务的请求实现查询和原子的、一致的、隔离的和持久的(ACID)事务。
在 monolith 应用程序中,实现 ACID 事务或实现查询逻辑很简单。要查询微服务,您需要格外小心地使用设计良好的方法来实现查询逻辑。
在软件行业中,有一些模式可以解决微服务的这种数据库管理挑战。接下来是对这些模式的简要概述。
每个服务的数据库
遵循每个服务一个数据库模式的微服务对于每个服务都有单独的数据库。这种模式在应用程序处理和计算级别以及存储级别促进了松散耦合。如果一个服务依赖于另一个服务的数据,它必须进行 API 调用。
传奇模式
有了 Saga 模式,微服务应用程序就有办法以类似事务的方式与多个服务进行通信。在这种情况下,单个业务事务作为每个依赖服务中的一系列事务执行。如果所有服务中的事务都成功,则整个事务都成功。在任何微服务中的事务失败的情况下,等效的反向事务回滚失败的事务。
API 组合模式
考虑一个基于多个微服务的业务应用。假设用户对一个关键性能指标(KPI)感兴趣,该指标依赖于来自三个不同微服务的数据。您可以部署一个 API 组合模式来简化这个过程,而不是期望用户为每个服务发出一个 API 请求。
使用 API 组合模式,您可以设计一个单独的服务来公开一个 API,该 API 负责抽象从多个服务中检索数据的技术细节。该服务将检索到的数据连接到内存中,并将 KPI 细节返回给用户。
事件来源模式
事件源模式提倡在微服务应用程序中存储每个传入事件,并为此铺平了道路。然后,您可以将这些持久化的事件用于审计,并在以后用于事件重放,例如在故障处理场景中。
共享数据库反模式
在这种反模式中,多个服务共享一个数据库。它构成了松散耦合的特性,并使微服务容易出现单点故障。注意,与前面描述的微服务设计模式相比,共享数据库方法是一种反模式。
什么是多语言持久性?
在大多数情况下,技术团队更喜欢每个服务一个数据库的模式,以从其松散耦合的服务中获益。随着数据库技术的进步,有多种数据存储解决方案可供选择。每一种都在其目标用例中表现最佳。
例如,如果您的微服务需要无模式、类似文档的存储解决方案的灵活性,您可以选择像 MongoDB 这样的文档存储。如果你的微服务需要在数百万或数十亿用户或实体之间建立连接,你可以使用 Neo4j 这样的图商店。
关键是您现在有多种多样的数据库选项来满足您的业务需求。这些选项在您的微服务用例中表现良好,这正是您需要多语言持久性的原因。
Polyglot 持久性涉及到在开发微服务时使用专门的数据库解决方案,因此每个微服务可以使用与另一个微服务不同类型的数据库。
以下是数据库技术类别及其最常见使用案例的列表:
- RDBMS:当需要严格的模式和 ACID 事务时,使用关系(RDBMS)数据库。
- 图形数据库:当快速遍历链接和关系是必要的时候,使用图形数据库。
- 基于文档的数据库:当需要灵活的模式或无模式结构,并且必须处理大量数据时,使用文档存储。
- 键值数据库:当快速读写很重要时,使用键值存储。
使用 polyglot 持久性模型,每个服务处理其数据库存储解决方案和数据库,以满足您的功能性和非功能性应用程序目标。
什么是多模型数据库?
多模型数据库是存储多种数据模型和类型的单个数据库引擎。使用一个后端引擎,您可以在多模型数据库中设计模式模型来存储关系、非关系、文档、键值和图形数据。
需要注意的是,并非所有多模型数据库引擎都支持市场上的每种数据模型格式。有些可能支持三种数据模型(关系、文档和图形),而有些可能支持四种模式模型(关系、文档、图形和键值存储)。
那么,多模型数据库引擎是如何在一个引擎中保持各种模式模型或数据类别的呢?答案在于语义模型。通常,多模型数据库通过抽象定义模型和从多个数据模型中检索数据的复杂性,使其数据定义语言和查询语言对用户来说变得简单。
组织可能使用多模型数据库引擎作为其后端选择的主要原因:
- 只需要管理一个数据库,而不是多个,但是仍然可以利用定义多个模型和存储不同数据类型的优势。
- 学习和使用一个语义模型与存储在多模型数据库中的数据进行交互。
多语言持久化与多模型数据库:哪一个最适合微服务?
微服务多语言持久性的优势包括:
- 服务保持松散耦合。
- 根据服务的专门数据存储要求,您可以选择最好、最可靠、可伸缩的数据存储,这些数据存储具有相对较长的使用历史和市场上经过验证的成功记录。
- 如果有多个供应商或开源数据库引擎可以为所选的数据模型提供服务,那么您可以根据团队的技能或偏好来选择数据库。
微服务的多模型持久性的优势包括:
- 您需要管理单个多模型数据库引擎,而不是多种数据库。
- 您只需要学习一种语义模型来与存储在数据库中的数据进行交互。
- 管理一种类型的数据库引擎而不是几种类型的数据库引擎使管理变得更加容易。
- 只需管理一个供应商许可证。
现在您已经看到了多语言和多模型持久性的好处,对于给定的用例,哪种解决方案优于另一种呢?答案可能各不相同。
如果您的微服务用例依赖于三种不同的模式模型,例如关系、文档和图形,请检查是否存在在单个数据库引擎中支持这三种模式的多模型数据库引擎。如果是这样,那么您应该为微服务使用多模型数据库。
但是,如果您的用例需要五种不同的模式模型,如关系、文档、列、键值和图形,您可能无法找到一个支持所有这些模型的多模型数据库引擎。在这种情况下,使用多语言持久性或多语言和多模式持久性的组合。
后续步骤
许多应用开发团队正在向基于微服务的架构发展。即使对于微服务应用,数据存储的重要性仍然很高。市场上已经发展出不同的模式来应对微服务的数据库管理挑战。
由于其松散耦合的好处和它提供的其他优势,企业已经广泛采用了每服务数据库的设计。当您使用每个服务一个数据库的模式来实现微服务时,您可以在多语言或多模型持久性之间进行选择。选择取决于用例、您的企业规模和您的工作模型。
如果您有一个大型企业,并且有一个由技术熟练的专业人员组成的专门的数据库团队来管理多个数据存储,那么您应该使用多语言持久性。多语言持久性允许您为每个数据模型段选择一个数据库引擎,该数据模型段在该类别中有一个经过验证的历史。
在多模型数据库空间的情况下,几乎所有的供应商都是新的,并且在生产环境中没有长期的记录。因此,如果您认为多模型持久性方法有风险,请选择多语言持久性。
然而,如果您是一家拥有相对较小团队的初创企业,您可以使用多模型数据库来满足您的微服务和异构数据存储需求。这将提高生产率,降低人工和硬件资源成本。
现在,您已经对这个领域有了足够的了解,可以选择最适合您的微服务应用程序的持久性模型了。为了继续优化您的微服务开发流程,考虑采用持续集成和持续交付来帮助您自动测试对您的微服务的更改和使用 Kubernetes 等流行工具部署您的应用。你可以从今天注册一个免费的 CircleCI 账户开始。
CircleCI 收购测试智能平台 Ponicode
今天,我们很高兴地宣布 CircleCI 已经收购了 Ponicode ,这是一个总部位于巴黎的用于分析源代码的人工智能引擎,其目标是帮助开发人员在其本地开发环境中生成更好的代码。
Ponicode 引起了我们的注意,他们专注于帮助开发人员处理他们最不喜欢的任务——围绕编写代码的辛劳——例如创作测试、注释代码、分析代码质量等等。Ponicode 围绕改善开发人员生活和更快投入生产的精神与我们的使命完全一致,并将帮助 CircleCI 管理变化,以便软件团队可以更快地创新。
使用人工智能更有效地进行测试
我们谈了很多 关于 代码覆盖率对于高质量产品代码的重要性,但是我们从我们的客户那里听到的响亮而清晰的是代码覆盖率本身,无论是 60%、80%还是 99%的覆盖率,都只是做出好决策的一部分。除了可以解决的问题,团队需要知道在哪里关注他们的测试优先级——代码库的哪个部分是最关键的?我们对 Ponicode 的收购将允许您应用人工智能的力量来确定需要填补的最高优先级缺口,并在许多情况下为您编写测试。
持续改进 CI/CD
我们的目标是提供最好的 CI/CD 工具。Ponicode 的测试编写和评分功能的添加进一步增强了这一目标。这次收购增强了我们为开发人员服务的能力,解除他们最不喜欢的任务,并继续支持他们快速、大规模地交付高质量的软件。
尽管 CircleCI 迄今为止为团队构建、测试、部署和发布他们的软件提供了最佳解决方案,但我们看到有很多机会可以帮助开发人员处理他们在个人工作中面临的日常挑战。
引入 Ponicode 团队不仅使我们能够为客户带来新的功能,还将帮助我们将现有的平台功能直接交付给开发人员,在他们的本地开发环境或 IDE 中,开发人员的工作效率最高。
CircleCI 现在将能够通过单元测试支持和代码覆盖优先级来扩展我们的工具,使开发人员能够在他们每天已经使用的工具和环境中编写更好的软件,同时也是一个中央 CI/CD 平台,在这里您可以将您的工作与团队的其他成员以及组织的其他成员的工作结合起来。
你能期待什么
如果你现在正在使用 Ponicode,请放心,当我们努力将它集成到 CircleCI 中时,这项服务仍将对你可用。我们邀请您下载 Ponicode VS 代码扩展并查看它。您还可以在 Ponicode 单元测试平台中看到更多,这是一个沙盒应用程序,您可以在其中尝试 Ponicode 的单元测试生成功能。
此次收购还将我们的全球足迹扩展到了法国,因为我们欢迎总部位于巴黎的 Ponicode 团队成为 CircleCI 的正式员工。对于我们来说,加快创新周期和减少开发人员的工作量是一项令人无比兴奋的任务。如果你同意,加入我们。我们正在招聘。
如果你是 CircleCI 的新手,现在就注册看看持续集成如何帮助你今天发布更好的软件。
让您的团队为持续部署做好准备| CircleCI
原文:https://circleci.com/blog/preparing-for-continuous-deployment/
任何 DevOps 团队的一个关键目标是缩短软件开发周期,并提供高质量软件的持续交付。大多数公司没有继续下一个逻辑目标,即持续部署,而是止步于此。开发的代码自动进入测试阶段,然后,成功的测试触发手动验收步骤。只有这样,应用程序才能部署到生产环境中。
本文描述了您需要建立的一些计划和实践,以便您的团队能够使用连续的部署过程频繁而可靠地部署应用程序。
持续交付与持续部署
在我们探索持续部署的世界之前,理解持续交付的终点是很重要的。随着您的团队走向持续部署,还有一些过程和设计需要考虑。当你接手这个项目时,保持 DevOps 心态也很重要。我将在文章的后面解释这是什么。
在持续的交付过程中,开发人员在每次变更时都要重新构建代码库,以确保没有人会破坏构建。用户验收测试和部署可能完全是手动步骤。相比之下,连续的部署试图自动化验收测试和部署,这样构建就可以自动地、定期地进入生产。
通常,连续交付回答了“它构建并运行了吗?”而持续部署回答“它工作吗?它部署了吗?”
持续部署的先决条件
要将您的应用程序转移到持续部署中,您的团队将需要采用技术和人员流程变更。不要求使用特定的工具或过程。相反,使用一致的开发和部署方法,并以自动化作为补充。
发布新版本时,应用程序的架构和停机时间会影响部署频率。本文的下一节将探讨其中的一些,为决定连续部署选项提供一些背景知识。
敏捷团队
持续交付管道的关键部分是敏捷团队。这些团队不断地构建和交付软件。您的团队对小型快速开发周期越熟悉,您的持续部署实践就越成功。
与每隔几个月进行重大变更相比,频繁地自动化、测试和部署小的变更要容易得多。如果出现问题,回滚小的更改也容易得多。
DevOps 心态
确保您的 DevOps 团队从一开始就一起工作来构建软件。从设计到部署,他们应该在功能、错误修复、构建和安全性方面进行协作。
开发和部署高质量的软件需要我们 DevOps 团队所有成员的持续投资。孤立的团队、缺乏安全性或测试不充分会破坏开发的连续性,并使部署变得困难。如果您的团队不相信持续部署是可能的,它就不会发生。
自动化测试
自动化测试工具应该已经是您的持续集成和持续交付(CI/CD)管道的一部分,以确保您交付的软件符合质量标准。如果没有一套全面的测试来验证您的应用程序的所有现有的和新的组件,您的团队就不能依靠该软件来执行。随着您在应用程序中构建更多的功能,确保对所有功能进行回归测试变得更加重要。
稳定的 CI/CD 管道
因为持续部署建立在持续交付的基础上,所以拥有一个稳定的 CI/CD 渠道来扩展将为您的团队取得成功做好准备。如果您的团队不能可靠且一致地交付他们的应用程序,他们也不能可靠且一致地部署应用程序。让专门的 CI/CD 工程师照看您的管道可以保持开发成果的一致性。你可以从电子书中了解更多如何成为 CI/CD 工程师。
灵活的架构
您的应用程序的体系结构也可以在持续部署中发挥重要作用,因为它会影响停机时间。例如,一个团队使用负载平衡器背后的可伸缩应用程序节点来构建应用程序。然后,该团队可以在将负载平衡器从一个版本切换到下一个版本之前,自动修补一批节点。
对数据库层采用非破坏性的方法还可以减少手动干预的需要。Martin Fowler 和 Pramod Sadalage 的文章进化数据库设计更深入地回顾了敏捷环境中的数据库方法。
开始连续部署
一旦在您的环境中嵌入了正确的组件,下一步就是检查您当前的端到端构建和交付流程。寻找任何可以简化或自动化的流程或操作。持续部署的目标不是跳过每个手动步骤。我们的目标是尽可能地自动化每一步,或者简化每一步,使其尽可能地快速和无痛。
例如,您仍然需要手工检查代码和合并拉请求。您可以快速、频繁地完成这些过程,以便尽快将特性和修复程序应用到您的应用程序中。
考虑两个我们可以自动化或简化的手动组件:
- 验收测试自动化
- 发布业务流程
验收测试自动化
在过去的几年里,终端用户测试自动化和机器人流程自动化工具取得了长足的进步。将这些工具添加到您的验收测试阶段可以帮助减少手工测试。
这些工具要求测试人员尽早参与开发过程。这些测试人员可以通过使用用户故事来记录非技术人员的测试。像 CloudQA 这样的工具使测试人员能够记录终端用户操作应用程序的情况。然后,您可以编写这些操作的脚本,并将它们添加到您的测试套件中,以便新功能和回归测试在整个验收测试阶段都是自动的。然后,您可以满怀信心地投入生产。
发布业务流程
发布流程编排通过验证您的应用部署了所有关键组件,增强了您的持续部署渠道。
将新特性快速部署到生产环境听起来不错,但是您仍然需要确保依赖关系和文档存在,并且您已经更新了数据层以反映这些变化。
添加发布协调工具和流程提供了一个由一组清晰的业务规则定义的规划和协调层。这一层可以包括修改基础设施和部署应用程序组件的自动化步骤。它将自动化与手动操作(如批准请求)相结合。版本编排确保您的团队已经正确配置了生产版本。
使用像蓝绿色部署这样的技术自动化您的部署也可以将您环境的停机时间减少到几乎为零。
后续步骤
优化您的部署流程不是一次性的活动。应该定期回顾您的 DevOps 团队的工作方式。
您还应该让开发人员体验工程师(DXEs)跟踪、衡量和优化开发人员的工作方式,并为开发管道寻找更好的工具和流程。要了解更多信息,请阅读为什么开发人员体验工程师(dxe)是加速您业务发展的关键。
让您现有的持续交付管道达到高水平的成熟度将有助于确保您的持续部署管道一致地工作。通过在此基础上添加验收测试自动化和发布流程编排,您将获得一个持续的部署管道,进一步简化您的 DevOps 团队的开发流程,并为您的团队提供专注于未来增强的机会。
进化架构的先决条件
原文:https://circleci.com/blog/prerequisites-for-evolutionary-architectures/
设计灵活多变的软件可以说是最重要的架构属性。
我经常听到其他软件架构师说“性能怎么样?”或者“安全性呢?”
我并不是说这些其他属性在早期考虑时不重要。他们是。然而,如果我们针对变化(可发展性)优化我们的架构,当我们发现性能问题或安全漏洞时,我们可以改变我们的系统来帮助解决它。快速响应这些问题的能力正是进化架构如此重要的原因。
进化中哪些属性是重要的?
你可以像看待进化建筑一样看待一个物种适应环境的方式。为了成功,动物需要产生具有优势特征的新一代,对环境的反馈做出反应,并通过退回到有效的方式为失败留下空间。
软件也差不多。你需要确保它是可适应的,并且你正在根据什么有效来改变你的系统。我们可以通过几种主要方式来创建这些适应性强的架构:
- 挑选约束条件以支持快速变化
- 将部署和发布的概念分开
- 收集并分享快速(适当)的反馈
- 发展中
- 生产中
- 建立一种积极响应的文化
选择支持可变性的约束
为了支持软件的进化,我们需要知道软件的约束和软件运行的环境。
作为软件架构师和开发人员,我们可以控制构建和运行软件的环境的某些方面。这里有一些我们可能要考虑的约束来支持变化/进化。
选择合适的建筑材料
在我职业生涯的开始,我相信任何图灵完全编程语言都是等价的,选择哪种语言并不那么重要。随着我接触越来越多的编程语言、范例、库和框架,我意识到我们选择的“建筑材料”对我们软件系统的固有属性有着巨大的影响,尤其是可变性。
当您开始构建新系统时,请考虑以下几点:
- 支持允许您从更大更复杂的结构中解析所需数据的语言、库和方法。
- 如果您使用的是强静态类型语言,请考虑使用推断类型的语言,以减少需要通过代码库进行的更改。
- 如果您使用弱类型语言,请考虑您可能需要什么库或习惯用法来添加对允许的更改类型的约束,以及它们如何发生(否则您将身处蛮荒的西部,一切都会发生)
- 支持不可变的数据结构——不可变性限制了状态在执行程序中改变的方式,从而简化了对状态的推理,尤其是在多线程环境中。如果你的语言不支持默认的不可变数据结构,那么大多数语言都有大量的库(寻找可重用内存的持久不可变数据结构)。
- 比起命令式方法,更喜欢声明式方法。
- 您对问题的理解是否足以在早期选择一个框架,或者您是否应该通过使用小型库构建解决方案来保持灵活性?选择支持反馈和部署的工具和方法
为了发展,我们的软件需要容易和快速地发布,并且我们需要在开发和生产过程中关于它的适当性的反馈。因此,我们应该选择支持这些属性的工具和方法。
以下是一些需要考虑的事项的非详尽列表:
- 连续累计
- 连续交货
- 黑暗部署
- 金丝雀部署
- 蓝色/绿色部署
- 自动化测试
- 自动化警报/监控
虽然这听起来很可怕,但是将生产测试和其他测试方法结合起来是很有用的。有时,减少测试并允许某些东西在失败时发出警报可能是值得冒的风险,甚至有利于检测生产中的实际问题。生产是唯一真正的测试环境。然而,这是一个依赖于问题、架构等的风险判断。
实现这些方法中的一些或全部可以使您能够通过快速向前修复来响应 bug,而不是采取过度测试和在 bug 发生时恢复的更具防御性的方法。
尽可能保持小规模
对于参与编写系统的每一个额外的人来说,你在你的团队中成倍地增加了交流的途径。如果你能保持你的团队和团队的数量尽可能的少,你就减少了实现每一个改变所需要的沟通和协调的数量。
我将把这个原则延伸到保持团队数量尽可能少。
在有限数量的(正确的)人的约束下工作将会产生解决问题的创新方法。只要确保其中之一是不要工作更长的时间(因此,考虑工作时间的上限作为另一个约束!).
全组织系统思维
即使是最“实体”的企业也通过软件进行大量的客户互动(即使是 B2B),因此你的组织应该将软件视为创收的主要手段。
将“项目思维”强加于软件开发是一个坏主意。试图在截止日期和预算内实现一些“特性”通常是必要的,但是如果你的软件的每一个变化都是这样发生的,那么短期的关注永远不会导致对产品或平台及其质量属性的长期考虑。
您可以使用项目来管理预算,但在选择实施什么时,请始终考虑产品或平台。
将部署与发布分开
为了进化,我们的软件必须产生新的“突变”一代。如果您可以在 canary 部署中部署您的变更,或者甚至,根据变更,在一个黑暗的部署中,您可以在唯一真实的测试环境,即生产环境中测试变更。
在不强制要求特定架构(例如微服务、事件流、模块化整体)的情况下,领域驱动开发(DDD)和事件风暴对于确定部署单元的边界非常有用。
不要孤立地考虑静态建模如果你孤立地构建一个数据、组件或类模型,你经常会关注错误的属性。例如,您可以对大学学生的数百个属性进行建模,但助学金系统可能只关心身份、费用和支付信息以及改变这些属性状态的事件,而学生服务关心更多的个人信息。使用系统的动态方面来引导哪些信息对该上下文是重要的。
考虑事件和流程通常会发现系统的组件(部署单元)。每个发送和/或接收消息的高级流程都是一个潜在的组件。
下面列出了一些有助于分离部署和发布的技术:
- 功能切换
- 通过抽象进行分支
- 持续集成/持续交付
- 不可变的服务器——将运行时环境的各个方面打包到不可变的映像中,使得部署和回滚的风险大大降低。
- 读取数据库上的模式——它们使得在部署中添加数据更加容易,前提是您总是“增加”您的模式。
- 采用 API“增长”——总是添加到您的 API 中,并且只在逻辑上贬低功能或数据,这对于客户端应用程序来说更容易处理,并且“突破性”的更改实际上是一种新的 API,所以要这样对待它。想想如果您不能完全控制您的客户端,并且不能相信它们只解析它们需要的数据,那么如何处理返回的数据(例如,是否所有的客户端都实现了宽容的读取器模式?).这就是即使在服务到服务的调用中使用 GraphQL 也有好处的地方,因为它的固有约束假设只返回所请求的内容(并且您得到了模式自省)。
快速适当的反馈
我喜欢把软件想象成像生物有机体一样“生活”在越来越大的生态系统中。
插图 1:生态系统反馈
图 1 展示了我们的软件“生活”的生态系统的层次。从这个例子中我们可以看出,内部生态系统会受到外部生态系统变化的影响,但是反过来,内部生态系统也会引起外部生态系统的变化。
此外,该图中未示出反馈频率的概念。
- 软件环境反馈以微秒/秒/分/小时为单位进行测量/采样。
- 团队/产品环境反馈可能在几天或几周内被测量/采样。
- 组织/部门的环境反馈可能在几周或几个月内被测量/取样。
- 全球/市场环境反馈可能会按季度/每半年/每年进行衡量/取样。
下面的插图没有详尽地列出可能用于提供反馈的度量标准和技术,但给了你一些你可能要考虑的想法,但和往常一样,没有银弹和 YMMV。
说明 2:软件环境度量
微生态系统转化为运行时环境和用于开发软件的软件开发实践。上图给出了一些可以提供该级别反馈的指标和技术。
说明 3:团队/产品环境度量
生态小区(或栖息地)的生物学概念转化为软件所属的团队和/或产品,图 3 给出了这一级别反馈的一些度量和技术示例。
说明 4:组织/部门环境指标
图 4 显示了一些在组织或部门级别提供反馈的度量和技术的例子。
说明 5:全球/市场环境指标
最后,说明 5 举例说明了目标市场或其他市场的潜在反馈机制。
正如你所看到的,我所建议的示例度量是生产/运行软件的过程的度量和可能影响该软件或受其影响的外部因素的度量的混合。重要的是,不要只关注于度量你可以直接改变的东西,还要度量你只能间接影响的因素,以使你的软件也能适应这些压力。
虽然我已经给出了一些指标,但是您应该从在每个生态系统级别中确定 2 到 5 个指标开始。我还尝试将较低级别的指标映射到上述生态系统中的指标,以确保某个指标正在推动期望的行为。
建设回应型文化
最后,如果没有一种拥抱变化、寻求变化并因变化而繁荣的文化,这些技术都无法有效实施。这种文化的典型特征就是你在敏捷和精益书籍/课程中看到的所有东西:
- 无责备文化
- 赋予团队/个人权力
- 将整个问题的责任和权力委托给团队
- 更小的团队以减少通信网络
- 清晰、高水平的目标/目的,带有清晰、可衡量的目标(参见“快速、适当的反馈”)
- 等等。
然而,我认为重要的是要强调这种文化需要渗透到整个组织中。有一个拥有这种文化的软件开发团队或部门是很好的,但是如果组织的其他部分与该团队的接口是在一个严格的等级命令和控制文化中,这将导致摩擦,并最终在响应变化时更加低效。那么,如果组织不认同这种文化,你会怎么做?
理想情况下,你可以说服“C”级管理层和董事会,这种文化是必需的,并展示如何通过采用一些建议的策略,通过一些“本地化”的成功来实现这种文化。
然而,我在将“C”级管理层关注的指标(往往是组织/部门环境或全球/市场环境中的指标)与以下生态系统中的指标联系起来方面取得了一些成功,以显示一个指标中的“移动指针”如何影响其他指标。我还发现,这有助于开始关于软件不是组织的“成本”,而是大多数组织未来创收的主要手段的对话。证明软件不仅仅是自动化过程,而且是在新的市场中创造与客户互动的新方式。这反过来会引起从项目集中开发到产品/平台集中开发的转变。
如果你试图说服高层管理人员/董事会,展示它如何影响他们关心的指标是非常强大的。
摘要
我在这篇文章中涉及了很多内容。然而,如果你只能从这里学到三样东西,我希望是:
- 在每个生态系统中挑选几个您希望改变的属性,并为它们确定度量标准(最好将较低级别的度量标准与生态系统“阶梯”中较高级别的度量标准联系起来)
- 选择几个你还没有实现的“约束”,它们可能有助于支持上述所需的变化,并考虑如何实现这些约束
- 使用你选择的约束来指导你使用的“材料”和你需要的“文化”。例如,如果您想更有效地处理数据/消息结构中的变化,您可能会决定专注于结构类型化和只增加接口和数据存储的方法。这可能会让你选择某些语言/库来支持结构化类型、简单的数据解析、宽容的读者模式和不可变的数据库。
当谈到进化架构时,没有放之四海而皆准的解决方案。相反,重要的是在适当的时间尺度上收集反馈,随着你的学习和成长调整你的方法,不要试图一下子改变一切。
我希望这篇文章能给你一些启发和一些实用的方法。
Chris Howe-Jones 是顾问 CTO、软件架构师、精益/敏捷蔻驰、开发人员和 DevCycle 的技术导航员。他主要从事 Clojure/ClojureScript、Java 和 Scala 方面的工作,客户从跨国组织到小型创业公司。
确定性构建-持续集成| CircleCI
原文:https://circleci.com/blog/preserve-build-integrity-prevent-problems-deterministic-builds/
为什么确定性构建很重要?
再现性和可靠性。
客户在支持单上最常说的话是,他们的构建突然失败了,尽管他们最终“什么都没有改变”。这几乎从来不是真的。
在这篇文章中,我想谈谈确定性构建。这里的想法是在一个构建中尽可能减少变化的部分。这意味着更少的神秘的失败构建,更少的支持票(对你和我们来说),并且可能通过简单地重新运行构建来完全相同地复制意外删除的二进制文件。我们都知道测试很重要。然而,一个构建中的变量越多,您对测试的信心就越低。随着目标的移动,在一个场景中通过的测试可能在 3 次提交后就不再通过了,即使有“相同的”依赖关系。
通过确保您的构建是真正确定的,您将减少您的构建中变化的部分,以便增加您的测试套件中的信心和增加可再现性。
在持续集成(CI) 的背景下,开发人员或组织可能会有许多方法来优化他们的构建。有一些众所周知的优化:减少构建时间或者减少反复测试。然而,这些策略忽略了大局。诸如此类的优化都集中在一个单一的构建上,只考虑了一部分的情况。考虑过去和未来的构建以及它们之间的关系也很重要。
把你的构建想象成一个连续体:随着时间的推移,一系列相互关联的部分。
通过现在采取主动措施,您可以防止将来出现问题,并保持以前构建的完整性。
什么是确定性构建?
确定性构建可以在提交时、明天甚至下个月“实时”运行,并以完全相同的结果结束。您可以想象从一个构建第一次运行时获取它的“指纹”,并在重新运行时再次这样做。它们应该完全匹配。
我们所说的相同的结果或指纹是什么意思?构建应该能够在将来重新运行,第一次失败的测试再次失败。第一次通过的测试应该会再次通过。构建会产生工件吗?理论上,这些文物应该是完全一样的。对于日志文件或截屏工件来说,这通常很容易,但金鹅是二进制文件。能够运行一个生成软件 X.Y 版本二进制文件的构建,并在 3 个月后生成完全相同的二进制文件,这是使用确定性构建所能达到的高度。
我如何使我的构建更具可复制性?
版本锁定
这可能是你能做的最简单的改变。尽可能声明依赖关系的最具体版本。例如,对于npm
,最好运行npm install react@16.0.0
而不是npm install react@16.0
或npm install react@">=16"
。
同样的道理也适用于 Docker 图像。最好这样做:
version: 2
jobs:
build:
docker:
- image: golang:1.9.3
steps:
...
比它应该做的:
version: 2
jobs:
build:
docker:
- image: golang:1.9
steps:
...
在这两种情况下,去掉版本的 bugfix/patch 号(第三个整数)允许您正在使用的依赖项的版本从您的下面更改出来。今天,- image: golang:1.9
可能运行版本 1.9.3 的 Docker 镜像,但是下周的构建可能运行版本 1.9.5 的 Docker 镜像。
当然,总有一个陷阱。包含 bugfix 发布号意味着如果有 bugfix(或安全补丁),你的软件不会自动开始使用它。你必须决定什么样的维护水平让你感到舒服。1.9.3
是确定性最强、最不“前沿”的,而1
是确定性最弱、最“前沿”的。
贮藏
是的,缓存。大多数人会考虑缓存一项技术来减少构建时间,这是事实,但它也可以用来增加构建的可靠性。
怎么会?有时,缓存用于存储编译后的二进制文件或以 CPU 密集型方式生成的数据。在这种情况下,我们节省时间,但不一定获得可靠性。当构建通过网络检索多个文件时,缓存可以提高可靠性。怎么会?如果网络暂时中断了,该怎么办?DNS 失败?一旦我们缓存了这些资产,我们就不需要担心这些问题会导致构建失败。如果当我们缓存了所有的依赖项时npm
的存储库关闭了,理论上我们是好的。构建将继续进行,不会出现问题。
缓存也有助于再现性。如果从互联网上的某个地方下载的资产从现在起两个月后不再可用,因为其所有者没有支付他们的托管费用,该怎么办?如果一个依赖项被释放,并且意外地重用了一个版本标签(Canonical 最近在 Ubuntu 17.10 中就是这么做的),该怎么办?或者更糟,如果这是恶意发生的?缓存确保了相同的版本,更确切地说,是构建开始时运行的相同代码,是它将再次运行的代码。
摘要
一个构建可能永远不会是 100%完全确定的。出于安全原因(安全/补丁发布)或者某种公司政策,一个构建可能必须包含一些“移动部分”。这篇文章背后的真正意义是确保你的构建对于你的用例来说是尽可能确定的。这将确保您的构建在可靠性和可再现性方面得到优化。
主动集成测试| CircleCI
Lumigo 软件工程师 Idan Sofer 概述了他如何利用 CircleCI 在一个完全无服务器的环境中主动根除脆弱的集成测试。
当你开发一个无服务器的应用程序时,集成测试是至关重要的。产品的顺利运行比以往任何时候都更加依赖于代码与不受您控制的第三方服务的良好配合。
问题是,在无服务器环境中编写集成测试会很快变得复杂。随着资源的增加,集成点的数量也在增加,复杂性存在于这些资源的配置中。
集成测试失败有几个原因。有时,测试会运行很长时间并超时。有时,一个资源可能会受到另一个无效资源状态的影响。例如,在 Lumigo 这里,我们遇到了一个问题,一些资源依赖于 DynamoDB 表,该表需要在每次测试开始之前为空。问题是,有时项目会从删除批次中被拒绝,并且没有被删除,导致测试失败。
这是一个不可靠测试的例子,失败与开发者新添加的特性没有直接关系。诸如此类的有问题的测试真的会拖团队的后腿,所以我们想要找到一个更好的测试策略,在团队被它们阻碍之前识别出易出问题的测试。
集成测试策略的目标
让我们从定义这个项目的目标开始:
- 我们希望生成一份最常失败的测试的报告,这样我们就可以知道哪些测试是不可靠的,需要在 R&D 团队试图合并功能时被阻止之前解决。
- 其次,我们想要通过记录每个失败测试的平均时间来识别运行最慢的测试。
- 最后,我们想要使用这个策略来识别任何干扰其他测试并导致它们失败的测试。
我们的集成测试堆栈
由于我们与 CircleCI 合作,我们将使用 CircleCI Insights 来生成报告。
我们开始吧
我们有一个专用于集成测试的共享环境。每个开发人员在提交新特性的代码以运行集成测试时都会部署特性堆栈。我们使用这个共享环境来运行我们的主动集成测试策略,模拟我们开发人员的工作。为此,我们复制了完整的开发周期,我们的每个工程师在集成环境中测试一个新特性时都会遵循这个周期。这意味着复制栈中所有 AWS 资源的完整部署,配置它们,并运行 Mocha 集成测试。
CircleCI 工作
我们想同时模拟几个开发人员的周期。为此,我们使用并行运行的 CircleCI 作业,每个作业代表一个开发人员。
这些作业中的每一个都运行相同的步骤(一个步骤是可执行命令的集合),只有一点不同——每一个都使用不同的变量来定义单独的堆栈:developer 1、developer 2 等。
例如:
jobs:
deploy-and-test-developer1:
executor: my-executor
steps:
- checkout_utils
- checkout_code
- prepare_deploy
- deploy_and_test:
to: "developer1"
deploy-and-test-developer2:
executor: my-executor
steps:
- checkout_utils
- checkout_code
- prepare_deploy
- deploy_and_test:
to: "developer2"
deploy-and-test-developer3:
executor: my-executor
steps:
- checkout_utils
- checkout_code
- prepare_deploy
- deploy_and_test:
to: "developer3"
deploy-and-test-developer4:
executor: my-executor
steps:
- checkout_utils
- checkout_code
- prepare_deploy
- deploy_and_test:
to: "developer4"
CircleCI 命令
由于每个作业中的步骤是相同的(变量除外),我们使用 CircleCI 命令来定义作业中要执行的步骤序列,这使我们能够在多个作业中重用单个命令定义。
为了处理用于单独标识每个作业的变量,我们使用了命令参数,它允许我们用一个键传递变量的字符串值。
下面是一个带有参数的部署和测试命令示例:
commands:
checkout_utils:
description: "Checkout various utilities"
steps:
# checkout git utils
checkout_code:
description: "Checkout code and test it"
steps:
- checkout
- run:
# Avoid annoying double runs after deploy.
# See https://discuss.circleci.com/t/job-runs-even-when-tags-ignore-filter-is-triggered-when-combined-with-branches-only/20664
name: Check if tagged
command: |
tags=$(git tag -l --points-at HEAD)
echo "Tags $tags"
if [[ ! -z "$tags" ]]
then
echo "A tagged commit, skip..."
circleci step halt
fi
- run: sudo chown -R circleci:circleci /usr/local/bin
- run: sudo chown -R circleci:circleci /usr/local/lib/python3.7/site-packages
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "requirements.txt" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run:
name: install dependencies
command: |
python3 -m venv venv
. venv/bin/activate
pip install -r requirements.txt --upgrade
# https://discuss.circleci.com/t/activate-python-virtualenv-for-whole-job/14434
- run: echo "source venv/bin/activate" >> $BASH_ENV
- run: pip install pytest-cov
- run: pre-commit install
- save_cache:
paths:
- ./venv
key: v1-dependencies-{{ checksum "requirements.txt" }}
prepare_deploy:
description: "Install and configure what is needed in order to run deployment scripts"
steps:
# integration-test setup
deploy_and_test:
description: "Deploy code and test it"
parameters:
to:
type: string
default: "developer1"
steps:
# run the deploy script for the first developer
- run: |
set +Eeo pipefail
cd ../utils/deployment/sls_deploy && python3 main.py --env << parameters.to >> --branch ${CIRCLE_BRANCH}
# deploy integration-tests
- run: cd ../integration-tests && export USER=<< parameters.to >> && ./scripts/deploy.sh
# run integration tests
- run: cd ../integration-tests && npm run test-proactive
- store_test_results:
path: ~/integration-tests/src/test/test-results
- store_artifacts:
path: ~/integration-tests/src/test/test-results
CircleCI 工作流程
为了准确模拟我们的日常部署和测试过程,我们需要并行运行这些作业。
使用circle ci workflows——一组用于定义作业集合及其运行顺序的规则——我们编写了一个工作流,用于并行运行五个开发人员作业,另一个工作流用于按顺序运行它们。
安排测试
我们不想在工作时间干扰团队,所以我们使用 Cron 语法在 UTC 时间安排工作流在晚上运行。
一旦我们将这些工作流设置为在主分支上运行,我们就能够成功地模拟我们的日常功能交付过程。
在这个阶段,我们有这样的东西:
workflows:
version: 2
deploy-and-test-parallel-workflow:
triggers:
- schedule:
cron: "30 21 * * *"
filters:
branches:
only:
- master
jobs:
- deploy-and-test-developer1
- deploy-and-test-developer2
- deploy-and-test-developer3
- deploy-and-test-developer4
生成测试报告
接下来,我们需要将我们的报告提供给 CircleCI Insights 。
首先,为了生成 JUnit XML,我们使用了 mocha-junit-reporter 插件,并将测试脚本(在 package.json 中)设置如下:
"test-proactive": "mkdir -p test-results && MOCHA_FILE=test-results/junit.xml mocha --reporter mocha-junit-reporter --timeout 300000 --recursive *.js || (cat test-results/junit.xml && exit 1)",
为了上传和存储构建的测试结果,我们使用了名为 store_test_result 的 CircleCI 步骤。它从 XML 文件中收集测试元数据,并使用它来为您的工作提供洞察力。
结论
有了这个管道,我们可以每天晚上使用 CircleCI 工作流复制我们的整个开发过程,以模拟我们的无服务器环境(使用无服务器框架)的完全部署,并运行我们所有的集成测试。
然后,我们可以从一个 XML 文件(带有mocha-junit-reporter
)中收集测试结果元数据,存储它,并在 CircleCI Insights 中查看它。这让我们可以看到哪些测试失败了,以及失败的原因。这里有一个这样的报告的例子:
它显示有三个失败的测试是不可靠的。例如,其中一个测试超时:
同样值得注意的是,对于成功的作业运行,我们可以在 Test Summary 选项卡下看到最慢的测试:
在真正的集成测试失败并阻止我们合并完全不相关的特性之前,拥有这些信息是非常有价值的。这不仅节省了我们修复故障的时间,也节省了我们找出故障原因的时间。
测试愉快!如果你对在你的开发工作流程中实现这个测试策略有任何问题,你可以在 Twitter @AiSofer 上找到我。我也很想听听你们用来识别不可靠测试的工具和技术。
Idan Sofer 是 Lumigo 的软件工程师,lumi go 是 SaaS 的一个平台,用于监控和调试无服务器应用程序。
面向生产就绪的 Dockerized Django 应用| CircleCI 的持续集成
原文:https://circleci.com/blog/production-ready-dockerized-django/
本教程涵盖:
- 克隆和设置演示项目
- 添加 CircleCI 配置文件,包括作业结构和工作流程
- 使用并行和分割测试
持续集成已经成为软件项目中被广泛接受的实践。随着越来越多的技术被引入到持续集成和软件开发中,开发人员正在寻找实用的方法来从中获益。涵盖玩具示例的基础教程对于现实生活中的从业者来说并不总是足够的。作为 Django、Docker 和 CircleCI 的实际用户,这无疑是我的一个痛点。这就是我写这篇教程的原因。
在本指南中,您将从一位同行那里了解如何为生产就绪的 Dockerized Django 3.2 应用程序建立持续集成管道。
为了让你可以轻松上手,我创建了一个 demo 项目供你从 GitHub 克隆。
当您完成教程后,您项目的 GitHub repo 的每次推送都会自动触发构建,并将覆盖文档上传到 Codecov.io 等代码覆盖云提供商。您甚至会了解如何使用 CircleCI 的并行模式来更快地运行您的测试,并实现更短的迭代周期。
先决条件
为了从本教程中获得最大收益,您需要:
我们的教程是平台无关的,但是使用 CircleCI 作为例子。如果你没有 CircleCI 账号,请在 注册一个免费的 。
因为每个 Django 项目都是不同的,我将通过使用演示项目来引导您完成建立持续集成管道的步骤。
我建议你从头到尾看两遍这个教程。第一次,使用演示项目。对于第二次检查,使用您自己的 Django 项目,从概念上重复相同的步骤,但是根据需要调整过程。
在教程的后面,我将提供一些调整你自己项目的步骤的技巧。现在,专注于跟随演示项目,这样你就能对整个过程从头到尾是如何工作的有一个直觉。
克隆演示项目
在这一步中,您将 git 克隆代码库,并确保您对代码库能够在本地运行测试感到满意。本质上,这个代码库遵循了 Django 文档中相同的经典七部分教程。演示项目代码库有一些不同。
主要区别在于:
- 演示项目维护基于函数的视图,而不是教程中使用的基于类的视图。
- 演示项目有一个
config
文件夹,其中有一个settings
子文件夹,包含base.py
、local.py
和production.py
,而不仅仅是settings.py
。 - 演示项目有一个带有子文件夹
users
的dockerized_django_demo_circleci
文件夹。
Git 克隆代码库:
$ git clone https://github.com/CIRCLECI-GWP/dockerized-django-demo.git
以下是结果输出:
[secondary_label Output]
Cloning into 'dockerized-django-demo-circleci'...
remote: Enumerating objects: 325, done.
remote: Counting objects: 100% (325/325), done.
remote: Compressing objects: 100% (246/246), done.
remote: Total 325 (delta 83), reused 293 (delta 63), pack-reused 0
Receiving objects: 100% (325/325), 663.35 KiB | 679.00 KiB/s, done.
Resolving deltas: 100% (83/83), done.
既然您已经克隆了演示项目,那么您就可以测试 Dockerized 演示项目的本地版本了。
在本地运行测试
在这一步中,您将继续上一步,以确保测试可以在您的本地计算机上成功运行。
启动 Docker 桌面应用程序。如果需要复习 how,请参考本教程。
进入 Django 的 Docker 容器并运行测试:
$ cd dockerized-django-demo
$ docker-compose -f local.yml run web_django bash
根据这个输出,进入 Django Docker 容器的 bash shell。
[secondary_label Output]
Creating network "dockerized-django-demo_default" with the default driver
Creating dockerized-django-demo_db_postgres_1 ... done
Creating dockerized-django-demo_web_django_run ... done
Going to use psycopg2 to connect to postgres
psycopg2 successfully connected to postgres
PostgreSQL is available
root@abc77c0122b3:/code#
现在,进行测试。
root@abc77c0122b3:/code# python3 manage.py test --keepdb
输出将是:
[secondary_label Output]
Using existing test database for alias 'default'...
System check identified no issues (0 silenced).
..........
----------------------------------------------------------------------
Ran 10 tests in 0.454s
OK
Preserving test database for alias 'default'...
这个输出意味着您的测试是正常的。当你把这个推给 CircleCI 的时候应该是一样的结果。
要退出 bash shell,请键入exit
root@abc77c0122b3:/code# exit
回到你的主机操作系统。
[secondary_label Output]
exit
$
要正确关闭 Docker:
$ docker-compose -f local.yml down --remove-orphans
以下是输出:
[secondary_label Output]
Stopping dockerized-django-demo_db_postgres_1 ... done
Removing dockerized-django-demo_web_django_run_8eeefb5e1d1a ... done
Removing dockerized-django-demo_db_postgres_1 ... done
Removing network dockerized-django-demo_default
你现在可以安全地停止 Docker 桌面。
到目前为止,您的 Django 项目和测试用例应该会令您满意。现在是时候将代码推送到 GitHub 了。
将您自己的 GitHub 存储库连接到 CircleCI
如果您一直在使用演示项目,并且已经创建了自己的 GitHub repo,那么您需要重命名从中克隆代码的 remote。改为将 GitHub repo 添加为 remote origin
。
将所有提及的greendeploy-io/test-ddp
替换为回购的实际名称- org-name/repo-name
$ git remote rename origin upstream
$ git remote add origin git@github.com:greendeploy-io/test-ddp.git
$ git push -u origin main
[secondary_label Output]
Enumerating objects: 305, done.
Counting objects: 100% (305/305), done.
Delta compression using up to 10 threads
Compressing objects: 100% (222/222), done.
Writing objects: 100% (305/305), 658.94 KiB | 2.80 MiB/s, done.
Total 305 (delta 70), reused 297 (delta 67), pack-reused 0
remote: Resolving deltas: 100% (70/70), done.
To github.com:greendeploy-io/test-ddp.git
* [new branch] main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.
添加 CircleCI 配置文件
在这一步中,您将创建一个 CircleCI 配置文件,并编写脚本来为您的项目配置一个持续集成管道。首先,创建一个名为.circleci
的文件夹,并在其中创建一个config.yml
文件。现在,打开新创建的文件,输入以下内容:
# Python CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/language-python/ for more details
#
version: 2.1
# adding dockerhub auth because dockerhub change their policy
# See https://discuss.circleci.com/t/authenticate-with-docker-to-avoid-impact-of-nov-1st-rate-limits/37567/23
# and https://support.circleci.com/hc/en-us/articles/360050623311-Docker-Hub-rate-limiting-FAQ
docker-auth: &docker-auth
auth:
username: $DOCKERHUB_USERNAME
password: $DOCKERHUB_PAT
orbs:
codecov: codecov/codecov@3.2.2
jobs:
build:
docker:
# specify the version you desire here
# use `-browsers` prefix for selenium tests, e.g. `3.7.7-browsers`
- image: cimg/python:3.8.12
# use YAML merge
# https://discuss.circleci.com/t/updated-authenticate-with-docker-to-avoid-impact-of-nov-1st-rate-limits/37567/35?u=kimsia
<<: *docker-auth
environment:
DATABASE_URL: postgresql://root@localhost/circle_test?sslmode=disable
USE_DOCKER: no
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/circleci-images/
- image: cimg/postgres:14.1 # database image for service container available at `localhost:<port>`
# use YAML merge
# https://discuss.circleci.com/t/updated-authenticate-with-docker-to-avoid-impact-of-nov-1st-rate-limits/37567/35?u=kimsia
<<: *docker-auth
environment: # environment variables for database
POSTGRES_USER: root
POSTGRES_DB: circle_test
working_directory: ~/repo
# can check if resource is suitable at resources tab in builds
resource_class: large
# turn on parallelism to speed up
parallelism: 4
steps: # a collection of executable commands
# add deploy key when needed esp when requirements point to github url
# - add_ssh_keys:
# fingerprints:
# - "ab:cd:ef..."
- checkout # special step to check out source code to the working directory
# using dockerize to wait for dependencies
- run:
name: install dockerize
command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
environment:
DOCKERIZE_VERSION: v0.4.0
# the actual wait for database
- run:
name: Wait for db
command: dockerize -wait tcp://localhost:5432 -timeout 1m
- restore_cache: # restores saved dependency cache if the Branch key template or requirements.txt files have not changed since the previous run
key: deps1-{{ .Branch }}-{{ checksum "requirements/base.txt" }}-{{ checksum "requirements/local.txt" }}
- run: # install and activate virtual environment with pip
command: |
python3 -m venv venv
. venv/bin/activate
pip install --upgrade setuptools && pip install wheel
pip install --upgrade pip==22.0.4
pip install --upgrade pip-tools
pip-sync requirements/base.txt requirements/local.txt
- save_cache: # special step to save dependency cache
key: deps1-{{ .Branch }}-{{ checksum "requirements/base.txt" }}-{{ checksum "requirements/local.txt" }}
paths:
- "venv"
- run:
name: run collectstatic
command: |
. venv/bin/activate
python3 manage.py collectstatic --noinput
- run: # run tests
name: run tests using manage.py
command: |
# get test files while ignoring __init__ files
TESTFILES=$(circleci tests glob "*/tests/*.py" | sed 's/\S\+__init__.py//g' | sed 's/\S\+factories.py//g')
echo $TESTFILES | tr ' ' '\n' | sort | uniq > circleci_test_files.txt
TESTFILES=$(circleci tests split --split-by=timings circleci_test_files.txt | tr "/" "." | sed 's/\.py//g')
. venv/bin/activate
# coverage's --parallel-mode will generate a .coverage-{random} file
# usage: https://docs.djangoproject.com/en/3.2/topics/testing/advanced/#integration-with-coverage-py
# add `--verbosity=3` between $TESTFILES --keepdb if need to debug
coverage run --parallel-mode manage.py test --failfast $TESTFILES --keepdb
# name: run tests using pytest
# command: |
# . venv/bin/activate
# pytest -c pytest.ini -x --cov-report xml --cov-config=.coveragerc --cov
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
destination: tr1
# save coverage file to workspace
- persist_to_workspace:
root: ~/repo
paths:
- .coverage*
fan-in_coverage:
docker:
# specify the version you desire here
# use `-browsers` prefix for selenium tests, e.g. `3.7.7-browsers`
- image: cimg/python:3.8.12
# use YAML merge
# https://discuss.circleci.com/t/updated-authenticate-with-docker-to-avoid-impact-of-nov-1st-rate-limits/37567/35?u=kimsia
<<: *docker-auth
working_directory: ~/repo
resource_class: small
parallelism: 1
steps:
- checkout
- attach_workspace:
at: ~/repo
- restore_cache: # restores saved dependency cache if the Branch key template or requirements.txt files have not changed since the previous run
key: deps1-{{ .Branch }}-{{ checksum "requirements/base.txt" }}-{{ checksum "requirements/local.txt" }}
- run: # install and activate virtual environment with pip
command: |
python3 -m venv venv
. venv/bin/activate
pip install --upgrade setuptools && pip install wheel
# because of https://github.com/jazzband/pip-tools/issues/1617#issuecomment-1124289479
pip install --upgrade pip==22.0.4
pip install --upgrade pip-tools
pip-sync requirements/base.txt requirements/local.txt
- save_cache: # special step to save dependency cache
key: deps1-{{ .Branch }}-{{ checksum "requirements/base.txt" }}-{{ checksum "requirements/local.txt" }}
paths:
- "venv"
- run:
name: combine coverage and generate XML report
command: |
. venv/bin/activate
coverage combine
# at this point, if combine succeeded, we should see a combined .coverage file
ls -lah .coverage
# this will generate a .coverage.xml file
coverage xml
- codecov/upload:
# xtra_args: '-F'
upload_name: "${CIRCLE_BUILD_NUM}"
workflows:
main:
jobs:
- build
- fan-in_coverage:
requires:
- build
为了方便地从这个脚本访问 Docker registry,您在上下文中设置了一个docker-auth
字段,并提供了您的 Docker Hub 凭证。这些凭证将作为环境变量包含在本教程的后面部分。
接下来, Codecov orb 从 CircleCI Orbs 注册表中被拉入。这个 orb 有助于将您的覆盖报告上传到 Codecov 而无需任何复杂的配置。
两个独立的任务构建项目,在 CircleCI 上运行测试,并将覆盖报告部署到 Codecov:
已配置的工作流将确保build
作业在fan-in_coverage
开始之前完全运行,因为其输出将被上传到 Codecov。
工作结构和工作流程
如前一节所述,有两个作业:build
和fan-in_coverage
。我在这里将它们折叠起来,以便于查看。
jobs: build:...
fan-in_coverage:...
workflows:
main:
jobs:
- build
- fan-in_coverage:
requires:
- build
注意fan-in_coverage
如何需要来自build
作业的输出,以及fan-in_coverage
如何将覆盖工件上传到 codecov.io。使用两个作业允许您使用并行性,并且仍然允许工件上传。
平行
为什么使用并行?答案很简单:并行加速构建。
# turn on parallelism to speed up
parallelism: 4
为了允许并行,你需要分割测试。为了分割测试,CircleCI 需要知道测试文件的完整列表。
通过拆分测试实现并行
第一行试图找到所有相关的测试文件,跳过__init__
和factories.py
。文件列表然后被写入circleci_test_files.txt
。
TESTFILES=$(circleci tests glob "*/tests/*.py" | sed 's/\S\+__init__.py//g' | sed 's/\S\+factories.py//g')
echo $TESTFILES | tr ' ' '\n' | sort | uniq > circleci_test_files.txt
TESTFILES=$(circleci tests split --split-by=timings circleci_test_files.txt | tr "/" "." | sed 's/\.py//g')
然后,CircleCI 将在并行模式下使用coverage.py
帮助运行测试。如果需要调试,添加一个--verbosity=3
标志。
# add `--verbosity=3` between $TESTFILES --keepdb if need to debug
coverage run --parallel-mode manage.py test --failfast $TESTFILES --keepdb
存储工件和文件
最后,配置告诉 CircleCI 在哪里存储工件和其他覆盖相关的文件,以便在fan-in_coverage
中使用:
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
destination: tr1
# save coverage file to workspace
- persist_to_workspace:
root: ~/repo
paths:
- .coverage*
提交所有的更改,并更新您在 GitHub 上的项目资源库。
将您的项目连接到 CircleCI
要将您的项目连接到 CircleCI,请登录您的 CircleCI 帐户。如果你注册了 GitHub 账户,你所有的库都可以在你的项目的仪表板上看到。从列表中找到您的项目;这种情况下是dockerized-django-demo
。点击设置项目。
系统会提示您选择项目中的.circleci/config.yml
文件。然后,输入分支机构名称,点击设置项目。
您的工作流将成功启动并构建。
将 Docker Hub 凭据作为环境变量添加到项目设置中
接下来,您需要将 Docker Hub 用户名和个人访问令牌作为环境变量添加到项目设置中。
进入 GitHub repo 的 CircleCI 项目页面,点击项目设置。从左侧菜单工具条中选择环境变量。
创建两个环境变量:DOCKERHUB_PAT
和DOCKERHUB_USERNAME
。使用您的 Docker Hub 帐户作为值的来源。
完成后,每次对 GitHub 库的推送或拉取请求都会在 CircleCI 上产生一个构建。
因为演示项目是一个公共 repo,所以 CircleCI 项目构建页面和 Codecov 覆盖页面也是公共的。
查看演示项目的 CircleCI 项目构建页面,了解完成所有步骤后您的构建页面应该是什么样子。
结论
在本文中,您克隆了一个生产就绪的 Django 3.2 演示项目。现在您可以对自己的 Dockerized Django 3.2 项目做同样的事情。更重要的是,在文章的后半部分,我深入讨论了应该在config.yml
文件中包含什么。
如前所述,我建议您在第一次尝试时使用演示项目来遵循教程。第二次,在使用自己的项目时,只要把每一次提到demo Django project
都换成your Django project
就可以了。
我强调了解释的某些部分,以便您知道它们如何帮助加快您的 CI/CD 构建。使用并行性可以加速构建,而不会牺牲 Docker 和上传到代码覆盖率提供者等关键部分。需要注意的一点是,这里的设置是为了持续交付,而不是持续部署。访问此页面了解更多差异。
KimSia 是一名独立的软件工程师,他编写面向企业的 ops 软件服务。他是一个狂热的 Python 开发者,他的项目包括 GreenDeploy 和 AutonomyFirst.app 。
使用项目上下文限制与独立项目共享机密| CircleCI
为 GitLab 组织引入项目上下文限制。此功能支持对不与 VCS 关联的独立项目的上下文进行基于项目的限制。独立项目目前仅在 GitLab 与circle ci 整合后可用。在这篇博文中,我们希望解释该功能的价值,以及如何利用它来进一步保护您的工作流程。
什么是项目限制?
上下文提供了一种在项目间保护和共享环境变量的机制。默认情况下,CircleCI 上的上下文是不受限制的。你们中的一些人可能已经熟悉了GitHub 组织以安全组的形式可用的上下文限制。安全组限制建立在 GitHub 的“团队”之上,允许用户将他们的上下文限制在特定的人群中。
因为安全组限制目前仅适用于 GitHub 组织,所以我们希望为用户提供一种方法来保护其 GitLab 组织内的上下文。项目限制允许用户将上下文及其环境变量限制在一个或多个项目中。这意味着,在运行时,上下文不受限制的项目下的任何工作流都不能访问上下文及其环境变量。如果作业试图使用受限的上下文,它们将会失败,并显示一个Unauthorized
状态。
为什么要使用项目限制?
如果没有限制,默认情况下,存储在上下文中的机密在整个组织范围内共享。项目受限环境允许 CircleCI 用户通过进一步保护其 CI/CD 管道来遵守更高的安全标准。项目限制提供了一种更安全的方式来授予对整个组织的机密的访问权限。他们可以完全由组织管理员管理,组织管理员可以创建、修改和删除项目限制。
对于组限制,需要特权用户来启动管道,以便使用上下文。项目限制通过允许批准的代码运行(基于项目的触发器和配置)改进了组限制,而不管哪个用户实际合并了上游 PR。
通过组织范围的上下文,项目的工作流可以使用上下文,即使它不需要该上下文;JavaScript 库的工作流可以将上下文用于一些不相关的东西,比如 Maven 存储库的凭证。通过项目上下文限制,您可以强制实施哪些项目需要哪些凭据。您的 JavaScript 库只需要带有 npm 访问令牌的上下文;它不需要 Maven 存储库凭证的上下文。
项目限制也适用于项目角色。任何 Org 贡献者都可以创建项目,这反过来会使他们成为项目管理员。通过添加限制,我们只允许组织管理员限制对“已批准”项目的上下文访问。然而,任何被分配了项目管理员角色的人都可以决定项目如何被触发,它使用哪个配置,以及它的作业将如何使用它可以访问的秘密。通过项目限制和角色的组合,用户能够以更精细的方式提高其管道的安全性和配置。
如何使用项目限制
您可以从“组织设置”中的“上下文”页面访问项目限制,在该页面中也可以找到环境变量。按照以下步骤设置您自己的项目限制。
您必须是组织管理员才能通过此方法限制上下文。如果您没有管理员权限,则添加项目限制按钮将不可用。根据您的角色,您可能能够查看限制列表。
进入 CircleCI web app 中您所在 GitLab 组织的组织设置>上下文页面。上下文列表将可用。
- 选择现有上下文的名称,或者如果您想使用新的上下文,单击创建上下文按钮。
- 点击添加项目限制按钮查看对话框。
- 选择要添加到上下文中的项目名称,并单击添加按钮。现在,上下文的使用仅限于指定的项目。目前,必须单独添加多个项目。
- 已定义的项目限制列表显示在上下文页面上。
- 如果您有环境变量,它们也应该出现在页面上。如果没有,您可以点击添加环境变量将它们添加到上下文中。然后点击添加按钮完成。这些环境变量的使用现在仅限于上面指定的项目。
现在,只有指定项目下的工作流可以使用上下文及其环境变量。关于设置和使用该功能的更详细的操作方法可以在这里找到。
下一步是什么?
除了基于项目的限制,我们希望在未来提供更细粒度的方法来保护上下文。我们正在考虑的这个特性的一些扩展是基于分支和标签的限制。
问题和评论
如果您有关于如何改进此功能或其他限制的想法,请访问 Canny 浏览或提交新想法。目前,这仅提供给 GitLab 组织,但如果您对该功能的扩展是否对其他 VCS 组织有用有反馈,我们很乐意听取您的想法。
您也可以查看我们的社区论坛,讨论,让我们知道您是如何使用项目限制的。有关该功能的更多详细信息,请访问文档。
DevSecOps -使用受限上下文保护机密| CircleCI
原文:https://circleci.com/blog/protect-secrets-with-restricted-contexts/
从欧盟 GDPR 数据隐私标准到似乎永无止境的数据泄露新闻报道,安全性前所未有地成为人们最关心的问题。安全性在软件开发中是一个如此重要的考虑因素,以至于现在有了一个全新的词 DevSecOps 来代表安全性在集成软件创建和交付管道的每个阶段中的重要性。锁定现代应用程序不是一项简单的任务,特别是当它们的源代码分布在多个存储库和团队中时,对于有全球贡献者的开源项目来说,这样做可能更令人生畏。如果您试图使用 CI/CD 创建一个自动化交付管道,管理敏感密钥可能是一个特别具有挑战性的部分。跨多个项目构建、测试和部署可能需要某些密钥,并且在维护它们的安全性的同时共享它们是困难的。
在 CircleCI,我们一直致力于平衡开发人员的需求(灵活性、自主性)和运营问题(安全性、合规性)。考虑到这一点,我们很高兴宣布受限上下文,这是我们最新的安全功能,允许管理员将上下文分配给特定的用户组。受限上下文旨在保持开发人员期望的易用性,同时启用 guardrails 使您的软件交付过程更加健壮和安全。
限制你的上下文
去年,我们引入了多上下文作为工作流的一部分,以允许跨项目轻松共享环境变量。这个特性受到了热烈的欢迎,我们从许多客户那里听说,他们想要对上下文有更多的控制,以便在他们的生产部署中使用它们。今天,我们引入了通过安全规则基于用户所属的组来限制特定用户对上下文的访问的能力。使用 GitHub 作为 VCS 的用户可以使用这个功能,并且已经建立了团队来对用户进行分组。在 CircleCI 中,可用的安全组将自动填充到您的 GitHub 团队中。
我们相信,受限的上下文将允许 CircleCI 用户创建更健壮的端到端 CI/CD 管道,同时保持高安全标准。例如,想要自动化从提交到部署的整个开发生命周期的组织现在可以更安全地做到这一点,方法是将部署密钥的访问权限限制在被授权将代码推入生产环境的特定用户。我们还希望基于 CircleCI 的开源项目将使用安全上下文来增强贡献者的能力,同时保持必要的控制层,以保持项目顺利运行。这些项目的主要维护者现在将能够在上下文中保护任何必要的环境变量,然后授权可信的贡献者在他们的构建中访问它们。
有了受限上下文,您现在可以更好地控制加密密钥的共享方式。准备好探索受限环境了吗?点击此处进入文档了解如何管理您的密钥和权限。
发布 Python 包| CircleCI
对于许多软件工程师和开发人员来说,使用标准库或内置对象是不够的。为了节省时间和提高效率,大多数开发人员都是在他人工作的基础上进行开发的。无论编码问题是什么,很可能有另一个程序员已经为它创建了一个解决方案。通常不需要重复解决问题的过程。这个原则被称为不重复自己或干。
超出标准库的代码通常在个人、团队甚至整个编码社区之间共享。如果你解决了一个编码问题,你可以把它作为开源代码库公开,让其他开发者从中受益。这些代码集合被称为包。
在本教程中,我们将构建一个简单的 Python 包,并为它创建一个持续集成(CI)管道。
如果你使用的是流行的编程语言,比如 JavaScript 或 Python,那么会有工具可以发布你的开源包。对于 JavaScript,可以使用节点包管理器( NPM )。对于 Python,可以使用 Python 包索引,也称为( PyPI )。在本教程中,我们将使用 PyPI。
先决条件
要完成本教程,您需要:
注意:test.pypi.org和pypi.org都要求您验证您的电子邮件地址。未经确认,您将无法发布包。本教程后面的步骤要求test.pypi.org和pypi.org的用户名和密码相似。例如,添加示例
创建 Python 包
正如我在介绍中提到的,如果标准 Python 库中没有您需要的功能,那么很可能有一个包可以提供它。在 pypi.org 上有成千上万种包装可供选择。您可以搜索您需要的功能,或者从流行或新的包中选择。
我们的第一个任务是创建一个简单的转换包,它可以在短时间内演示包的功能。我们创建的包将允许程序员转换以下值:
- 温度(摄氏和华氏之间)
- 距离(公里和英里之间)
- 和其他人,如果你想添加他们
使用典型的包维护工作流作为我们的模型,我们将随着时间的推移提供更新,以扩展包的有用性并提高安全性。我们将使用变更日志来跟踪更新并通知用户变更。稍后会详细介绍。
项目结构
在系统中选择一个位置并创建一个项目:
python-package
├── convrsn
│ ├── __init__.py
│ └── temperature.py
├── .gitignore
└── setup.py
该项目具有:
- 名为
python-package
的基本文件夹(您可以使用任何您喜欢的名称) - 名为
convrsn
的模块/库文件夹(这是实际的包) - 包文件夹中名为
temperature.py
的 Python 文件
确保模块/库文件夹名称是唯一的名称,未被测试 Python 包索引test.pypi.org或 Python 包索引pypi.org中的现有包使用,进行搜索查询以确认您想要使用的名称的可用性。
随着项目的创建和命名,我们可以开始构建将温度从fahrenheit
转换到celsius
并返回的功能。该逻辑将包含在文件temperature.py
中。
复制这段代码并粘贴到./convrsn/temperature.py
文件中:
def c2f(celcius):
return (float(celcius) * 9/5) + 32
def f2c(fahrenheit):
return (float(fahrenheit) - 32) * 5/9
接下来,将这段代码复制到__init__.py
文件中:
from .temperature import c2f, f2c
将此代码复制到setup.py
,然后使用您的信息更新author
和author_email
:
from setuptools import setup, find_packages
VERSION = '0.0.1'
DESCRIPTION = 'A conversion package'
LONG_DESCRIPTION = 'A package that makes it easy to convert values between several units of measurement'
setup(
name="convrsn",
version=VERSION,
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
author="<Your Name>",
author_email="<your email>",
license='MIT',
packages=find_packages(),
install_requires=[],
keywords='conversion',
classifiers= [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
'License :: OSI Approved :: MIT License',
"Programming Language :: Python :: 3",
]
)
要完成这一步,可以使用 GitHub。Python 的 gitignore 模板。将内容复制到这个项目的.gitignore
文件中。
代码应该类似于这个的。
通过运行以下命令克隆它:
git clone -b setup --single-branch https://github.com/CIRCLECI-GWP/python-package.git
构建包文件
在我们开始这一步之前,我应该提到我正在使用 Python3。根据您的 Python 安装,您可能需要使用python3
而不是python
,如示例所示。
在您的终端(项目的根目录)中运行:
python setup.py sdist bdist_wheel
该命令创建一个源分发和一个可共享的轮子,可以在pypi.org上发布。
为了测试这一点,创建一个虚拟 Python 环境。
注意: 使用不同的位置来防止模块间的名称冲突。
然后,使用车轮分配装置安装convrsn
组件。根据您的 Python 安装,您可能需要使用pip3
。
运行:
pip install <relative-path>/python-package/dist/convrsn-0.0.1-py3-none-any.whl
创建一个名为test.py
的 python 脚本文件,并输入:
from convrsn.temperature import f2c, c2f
print(f"{f2c(32)}") # Result should be 0.0
print(f"{c2f(0)}") # Result should be 32.0
要进行测试,请在虚拟 Python 环境中运行脚本。
我们的 Python 包已经可以发布了。
发布 Python 包
在本教程的这一步,我们将遵循现实生活中的软件包创建者和维护者可能使用的工作流程。首先,我们将首先向 test.pypi.org 发布我们的包,以确保一切正常。当我们准备好向我们的软件包用户发布时,我们将继续前往 pypi.org 的。
Python Packaging Authority (PYPA)有一个漂亮的包来帮助解决这个问题,这个包叫做twine
。我们将安装twine
,然后发布到 test.pypi.org。您将被要求输入您的站点凭据。
在您的终端中,导航到您的包的根文件夹并运行:
pip install twine
twine upload --repository testpypi dist/*
在一个单独的 Python 虚拟环境中,pip 安装包。当你运行pip install <package-name>
,pip
在 pypi.org 的上的官方 Python 包索引中搜索包文件。
运行test.py
脚本:
pip install --index-url https://test.pypi.org/simple convrsn
python3 test.py
以下是我的执行方式。
成功!现在我们可以用 CircleCI 自动化发布过程。
使用 CircleCI 自动发布包
众所周知,CircleCI 非常适合自动化脚本。事实证明,它对于创建包发布的可重复过程也是很棒的。我们将创建一个能够:
- 从测试 PyPI 升级到 PyPI
- 维护检查(如果包括测试)
- 允许凭据仅由管道使用,而不与处理包的每个开发人员共享
首先,创建一个名为tests
的文件夹。在其中,创建一个文件并将其命名为test_temperature.py
。在tests/test_tempertaure.py
中,输入:
from convrsn.temperature import f2c, c2f
def test_f2c():
assert f2c(32) == 0.0
def test_c2f():
assert c2f(0) == 32.0
当您在包含我们的convrsn
包的 Python 虚拟环境中时,转到根文件夹。Pip 安装pytest
,然后运行pytest
命令:
pip install pytest
pytest
两个测试都应该成功通过。
接下来,将 CircleCI 配置文件添加到项目中。创建一个名为.circleci
的文件夹。在新文件夹中,创建一个名为config.yml
的文件。在.circleci/config.yml
中,复制粘贴:
version: 2.1
jobs:
build_test:
docker:
- image: circleci/python:3.6.4
steps:
- checkout # checkout source code to working directory
- run:
command: | # create whl and use pipenv to install dependencies
python setup.py sdist bdist_wheel
sudo pip install pipenv
pipenv install dist/convrsn-0.0.2-py3-none-any.whl
pipenv install pytest
- run:
command: | # Run test suite
pipenv run pytest
test_pypi_publish:
docker:
- image: circleci/python:3.6.4
steps:
- checkout # checkout source code to working directory
- run:
command: | # create whl, install twine and publish to Test PyPI
python setup.py sdist bdist_wheel
sudo pip install pipenv
pipenv install twine
pipenv run twine upload --repository testpypi dist/*
pypi_publish:
docker:
- image: circleci/python:3.6.4
steps:
- checkout # checkout source code to working directory
- run:
command: | # create whl, install twine and publish to PyPI
python setup.py sdist bdist_wheel
sudo pip install pipenv
pipenv install twine
pipenv run twine upload dist/*
workflows:
build_test_publish:
jobs:
- build_test
- test_pypi_publish:
requires:
- build_test
filters:
branches:
only:
- develop
- pypi_publish:
requires:
- build_test
filters:
branches:
only:
- main
此配置文件指示管道安装必要的依赖项、运行测试并发布包。配置的workflow
部分指定了filters
,作业应该执行的顺序,以及它们的依赖关系。
例如,如果test
作业失败,作业test_pypi_publish
和pypi_publish
将无法运行。test_pypi_publish
和pypi_publish
任务分别只在develop
和main
分支运行。
将项目连接到 CircleCI
从将你的项目推送到 GitHub 开始。
你的知识库应该类似于这个。
现在,转到 CircleCI 仪表板上的添加项目页面。点击设置项目。
点击使用现有配置(因为我们已经有了自己的配置文件)。在出现的模式中点击开始建造。
CircleCI 选择默认的分支,所以如果您使用不带.circleci/config.yml
文件的分支,您将会得到一个构建错误。build_test
任务将会通过,但是如果您在main
或者develop
分支中,那么构建将会失败。这是因为test_pypi_publish
和pypi_publish
作业还不能运行。
尽管当我们使用twine
发布包时,测试 PyPI 凭证是期望的,但是当终端在管道中运行命令时,我们不能与终端交互。为了提供这些凭证,我们可以向命令添加标志:twine upload -u USERNAME -p PASSWORD
。然而,因为 Git 跟踪配置文件,而且我们的 Git 存储库是公共的,所以使用标志会有安全风险。我们可以通过创建环境变量来避免这种风险。
创建环境变量
在 CircleCI 项目(python-package
)上,点击页面右上方的项目设置。
从右侧菜单中选择环境变量。然后,点击添加环境变量。
输入TWINE_USERNAME
和TWINE_PASSWORD
的键值对。这是预期的结果。
我们的下一步是创建一个变更日志。
添加更改日志
在您的本地项目中,创建一个名为CHANGELOG.md
的文件,并输入:
# Change Log
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.0.2] - 2020-10-30
### Added
- Temperature conversion
为了确保构建成功,我们还需要将setup.py
中的版本提升到0.0.2
中,以匹配变更日志。
注意 : 如果不撞版本,发布作业会失败,因为不能两次发布同一个版本。
提交更改并推送到 GitHub 以触发构建。
更新包
要扩展包的功能,您可以创建另一个分支并对其进行更新。之后:
- 为它创建测试
- 在
setup.py
撞版本 - 在 CircleCI 配置文件中插入
whl
文件的版本 - 更新
ChangeLog
- 推送至 GitHub
当您成功构建时,创建一个 GitHub Pull 请求(PR)到develop
分支。如果好看就合并。当构建成功时,创建一个到main
分支 PR,检查它,并合并。
看一下这些拉取请求的例子:
测试版 PyPI 和 PyPI 上的包具有从公里到英里以及相反方向的附加功能distance conversion
。当构建成功时,PRs 被合并。
结论
现在你有了它,一个成功的、可重复的过程,适合于由 CI/CD 支持的 Python 包维护者。我们创建了一个 Python 包项目,创建了 Python 轮子,并使用一个whl
文件安装了一个包。我们从本地机器发布了这个包,然后使用 CircleCI 自动化了这个过程。
继续建!
Stanley 是一名软件工程师和技术文案,他身兼数职,包括技术团队领导和社区参与。他把自己描述成一个数字人(在数字空间中有文化)。
使用 Gradle | CircleCI 将 Android 库持续部署到 Maven Central
原文:https://circleci.com/blog/publishing-java-android-libraries/
本文将带您使用 Gradle 为构建、测试和发布库到 Maven Central 设置 CI/CD 集成。随着 jCenter 的关闭,Maven Central 再次成为所有 Android 和 Java 库的主要目的地。在 jCenter 关闭后,库发布者需要将他们的库移植到 Maven Central,以保持他们的库可用。
本文主要关注 CI/CD 集成。为手动发布设置库本身不在讨论范围之内,但是您可以找到一些有用的文章和入门指南的链接。
先决条件
要从这篇文章中获得最大的收获,您需要几样东西:
- Android 或 Java 的一些经验
- 与格拉德的经历
- 熟悉 Java 或 Android 库发布流程
- 一个圆的账户
关于项目
样本库是我在 2016 年发布的测试工具。相关部分都在 Gradle 构建脚本和 CircleCI 配置:.circleci/config.yaml
中。
有两个模块:
- 库模块
scrollableScroll
是正在发布的库 - Android 应用模块
sample
是我们用来测试库的应用
注意: 单独测试一个库有些困难。
使用 Gradle 将库发布到 Maven Central
如果您以前没有使用 Gradle to Maven Central 发布过库,这一节是为您准备的。如果你做到了这一点,你可以跳到下一节。
Maven Central 或者也叫 Sonatype OSSRH(OSS Repository Hosting)提供了一个官方指南。
该过程需要几个步骤:
- 注册一个
group
来发布。这可以是你的包名;对于我的项目,我使用了com.zmarkan
。您需要创建一个吉拉帐户,并为 Sonatype Nexus 开一张票,以便为您创建一个群组。您将在该组下发布您的所有项目。请注意,如果您使用com.[domain name]
命名法,您可能会被要求证明您拥有该域名。 - 准备你的图书馆出版和签名。 Gradle 有
maven-publish
和signing
插件可以帮助解决这个问题。有几篇文章涵盖了这一过程。也有官方的分级指南——出版指南、签约指南和签约指南。 - 创建一个 GPG 键。这是你用来签署包裹的东西。
您可以在 GitHub 上的我的示例项目中找到完整的示例。
如果你已经手动设置好了一切,你应该让./gradlew assemble publish
开始工作,这意味着你已经准备好了。手动发布过程还要求您登录到 web 上的 Maven Central,并且您需要手动关闭和发布在 Gradle publish 任务期间创建的临时存储库。
使用 Gradle 持续部署库
当然,上一节描述的过程适用于本地发布。在 CI/CD 环境中,这将略有不同。首先,我们不想执行所有这些手动任务。我使用的是 Gradle Nexus 发布插件,它建立在maven-publish
配置的基础上,可以自动完成发布的最后一部分。
其次,我们需要确保所有的密钥都被安全地存储,并且在为签名的 CI/CD 过程中是可访问的。正确的版本也必须到位。
使用 Gradle Nexus 发布插件自动发布
这个 Gradle 插件提供了两个新的 Gradle 任务来代替现有的publish
Gradle 任务:
publishToSonatype
closeAndReleaseSonatypeStagingRepository
要包含插件,将其添加到项目级build.gradle
文件:
plugins {
id "io.github.gradle-nexus.publish-plugin" version "1.0.0"
}
group = 'com.zmarkan' //Or whatever your Nexus publishing group is
version = findProperty('LIBRARY_VERSION')
// Rest of the setup ...
nexusPublishing {
repositories {
sonatype {
//only for users registered in Sonatype after 24 Feb 2021
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
username = findProperty('OSSRH_USERNAME')
password = findProperty('OSSRH_PASSWORD')
}
}
}
在本例中,username
和password
值是我的 Sonatype OSSRH 凭证,与 web 界面和吉拉问题跟踪器相同。如果您不想使用自己的凭证,也可以在 Sonatype OSSRH web UI 中创建单独的令牌。
您需要将凭证存储在您的 CircleCI 环境变量中,命名为ORG_GRADLE_PROJECT_OSSRH_USERNAME
和ORG_GRADLE_PROJECT_OSSRH_PASSWORD
。
注意: 务必保留ORG_GRADLE_PROJECT_
前缀。如果没有前缀,Gradle 中的findProperty
查找将不起作用。
部署流程
库的 CI/CD 管道做了几件事:
- 在每次提交时测试库。库测试在相应的示例应用程序上运行。这将尽快捕获任何会破坏构建的提交
- 从
main
分支释放一个快照。世界协调时每天午夜都会发生这种情况。它允许通过指向快照存储库来测试最新版本 - 在每个标签上发布一个新版本。这是大多数最终用户在使用该库时会与之交互的版本
建立一个 CI/CD 管道来发布你的库意味着你的发布过程将会一直工作,不管你的开发机器发生了什么,或者谁在做开发。您还将保护所有凭据或签名密钥的安全。
设置签名密钥是这个过程中最复杂的部分,所以我们应该开始了。
签名密钥
在 CI/CD 环境中对项目进行签名需要一个签名密钥,您可以在构建时将它传递给 Gradle。为此,您需要以字符串格式导出密钥,并将密钥本身及其密码存储在 CircleCI 的环境变量中。最后,您需要在构建时将密钥本身注入到项目的gradle.properties
中。
这意味着在你的库模块的build.gradle
中,你可以使用useInMemoryPgpKeys
函数来代替。这个函数使用基于字符串的signingKey
而不是密匙环文件。这两个键都可以存储为 Gradle 属性或环境变量:
def signingKey = findProperty('SIGNING_KEY')
def signingKeyPwd = findProperty('SIGNING_KEY_PWD')
signing {
useInMemoryPgpKeys(signingKey, signingKeyPwd)
sign publishing.publications
}
当您运行命令导出签名密钥时,请用用于生成密钥的电子邮件替换 your_email@example.com。您将被要求提供创建签名密钥时使用的密码。
gpg --armor --export-secret-keys zan@circleci.com \
| awk -v ORS='\\n' '1' \
| pbcopy
该命令的第一部分gpg --armor --export-secret-keys
将您的密钥导出为 ASCII 字符串。awk
命令用\n
替换任何换行符,pbcopy
将它存储在剪贴板中进行粘贴。
导出的密钥如下所示(注意中间大量的乱码行):
-----BEGIN PGP PRIVATE KEY BLOCK-----
[hundreds of lines of crypto gibberish]
-----END PGP PRIVATE KEY BLOCK-----
然后,您可以在项目的环境设置中创建新的 CircleCI 签名变量GPG_SIGNING_KEY
时粘贴它:
您还需要添加该密钥的密码,作为用于签名的环境变量:ORG_GRADLE_PROJECT_SIGNING_KEY_PWD
。注意,如果你想在 Gradle 中用findProperty
直接使用密码,前缀ORG_GRADLE_PROJECT_
是必需的。
至于键本身,我发现 Gradle 不喜欢它被存储为环境变量。我不得不在编译之前用.circleci/config.yml
中的这个运行调用将它写入gradle.properties
:
- run:
name: Inject Maven signing key
command: |
echo $GPG_SIGNING_KEY \
| awk 'NR == 1 { print "SIGNING_KEY=" } 1' ORS='\\n' \
>> gradle.properties
用 Git 标签对库进行版本控制
Maven Central 中的所有项目都需要进行正确的版本控制。我喜欢使用 Git 标签来驱动版本控制,因为我们可以从命令行创建它们,并且它们总是与提交绑定在一起。
对于任何带标签的提交,CircleCI 会将标签作为环境变量$CIRCLE_TAG
出现。我用那个标签来表示一个发布版本的版本。对于所有其他缺少标记的提交,我将它们视为快照构建。
我们将把它存储为另一个环境变量$ORG_GRADLE_PROJECT_LIBRARY_VERSION
,可以在 Gradle 中获取。使其可用的方法是在 CircleCI 的run
步骤中将任何变量推送到$BASH_ENV
。
这是我用的剧本。它或者将当前的 Git 标签设置为环境变量(例如1.0.21
),或者如果它丢失了,则使用最近使用的标签并在标签后附加-SNAPSHOT
。
- run:
name: Define ORG_GRADLE_PROJECT_LIBRARY_VERSION Environment Variable at Runtime
command: |
if [ $CIRCLE_TAG ]
then
echo 'export ORG_GRADLE_PROJECT_LIBRARY_VERSION=$CIRCLE_TAG' >> $BASH_ENV
else
echo "export ORG_GRADLE_PROJECT_LIBRARY_VERSION=`git tag | tail -1`-SNAPSHOT" >> $BASH_ENV
fi
source $BASH_ENV
将工件发布到 Sonatype OSSRH
发布的最后一步是在运行assemble
之后立即运行两个 Gradle publishing 命令:
publishToSonatype
closeAndReleaseSonatypeStagingRepository
这将使用 Sonatype Staging 插件部署库及其所有工件,并将其发布到产品中。
- run:
name: Publish to Maven Central
command: ./gradlew assemble publishToSonatype closeAndReleaseSonatypeStagingRepository
我的.circleci/config.yml
中的完整部署工作:
jobs:
...
deploy-to-sonatype:
executor:
name: android/android-machine
resource-class: xlarge
steps:
- checkout
- run:
name: Define ORG_GRADLE_PROJECT_LIBRARY_VERSION Environment Variable at Runtime
command: |
if [ $CIRCLE_TAG ]
then
echo 'export ORG_GRADLE_PROJECT_LIBRARY_VERSION=$CIRCLE_TAG' >> $BASH_ENV
else
echo "export ORG_GRADLE_PROJECT_LIBRARY_VERSION=`git tag | tail -1`-SNAPSHOT" >> $BASH_ENV
fi
source $BASH_ENV
- run:
name: Inject Maven signing key
command: |
echo $GPG_SIGNING_KEY \
| awk 'NR == 1 { print "SIGNING_KEY=" } 1' ORS='\\n' \
>> gradle.properties
- run:
name: Publish to Maven
command: ./gradlew assemble publishToSonatype closeAndReleaseSonatypeStagingRepository
要让 CircleCI 运行此作业,您需要将其包含在工作流中。
发布发布版本
我的发布工作流程如下所示:
workflows:
build-test-deploy:
jobs:
- android/run-ui-tests:
name: build-and-test
system-image: system-images;android-23;google_apis;x86
test-command: ./gradlew assemble sample:connectedDebugAndroidTest
filters:
tags:
only: /^[0-9]+.*/
- hold-for-approval:
type: approval
requires:
- build-and-test
filters:
tags:
only: /^[0-9]+.*/
branches:
ignore: /.*/
- deploy-to-sonatype:
name: Deploy to Maven Central
requires:
- hold-for-approval
filters:
tags:
only: /^[0-9]+.*/
最佳实践是将测试设置为在任何分支的每一次提交时运行,但是只有在提交被标记为版本时才继续部署。这是第filters
节所允许的。在这种情况下,部署作业只在以数字和句点开头的版本化标记上运行,比如1.something
。这样,您仍然可以将标签用于其他目的,但是它们不会触发发布版本。
filters:
tags:
only: /^[0-9]+.*/
branches:
ignore: /.*/
我还添加了一个hold-for-approval
,这是一个approval
作业,需要在部署库之前进行手动确认。这是可选的,但我发现手动步骤令人放心。
发布快照
快照是特殊类型的发布,发布速度更快,而且很容易被覆盖,不像常规的版本发布,不能被覆盖。这使得快照对于测试新版本的库非常有用。
要发布快照,您需要做的就是将-SNAPSHOT
附加到版本字符串的末尾。该过程将在本文的版本控制一节中介绍。我已经设置了我的快照部署管道,以便使用 CircleCI 的预定触发器特性在世界协调时的每晚午夜构建库:
nightly-snapshot:
triggers: #use the triggers key to indicate a scheduled build
- schedule:
cron: "0 0 * * *" # use cron syntax to set the schedule
filters:
branches:
only:
- main
jobs:
- android/run-ui-tests:
name: build-and-test
system-image: system-images;android-23;google_apis;x86
test-command: ./gradlew assemblesample:connectedDebugAndroidTest
- deploy-to-sonatype:
name: Deploy Snapshot to Sonatype
requires:
- build-and-test
结论
在本文中,我介绍了如何使用 Gradle 为 Maven Central 的 Android 或 Java 库建立一个持续的部署管道。您已经学习了如何设置库签名的秘密,如何使用 Git 标记对库进行版本控制,以及如何在每次发布新标记时自动发布库。我希望这对您自己的项目有用。
如果你对我接下来应该报道的话题有任何反馈或建议,请通过 Twitter - @zmarkan 联系我。
使用 CircleCI 发布 npm 软件包
原文:https://circleci.com/blog/publishing-npm-packages-using-circleci-2-0/
这是由 packagecloud 的联合创始人 Armando Canals 写的一篇客座博文。
本文将介绍使用 CircleCI 2.0 为 NodeJS 项目实现连续部署管道的必要步骤。
我们将使用新的 CircleCI 2.0 配置来设置项目,在 CircleCI 中运行测试,并在标记提交被推送到 git 存储库时将包部署到官方 npm 注册表。
npm 和 npm 包
Npm 是 JavaScript 编程语言的包管理器,它是与 Node.js JavaScript 运行时环境捆绑在一起的默认包管理器。
Npm 包用于方便可靠地分发 JavaScript 代码。使用 Npm 包最常见的情况是分发 Node.js 程序,并轻松地将它们安装在不同的系统上。
在 npm 文档网站上阅读更多关于 npm 包的信息,或者阅读更多关于使用 CircleCI 和 packagecloud 进行自动 npm 发布的信息。
为什么要发布 npm 包?
如果您想与其他开发人员、商业客户甚至持续集成工具(如 CircleCI)共享 Node.js 程序和 JavaScript 库,将 npm 包发布到注册表是最简单的方法。
在本帖中,我们将回顾将软件包发布到官方(公共)npm 注册表和托管在 packagecloud.io 上的私有 npm 注册表。
使用 CircleCI 2.0 配置 npm 注册表
向 NodeJS 项目添加一个.circleci/config.yml
在项目根目录中,创建一个名为.circleci
的文件夹,并在该文件夹中创建一个名为config.yml
的文件。
以下是这篇博文的完整示例 .circleci/config.yml
文件:
# Javascript Node CircleCI 2.0 configuration file
#
# Check {{ '/language-javascript' | docs_url }} for more details
#
version: 2
defaults: &defaults
working_directory: ~/repo
docker:
- image: circleci/node:8.9.1
jobs:
test:
<<: *defaults
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: npm install
- run:
name: Run tests
command: npm test
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
- persist_to_workspace:
root: ~/repo
paths: .
deploy:
<<: *defaults
steps:
- attach_workspace:
at: ~/repo
- run:
name: Authenticate with registry
command: echo "//registry.npmjs.org/:_authToken=$npm_TOKEN" > ~/repo/.npmrc
- run:
name: Publish package
command: npm publish
workflows:
version: 2
test-deploy:
jobs:
- test:
filters:
tags:
only: /^v.*/
- deploy:
requires:
- test
filters:
tags:
only: /^v.*/
branches:
ignore: /.*/
现在,让我们分解这个配置来解释每项工作中涉及的步骤。
设置默认值
在config.yml
中使用的下面一节允许我们通过定义一个名为defaults
的映射并使用 YAML 合并(<<: *)
键将其插入配置文件的后续部分来节省一些击键。
defaults: &defaults
working_directory: ~/repo
docker:
- image: circleci/node:8.9.1
这些默认值设置项目代码将被检出的工作目录,以及用于作业的版本NodeJS
。
在 CircleCI 中设置$npm_TOKEN
环境变量
使用 npm CLI 并使用login
命令检索 npm 身份验证令牌。在终端中运行npm login
命令,并在出现提示时使用您的 npm 凭据。
一旦完成,一个.npmrc
文件将在运行该命令的机器上的用户目录中创建。
通常这是在~/.npmrc
里。
这个令牌是假的,永远不要发布你的授权令牌。
从 npm 中检索到 auth 令牌后,在 CircleCI 上的项目设置中创建一个名为$npm_TOKEN
的环境变量。
或者,如果您喜欢将您的敏感环境变量签入 git,但是加密,您可以遵循在 circleci/encrypted-files 中概述的过程。
作业配置
config.yml
的作业部分定义了将在工作流中运行的作业,以及每个作业的步骤。
试验
我们定义的第一个作业是test
作业,它检索项目代码,将依赖项安装到我们的工作区,并运行我们的测试:
jobs:
test:
<<: *defaults
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}.
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: npm install
- run:
name: Run tests
command: npm test
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
- persist_to_workspace:
root: ~/repo
paths: .
由npm test
运行的命令定义在 npm 包中package.json
的scripts
部分。
此步骤还会从缓存中恢复任何依赖关系,以便在从以前的作业重建时节省资源。我们可以使用{{checksum “package.json”}}
作为 CircleCI 的缓存键,这样它只在package.json
的校验和改变时安装依赖项。
在 CircleCI 文档中了解有关此配置的更多信息。
部署
部署作业有几个步骤,运行这些步骤是为了向官方 npm 注册表进行身份验证和发布:
jobs:
...
deploy:
<<: *defaults
steps:
- attach_workspace:
at: ~/repo
- run:
name: Authenticate with registry
command: echo "//registry.npmjs.org/:_authToken=$npm_TOKEN" > ~/repo/.npmrc
- run:
name: Publish package
command: npm publish
-
步骤 1 -连接到我们配置的工作区
-
步骤 2 -使用 CircleCI 项目设置中设置为环境变量的
$npm_TOKEN
向 npm 注册表进行身份验证。 -
步骤 3 -从工作目录运行
npm publish
命令。
打包回购
部署到私有 npm 注册表
将包部署到私有注册表非常类似于在 npm 提供的官方注册表上发布包。
通过在使用 npm 注册表进行身份验证之前添加额外的步骤,我们可以指定 npm 命令在部署时将使用哪个注册表。
我们将以 packagecloud 上的 npm 注册表为例:
jobs:
...
deploy:
<<: *defaults
steps:
- attach_workspace:
at: ~/repo
- run:
name: Set registry URL
command: npm set registry https://packagecloud.io/armando/node-test-package/npm/
- run:
name: Authenticate with registry
command: echo "//packagecloud.io/armando/node-test-package/npm/:_authToken=$npm_TOKEN" > ~/repo/.npmrc
- run:
name: Publish package
command: npm publish
https://packagecloud.io/:username/:repo_name/npm/
其中,:username
是 packagecloud npm 注册表上的用户,:repo_name
是由 packagecloud 用户创建的 npm 注册表的名称。
-
步骤 3 -使用 CircleCI 项目设置中设置为环境变量的
$npm_TOKEN
向 npm 注册表进行身份验证。在这种情况下,有效的 packagecloud API 令牌将授予对 packagecloud npm 注册表的写访问权限。查看 packagecloud.io 上的npm login
文档了解更多信息。 -
步骤 4 -从工作目录运行
npm publish
命令。
使用 CircleCI 工作空间
我们在config.yml
中设置了一个工作区,以便在作业之间共享数据。创建一个工作空间允许我们签出项目代码,并在test
和deploy
任务之间共享。
作为test
作业中的一个步骤,我们将作业中之前完成的步骤保存到工作区,包含以下部分:
- persist_to_workspace:
root: ~/repo
paths: .
然后,在随后的deploy
作业中,我们使用以下配置连接到工作区,作为作业的第一步:
- attach_workspace:
at: ~/repo
Git 标记作业执行-在标记提交时部署
作为我们连续交付管道的一部分,我们希望确保deploy
作业/仅/在提交被标记了版本号并被推送时运行。
workflows:
version: 2
test-deploy:
jobs:
- test:
filters:
tags:
only: /^v.*/
- deploy:
requires:
- test
filters:
tags:
only: /^v.*/
branches:
ignore: /.*/
上面的工作区配置将在所有分支和所有标记上运行test
作业,并且仅当提交被标记了版本号并被推送时才运行deploy
作业。
tags
部分使用正则表达式来匹配以v
开头的标签,例如v0.1.0
。
结论
在软件开发生命周期中自动化手动过程可以显著减少错误、开发时间和用户挫折感。使用 CircleCI 这样的工具可以让您的团队构建一个健壮的软件分发管道,快速可靠地将软件提供给用户,而无需增加额外的基础设施。
使用功能强大的 CircleCI 2.0 自动化工具,开始将 npm 软件包直接发布到任何公共或私有注册表。
使用 CircleCI - CircleCI 自动发布 GitHub
原文:https://circleci.com/blog/publishing-to-github-releases-via-circleci/
Releases 是 GitHub 的一个特性,它允许你在 GitHub 漂亮的用户界面上展示代码的重要快照,用 git 标签标记。如果您目前没有使用发行版,我想向您展示为什么您可能想要使用,以及如何自动实现它们。
对于 release,您可以获得标签所提供的内容——版本号和描述——但是您也可以获得一个更长的部分用于发布说明,以及一个存储和显示发布工件的地方。这意味着你的软件是二进制的。黛比。每个版本的 rpm 和 AppImage 文件都将由 GitHub 托管,为用户安装您的软件提供了一个方便的地方。
在这篇文章中,我将向你展示如何在 CircleCI 中创建发布。关于更全面的概述,请参见 GitHub 关于创建版本的文档。
其核心是,GitHub 版本只是一个 GitHub 特性,位于 git 标签之上。让我们来分解一下:
Git 标签
Git 标签给你一个版本号来标记一个特定的 git 提交和一个简短的描述。信不信由你,只要把一个 git 标签推送到 GitHub,就会在 GitHub 上你的项目的“Releases”标签里创建一个新的发布。这是一个只包含标签信息的准系统版本。
放
发布版通过提供更长的、“丰富的”描述、将标签标记为普通发布版或预发布版的能力,以及最重要的上传工件(如该发布版的二进制文件)的能力,为 git 标签增添了新的功能。我们将从 CircleCI 上传这些艺术品。
工具
有几种方法可以将 CircleCI 版本的工件发布到 GitHub 版本:
使用 ghr
ghr
命令的全部细节可以在 GitHub 上的中找到,但是,我们真的只需要学习一个子命令:
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/
这个命令将特定的工件(通常是二进制文件)上传到我们在 GitHub 上发布的 GitHub 版本。让我们来分解一下:
- 这里我们启动命令,并传递一个 GitHub 令牌给它进行认证。这是一个 GitHub 个人访问令牌,而不是 OAuth 令牌/密钥。
这是通过您的个人 GitHub 帐户专门创建的,您会希望保护这个令牌,并且只通过一个私有环境变量将它加载到 CircleCI 环境中。
-
这是我们传递 GitHub 用户/组织名称和存储库名称的地方。这两个值都通过内置的 CircleCI 环境变量传递。不需要额外的工作。
-
-c ${CIRCLE_SHA1}
-这里我们向ghr
提供 Git 提交散列(在 CircleCI 中作为$CIRCLE_SHA1
提供)。这告诉它释放是针对哪一个提交,以及标签。 -
-delete
-删除 git 标签,如果已经存在,则释放。 -
${VERSION}
-我们通过一个$VERSION
环境变量设置 git 标签/发布版本。这可以是您想要的任何变量或字符串。通常,这与您的软件版本相同。 -
./artifacts/
——然后我们设定路径寻找神器。在这种情况下,当前目录内名为artifacts
的目录中的所有内容。
普通 CircleCI 2.0 示例
我们已经介绍了如何使用ghr
创建 GitHub 版本,但是让我们看看它在 CircleCI 2.0 配置文件中的样子:
publish-github-release:
docker:
- image: circleci/golang:1.8
steps:
- attach_workspace:
at: ./artifacts
- run:
name: "Publish Release on GitHub"
command: |
go get github.com/tcnksm/ghr
VERSION=$(my-binary --version)
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/
CI 构建 CircleCI 2.0 示例
这是一个名为publish-github-release
的 CircleCI 作业,它使用了一个 CircleCI Go 便利图像。这是细目分类:
- 使用工作空间,我们从之前的工作中为我们的项目引入二进制文件(不在本文中讨论)。
- 这个作业通过
go get
拉取并编译ghr
(因为ghr
是用 Go 写的)。 - 它用
my-binary --version
的输出填充$VERSION
,这是我们这篇文章的示例应用程序。 - 然后我们使用
ghr
命令将artifacts
目录中的二进制文件上传到 GitHub。
通过使用来自 CI 构建的 ghr
Docker 图像,我们可以将上述工作缩短几秒钟。这是一个已经安装了ghr
的轻量级 Docker 映像。下面是新的配置示例:
publish-github-release:
docker:
- image: cibuilds/github:0.10
steps:
- attach_workspace:
at: ./artifacts
- run:
name: "Publish Release on GitHub"
command: |
VERSION=$(my-binary --version)
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/
工作流示例
下面是一个例子,说明如何在一个工作流中实现上述工作的一个变体,以将标记的提交发布到 GitHub 版本:
workflows:
version: 2
main:
jobs:
- build:
filters:
tags:
only: /^\d+\.\d+\.\d+$/
- publish-github-release:
requires:
- build
filters:
branches:
ignore: /.*/
tags:
only: /^\d+\.\d+\.\d+$/
在这个例子中,当 git 标签被推送时,CircleCI 启动了publish-github-release
任务。具体如何选择何时发布 GitHub 版本取决于您,但是这里我们将使用一个类似 SemVer 的 git 标签来完成。我们说 SemVer-like 是因为我们使用的标签 regex/^\d+\.\d+\.\d+$/
匹配诸如1.2.3
的标签,但是没有考虑到 SemVer 的所有规则(语义版本)。为了完整起见,下面是根据rgxdb.com的说法,一个完整的 SemVer 正则表达式应该是什么样子:
/(?<=^[Vv]|^)(?:(?<major>(?:0|[1-9](?:(?:0|[1-9])+)*))[.](?<minor>(?:0|[1-9](?:(?:0|[1-9])+)*))[.](?<patch>(?:0|[1-9](?:(?:0|[1-9])+)*))(?:-(?<prerelease>(?:(?:(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?|(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?)|(?:0|[1-9](?:(?:0|[1-9])+)*))(?:[.](?:(?:(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?|(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?)|(?:0|[1-9](?:(?:0|[1-9])+)*)))*))?(?:[+](?<build>(?:(?:(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?|(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?)|(?:(?:0|[1-9])+))(?:[.](?:(?:(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?|(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)(?:[A-Za-z]|-)(?:(?:(?:0|[1-9])|(?:[A-Za-z]|-))+)?)|(?:(?:0|[1-9])+)))*))?)$/
阅读更多信息:
使用 CircleCI | CircleCI 发布到 Rubygems
如果您维护一个 Ruby gem,那么您肯定熟悉围绕新版本发布的重复性手工任务。这样做了一段时间后,您不可避免地会开始思考这些步骤中的一些可以自动化。他们可以!通过几行代码,您可以将连续交付的惊人世界带到您的项目中,并增加整个过程的可靠性,同时节省您的一些时间。双赢!
在本文中,我将演示如何围绕一个简单的 gem 创建一个完整的过程,这样您就可以看到一些可能性。
自动化的案例
将连续交付(CD)过程引入任何专业或激情项目有几个原因。我将从可能是最明显的一个开始:它节省你的时间。当然,初始设置将需要一些前期投资,而你研究和尝试的东西,直到一切都到位。然而,在那之后,好处会在你的项目的整个生命周期中累积。
另一个很大的优点是它带来的可重复性。自动化意味着可预测性,这可以通过总是以相同顺序运行的配置文件和命令来表达。反过来,如果您在部署过程中遇到问题,这使得调试更加容易。
最后,上面提到的决定论肯定会在发布阶段导致更少的错误和灾难。当您准备好部署一个新版本的 gem 时,不要试图记住要运行的命令的正确顺序,您只需要依靠您之前设置的经过尝试和测试的过程来完成这项工作。它被记录在案,你知道它会被完美地执行。
构建演示
在本教程中,我将带领你创建一个小项目,作为一个可出版的宝石。我们将使用一些简单的东西:一个将方法zigzagcase
添加到 String 对象的 Ruby gem。它将返回字符大小写交替的字符串的副本:LiKe tHiS
。为了简单起见,我们将保持我们的代码为普通的 Ruby,它易于编码和测试。您可以省去为另一个项目编写需要与 Rails、Sinatra 或其他 Ruby 框架一起工作的东西。
先决条件
跟随本教程,您将需要一些东西:
- 一个 GitHub 账户。你可以在这里创建一个。
- 一个 Rubygems 账户来发布我们的 gem
- 一个 CircleCI 账户。你可以在这里创建一个。要轻松连接您的 GitHub 项目,您可以注册您的 GitHub 帐户。
注意: 确保捆扎机已安装。您将使用它来简化宝石的创建。使用 Bundler 是 Ruby 社区中的人们管理他们的 gem 的主要方式,所以使用它来创建一个是有意义的。
设置项目
从跑步开始:
$ bundle gem zigzagcase --test=minitest
这个简单的命令创建一个脚手架文件夹,并初始化一个 git 存储库。如果这是你第一次使用 Bundler 2.1.4 运行bundle gem
,会询问你是否要包含行为准则和许可证。推荐你加一个;麻省理工学院适合这个项目。我们传递的test
标志将自动选择 minitest 作为我们的测试框架。
你可以阅读更多由 Bundler 创建的脚手架(有相当多的文件),但要点是:
zigzagcase/lib/zigzagcase.rb
被用作占位符来导入其他带有我们代码的文件zigzagcase/lib/zigzagcase/version.rb
持有当前版本的 gem- 定义宝石:里面有什么,谁做的,许可证,以及其他技术细节
在前进之前,有一点家务要做。转到cd zigzagcase
文件夹并运行grep TODO zigzagcase.gemspec
。Bundler 留下了一些我们需要自己照顾的东西。如果我们不这样做,我们将很难运行其他命令,所以我们最好现在就解决这个问题。深入研究该文件,进行下一个代码块中所示的更改。
require_relative 'lib/zigzagcase/version'
Gem::Specification.new do |spec|
spec.name = "zgzgcase"
spec.version = Zigzagcase::VERSION
spec.authors = ["Gonçalo Morais"]
spec.email = ["author.email@example.com"]
spec.summary = %q{MaKe sTrInGs lOoK LiKe tHiS}
spec.description = %q{Alternates upper case and lower case across letters}
spec.homepage = "https://github.com/CIRCLECI-GWP/zigzagcase"
spec.license = "MIT"
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
spec.metadata["allowed_push_host"] = "https://rubygems.org"
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = "https://github.com/CIRCLECI-GWP/zigzagcase"
spec.metadata["changelog_uri"] = "https://github.com/CIRCLECI-GWP/zigzagcase/blob/main/CHANGELOG.md"
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
end
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
end
rubygems 中已经存在一个名为zigzagcase
的 gem。对于本教程,我们使用名称zgzgcase
代替。这是在前一个代码块中分配给spec.name
的名字。为了成功发布您自己的 gem,您需要选择一个替代名称,并确认没有使用该名称的 gem。在 rubygems 搜索,如果没有你选择的宝石名称,你可以继续学习教程。用您的特定名称替换每个zgzgcase
实例。
我们的宝石依赖于其他一些现成的宝石。可以查看Gemfile
看看是哪些。通过运行bundle install
安装它们,并提交由此产生的Gemfile.lock
。
更新README
文件也是一个很好的做法。首先,在安装部分,更新 gem 名称。用你的 GitHub repo 的 URL 更新贡献部分的 URL。接下来我们将创建回购。
在开始编写代码之前,先将这个起始结构保存在 GitHub 上。确保您创建了一个空的 repo 。Bundler 为您初始化了一个本地 git 存储库,如果项目是空的,直接将您拥有的内容发送到 GitHub 会更容易。如果你不熟悉这个过程或者不熟悉 GitHub,请跟随这篇教程。
测试作为一项功能
一个健康的连续交付工作流程的一部分是建立一个可靠的测试套件,让你对你发布的东西有信心。因为这是如此重要的一部分,所以让我们确保添加一些测试来保持我们的 gem 的完整性。在搭建期间,我们选择了 minitest 作为我们的测试框架——但是你可以自由选择任何其他框架,重要的是编写有意义的测试。
如果您检查由 Bundler 创建的Rakefile
,您将看到 Rake 的默认任务是运行测试,因此我们现在可以通过执行bundle exec rake
来运行它,而不做任何更改。
Run options: --seed 21588
# Running:
.F
Finished in 0.001040s, 1923.0769 runs/s, 1923.0769 assertions/s.
1) Failure:
ZigzagcaseTest#test_it_does_something_useful [zigzagcase/test/zigzagcase_test.rb:9]:
Expected false to be truthy.
2 runs, 2 assertions, 1 failures, 0 errors, 0 skips
rake aborted!
Command failed with status (1)
/Users/circleci/.rbenv/versions/2.6.5/bin/bundle:23:in `load'
/Users/circleci/.rbenv/versions/2.6.5/bin/bundle:23:in `<main>'
Tasks: TOP => default => test
(See full trace by running task with --trace)
打开test/zigzagcase_test.rb
并用我们方法的几个简单测试来代替它:
require "test_helper"
class ZigzagcaseTest < Minitest::Test
def test_alternates_upcase_and_downcase
assert_equal "AdVeNtUrE TiMe!", "Adventure Time!".zigzagcase
end
def test_keeps_original_string_untouched
original_string = "Adventure Time!"
expected_string = original_string.dup
original_string.zigzagcase
assert_equal expected_string, original_string
end
end
如果您碰巧试图运行这些测试,您会得到一个错误:NoMethodError: undefined method 'zigzagcase' for "Adventure Time!":String
。我们得到这个错误是因为我们还没有为我们的方法写任何代码。
在lib/zigzagcase
文件夹中创建一个string.rb
文件。我们将在那里添加我们的代码(lib/zigzagcase/string.rb
)。为了简单起见,这个 gem 会自动给 Ruby 的 String 类添加一个zigzagcase
方法。
class String
def zigzagcase
modifiers = %i[upcase downcase].cycle
self.chars.map{ |char| char.send(modifiers.next) }.join
end
end
简而言之,这段代码获取字符串中所有字符的数组,在对每个字符交替调用upcase
或downcase
的同时返回该数组的副本,最后将它们连接在一起返回一个新字符串。这个新文件还没有在任何地方使用,所以我们刚刚写的代码没有被拾取。我们将通过主 gem 类(lib/zigzagcase.rb
)导入它来解决这个问题。将该文件的内容替换为:
require "zigzagcase/version"
require "zigzagcase/string"
现在我们已经有了我们的代码,我们可以再次运行测试用例,只是为了看到它们成功地通过。
Run options: --seed 48145
# Running:
..
Finished in 0.000964s, 2074.6888 runs/s, 2074.6888 assertions/s.
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
自动化流程
我们已经在本地运行,现在我们希望确保“机器人”为我们工作。我们将使用这个例子作为参考,但是我们的配置将更加简单(.circleci/config.yml
):
version: 2.1
orbs:
ruby: circleci/ruby@1.0.4
jobs:
test:
docker:
- image: cimg/ruby:2.7
steps:
- checkout
- ruby/install-deps
- run:
name: Run tests
command: bundle exec rake
workflows:
version: 2
deploy:
jobs:
- test
添加后,确保提交上面的.circleci/config.yml
并推送到 GitHub。这将自动触发您的项目在 CircleCI 上的首次构建。
准备发布
我们将遵循语义版本来发布这个 gem。希望你正在遵循类似的实践。如果运用得当,这是一种快速简单的方法,可以让人们清楚地了解发生的变化。
使用版本化策略将允许我们实现连续交付和连续部署过程之间的关键区别。在构建 web 应用程序时,您可能希望尽可能快地向用户提供每一处代码更改。没有发布日期,因为发布意味着在没有人工干预或批准的情况下将新功能和变化提供给用户。对于连续交付,您希望保留手工发布过程。当在 gem 上工作时,每当主分支被合并时,您可能希望所有最新的代码都在主分支上,但是您不希望每次都发布新的版本。
作为一个额外的细节,有一个单一的地方来快速浏览版本之间的变化是非常有用的,而 changelog 文件是一个完美的地方。在下一节中,我们将创建一个并将其添加到我们的脚手架中。
现在我们将在 CircleCI 上设置一些很酷的东西:如果我们将一个 git 标签推送到 GitHub,就可以运行部署步骤。每当有东西被合并到我们的主分支中,我们只想运行我们的测试。但是当我们标记要发布的东西时,我们希望 CircleCI 来做艰苦的工作。在我们的测试通过之后,我们希望它自动构建我们的 gem 的新版本,并将其发布在 Rubygems 上。
我们将在 Rubygems 上设置一种认证方式,这样它就能以我们的名义发布 gems。在检查了文档之后,我们知道可以用来自 Rubygems 的 API 密钥页面的密钥在我们的服务器中设置一个GEM_HOST_API_KEY
。点击新 API 键创建一个。输入一个名称来标识 API 键,选择所需的范围,然后单击创建。在我们的例子中,只检查了Push rubygems
范围。
在您项目中设置变量。
有了凭证之后,我们需要更改 CircleCI 配置,以包含这个额外的步骤。我们正在添加一个部署作业,该作业将仅针对标记运行。下面是更新后的配置文件(.circleci/config.yml
):
version: 2.1
orbs:
ruby: circleci/ruby@1.0.4
jobs:
test:
docker:
- image: cimg/ruby:2.7
steps:
- checkout
- ruby/install-deps
- run:
name: Run tests
command: bundle exec rake
deploy:
docker:
- image: cimg/ruby:2.7
steps:
- checkout
- ruby/install-deps
- run:
name: Publish gem to Rubygems
command: bundle exec rake release
workflows:
version: 2
test-and-deploy:
jobs:
- test:
filters:
tags:
only: /.*/
- deploy:
requires:
- test
filters:
tags:
only: /^v.*/
branches:
ignore: /.*/
这里有两个很大的区别:
- 我们的新工作是负责使用 Bundler 构建和发布我们的 gem
- 我们的工作流部分进行了更改,以适应过滤触发部署作业的事件的逻辑
在这个例子中,我们匹配任何分支(因此忽略所有分支),并且只匹配以 v 开头的标签(v0.0.1 等等)。
把一切都包起来
现在是时候启动我们的工作流程了。每当你对合并到主分支中的更改感到满意,并且准备剪切出我们应用程序的新版本时,你只需要正确地标记它,并将其推送到 GitHub。
如果你想让你的项目保持出色,考虑记录宝石经历的变化。这个记录可以作为它演变的时间线。我们将在CHANGELOG.md
写一份我们创业板的变化总结。这里有一个例子。
# Change Log
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.1.0] - 2020-09-19
### Added
- First release
根据经验编辑你的。保持这个文件是最新的,因为在这里人们可以找到关于版本之间发生的变化的摘要和有用的链接。尤其重要的是要仔细检查可能的回归和安全修复。
另外,确保使用注释标签给你的宝石贴上尽可能多的信息。这将有助于您保持有用和丰富的 git 历史。
提交更改并标记提交:
git add .
git commit -m "Update CircleCI config & add CHANGELOG"
git tag -a v0.1.0 -m "First release"
这里有一个您必须经常检查以确保一切顺利运行的细节:保持 git 标签和 gem 版本同步。在这种情况下,我们用0.1.0
标记我们的宝石。这与我们在lib/zigzagcase/version
和Gemfile.lock
看到的版本相同。每当您准备好更新您的 gem 版本时(例如,因为您发布了一个错误修复),请确保:
- 在
lib/zigzagcase/version
更新 - 之后在控制台运行
bundle
您的Gemfile.lock
将反映版本增量,所以不要忘记git commit
它。
现在,我们准备通过运行以下命令向世界发布我们闪亮的新版本:
git push origin main
git push origin --tags
返回 CircleCI dashboard 查看您的成功发布。
结论
您现在已经自动发布了 gem!你可以在 Rubygems 网站上找到你发布的 gem 的新版本。每当你标记你的 gem 发布时,你用 CircleCI 设置的过程将确保你的新代码对 Ruby 社区可用。
我希望这篇教程能启发你自动化你自己的 Rubygems 部署,并激发你对还能自动化什么的好奇心。您可以对 NPM 模块、iOS pods 或 Unix tarballs 应用类似的过程。为什么不把所有的东西都录下来?
贡萨洛·莫莱斯是一名计算机工程师,对网络情有独钟。他目前正在帮助 BridgeU 的学生选择更好的职业,由 Rails 和 JavaScript 提供支持。Gonç alo 是 Recurse 中心的校友,偶尔也是 ultrarunner 和 boulderer。
将项目推送到 GitHub | CircleCI
本教程涵盖:
- 什么是 GitHub,为什么你想使用它
- 设置和创建 GitHub repo
- 将您的项目推向新的回购
GitHub 是一个基于 web 的平台,用于项目版本控制和代码库托管。GitHub 使用 Git,一个广泛使用的版本控制系统。 GitLab 和 Bitbucket 是类似的工具。
使用 GitHub 是 CircleCI 博客上大多数教程的必备条件,所以学习使用它是有帮助的。在本教程中,我将向您展示如何将项目推送到 GitHub 。
先决条件
要遵循本教程,需要做一些事情:
- Git 的基础知识
- 安装在您系统上的 Git
- GitHub 的一个账户
有了这些东西,我们就可以开始教程了。
安装
为了简单起见,我们将使用一个 HTML 文件。你可以在这里查看 HTML 文件。
有几种方法可以获得 HTML 文件:
- 将要点中的代码复制到一个
index.html
文件中 - 进入这个链接,然后点击下载这个单页网页简介。
- 使用终端(或类似的命令提示符或 PowerShell)。转到要使用的项目文件夹并运行:
wget https://raw.githubusercontent.com/CIRCLECI-GWP/profile/main/index.html
使用这些方法中的任何一种都会导致在你的根目录下出现一个index.html
。
在本教程的其余部分,我们将使用终端来运行命令。除非另有指示,否则请在项目目录的根级别运行命令。
正在初始化 Git
因为我们在本地创建了我们的文件,所以我们需要将它推送到 GitHub 来存储在那里。第一步是初始化 Git。
运行:
git init
git init 命令将您的目录变成一个新的 git 存储库。
添加文件
Git 初始化后,我们需要标记 HTML 文件,以便它包含在下一次提交中。这个过程也称为暂存。
注意: 提交是对文件变化历史的快照。
运行:
add index.html
这个命令标记了index.html
文件,这样它就可以包含在下一次提交中。
提交文件
我们的文件现在被标记并准备好第一次提交。
运行:
git commit -m "Add index.html"
-m 后面的文本是提交消息。这是一个关于提交中有什么变化的友好提示。
推送至 GitHub
将所有本地提交上传到远程存储库。这使得您的同事可以使用您文件中的更改。这个过程有两个部分:
- 创建存储库
- 推进项目
创建 GitHub 存储库
在你的浏览器中,去 github.com 登陆,如果你还没有的话。单击页面右上角的加号图标。然后选择新建存储库。
选择所有者,输入库名,然后点击创建库。我在本教程中使用了名称new-repository
。你以后会用到这个名字,所以记下来。
你有了它,一个闪闪发光的新仓库。如果这是你的第一次,恭喜你!您已经达到了一个编程里程碑。
停留在此页面完成下一步。
将项目推向 GitHub
请记住,您已经有了一个包含一个文件的本地存储库,并且您已经提交了对它所做的更改。下一步是将这些更改推送到新创建的 GitHub 存储库。
将这些命令粘贴到您的终端中,并按下 Enter 来执行它们:
git remote add origin https://github.com/NdagiStanley/new-repository.git
git branch -M `main`
git push -u origin `main`
注意 : 用你的 GitHub 用户名替换例子中显示的用户名。。
运行这些命令后,重新加载浏览器页面。您的index.html
文件现在列在在线存储库中。
您可以通过按顺序运行以下命令对存储库进行更多更新:
git add .
git commit -m "Commit message"
git push origin main
用您自己的描述性文本替换示例文本Commit message
。如果您正在处理main
以外的分支,请使用您的分支的名称。你可以在这里阅读更多关于 git 分支的内容。
结论
在本教程中,您将一个本地 Git 存储库推送到 GitHub。熟悉了 Git 和 GitHub 之后,您就可以进入下一步,基于您的 GitHub 存储库建立一个 CircleCI 项目。GitHub 和其他基于 Git 的版本控制系统广泛应用于软件开发和其他需要版本控制的学科。理解它们是开发人员工具箱的一个重要补充。
–
Stanley 是一名软件工程师和技术文案,他身兼数职,包括技术团队领导和社区参与。他把自己描述成一个数字人(在数字空间中有文化)。
将 React 应用程序部署到 Netlify | CircleCI
本教程涵盖:
- 设置连续部署服务器
- 向构建过程中添加并执行任何自定义步骤
- 将应用程序部署到 Netlify
React 是一个用于构建用户界面的前端框架,它使用基于组件的架构和非固执己见的设计原则,这使它成为开发人员的最爱。React 已被广泛采用,并有一个庞大的开发人员社区支持它。Netlify 是一个用于托管 React 应用程序的流行框架,但是它不能为您的团队提供对部署过程的最高级别的控制。结果,您不能执行像运行自动化测试这样的重要任务。
在本教程中,您将学习如何通过使用 CircleCI 作为连续部署服务器来控制部署过程。通过使用本教程设置的系统,您可以执行所需的任何自定义步骤来完成构建过程并将 React 应用程序部署到 Netlify。
先决条件
要完成本教程,您需要:
我们的教程是平台无关的,但是使用 CircleCI 作为例子。如果你没有 CircleCI 账号,请在 注册一个免费的 。
所有这些安装和设置,我们可以开始教程。
创建新的 React 站点
在系统中选择一个位置,创建将部署到 Netlify 的 React 站点。运行以下命令:
npx create-react-app netlify-react-app
这个命令将调用create-react-app
npm 包来构建一个新的 React 应用程序。
结果是在netlify-react-app
文件夹中创建了一个 React 项目。使用以下命令运行项目:
cd netlify-react-app
npm start
这些命令在本地 URL: http://localhost:8000
启动 React 站点。将此地址加载到您的浏览器中。
设置 GitHub 项目
你需要在 GitHub 上设置你的 React 项目,然后才能在 Netlify 和 CircleCI 上获得它。首先,在 React 项目中安装 Netlify CLI 包作为开发依赖项。在项目的根目录下运行以下命令:
npm install --save-dev netlify-cli
注意 : 将 Netlify CLI 添加为依赖项可以防止在 CI 环境中对部署进行重大更改。
你现在可以将你的项目推送到 GitHub 了。一旦你的项目在 GitHub 上,创建一个新的分支。给这个分支取任何你想要的名字;对于本教程,我将命名为netlify-deploy-ignore
。
您可能想知道为什么需要这个分支。Netlify 需要一个项目分支来自动触发应用程序的部署。当更改被推送到这个分支时,Netlify 将启动部署过程。您希望避免 Netlify 和 CircleCI 管道并行运行两个部署进程的情况。这个新的分支充当了 Netlify 监视的诱饵。不要将更改推送到此分支。其目的是“分散”Netlify 部署过程的注意力,以便您可以使用 CircleCI 进行部署。你可以在 GitHub 上设置一个受保护的分支,这样你的团队中就没有人会误把它推上来。
创建网络应用程序
要创建新的 Netlify 应用程序,请转到您的 Netlify 仪表板,然后单击 Git 按钮中的新站点。接下来,选择 GitHub 作为您的提供商并搜索项目。
选择项目并转到下一步,为 Netlify 选择要部署的分支。选择你的诱饵分支。
接下来,点击 Deploy site 按钮,这样 Netlify 将执行您站点的第一次部署。单击链接,它应该类似于:sitename.netlify.app
要在 CircleCI 管道中执行自动部署,您需要从 Netlify 应用程序和帐户中获取两个配置值。
- 您刚刚创建的应用程序的
APP ID
可以在您的 Netlify 应用程序的站点详细信息部分找到。 - 个人访问令牌允许从您的部署管道访问您的 Netlify 帐户。在
User Settings > Applications
生成访问令牌。
将您的访问令牌保存在一个安全的地方,因为 Netlify 不允许您在创建后查看该值。你只能改变它。
在 CircleCI 建立项目
是时候在 CircleCI 上建立你的项目了。将.circleci
文件夹添加到项目的根目录。在其中,添加一个空的config.yml
文件。在下一节中,您将向该文件添加配置。将您的更改推送到 GitHub 项目。
接下来,转到 CircleCI 仪表板上的 Add Projects 页面来添加项目。
点击设置项目。这将加载一个对话框,CircleCI 自动检测您的配置文件。
单击 Let's Go 首次触发您的构建管道。构建将失败,因为您尚未向配置中添加代码。稍后您将执行此步骤。
在编写配置之前,您需要在 CircleCI 项目中添加 Netlify APP ID
和 access token 作为环境变量。
确保您的项目是“管道”页面上当前选定的项目。点击项目设置按钮。在设置页面上,从侧面菜单中选择环境变量。在这个新页面上,点击添加环境变量按钮,输入以下信息:
NETLIFY_SITE_ID
:您的网络应用程序的 API ID- 您的网络个人访问令牌。
编写部署配置
该过程的最后一步是编写部署配置。打开config.yml
文件并添加以下配置:
version: 2.1
jobs:
build:
working_directory: ~/repo
docker:
- image: cimg/node:16.13.2
steps:
- checkout
- run:
name: Update NPM
command: "sudo npm install -g npm"
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: Install Dependencies
command: npm install
- save_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
paths:
- ./node_modules
- run:
name: Build React App
command: npm run build
- save_cache:
key: app-build-cache-{{ .Branch }}
paths:
- ./build
- run:
name: Deploy to Netlify
command: ./node_modules/.bin/netlify deploy --site $NETLIFY_SITE_ID --auth $NETLIFY_ACCESS_TOKEN --prod --dir=build
workflows:
version: 2
build-deploy:
jobs:
- build:
filters:
branches:
only:
- main
在这个配置中,项目从存储库中签出,项目依赖项被安装和缓存。缓存完依赖项后,运行 React 构建命令npm run build
。它在项目根目录下的build
目录中创建应用程序的生产版本,然后缓存该版本。
最后,Netlify CLI 使用$NETLIFY_SITE_ID
和$NETLIFY_ACCESS_TOKEN
变量部署站点。然后,工作流配置确保只有main
分支触发网络部署。
在为 CircleCI 推送这个配置以部署站点之前,请将<p>
标记中的 React 消息改为:
<p>Successfully Deployed <code>A React application</code> to Netlify with CircleCI</p>
这使您能够查看已部署应用程序中的更改。
最后,提交你所有的修改,推送到 GitHub。这将自动触发部署管道和成功的构建。
单击构建以查看部署详细信息。
成功构建后,再次访问您的网站以验证您部署的更改。它应该显示新消息。
厉害!
要确认 Netlify 没有运行并行部署,请检查 Netlify 部署日志。生产部署中只有一个部署,第一个部署由分支netlify-deploy-ignore
触发。其余的没有显示分支,因为它们是由 CircleCI 管道触发的。
结论
这就是使用 CircleCI 将 React 站点自定义部署到 Netlify 的方法。这种设置为您提供了更多的灵活性和对网络部署的控制。这使您的团队能够在流程中运行更多的自定义步骤。
编码快乐!
Fikayo Adepoju 是 LinkedIn Learning(Lynda.com)的作者、全栈开发人员、技术作者和技术内容创建者,精通 Web 和移动技术以及 DevOps,拥有 10 多年开发可扩展分布式应用程序的经验。他为 CircleCI、Twilio、Auth0 和 New Stack 博客撰写了 40 多篇文章,并且在他的个人媒体页面上,他喜欢与尽可能多的从中受益的开发人员分享他的知识。你也可以在 Udemy 上查看他的视频课程。
构建 React 仪表板以可视化工作流和作业事件| CircleCI
数据可视化是将大型数据集和指标转化为图表、图形和其他视觉效果的过程。由此产生的数据可视化表示使得识别和共享实时趋势、异常值以及关于数据中所表示信息的新见解变得更加容易。使用 CircleCI webhooks ,我们可以收集工作流和作业事件的数据。在本教程中,我将引导您创建一个基于 React 的仪表板来可视化这些数据。
先决条件
要跟随本教程,您需要很好地掌握 JavaScript ES6。您还需要了解一些基本的 React 概念,比如钩子和功能组件。
您需要在工作站上安装这些软件:
- 一个最新安装的 node.js ,还有一个类似 npm 或者 yarn 的包管理器
- 您首选的代码编辑器
- 一个与你的 CircleCI webhook 通信的 API。你可以在这里了解如何设置一个。
- 确保本教程中涉及的Laravel API circle ci web hook已经启动并运行。
我们的教程是平台无关的,但是使用 CircleCI 作为例子。如果你没有 CircleCI 账号,请在 注册一个免费的 。
入门指南
使用以下命令创建一个新的 React 应用程序:
yarn create react-app circleci_workflow_dashboard
cd circleci_workflow_dashboard
对于这个项目,我们将使用 Ant Design 来布置 UI,使用 react-chartjs-2 来显示我们的图表。
使用 yarn 添加项目依赖关系:
yarn add antd react-chartjs-2@3.3.0 chart.js
下一步是为 ant design 导入样式。用这个更新src/App.css
:
@import '~antd/dist/antd.css';
添加实用功能
接下来,我们需要一种与 API 通信的方式。在src
文件夹中,创建一个名为utility
的新文件夹。在utility
文件夹中,创建一个名为API.js
的新文件。在src/utility/API.js
文件中,添加以下内容:
export const makeGETRequest = (url) => {
return fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
}).then((response) => response.json());
};
使用这个函数,我们可以向 API 发出一个GET
请求。该函数从 API 返回 JSON 响应。
为了简单起见,函数中不包括错误处理。
为了以可读的格式表示日期,我们可以创建一个实用函数。在utility
文件夹中,创建一个名为Date.js
的新文件,并添加:
export const formatDate = (date) =>
new Date(date).toLocaleString("en-GB", {
month: "long",
weekday: "long",
day: "numeric",
year: "numeric",
});
构建仪表板组件
我们的控制面板将显示一个事件表,您可以根据状态和事件类型进行筛选。您还可以在表格显示和图表显示之间切换仪表板。
在src
文件夹中,创建一个名为components
的新文件夹。该文件夹将保存我们将创建的所有自定义组件。
在components
目录中,创建一个名为StatusTag.jsx
的新文件。该组件将用于显示一个标签,标签的颜色由事件的状态决定。将此添加到StatusTag.jsx
:
import React from "react";
import { Tag } from "antd";
const StatusTag = ({ status }) => {
const statusColours = {
success: "green",
failed: "volcano",
error: "red",
canceled: "orange",
unauthorized: "magenta",
};
return <Tag color={statusColours[status]}>{status}</Tag>;
};
export default StatusTag;
接下来,在components
目录中创建一个名为TableView.jsx
的新文件。该组件将通知作为道具,并将其呈现在一个表格中,能够根据类型和状态进行过滤。将此代码添加到TableView.jsx
:
import React from "react";
import { Table } from "antd";
import { formatDate } from "../utility/Date";
import StatusTag from "./StatusTag";
const TableView = ({ notifications }) => {
const filterHandler = (value, record, key) => record[key] === value;
const columns = [
{
title: "Subject",
dataIndex: "commit_subject",
},
{
title: "Commit Author",
dataIndex: "commit_author",
},
{
title: "Happened At",
dataIndex: "happened_at",
render: (text) => formatDate(text),
},
{
title: "Event Type",
dataIndex: "type",
filters: [
{ text: "Job", value: "job-completed" },
{ text: "Workflow", value: "workflow-completed" },
],
onFilter: (value, record) => filterHandler(value, record, "type"),
},
{
title: "Event Status",
dataIndex: "event_status",
filters: [
{ text: "Success", value: "success" },
{ text: "Failed", value: "failed" },
{ text: "Error", value: "error" },
{ text: "Canceled", value: "canceled" },
{ text: "Unauthorized", value: "unauthorized" },
],
render: (text) => <StatusTag status={text} />,
onFilter: (value, record) => filterHandler(value, record, "event_status"),
},
{
title: "Notification ID",
dataIndex: "notification_id",
render: (text, record) => (
<a href={record["workflow_url"]} target="_blank" rel="noreferrer">
{text}
</a>
),
},
];
return (
<Table dataSource={notifications} columns={columns} rowKey="id" bordered />
);
};
export default TableView;
该表有六列:主题、提交作者、发生时间、事件类型、事件状态和通知 ID。
每一列由columns
常量中的一个对象表示。在不需要特殊渲染的地方,title
和dataIndex
对于列声明就足够了。title
用作列的标题,而dataIndex
条目让 antd 知道该列填充了哪个属性。
为了指定要在列中呈现的组件或 JSX 元素,我们向列的对象表示添加了一个render
键。我们用它来呈现我们之前创建的StatusTag
组件。我们还使用它为Happened At
列呈现一个格式良好的日期,并将通知 ID 作为工作流的链接。
构建图表组件
对于本教程,我们将在三个图表中呈现数据:
- 按状态显示事件分布的饼图:成功、失败、错误、已取消或未授权。
- 按类型显示事件分布的条形图:工作流或作业
- 显示事件时间线的折线图
要构建饼图,在components
文件夹中创建一个名为StatusDistribution.jsx
的新文件,并将以下代码添加到其中:
import React from "react";
import { Pie } from "react-chartjs-2";
const StatusDistribution = ({ notifications }) => {
const sortedNotifications = notifications.reduce(
(sortedNotifications, notification) => {
sortedNotifications[notification["event_status"]]++;
return sortedNotifications;
},
{ error: 0, failed: 0, success: 0, unauthorized: 0, canceled: 0 }
);
const data = {
labels: ["Error", "Failed", "Success", "Unauthorized", "Canceled"],
datasets: [
{
data: Object.values(sortedNotifications),
backgroundColor: [
"rgba(255, 99, 132, 0.2)",
"rgba(255, 206, 86, 0.2)",
"rgba(75, 192, 192, 0.2)",
"rgba(153, 102, 255, 0.2)",
"rgba(255, 159, 64, 0.2)",
],
borderColor: [
"rgba(255, 99, 132, 1)",
"rgba(255, 206, 86, 1)",
"rgba(75, 192, 192, 1)",
"rgba(153, 102, 255, 1)",
"rgba(255, 159, 64, 1)",
],
borderWidth: 1,
},
],
};
return (
<>
<div className="header">
<h1 className="title">Status Distribution</h1>
</div>
<Pie data={data} height={50} />
</>
);
};
export default StatusDistribution;
使用通知阵列上的reduce
功能,根据状态对通知进行分类和计数。然后,这些值被传递给datasets
配置中的data
键,该键被传递给Pie
组件。
要构建条形图,在components
目录中创建一个名为TypeDistribution.jsx
的新文件,并将这段代码添加到其中。
import React from "react";
import { Bar } from "react-chartjs-2";
const TypeDistribution = ({ notifications }) => {
const sortedNotifications = notifications.reduce(
(sortedNotifications, notification) => {
sortedNotifications[notification["type"]]++;
return sortedNotifications;
},
{ "job-completed": 0, "workflow-completed": 0 }
);
const data = {
labels: ["Job", "Workflow"],
datasets: [
{
label: "Event Type",
data: Object.values(sortedNotifications),
backgroundColor: ["rgba(54, 162, 235, 0.2)", "rgba(75, 192, 192, 0.2)"],
borderColor: ["rgba(54, 162, 235, 1)", "rgba(75, 192, 192, 1)"],
borderWidth: 1,
},
],
};
const options = {
scales: {
y: {
beginAtZero: true,
},
},
};
return (
<>
<div className="header">
<h1 className="title">Type Distribution</h1>
</div>
<Bar data={data} options={options} height={500} />
</>
);
};
export default TypeDistribution;
正如我们对状态分布图所做的那样,我们根据通知类型对通知进行排序和计数。然后,这些值被传递给datasets
配置中的data
键,该键被传递给Bar
组件。
要构建通知的时间线,在components
目录中创建一个名为Timeline.jsx
的新文件,并向其中添加以下代码:
import React from "react";
import { Line } from "react-chartjs-2";
import { formatDate } from "../utility/Date";
const Timeline = ({ notifications }) => {
const sortedNotifications = notifications.reduce(
(sortedNotifications, notification) => {
const notificationDate = formatDate(notification["happened_at"]);
if (notificationDate in sortedNotifications) {
sortedNotifications[notificationDate]++;
} else {
sortedNotifications[notificationDate] = 1;
}
return sortedNotifications;
},
{}
);
const data = {
labels: Object.keys(sortedNotifications),
datasets: [
{
label: "Number of events",
data: Object.values(sortedNotifications),
fill: false,
backgroundColor: "rgb(255, 99, 132)",
borderColor: "rgba(255, 99, 132, 0.2)",
},
],
};
const options = {
scales: {
y: {
beginAtZero: true,
},
},
};
return (
<>
<div className="header">
<h1 className="title">Event Timeline</h1>
</div>
<Line data={data} options={options} height={500} width={1500} />
</>
);
};
export default Timeline;
该组件的排序功能略有不同。因为我们不能在初始对象中指定所有可能的日期,所以我们从一个空对象开始。然后,对于每个通知,我们检查该日期是否已经存在一个键。如果是,我们增加计数,如果不是,我们把日期的值加 1。
接下来,我们需要构建一个组件来呈现网格中的所有图表。在components
目录中创建一个名为ChartView.jsx
的新文件,并将以下代码添加到其中:
import React from "react";
import StatusDistribution from "./StatusDistribution";
import { Col, Row } from "antd";
import TypeDistribution from "./TypeDistribution";
import Timeline from "./Timeline";
const ChartView = ({ notifications }) => {
return (
<>
<Timeline notifications={notifications} />
<Row style={{ marginTop: "30px" }} gutter={96}>
<Col>
<StatusDistribution notifications={notifications} />
</Col>
<Col offset={6}>
<TypeDistribution notifications={notifications} />
</Col>
</Row>
</>
);
};
export default ChartView;
在这个组件中,我们将状态分布和类型分布与上面的时间轴并排呈现。
像我们在这里所做的那样传递通知被称为道具演练。虽然一般不提倡。我们这样做是为了简化教程。在生产应用程序中,您应该考虑适当的状态管理实现。
把所有的放在一起
所有子组件就位后,更新src/App.js
以匹配:
import "./App.css";
import { makeGETRequest } from "./utility/Api";
import { useEffect, useState } from "react";
import { Card, Col, Row, Switch } from "antd";
import TableView from "./components/TableView";
import ChartView from "./components/ChartView";
const App = () => {
const [notifications, setNotifications] = useState([]);
const [showTableView, setShowTableView] = useState(false);
useEffect(() => {
makeGETRequest("http://127.0.0.1:8000/api/circleci").then((response) => {
setNotifications(response);
console.log(response);
});
}, []);
const handleSwitchValueChange = () => {
setShowTableView((showTableView) => !showTableView);
};
return (
<Card style={{ margin: "2%" }}>
<Row style={{ marginBottom: "10px" }}>
<Col span={6} offset={18}>
Show Data as Table
<Switch checked={showTableView} onChange={handleSwitchValueChange} />
</Col>
</Row>
{showTableView ? (
<TableView notifications={notifications} />
) : (
<ChartView notifications={notifications} />
)}
</Card>
);
};
export default App;
在这个组件中,我们从 API 的useEffect
钩子中检索通知,并使用setNotifications
函数将其保存到 state。然后我们声明一个函数来处理showTableView
状态变量的切换,它决定数据是显示在表格中还是图表中。
为了在视图之间切换,我们渲染一个Switch
,并把showTableView
和handleSwitchValueChange
值作为道具传递给它。
最后,根据showTableView
的值,渲染TableView
或ChartView
组件。
表格视图
图表视图
结论
在本教程中,我们研究了如何使用 API 构建 React 仪表板来可视化管道事件。通过在图表中可视化数据,我们可以对我们的管道有一个高层次的了解,也可以理解数据集,不管它有多大。虽然我们没有实现这一功能,但您也可以将数据导出到电子表格中以供进一步分析。与您的团队分享这个示例项目并扩展您的学习!
这篇文章的代码可以在 GitHub 上找到,CircleCI webhooks 的完整文档可以在这里找到。
Oluyemi 是一名拥有电信工程背景的技术爱好者。出于对解决用户日常遇到的问题的浓厚兴趣,他冒险进入编程领域,并从那时起将他解决问题的技能用于构建 web 和移动软件。Oluyemi 是一名热衷于分享知识的全栈软件工程师,他在世界各地的几个博客上发表了大量技术文章和博客文章。由于精通技术,他的爱好包括尝试新的编程语言和框架。
Oluyemi 是一名拥有电信工程背景的技术爱好者。出于对解决用户日常遇到的问题的浓厚兴趣,他冒险进入编程领域,并从那时起将他的问题解决技能用于构建 web 和移动软件。Oluyemi 是一名热衷于分享知识的全栈软件工程师,他在世界各地的几个博客上发表了大量技术文章和博客文章。作为技术专家,他的爱好包括尝试新的编程语言和框架。
重新设计以社区为中心的话语
原文:https://circleci.com/blog/redesigning-discourse-to-center-community-members/
强大的社区建立在信任和团队合作的基础上。我们的社区论坛,我们的开发者中心,甚至我们的 Twitter feed 和 Twitch channel 都在蓄势待发——这就是团队合作。建立信任是一个持续的过程,作为您的技术社区经理,这正是我的职责所在。
我想让你的项目、想法和反馈成为我们社区的中心。这意味着做出改变,让每个人都有空间。一个重要的改变已经在我们的社区论坛上实现了,讨论:一个行为准则。行为准则的主要作用是为我们在该领域的互动方式设定预期,并确保每个人的安全。
我们也在重新设计讨论,所以我想花一点时间来谈谈我们希望用一个新的设计来完成什么。
重新设计我们的社区论坛
我希望社区成员感受到欢迎、参与和支持。我是第一个承认 Discuss 对我们社区支持不够的人。我们只是没有资源。但我现在在这里,我的工作是确保我们的用户是我们社区的中心。重新设计讨论是打造专注于为用户提供资源、答案和联系的体验的绝佳机会。
使用话语
我们的社区论坛建立在 Discourse 之上,这是一个流行的社区建设平台。它是开源的,有大量的特性,并且高度可定制,这是它如此受欢迎的原因。我们正在做出改变,专注于尽快向用户呈现最重要的信息。例如,现在有一个专门的区域供建议阅读,这样有价值的资源总是在同一个地方。你还可以找到一个专门针对未回答问题的板块,这样我们就可以利用我们的综合知识来帮助每个人。
重新设计社区的目标
我希望这一重新设计能让寻找资源、提问和参与社区变得更加容易。无论你是 CI 爱好者、DevOps 实践者,还是像我一样的永久学习者,当我们共享空间和对话时,我们可以以我们尚未发现的方式互相帮助。
改变是不舒服的,但和朋友在一起更容易。这就是为什么我邀请你加入我们的社区空间,提出问题,并帮助他人学习。
在讨论的任何时候联系我,或者在 katy@circleci.com 给我发电子邮件。我们网上见!
如何减少易出故障的测试
测试有助于您在发布软件之前发现错误,使您能够向客户交付高质量的产品。然而,有时测试是不可靠的。
什么是片状测试?
易变的测试,也被称为飘忽不定,不能产生准确和一致的结果。由于新编写的代码或外部因素,这些测试可能不可靠。
如果你的测试不可靠,它们不能帮助你找到(并修复)所有的错误,这会对用户体验产生负面影响。在本文中,我将帮助您发现您的测试是否有问题,并向您展示如何修复它们。
如何发现古怪的测试
片状测试主要是由于测试数据不足、测试环境范围狭窄和技术复杂造成的。导致测试不可靠的其他一些因素有:
- 异步等待
- 超时设定
- 一天中的时间
- 并发
- 测试订单相关性
本文接下来的部分将描述如何识别这些因素何时导致测试剥落。也有如何防止这些类型的片状剥落的描述。
异步等待
开发人员经常根据特定的数据编写测试。如果测试在数据加载之前运行,它们将变得不可靠。这在广泛使用异步 API 的语言中尤其常见,比如 JavaScript、C#和 Go。
例如,考虑一个从外部 API 获取数据的集成测试。如果应用程序代码异步调用外部 API,但没有明确地等待数据准备好,这可能会导致测试失败。有时候,数据会在测试需要的时候准备好。有时候,不会。成功或失败可能取决于运行代码的机器的速度、网络连接的质量以及许多其他因素。
让测试等待可以提高效率和减少剥落。然而,在自动化测试过程中,等待也会导致长时间的系统延迟。重要的是要注意,由于不恰当的异步调用而导致测试失败的代码通常是在被测试的应用程序代码中,而不是在测试本身中。
超时设定
另一方面,异步等待的问题是,如果出现问题,它们可能会永远持续下去。设置超时可以解决这个问题,但是超时会引入一个新的问题。例如,如果从一个 API 调用加载数据有很长的延迟,测试就会失败,因为等待时间超过了超时限制。这也会导致测试不稳定。
加载数据时可能会发生超时,原因有几个:
- 对负载过重的外部 API 的调用
- 在慢速机器上从硬盘中检索数据
- 文件从计算机上传到服务器
从 API 加载数据、从相对较慢的磁盘(如 EBS)读取数据或等待文件上传可能需要不同的时间,因此有时会发生超时,因为您必须设置某种时间限制。你不能永远等着事情结束。有时会发生超时,而其他时候测试会成功运行而不会超时。
要解决这个问题,模拟对外部系统的调用通常是一个好主意,以确保您的测试是在测试您的代码,而不是第三方 API 的可靠性。不过,请用你最好的判断力。例如,了解您的应用程序集成的服务是否经常缓慢或不可用是很有用的。在这种情况下,“不可靠”的测试为您提供了有价值的信息,您可能不想做任何更改。
一天中的时间
有时,代码行为会在一天中发生变化。这意味着测试的成功可能取决于测试运行的时间。例如,假设我们正在为一个预约系统编写自动化测试,该系统包括特定时间间隔的预约时间段。如果我们在生产管道上运行这个测试,它会在不同的时间产生不同的结果。时间段的可用性取决于一天中的时间,这增加了测试的片面性。
并发
在应用程序代码或测试本身中可能存在数据竞争、死锁或并发问题。当开发人员对不同线程执行的操作顺序做出不正确的假设时,这可能会导致不可靠的测试。
测试执行中的不确定性不一定是一个问题,因为在一些情况下,多个代码行为是正确的。当测试只检查所有可能的有效行为的子集时,结果可能是剥落。
测试订单相关性
测试可能会因为之前或之后运行的测试而失败。这是因为许多测试同时使用共享数据,如状态变量、输入和依赖关系。
我们需要完全消除或最小化这些测试之间的依赖性,以提高准确性并减少碎片。只要你的测试依赖于另一个模块,就使用存根和模拟。存根是具有对请求的预定义响应的对象。仿制品(也称为赝品)是模仿工作表现的物体,但不是 100%的产品。模仿和存根会创建孤立运行的测试。
如何防止片状测试
以下是您可以使用的策略的快速概述:
- 加载数据时,通过观察超时和延迟来避免异步等待
- 模拟对外部系统的调用以防止超时
- 将测试移到对您的团队来说最理想的时间
- 微调操作顺序以实现最大并发性
- 最小化测试订单依赖性
自动化薄片测试检测
回顾你所有的测试,更不用说每一个具体的剥落因素,是一个耗时的过程。自动化脆弱测试检测的最简单的方法是自动化运行您的测试并收集和显示数据的系统。像 CircleCI 这样的持续集成和持续交付(CI/CD)工具可以提供帮助。
失败的原因是什么:应用程序还是测试?
测试失败只有两种方式。要么是测试不可靠,要么是应用程序没有按预期工作。有必要了解测试失败是由于剥落还是真正的应用问题。如果是片状测试,我们可以优化测试以减少片状。如果是应用程序故障,您应该向相关的风险承担者报告这个有效的故障。
成功率可以是主分支中唯一的真实来源,因为主分支上不应该有测试失败。它应该有百分之百的成功率。如果成功率低于 100 %,要么是由于应用程序问题导致测试失败,要么是测试不可靠。
利用来自改进的洞察力的数据
一旦您有了自动化的测试运行程序,您就可以开始收集数据了。例如,CircleCI 提供了一个带有片状测试检测的 Test Insights 仪表板,可以帮助您分析和诊断间歇性测试故障和事件。Test Insights 提供了您最近 100 次测试执行的详细概述,并将自动标记非确定性失败的测试以及那些长期运行或最常失败的测试。
CircleCI 还提供了 webhooks ,用于集成第三方监控和观察工具,如 Data Dog 和 Sumo Logic。这提供了实时工作绩效监控和具有用户友好界面的高级 CI 分析等优势。这些集成的仪表板提供了早期指标的识别,因此开发人员可以有效地解决影响其应用程序的问题。
更快地发现和交流故障模式
仪表板面板包括一个作业分析概览,用于跟踪和显示项目中的作业状态等实时数据,CircleCI 管道的作业结果可视化,以及管道中十大最慢成功作业的可视化。
像 CircleCI 这样的工具可以检测出不稳定的测试,因为自动化可以让你更频繁地运行测试,从而更容易注意到你可能会忽略的失败模式。自动化还有助于开发更好的技术来检测不稳定的测试,减少不确定性,修复测试,将测试失败标记为不稳定或不稳定,并防止未来的不稳定测试。
包裹
既然您对易变测试的危险、如何检测它们以及如何修复它们有了更多的了解,那么考虑使用自动化工具来帮助您更好地进行测试。我希望我已经说服您尝试使用您的 CI/CD 管道来自动发现不可靠的测试。您可以马上开始,今天就注册您的 CircleCI 免费试用。
通过共享库降低微服务开销| CircleCI
原文:https://circleci.com/blog/reducing-microservice-overhead-with-shared-libraries/
单片与微服务的利弊
这是一个常见的故事:产品团队获得了早期的成功,并成长为一个大型的整体代码库。虽然一切都在一个代码库中,但是可以快速添加功能。这部分是因为能够在代码库中的每个特性之间利用共享代码。
当您的团队添加新功能时,开发人员可以利用现有的代码库来满足需求,如日志记录或特殊错误处理。这使得开发人员有更多的时间来专注于编写为最终用户带来直接价值的代码。
随着产品和组织的持续增长,棘手的部分出现了。随着增长,向代码库添加新功能变得很麻烦。很快就有太多的开发人员在同一个代码上工作,而且,有这么多并发的变化,协调发布变得更加困难。面对这个问题,许多组织决定将整体结构分解为微服务。
将整体结构分解成微服务
虽然它使更多的开发人员能够相互独立地工作,但是分解会带来新的障碍。一个新问题是服务间网络调用的额外复杂性和不可靠性。
这种复杂性也减缓了开发。每当一个新的服务被添加到架构中时,它就需要解决相同类型的问题。该服务不再仅仅从其他特性中调用以前编写的函数。
现在团队必须决定如何处理这种情况。
应该使用哪些第三方库?什么是正确的界面?应该测试多少?这是每个团队都要承担的成本,他们被迫花费时间来实现跨领域问题的冗余解决方案,而不是解决服务的主要问题。
一个可能的解决方案是跨微服务使用公共的内部共享库。这种方法使团队能够共享常见问题的解决方案,同时保留在需要时使用定制解决方案的自主权。
在 CircleCI,我们像第三方库一样开发和管理这些库(例如,每个版本都有版本,服务声明依赖于它们的首选版本)。
共享库的示例和优势
有些情况下,在一个地方解决问题比使用单独、不同的解决方案更有意义。
共享日志库
虽然只使用每种语言的标准库很容易让日志工作,但是团队可以从一个公共库受益,该库可以处理常见日志需求的样板文件。这包括诸如转发、旋转、格式化和机密编辑的配置等功能。
共享跟踪库
追踪是一个类似的例子。一旦每个服务都导出了跟踪,那么可观察性就有了很大的好处,比如能够观察到单个客户请求中涉及的每个服务。
与日志记录一样,团队受益于开箱即用的跟踪工作,而不是被迫以同样的方式配置第三方库。该库还可以包含特定于您的组织的逻辑。您可能有一些有意义的字段(例如 customer-id、api-version ),但是要求每个服务都遵循相同的方法来标记跟踪。
共享依赖冲突管理库
最后,另一种情况是保持对第三方的依赖。这可能包括错误修复、性能改进或安全补丁。
对于每个服务来说,当他们的 JSON 解析库的版本有一个关键的安全更新需要修补时,注意到这一点是相对简单的。但是,对于每个服务来说,知道某个特定版本是否与该服务正在使用的其他库(如日志和跟踪)兼容可能更具挑战性。在一个地方管理这些依赖关系减少了团队保护代码所需的步骤。
在 CircleCI,我们对这个问题的解决方案叫做 clj-parent。实际上,clj-parent 在我们的各种服务中充当了一个共享的、版本化的依赖锁文件。它使我们能够在一个地方解决这些版本冲突,因此团队只需简单地删除它的单一版本,就可以获得正确的依赖版本。
共享库的缺点
没有完美的解决方案,共享库也有自己的挑战。
我们的 clj-parent 项目给需要新版本的托管依赖项的团队带来了额外的开销。我们需要更新 clj-parent 中的依赖版本,并在团队将它引入他们的构建之前发布它。如果我们更新 clj-parent 的速度很慢,那么这个团队现在就会被阻止等待它。
共享库也需要更多的前期工作来做好。添加新特性需要在方法上获得不同团队的一致意见。测试和良好的文档对于确保它能被多个团队使用是至关重要的。最后,保持向后兼容性对于防止使用旧版本破坏服务是必要的。
实现共享库的技巧
正如 Sam Newman 在构建微服务中所描述的,重要的是不要在微服务之间引入不必要的耦合。共享库应该提供横切基础设施,而不是服务领域逻辑或产品功能。
对于 CircleCI 来说,更新瓶颈挑战的解决方案是让共享库有一个专用的所有者。拥有一个专门的团队可以确保库得到良好的维护,并在出现新的 CVE 时及时更新。
更重要的是,拥有一个独立的团队提供了一个重要的检查点,以确保为一个库提出的代码对不止一个服务有益。这些库应该用于巩固而不是收集功能。换句话说,我们正在标准化一个通用的解决方案,而不是把不同团队的解决方案放在一个地方。
让一个团队专门负责构建共享库对您的组织来说可能是不可行的。在这种情况下,重要的是要有一种图书馆心态,对进入共享库中的内容保持谨慎。
降低构建和维护成本
单片与微服务的利弊仍在争论之中。在 CircleCI,我们试图用定义明确的领域来提供服务。在这样做的过程中,一组针对日志、跟踪和度量等问题的共享库帮助降低了构建和维护这些新服务的成本。
通过立即注册您的 CircleCI 免费试用,您可以立即开始降低成本并提高效率。
我们如何推出 CircleCI orbs | CircleCI
原文:https://circleci.com/blog/reflections-major-feature-launch/
摘自一位产品经理关于 CircleCI orbs 发布的回顾。
2018 年 11 月,我们推出 CircleCI orbs 。我最近写了关于我们在构建新的包管理器时所做的设计决定。在这里,我想分享一些我们在推出主要新功能时学到的(或重新学到的)经验。你可能不会感到惊讶,我们想了很多关于如何发布工作软件,我们的追溯过程帮助我们把我们观察到的和学到的用语言表达出来。
以下是我们关于 orbs 启动计划的内部回顾的摘录,为公众消费做了一些编辑。
_ __ * * _
摘自题为“值得分享的观察与思考”的一节:
投入专门但有限的时间进行内部概念验证是非常宝贵的。
内部截止日期是保持专注于工作原型的巨大动力。在我们的例子中,它也给了我们一个清晰但相当低的“足够好”的标准来测试这个概念。在 CircleCI 2.0 发布后的第一次主要迁移浪潮中,我们对解决引导和管理新配置格式的困难有了新的兴趣。2018 年 3 月(orbs 上市前 8 个月),我们开始了 orbs 的初步设计工作。
在那段时间,我们还为整个工程组织(来自 11 个州和 10 个国家至少 25 个不同地点的 50 多名工程师)的大型聚会制定了议程。那次会议提供了一个完美的机会:不仅仅是许多不同类型的工程师作为一个被俘虏的、投入的观众解决了无数的问题,而且他们所有人都是他们必须交付的项目中我们平台的用户。像这样的测试有一个短的截止日期比交付生产软件的短的截止日期更容易,因为我们没有做出设计妥协,而是选择妥协一些端到端的功能和润色,以便测试我们最危险的假设。我们投入了我们(当时新成立的)开发人员体验团队一个月的努力,创建了足够的工作软件来测试 orb 的核心设计概念。经过 4 名工程师一个月的投资,仅用了一天时间就迅速获得了对初始设计决策的各种反馈,这让我们不仅可以验证概念的强度,还可以设定目标和优先级,以使 orbs 可以投入生产。
在计划开始的时候,有几周的时间从 backlog 交付的责任中抽身出来,进行开放式设计,这是非常宝贵的。
我们花了大量时间进行开放式设计讨论,包括许多具体草图、理论探索和大图概念开发。除了功能细节之外,我们必须围绕 orb 开发发明一个习语,包括形式化我们的术语,以便就 DSL 设计进行富有成效的对话。我们漫无目的地交谈,没有明确的议程,这在商业环境中通常是令人讨厌的。这在当时可能很容易被认为是一种气味,因为团队的速度急剧下降,但是这些种类的抽象和框架对于完成将 orbs 转化为生产就绪软件的工程工作是必不可少的。
“文件-第一设计”运作良好。
你可能听说过这样的故事,亚马逊在早期使用一种模式在产品计划开始之前而不是之后写新闻稿,作为展示最终状态的一种方式。在我们的案例中,我们在预览文档中对文档做了类似的处理。我们的 preview SDK 至少是从另外两个 repos 发展而来的,被设计成一个地方来绘制我们内部使用的具体代码和伪文档。我们把它竖起来,让潜在客户阅读,就好像它是一个工作产品的文档,然后我们才把它投入使用。这意味着我们可以从一些早期采用者那里获得稳定的反馈,了解我们是否在正确的轨道上。当我们去预览发布时,我们的文档已经足够丰富,从开发到生产的过渡相对“便宜”。
在市场发布之前,增量、安静的发布是至关重要的。
在 orbs 正式发布之前,许多用户可以尝试不同版本的 orbs。在我们的营销发布前一个月,我们的功能已经完成,并悄悄地邀请我们的合作伙伴和客户使用它们。在发布之前留出时间,让团队可以进行几波审查、修复 bug、技术债务以及与其他团队的协调,这被证明是相当稳定的发布所必需的。
拥有一个小而活跃的用户群的价值真的不能被夸大。我们很幸运能够与我们的一些客户接触,而不必保密。我们能够限制每个阶段的风险范围,从验证概念到具体的语法决策。我们能够在大范围发布之前解决一些关键的操作和逻辑错误。我们能够在受控但真实的使用模式下安装我们的监控系统并设置我们的运营手册,然后在我们的营销发布后经历流量高峰。
有些想法需要很长时间才能变成现实。
球体的想法花了很长时间才最终获得批准。可重用配置组件的最初概念可以追溯到几年前,可以追溯到我们的创始人 Paul Biggar 在 2015 年(orbs 推出前 3 年)所做的设计工作。这个概念在得到产品工程团队的热切关注之前,是断断续续发展起来的。即使在它被首次提出后,也花了几个月的时间让每个人都适应这个概念。最终,这个几乎被遗忘的想法变成了我们的“下一步”计划,因为星星围绕着我们的软件成熟度、我们的中期业务需求和各种客户请求的时间排列。
跨组织协调工作应尽早开始。
一次成功的发布有许多部分来自工程之外的部门,所有这些都需要不小的准备时间。我们通常的做法是在项目开始时,而不是结束时,与我们的 SRE、安全、文档、营销和解决方案部门进行基本的情况介绍。特别是对于此次发布,它还包括对我们新的代码共享服务条款的法律帮助、对 orb 发布/导入模型的安全审查、文档、连接分析、与营销部门协调以及建立生产就绪运营状态,这些都是发布计划中必须考虑的部分。每一个跨团队的流程都要花费数周的时间才能完成,所以如果我们希望如期完成,提前做好计划是非常重要的。
在 CircleCI,我们对软件团队的实践进行了大量的思考,同时,我们也在不断地管理我们自己不断增长的团队和雄心。虽然上面的追溯文档要长得多,但我已经分享了包含一般经验的部分,希望对您的团队有价值。
我们喜欢听到团队所依赖的实践和技术来持续地发布高质量的软件产品,我们也喜欢听到你的意见!把你的故事和教训发推特给我们。
在 SQL 数据库| CircleCI 上执行数据库测试
测试是那些活动中的一种,如果不是详尽的,就不会对你的软件开发过程产生完全的影响。开发人员常常只关心测试系统的应用层(也称为代码库),而忽略了测试数据层(数据库),这与测试代码本身同样重要。
什么是数据库测试?数据库测试主要包括构建 SQL 查询,以断言和验证数据库应用程序所需的不同数据库操作、结构和属性。这些可能包括验证模式、测试 CRUD 操作和事务,以确保正确设置数据库来为应用程序服务。关于数据库测试和执行测试的策略的更多信息,您可以查看我们的数据库测试介绍文章。
在本教程中,您将学习并演示如何测试关系型 MySQL 数据库,然后着手建立一个持续集成管道来自动化测试过程。
先决条件
要跟进,你需要几样东西:
安装并设置好所有这些之后,您就可以开始了。
获取远程 MySQL 实例
运行数据库测试时,您希望确保不是在生产数据库上运行这些测试。确保测试数据库与生产数据库完全相似。
对于本教程,您将需要一个远程 MySQL 数据库来运行测试。许多云提供商提供免费的 MySQL 数据库,您可以在以后使用和删除。你可以从任何你喜欢的服务中免费获得你的 MySQL 数据库,只要你有连接细节。
对于本教程,我使用来自云团的数据库。一旦你注册,导航到 MySQL 。MySQL 的 Express 计划对我们的项目来说应该足够好。点击免费试用。使用默认设置,建立一个免费的 MySQL 数据库。我们将在下一节使用数据库细节。
注意事项:
- 如果您使用的服务在实例化时不创建默认表,则创建一个表。
- 对于本教程,您可以使用 MySQL 5.7 或 MySQL 8.0 版本,以及
latin
、utf8
、utf8mb4
和gbk
字符集。唯一的例外是 MySQL 8.0 版本中的utf8mb4
字符集,它会抛出一个错误。
你可以在 GitHub 上获得最终的项目代码。这里使用的 MySQL 实例是 MySQL 5.7 版本。名为circleci
的数据库使用了utf8
字符集。
用 Jest 和 MySQL SDK 设置测试项目
一旦设置好远程 MySQL,现在就可以开始设置测试环境了。不同的代码库有不同的测试运行器来执行测试。在本教程中,您将使用 Jest 测试框架和 MySQL Node.js SDK 来执行数据库测试,因为我们使用的是 JavaScript 项目。
通过创建一个文件夹并移动到其根目录来启动一个新的 Node.js 项目:
mkdir relational-db-testing
cd relational-db-testing
然后,使用以下命令初始化 Node.js 项目并构建一个基本的package.json
文件:
npm init -y
接下来,安装jest
作为开发依赖项:
npm install --save-dev jest
然后,安装mysql2
和faker
包以连接到您的 MySQL 实例并生成假的测试数据:
npm install mysql2 faker
现在您已经有了项目设置和所有的包,是时候开始编写测试了。
CRUD 测试的数据完整性
对于本教程,您将运行简单的 CRUD(创建-读取-更新-删除)测试来验证 CRUD 操作中的数据完整性。
在项目的根目录下创建一个新文件users.test.js
。在这个文件中,您将通过添加用户、读取添加的用户、更新用户和删除用户数据来测试一个users
数据库表。
将以下代码添加到文件中。包括测试CREATE
和READ
操作的测试。
const { createPool } = require("mysql2/promise");
const faker = require("faker");
describe("Database Tests", () => {
let connection;
beforeEach(async () => {
let createTableSQL =
"CREATE TABLE `users` ( `id` INT(2) NOT NULL AUTO_INCREMENT , `name` VARCHAR(100) NOT NULL , `email` VARCHAR(50) NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;";
connection = await createPool({
host: "YOUR_DB_HOST",
user: "YOUR_DB_USER",
password: "YOUR_DB_PASSWORD",
port: YOUR_DB_PORT,
database: "YOUR_DB_NAME"
});
console.log("Connected to database");
await connection.query(createTableSQL);
});
it("Test CREATE and READ", async () => {
try {
const total_test_users = 3;
let insertQueries = [];
for (let i = 0; i < total_test_users; i++) {
let insertSQL = `INSERT INTO users (id, name, email) VALUES (NULL, '${faker.name.findName()}', '${faker.internet.email()}');`;
insertQueries.push(connection.query(insertSQL));
}
await Promise.all(insertQueries);
const [rows, fields] = await connection.query("SELECT * FROM users");
expect(rows.length).toBe(total_test_users);
} catch (error) {
console.log(error);
let dropTableSQL = "DROP TABLE IF EXISTS `users`";
await connection.query(dropTableSQL);
await connection.end();
}
}, 60000);
afterEach(async () => {
let dropTableSQL = "DROP TABLE IF EXISTS `users`";
await connection.query(dropTableSQL);
await connection.end();
});
});
这项测试:
- 建立与数据库实例的连接
- 在
beforeEach
函数调用中,它创建了一个带有字段id
、name
和email
的users
表 - 在
afterEach
函数调用中,表格被删除,连接结束
删除这个表可以确保套件中的每个测试都有一个新的users
表实例,以避免测试使用相同的共享数据。记住分别用您的远程 MySQL 数据库主机、用户、密码、端口和数据库名称替换YOUR_DB_HOST
、YOUR_DB_USER
、YOUR_DB_PASSWORD
、YOUR_DB_PORT
和YOUR_DB_NAME
。
在Test CREATE and READ
测试用例中,faker
库用于向users
表添加 3 个用户。然后查询该表,以确保它包含刚刚添加的用户的确切数量。
要运行这个测试,更新您的package.json
文件中的test
脚本:
....,
"scripts" : {
"test" : "jest"
}
现在转到您的终端,通过运行以下命令来运行测试:
npm run test
测试完成后,您的终端会显示以下输出。
> jest
console.log
Connected to database
at Object.<anonymous> (users.test.js:18:17)
PASS ./users.test.js (5.5 s)
Database Tests
✓ Test CREATE and READ (4292 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.554 s
Ran all test suites.
通过了对CREATE
和READ
的测试,这意味着可以用简单的INSERT
查询对我们的模式和数据库执行这些操作。这可以进一步扩展,以检查重复的电子邮件或用户名等情况。
对于UPDATE
和DELETE
操作,继续在第一个测试的it
模块下添加以下测试用例:
it("Test UPDATE and DELETE", async () => {
try {
let name = "Test user";
let email = "test@user.com";
let nameUpdate = "My Test User";
let insertSQL = `INSERT INTO users (id, name, email) VALUES (NULL, '${name}', '${email}');`;
await connection.query(insertSQL);
//Run and test update
let updateSQL = `UPDATE users SET name='${nameUpdate}' WHERE email='${email}'`;
await connection.query(updateSQL);
const [rows, fields] = await connection.query("SELECT * FROM users");
expect(rows[0].name).toBe(nameUpdate);
//Run and test delete
let deleteSQL = `DELETE FROM users WHERE email='${email}'`;
await connection.query(deleteSQL);
const [allrows] = await connection.query("SELECT * FROM users");
expect(allrows.length).toBe(0);
} catch (error) {
console.log(error);
let dropTableSQL = "DROP TABLE IF EXISTS `users`";
await connection.query(dropTableSQL);
await connection.end();
}
}, 60000);
在这个测试中,通过创建一个新用户并更新该用户的name
字段来测试UPDATE
操作。然后查询该表以检查更新是否持续。
接下来,使用用户的电子邮件对用户数据运行一个DELETE
操作。再次查询该表以检查该用户是否不再存在。
保存该文件并再次运行测试命令(npm run test
)。这一次,您将在终端中看到以下输出:
> jest
console.log
Connected to database
at Object.<anonymous> (users.test.js:18:17)
console.log
Connected to database
at Object.<anonymous> (users.test.js:18:17)
PASS ./users.test.js (8.165 s)
Database Tests
✓ Test CREATE and READ (3452 ms)
✓ Test UPDATE and DELETE (3205 ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 8.226 s
Ran all test suites.
自动化测试过程
既然您已经正确地运行了您的测试,那么是时候自动化测试过程了。首先,您需要保存对测试文件的所有更改。
然后,将当前目录作为本地 git 存储库,并添加一个.gitignore
文件。运行这些命令:
git init
wget https://raw.githubusercontent.com/github/gitignore/master/Node.gitignore && mv Node.gitignore .gitignore
第二个命令复制 GitHub 官方文件.gitignore
的内容,用于托管在这里的 JavaScript 项目。
接下来,将项目推送到 GitHub 。
转到 CircleCI 仪表板上的Projects page
添加项目。
点击设置项目开始设置项目。在弹出的模式上点击跳过这一步。我们将在本教程的后面手动添加 CircleCI 配置。
在设置页面上,单击使用现有配置以指示 CircleCI 您将手动添加一个配置文件,而不是使用示例。接下来,系统会提示您下载管道的配置文件或开始构建。
点击开始建造。这个构建将会失败,因为我们还没有设置配置文件。那将是你的下一步。
要添加持续集成管道脚本,请返回到您的项目。在项目文件夹的根目录下创建一个名为.circleci
的文件夹,并向其中添加一个名为config.yml
的文件。在config.yml
里面,输入这个代码:
version: 2.1
jobs:
build:
working_directory: ~/repo
docker:
- image: circleci/node:12
steps:
- checkout
- run:
name: Update NPM
command: "sudo npm install -g npm"
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: Install Dependencies
command: npm install
- save_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
paths:
- ./node_modules
- run:
name: Run tests
command: npm run test
这个配置获取适当的 Node.js 映像并更新其中的npm
。然后安装并缓存依赖项,以加快后续的构建。最后,使用npm run test
命令运行测试。
提交对项目的所有更改,并推送到您的远程 GitHub 存储库。这将触发构建管道,应该会成功。
点击构建,然后展开运行测试查看测试细节。
结论
在本教程中,您已经演示了如何测试关系数据库(MySQL)以及如何使用 CircleCI 自动化测试过程。如果测试对你的团队来说不是优先考虑的事情,与他们分享这个教程,这样他们就可以自己了解设置测试是多么容易。数据是您将要开发的几乎每个应用程序的核心。故障、损坏或数据泄露会导致用户对产品的可信度失去信心,并导致公司失去业务。在您的软件开发操作中,将数据库测试放在首位。
编码快乐!
Fikayo Adepoju 是 LinkedIn Learning(Lynda.com)的作者、全栈开发人员、技术作者和技术内容创建者,精通 Web 和移动技术以及 DevOps,拥有 10 多年开发可扩展分布式应用程序的经验。他为 CircleCI、Twilio、Auth0 和 New Stack 博客撰写了 40 多篇文章,并且在他的个人媒体页面上,他喜欢与尽可能多的从中受益的开发人员分享他的知识。你也可以在 Udemy 上查看他的视频课程。
使用 AWS CDK 通过 Lambda 授权器自动部署 REST APIs
本教程涵盖:
- 创建新的 AWS CDK 应用程序
- 添加 Lambda 授权器并定义 CDK 结构
- 自动化和测试 CDK 堆栈的部署
这是由两部分组成的系列教程的第二部分。您还可以了解如何自动将 AWS Lambda 功能部署到 AWS CDK 。
AWS 云开发工具包(AWS CDK)是一个开源框架,允许您使用自己选择的编程语言来定义和部署云资源。AWS CDK 是一个基础设施即代码(IaC)解决方案,类似于 Terraform ,它允许您使用面向对象编程语言的表达能力来定义您的云资源。AWS CDK 框架为所有主要的 AWS 服务提供了大多数流行编程语言的库。您可以使用这些库轻松地为您的整个系统定义云应用程序栈。它消除了上下文切换,有助于加速开发过程。开发者不需要学习新的编程语言或新的工具来从 AWS CDK 中获益。
在本教程中,我将指导您使用 AWS CDK 通过基于 AWS Lambda 的授权器部署 REST APIs。您将了解如何通过添加授权者、使用计划、节流、速率限制等来使用 API 网关构造定制 API 的行为。
先决条件
对于本教程,您需要在您的机器上设置 NodeJS,因为您将使用它来定义 AWS CDK 应用程序和 AWS Lambda 处理程序。您还需要在您的系统上安装 AWS CLI 和 AWS CDK CLI,以便您可以配置 AWS 凭据并手动构建您的 CDK 应用程序。您将需要一个用于部署应用程序的 AWS 帐户和一个用于自动化部署的 CircleCI 帐户。下面是跟随本教程所需的所有东西的列表:
我们的教程是平台无关的,但是使用 CircleCI 作为例子。如果你没有 CircleCI 账号,请在 注册一个免费的 。
创建新的 AWS CDK 项目
为 CDK 项目创建一个新目录,并导航到其中。运行这些命令:
mkdir aws-cdk-api-auth-lambda-circle-ci
cd aws-cdk-api-auth-lambda-circle-ci
使用 CDK CLI,运行cdk init
命令,在 TypeScript 中创建一个新的 CDK 项目:
cdk init app --language typescript
这个命令创建一个新的 CDK 项目,它只有一个堆栈和一个应用程序。
注意: AWS CDK 支持所有主流编程语言,包括 TypeScript、Python、Java、C#。如果您选择不同的编程语言,您仍然可以遵循本教程中的步骤,但是语法将根据您选择的编程语言而改变。
添加 NodeJS Lambda 函数
在本节中,您将使用 NodeJS 定义一个 AWS Lambda 函数,该函数可用于与 AWS API Gateway 的代理集成。API Gateway 的 AWS Lambda 代理集成提供了一种简单而强大的机制来构建 API 的业务逻辑。每当通过 API Gateway 调用 REST API 时,代理集成允许客户端在后端调用单个 AWS Lambda 函数。
这个例子使用的 AWS Lambda 函数与本系列第一篇教程中定义的非常相似,自动将 AWS Lambda 函数部署到 AWS CDK 。只需更改 AWS Lambda 代理集成的请求和响应对象。
首先,在 CDK 项目的根目录下创建一个lambda
目录。在lambda
文件夹中,创建另一个名为processJob
的文件夹。在processJob
目录下创建一个package.json
文件,用于定义依赖关系。打开package.json
文件,添加以下内容:
{
"name": "circle-ci-upload-csv-lambda-function",
"version": "0.1.0",
"dependencies": {
"csv-stringify": "^6.0.5",
"fs": "0.0.1-security",
"uuid": "^8.3.2"
}
}
这个脚本定义了项目的名称,并添加了一些 Lambda 处理程序将使用的依赖项。
现在,从终端导航到 processJob 文件夹来安装 NPM 包。
cd lambda/processJob
npm install
接下来,在processJob
目录中创建一个index.js
文件,并向其中添加以下代码片段。我们从链接教程中获取了完整的代码片段,并修改了请求和响应对象。
"use strict";
const AWS = require('aws-sdk');
const { v4: uuidv4 } = require('uuid');
var fs = require('fs');
const { stringify } = require('csv-stringify/sync');
AWS.config.update({ region: 'us-west-2' });
var ddb = new AWS.DynamoDB();
const s3 = new AWS.S3();
const TABLE_NAME = process.env.TABLE_NAME;
const BUCKET_NAME = process.env.BUCKET_NAME;
exports.handler = async function (event) {
try {
const uploadedObjectKey = generateDataAndUploadToS3();
const eventBody = JSON.parse(event["body"]);
const jobId = eventBody["jobId"];
console.log("event", jobId);
var params = {
TableName: TABLE_NAME,
Item: {
jobId: { S: jobId },
reportFileName: { S: uploadedObjectKey },
},
};
// Call DynamoDB to add the item to the table
await ddb.putItem(params).promise();
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
status: "success",
jobId: jobId,
objectKey: uploadedObjectKey,
}),
};
} catch (error) {
throw Error(`Error in backend: ${error}`);
}
};
const generateDataAndUploadToS3 = () => {
var filePath = "/tmp/test_user_data.csv";
const objectKey = `${uuidv4()}.csv`;
writeCsvToFileAndUpload(filePath, objectKey);
return objectKey;
};
const uploadFile = (fileName, objectKey) => {
// Read content from the file
const fileContent = fs.readFileSync(fileName);
// Setting up S3 upload parameters
const params = {
Bucket: BUCKET_NAME,
Key: objectKey,
Body: fileContent,
};
// Uploading files to the bucket
s3.upload(params, function (err, data) {
if (err) {
throw err;
}
console.log(`File uploaded successfully. ${data.Location}`);
});
return objectKey;
};
function writeCsvToFileAndUpload(filePath, objectKey) {
var data = getCsvData();
var output = stringify(data);
fs.writeFileSync(filePath, output);
// we will add the uploadFile method later
uploadFile(filePath, objectKey);
}
function getCsvData() {
return [
['1', '2', '3', '4'],
['a', 'b', 'c', 'd']
];
}
请求和响应对象需要修改是有原因的。当通过 API Gateway 调用 Lambda 函数时,请求对象由一个 JSON 组成,其中包括请求体、HTTP 方法类型、REST API 资源路径、查询参数、头和请求上下文。下面是展示这一点的一个片段:
{
"body": "{\"jobId\": \"1\"}",
"path": "/path/to/resource",
"httpMethod": "POST",
"isBase64Encoded": false,
"queryStringParameters": {
...
},
"headers": {
...
},
"requestContext": {
...
}
}
JSON 请求负载被字符串化,并在body
参数下设置。要提取 Lambda 中的有效负载,您必须修改代码,如下所示。
const eventBody = JSON.parse(event["body"]);
const jobId = eventBody["jobId"];
此外,由于 Lambda 响应将被 API 网关直接使用,因此您需要将响应格式化为 JSON REST API 响应,其中包括状态代码、状态、消息头和响应体。因此,您将实际的响应字符串化并添加到 JSON 对象的body
参数下,如下所示。
{
"statusCode": 200,
'headers': {'Content-Type': 'application/json'},
"body": JSON.stringify({
"status": "success",
"jobId": jobId,
"objectKey": uploadedObjectKey
})
}
添加 Lambda 授权者
接下来,定义另一个 AWS Lambda 函数作为定制授权器。每当客户端调用 REST API 时,API Gateway 都会调用这个 Lambda 函数,并将Authorization
头值传递给它。Lambda 处理程序将验证在Authorization
头中发送的令牌,如果令牌有足够的权限,将返回一个 IAM 策略语句。如果令牌没有被授权调用 REST API,Lambda 处理程序将返回一个错误响应。
首先,在 CDK 项目的根目录下创建一个lambda/authorizer
目录。在authorizer
目录中添加一个用于定义依赖关系的package.json
文件。在package.json
中,定义项目的名称,并添加一些 Lambda 处理程序将使用的依赖项。
{
"name": "circle-ci-auth-lambda-function",
"version": "0.1.0",
"dependencies": {}
}
接下来,在authorizer
目录中为授权者 Lambda 处理程序创建一个index.js
文件,并向其中添加一个空的 Lambda 处理程序。
exports.handler = function(event, context, callback) {
};
在实现 Lambda 处理程序之前,定义一个生成 IAM 策略语句的方法,将execute-api:Invoke
权限授予调用授权 Lambda 的 REST API。
var generatePolicy = function(principalId, effect, resource) {
var authResponse = {};
authResponse.principalId = principalId;
if (effect && resource) {
var policyDocument = {};
policyDocument.Version = '2012-10-17';
policyDocument.Statement = [];
var statementOne = {};
statementOne.Action = 'execute-api:Invoke';
statementOne.Effect = effect;
statementOne.Resource = resource;
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
}
return authResponse;
}
既然已经定义了generatePolicy
函数,那么就实现 Lambda 处理程序。Lambda 处理程序将从event
参数中提取授权令牌,然后验证该令牌。对于有效的令牌,它将调用generatePolicy
方法来返回适当的 IAM 策略。
exports.handler = function (event, context, callback) {
var token = event.authorizationToken;
switch (token) {
case "allow":
callback(null, generatePolicy("user", "Allow", event.methodArn));
break;
case "deny":
callback(null, generatePolicy("user", "Deny", event.methodArn));
break;
case "unauthorized":
callback("Unauthorized"); // Return a 401 Unauthorized response
break;
default:
callback("Error: Invalid token"); // Return a 500 Invalid token response
}
};
既然已经为处理作业定义了 Lambda,也为授权定义了 Lambda,那么就可以为应用程序定义 CDK 结构了。
为应用程序定义 CDK 结构
AWS CDK 构造封装了多个 AWS 服务的配置细节和粘合逻辑。CDK 提供了大多数主流编程语言的库。
用下面的代码片段替换lib/aws-cdk-api-auth-lambda-circle-ci-stack.ts
文件的内容,该代码片段定义了 AWS S3 桶、AWS Lambda 函数和 AWS DynamoDB 的构造。
import {
Stack,
StackProps,
aws_s3 as s3,
aws_dynamodb as dynamodb,
aws_lambda as lambda,
Duration
} from 'aws-cdk-lib';
import { Construct } from 'constructs';
export class AwsCdkApiAuthLambdaCircleCiStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
// we will add all the constructs here
// replace bucket name with a unique name
const circleCiGwpBucket = new s3.Bucket(this, "CircleCIGwpAuthExampleBucket", {
bucketName: "<YOUR_BUCKET_NAME>",
});
const circleCiGwpTable = new dynamodb.Table(this, "CircleCIGwpAuthExampleTable", {
tableName: "CircleCIGwpAuthExampleTable",
partitionKey: { name: "jobId", type: dynamodb.AttributeType.STRING },
});
const circleCiGwpLambda = new lambda.Function(
this,
"CircleCiGwpProcessJobLambda",
{
runtime: lambda.Runtime.NODEJS_14_X,
handler: "index.handler",
timeout: Duration.seconds(30),
code: lambda.Code.fromAsset("lambda/processJob/"),
environment: {
TABLE_NAME: circleCiGwpTable.tableName,
BUCKET_NAME: circleCiGwpBucket.bucketName
},
}
);
circleCiGwpBucket.grantPut(circleCiGwpLambda);
circleCiGwpTable.grantReadWriteData(circleCiGwpLambda);
}
}
这些构造与本系列第一篇教程中定义的相匹配,自动将 AWS Lambda 函数部署到 AWS CDK ,所以我不会详细介绍这些构造的创建。这些是基本的 CDK 构造,使用在lambda/processJob
目录中定义的 Lambda 处理程序创建一个新的 S3 桶、一个新的 DynamoDB 表和一个 Lambda 函数。定义构造后,向 Lambda 函数授予适当的 IAM 权限。
AWS S3 时段名称在所有 AWS 帐户中都是唯一的,因此您需要为您的时段提供一个唯一的名称。
TABLE_NAME
和BUCKET_NAME
作为environment
变量被传递,它们可以在 AWS Lambda 处理程序中使用。
在定义任何更多的构造之前,您需要在堆栈中定义:
- 定义授权 Lambda 的 CDK 构造。
- API 网关
TokenAuthorizer
构造使用授权 Lambda 作为其处理程序。 - 服务的 API 网关
RestApi
构造。 - API 网关
LambdaIntegration
构造使用流程作业Lambda
作为其处理程序。
使用 Lambda 集成构造将授权处理程序方法添加到 API 令牌授权器资源设置中。
定义授权 Lambda
接下来,添加一个 CDK 构造来为自定义授权创建一个 AWS Lambda 函数。授权 Lambda 将使用 NodeJS 运行时和您在lambda/authorizer
目录中定义的代码。
constructor(scope: Construct, id: string, props?: StackProps) {
// add this snippet below the existing code
const circleCiAuthLambda = new lambda.Function(
this,
"CircleCiAuthLambda",
{
runtime: lambda.Runtime.NODEJS_14_X,
handler: "index.handler",
timeout: Duration.seconds(30),
code: lambda.Code.fromAsset("lambda/authorizer/"),
}
);
}
定义授权令牌
要定义 API 网关令牌授权者,需要为TokenAuthorizer
添加一个 CDK 构造。令牌授权器使用您之前定义的授权 Lambda 函数。
import {
Stack,
StackProps,
aws_s3 as s3,
aws_dynamodb as dynamodb,
aws_lambda as lambda,
//update existing import to add aws_apigateway
aws_apigateway as apigateway,
Duration
} from 'aws-cdk-lib';
constructor(scope: Construct, id: string, props?: StackProps) {
// add this snippet below the existing code
const circleCiAuthorizer = new apigateway.TokenAuthorizer(this, 'CircleCIGWPAuthorizer', {
handler: circleCiAuthLambda
});
}
定义 REST API 服务
接下来,定义一个 API Gateway REST API 服务,并为其提供名称和描述。您将使用这个RestApi
服务向其添加资源。
constructor(scope: Construct, id: string, props?: StackProps) {
// add this snippet below the existing code
const circleCiGwpApi = new apigateway.RestApi(this, "CircleCIGWPAPI", {
restApiName: "Circle CI GWP API",
description: "Sample API for Circle CI GWP"
});
}
您现在可以向circleCiGwpApi
服务添加资源。资源是您正在创建的实际端点,不包括基本 URL。
constructor(scope: Construct, id: string, props?: StackProps) {
// add this snippet below the existing code
const jobResource = circleCiGwpApi.root.addResource("jobs");
}
添加 Lambda 集成
Lambda 集成将 AWS Lambda 函数集成到 API 网关方法中。您将使用前面定义的流程作业 Lambda 函数作为 Lambda 集成的处理程序。
constructor(scope: Construct, id: string, props?: StackProps) {
// add this snippet below the existing code
const processJobIntegration = new apigateway.LambdaIntegration(
circleCiGwpLambda
);
}
添加具有 lambda 授权的 api 网关方法
最后,向jobResource
添加一个POST
方法,并使用授权 Lambda 作为 auth 处理程序。
constructor(scope: Construct, id: string, props?: StackProps) {
// add this snippet below the existing code
jobResource.addMethod("POST", processJobIntegration, {
authorizer: circleCiAuthorizer,
authorizationType: apigateway.AuthorizationType.CUSTOM,
});
}
定制 API 使用计划
在本节中,您将学习如何通过定义使用计划、节流设置和速率限制来定制 REST APIs 的行为和体验。
该计划使用 API 键来标识 API 客户端以及谁可以访问每个键的相关 API 阶段。
- 使用计划:使用计划指定谁可以访问已部署的 API。可以选择在方法级别设置使用计划。API 键与一个使用计划相关联,并用于标识可以访问每个键的 API 的 API 客户端。
- API 密钥:API 密钥是字符串值,可用于授予对 API 的访问权限。
- 节流限制:节流限制确定请求节流应该开始的阈值,它可以在 API 或方法级别设置。
现在您可以为 API 定义一个使用计划了。
constructor(scope: Construct, id: string, props?: StackProps) {
// add this snippet below the existing code
const circleCiUsagePlan = circleCiGwpApi.addUsagePlan('CircleCiUsagePlan', {
name: 'CircleCiEasyPlan',
throttle: {
rateLimit: 100,
burstLimit: 2
}
});
}
请注意,您还将定义带宽限制和使用计划。rateLimit
指的是 API 在一段时间内每秒的平均请求数。burstLimit
指的是从一秒到几秒时间范围内的最大 API 请求速率限制。设置节流限制是可选的,您可以选择不对您的 API 实施任何此类限制。
因为使用计划需要一个与之相关联的 API 密钥来标识客户端,所以需要为其添加一个 API 密钥。
constructor(scope: Construct, id: string, props?: StackProps) {
// add this snippet below the existing code
const circleCiApiKey = circleCiGwpApi.addApiKey('CircleCiApiKey');
circleCiUsagePlan.addApiKey(circleCiApiKey);
}
CircleCiApiKey
与 CircleCI 仪表板没有任何关联。根据您的需求,您可以为 API 键使用任何其他名称。
部署 CDK 堆栈
既然已经在堆栈中定义了 CDK 结构,那么就可以继续将应用程序部署到 AWS 帐户了。首先,在使用 CircleCI 自动化部署之前,手动部署应用程序。确保您的系统上安装了 AWS CDK CLI。除此之外,您还需要安装 AWS CLI 并配置访问凭据。您可以按照先决条件部分中的链接安装 CLI 并配置凭据。
运行以下命令来引导并部署应用程序。
cdk bootstrap
cdk deploy
当您执行cdk deploy
命令时,它会提示您确认将应用于您的帐户的 IAM 角色/策略更改。注意,终端将显示您部署的 REST API 服务的基本 URL。获取这个 URL 并保存在手边,因为您将在下一节中使用它来测试 API。
测试部署的 API
现在应用程序已经部署到 AWS 帐户,通过使用curl
调用 API 端点来测试 API。在curl
请求中替换您在上一节中获得的基本 URL。
curl -X POST \
'<base_URL>/jobs' \
--header 'Accept: */*' \
--header 'Authorization: allow' \
--header 'Content-Type: application/json' \
--data-raw '{
"jobId": "1"
}'
注意,您已经设置了Authorization: allow
头,它充当将由授权 Lambda 验证的令牌。
在执行curl
请求时,您应该会收到一个类似如下所示的成功响应。
不要在Authorization
头中传递allow
值,而是尝试传递deny
或其他值,以确保 API 只有在收到有效令牌时才返回成功响应。
curl -X POST \
'<baseURL>/jobs' \
--header 'Accept: */*' \
--header 'Authorization: deny' \
--header 'Content-Type: application/json' \
--data-raw '{
"jobId": "1"
}'
正如所料,API 返回一个授权响应,因为令牌无效。
使用 CircleCI 自动化应用程序部署
既然您已经能够使用命令行手动部署 CDK 应用程序,那么请自动化工作流,以便在您每次将代码推送到主分支时,可以自动打包和部署基础架构更改。要实现部署自动化,您需要:
- 更新。gitignore
- 更新 NPM 脚本
- 添加配置脚本
- 创建 CircleCI 项目
- 设置环境变量
更新。gitignore
由cdk init
命令生成的代码包含一个默认忽略所有.js
文件的.gitignore
文件。确保用下面的代码片段替换.gitignore
的内容。
!jest.config.js
*.d.ts
node_modules
# CDK asset staging directory
.cdk.staging
cdk.out
更新 NPM 脚本
我们的 CircleCI 部署配置使用 NPM 脚本来执行 deploy 和 diff 命令。将以下脚本添加到根级package.json
文件中。
// update the aws-cdk-api-auth-lambda-circle-ci/package.json file with the following scripts
{
...
"scripts": {
...
// add the ci_diff and ci_deploy scripts
"ci_diff": "cdk diff -c env=${ENV:-stg} 2>&1 | sed -r 's/\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g' || true",
"ci_deploy": "cdk deploy -c env=${ENV:-stg} --require-approval never"
},
...
}
添加配置脚本
首先,在包含 CI 管道配置文件的项目根目录中添加一个.circleci/config.yml
脚本。将下面的代码片段添加到config.yml
中。
version: 2.1
orbs:
aws-cli: circleci/aws-cli@2.0.6
executors:
default:
docker:
- image: "cimg/node:14.18.2"
environment:
AWS_REGION: "us-west-2"
jobs:
build:
executor: "default"
steps:
- aws-cli/setup:
aws-access-key-id: AWS_ACCESS_KEY
aws-secret-access-key: AWS_ACCESS_SECRET
aws-region: AWS_REGION_NAME
- checkout
- run:
name: 'Install Authorizer lambda packages'
command: |
cd lambda/processJob && npm install
- run:
name: 'Install Process Job lambda packages'
command: |
cd lambda/processJob && npm install
- run:
name: "build"
command: |
npm install
npm run build
- run:
name: "cdk_diff"
command: |
if [ -n "$CIRCLE_PULL_REQUEST" ]; then
export ENV=stg
if [ "${CIRCLE_BRANCH}" == "develop" ]; then
export ENV=prd
fi
pr_number=${CIRCLE_PULL_REQUEST##*/}
block='```'
diff=$(echo -e "cdk diff (env=${ENV})\n${block}\n$(npm run --silent ci_diff)\n${block}")
data=$(jq -n --arg body "$diff" '{ body: $body }') # escape
curl -X POST -H 'Content-Type:application/json' \
-H 'Accept: application/vnd.github.v3+json' \
-H "Authorization: token ${GITHUB_TOKEN}" \
-d "$data" \
"https://api.github.com/repos/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/issues/${pr_number}/comments"
fi
- run:
name: "cdk_deploy"
command: |
if [ "${CIRCLE_BRANCH}" == "main" ]; then
ENV=prd npm run ci_deploy
elif [ "${CIRCLE_BRANCH}" == "develop" ]; then
ENV=stg npm run ci_deploy
fi
CI 脚本使用 aws-cli orb 来设置 aws 配置,例如访问密钥和密码。
cdk_deploy
命令检查分支,并相应地部署在prd
或stg
环境中。注意,cdk_deploy
命令执行在package.json
文件中定义的ci_deploy
脚本。
我们的管道配置将负责构建、打包和部署 CDK 堆栈到指定的 AWS 帐户。提交更改并将其推送到 GitHub 存储库。
为应用程序创建一个 CircleCI 项目
接下来,使用 CircleCI 控制台将存储库设置为 CircleCI 项目。在 Circle CI 控制台上,单击项目选项卡并搜索 GitHub repo 名称。为您的项目点击设置项目按钮。
将出现一个对话框提示,询问您是要手动添加新的配置文件还是使用现有的配置文件。因为您已经将所需的配置文件推送到代码库,所以选择最快的选项,并输入承载您的配置文件的分支的名称。单击“设置项目”继续。
完成设置将自动触发管道。管道在第一次运行时会失败,因为您还没有定义环境变量。
设置环境变量
在项目页面,点击项目设置,进入环境变量选项卡。在出现的屏幕上,点击添加环境变量按钮,添加以下环境变量。
AWS_ACCESS_KEY
从 AWS 控制台的 IAM 角色页面获得AWS_ACCESS_SECRET
从 AWS 控制台的 IAM 角色页面获得AWS_REGION_NAME
到您想要部署应用程序的区域
一旦您添加了环境变量,它应该在仪表板上显示键值。
现在环境变量已经配置好了,再次触发管道。这一次构建应该会成功。
结论
在本教程中,您了解了如何使用 AWS CDK 构造轻松部署应用程序,并使用 REST APIs 公开其功能。CDK 可用于轻松插入基于 Lambda 的定制授权器,并通过定义使用计划、API 密钥和节流限制来进一步定制应用体验。AWS CDK 允许您使用熟悉的工具和编程语言编写 IaC 代码。它还允许您编写可测试的代码,并将基础设施代码与您现有的代码评审工作流集成在一起。
你可以在 GitHub 上查看本教程中使用的完整的源代码。如果你想定义一个类似的堆栈,GitHub 项目也可以作为你的模板。
Vivek Kumar Maskara 是 JP 摩根的一名软件工程师。他喜欢写代码,开发应用程序,创建网站,并写关于他的经历的技术博客。他的简介和联系方式可以在maskaravivek.com找到。
正在为工作流重新构建 Litho 的 CircleCI 配置- CircleCI
原文:https://circleci.com/blog/restructuring-litho-s-circleci-config-for-workflows/
这是 Pavlos-Petros Tournaris 的客座博文。它最初出现在他的博客这里。Pavlos-Petros 在 Workable 担任 Android 软件工程师。我们希望你喜欢!
大约一年半前,脸书发布了作为开源项目的 Litho 。Litho 是一个 Android 框架,它让你以声明的方式定义你的 UI。它立即引起了我的兴趣,我开始用一些例子和宠物项目来弄脏我的手。这是一个很好的经验,接触石和它的反应一样的性质,这是我的第一次。对这个新领域的兴趣让我意识到我可以更深入地研究它,也可以为这个项目做贡献。因此,就像我喜欢的任何其他开源项目一样,我开始检查“问题”选项卡,看看我是否能帮助解决任何错误或贡献新功能。
自动化测试
Litho,就像大多数开源项目一样,包含许多测试(没有测试通常很难获得开发人员的信任)。Litho 上的测试基础设施是基于单元测试的,这些单元测试要么通过 Buck 运行,要么使用 Gradle。
但是如果你不能对你的 PRs 有适当的反馈,并且确保进入master
分支的每个提交都是绿色的,那么测试本身没有任何意义。
Litho 使用 CircleCI 来利用其单元测试套件的运行,收集测试结果,并为每个推送到master
的提交发布快照。
以前的情况
此前,Litho 使用的是由 Litho 的机器人工程师之一 Pascal Hartig 制作的定制 Docker 图像。
这个映像正在下载 Buck,构建它并保存在 Docker 环境中。同样的事情也分别发生在安卓 NDK 和安卓 SDK 上。当 CircleCI 开始构建 commit 时,它会配置一些所需的键,用于上传档案和将 Buck 导出到 Path 中。最后,它开始执行样本项目的所有 Buck & Gradle 构建,以及执行 BUCK & Gradle 测试,这通过发布快照和存储测试结果来完成。
虽然这样做效果很好,但是仍然需要为 Buck 和 Gradle 测试以及样本构建配置并行作业。
以前情况的问题
在开始调查哪些地方可以改进的时候,我注意到了一些可以提高性能的东西,还有一些是在与 Pascal 讨论之后出现的。
- CircleCI 配置使用自定义的 Docker 映像,由于 CircleCI 在其容器上缓存 Docker 映像的方式,每次作业运行时都会有效地提取该映像,运行一个环境大约需要 5-6 分钟。
-
构建和测试不是并行运行的,这意味着即使 Buck 构建需要两分钟来执行测试,它也必须等待 Gradle 完成测试后才能继续。
-
用于收集测试结果的正则表达式没有收集 Buck 测试结果。
工作流来了
CircleCI 提供了一个名为 Workflows 的特性,它允许我们定义并行运行的作业列表,但是您也可以声明作业之间的依赖关系,这将有效地将它们的执行更改为顺序执行。
- checkout_code
- build:
requires:
- checkout_code
- buck_sample_build:
requires:
- build
- buck_sample_barebones_build:
requires:
- build
- buck_sample_codelab_build:
requires:
- build
- gradle_sample_kotlin_build:
requires:
- build
- buck_litho_it_tests_run:
requires:
- build
- buck_litho_it_powermock_tests_run:
requires:
- build
- gradle_tests_run:
requires:
- build
将平版印刷转变为使用工作流程
第一步是创建一个检查回购代码的工作,并开始设置所需的依赖项,包括:Buck,将帮助我们构建 Buck 的依赖项,如 Ant 和 Android NDK。这项工作被定义为 Lithobuild_and_test
工作流程的第一步。
依赖于该作业的是build
作业,它负责构建 Litho 并保存任何 Gradle 产生的缓存。
之后,build 将分发给其他七个负责构建样本和执行测试的任务。这些作业中的每一个还被配置为存储测试结果并上传任何产生的工件。
扇出
这个工作流由一个publish_snapshot
作业完成,这个作业依赖于每一个执行测试的作业。在它们成功执行后,Gradle 任务负责将包含最新更改的档案作为快照上传到 Bintray 上。
- publish_snapshot:
requires:
- buck_litho_it_tests_run
- buck_litho_it_powermock_tests_run
- gradle_tests_run
缓存和工作空间
CircleCI 的配置 DSL,其中包括cache
& workspace
。为了更好地解释这些术语,让我将它们定义如下:
cache
是将在工作流执行之间保留的已保存内容。
缓存在 CircleCI
- &save-repo-cache
paths:
- ~/.gradle/caches
- ~/.gradle/wrapper
workspace
是保存的内容,将在同一工作流程的作业执行中保留。
用工作区保存内容
- persist_to_workspace:
root: workspace
paths:
- repo
例如,只要有适当的缓存破坏机制,任何梯度缓存的依赖项都是值得缓存的。然而,我们的回购代码是所有作业都需要的,因此值得将它保存到我们的工作区,以便每个作业都能够“附加”并访问该内容。
attach_workspace: &attach_workspace
attach_workspace:
at: ~/litho-working-dir/workspace
作业配置
作为工作流一部分的作业也可以为分支定义filters
。这尤其有用,因为我们不想执行publish_snapshot
作业,以防我们运行的提交不在master
分支上,而是在 PR 上。这可能看起来是一个小变化,但它可以节省 7-8 分钟等待工作流执行完成的时间。
filters:
branches:
only: master
重组后的改进
在大量的提交、工作流执行和与 Pascal 讨论可改进点之后,我们最终设法在大约 15-20 分钟内运行了一个工作流。以下是我们改进的几点:
- 我们通过使用 CircleCI 的 Android 映像并在启动时提取任何所需的依赖项,删除了自定义 Docker 映像。为我们提供了 95%(甚至更多)的作业执行的 1 秒容器配置。
- 我们用了 CircleCI 的 Android Docker 镜像 Android SDK,而不是从头开始下载。
- 我们让测试和构建工作并行运行,例如,在某个“示例”模块出现问题时,允许早期反馈。与 Github 的集成相结合,这真的提供了很多信息,因为如果负责对您的更改执行测试的工作失败了或没有失败,您可以立即得到通知,而不必等待整个工作流完成。
- 我们从 Buck 测试执行中收集了以前未收集的测试结果。
结论
这是一次有趣的经历,也是一次大开眼界的经历。我以前从未在这种程度上处理过 CI 工具,我完全可以说开发运维或 CI 配置肯定是一项困难的工作(尊重我们每天都要处理这些工作的同事)。
此外,感谢 Pascal Hartig 对流程的帮助以及对改进点的讨论。
你可以在这里找到重构 config.yml 的 commit:https://github . com/Facebook/litho/commit/2c 411830d 11d 08 fc 2 af 194 D8 ea 244d 0 cbdf 33 f 76
而 Litho 的最终 config.yml 在这里:https://github.com/facebook/litho/blob/master/.circleci/config.yml
使用 Pulumi | CircleCI 从您的 CI 渠道构建云基础架构
原文:https://circleci.com/blog/reusable-ci-cd-components-with-circleci-orbs-and-pulumi/
现代软件系统非常复杂,服务分布在世界各地的许多区域的数据中心。我们管理专用于我们组织的单个服务器的日子已经一去不复返了,因为我们对自己设置的独特之处了如指掌。
现在,我们依靠其他人来管理大规模的数据中心,我们借用共享硬件上的小块虚拟空间,通过共享网络传输,所有这些都在我们称为云的系统中。管理与云的交互是云工程的一部分。
为了干净地交付应用程序,您需要像管理连续交付一样管理管道基础设施。随着云工程的成熟,您可以将应用交付的实践作为代码引入基础设施。
使用 Pulumi 和 CircleCI 开始云工程
云工程使云更接近应用程序开发,将工程实践和原则应用于基础设施,并在整个团队中更快地进行创新和协作。Pulumi 是一个基础设施即代码平台,您可以用它来帮助在您的组织中创建云工程文化。这种文化包括持续集成、持续部署和交付,以及测试驱动的开发。
对于本教程,我们已经创建了一个构建在 Falcon 之上的小示例应用程序,这是一个用于 Python 的 RESTful API 框架。你可以在 https://github.com/nimbinatus/circleci-pulumi-sample 的找到源代码。
你可以用 Pulumi 使用很多语言,比如 Python 或者 Go。对于本教程,我们将使用 Pulumi 的 TypeScript SDK 来部署它。
先决条件
如果你愿意,克隆回购,或者复制代码。我们明确地不需要特定的版本控制系统,因为带有 CircleCI 的 Pulumi 可以使用它们中的任何一个。
让我们从一个工作流声明开始,它将我们的回购部署到谷歌云平台(GCP)的容器注册表,然后部署到云运行。
使用声明性工作流的参数构建基础结构
我们可以参数化 orb,这样我们就可以设置要下载的 Pulumi CLI 的版本,选择更新是否应该跳过预览,等等。对于其他 CI/CD 系统,您通常需要编写一次性的 Bash 脚本来从https://get.pulumi.com
下载客户机,将其添加到当前的$PATH
中,等等。orb 允许更简单、更具组合性和声明性的 CI/CD 工作流。
对于我们的例子,我们可以清楚地声明我们的初始工作流:
version: 2.1
orbs:
pulumi: pulumi/pulumi@2.1.0
gcp-cli: circleci/gcp-cli@2.4.0
gcp-gcr: circleci/gcp-gcr@0.14.1
node: circleci/node@5.0.0
jobs:
build:
docker:
- image: cimg/node:17.5.0
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: false
- pulumi/login:
access-token: ${PULUMI_ACCESS_TOKEN}
- node/install-packages:
app-dir: ~/project/infra-ts/image
- gcp-cli/install
- gcp-cli/initialize
- gcp-gcr/gcr-auth:
registry-url: us-central1-docker.pkg.dev
- pulumi/update:
stack: ${CIRCLE_PROJECT_USERNAME}/docker-image-circleci/dev-personal
working_directory: ~/project/infra-ts/image
deploy:
docker:
- image: cimg/node:17.5.0
steps:
- checkout
- pulumi/login:
access-token: ${PULUMI_ACCESS_TOKEN}
- node/install-packages:
app-dir: ~/project/infra-ts/cloud-run
- gcp-cli/install
- gcp-cli/initialize
- gcp-gcr/gcr-auth:
registry-url: us-central1-docker.pkg.dev
- pulumi/update:
stack: ${CIRCLE_PROJECT_USERNAME}/circleci-pulumi/dev-personal
working_directory: ~/project/infra-ts/cloud-run
workflows:
version: 2.1
pulumi-deploy:
jobs:
- build:
filters:
branches:
only:
- main
- deploy:
requires:
- build
filters:
branches:
only:
- main
注意,在整个工作流程中,我们使用带有环境变量的标准 orb 调用。没有一次性脚本。不乱。漂亮、干净、易于阅读、重用和共享。
烟雾测试变得简单
有了 Pulumi 和 CircleCI,您可以构建短暂的环境来进行任何您想要的测试,所有这些都是您的管道的一部分。我们可以使用参数来创建独特的环境,并将测试结果发送回一个拉取请求或其他地方。例如,我们可以使用这样的代码来测试对 GitHub repo 的 pull 请求,以确保我们的端点按照预期做出响应,然后在测试通过后完全拆除测试环境。将这段代码添加到您的.config.yaml
文件中:
# …
jobs:
preview:
docker:
- image: cimg/node:17.5.0
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: false
- pulumi/login:
access-token: ${PULUMI_ACCESS_TOKEN}
- node/install-packages:
app-dir: ~/project/infra-ts/cloud-run
- node/install-packages:
app-dir: ~/project/infra-ts/image
- gcp-cli/install
- gcp-cli/initialize
- gcp-gcr/gcr-auth:
registry-url: us-central1-docker.pkg.dev
- pulumi/stack_init:
stack: ${CIRCLE_PROJECT_USERNAME}/docker-image-circleci/qa_${CIRCLE_BUILD_NUM}-image
working_directory: ~/project/infra-ts/image
copy: ${CIRCLE_PROJECT_USERNAME}/docker-image-circleci/dev-personal
- pulumi/stack_init:
stack: ${CIRCLE_PROJECT_USERNAME}/circleci-pulumi/qa_${CIRCLE_BUILD_NUM}-cr
working_directory: ~/project/infra-ts/cloud-run
copy: ${CIRCLE_PROJECT_USERNAME}/circleci-pulumi/dev-personal
- pulumi/preview:
stack: ${CIRCLE_PROJECT_USERNAME}/docker-image-circleci/qa_${CIRCLE_BUILD_NUM}-image
working_directory: ~/project/infra-ts/image
- pulumi/preview:
stack: ${CIRCLE_PROJECT_USERNAME}/circleci-pulumi/qa_${CIRCLE_BUILD_NUM}-cr
working_directory: ~/project/infra-ts/cloud-run
- pulumi/update:
stack: ${CIRCLE_PROJECT_USERNAME}/docker-image-circleci/qa_${CIRCLE_BUILD_NUM}-image
working_directory: ~/project/infra-ts/image
skip-preview: true
- pulumi/update:
stack: ${CIRCLE_PROJECT_USERNAME}/circleci-pulumi/qa_${CIRCLE_BUILD_NUM}-cr
working_directory: ~/project/infra-ts/cloud-run
skip-preview: true
- pulumi/stack_output:
stack: ${CIRCLE_PROJECT_USERNAME}/circleci-pulumi/qa_${CIRCLE_BUILD_NUM}-cr
property_name: cloudRunUrl
env_var: TEST_URL
- run:
name: Test URL
command: |
curl -sSJL $TEST_URL/hello
- pulumi/destroy:
stack: ${CIRCLE_PROJECT_USERNAME}/docker-image-circleci/qa_${CIRCLE_BUILD_NUM}-image
working_directory: ~/project/infra-ts/image
- pulumi/destroy:
stack: ${CIRCLE_PROJECT_USERNAME}/circleci-pulumi/qa_${CIRCLE_BUILD_NUM}-cr
working_directory: ~/project/infra-ts/cloud-run
- pulumi/stack_rm:
stack: ${CIRCLE_PROJECT_USERNAME}/docker-image-circleci/qa_${CIRCLE_BUILD_NUM}-image
- pulumi/stack_rm:
stack: ${CIRCLE_PROJECT_USERNAME}/circleci-pulumi/qa_${CIRCLE_BUILD_NUM}-cr
# …
workflows:
version: 2.1
pulumi-preview:
jobs:
- preview:
filters:
branches:
ignore:
- main
# …
在这个工作流程中,我们为每组资源构建一个新的堆栈,运行一些测试,然后在一个管道中分解所有的东西。非常方便!
与现有系统集成
Pulumi 与 CircleCI 的集成并不以一个基本的工作流结束。Pulumi 命令行客户端将 CircleCI 构建元数据与堆栈更新相关联,因此您可以将来自 Pulumi 服务的链接添加到 CircleCI 工作流期间发生的任何堆栈更新或预览中。
此外,由于 CircleCI orb 显示为 CI 系统,用于来自 CircleCI 工作流的任何更改,因此您可以审核更改来自何处。
您的 Pulumi 输出也显示在 CircleCI 应用程序的 CircleCI 构建信息中。
此外,如果您将 CircleCI 与 GitHub 一起使用,那么将 Pulumi 与 CircleCI 连接起来将会在源 GitHub pull request (PR)上显示来自 CI/CD 的任何预览或更新的结果。了解拉取请求是否会导致您的云基础架构发生变化总是有好处的!
如果你在 GitHub 上,去 PR 上找到预览工作流程的视图。
Details 链接将您带到 CircleCI 上的构建,这样您就可以找到该构建的所有信息。如果你想让 Pulumi 在任何 PR 上添加一个关于基础设施变更的评论,你也可以使用 Pulumi GitHub 集成来展示任何和所有的变更。
如果您正在检查最近的提交,当您选择标准勾号时,来自 CircleCI 的检查会出现,包括您设定在该分支上运行的所有工作流。
结论
我们一直致力于使 Pulumi 成为 CI/CD 工作流中“持续部署”部分的最佳工具,有了 CircleCI 的 orbs,事情就简单多了。通过创建免费账户和建立 CircleCI 工作流程来试试吧,让我们知道你的想法!
如何查看您的 CircleCI 配置
原文:https://circleci.com/blog/review-your-circleci-configuration/
配置文件可能需要一些时间来设置,但是在初始推送之后,它们很容易被遗忘。“如果它没有坏,就不要修复它”是许多开发人员对他们的配置文件采取的一种常见方法。但是当涉及到您的持续集成管道时,小的改变可以带来巨大的好处。在 CircleCI,我们推出新功能和更新的速度比以往任何时候都快,定期检查和更新您的配置可以显著缩短工作流程持续时间。
为了帮助投资了我们的支持计划的客户充分利用 CircleCI,我们的 DevOps 客户工程(DCE)团队定期提供配置文件审查。DCE 团队审查了数百个配置文件,并创建了一个可用于任何项目的流程。在本文中,我们将介绍 DCE 的审核流程,以便您能够进行自己的配置审核。你可以成为减少管道反馈回路的人。想象一下,你的团队能够在倒满咖啡的时间内,而不是在吃午饭的时间内,推送代码和接收反馈。
什么是配置审查?
配置审查不仅仅是查看 YAML 文件。它应该被视为一个关键过程,被计划出来,并记录在案。我们建议选择一个项目并至少每年进行一次评审,如果不是每六个月一次的话,然后将结果记录在团队其他成员可以访问的文档中。记录基线度量、评审期间做了什么,以及结果可以说与评审本身一样有价值。有了这些信息,您的团队可以建立一个特定于您的用例的 CI 知识库,并可以用于未来的评审或新项目。
配置审查的主要目标是分析您项目的配置文件,以确保与您的度量目标相比,管道以最佳方式运行。我们的 Insights 仪表盘是在此过程中跟踪指标的绝佳方式。如果您的团队使用另一个度量应用程序,您可以使用我们的 Insights API 将数据拉入其中。为您的团队执行配置审查也是了解 CircleCI 如何工作的一个好方法。我们建议利用这段时间记录特性如何与您的用例一起工作,以及它们是如何在您的工作流中实现的。
准备配置审查
在开始审查您的配置之前,有几个重要的步骤将有助于确保您的审查是有效的。在准备阶段,您需要选择一个要评审的项目,确定您的评审目标,并建立一些基准指标来衡量您向目标迈进的进度。
选择要审阅的项目和工作流
大多数团队都有不止一个项目,审查多个项目配置可能会让人不知所措。当 DCE 与客户一起审查他们的配置文件时,我们一起决定要关注的特定项目。选择项目的三大原因是:
- 高信用消费
- 长工作流程持续时间
- 过于复杂的配置
选择一个项目还有很多其他原因。也许这是对其他项目的概念验证,一个已经有一段时间没有接触过的项目,或者一个有着令人担忧的度量标准的项目。
决定目标
选择一个项目后,您需要为评估确定一个目标。您是否希望通过缩短工作流程来提高开发人员的工作效率?信用消费需要优化吗?自从上次检查配置以来,您可能正在考虑实现新的 CircleCI 特性。
无论您的团队选择什么目标,都要写下来,以便将来进行评审的任何人都可以理解评审使用了哪种方法。
利用洞察力收集基线指标
既然你已经设定了目标,你如何知道你的评估过程是否成功呢?
CircleCI 是为数不多的能够洞察团队指标的 CI 工具之一。使用 Insights 工具,您需要收集项目和/或工作流的四个关键指标:
- 持续时间
- 吞吐量
- 接通率
- 平均恢复时间
如果您想了解更多关于我们为什么认为这是四个关键指标的信息,我们建议通读我们的软件交付状态报告(2022) ,在那里我们将对它们进行更详细的分析。
这些指标可能并不是跟踪你的目标成功所必需的,但是记下它们来设定一个基线是有帮助的。在实现了对配置文件的更改之后,我们将获取额外的指标快照,并将它们与这个快照进行比较,以查看更改是如何实现的。
这也是确定哪些指标对这个项目很重要的好时机。每个项目都是不同的,所以用来决定每个项目成功的标准也是不同的。如果你正在优化信用消费,那么持续时间是一个很好的衡量标准,因为它会直接影响信用。如果您的目标是整体效率,那么除了持续时间之外,您还需要关注使用 Docker 执行器的作业的资源使用情况。
除了这四个关键指标之外,可能还有其他对单个工作流很重要的指标。其中许多,像古怪的测试和信用使用,都包含在 Insights 中。如果洞察工具中添加了您感兴趣的指标,请告诉我们。
查看您的 CircleCI 配置文件
一旦您确定了您的目标并记录了您的基线指标,您就准备好开始评审的实质内容了。如果您想通过回顾来了解更多关于您的 pipelines 和/或 CircleCI 的信息,建议您通读下一部分,以便更熟悉您团队的用例。如果您对团队的管道工作方式感到满意,可以直接跳到配置文件。
了解您的工作流程
如果您不熟悉 CircleCI 和/或您团队的工作流程,我们建议您从 Insights 仪表盘开始查看项目。在审查配置时,对工作流中的预期内容及其结果有一个大致的了解是很有帮助的。使用 Insights 工具,您可以查看项目中特定工作流和作业的指标。Insights dashboard 最多显示最近 100 次工作流运行,您可以调查任何异常运行。还建议单击最近 10 次左右的工作流运行,以确保作业以一致的方式运行。回顾每项工作中的步骤、它们的输出以及它们各自的度量标准也有助于确定任何不一致之处。
深入配置文件
在 UI 中打开配置文件将使您能够访问 CircleCI 自动提供的任何通知横幅或建议。不要觉得你需要先解决这些问题。请记下它们,以便稍后我们深入查看配置文件时使用。在整个评估过程中,记录与您的目标相关的变化以及出现的任何问题及其答案。这也是记录任何可以解决的小问题的好时机,比如过时的注释或不一致的命名约定。
在这一点上,如果您觉得更舒服,可以随意切换到您最喜欢的代码编辑器。你花时间设置这些颜色是有原因的。在回顾过程中,我们将在工作流/作业用户界面和配置文件之间切换,以便同时打开两者。如果您还没有,请不要忘记在 UI 中至少打开一次配置文件,以查看任何重要的通知。
配置文件布局
CircleCI 的配置文件不要求配置文件版本、设置、orb、作业或工作流的特定顺序。如何组织他们取决于你的团队。然而,以下形式对许多团队来说是成功的:
- 配置文件版本(当前为 2.1)
- 设置,如果您的团队使用动态配置
- 定制逻辑,例如:
- 球
- 命令
- 因素
- YAML 锚
- 乔布斯
- 工作流程
- 提交触发的工作流
- 工作流设置为通过预定管道触发
定制的逻辑、执行器和球体
配置文件版本和使用 setup boolean 非常简单,所以让我们回顾一下 CircleCI 提供的各种定制选项,从定制逻辑开始。
定制逻辑
定制逻辑包括命令,参数,执行器,YAML 锚点。您很可能需要保留您当前使用的所有参数,但是最好回顾一下它们,看看另一个特性,比如 contexts ,是否会更合适。命令、执行器和 YAML 锚我们可以做得更多。当您需要时,在配置文件中编写快速定制确实很容易,但是在您添加了一些之后,它会开始降低配置文件的可读性。这就是球体可以帮忙的地方。
球
如果你的团队还没有使用球体,它们会非常方便。本质上,它们是 YAML 逻辑的包,可以用命令访问,而不用在配置文件中写出完整的逻辑。已经有很多经过认证的第三方 orb 可用,搜索一下 orb 注册表看看哪些对你的技术有帮助是个好主意。
如果你的团队不愿意使用公共球,CircleCI 还提供了私有球来限制一个球只能被你的组织使用。orb 对于从配置文件中提取冗长的代码、多次使用的代码或者在多个项目中使用的代码非常有用。当您浏览配置文件时,考虑您可以向 orb 传输什么。
如果您的团队已经在使用 orb,您将需要检查您正在使用的 orb 是否有任何更新的版本。在 orb 注册表中的 orb 页面上可以找到其他可用的命令、作业或其他功能。
实施者
CircleCI 提供了各种各样的执行环境选项供您选择,因此查看当前正在使用的选项是实现您的查看目标的一个很好的方式。
作为第一步,检查执行环境是否有更新是一个好主意。假设你的团队正在使用一个便利 Docker 镜像,去镜像库看看有什么更新。如果你的团队没有使用这些图片,为什么不检查一下呢?它们被很好地缓存,这减少了启动时间。如果您正在使用另一个执行环境,您可以在配置参考文档中找到可用选项的列表,并检查是否有任何更新。
除了检查应该使用哪个执行器之外,您还可以确认当前使用的资源类是否适合这项工作。当使用 Docker executor 时,CircleCI 提供了在作业生命周期中监控资源消耗的能力,这对于这一阶段的审查非常方便。您还可以在 Insights 的“任务”选项卡上查看多次运行的任务资源使用历史记录。我们希望在将来为其他执行环境添加相同的特性,但是现在,如果您使用的是除 Docker 之外的 executor,您将需要自己在作业中添加资源日志。
默认情况下,新作业将使用更大的资源类,因此对于像林挺或 API 调用这样的简单作业,可以考虑降低资源类的大小。我们建议总是在配置文件中设置您自己的资源类,而不是使用默认的。
工作和功能
每个作业都是独特的,因此很难提供关于如何在作业级别进行优化的一揽子建议。好消息是 CircleCI 提供了相当多的关于工作中发生的事情的可见性。您想要首先查看哪个作业取决于您,但是最终,您会想要查看 UI 中的每个作业,以确认它是否按预期工作,并查看是否可以根据您的配置审查目标来优化它。
首先在 CircleCI 中打开作业,并逐一检查每个步骤。检查作业的多次运行以确认某项内容是否为异常值会很有帮助。使用这种方法可能会遇到的一些常见问题有:
如果缩短持续时间是您的目标之一,那么考虑引入或增加作业的并行性是一个好主意。与测试分割一起使用, parallelism 可以极大地减少持续时间,这取决于设置测试需要多长时间。如果您的目标是信用优化,考虑是否可以减少使用的容器数量。
当您浏览您的作业时,您可能会发现相同的测试正在不同的执行环境中运行。为了帮助提高配置可读性,考虑是否可以利用矩阵作业。你可能还会发现,逻辑在不同的工作中不断重复。这是 orbs 可以提供帮助的另一个地方,通过提取逻辑,使它不仅可用于这个项目,而且可用于您的整个组织。
根据您最后一次进行配置审查的时间,可能已经发布了一些新的特性,您的团队可以利用这些特性。我们的公开路线图和我们的变更日志是很好的资源,可以看看最近都有什么。虽然 CircleCI 上的所有特性在配置文件中都是可用的,但另一个资源是配置文件参考,在那里你可以读到任何你不熟悉的特性。
工作流程
与工作类似,工作流往往是独特的。虽然有像构建-测试-部署这样的标准工作流,但是每个组织都有自己的设置方法。与 jobs 一样,首先在 CircleCI 中打开工作流。
当您查看工作流程时,需要记住一些一般提示,其中最主要的是工作顺序是否符合您当前的需求。也许你可以通过加速失败或者将较短的测试放在较长的测试之前来给你的开发人员提供反馈。当您的某些作业可以并行运行时,它们可能会按顺序运行。为你的团队创造合适的需求可能需要一些实验,这没关系。Insights dashboard 提供了许多功能和指标,可以帮助您决定哪个工作流设置适合您。
最后,如果你正在使用任何过滤或逻辑语句,你将想要确认它们是否按预期工作。如果您不使用这些功能,请考虑将它们添加到您的工作流中,以帮助确保作业仅在需要时运行,例如仅在默认分支上运行的部署作业。看看他们,看看他们是否适合你的团队。
事后审查
既然审查已经完成,还需要采取一些步骤,但最困难的部分已经完成。干得好!
适时给予的应得的赞扬
当您在进行配置评审时,您团队中的某个人可能已经实现了一些节省每个人时间的东西。也许在另一个项目中有一些你没有意识到的有用的逻辑。如果你确实发现了一些很酷的东西,那就把它归功于实现它的人。每个人的工作都应该得到认可,包括你做的评估。
实施变更
实施变革时,确定预期的结果很重要。如果您的团队决定增加某项工作的并行性,单独测试这一变化可能是个好主意,这样度量就不会被其他变化所掩盖。如果您发现有多个变更具有较小的预期结果,请在测试时随意将它们组合在一起。为这些变更创建一个测试分支将允许您快速确定一个变更将如何在短期内影响您的度量,并允许您测试各种程度的变更,例如不同的并行级别。
比较指标
无论您选择哪种方式来实施变更,在变更之前和之后定期使用洞察力来进行度量都是一个好主意。一旦进行了更改,建议以逐渐增加的时间间隔拍摄项目度量的快照。这不仅会证实或否定预期的结果,而且还允许您监控任何指标的波动。
仍然不相信 Insights 如何帮助您检查和优化您的配置文件?查看我们的 Insights 团队如何使用 Insights 来执行他们自己的配置审查。
结论
在整个过程中,您已经学习了如何使用 Insights 来为项目设定目标,审查 CircleCI 配置文件,并实施变更以达到那些指标目标。我们发现 CircleCI 提供了您需要的一切,从见解到 UI 横幅建议,帮助您完成配置审查之旅。我希望这里已经为您展示了深入研究您团队的配置文件的价值。
如果你做到了这一步,恭喜你!你可能已经注意到这个过程可能相当复杂,并对自己说,“我没有时间做这个,但我看到了好处。”好消息是,我们之前谈到的那些 DevOps 客户工程师可以为您的团队进行配置审查。我们的 DCE 已经完成了大量配置审查,并针对各种使用情形汇编了最佳实践数据。除了配置审查,我们还提供许多增值服务供您的团队利用,包括帮助项目迁移到 CircleCI 和功能培训。如果您的团队希望与我们的 DCE 会面进行配置审查,请随时联系我们的销售团队来升级您的支持计划。
在 24 小时内用 Clojure 重写你的测试套件
原文:https://circleci.com/blog/rewriting-your-test-suite-in-clojure-in-24-hours/
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
这是一个关于我如何构建一个编译器,在大约 24 小时内自动将 CircleCI 的14000 行测试套件翻译成另一个测试库的故事。
CircleCI 的测试套件可能是当今 Clojure 世界中最大的测试套件之一。我们的服务器端代码是 100% Clojure,包括测试套件,目前是 14000 行,在 140 个文件中,有 5000 个断言。没有并行化,运行需要 40 分钟。
在这次冒险的开始,所有这些测试都是在 Midje 中编写的,这是一个 BDD 测试库,有点类似于 RSpec。我们对 Midje 不是很满意,决定转移到 clojure.test
,这可能是最常用的测试库。clojure.test
更简单,不那么神奇,有更大的工具和插件生态系统。
显然,手工重写 5000 个测试是不实际的。相反,我们决定使用 Clojure 来自动重写它们,使用 Clojure 的内置语言操作特性。
Clojure 是同形的,这意味着所有源文件都可以表示为数据结构。我们的翻译器将每个测试文件解析成一个 Clojure 数据结构。从那里,我们在将代码写回磁盘之前对其进行转换。一旦放到磁盘上,我们就可以加载并运行测试,如果测试通过,甚至可以自动将文件检回版本控制,所有这些都不需要离开 REPL。
阅读
这整个操作的关键是read
。read-string
是一个内置的 Clojure 函数,它接受包含任何 Clojure 代码的字符串,并将其作为 Clojure 数据结构返回。这与编译器加载源文件时使用的函数相同。一个简单的例子:(read-string "[1 2 3]")
返回[1 2 3]
。
我们使用read
将我们的测试代码变成一个大的嵌套列表,它可以被普通的 Clojure 代码修改。
转换
我们的测试是使用midje
编写的,我们希望将它们转换成使用clojure.test
。这里有一个使用midje
的测试示例:
(ns circle.foo-test
(:require [midje.sweet :refer :all]
[circle.foo :as foo]))
(fact "foo works"
(foo x) => 42)
以及使用clojure.test
的转换版本:
(ns circle.foo-test
(:require [clojure.test :refer :all]))
(deftest foo-works
(is (= 42 (foo x))))
这种转变包括替换:
-
midje.sweet
与clojure.test
呈 ns 形式 -
(fact "a test name"...)
用(deftest a-test-name ...)
,因为clojure.test
名字是变量,不是字符串 -
(foo x) => 42
同(is (= 42 (foo x)))
-
一些小细节,我们现在忽略了
转换是一个简单的深度优先的树遍历:
(defn munge-form [form]
(let [form (-> form
(replace-midje-sweet)
(replace-foo)
...)]
(cond
(or (list? form)
(vector? form)) (-> form
(replace-fact)
(replace-arrow)
(replace-bar)
...
(map munge-form)))
:else form))
->
语法的行为很像 Ruby 或 JQuery 或 Bash 管道中的链接:它将函数调用的结果作为参数传递给下一个函数调用。
第一部分(let [form ...])
采用 Clojure 形式,并在其上调用每个翻译函数。第二部分接受表单列表——表示其他 Clojure 表达式和函数——并递归地转换它们。
有趣的工作发生在替换函数中。它们通常都是以下形式:
(if (this-form-is-relevant? form)
(some-transformation form)
form)
也就是说,他们检查传入的表单是否与他们的兴趣相关,如果是,就适当地转换它。所以replace-midje-sweet
看起来像
(defn replace-midje-sweet [form]
(if (= 'midje.sweet form)
'clojure.test
form))
箭头
Midje 的大多数测试行为都围绕着“arrows”展开,这是 Midje 用来实现 BDD 风格的声明性测试用例的一种不通顺的结构。一个简单的例子:
(foo 42) => 5
断言(foo 42)
返回 5。
根据箭头的用途以及箭头两侧的类型,有大量可能的行为。
(foo 42) => map?
例如,如果上面的右边是一个函数,当传递给函数map?
时,它断言结果为真。在标准 clojure 中,这将是:
(map? (foo 42))
以下是一些 midje 箭头的例子:
(foo 42) => falsey
(foo 42) => map?
(foo 42) => (throws Exception)
(foo 42) =not=> 3
(foo 42) => #"hello world" ;; regex
(foo 42) =not=> "hello"
更换箭头
实际的翻译由大约 40 个 core.match 规则处理。他们看起来像
(match [actual arrow expected]
[actual '=> 'truthy] `(is ~actual)
[actual '=> expected] `(is (= ~expected ~actual)
[actual '=> (_ :guard regex?)] `(is (re-find ~contents ~actual))
[actual '=> nil] `(is (nil? ~actual)))
(对于 Clojure 专家,我在上面的宏中省略了很多~ '字符以提高可读性。阅读实际的源代码,看看这到底是什么样子。)
这些翻译大部分都很简单。然而,对于contains
表单,事情变得复杂得多:
(foo 42) => (contains {:a 1})
(foo 42) => (contains [:a :b] :gaps-ok)
(foo 42) => (contains [:a :b] :in-any-order)
(foo 42) => (contains "hello")
最后一个案例特别有意思。在表达式中
(foo 42) => (contains "hello")
有两种完全不同的价值观可以让这个测试通过。(foo 42)
可以是包含项目“hello”的列表,也可以是包含子字符串“hello”的字符串:
"hello world" => (contains "hello")
["foo" "hello" "bar"] => (contains "hello")
一般来说,contains
表格很难自动翻译。有些情况需要运行时信息(比如上一个例子),因为在标准 Clojure 中没有许多contains
情况的现有实现,比如(contains [:a :b] :in-any-order)
,我们决定放弃所有contains
情况。“失败”规则看起来像是:
[actual arrow expected] (is (~arrow ~expected ~actual))
这将(foo 42) => (contains bar)
变成了(is (=> (contains bar) (foo 42)))
。这是故意不编译的,因为 midje 的 arrow 函数定义没有加载,所以我们可以手动修改。
运行时类型信息
自动翻译还有一个额外的复杂性。如果我有两个表达式:
(let [bar 3]
(foo) => bar
和
(let [bar clojure.core/map?]
(foo) => bar
Midje 的 arrow 调度依赖于右边表达式的类型,这只能在运行时确定。如果bar
解析为数据,比如字符串、数字、列表或映射,midje 测试是否相等。但是如果bar
解析为一个函数,midje 代替调用这个函数,即(is (= bar (foo)))
vs (is (bar (foo)))
。我们 90%的解决方案require
是原始的测试名称空间,而resolve
是翻译过程中的功能:
(defn form-is-fn? [ns f]
(let [resolved (ns-resolve ns f)]
(and resolved (or (fn? resolved)
(and (var? resolved)
(fn? @resolved)))))))
这在大多数情况下非常有效,但是当局部变量隐藏了全局变量名称时就会失败:
(let [s [1 2 3]
count (count s)]
(foo s) => count)
在这种情况下,我们想要(is (= count (foo s)))
,但是却得到了(is (count (foo s)))
,这失败了,因为在局部范围内,count 是一个数字,(3 [1 2 3])
是一个错误。幸运的是,这种情况并不多,因为要正确解决这个问题,需要编写一个全面的编译器,并理解作用域中的局部变量。
运行测试
一旦翻译代码写好了,我们需要一种方法来判断它是否工作。因为我们在运行时在 REPL 中运行代码,所以使用clojure.test
的内置函数在翻译后立即运行测试是微不足道的。
clojure.test
的设计决策使得将翻译和评估过程结合在一起变得很容易。所有的测试函数都可以直接从 REPL 调用,而不需要加壳,(clojure.test/run-all-tests)
甚至返回一个有意义的返回值,一个包含测试、通过和失败次数的映射:
{:pass 61, :test 21, :error 0, :fail 0}
能够从 REPL 运行测试使得在一个非常紧密的反馈循环中修改编译器和重新测试变得非常方便。
《朗读者》
然而,并不是所有事情都这么简单。
“阅读器”(Clojure 的术语,指实现read
函数的编译器部分)被设计成将源文件转换成数据结构,主要供编译器使用。阅读器去掉注释,展开宏,要求我们手动检查所有差异,并恢复删除注释或包含宏定义的行。谢天谢地,在测试中只有很少的几个。我们的编码风格倾向于选择文档字符串而不是注释,宏倾向于被隔离到一小组实用程序文件中,所以这对我们没有太大影响。
代码缩进
我们没有找到一个非常好的库来缩进我们的新代码。我们使用了clojure.pprint
的代码模式,尽管它可能是最好的库,但并没有做得那么好。在这个项目中,我们不想写那个库,所以一些文件用非惯用的间距和缩进写回磁盘。现在,当我们用锯齿状的锉刀工作时,我们用手来修补。真正解决这个问题需要一个知道惯用格式的缩进器,并且可能会考虑阅读器数据上的文件和行元数据。
在实际重写测试套件和这篇博文发表之间有很长的延迟。与此同时, rewrite-clj 已经发布。我没用过,但它看起来正是我们所缺少的。
结果
大约 40%的测试文件在没有人工干预的情况下通过了,考虑到我们将这个解决方案组合在一起的速度,这是相当惊人的。在剩余的文件中,大约 90%的测试断言被翻译并通过。因此,所有文件中 94%的断言都可以自动翻译,这是一个很好的结果。
我们的代码在 GitHub 上这里。如果你最终使用它,请让美国知道。虽然我们不推荐它用于无监督的翻译,特别是因为注释和宏问题,但它作为监督过程的一部分,对 CircleCI 非常有用。
在黑客新闻T3 上讨论这个帖子
为什么每个企业都需要 CI/CD 才能成功| CircleCI
今年 4 月,我们欢迎风险投资公司 IVP 的合伙人 Cack Wilhelm 加入 CircleCI 董事会。Cack 是我们的第一位女性董事会成员,之前曾在 Scale Venture Partners 的 B 轮投资中与 CircleCI 合作。在 IVP,Cack 专注于投资成长期的科技公司。
我们的首席执行官吉姆·罗斯最近在 IVP 的超增长播客上与 Cack 坐下来,讨论了软件交付作为竞争优势的兴起,以及实现超增长和调整 CircleCI 作为一个组织是什么样的。Jim 和 Cack 还讲述了持续集成和部署如何成为企业取得成功的关键,开发人员的决策权,以及 CircleCI 成为 CI/CD 领域领导者的原因。
我很想听听 CircleCI 背后的长期趋势。您认为目前行业中有哪些因素在继续推动对 CircleCI 这样的解决方案的需求?
Jim Rose: 早在 2011 年和 2012 年,当 CircleCI 成立时,这种能够在你做出更改的同时快速发布代码的想法是相当新的。在过去的九年中,持续部署不再是一个真正的边缘想法,而是成为了几乎所有软件交付团队渴望的北极星。
例如,在 COVID 出现的那一刻,每个人都变得不仅仅是远程优先,而是仅远程,你意识到你不能再依赖坐在某人桌子下面的构建机器。
这种自动化的想法,即能够快速移动和可靠移动,已经不仅仅是一种“美好的拥有”作为一个软件交付团队,它已经成为你必须做的核心。
CW: 我们在这里真正谈论的是开发人员——开发人员是组织中拥有预算和决策权的一流个人。
你知道,我从事风险投资的时间不长,可能只有六七年,我仍然记得谈论过 IT 购买者。这主要是关于 It 和安全性购买者,我们从来没有讨论过开发人员购买者。但是,考虑到您的走向市场战略和您对开发人员的重视,系统软件的购买者在过去的六七年中是如何发展的呢?
很长一段时间以来,开发人员只是被期望去构建东西。他们获得了一套工具,被告知要建造什么,并被视为成本中心,而不是机会和投资。随着越来越多的公司变得软件驱动,这种观点真的发生了转变。不要把你的开发团队看作是你试图控制的成本中心,你真正要考虑的是如何最大化这些很难雇佣的非常昂贵的人的能力和生产力。
对于 IT 组织来说,速度的价值是非常明显的。这不再是控制的问题。它是关于尝试创造胡萝卜或激励来使用相同的工具集。它是关于代表企业内部所有软件开发团队构建工具。
你经常会发现,开发人员喜欢挑选最适合这项工作的工具,不管这项工作是什么,然后让公司自己想办法如何使用他们工具箱里的其他工具。
CW: 我想谈谈捆绑和非捆绑的想法。例如,移动和 Linux 提供相同的东西,看起来像是软件真的要经历周期。有一股大力推动捆绑销售的力量,然后又有一股相反的力量。你认为我们现在处于循环的哪个阶段?是什么让你相信成为一个同类最佳的平台,一个提供软件交付生命周期的一大块的平台,是正确的方法?
最近市场上有很多人试图在开发领域建立端到端的平台。但是,如果你看看那些建立了这些平台的公司,通常情况下,他们 80%到 90%的收入仍然来自他们的核心产品。
我们从客户那里听到的是,他们将在特定时间针对特定问题实施他们所能找到的最佳工具。作为一家公司,我们的立场、目标和口号是:我们贴近客户。我们确保满足他们的特殊要求和需求,其他一切都会迎刃而解。
CW: 我想换个话题,更具体地谈谈 CircleCI,尤其是你现在正在经历的高速增长阶段。我有幸看到 CircleCI 的 B 轮阶段,然后再次参与来自 IVP 的E 轮。我必须承认,休息一段时间,然后回到一个大规模、成熟的组织,却看不到所有的增长,这是一种乐趣。
你能分享一下最近这轮融资进入的增长阶段吗,以及达到超增长和调整组织是什么感觉?我确信走向市场的功能与工程之间存在一些差异,那么像招聘、入职和办公空间这样的日常事务呢?
你扩展的方式是让平凡变得高度可操作。我对我们高管团队中每个人的第一个要求是,他们必须是一名出色的招聘人员。这不是雇佣第一个人的问题——在接下来的六个月里,你必须再雇佣 15 个人。随着时间的推移,如何使其可扩展和可重复?基础设施也是如此,无论是办公空间、工具,还是有一套全球管理的人力资源政策。
作为一名创始人,你试图将所有这些都落实到位,并使其可扩展、有效,并能适应变化。这是马拉松,不是短跑,但你跑得真的很快。你必须确保在继续成长的过程中尽你所能调整自己的步伐。
亚马逊、微软和 Alphabet 肯定有很大一部分业务与云基础设施支出挂钩,就像 CircleCI 一样。亚马逊、微软、Alphabet 和谷歌的很大一部分业务类似于云基础设施 CircleCI 做的很多事情。如果你还包括 DataDog 和 Atlassian,这些公司表现出非常强劲的增长,非常强劲的公开表现。从你的角度来看,你如何看待与 CircleCI 相关的公开市场以及你的下一步行动?
我认为公众市场投资者的理解真的发生了变化。当我们在 2015 年和 2016 年与公共市场投资者交谈时,他们完全不知道我们做了什么。他们不理解软件开发是公司的关键区别,不仅仅是软件公司,而是整个公司。我想到了像亚马逊、谷歌、网飞、脸书和其他公司——他们快速构建软件的能力是他们如何在其业务中创造价值的。
越来越多的是,如果你看看过去四五年里在技术上进行真正投资的更传统的行业,他们今天正在收获这些收益。自从 COVID 开始以来,Target 和沃尔玛在过去的六个月里取得了巨大的收益,因为他们在这一点上有一个非常强大的技术平台。
当你将其与没有进行投资的更传统的零售商进行比较时,你可以开始看到软件的价值和敏捷流程在创造企业价值中的价值。市场肯定已经意识到软件作为一台运转良好的软件交付机器所提供的竞争优势。
CW: 未来一两年,当你进入下一个增长阶段时,你有什么期待?有什么刺激的?
令人兴奋的是,我们可能在 2015 年就与一些公司谈过,但这些公司还没有为我们做好准备,现在他们已经准备好采用持续交付,并准备部署 CircleCI 作为这种变化的基础。
我们与之合作的一些品牌正在做令人难以置信的有趣的事情,从自动驾驶汽车到太空飞行器,从机器学习到日常移动应用,以及介于两者之间的一切。
当我和我们想要雇佣的人交谈时,我总是说我们在互联网的机房里。我们是一家以令人兴奋、有趣和新颖的方式,帮助构建您每天消费的所有体验的公司。无论你是在为美国政府建立一个小型开源图书馆还是什么,你都可以使用 CircleCI 来支持它。
与 CI/CD | CircleCI 并行运行 dbt 测试
软件开发周期中的一个困难挑战是提高开发速度,同时确保代码质量保持不变。近年来,数据世界已经采用软件开发实践来测试部署前的数据更改。测试过程可能非常耗时,并且容易出现意外错误。
例如,在 CircleCI,我们的数据团队在规模上使用 dbt 。直到最近,我们一直在经历由 dbt 云中的长时间测试运行导致的部署瓶颈。为了解决这个问题,我们建立了 CircleCI 来自动测试和部署我们的数据变更,以便我们能够尽快地向我们的数据消费者交付高质量的数据模型版本。在本帖中,我们将带您了解如何使用 CircleCI 和 dbt 根据生产副本自动测试您的数据更改,以确保数据完整性并提高您的开发速度。
本教程假设你是一个活跃的 CircleCI 用户。如果你是这个平台的新手,你可以注册一个免费账户并按照我们的快速入门指南进行设置。
dbt 是什么?
dbt 是一个数据转换工具,它允许数据人员将模块化的 SQL 与软件工程的最佳实践相结合,以实现可靠、迭代和快速的数据转换。您可以通过 dbt CLI(命令行界面)或 dbt Cloud 与 dbt 进行交互。在 CLI 版本中,您可以完全控制您的数据项目配置,并能够根据需要发布文档,而 dbt Cloud 提供了一个用户界面,可以为您设置一些配置并自动生成 dbt 文档。
为什么 dbt 在数据工程和分析中有用?
dbt 是一个强大的数据工具,允许您遍历表的变化,而无需手动修改 UPSERT 语句。您可以用模块化的方式编写 SQL,并使用参数化查询或 dbt 提供的本机测试函数来配置数据测试。它有助于跟踪您的数据依赖性,并集中您的数据转换和文档,确保重要业务指标的单一来源。此外,它允许您在将数据发布到生产环境之前,测试您对数据的假设,以确保数据的完整性。
dbt 云中的并发问题
通过使用 dbt slim CI 函数,您可以使用 dbt Cloud 为您的数据测试建立一个持续集成和持续交付(CI/CD)管道。您可以将它连接到您的 GitLab 或 GitHub 存储库,并配置测试作业在每个新的 pull 请求时触发。测试作业状态将直接出现在拉式请求中,以帮助您提高审核流程的效率。
但是,当前的 dbt 云 CI/CD 流程存在一些问题。首先,dbt Cloud CI/CD 流程目前一次只允许一个作业,如果有多个拉请求合并到生产中,这会降低部署速度。它适用于只有一两个活跃 dbt 贡献者的小型数据团队,但当我们的分析部门开始扩展,分析师开始更频繁地贡献和部署 dbt 时,它就会显示出它的局限性。对于大型数据部署,它可能会使所有其他部署变慢一整天。
此外,当拉请求中有多个提交时,dbt 云测试不能自动取消冗余工作流,这会影响分析开发的测试速度。
如何使用 CircleCI 并行运行 dbt 测试并启用自动取消
通过使用 CircleCI、dbt 和雪花,针对生产数据运行并行 dbt 测试和自动取消冗余工作流变得可行。概括地说,这些步骤是:
- 为 dbt CI 作业创建一个 dbt 概要文件,以验证您的数据模型和测试。
- 配置 dbt 来设置自定义模式,以允许 pull 请求在其各自的容器中运行数据模型和数据测试。
- 在 CircleCI 中设置一个 Python 环境,为 dbt 测试做准备。
- 在 CircleCI 中设置 dbt 运行和测试,以测试修改后的数据文件。
创建 dbt 概要文件
首先,在profiles.yml
中配置一个 dbt-ci 概要文件:
circleci:
# use user and password auth
type: snowflake
account: "nxa13674.us-east-1"
user: "{{ env_var('DBT_DATA_MODELING_SNOWFLAKE_USER', 'not_set') }}"
password: "{{ env_var('DBT_DATA_MODELING_SNOWFLAKE_PASSWORD', 'not_set') }}"
role: "{{ env_var('DBT_DATA_MODELING_SNOWFLAKE_ROLE', 'not_set') }}"
database: "{{ env_var('DBT_DATA_MODELING_SNOWFLAKE_DATABASE', 'not_set') }}"
warehouse: "XSMALL_WAREHOUSE"
schema: "public"
query_tag: "circleci_dbt"
之后,转到 CircleCI 组织设置页面,设置一个 CircleCI 上下文,安全地共享项目中的环境变量。该上下文被命名为dbt-ci-cd
,因此您可以在 CircleCI YAML 文件中引用它。
在 dbt 中设置自定义模式
接下来,设置一个 dbt 定制模式宏,以允许 pull 请求在容器化的环境中运行数据模型和数据测试:
- 在您的 dbt 项目的根目录中创建
macros/get_custom_schema.sql
来定制模式配置。 - 在
get_custom_schema.sql
文件中复制并粘贴下面的代码:
{% macro generate_schema_name(custom_schema_name, node) -%}
{%- set default_schema = target.schema -%}
{%- if target.name == "circleci" -%}
{# replace everything except letter and number in branch name to be underscore#}
{% set re = modules.re %}
{% set git_branch = modules.re.sub('[^a-zA-Z0-9]', '_', env_var('CIRCLE_BRANCH')).lower() %}
{%- if custom_schema_name is none -%}
z_pr_{{ git_branch }}_{{ default_schema }}
{%- else -%}
z_pr_{{ git_branch }}_{{default_schema}}_{{ custom_schema_name | trim }}
{%- endif -%}
{%- else -%}
{%- if custom_schema_name is none -%}
{{ default_schema }}
{%- else -%}
{{ custom_schema_name | trim }}
{%- endif -%}
{%- endmacro -%}
它将在 CircleCI CI 流程中使用标准模式格式z_pr_<the branch name>_<default schema>
构建 dbt 数据模型,以将 CI 流程生成的数据模型容器化。
配置 CircleCI 管道以测试 dbt 数据
最后,为 dbt 配置 CircleCI。
CircleCI 的工作流程是在您的 dbt 项目根目录下的.circleci/config.yml
下的 YAML 文件中定义的。默认情况下,CircleCI 会在每次提交到存储库时自动触发测试过程,也可以手动触发。以下是设置流程的步骤:
- 在您的 dbt 项目的根目录中创建
.circleci/config.yml
- 复制并粘贴下面的 YAML,以设置您的 dbt CI 流程。我们在 CircleCI 使用poems来管理我们的 Python 依赖,但是你可以使用任何你喜欢的 Python 依赖管理工具。dbt CI 流程使用 dbt 的 state:modified run 方法在生产环境中运行和测试修改后的 dbt 数据模型。
commands:
setup-python-dependencies:
description: Setup the python environment for testing and linting
steps:
- checkout:
path: ~/project
- restore_cache:
keys:
- v1-poetry-cache-{{ arch }}-{{ .Branch }}-{{ checksum "poetry.lock" }}
- v1-poetry-cache-{{ arch }}-{{ .Branch }}
- v1-poetry-cache
- run: echo "export PATH=$HOME/.poetry/bin:$PATH" >> $BASH_ENV
- run: curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
- run: poetry install
- save_cache:
key: v1-poetry-cache-{{ arch }}-{{ .Branch }}-{{ checksum "poetry.lock" }}
paths: /virtualenvs
jobs:
dbt-build-ci:
executor: python
steps:
- setup-python-dependencies
- run: poetry run dbt deps --project-dir resources
- run:
name: compile dbt manifest in master branch
# use --target prod so the --defer will work correctly
command: |
git checkout master
poetry run dbt debug --project-dir resources --target prod
poetry run dbt compile --project-dir resources --target prod
mv resources/target/manifest.json .
- run: git checkout ${CIRCLE_BRANCH}
# separate run and test because "dbt build" will fail all downstream if upstream tests fail. We still want to see all tests results if the test failure is from production
# currently there is a version bug with state:modified.body, we will add state:modified.body back once we update dbt to be 1.0.4;
- run: poetry run dbt run --models state:modified --defer --state ~/project --exclude tag:skip-ci --project-dir resources -x
- run: poetry run dbt test --models state:modified --defer --state ~/project --exclude tag:skip-ci --project-dir resources
workflows:
commit:
- dbt-build-ci:
context: dbt-ci-cd
filters:
branches:
ignore:
- master
在 CI/CD 管道中运行并行 dbt 测试
一旦您将您的变更推送到您的版本控制工具,CircleCI 将自动为您启动测试过程。单个任务可以并行运行。
现在你有一个成功的构建!
结论
您已经成功地在持续集成管道中设置了数据测试工作流!随着您的数据团队开始扩展,在发布到生产环境之前测试您的数据更改是一个重要的步骤。它帮助数据团队更快地迭代,并确保涉众对数据的信任。在持续集成管道中运行数据模型测试可以帮助您的数据团队扩展其工程和分析过程。
如果您有兴趣了解更多关于 CircleCI 的信息,并加入我们的数据团队,了解我们如何将软件开发流程应用于数据,请查看我们招聘页面上的空缺职位。
使用 CircleCI runner | CircleCI 运行私有云及内部作业
原文:https://circleci.com/blog/run-private-cloud-and-on-premises-jobs-with-circleci-runner/
CircleCI 发布了一个名为 CircleCI runner 的新功能。runner 特性增强并扩展了 CircleCI 平台的功能,使开发人员能够多样化他们的构建/工作负载环境。多样化的执行环境满足了我们在 CircleCI runner 公告中提到的一些特定的边缘情况。例如,我们在金融和医疗保健等高度监管行业的一些较大客户必须满足合规性要求,以防止他们在云中运行一些工作负载。其他从事嵌入式系统或物联网工作的人需要在云中根本不存在的硬件上构建。我们构建了 runner,以便即使是具有最严格的安全性和合规性要求的客户也可以使用 CircleCI 满足其 100%的软件交付需求。在这篇文章中,我将介绍 CircleCI 流道,并演示如何在管道中使用流道。
先决条件
在开始使用跑步者之前,您需要完成一些任务:
运行节点平台
CircleCI runner 的当前产品在这些平台和操作系统上安装和运行:
安装目标 | 目标值 |
---|---|
Linux x86_64 | 平台=linux/amd64 |
Linux ARM64 | 平台=linux/arm64 |
macOS x86_64 | 平台=达尔文/amd64 |
在本教程中,我们将使用 Ubuntu 20.04 和platform=linux/amd64
平台。
供应 CircleCI 流道配置
在部署 runner 节点和处理作业之前,您需要创建名称空间和资源类,并生成 runner 令牌。这些操作在本教程的先决条件部分提到过,但是我将在这里更详细地介绍它们。
创建 CircleCI 命名空间
CircleCI 要求您在平台上创建一个名称空间,定义您在 CircleCI 平台中的唯一身份。这个名称空间也用于其他功能,如 CircleCI Orbs registry。如果您已经创建了一个名称空间,那么您可以前进到下一节。
使用 CircleCI CLI 工具创建新的名称空间:
circleci namespace create <your namespace> <vcs-type> <org-name>
指定您的名称空间名称/值和您的 VCS/版本控制类型(github 等..).然后指定您的组织名称(通常是您的 GitHub 用户/组织名称)。
创建新的资源类
创建名称空间之后,您需要为您的跑步者创建一个新的资源类。资源类别允许您对相似的跑步者资源进行分组,并可用于对跑步者进行分类。例如,如果您希望使用 macOS 和 Linux 运行程序,您应该创建两个资源类,一个用于 macOS 资源,一个用于 Linux 资源。您还可以根据计算节点硬件资源(如 CPU、RAM、磁盘和网络功能)对资源类进行分类。运行以下命令创建新的 runner 资源类:
circleci runner resource-class create <my-namespace/resource-class> <description>
第<my-namespace/resource-class>
节要求您指定您的 CircleCI 名称空间,然后创建一个资源类名。在这个例子中:punkdata/do-ub-linux-cpu4-16gbram
,名称空间是punkdata
,do-ub-linux-cpu4-16gbram
是资源类名。do-ub-linux-cpu4-16gbram
是一个描述性的名称,代表使用 DigitalOcean 计算节点的 Ubuntu Linux 平台,该节点具有 4 个 CPU 和 16 GB RAM。如果需要,您可以通过填充<description>
参数来给出关于资源类的更多细节。
创建跑步者令牌
使用 CLI 工具完成的最后一项任务是生成运行者令牌,运行者节点使用该令牌根据 CircleCI 平台进行身份验证。
注意: 令牌无法再次检索,因此请确保在创建后安全存放。
通过运行以下命令生成运行者令牌:
circleci runner token create <my-namespace/resource-class> <nickname>
和前面一样,指定您的<my-namespace/resource-class>
参数值,然后为您的令牌指定一个描述性的、容易记忆的<nickname>
值。
CircleCI 转轮手动安装
CircleCI 运行人员使用手动安装过程,该过程详细说明了如何在相应的计算节点上安装和配置所需的软件。我鼓励您熟悉这个手动流程。不过,在本教程中,我们将使用 Terraform 在 DigitalOcean 平台上提供 CircleCI 跑步者。
CircleCI 转轮提供自动化
接下来,我将介绍我使用 Terraform 构建的自动化跑步者供应流程。从这个回购中获得的代码是我们将用来提供 CircleCI 跑步者的代码。我希望您已经完成了先决条件一节中的步骤。您现在需要完成它们,以便完成本教程。
此项目回购结构中的相关代码位于以下目录中:
runner-scripts/
|_runner-agent-install
|_runner-provisioner
terraform/
|_do/
|_main.tf
|_variables.tf
在下一节中,我将讨论 runner-agent_install 和 runner-provisioner 文件的内容。这些文件使一些手动转轮安装过程自动化。
跑步者脚本
runner-scripts/
目录包含脚本文件,这些文件自动安装和提供运行器节点。这些脚本由代码和命令组成,封装了手动转轮安装过程中定义的各种脚本和命令。这些脚本无需修改即可用于安装和供应运行程序节点。在本教程中,我们将在 Terraform 代码中利用它们。这是这些文件的快速分类。
runner-agent-install
脚本有一个“平台”参数,必须为其分配以下值之一:
- 平台=linux/amd64
- 平台=linux/arm64
- 平台=达尔文/amd64
必须首先执行该脚本来安装适当的运行程序代理。
runner-provisioner
脚本提供并配置运行程序代理和底层操作系统服务。此脚本具有必须分配的参数:
PLATFORM
需要是上面列出的受支持平台值之一RUNNER_NAME
标识跑步者节点;我建议使用节点的主机名RUNNER_TOKEN
是您在本教程中前几步生成的跑步者令牌
这个脚本自动执行运行器配置和操作系统级操作,比如用户配置和运行器相关的服务实现。在计算节点上运行这些脚本将产生一个功能完整的 CircleCI runner 节点,它可以在您的 CircleCI 配置中用作执行器。
接下来,我将讨论 Terraform 代码,它利用这些脚本将基础设施作为代码来提供 runner 节点。
Terraform runner 供应器代码
让我花点时间浏览一下提供 runner 节点的 Terraform 代码。看看terraform/do/
目录中的文件,从variables.tf
文件开始。
variables.tf
文件保存了 Terraform 项目使用的所有变量。该文件中指定的变量定义允许使用 Terraform CLI 传递值。下面是一个variables.tf
文件的例子:
variable "do_token" {
type = string
description = "Your DigitalOcean API token. See https://cloud.digitalocean.com/account/api/tokens to generate a token."
}
variable ssh_key_file {
type = string
description = "The private ssh certificate in the user's .ssh/ dir -> $HOME/.ssh/id_rsa. Terrform uses this to access & run commands on node."
}
variable do_ssh_key {
type = string
default = "ariv3ra-ssh"
description = "Specifies a Public SSH cert that exists in the DO Account Security section. See https://www.digitalocean.com/docs/droplets/how-to/add-ssh-keys/to-account/"
}
variable file_agent_install {
type = string
default = "../../runner-scripts/runner-agent-install"
description = "Specifies the runner-agent-install script file path"
}
variable file_provisioner {
type = string
default = "../../runner-scripts/runner-provisioner"
description = "Specifies the runner-provisioner script file path"
}
variable runner_platform {
type = string
default = "linux/amd64"
description = "Defines the runner architecture platform choose one: [linux/amd64, linux/arm64, darwin/amd64]"
}
variable runner_name {
type = string
description = "The host name of the CircleCI Runner which is also the Droplet name."
}
variable runner_token {
type = string
description = "The CircleCI Runner token from the CLI"
}
variable "region" {
type = string
description = "The region where the assets should be assigned to"
default = "nyc3"
}
variable "node_size" {
type = string
description = "defines the size of the compute node"
default = "s-4vcpu-8gb"
}
variable "image_name" {
type = string
description = "defines the DigitalOcean image name to install to compute node"
default = "ubuntu-20-04-x64"
}
variable droplet_count {
type = number
default = 2
description = "The number of Droplet nodes you want to create"
}
本例中列出的变量有description
字段,告诉您每个变量指定的内容。使用这些变量,您可以为运行者节点计数、计算节点大小、运行者平台以及任何其他关于运行者或云提供商的相关数据赋值。在这种情况下,Terraform 代码适用于以数字海洋水滴的形式提供 runner 节点,这些节点本质上是计算节点。
接下来,看一下main.tf
文件,它包含了这个项目的核心 Terraform 功能。下面是一个main.tf
文件的例子:
terraform {
required_version = ">= 0.13"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
}
local = {
source = "hashicorp/local"
}
}
# This backend uses the terraform cloud for state.
backend "remote" {
organization = "datapunks"
workspaces {
name = "dorunner"
}
}
}
provider "digitalocean" {
token = var.do_token
}
data "digitalocean_ssh_key" "terraform" {
name = var.do_ssh_key
}
resource "digitalocean_droplet" "dorunner" {
count = var.droplet_count
image = var.image_name
name = "${var.runner_name}-${count.index}"
region = var.region
size = var.node_size
private_networking = true
ssh_keys = [
data.digitalocean_ssh_key.terraform.id
]
connection {
host = self.ipv4_address
user = "root"
type = "ssh"
private_key = file(var.ssh_key_file)
timeout = "3m"
}
#Upload runner agent install script
provisioner "file" {
source = var.file_agent_install
destination = "/tmp/runner-agent-install"
}
#Upload runner provisioner script
provisioner "file" {
source = var.file_provisioner
destination = "/tmp/runner-provisioner"
}
provisioner "remote-exec" {
inline = [
"export PATH=$PATH:/usr/bin",
"sudo apt update",
"sudo apt -y upgrade",
"curl -sL https://deb.nodesource.com/setup_14.x | sudo bash -",
"sudo apt install -y nodejs",
"cd /tmp",
"chmod +x /tmp/runner-agent-install",
"chmod +x /tmp/runner-provisioner",
"/tmp/runner-agent-install ${var.runner_platform}",
"/tmp/runner-provisioner ${var.runner_platform} ${var.runner_name} ${var.runner_token}",
]
}
}
output "runner_hosts and ip_addresses" {
value = {
for instance in digitalocean_droplet.dorunner :
instance.name => instance.ipv4_address
}
}
这个文件由多个元素组成。我将把它们分开,并在每个代码片段后提供一个描述。
terraform {
required_version = ">= 0.13"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
}
local = {
source = "hashicorp/local"
}
}
# This backend uses the terraform cloud for state.
backend "remote" {
organization = "datapunks"
workspaces {
name = "dorunner"
}
}
}
这个片段指定了我们的目标平台提供商(DigitalOcean)。它还指定了维护项目状态的位置。在这种情况下,它位于您在先决条件部分创建的dorunner
Terraform Cloud 工作空间中。
provider "digitalocean" {
token = var.do_token
}
data "digitalocean_ssh_key" "terraform" {
name = var.do_ssh_key
}
这个片段指定了在 DigitalOcean 平台中配置的 DigitalOcean API 令牌和 SSH 密钥。使用在variables.tf
文件中声明的变量指定这些值。
resource "digitalocean_droplet" "dorunner" {
count = var.droplet_count
image = var.image_name
name = "${var.runner_name}-${count.index}"
region = var.region
size = var.node_size
private_networking = true
ssh_keys = [
data.digitalocean_ssh_key.terraform.id
]
connection {
host = self.ipv4_address
user = "root"
type = "ssh"
private_key = file(var.ssh_key_file)
timeout = "3m"
}
这个代码片段代表了实际创建和提供 runner 节点的自动化。您需要了解的主要因素:
name
是现有变量的插值,生成唯一的运行节点主机名ssh_keys
使用 data.digitalocean_ssh_key 块从 DO 中检索公共 ssh 密钥- Terraform 需要访问权限才能在新创建的节点上执行命令。
private_key
指定与在var.do_ssh_key
变量中指定的公共 ssh 密钥相对应的私有 ssh 密钥
#Upload runner agent install script
provisioner "file" {
source = var.file_agent_install
destination = "/tmp/runner-agent-install"
}
#Upload runner provisioner script
provisioner "file" {
source = var.file_provisioner
destination = "/tmp/runner-provisioner"
}
这个代码片段将runner_agent_install
和runner_provisioner
上传到新创建的计算节点。这些文件将安装 CircleCI runner 代理,并将节点设置为 CircleCI runner 节点。
provisioner "remote-exec" {
inline = [
"export PATH=$PATH:/usr/bin",
"sudo apt update",
"sudo apt -y upgrade",
"curl -sL https://deb.nodesource.com/setup_14.x | sudo bash -",
"sudo apt install -y nodejs",
"cd /tmp",
"chmod +x /tmp/runner-agent-install",
"chmod +x /tmp/runner-provisioner",
"/tmp/runner-agent-install ${var.runner_platform}",
"/tmp/runner-provisioner ${var.runner_platform} ${var.runner_name} ${var.runner_token}",
]
}
}
这个代码片段在计算节点上远程执行命令。inline
参数是要在计算节点上执行的命令列表。这些命令构成了 CircleCI runner 节点的配置过程。我想指出的是,这个模块有一些重要的命令:
curl -sL https://deb.nodesource.com/setup_14.x | sudo bash -
下载 Node.js 版本 14/tmp/runner-agent-install ${var.runner_platform}
使用var.runner_platform
值执行转轮安装脚本/tmp/runner-provisioner ${var.runner_platform} ${var.runner_name} ${var.runner_token}
使用多个变量(如平台、运行程序名称和令牌)执行运行程序配置脚本
output "runner_hosts_and_ip_addresses" {
value = {
for instance in digitalocean_droplet.dorunner :
instance.name => instance.ipv4_address
}
}
最后,这个片段是 Terraform 输出,它遍历所有节点并打印主机名和相应的 IP 地址。当 Terraform 执行完成时,您将拥有功能齐全的 CircleCI runner 节点,可以开始处理您的 runner 管道工作负载。
创建 CircleCI 转轮节点
我将使用示例代码 repo 来演示在 DigitalOcean 中创建 runner 节点。
在终端类型中:
cd terraform/do
terraform init
前面的命令将初始化 Terraform 项目。下一个命令将执行 Terraform 创建过程:
terraform apply \
-var "do_token=${DIGITAL_OCEAN_TOKEN}" \
-var "runner_token=${RUNNER_TOKEN}" \
-var "runner_name=dorunner" \
-var "ssh_key_file=${HOME}/.ssh/id_rsa" \
-auto-approve
此 Terraform 命令指定 Terraform 变量的值。我建议将敏感数据分配给本地系统环境变量,以防止暴露,并在终端中工作时保持一致的行为。如果 Terraform 变量分配了default
值,则这些值将被使用,而不是必需的。您可以通过简单地指定您想要覆盖的变量并提供要使用的新值来覆盖default
变量值。
运行此命令后,您应该会看到类似如下的结果:
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
runner_hosts_and_ip_addresses = {
"dorunner-0" = "68.183.55.75"
"dorunner-1" = "68.183.61.144"
}
正如您在这里看到的,已经创建了两个单独的 DigitalOcean Droplet 节点,并将其配置为 CircleCI runner 节点。这些节点已经准备好开始处理管道工作负载。结果显示了节点的“主机名”和相应的 IP 地址,如果需要,您可以使用 SSH 来访问它们。
修改流道节点
现在您已经使用 Terraform 创建了 runner 节点,您还可以使用 Terraform 来管理这些节点。例如,我最初创建了两个 runner 节点来处理管道工作负载。如果事实证明两个节点不足以处理所有工作负载,您可以添加更多节点。如果需要,可以添加三个额外的 runner 节点来帮助处理工作负载。在这种情况下,您可以将var.droplet_count
值调整为您需要的节点数量。为什么不将数量增加一倍,达到 4 个活动运行程序,这样我们就有足够的节点来处理工作负载了?下面是一个示例命令,您可以使用它来添加两个新的跑步者节点:
terraform apply \
-var "do_token=${DIGITAL_OCEAN_TOKEN}" \
-var "runner_token=${RUNNER_TOKEN}" \
-var "runner_name=dorunner" \
-var "ssh_key_file=${HOME}/.ssh/id_rsa" \
-var "droplet_count=4" \
-auto-approve
我使用这个命令,通过在 apply 命令中将新的数量分配给 Terraform 变量-var "droplet_count=4"
,将现有的流道节点数从 2 更改为 4。运行该命令后,您应该会看到如下结果:
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
runner_hosts_and_ip_addresses = {
"dorunner-0" = "68.183.55.75"
"dorunner-1" = "68.183.61.144"
"dorunner-2" = "64.225.21.96"
"dorunner-3" = "64.225.21.95"
}
在这些结果中,您可以看到添加了两个新的跑步者节点。现在总共有四个 runner 节点准备好处理工作负载。您可以使用这种方法减少所需的节点总数。在任何情况下,droplet-count
变量都是节点总数的控制点。
CircleCI runner 示例 config.yml
现在我们已经有了一些 CircleCI runner 节点,可以开始使用它们来处理 CI/CD 管道中的工作负载。我创建了一个示例 config.yml,演示了如何在管道作业中使用 CircelCI runner 节点:
version: 2.1
jobs:
runner_build_test:
machine: true
resource_class: datapunks/dorunner
steps:
- checkout
- run:
name: Install npm dependencies
command: |
npm install
- run:
name: Run Unit Tests
command: |
./node_modules/mocha/bin/mocha test/ --reporter mochawesome --reporter-options reportDir=test-results,reportFilename=test-results
- store_artifacts:
path: test-results
workflows:
build_test_deploy:
jobs:
- runner_build_test
这个示例显示了一个在 runner 节点上执行的简单作业。runner_build_test:
任务展示了如何使用machine:
和resource_class:
键定义跑步者执行者。使用namespace/resource_class_name
组合键分配resource_class:
键。当指定resource_class:
键的值时,必须使用该组合。该示例来自 Node.js 项目,要求 Node.js 安装在 runner 节点上,这发生在 Terraform 供应过程中。
既然我已经演示了如何在 config.yml 文件中实现和使用 runner,那么您应该很清楚如何在您选择的平台和云提供商上使用 Terraform 轻松地启动和运行 CircleCI runner 节点。作为奖励,我将在下一部分描述一些你可以用来帮助跑步者做好准备的技巧。
CircleCI 跑步提示
CircleCI 跑步器仍然非常新,所以我想分享一些建议,你可能会发现在使用跑步器时有帮助:
- 在转轮节点上安装软件。Runner 必须在本地安装您的构建所依赖的所有软件:Node.js、git、gcc、python、Docker client 以及其他任何需要的软件。
- 了解跑步者安全政策。如果您在云提供商中运行节点,请确保您完全理解各个提供商内部安全性的复杂性。理解诸如安全组、基于角色的访问和防火墙配置之类的东西对于运行 runner 节点是至关重要的。
- 给你的跑步者贴上补丁。运行器本质上是计算节点,可以增强您当前的 CircleCI 管道。这些节点的管理完全由您的组织负责。这包括更新适当的软件/依赖项以及操作系统安全和漏洞补丁。维护最新的软件和安全的 runner 节点对于保护您的工作负载至关重要。
- 了解跑步者的局限性。在这里熟悉一下目前跑步者的局限性,这样你就明白跑步者能做什么,不能做什么。
结论
CircleCI 平台处理所有的 CI/CD 处理,但是 CircleCI runners 是扩充您的 CI/CD 管道的一个好方法。如果您需要执行专门的处理,或者如果您有定制或规定的要求,流道会特别有用。仅在特定的用例中使用 runners,比如在本地运行作业的需求。当存在由于更严格的隔离而限制访问的基础设施时,或者如果您使用 CircleCI 不作为资源类提供的独特计算架构时,运行者可以提供帮助。
在这篇文章中,我简单介绍并演示了如何使用脚本和 Terraform 自动创建和提供 CircleCI runners。我还演示了如何定义管道中的runner
作业。我很想听听你的反馈、想法和意见,所以请发推特给我 @punkdata 加入讨论。
感谢阅读!
持续部署 Rust 应用程序| CircleCI
Rust 是一种速度极快、记忆效率高的语言,大约在十年前首次出现。随着 WebAssembly 的流行,Rust 最近获得了很大的动力,这种语言允许像 C++、C 和 Rust 这样的语言在 web 浏览器中运行。这使得开发人员能够构建高性能的应用程序,并为 web 应用程序提供 web 平台上没有的本机功能。
在本教程中,您将学习如何将 Rust 应用程序部署到托管平台。然后,您将通过构建一个连续部署管道来自动化其部署。管道将确保应用程序的更改在被推送到您的远程存储库后得到部署。
先决条件
要完成本教程,需要做一些事情:
所有这些安装和设置,我们可以开始教程。
创建 Rust API 项目
首先,通过运行以下命令创建一个新的 Rust 应用程序:
cargo new --bin rust-api
这将在rust-api
文件夹中快速搭建一个基本的 Rust 应用程序。标志指定我们正在构建一个应用程序而不是一个库。一旦这个过程完成,用cd rust-api
进入文件夹的根目录。
您将使用火箭包构建一个简单的 API 项目。Rocket 提供了一个用 Rust 开发 web 应用的 API。火箭需要nightly
版本的 Rust。您可以通过在系统的任何地方运行以下命令来全局设置/安装nightly
版本:
rustup default nightly
如果您愿意,您可以通过在项目的根目录下运行以下命令,只为您正在处理的项目设置nightly
版本:
rustup override set nightly
当nightly
版本设置好后,打开项目根目录下的Cargo.toml
文件。将火箭添加为依赖项:
.....
[dependencies]
rocket = "0.4.7"
web API 将包含两个端点:
- 根(
/
)端点发送欢迎消息 greet
端点接受 URL 参数name
并以问候作为响应
找到文件src/main.rs
并用以下代码替换其内容:
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use] extern crate rocket;
#[get("/")]
fn index() -> &'static str {
"Welcome to the Rust API"
}
#[get("/greet/<name>")]
fn greet(name: String) -> String {
format!("Hey {}, glad to have you here!", name)
}
fn main() {
rocket::ignite().mount("/", routes![index,greet]).launch();
}
这段代码定义了两个路由处理函数,每个路由一个:
index
函数向客户端返回一条欢迎消息greet
函数将name
URL 参数作为其参数,并返回一个包含问候消息中的name
的字符串
main
函数实例化rocket
web package,装载定义的路由,并启动 web 应用程序。
要运行此项目,请转到应用程序的根目录,并运行:
cargo run
这将引导 web 服务器,并在 CLI 上显示以下消息,表明服务器已启动并正在运行。
然后,您可以访问这两个端点。
现在您已经有了一个用 Rust 构建的工作 web API,您可以将它部署到 web 上了。
创建 Heroku 应用程序
要部署 Rust API,您需要设置一个 Heroku 应用程序来托管该应用程序。导航到你的 Heroku 账号管理控制台,进入新建->-新建 app 。创建一个新的应用程序,给它起一个你喜欢的名字。
记下您刚刚输入的应用程序名称;在本教程的后面部分,您将需要用到它。在你的仪表盘的 账户设置部分找到你的 Heroku API 密匙,并记下来。在教程的后面,您将需要它。
最后,安装 Rust buildpack。构建包是在部署应用程序时运行的脚本。在这个练习中,您将需要https://github.com/emk/heroku-buildpack-rust
构建包。
转到 Heroku 应用程序的设置页面,向下滚动到 Buildpacks 。点击添加构建包。在弹出的界面上,将https://github.com/emk/heroku-buildpack-rust
链接输入输入栏,然后点击保存更改。
为部署设置 CircleCI 项目
要在 CircleCI 上设置您的项目,您首先需要将项目推送到 GitHub 。
接下来,转到 CircleCI 仪表板上的 Add Projects 页面。
点击设置项目按钮。
在设置页面上,点击 Use Existing Config 表示您将手动添加一个配置文件,并且不使用示例。系统会提示您下载管道的配置文件或开始构建。
点击开始建造。此构建将失败,因为您尚未设置配置文件。我们稍后将完成这项任务。
现在我们需要为刚刚添加的项目设置环境变量。这将为项目提供对 Heroku 应用程序进行部署的认证访问。
确保您的项目是当前选定的项目。点击位于 Pipelines 页面右上角的项目设置按钮,进入您的项目设置。
您现在应该在项目设置页面上。在这个页面上,点击侧面菜单中的环境变量。
在环境变量页面上,点击添加环境变量。
添加这些环境变量:
HEROKU_APP_NAME
是您的 Heroku 应用程序的名称(在本教程中为cci-rust-api
HEROKU_API_KEY
是你的 Heroku 账号 API 密匙(可以在 Heroku 账号设置下的账号标签下找到)
现在,您已经在 CircleCI 控制台上为部署到 Heroku 做好了一切准备。
自动化 Rust API 应用程序的部署
您的 Rust 配置管理至关重要。配置 Rust 应用程序以部署到 Heroku,这包括为部署设置应用程序和创建部署管道。这个过程有三个步骤:
- 创建一个
Procfile
来指导 Heroku 如何运行应用程序 - 创建一个
RustConfig
文件来定义 Heroku 要使用的 Rust 版本(nightly
) - 创建配置管道文件
在 Rust 项目的根目录下,创建一个Procfile
。添加此命令:
web: ROCKET_PORT=$PORT ./target/release/rust-api
该命令指示 Heroku 运行位于./target/release/rust-api
的构建应用程序的发布版本。通常,这足以让 Heroku 运行大多数 Rust 应用程序。然而,Heroku 使用动态端口,您需要告诉 Rocket 将 web 应用程序绑定到该端口。在指定应用程序的构建位置之前,添加命令ROCKET_PORT=$PORT
。这将 rocket 的端口环境变量ROCKET_PORT
设置为 Heroku 在部署时分配的动态端口$PORT
。
接下来,在项目的根目录下创建文件RustConfig
。添加这一行:
VERSION=nightly
创建部署管道脚本
在这个脚本中,我们将使用 CircleCI 的 Heroku orb 将应用程序从您的 GitHub 存储库直接部署到 Heroku。orb 是易于使用的包,它抽象了许多样板文件和复杂的命令和工作流。使用 orbs 提供了一个开发人员友好的 API,可以在配置文件中使用。
在项目的根目录下,创建一个名为.circleci
的文件夹,并在其中创建一个名为config.yml
的文件。在config.yml
里面,输入这个代码:
version: 2.1
orbs:
heroku: circleci/heroku@0.0.10
workflows:
heroku_deploy:
jobs:
- heroku/deploy-via-git
在这个配置中,Heroku orb circleci/heroku@0.0.10
被拉入,它提供了对一组强大的 Heroku 作业和命令的访问。其中一个任务是heroku/deploy-via-git
,它直接从 GitHub repo 向 Heroku 帐户部署应用程序。这项工作已经负责安装 Heroku CLI、安装项目依赖项、运行构建脚本和部署应用程序。它还获取您的环境变量,以便顺利部署到 Heroku 应用程序。
是时候运行部署设置了。提交对项目的所有更改,并推送到您的远程 GitHub 存储库。这将触发部署管道,您应该会获得一个成功的构建。
单击构建作业heroku/deploy-via-git
查看部署的详细信息。
有时,成功的构建并不完全等同于成功的部署。Heroku 平台本身还有很多可以出错的地方。为了确认你真的成功部署了,进入你的 Heroku 应用仪表板,点击打开应用。这将在 Heroku 指定的 URL https://[APP_NAME].herokuapp.com
打开应用程序。对于本教程,这将是https://cci-rust-api.herokuapp.com/
。欢迎消息确认部署成功。
干得好!
管理您的 Rust API 部署及其他
Rust 编程语言为 web 应用程序带来了大量强大的功能。它能够轻松地与诸如 Node.js 之类的框架集成,这使得开发人员能够为 Node.js 应用程序和整个 web 添加更多的性能。在本教程中,您已经学习了如何通过使用 CircleCI 建立到 Heroku 的部署管道来轻松部署 Rust 应用程序。这些知识为您提供了另一个易于使用的工具集来添加到您的 Rust 设置中。
为了扩展你的知识面,学习如何为 Rust 应用建立持续集成。从开始到结束,你可以为你的团队增加 Rust 的价值,这样你就可以更快地构建更好的应用。
要开始将你所学到的应用到你自己的工作中,今天就注册参加你的 CircleCI 免费试用。
编码快乐!
Fikayo Adepoju 是 LinkedIn Learning(Lynda.com)的作者、全栈开发人员、技术作者和技术内容创建者,精通 Web 和移动技术以及 DevOps,拥有 10 多年开发可扩展分布式应用程序的经验。他为 CircleCI、Twilio、Auth0 和 New Stack 博客撰写了 40 多篇文章,并且在他的个人媒体页面上,他喜欢与尽可能多的从中受益的开发人员分享他的知识。你也可以在 Udemy 上查看他的视频课程。
Rust 应用的持续集成| CircleCI
介绍
Rust 是一种强大的语言,建立在对性能和可靠性的承诺之上。由于没有运行时或垃圾收集器,它很容易在任何环境中运行,并且可以集成到任何现有的语言或框架中。随着 WebAssembly 的出现。Rust 在 web 开发领域变得更加重要。Rust 与 Node.js 无缝对等,以构建高性能的功能,这让 web 开发人员欣喜不已。许多 web 开发人员宁愿编写 Rust,也不愿学习/编写用于 WebAssembly 集成的 C++或 C。
在本教程中,您将学习并演示如何在将 Rust 应用程序中的更新和升级推送到远程存储库时自动测试 Rust 应用程序。
先决条件
要按照教程进行操作,需要做一些事情:
安装并设置好所有这些之后,是时候开始本教程了。
创建新的 Rust 项目
如果你在开始这个项目之前已经安装了 Rust,确保你运行rustup update
以便你当前的安装是兼容的。首先,通过运行以下命令创建一个新的 Rust 应用程序:
cargo new rust-testing
这个命令在rust-testing
文件夹中快速搭建一个准系统 Rust 应用程序。您将构建一个简单的 CLI 应用程序,提示用户输入名称,并将问候消息打印回 CLI。
找到文件src/main.rs
并用以下代码替换其内容:
fn main() {
let mut line = String::new();
println!("Please enter your name: ");
std::io::stdin().read_line(&mut line).unwrap();
println!("{}", build_message(line));
}
fn build_message(name: String) -> String{
let message = "You are welcome ".to_owned() + &name;
return message;
}
在上面的代码中,定义了一个build_message
函数,它接受一个String
类型的name
参数,并返回一个使用name
参数构造的问候消息。
main
功能提示用户输入名称。然后,该名称被传递给build_message
函数,其返回值被打印到 CLI 输出。
通过在项目文件夹的根目录下运行下面的命令来运行该程序。
cargo run
应用程序将提示您输入姓名。输入您的姓名并按下Enter
键,以查看打印到 CLI 屏幕的问候消息,如下所示。
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/rust-testing`
Please enter your name:
Fikayo Adepoju
You are welcome Fikayo Adepoju
为 Rust 应用程序添加测试
Rust 的一个优点是它捆绑了自己的测试框架,不需要额外的设置。您将为build_message
函数编写一个单元测试来断言它的输出。与其他大多数将测试放在单独文件中的语言和框架不同,Rust 实际上鼓励您将单元测试放在与被测试代码相同的文件中。
在 Rust 应用程序中运行测试的cargo test
命令将定位项目中任何具有#[test]
属性的函数,并将其作为测试用例运行。
在build_message
函数下面添加这个测试用例:
#[test]
fn test_build_message(){
let name = String::from("Fikayo");
assert_eq!(build_message(name), "You are welcome Fikayo")
}
在这个测试中,声明了一个name
变量,并赋予了一个Fikayo
的String
值。下一行检查build_message
函数是否为传入的参数返回预期的消息。
要运行此测试,请运行:
cargo test
测试应该会成功运行。这是将出现在您的 CLI 上的输出:
Compiling rust-testing v0.1.0 (/Users/stanmd/Projects/CCI/rust-testing)
Finished test [unoptimized + debuginfo] target(s) in 0.63s
Running target/debug/deps/rust_testing-5cffbb684dbc6899
running 1 test
test test_build_message ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
将 Rust 应用程序连接到 CircleCI
要开始 Rust 项目的测试自动化过程,您需要将项目推送到 GitHub 。
接下来,转到 CircleCI 仪表板上的项目页面。选择适当的 GitHub 帐户并添加项目。
点击设置项目。
在设置页面上,点击 Use Existing Config 以指示您正在手动设置配置文件,并且不使用显示的示例。接下来,您会得到提示,要么下载管道的配置文件,要么开始构建。
点击开始建造。此构建将失败,因为您尚未设置配置文件。我们将在下一步构建这个文件。
Rust 应用程序的自动化测试
是时候编写持续集成脚本了,该脚本将在更新被推送到远程代码存储库时自动运行项目中的测试。
在项目的根目录下,创建一个名为.circleci
的文件夹,并向其中添加一个名为config.yml
的文件。在config.yml
里面,输入这个代码:
version: 2.1
jobs:
build:
docker:
- image: cimg/rust:1.50.0
steps:
- checkout
- run: cargo --version
- run:
name: Run Tests
command: "cargo test"
这段代码拉入 CircleCI 铁锈图像 cimg/rust:1.50.0
。通过此图像,您可以访问 Rust 安装可用的所有 CLI 命令。
然后从远程存储库中提取代码,标记为Run Tests
的步骤使用cargo test
命令运行项目中包含的测试。
提交对项目的所有更改,并推送到您的远程 GitHub 存储库。集成管道被触发,您应该有一个成功的构建。
有关部署的详细信息,请单击构建作业。
现在,每当您更改代码时,无论是通过添加更多的特性还是更多的测试,然后推送到您的远程存储库,您的测试都会自动运行。如果您的构建由于一段代码损坏而失败,您将收到警告。
平稳!
结论
在本教程中,您已经学习并演示了如何使用持续集成管道在 Rust 项目中自动化测试。为了扩展您的知识,请学习如何持续部署 Rust 应用程序。从开始到结束,你可以为你的团队增加 Rust 的价值,这样你就可以更快地构建更好的应用。
编码快乐!
Fikayo Adepoju 是 LinkedIn Learning(Lynda.com)的作者、全栈开发人员、技术作者和技术内容创建者,精通 Web 和移动技术以及 DevOps,拥有 10 多年开发可扩展分布式应用程序的经验。他为 CircleCI、Twilio、Auth0 和 New Stack 博客撰写了 40 多篇文章,并且在他的个人媒体页面上,他喜欢与尽可能多的从中受益的开发人员分享他的知识。你也可以在 Udemy 上查看他的视频课程。
如何在不停机的情况下更换关键系统| CircleCI
原文:https://circleci.com/blog/safely-changing-critical-systems-without-downtime/
如何在不停机的情况下更换关键系统
自信地在生产中测试对于工程师快速交付软件至关重要。在这篇博客中,我们将讨论 CircleCI 团队在改变我们的关键系统时学到了什么。
权限检查带来的挑战
每当 CircleCI 用于任何事情时,我们都要检查以确保该操作得到授权。有时我们需要对一个动作进行多次检查。
例如,如果您使用我们的 API 启动一个新的工作流,我们需要检查您是否被允许启动该项目的工作流,以及您是否被授权使用该工作流使用的任何上下文。这意味着大量的权限检查。我们每秒钟执行数千次这样的检查,每次都需要尽可能快地得到正确的答案。
这些权限检查的最初实现完全基于 GitHub 的访问模型。如果我们需要检查你是否可以开始一个新的构建,我们会检查你是否可以推到相应的 GitHub 库。
随着时间的推移,这逐渐发展到包括缓存层、重试、位存储桶支持和许多其他复杂性,包括:
- 实现逻辑被分成两个子系统,所以我们必须知道使用哪一个取决于我们正在做的权限检查的类型。这是之前一次重构尝试的结果,由于优先级冲突,该尝试中途被放弃。
- CircleCI 曾经是一个单一的服务,其中一个实现仍然在代码库中。这使得新服务很难执行授权检查。
- 由于许多权限检查依赖于调用 Bitbucket 或 GitHub APIs,我们很容易受到这些 API 的性能或可用性问题的影响。两个独立的实现在这里也没有帮助,因为它们在停机期间的行为是不一致的。
- 一些检查依赖于“用户档案”,这是一个昂贵的构建缓存,包含我们从用户的 GitHub 和 Bitbucket 账户中了解到的所有信息。这导致了负面的长尾效应。如果你从许多不同的 Bitbucket/GitHub 组织建立了大量的回购,你要么得到一个即时的结果,要么等待配置文件建立超时。
- 我们希望允许用户明确地授予任何权限,而不被绑定到他们的 Bitbucket/GitHub 访问,但是只有一个子系统支持这样做。
在不影响客户体验的情况下执行迁移
我们决定通过将所有检查从整体转移到另一个子系统来完成之前尝试的重构。同时,我们对其进行了清理,删除了对“用户配置文件”的使用,以提高可靠性和性能。执行迁移必须在不对客户体验产生负面影响的情况下完成,因此我们在大约一年的时间里缓慢而谨慎地完成了迁移。
第一步:添加日志记录并收集指标
我们的第一步是在两个权限实现上创建一个外观,并强制所有检查都通过该外观来执行。这为我们提供了一个添加日志记录和收集指标的地方,这样我们就可以测量每个权限检查的基线性能和可用性。更重要的是,它允许我们在每个权限检查的实现之间切换,而不需要更改调用代码。
第二步:一次迁移一个权限检查
下一步是将每个权限检查迁移到最终目的地。我们对这些迁移非常谨慎,因为我们必须满足一些严格的标准:
- 新实现必须返回与旧实现相同的结果。我们不能意外地允许某人访问他们应该被禁止使用的东西。
- 新的实现必须至少和旧的一样快。不仅其平均延迟必须相同或更低,而且其长尾延迟应保持在可接受的范围内。比较了平均值、中间值、第 95 个百分位数和第 99 个百分位数的计时,以确保性能可以接受。
- 新的实现必须具有与旧的实现相似或更好的可用性。Bitbucket 和 GitHub 中断期间的行为必须是可预测的,并且必须尽可能将这些中断的影响降至最低。
如何进行个人权限迁移
对于每个单独的权限迁移(从实现 A 到实现 B ),我们遵循以下过程:
- 原状。我们调用了所有检查。
- 脚趾在水里。我们为所有的检查调用了 A,但是在后台线程中,我们也为 1%的检查调用了 B。我们比较了结果,并记录了它是否产生了相同的答案。我们还记录了 A 和 b 的性能数据。
- 在后台慢慢调高音量。我们继续提供来自 A 的所有答案,但是我们逐渐增加了通过 B 在后台完成的检查的百分比。此时,我们正在寻找实现中明显的 bug,并确保 B 可以安全地承担 100%的请求负载。
- 在后台 100%运行 B 时,我们检查了任何不一致之处,并修复了导致这些不一致的错误。这是我们能够做一些快速迭代的地方,因为我们在 B 上迭代,它仍然在后台,对客户不可见。部署后,我们的反馈几乎是即时的,但如果我们犯了错误,几乎没有破坏东西的风险,所以您可以快速移动,直到我们在 A 和 b 之间达成 100%的一致。
- 慢慢地部署它。我们停止在后台运行 B 的签入。在一段时间内,我们向 A 发送了 X%的流量,向 B 发送了 Y%的流量,慢慢地减少了 X %,增加了 Y%。我们知道现在的实现是等效的,但是我们希望小心不要在性能不同的情况下突然引起客户可见的变化。
- 让它浸泡。我们让 B 以 100%运行一段时间,这样我们可以在一个完整的负载周期内验证它的性能和行为。这让我们建立了更多的信心,我们没有错过任何极其罕见的错误。
- 移除旧的实现。通过移除一个。
选择使用哪个实现和/或在后台运行的百分比都是由可以立即改变的开关驱动的。即使到了上面的第六步,我们也可以用一个命令立即恢复到最初的实现。
第三步:清理安全权限
最后一步是清理用于切换的代码。度量和日志记录代码可以保留,因为随着时间的推移监视我们的权限检查的行为仍然是有价值的,但是我们能够删除用于在实现之间切换的代码。这种简化很重要,因为它允许新工程师更快地熟悉代码。
从生产测试中吸取的经验教训
生产中的测试为我们的团队提供了许多见解,帮助我们更快地交付更好的软件:
- 缓慢而小心地移动可以提高整体开发速度。当工程师对他们做出改变而不面对客户后果的能力有信心时,他们可以快速迭代。
- 在客户看不见的背景下推出新的东西,是在生产中进行测试的强大工具。我们可以通过良好的设计和生产前测试来建立信心,但总是有未知的未知,而这些只能在生产中发现。如果你能在顾客没有注意到的情况下做到这一点,你就能在他们造成任何有意义的影响之前解决他们。
- 功能切换是恢复错误更改的最佳方式。服务需要时间回滚到以前的版本,恢复源代码控制和重新部署中的更改甚至更慢(这使得您的 git 历史更难跟踪!).
- 良好的遥测技术至关重要。我们能够使用数十亿个数据点来建立对我们的变化的信心,并产生非常稳定的时间分布。
要了解关于这个主题的更多信息,请下载我们的电子书,生产测试的理性指南。
适用于 CircleCI 的 Salesforce orbs 自动交付 Salesforce 应用程序| CircleCI
今天,我们推出了新的和更新的Salesforce orbs——将 CircleCI 配置捆绑到可重用包中的集成——使开发人员更容易构建、测试和部署 sales force 应用程序。
我们最新的 Salesforce Apex orb 允许 Apex 开发人员利用世界一流的 CI/CD 加快他们的构建和测试流程。以前,开发人员需要花费数小时来学习正确配置、构建和部署所需的命令和语法。CircleCI Salesforce Apex orb 使他们能够更高效地实现相同的功能。
新的 Salesforce Apex orb 的其他功能包括:
- 自动将更改部署到生产应用程序。
- 对应用程序运行单元测试,并在 Salesforce UI 中显示结果。
- 提交时构建 VCS 应用程序,并将更改部署到临时组织。
我们还更新了我们的 Salesforce CLI orb ,它于12 月首次亮相,以确保方便地访问常用命令。
CircleCI 业务开发副总裁 Tom Trahan 表示:“Salesforce 正在引领下一代云计算,这在很大程度上是因为他们与在其平台上部署、支持和开发的人员有着独特的关系。“我们期待继续为 Salesforce 开发人员社区提供他们需要的 CI/CD 解决方案,以专注于构建最重要的东西。”
Salesforce 的产品管理 said Wade Wegner 表示:“Salesforce 平台使开发人员能够快速创新,充满信心地进行扩展,并增强组织中每个人的能力。“与 CircleCI 的最新集成将进一步为我们的开发人员社区提供 CI/CD 工具,以建立和自动化他们的开发流程。”
立即开始使用新的 Salesforce Apex orb 和更新的 Salesforce CLI orb 构建 Salesforce 应用程序。有关详细说明和教程,请参见这篇关于使用 CircleCI 为 Salesforce 应用程序建立自动化开发管道的文章。
向 CircleCI 2.0 - CircleCI 问好
来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档或博客中搜索最新信息。
更新:截至 2017 年 7 月 11 日,CircleCI 2.0 已出测试版,正式发布。你可以在这里阅读更多。
今天我们将发布 CircleCI 2.0 的封闭测试版。2.0 是一个高度可定制的强大平台,包括一流的容器支持。它将帮助各种规模的团队实现 CI 和 CD 带来的更多好处。新的和扩展的 CircleCI 2.0 功能使团队能够做最好的工作,并保持在软件开发的前沿。
如今,围绕软件的制作和交付方式正在发生范式转变,从单一架构和稳定的发布周期到微服务、容器和持续交付。自从 CircleCI 五年前成立以来,我们的客户使用的技术和方法数量呈指数级增长。我们迅速采取行动来满足这些要求,并在此过程中学到了很多东西。
下面先睹为快,看看 2.0 的一些关键特性:
本机 Docker 支持
有了 CircleCI 2.0,我们把 Docker 当成了一等公民。如果能和 Docker 一起用,就能和 CircleCI 2.0 一起用。您可以从容器注册表中提取任何图像。您还可以构建映像并将其推送到 Docker 注册表或部署环境中。您甚至可以在任何注册表上使用现有的 docker 文件和图像。您可以控制何时/是否更新或更改您的构建。你可以使用 Docker 生态系统中的任何工具。
灵活的资源分配
我们认识到持续部署不是万能的。使用 CircleCI 2.0,团队可以轻松地配置他们的资源(计算/内存),以最大限度地满足他们对特定构建的需求。
可定制的图像
CircleCI 2.0 为开发人员提供了高度可定制的构建映像。这使得团队可以根据他们的需求制作轻量级或复杂的图像。支持任何语言、任何框架和任何数据库。
本地构建
一个新的“本地构建”特性让你可以在本地启动容器和调试应用程序,减少了调试构建所需的时间。
我们还在为 2.0 计划什么?
除了这些特性之外,CircleCI 2.0 将具有显著更快的构建性能、更智能的测试分割、更全面的缓存等等。
我们将在测试版和正式版中推出新功能,但使 CircleCI 成为最受欢迎的 CI 平台之一的东西将会保留。CircleCI 2.0 将继续拥有用户友好的 UI、 SSH 访问、通知、洞察、并行、GitHub 和 Bitbucket 集成、OS X 和安卓支持、与 Slack/HipChat/JIRA 的集成,以及出色的客户支持。
请求封闭测试版邀请
如果您有兴趣了解 CircleCI 2.0 能为您和您的团队做些什么,请在此注册。我们将在未来几周内逐步增加功能和客户,并在我们开放更多测试项目时向您发送邀请。
更新:截至 2017 年 7 月 11 日,CircleCI 2.0 已出测试版,正式发布。你可以在这里阅读更多。
使用 Indeni | CircleCI 向 CI 管道添加 IaC 安全扫描
使用 CircleCI,有许多不同的 CI/CD 流可以实现自动化。其中一个流程是使用基础设施即代码(IaC)来构建云环境。例如,您可以使用 CircleCI 来自动化构建 Terraform 计划并应用它们的过程,以便在 AWS、Azure、GCP 和其他云环境中创建大规模的生产设置。
能够在构建环境之前扫描 IaC 具有巨大的好处。例如,您可以检查您的代码,看看基础设施是否符合您的云安全需求。这使您能够在管道中更早地发现安全漏洞,此时解决它们更容易且更具成本效益。这是一个被称为“左移”测试的概念。在这篇文章中,我们将仔细研究如何在 CircleCI 中使用 Indeni Cloudrail(一个 IaC 安全分析工具)来实现左移测试。
先决条件
在我们的例子中,我们将使用一些 Terraform 代码来构建一个 RDS 数据库,该数据库向公共互联网公开。显然,这是一个大禁忌,所以我将展示如何通过在您的管道中包含 Cloudrail 来发现这种错误。我们将选择的地形代码在此处可用。。当然,您可以使用任何想要的 Terraform 代码示例。
为 Terraform 设置 CircleCI 配置
我们的第一步是将 Terraform 计划添加到您的 CircleCI 工作流中。首先,我们将使用一个 CircleCI 配置文件来创建一个 Terraform 计划。该计划列出了 Terraform 将在您的 AWS 帐户中创建的资源:
version: 2.1
workflows:
main:
jobs:
- tf_plan
jobs:
tf_plan:
working_directory: /tmp/test_tf
docker:
- image: hashicorp/terraform:0.13.5
steps:
- checkout
- run:
name: terraform init & plan
command: |
cd test/aws/terraform/public_access_db_rds/individual-instance/vpc-controlled-public
terraform init -input=false
terraform plan -out plan.out
为了让 Terraform 计划成功运行,您需要设置提供对 AWS 的访问的环境变量:AWS_ACCESS_KEY_ID
和AWS_SECRET_ACCESS_KEY
。如果您现在尝试运行您的管道,作业应该会成功完成,输出会详细说明 Terraform 将要构建的内容。
将 Cloudrail 作为作业添加到工作流中
是时候将 Cloudrail 作业添加到工作流中了。这里的目标是让 Cloudrail 检查计划,看看是否有任何正在构建的东西不符合您组织的政策。这是 CircleCI 配置现在的样子:
version: 2.1
# For more details about how to use this Orb, please see:
# https://circleci.com/developer/orbs/orb/indeni/cloudrail
orbs:
cloudrail: indeni/cloudrail@2.0.2
workflows:
main:
jobs:
# Cloudrail requires a Terraform plan as an input, so we must create a plan first.
- tf_plan
- security_check_terraform:
requires:
- tf_plan
jobs:
tf_plan:
working_directory: /tmp/test_tf
docker:
# The example TF code we use here only works in v0.13, however Cloudrail supports 0.14 as well.
- image: hashicorp/terraform:0.13.5
steps:
- checkout
- run:
name: terraform init & plan
command: |
cd test/aws/terraform/public_access_db_rds/individual-instance/vpc-controlled-public
terraform init -input=false
terraform plan -out plan.out
# This persistence is important in order to pass the plan.out to Cloudrail
- persist_to_workspace:
root: .
paths:
- .
security_check_terraform:
executor: cloudrail/default
steps:
# This loads the plan.out file:
- attach_workspace:
at: .
- run: # Tests must be in a sub directory, per https://support.circleci.com/hc/en-us/articles/360021624194-Test-summary-troubleshooting
name: Create test result directory (if not exists)
command: |
mkdir test_results
# This will run Cloudrail and produce Junit test results. The idea is that if there are any rules
# that are set to MANDATE, and they find violations, we will have "failed" tests in the Junit output.
# This will then cause CircleCI to stop the pipeline and list the failed tests, allowing dev's to fix
# the violations.
# Note that Cloudrail has other output formats as well, please see the Orb documentation for more information.
# Also note that rules that are set to ADVISE (which is the default) will _not_ be included in the output by default.
- cloudrail/scan_terraform_junit:
cloud-account-id: $AWS_ACCOUNT_ID
cloudrail_api_key: $CLOUDRAIL_API_KEY
plan_output_file: test/aws/terraform/public_access_db_rds/individual-instance/vpc-controlled-public/plan.out
tf_directory: test/aws/terraform/public_access_db_rds/individual-instance/vpc-controlled-public
junit-output-file: test_results/cloudrail-junit.xml
- store_test_results:
path: test_results
- store_artifacts:
path: test_results/cloudrail-junit.xml
您可能会注意到这里添加了两个额外的环境变量:
CLOUDRAIL_API_KEY
是您开户时从 Cloudrail 收到的 API 密钥AWS_ACCOUNT_ID
是此 Terraform 工作流的目标 AWS 客户的 ID
现在在 CircleCI 中再次运行工作流,Cloudrail 成功运行,并显示结果摘要。在这个阶段,所有的违规都被认为是警告,因为我们还没有为Mandate
设置任何规则。Mandate
设置告诉 Cloudrail,如果违反了特定的规则,就停止管道,这迫使开发人员在继续之前修复问题。
正如你所看到的,当 Cloudrail 第一次被使用时,它并没有中断工作流。相反,它会查看正在发生的事情并生成评估,而不会阻止任何事情。一开始这很好,但是要让开发人员真正解决问题,您需要制定强制性的规则。
使用 Cloudrail 实施安全控制
本节介绍在 Cloudrail 中查看评估和修改强制配置。CircleCI 中 Cloudrail 的输出包含一个到 Cloudrail 中评估的链接。单击该链接。
Cloudrail 发现了一些违规行为。这些现在都被认为是警告,因为规则被设置为“建议”。Cloudrail 可以建议我们应该将哪些规则设置为“强制”。
现在,我们已经创建了一个策略,并设置了一些强制规则,我们可以继续并再次运行我们的工作流。
在 CircleCI 中与 Cloudrail 的输出交互
跳回 CircleCI 控制台,再次运行工作流。这一次,Cloudrail 的工作失败了。
它失败了,因为 Cloudrail 发现违反了规定的规则。这就是 Cloudrail 的目标:如果资源违反了公司的安全策略,就阻止它们被供应。当发生这样的故障时,CircleCI 会通知开发人员管道出现故障,并向他们指出 Cloudrail 扫描失败的特定资源。
通过 Cloudrail 的 JUnit 输出和 CircleCI 集成,开发人员可以通过点击 Test 选项卡轻松找出失败的原因。在这里,开发人员可以看到失败的原因,以及如何修复问题的说明。
一旦他们解决了问题,开发人员就可以推送他们的更改并再次触发工作流。这一次,Terraform 计划将通过,工作流将成功完成。
结论
基础设施即代码为开发团队带来了新的机会,可以运行得更快,提供更有弹性的环境。云技术有助于实现这一点,但也将关键用户数据暴露给潜在的黑客和坏人。使用 CircleCI 和 Cloudrail,开发团队可以确保他们的基础设施以可预测和一致的方式部署,没有潜在的漏洞。这允许开发人员在提高他们构建的应用程序的安全性的同时保持速度。
Yoni Leitersdorf 是领先的安全基础设施自动化公司 Indeni 的创始人兼首席执行官。Indeni 有一个名为 Cloudrail 的云安全分析新解决方案,它与 CircleCI 集成在一起。要了解 CircleCI 客户如何使用 Indeni 扫描基础设施代码文件并通过在必要时停止管道来实施安全策略,请阅读最新的新闻稿。
在 Node.js 应用程序| CircleCI 中为 MongoDB 安排数据库备份
本教程涵盖:
- 为 Node.js 应用程序设置 MongoDB 备份
- 按照定义的固定时间间隔计划备份
- 使用预定管道将应用程序部署到 Heroku
数据库备份通过在本地或远程备份服务器上创建数据库副本来保护您的数据。该操作通常由数据库管理员手动执行。像所有其他依赖于人类的活动一样,它容易出错,并且需要大量时间。
在操作系统出现故障或安全漏洞的情况下,定期计划的备份对保护您客户的详细信息大有帮助。在本教程中,我将指导您使用调度管道在定义的固定时间间隔调度应用程序数据库的备份版本。
为了更好地掌握自动化数据库备份操作的过程,我们将使用 MongoDB 数据库为 Node.js 应用程序设置一个数据库备份。该应用程序将使用 CircleCI 上的部署管道部署在 Heroku 上。MongoDB 数据库将托管在 MongoDB Atlas 上;用于数据库托管和部署的多云平台。
为了方便访问,为我们的应用程序生成的、备份的 MongoDB 集合将存储在 Microsoft Azure 存储上。。
先决条件
以下是您成功完成本教程所需的内容:
克隆演示应用程序
首先,运行以下命令来克隆演示应用程序:
git clone https://github.com/yemiwebby/db-cleanup-starter.git db-back-up-schedule
接下来,进入新克隆的应用程序并安装其所有依赖项:
cd db-back-up-schedule
npm install
该应用程序包含以下端点:
- 通过指定公司名称及其创建者来创建新公司。
- 从数据库中检索公司列表。
安装过程完成后,创建一个.env
文件,并用以下内容填充它:
MONGODB_URI=YOUR_MONGODB_URL
如果您愿意,您可以简单地运行这个命令,从 starter 项目中的.env.sample
文件复制内容:
cp .env.sample .env
当然,您需要用用于远程 MongoDB URI 的连接字符串替换YOUR_MONGODB_URL
占位符。本教程使用 MongoDB Atlas 数据库,您可以很容易地设置一个。接下来我将解释如何去做。
创建 MongoDB Atlas 帐户和数据库
在这里创建一个免费的 Atlas 帐户,并按照说明部署一个免费层集群。设置好集群和数据库用户后,打开并编辑.env
文件。
用从您的 MongoDB Atlas 仪表板中提取的连接字符串替换YOUR_MONGODB_URL
占位符:
MONGODB_URI=mongodb+srv://<username>:<password>@<clustername>.mongodb.net/<dbname>?retryWrites=true&w=majority
用集群的值替换<username>
、<password>
、<clustername>
和<dbname>
。
运行演示应用程序
正确创建和配置数据库后,打开一个终端并运行演示应用程序:
npm run start
您将得到以下输出:
> db-back-up-schedule@1.0.0 start
> node server.js
Server is running at port 3000
Connected successfully
创建公司
通过创建新的公司详细信息来测试演示应用程序。打开 Postman 或者你喜欢的 API 测试工具。使用这个 JSON 数据向http://localhost:3000/new-company
端点发送一个POST
请求:
{
"name": "Facebook",
"founder": "Mark"
}
查看公司列表
接下来,向http://localhost:3000/companies
发送一个GET
请求来检索公司列表。
在 Heroku 上创建应用程序
接下来,在 Heroku 上创建一个新的应用程序来托管和运行 Node.js 项目。前往 Heroku 仪表盘开始。点击新增,然后点击新增 App 。在表单中填写您的应用程序和您所在地区的名称。
注:Heroku 上的应用名称是唯一的。选择一个可用的并记下来。
点击创建应用按钮。您将被重定向到新创建的应用程序的部署视图。
接下来,创建一个配置变量来引用前面从 MongoDB Atlas 仪表板中提取的 MongoDB URI。为此,导航到设置页面,向下滚动,并单击显示配置变量按钮。
如下所示指定键和值,完成后点击添加。
最后,您需要检索 Heroku 帐户的 API 密钥。此密钥将用于连接您的 CircleCI 管道和 Heroku。要获取您的 API 密钥,请打开帐户设置页面。
滚动到 API 键部分。
点击显示按钮,复制 API 密钥。将它保存在您以后可以轻松找到的地方。
创建 Azure 存储帐户
如前所述,Microsoft Azure 存储将用于托管我们数据库的备份 MongoDB 集合。为此,你需要在 Azure 上注册一个免费账户。然后转到您的 Azure 门户仪表板。
从服务列表中单击存储帐户,或者通过在搜索栏中键入“存储”来使用搜索功能。
在存储帐户页面中,单击创建。在这个新页面上,指定您的存储帐户的详细信息。
接下来,执行以下操作:
- 选择一个订阅。
- 选择现有的资源组或创建新的资源组。
- 输入存储帐户名称。对于本教程,我将我的命名为
dbblobs
。 - 选择一个离你近的地区。
点击审核+创建,然后点击创建按钮。将创建并部署您的存储帐户。
值得一提的是,Azure blob 存储帐户提供了以下资源:
- 刚刚创建的存储帐户。
Container
,帮助组织一组 blobs,类似于文件系统中的一个目录。blob
,通常存储在类似于文件存储在目录中的容器中。
至此,您拥有了一个正常运行的存储帐户。接下来要做的是创建一个容器来存放 blobs(在我们的例子中是 MongoDB 集合备份)。在您的新存储帐户页面上,单击侧面菜单栏中的容器。然后点击 +容器创建一个新的容器。
为您的容器命名并更改公共访问级别。完成后点击创建按钮。
正在检索访问密钥
要轻松建立远程连接,以便从您的存储帐户存储或检索文件,您需要访问密钥。默认情况下,Azure 上的每个存储帐户都有两个不同的访问密钥,这允许你在使用另一个时替换其中一个。要显示您的密钥,请点击显示密钥并复制其中一个密钥,最好是第一个。
将密钥粘贴到计算机上安全的地方;你以后会需要它的。
添加管道配置脚本
接下来,您需要为 CircleCI 添加管道配置。管道将由安装项目依赖项和编译生产应用程序的步骤组成。
在项目的根目录下,创建一个名为.circleci
的文件夹。在该文件夹中,创建一个名为config.yml
的文件。在新创建的文件中,添加以下配置:
version: 2.1
orbs:
heroku: circleci/heroku@1.2.6
jobs:
build:
executor: heroku/default
steps:
- checkout
- heroku/install
- heroku/deploy-via-git:
force: true
workflows:
deploy:
jobs:
- build
这种配置引入了 Heroku orb circleci/heroku
,它自动提供对一组健壮的 Heroku 作业和命令的访问。其中一个任务是heroku/deploy-via-git
,它直接从你的 GitHub repo 将你的应用程序部署到你的 Heroku 账户。
接下来,在 GitHub 上建立一个存储库,并将项目链接到 CircleCI。查看将项目推送到 GitHub 以获得分步说明。
登录您的 CircleCI 帐户。如果你注册了你的 GitHub 账户,你所有的库都可以在你项目的仪表盘上看到。
点击为您的db-clean-up
项目设置项目。
将提示您配置文件的几个选项。选择use the .circleci/config.yml in my repo
选项。在 GitHub 上输入你的代码所在的分支名称,然后点击设置项目按钮。
您的第一个工作流将开始运行,但会失败。这是因为您没有提供 Heroku API 密钥。你现在可以弥补了。
点击项目设置按钮,然后点击环境变量。添加这两个新变量:
HEROKU_APP_NAME
是 Heroku (db-clean-up
)中的应用名称HEROKU_API_KEY
是您从帐户设置页面获取的 Heroku API 密钥
从失败的中选择重新运行工作流以重新运行 Heroku 部署。这一次,您的工作流将成功运行。
要确认工作流是否成功,您可以在浏览器中打开新部署的应用程序。您的应用程序的 URL 应该是这样的格式https://<HEROKU_APP_NAME>.herokuapp.com/
。
下面是到目前为止你所做和所学的快速回顾。你有:
- 在本地创建工作应用程序
- 在 Heroku 上创建了一个功能应用程序
- 已创建 Microsoft Azure blob 存储帐户
- 使用 CircleCI 成功地建立了一个管道来自动将您的应用程序部署到 Heroku
生成并上传备份文件
MongoDB 将数据记录存储为文档;具体为 BSON 文献,汇集在馆藏。
在本节中,您将创建一个脚本来为您的项目生成数据库备份文件(BSON 文档),并将该文件上传到 Microsoft Azure。为此,我们将使用两种不同的工具:
- 通过运行一个简单的命令来工作。它是一个实用工具,可用于创建数据库内容的二进制导出。MongoDump 工具是 MongoDB 数据库工具包的一部分,将在 CircleCI 上部署应用程序后安装。
[Azure Storage Blob](https://www.npmjs.com/package/@azure/storage-blob)
是一个 JavaScript 库,它使从 Node.js 应用程序消费 Microsoft Azure 存储 blob 服务变得容易。在本教程中,这个库已经作为我们项目的依赖项包含和安装了。
要生成并上传备份文件,在应用程序的根目录下创建一个名为backup.js
的新文件,并使用以下内容:
require("dotenv").config();
const exec = require("child_process").exec;
const path = require("path");
const {
BlobServiceClient,
StorageSharedKeyCredential,
} = require("@azure/storage-blob");
const backupDirPath = path.join(__dirname, "database-backup");
const storeFileOnAzure = async (file) => {
const account = process.env.ACCOUNT_NAME;
const accountKey = process.env.ACCOUNT_KEY;
const containerName = "dbsnapshots";
const sharedKeyCredential = new StorageSharedKeyCredential(
account,
accountKey
);
// instantiate Client
const blobServiceClient = new BlobServiceClient(
`https://${account}.blob.core.windows.net`,
sharedKeyCredential
);
const container = blobServiceClient.getContainerClient(containerName);
const blobName = "companies.bson";
const blockBlobClient = container.getBlockBlobClient(blobName);
const uploadBlobResponse = await blockBlobClient.uploadFile(file);
console.log(
`Upload block blob ${blobName} successfully`,
uploadBlobResponse.requestId
);
};
let cmd = `mongodump --forceTableScan --out=${backupDirPath} --uri=${process.env.MONGODB_URI}`;
const dbAutoBackUp = () => {
let filePath = backupDirPath + `/db-back-up-schedule/companies.bson`;
exec(cmd, (error, stdout, stderr) => {
console.log([cmd, error, backupDirPath]);
storeFileOnAzure(filePath);
});
};
dbAutoBackUp();
该文件中的内容导入了所需的依赖项,包括 Azure storage client SDK,并指定了存放备份文件的路径。接下来,它创造了:
cmd
是一个mongodump
命令,将被执行并用于生成备份文件。--out
标志指定文件所在文件夹的路径,而--uri
指定 MongoDB 连接字符串。*storeFileOnAzure()*
函数获取备份文件的精确绝对路径,并使用 Azure Storage Blob 客户端库将其上传到创建的 Azure 存储容器。*dbAutoBackUp()*
函数使用 JavaScript 中的[exec](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback)
()函数创建一个新的 shell,并执行指定的 MongoDump 命令。另外,
filepath`引用了生成的 bson 文件的确切位置(在本例中是 companies.bson)。
注: companiesdb
和companies.bson
表示在 MongoDB Atlas 上看到的应用程序的数据库名和表名。所以,如果你的数据库名是userdb
,表名是users
,那么你的文件路径将指向userdb/user.bson
文件。
创建和实施预定的管道
从头开始设置计划管道有两种不同的选择:
- 使用 API
- 使用项目设置
在本教程中,我们将使用 API,因此您需要:
- CircleCI API 令牌
- 存储库所在的版本控制系统的名称
- 您的组织名称
- CircleCI 上的当前项目 ID
要获取令牌,请转到您的 CircleCI 仪表盘,然后单击您的头像:
您将被重定向到您的用户设置页面。从那里,导航到个人 API 令牌,创建一个新的令牌,为您的令牌命名,并将其保存在某个安全的地方。
现在,从项目的根目录打开.env
文件,并添加:
VCS_TYPE=VERSION_CONTROL_SYSTEM
ORG_NAME=ORGANISATION_NAME
PROJECT_ID=PROJECT_ID
CIRCLECI_TOKEN=YOUR_CIRCLECI_TOKEN
MONGODB_URI=YOUR_MONGODB_URL
用您的值替换占位符:
VCS_TYPE
是你的版本控制系统,比如github
。ORG_NAME
是您的 GitHub 用户名或组织名称。- 是您在 CircleCI 上的项目 ID。对于示例项目是
db-clean-up
。 - 是你的 CircleCI 令牌。
- 从 MongoDB Atlas dashboard 中提取的 MongoDB URI 字符串。
接下来要做的是在项目的根目录下创建一个名为schedule.js
的新文件,并为其使用以下内容:
const axios = require("axios").default;
require("dotenv").config();
const API_BASE_URL = "https://circleci.com/api/v2/project";
const vcs = process.env.VCS_TYPE;
const org = process.env.ORG_NAME;
const project = process.env.PROJECT_ID;
const token = process.env.CIRCLECI_TOKEN;
const postScheduleEndpoint = `${API_BASE_URL}/${vcs}/${org}/${project}/schedule`;
async function scheduleDatabaseBackup() {
try {
let res = await axios.post(
postScheduleEndpoint,
{
name: "Database backup",
description: "Schedule database backup for your app in production",
"attribution-actor": "current",
parameters: {
branch: "main",
"run-schedule": true,
},
timetable: {
"per-hour": 30,
"hours-of-day": [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23,
],
"days-of-week": ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"],
},
},
{
headers: { "circle-token": token },
}
);
console.log(res.data);
} catch (error) {
console.log(error.response);
}
}
scheduleDatabaseBackup();
这段代码创建了一个名为**scheduleDatabaseBackup()**
的函数,将管道进度细节发送到 CircleCI API 。
有效载荷包括:
name
,即日程名称。它需要是独特的。description
是可选字段,用于描述日程安排。attribution-actor
可以是中立角色的system
,也可以是current
,它获取当前用户的权限(根据您使用的令牌)。- 对象指定触发哪个分支。它包括一个额外的值,用于检查何时运行管道。
timetable
定义运行预定管道的时间和频率。这里使用的字段是per-hour
、hours-of-day
和days-of-week
。
注意timetable
没有采用 cron 表达式,这使得它更容易被使用 API 推理的人解析。对于本教程,时间表设置为在一小时内运行 30 次,大约每 2 分钟运行一次。
该代码还将 CircleCI 标记传递给头部。
更新配置文件
在运行计划管道之前,我们需要更新 CircleCI 管道配置脚本。打开.circleci/config.yml
文件,将其内容替换为:
version: 2.1
orbs:
heroku: circleci/heroku@1.2.6
jobs:
build:
executor: heroku/default
steps:
- checkout
- heroku/install
- heroku/deploy-via-git:
force: true
schedule_backup:
working_directory: ~/project
docker:
- image: cimg/node:17.4.0
steps:
- checkout
- run:
name: Install MongoDB Tools.
command: |
npm install
sudo apt-get update
sudo apt-get install -y mongodb
- run:
name: Run database back up
command: npm run backup
parameters:
run-schedule:
type: boolean
default: false
workflows:
deploy:
when:
not: << pipeline.parameters.run-schedule >>
jobs:
- build
backup:
when: << pipeline.parameters.run-schedule >>
jobs:
- schedule_backup
该配置现在包括一个名为schedule_backup
的新作业。它使用 Docker 镜像安装 Node.js 和 MongoDB 工具。配置包括参数,并使用run-schedule
管道变量来检查何时运行工作流。
对于所有工作流,添加表示当run-schedule
为true
时运行它们并且除非run-schedule
为false
否则不运行其他工作流的when
表达式。
在 CircleCI 上创建更多的环境变量
在将所有更新添加到 GitHub 之前,将 MongoDB 连接字符串、Azure 帐户名和 key 作为环境变量添加到 CircleCI 项目中。
从当前项目管道页面,点击项目设置按钮。接下来,从侧面菜单中选择环境变量。添加这些变量:
ACCOUNT_KEY
是您的 Microsoft Azure 存储帐户密钥。ACCOUNT_NAME
是 Microsoft Azure 存储帐户名(本教程中为dbblobs
)。MONGODB_URI
是您的 MongoDB 连接字符串。
现在,更新 git,把你的代码推回 GitHub。
运行计划的管道
时间表配置文件已更新并准备就绪。要创建计划的管道,请从项目的根目录运行以下命令:
node schedule.js
输出应该如下所示:
{
"description": "Schedule database backup for your app in production",
"updated-at": "2022-03-07T07:07:25.408Z",
"name": "Database backup",
"id": "caa627c8-2768-4ac7-8150-e808fb566cc6",
"project-slug": "gh/CIRCLECI-GWP/db-back-up-schedule",
"created-at": "2022-03-07T07:07:25.408Z",
"parameters": { "branch": "main", "run-schedule": true },
"actor": {
"login": "daumie",
"name": "Dominic Motuka",
"id": "335b50ce-fd34-4a74-bc0b-b6455aa90325"
},
"timetable": {
"per-hour": 30,
"hours-of-day": [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23
],
"days-of-week": ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"]
}
}
查看您计划的运行中的管道
返回 CircleCI 上的 pipeline 页面。你的管道将每两分钟被触发一次。
这是在你的 Azure 存储帐户中打开容器以确认文件已成功上载的好时机。
附加部分:检索时间表列表和删除时间表
在最后一部分,您将了解到:
- 如何检索特定项目的所有时间表
- 如何删除任何计划
检索项目的时间表列表
要获取所有的时间表,在项目的根目录下创建一个名为get.js
的新文件。输入以下内容:
const axios = require("axios").default;
require("dotenv").config();
const API_BASE_URL = "https://circleci.com/api/v2/project";
const vcs = process.env.VCS_TYPE;
const org = process.env.ORG_NAME;
const project = process.env.PROJECT_ID;
const token = process.env.CIRCLECI_TOKEN;
const getSchedulesEndpoint = `${API_BASE_URL}/${vcs}/${org}/${project}/schedule/`;
async function getSchedules() {
let res = await axios.get(getSchedulesEndpoint, {
headers: {
"circle-token": `${token}`,
},
});
console.log(res.data.items[0]);
}
getSchedules();
这个代码片段获取并记录终端中的时间表,但只是时间表数组中的第一项。要查看所有项目,请将res.data.items[0]
替换为res.data.items
。
现在用node get.js
运行文件。您的输出应该如下所示:
{
description: 'Schedule database backup for your app in production',
'updated-at': '2022-03-07T10:49:58.123Z',
name: 'Database backup',
id: '6aa72c63-b4c4-4dc0-b099-b8661a7a2052',
'project-slug': 'gh/yemiwebby/db-back-up-schedule',
'created-at': '2022-03-07T10:49:58.123Z',
parameters: { branch: 'main', 'run-schedule': true },
actor: {
login: 'yemiwebby',
name: 'Oluyemi',
id: '7b490556-c1bb-4b42-a201-c1785a00005b'
},
timetable: {
'per-hour': 30,
'hours-of-day': [
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23
],
'days-of-week': [
'MON', 'TUE',
'WED', 'THU',
'FRI', 'SAT',
'SUN'
]
}
}
删除任何计划
删除计划需要其唯一的ID
。在本次演示中,我们可以使用上一节中的时间表中的ID
。
创建另一个名为delete.js
的文件,并将以下代码粘贴到其中:
const axios = require("axios").default;
require("dotenv").config();
const API_BASE_URL = "https://circleci.com/api/v2/schedule";
const vcs = process.env.VCS_TYPE;
const org = process.env.ORG_NAME;
const project = process.env.PROJECT_ID;
const token = process.env.CIRCLECI_TOKEN;
const schedule_ids = ["YOUR_SCHEDULE_ID"];
async function deleteScheduleById() {
for (let i = 0; i < schedule_ids.length; i++) {
let deleteScheduleEndpoint = `${API_BASE_URL}/${schedule_ids[i]}`;
let res = await axios.delete(deleteScheduleEndpoint, {
headers: { "circle-token": token },
});
console.log(res.data);
}
}
deleteScheduleById();
用从上一节提取的ID
替换YOUR_SCHEDULE_ID
占位符,并保存文件。接下来,从终端运行node delete.js
。输出:
{ message: 'Schedule deleted.' }
结论
在本教程中,您从 GitHub 下载了一个示例项目,并在通过 CircleCI 将它部署到 Heroku 平台之前,在您的机器上本地运行它。然后,您在 MongoDB 数据库中创建了一些记录,并使用 MongoDB 工具创建了一个脚本来生成数据库的备份集合。您将备份文件存储在 Microsoft Azure 上,并使用 CircleCI 的计划管道功能以合理的时间间隔自动执行文件备份过程。
本教程涵盖了调度管道的一个重要用例,因为它自动化了一项原本需要手动完成的任务。像安排数据库清理这样的任务太重要了,不能留给人类去做。它们占用了开发人员宝贵的时间,在繁忙或紧张的时候很容易忘记它们。为数据库清理安排管道解决了这些问题,因此您和您的团队有更多的时间来开发和发布应用程序。
我希望本教程对你有所帮助。完整的源代码可以在 GitHub 的这里找到。
Oluyemi 是一名拥有电信工程背景的技术爱好者。出于对解决用户日常遇到的问题的浓厚兴趣,他冒险进入编程领域,并从那时起将他解决问题的技能用于构建 web 和移动软件。Oluyemi 是一名热衷于分享知识的全栈软件工程师,他在世界各地的几个博客上发表了大量技术文章和博客文章。由于精通技术,他的爱好包括尝试新的编程语言和框架。
Oluyemi 是一名拥有电信工程背景的技术爱好者。出于对解决用户日常遇到的问题的浓厚兴趣,他冒险进入编程领域,并从那时起将他的问题解决技能用于构建 web 和移动软件。Oluyemi 是一名热衷于分享知识的全栈软件工程师,他在世界各地的几个博客上发表了大量技术文章和博客文章。作为技术专家,他的爱好包括尝试新的编程语言和框架。
使用预定管道自动清理数据库| CircleCI
原文:https://circleci.com/blog/schedule-recurring-database-cleanup/
本教程涵盖:
- 克隆示例应用程序
- 创建和配置计划管道
- 运行计划的管道
RESTful API 项目通常要求开发人员授予对特定资源的临时访问权。有时这发生在特定的时间间隔内,如几天或几个月。当权限过期时撤销权限可能意味着在身份验证过程中包含额外的逻辑,或者编写一个中间件函数来附加到安全端点。或者,这个逻辑可以抽象为一个单独的部分,并配置为定期检查和管理权限。
在本教程中,我将向您展示如何使用调度管道自动清理数据库和撤销授予 API 用户的临时访问权。为了简单起见,我为您创建了一个演示应用程序,用于克隆和部署到 Heroku。这个应用程序使用 MongoDB 来持久化数据。
本教程在 Node.js 应用程序中的为 MongoDB 安排数据库备份中继续。
先决条件
以下是您正确学习本教程所需的内容:
克隆演示应用程序
首先,运行以下命令来克隆演示应用程序:
git clone https://github.com/yemiwebby/db-cleanup-starter.git db-clean-up
接下来,进入新克隆的应用程序并安装其所有依赖项:
cd db-clean-up
npm install
此应用程序包含以下端点:
/create-permission
是一个端点,用于创建具有特定访问过期日期的用户。/check-access
将用于确保用户的临时访问结束日期早于当前日期。如果是,该用户的访问权限将被取消。/secured
端点将特定用户的电子邮件地址作为路由参数,并检查用户是否可以访问受保护的资源。/all-access
显示用户列表。
安装过程完成后,创建一个.env
文件,并用以下内容填充它:
MONGODB_URI=YOUR_MONGODB_URL
如果您愿意,您可以简单地运行以下命令,从 starter 项目中的.env.sample
文件复制内容:
cp .env.sample .env
当然,您需要用用于远程 MongoDB URI 的连接字符串替换YOUR_MONGODB_URL
占位符。本教程使用 MongoDB Atlas 数据库,您可以很容易地设置一个。接下来我将解释如何做。
创建 MongoDB Atlas 帐户和数据库
在这里创建一个免费的 Atlas 帐户,并按照说明部署一个免费层集群。设置好集群和数据库用户后,打开并编辑.env
文件。
用从您的 MongoDB Atlas 仪表板中提取的连接字符串替换YOUR_MONGODB_URL
占位符:
MONGODB_URI=mongodb+srv://<username>:<password>@<clustername>.mongodb.net/<dbname>?retryWrites=true&w=majority
用集群的值替换<username>
、<password>
、<clustername>
和<dbname>
。
运行演示应用程序
正确创建和配置数据库后,打开一个终端并运行演示应用程序:
npm run start
您将得到以下输出:
> db-cleanup-starter@1.0.0 start
> node server.js
Server is running at port 3000
Connected successfully
创建有权限的用户
在 Postman 中,使用以下 JSON 数据向http://localhost:3000/create-permission
端点发出一个POST
请求:
{
"username": "sample",
"email": "sample@mail.com",
"endDate": "2021-07-30",
"hasAccess": true
}
创建更多用户:
{
"username": "demo",
"email": "demo@mail.com" ,
"endDate": "2021-05-22",
"hasAccess": true
}
{
"username": "webby",
"email": "webby@mail.com" ,
"endDate": "2022-07-30",
"hasAccess": true
}
您刚刚创建了具有用于临时访问的endDate
值和一个表示他们能够访问数据库的hasAccess
标志的用户。默认设置为false
,如/models.js
中的UserSchema
所定义:
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
endDate: {
type: Date,
default: Date.now(),
},
hasAccess: {
type: Boolean,
default: false,
},
});
const User = mongoose.model("User", UserSchema);
module.exports = User;
检索安全资源
此时,所有创建的用户都可以通过发送一个带有电子邮件地址作为路由参数的GET
请求来查看和检索来自/secured
端点的安全资源。
在本地测试check-access
端点
在./routes.js
文件中定义的/check-access
端点将检查每个用户的endDate
是否小于当前日期,如果是,则撤销其访问。
app.get("/check-access", async (request, response) => {
const result = await userModel.updateMany(
{ endDate: { $lt: Date.now() } },
{ hasAccess: false }
);
try {
response.send(result);
} catch (error) {
response.status(500).send(error);
}
});
换成邮差试试看。
最后,检索所有用户的列表。
此时,endDate
小于当前日期的用户已经被拒绝访问,如用hasAccess
标志所指示的。向/check-access
端点发送 GET HTTP 请求的过程是我们想要自动化的。
在 Heroku 上创建应用程序
接下来,在 Heroku 上创建一个新的应用程序来托管和运行 Node.js 项目。前往 Heroku 仪表盘开始。点击新增,然后点击新增 App 。在表单中填写您的应用程序和您所在地区的名称。
注:Heroku 上的应用名称是唯一的。选择一个可用的并记下来。
点击创建应用按钮。您将被重定向到新创建的应用程序的部署视图。
接下来,创建一个配置变量来引用前面从 MongoDB Atlas 仪表板中提取的 MongoDB URI。为此,导航到设置页面,向下滚动,并单击显示配置变量按钮。
如下所示指定键和值,完成后点击添加。
最后,您需要检索 Heroku 帐户的 API 密钥。此密钥将用于连接您的 CircleCI 管道和 Heroku。要获取您的 API 密钥,请打开帐户设置页面。
滚动到 API 键部分。
点击显示按钮,复制 API 密钥。将它保存在您以后可以轻松找到的地方。
添加管道配置脚本
接下来,我们需要为 CircleCI 添加管道配置。管道将由安装项目依赖项和编译生产应用程序的步骤组成。
在项目的根目录下,创建一个名为.circleci
的文件夹。在该文件夹中,创建一个名为config.yml
的文件。在新创建的文件中,添加以下配置:
version: 2.1
orbs:
heroku: circleci/heroku@1.2.6
jobs:
build:
executor: heroku/default
steps:
- checkout
- heroku/install
- heroku/deploy-via-git:
force: true
workflows:
deploy:
jobs:
- build
这种配置引入了 Heroku orb circleci/heroku
,它自动提供对一组健壮的 Heroku 作业和命令的访问。其中一个任务是heroku/deploy-via-git
,它直接从你的 GitHub repo 将你的应用程序部署到你的 Heroku 账户。
接下来,在 GitHub 上建立一个存储库,并将项目链接到 CircleCI。查看将项目推送到 GitHub 以获得分步说明。
登录您的 CircleCI 帐户。如果你注册了你的 GitHub 账户,你所有的库都可以在你项目的仪表盘上看到。
点击为您的database-clean-up
项目设置项目。
将提示您几个关于配置文件的选项。选择use the .circleci/config.yml in my repo
选项。在 GitHub 上输入你的代码所在的分支名称,然后点击设置项目按钮。
您的第一个工作流将开始运行,但会失败。这是因为您没有提供 Heroku API 密钥。你现在可以弥补了。
点击项目设置按钮,然后点击环境变量。添加这两个新变量:
HEROKU_APP_NAME
是 Heroku (database-clean-up
)中的应用名称HEROKU_API_KEY
是您从帐户设置页面获取的 Heroku API 密钥
从失败的中选择重新运行工作流以重新运行 Heroku 部署。这一次,您的工作流将成功运行。
要确认工作流是否成功,您可以在浏览器中打开新部署的应用程序。您的应用程序的 URL 应该是这样的格式https://<HEROKU_APP_NAME>.herokuapp.com/
。对于这个教程项目,我使用了:“https://database-clean-up . heroku app . com”
创建和实施预定的管道
如 CircleCI 官方文件中所述,设置预定管道有两种不同的选择:
- 使用 API
- 使用项目设置
出于本教程及其目标的考虑,我们将使用 API。为了方便起见,我们需要 CircleCI API 令牌、您的存储库所在的版本控制系统的名称、您的组织名称以及 CircleCI 上的当前项目 id。要获得代币,请导航至您的 CircleCI 仪表盘并点击您的头像:
您将被重定向到您的用户设置页面。从那里,导航到个人 API 令牌,创建一个新的令牌,为您的令牌命名,并将其保存在某个安全的地方。
现在,从项目的根目录打开.env
文件,并添加以下内容:
VCS_TYPE=VERSION_CONTROL_SYSTEM
ORG_NAME=ORGANISATION_NAME
PROJECT_ID=PROJECT_ID
CIRCLECI_TOKEN=YOUR_CIRCLECI_TOKEN
MONGODB_URI=YOUR_MONGODB_URL
用适当的值替换占位符:
VCS_TYPE
:你的版本控制系统,比如github
。ORG_NAME
:您的 GitHub 用户名或组织名称- 你在 CircleCI 上的项目 Id。在我们的例子中
- 你的戒指令牌
- 从 MongoDB Atlas dashboard 中提取的 MongoDB URI 字符串。
接下来要做的是在项目的根目录下创建一个名为schedule.js
的新文件,并使用以下内容:
const axios = require("axios").default;
require("dotenv").config();
const API_BASE_URL = "https://circleci.com/api/v2/project";
const vcs = process.env.VCS_TYPE;
const org = process.env.ORG_NAME;
const project = process.env.PROJECT_ID;
const token = process.env.CIRCLECI_TOKEN;
const postScheduleEndpoint = `${API_BASE_URL}/${vcs}/${org}/${project}/schedule`;
async function scheduleDatabaseBackup() {
try {
let res = await axios.post(
postScheduleEndpoint,
{
name: "Database backup",
description: "Schedule database backup for your app in production",
"attribution-actor": "current",
parameters: {
branch: "main",
"run-schedule": true,
},
timetable: {
"per-hour": 30,
"hours-of-day": [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23,
],
"days-of-week": ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"],
},
},
{
headers: { "circle-token": token },
}
);
console.log(res.data);
} catch (error) {
console.log(error.response);
}
}
scheduleDatabaseBackup();
在这里,我们创建了一个名为**scheduleDatabaseBackup()**
的函数,将管道进度细节发布到 CircleCI API 。指定的有效载荷有:
name
:这是日程名称。它需要是独特的。description
:可选字段,用于描述日程安排。- 有效负载还包括一个
attribution-actor
,它可以是一个中立角色的system
,也可以是获取当前用户权限的current
(根据您使用的令牌) parameters
:在此对象中指定了要触发的分支和一个检查何时运行管道的附加值。- 这是我们定义运行预定管道的时间和频率的地方。这里使用的字段是
per-hour
、hours-of-day
和days-of-week
。请注意,这没有采用 cron 表达式,这使得它更容易被使用 API 推理的人解析。在这里,我们将计划设置为在一小时内运行 30 次。这相当于大约每两分钟一次。
最后,CircleCI 标记在头中传递。
更新配置文件
在运行计划管道之前,我们需要更新 CircleCI 管道配置脚本。打开.circleci/config.yml
文件,将其内容替换为:
version: 2.1
orbs:
heroku: circleci/heroku@1.2.6
jobs:
build:
executor: heroku/default
steps:
- checkout
- heroku/install
- heroku/deploy-via-git:
force: true
schedule_backup:
working_directory: ~/project
docker:
- image: cimg/node:17.4.0
steps:
- checkout
- run:
name: Install MongoDB Tools.
command: |
npm install
sudo apt-get update
sudo apt-get install -y mongodb
- run:
name: Run database back up
command: npm run backup
parameters:
run-schedule:
type: boolean
default: false
workflows:
deploy:
when:
not: << pipeline.parameters.run-schedule >>
jobs:
- build
backup:
when: << pipeline.parameters.run-schedule >>
jobs:
- schedule_backup
这里,我们包含了一个名为schedule_backup
的新任务。它使用 docker 镜像来安装 Node.js 和 MongoDB 工具。此外,我们包含参数并使用名为run-schedule
的管道变量来检查何时运行工作流。对于所有工作流,添加when
表达式,表示当run-schedule
为true
时运行这些工作流,除非run-schedule
为false
否则不运行其他工作流。
在 CircleCI 上创建更多的环境变量
在将所有更新添加并推送到 GitHub 之前,您将添加 MongoDB 连接字符串、Azure 帐户名和 Key 作为 CircleCI 项目上的环境变量。从当前项目管道页面,点击项目设置按钮。接下来,从侧菜单中选择环境变量,并添加以下变量:
ACCOUNT_KEY
:这是你的 Microsoft Azure 存储帐户密钥ACCOUNT_NAME
:这是微软 Azure 存储帐户名,在本教程中为dbblobs
MONGODB_URI
:你的 MongoDB 连接字符串。
此时,您的环境变量页面应该如下所示:
现在,更新 git,把你的代码推回 GitHub。
为数据库清理运行计划的管道
准备好调度配置文件后,您就可以运行管道了。从项目的根目录中,运行以下命令:
node schedule_pipeline
或者您可以使用在package.json
文件中指定的 npm 脚本命令:
npm run schedule
您的输出应该如下所示:
{
description: 'Check and revoke permissions assigned to users.',
'updated-at': '2022-02-06T13:54:01.993Z',
name: 'Check and Change permission',
id: '821f4190-7261-4433-ae08-d5d41d700879',
'project-slug': 'gh/CIRCLECI-GWP/database-clean-up',
'created-at': '2022-02-06T13:54:01.993Z',
parameters: { branch: 'main', 'run-schedule': true },
actor: {
login: 'daumie',
name: 'Dominic Motuka',
id: '335b50ce-fd34-4a74-bc0b-b6455aa90325'
},
timetable: {
'per-hour': 30,
'hours-of-day': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
'days-of-week': [ 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN' ]
}
}
更新配置文件
导航到.circleci/config.yml
文件并更新其内容,如下所示:
version: 2.1
orbs:
heroku: circleci/heroku@1.2.6
jobs:
build:
executor: heroku/default
steps:
- checkout
- heroku/install
- heroku/deploy-via-git:
force: true
permission:
docker:
- image: cimg/node:17.4.0
steps:
- run:
name: Check access and update permissions for users
command: "curl https://<YOUR_HEROKU_APP_NAME>.herokuapp.com/check-access"
parameters:
run-schedule:
type: boolean
default: false
workflows:
deploy:
when:
not: << pipeline.parameters.run-schedule >>
jobs:
- build
check_permissions:
when: << pipeline.parameters.run-schedule >>
jobs:
- permission
这个配置包括一个名为permission
的新作业。它使用 Docker 映像来安装 Node.js,并运行 curl 命令来调用部署的应用程序的check-access
端点。该配置还包括参数,并使用名为run-schedule
的管道变量来检查何时运行工作流。对于所有工作流,添加when
表达式,表示当run-schedule
为true
时运行这些工作流,除非run-schedule
为false
否则不运行其他工作流。
现在,更新 git,把你的代码推回 GitHub。在 CircleCI 上查看您的管道。
管道将每两分钟运行一次。这个间隔很短,但只是为了演示。
在生产中,您不会希望在如此短的时间间隔内触发管道。您可以修改./schedule_pipeline.js
文件的内容,并根据您的工作流程进行调整。每天晚上运行这个数据库清理工作流可能是个好主意。
结论
预定的管道可以在特定的时间间隔触发您的构建。让自动化处理应用程序的持续集成、部署和自动化操作。利用你节省下来的时间,专注于实现更多的特性,编写更多的测试,更快地修复 bug。你的团队会喜欢的!
关于 CircleCI 上预定管道的更多信息,请参考官方文件。
我真的希望这对你有所帮助。完整的源代码可以在 GitHub 上找到。
Oluyemi 是一名拥有电信工程背景的技术爱好者。出于对解决用户日常遇到的问题的浓厚兴趣,他冒险进入编程领域,并从那时起将他解决问题的技能用于构建 web 和移动软件。Oluyemi 是一名热衷于分享知识的全栈软件工程师,他在世界各地的几个博客上发表了大量技术文章和博客文章。由于精通技术,他的爱好包括尝试新的编程语言和框架。
Oluyemi 是一名拥有电信工程背景的技术爱好者。出于对解决用户日常遇到的问题的浓厚兴趣,他冒险进入编程领域,并从那时起将他的问题解决技能用于构建 web 和移动软件。Oluyemi 是一名热衷于分享知识的全栈软件工程师,他在世界各地的几个博客上发表了大量技术文章和博客文章。作为技术专家,他的爱好包括尝试新的编程语言和框架。