CircleCI-博客中文翻译-五-

CircleCI 博客中文翻译(五)

原文:CirecleCI Blog

协议:CC BY-NC-SA 4.0

使用计划管道运行常规安全扫描| CircleCI

原文:https://circleci.com/blog/scheduled-security-scans/

安全性是应用程序开发中至关重要的一部分,但在攻击者利用系统中的漏洞之前,它可能会被忽略。安全漏洞的后果会损害应用程序的完整性以及公司的声誉和收入。软件架构师和工程师需要特别注意保护他们工作的系统。

运行基于安全性的测试并将安全性自动化到 CI/CD 工作流中是 DevSecOps 背后的灵感,这是 DevOps 方法在应用安全领域的扩展。

在本文中,我将带领您完成应用程序中的自动化安全检查。我将向您展示如何在一个 web 表单上运行一个测试,将恶意域注入到 URL 字段中。测试的目标是破坏 web 表单。为了阻止攻击,web 表单受到了逻辑保护,该逻辑检查用户是否试图进入恶意域。我将向您展示如何使用调度管道来定期运行安全测试。

先决条件

要完成本教程,您需要准备好以下内容:

一旦你设置好这些,你就可以开始了。

获取 Webshrinker 凭据

Webshrinker 是由 DNS 安全公司 DNSFilter 拥有的 AI 驱动的域名分类系统。Webshrinker 能够识别威胁域,并根据威胁类别对其进行标记。网络钓鱼、恶意软件和僵尸网络只是 Webshrinker 可以识别的三种威胁类型。

我们在项目中保护的表单在其 URL 字段中采用完全限定的域名。它会将域名发送到 Webshrinker API 进行威胁扫描。如果结果表明它是恶意的,Webshrinker 返回一个威胁标识符。该表单使用威胁标识符拒绝处理域条目。

在这个演示中,您需要一个 API 密匙和 API 密码来使用 Webshrinker。一旦您创建了一个帐户,您就可以通过转到 API 访问密钥页面并点击创建 API 密钥按钮来创建新的 API 凭证。生成您的 API 秘密和令牌。

您将在下一步中使用您的凭据。

克隆和运行演示应用程序

第一步是在本地运行演示 web 表单并检查其行为。您可以从这个存储库中克隆表单的代码,方法是在系统的任何地方运行这个命令:

git clone -b base-project --single-branch https://github.com/coderonfleek/scheduled-security-scan.git 

一旦您的系统上有了代码,通过运行以下命令安装依赖项:

cd scheduled-security-scan
npm install 

现在转到项目根目录下的index.html文件中的第65行和第66行,分别用 API 密匙和 API 秘密替换占位符。

依赖项安装完成后,使用以下命令运行应用程序:

npm start 

应用程序启动,您可以在https://localhost:5000查看 web 表单。

在电子邮件字段中输入电子邮件地址。在 URL 字段中,输入一个类似于facebook.com的安全域,然后点击提交。你会在表格的右边得到一个安全的回答。

Regular domain test

现在,使用这个基于威胁的域名:selcdn.ru测试表单的 URL 字段。请,不要在您的浏览器中直接输入该域名。您将收到一条威胁警报消息。

Malicious Domain test

添加安全测试

下一步是编写自动执行安全检查的测试。您将使用 Google 的木偶师添加一些自动化功能测试。木偶师模拟真实世界的用户填写表单的方式。

在项目的根目录下添加一个名为login.test.js的新文件,并输入以下代码:

const puppeteer = require("puppeteer");

const user_email = "test@example.com";
const non_threat_site = "facebook.com";
const malicious_site = "selcdn.ru";
const phishing_site = "mail.glesys.se";
const expected_safe_site_message = "Entry clean, process form";
const expected_threat_site_message = "Threat Detected. Do not Process";

test("Check Non-threat Site Entry", async () => {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();

    await page.goto("http://localhost:5000");

    await page.type("#userEmail", user_email);
    await page.type("#userSite", non_threat_site);
    await page.click("#submitButton");

    let messageContainer = await page.$("#infoDisplay");
    await page.waitForTimeout(4000);
    let value = await messageContainer.evaluate((el) => el.textContent);

    console.log(value);

    expect(value).toBe(expected_safe_site_message);
  } finally {
    await browser.close();
  }
}, 120000);

test("Check Malicious Site Entry", async () => {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();

    await page.goto("http://localhost:5000");

    await page.type("#userEmail", user_email);
    await page.type("#userSite", malicious_site);
    await page.click("#submitButton");

    let messageContainer = await page.$("#infoDisplay");
    await page.waitForTimeout(4000);

    let value = await messageContainer.evaluate((el) => el.textContent);

    console.log(value);

    expect(value).toBe(expected_threat_site_message);
  } finally {
    await browser.close();
  }
}, 120000); 

这个文件包含两个测试用例。第一个检查像facebook.com这样的非威胁域没有被系统阻止。这可以防止您的安全实现过度阻止常规域。

第二个测试用例通过使用一个示例恶意域来检查恶意条目。如果表单阻止了这个域,测试就通过了。如果恶意域未被阻止,测试将失败。

保存文件,并在终端中转至项目的根目录。确保应用程序正在另一个 shell 中运行;测试需要它才能成功运行。运行以下命令:

npm run test 

测试运行完成后,您将在 CLI 中看到如下结果:

MACs-MBP-2:scheduled-security-scan mac$ npm run test

> xss-attack@1.0.0 test /Users/mac/Documents/CircleCiProjects/2022/scheduled-security-scan
> jest

  console.log
    Entry clean, process form

      at Object.<anonymous> (login.test.js:26:13)

 PASS  ./login.test.js (109.034 s)
  ✓ Check Non-threat Site Entry (80318 ms)
  ✓ Check Malicious Site Entry (8210 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        120.252 s
Ran all test suites.
  console.log
    Threat Detected. Do not Process

      at Object.<anonymous> (login.test.js:54:13) 

开发人员可能倾向于编写与他们的代码能力相匹配的测试。作为最佳实践,一定要将开发和测试团队分开。这种实践让测试团队编写详尽的测试,并保护开发人员免受其害。

添加管道配置脚本

为了自动化测试过程,我们可以使用 CircleCI 构建一个持续集成(CI)管道。

要设置 CI 管道,您需要一个管道配置脚本。您可以将这个脚本添加到一个.circleci/config.yml文件中。在项目的根目录下创建文件,并输入:

version: 2.1
jobs:
  build:
    working_directory: ~/repo
    docker:
      - image: cimg/node:14.18-browsers
    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 the application
          command: node server.js
          background: true
      - run:
          name: Run tests
          command: npm run test 

该配置脚本:

  • 拉进一个安装了浏览器和 Node.js 的 Docker 映像
  • 更新npm并安装所需的项目依赖项
  • 在后台运行应用程序,以便 Cypress 测试可以加载它
  • 运行测试

保存文件并将项目推送到 GitHub

接下来,将存储库添加为 CircleCI 项目。

Add Project - CircleCI

Select Branch - CircleCI

一旦在 CircleCI 上建立了项目,测试就会立即运行。当构建完成时,您将获得一个成功的构建状态。

Build Success - CircleCI

单击构建链接查看构建详细信息。

Build Details - CircleCI

在 CircleCI 上设置预定管道

通常,您的 CI 管道只有在对您在设置管道时使用的远程代码存储库进行新的提交时才会运行。为了从我们编写的测试这样的安全扫描中获得最大收益,即使没有新代码被推送,应用程序的安全状态也应该被更新。

这意味着我们的管道需要定期运行,就像一个cron作业,以准确地报告应用程序的安全状态。为了保持效率,最好不要将安全性测试与检查错误或验证应用程序功能的功能性测试混在一起。

通过 CircleCI 上的计划管道,您可以将管道配置为仅在特定的一天运行,或者在一周的所有天的特定时间运行。

对于本教程,我们将设置管道在一周的每一天每五分钟运行一次。为此项目设置的时间间隔不是基于任何实际情况、详细考虑或最佳实践。这只是为了演示管道在演示期间定期运行。

要配置您的管道按计划运行,请转到 CircleCI 并选择项目设置,然后触发

在触发页面,点击添加预定触发显示触发表单。填写表单,将管道配置为每天每五分钟运行一次。

Set up trigger - CircleCI

点击保存触发器,您的触发器将被创建。

返回到管道页面,等待至少五分钟。你的管道会被 CircleCI 系统触发。

Scheduled Runs

每次运行之间的五分钟间隔表明配置完全有效。

结论

在本教程中,我演示了如何使用 CircleCI 的调度管道在您的应用程序上运行定期安全扫描。您可以让系统不断运行安全检查,并在出现问题或易受攻击时进行报告,而不是等待新代码的生成。

安全扫描只是这种自动化的众多用例之一。日志和数据归档等清理任务也是配置时间表的理想选择。与您的团队分享您所学到的知识,并开始尝试预定的管道。

编码快乐!


Fikayo Adepoju 是 LinkedIn Learning(Lynda.com)的作者、全栈开发人员、技术作者和技术内容创建者,精通 Web 和移动技术以及 DevOps,拥有 10 多年开发可扩展分布式应用程序的经验。他为 CircleCI、Twilio、Auth0 和 New Stack 博客撰写了 40 多篇文章,并且在他的个人媒体页面上,他喜欢与尽可能多的从中受益的开发人员分享他的知识。你也可以在 Udemy 上查看他的视频课程。

阅读 Fikayo Adepoju 的更多帖子

使用 Percy orb | CircleCI 进行并行可视化测试

原文:https://circleci.com/blog/seamless-parallelized-visual-testing-with-the-percy-circleci-orb/

我们在 Percy 的目标是帮助团队满怀信心地部署他们的应用程序,使其看起来完全符合预期。

我们的视觉测试和审查解决方案会自动为您检查 UI,而不是在浏览器和屏幕之间手动检查。将 Percy 与您的测试相集成,我们定制的渲染基础设施会在您每次进行更改时生成您的用户界面快照。

该工作流可以无缝地融入您现有的 CI/CD 套件中,帮助您消除每次提交时出现视觉倒退的风险。借助 Percy,您还可以在并行 CI 构建的同时运行可视化测试——而我们的 CircleCI orb 让这变得前所未有的简单。

珀西·切尔莱西球体

因为 Percy 在 CI 服务的下游运行,所以它需要一个清晰的信号来指示构建何时完成,这样它就知道何时拥有完整的快照集。我们的 orb 确保没有一个迟到的测试者没有完成。

Percy Orb 的典型工作流程如下:

parallel builds on circleci

您的测试套件开始了,将运行测试的工作分成多个测试运行程序。一旦它们都完成了,就调用percy/finalize_all命令,触发完成一个构建并将构建中的所有快照视为已接收。

环境变量

一旦您创建了一个与 CircleCI 集成的 Percy 项目,请在您的项目设置下检索您的 PERCY_TOKEN。

PERCY_PARALLEL_TOTAL设置为-1。这告诉 Percy,我们不知道这次运行的并行化程度如何,但是当我们听到来自 orb 的percy/finalize_all命令时,我们将认为我们的 Percy 构建已经完全完成。

工作流配置

您的 CircleCI 工作流配置导入了percy/agent orb,一旦所有测试完成,它就执行percy/finalize_all命令。

概括地说,.circleci/config.yml文件应该是这样的:

version: 2.1

orbs:
 percy: percy/agent@volatile

jobs:
 run_tests:
   steps:
     - attach_workspace:
         at: .
     - run: echo "your tests run here"

workflows:
 test:
   jobs:
     - run_tests:
         parallelism: 3
     - percy/finalize_all:
         requires:
           - run_tests 

运行测试

一旦您将 Percy CircleCI Orb 添加到您的配置文件(.circleci/config.yml)中,每当您的测试运行时,快照将跨浏览器和屏幕尺寸呈现。

</blog/media/2018-12-06-diff-min.mp4>

在上面的例子中,发生变化的像素以红色“视觉差异”突出显示 Percy 的 SDK使得向任何前端框架、测试框架、组件库或风格指南添加可视化测试变得容易。关于将我们的 orb 整合到你的 CircleCI 配置文件中的更多信息,请访问 Percy docs

我们很高兴成为 CircleCI 的技术合作伙伴,使视觉测试更加高效和可扩展,我们期待着为未来的用例发展我们的 Orb。

在 2019 年 1 月 22 日星期二上午 10:00 PST/18:00 UTC 举行的我们的网络研讨会“Percy 和 CircleCI orbs 在您的 CI/CD 工作流程中的视觉测试”中,了解更多关于 CircleCI 和 Percy 的信息


大卫·琼斯是珀西 T2 公司的工程总监。他喜欢与工程师一起工作,开发工具,让他们在发布软件时更有信心。你可以跟着他 @d_jones

如何保护您的 CI 渠道| CircleCI

原文:https://circleci.com/blog/secure-ci-pipeline/

许多企业仍在努力获得安全权限。为了保护他们的业务,他们必须在整个基础设施和应用程序生命周期中关注安全性,包括持续集成 (CI)。

随着开发人员过渡到 DevOps 工作方式,他们变得更加自主,越来越多的人需要访问生产系统。这些开发人员不是安全专家,安全团队也不是开发人员,但是随着自主性的增加,开发团队必须学会如何安全地交付高质量的应用程序。

为企业保护 CI/CD 管道

如今,大多数组织不再只有防火墙或外部安全边缘,而是构建了多个防御层来最小化安全风险。开发人员添加安全机制的最佳地方之一是在持续集成/持续交付(CI/CD)管道中。这些管道可以包括许多全自动保护措施,以满足您的许多安全要求。

在本帖中,我们将描述安全机制,您可以使用这些机制来更好地保护 CI/CD 管道,同时使开发人员能够快速、安全地推送他们的代码。

瑞士奶酪的型号是什么?

许多组织使用的一系列安全措施通常会提供多层保护。然而,这些层中的每一层在其防御结构中都包含弱点(漏洞)。我们称之为“瑞士奶酪模式”这是考虑层和每层中的孔的一种方便的方法。理想情况下,这些层排成一行,这样一层中的孔就不会在下一层的相同位置。换句话说,漏洞不会一直存在。你希望你的三明治不断覆盖安全奶酪。

瑞士奶酪模型是由 James T. Reason 和 Dante Orlandella 在研究包括挑战者号航天飞机在内的几次工程灾难中的“累积行为效应”和人为错误的主题时推出的。

将瑞士奶酪模型应用于软件应用程序和基础设施可以帮助您理解为什么额外的防御层仍然会失败。将每一层防御看做一片瑞士奶酪,可以看出如果这些漏洞完全排成一行,安全仍然允许非预期的系统访问。在瑞士奶酪模型中,任何有人类参与的系统仍然可能失败。很简单,人类会犯错。

人的因素意味着这些防御层本质上不仅仅是技术性的。社会工程攻击直接针对员工,让他们透露密码或个人信息。宣传活动和培训有助于这些员工认识到这种企图。然而,人类是这个链条中最薄弱的一环。

与其试图阻止人类犯错,不如将人类完全排除在等式之外更有意义。通过自动化安全保护,企业降低了安全层漏洞的风险。

使用 CI/CD 加强安全层

CI/CD 是实现不同安全层的好地方,因为 CI/CD 完全是自动化。这是通过自动化流程去除不安全的人为因素并减少层中漏洞的自然位置。

实施 CI/CD 使您能够消除对某些任务的手动、人工访问。例如,自动创建虚拟服务器后,您的工程师不再需要权限来手动创建这些服务器。将这种方法扩展到您的所有基础架构和应用程序,您可以确保您的整个环境构建一致。确保为这些管道实现适当的安全机制,例如只授予最低特权权限。

保护 CI/CD 管道层的其他选项包括:

  • 常规扫描
  • 安全测试
  • 监视

保护管道的扫描方法

通过 CI/CD 完全部署您的基础设施和应用程序的一个好处是,您的整个环境都是用代码描述的。您可以自动扫描这些代码,以及作为 CI/CD 管道一部分的其他工件,从而毫不费力地解决安全性问题。

通常与持续集成相关的不同阶段是构建、测试和发布可部署的工件。在测试阶段,除了单元和系统测试之外,您还可以实现安全扫描。

许多免费的开源工具可用于不同类型的扫描。您应该在管道中实施的扫描包括:

  • 扫描源代码
  • 扫描容器
  • 扫描基础设施

扫描源代码。源代码可以包含不同类型的漏洞。如果外部数据(如用户输入)没有得到适当的清理,恶意数据可能会导致漏洞,如 SQL 注入或跨站点脚本。高级扫描工具可以在您的源代码中检测到此类模式。

外部依赖项,例如通过 maven 或 npm 包含在应用程序中的库,也可能包含此类漏洞。使用依赖性漏洞扫描工具来分析已知 CVE 漏洞的依赖性。

最后,扫描源代码,看是否使用了明文秘密,比如数据库密码或令牌。此类机密必须始终加密或存储在外部 secret manager 解决方案中,以最大限度地减少这些值的泄露。

扫描集装箱。许多组织通过容器部署他们的应用程序。容器遵循分层模式,应用程序构建在公共容器上,比如 Docker Hub。扫描每一层以减少在基础设施中部署已知漏洞的机会。

扫描基础设施。基础设施可以通过许多不同的方式进行配置,有些配置比其他配置更安全。是否启用加密通常就像云中的复选框一样简单。端点可以配置加密流量或不加密流量(HTTPS)。通过基础架构安全扫描,确保您的基础架构代码符合您组织中定义的安全标准和最佳实践。

保护 CI/CD 管道的测试

CI/CD 管道可以执行几种不同的安全测试,作为其工作流的一部分。这种测试通常分为两类。

  • 静态应用程序安全性测试
  • 动态应用安全测试

第一类是静态应用程序安全测试(SAST),包括上一节描述的大多数扫描工具。这种扫描可以在将任何东西部署到实际运行时环境之前,在管道的早期发现漏洞。这层防御可以防止安全问题进入您的生产环境。

除了防止您环境中的问题,这还为您的工程师提供了早期反馈。他们可以在积极地处理代码的同时迅速地减轻任何顾虑。这种实践被称为左移:在软件开发生命周期中尽可能早地检测和缓解问题。

第二类是动态应用程序安全测试(DAST ),扫描当前部署的基础设施或应用程序。该第二扫描层识别一些与 SAST 扫描相似的弱点以及不同的弱点。您可以在一个临时环境中运行这些扫描——同样,防止问题出现在您的生产环境中——但是您也应该在您的生产环境中运行这些扫描。

实施所有这些测试的组合有助于实现多层防御。最重要的是,所有这些扫描都可以完全自动化。

监控和审计,确保管道安全

谈到安全性时,不要忽视 CI/CD 管道本身。管道是一个关键系统,具有对基础设施和应用程序进行更改的权限。该系统中的漏洞会危及您的整个环境。监控和审计您的管道,以确保它们按预期工作并且安全。

与您的管道相关的风险包括访问密钥没有轮换,以及给了错误的人访问权限。监控工具检查问题并整合警报,以提供正确的见解并确保漏洞得到解决。他们还会在出现问题时提醒相关人员。

考虑实施自动补救措施,例如撤销访问权限和禁用访问密钥,以提高安全性。使用这些审计工具审查管道本身的任何变更,并实施一个流程,包括自动检查和人工审查,以保护管道质量。

包裹

我们研究了许多不同的方法来帮助保护您的基础架构和应用程序。

  • 实施几层防御措施效果很好,但是请记住瑞士奶酪模型,并注意您的安全中的潜在漏洞。
  • 要明白,当涉及到安全性时,人是最大的风险,并尽可能地自动化您的流程。
  • 跨您的基础架构和应用程序生命周期进行扫描、测试、监控和审计。还要记住,不要忽视管道本身的管理和保护。

既然您已经了解了管道安全的好处以及 CircleCI 的适用范围,为什么不试一试呢?立即注册 CircleCI 免费试用以简化您的开发工作,并让您的团队迁移到现代云。

需要关于 CI/CD 安全性的更多信息?参见CI/CD 安全性和开发安全操作终极指南

软件供应链:它是什么以及如何保证它的安全

原文:https://circleci.com/blog/secure-software-supply-chain/

随着国际供应网络瓶颈导致的消费品短缺和价格上涨变得越来越普遍,全球供应链及其脆弱性已成为许多人最关心的问题。对于开发者来说,最近几个引人注目的软件安全漏洞凸显了类似供应商网络的固有风险:软件供应链。

软件应用程序不再完全由定制代码构建。相反,它们是由开放源码组件和库的复杂网络组成的,其中大部分继承了其他第三方来源的功能。这种依赖链使开发人员能够利用他们喜欢的工具,并使团队能够以令人难以置信的速度将功能软件交付给他们的用户,但它也使组织和他们的客户暴露于他们直接控制之外的变化所带来的漏洞。

在本文中,我们将仔细研究什么是软件供应链,它给软件生产商带来了什么风险,以及您的组织如何利用持续集成来自动化安全性和合规性检查,这可以让您充分利用开源生态系统,同时降低供应链风险。

使用新的触发器和权限控制来管理您的生态系统中的变化

Learn More

什么是软件供应链?

很难想象今天有哪个组织的日常运作不依赖于多个软件。在几乎所有的情况下,软件都依赖于预先构建的外部组件。因此,默认情况下,组织继承了其软件所有部分的软件供应链。

软件供应链由代码、配置、专有和开源二进制文件、库、插件和容器依赖项组成。它还包括构建编排器和工具,如汇编器、编译器、代码分析器和存储库、安全、监控和日志操作工具。软件供应链还包括软件开发中涉及的人员、组织和过程。

由于软件供应链可以变得如此庞大和复杂,规避风险的企业和政府通常会要求一份描述部分或大部分供应链的软件物料清单 (SBOM)。例如,2021 年 5 月,拜登政府发布了一项行政命令,要求与联邦政府合作的软件供应商为他们的项目提供一个 SBOM。

考虑到内部、外包、专有或开源软件通常使用外部组件,坚持使用 SBOM 是可以理解的。虽然以这种方式开发软件的明显好处——例如加快开发、降低生产成本和缩短上市时间——是可取的,但是利用这些组件中的常见漏洞和暴露(CVE)的不良行为者的威胁却是不可取的。

软件供应链攻击的例子

过去两年中对软件供应链的大量网络攻击说明了软件供应链可能带来的风险增加。仅在 2021 年就发生了至少四起著名的袭击事件——网络安全管理软件产品、Codecov、Kaseya 和 Log4j。

在 2020 年 3 月至 6 月的网络安全管理软件产品攻击中,Orion 平台上估计有 18,000 名客户下载了被注入恶意代码的更新,其中包括一些美国政府机构。该代码允许未经授权的后门访问系统和专用网络。网络安全管理软件产品直到 2020 年 12 月才发现漏洞,促使召回、修补和向客户发出咨询

几周后,2021 年 1 月,由于构建过程中的错误,攻击者获得了 Codecov 的 Docker 映像创建过程中使用的凭据。凭证允许修改 bash 上传脚本。通过该脚本,他们窃取了信任客户的持续集成(CI)环境中的凭据和额外资源。Codecov 直到几个月后的 4 月份才发现漏洞。

2021 年 7 月 2 日,大约 90 天后,一个复杂的勒索软件组织利用了 Kaseya 虚拟系统管理员(VSA) 服务器中的一个漏洞——影响了大约 1500 家小企业。与上面提到的案例不同,Kaseya 当天就发现了漏洞——可能是因为攻击者要求受影响方支付 45,000 美元到 500 万美元不等的赎金。Kaseya 建议客户在修复问题时关闭他们的 VSA 服务器。幸运的是,补丁早在两天后就可用了,直到 7 月 22 日 Kaseya 从第三方获得了一个工作解密工具。技术支持仍在继续,一个咨询随后在下个月发布。

2021 年 12 月,Kaseya 事件几个月后,发生了可以说是最简单但最普遍的软件供应链攻击。在流行的 Java 日志框架 Apache Log4j 中关于远程代码执行(RCE)的概念证明(POC) 被披露后,攻击者开始大规模利用一个漏洞。该漏洞名为 CVE-2021-44228 ,允许攻击者将恶意软件推送到易受攻击的轻量级目录访问协议(LDAP)服务器上。尽管解决该问题的更新相对较快,但挥之不去的 log4j 漏洞仍不断涌现,促使最近的网络安全和基础设施安全局(CISA)于 2022 年 4 月发布咨询

为什么软件供应链容易受到攻击?

在它的核心,软件供应链是一个越来越大、越来越复杂、越来越互联的技术、人员和过程接触点系统,呈现出多种攻击媒介。坏演员可以利用这些接触点渗透到软件供应链中。

“技术”接触点通常由基础设施、软件和代码库组成。

基础设施是指用于运行软件的虚拟或物理设备。服务器、虚拟机、存储和网络设备等基础设施易受错误配置错误的影响,从而使关键资源暴露在网络攻击之下。根据 Aqua Security 的 2021 年云安全报告,90%的组织由于错误配置的云基础架构而面临安全漏洞的风险。

由专有代码组成的软件,尤其是开源库和第三方工具,很容易被插入恶意代码利用代码中的错误,精心设计软件包依赖性混乱,劫持更新,破坏代码签名过程。

存放这些程序的代码库也容易受到攻击。Synopsys 报告称,多达 88%的包含开源软件的商业代码库的组件比用户更新晚了两年。

“人”接触点由开发人员和其他人组成,他们恶意地或无意地在软件供应链中引入漏洞。例如,2017 年的 crossenv 域名抢注事件是对 npm 注册表的恶意攻击,目的是欺骗不知情的开发人员安装包含恶意软件的 crossenv(而不是 cross-env)包。

“进程”是攻击者感兴趣的接触点,尤其是身份和访问管理(IAM)进程。攻击者可以通过员工或系统破坏 IAM 控制,从而引入间谍软件和勒索软件。

如何提高软件供应链的安全性

保护软件供应链的第一步是实现组件的可见性。供应商和最终用户可以通过列出您分发和使用的软件中所有第三方组件和依赖项的 SBOM 来实现这一点。

SBOM 概述了当前发生的情况,展示了安全意识和许可证合规性,并可作为影响软件组件的最新建议的指南。通过采用各种自动化漏洞扫描技术,您可以获得软件供应链的进一步可见性和安全性。

仅仅扫描或跟踪常见漏洞是不够的。您修复漏洞的速度和彻底程度会影响您的暴露程度。因此,有一个专门的事件响应团队来根据需要提供及时的补丁或更新会有所帮助。确保您拥有编写良好的故障转移流程,并定期进行严格的测试。

仅对供应链中的供应商使用可信的存储库和经过验证的来源,并对库、框架和供应商进行定期风险评估。一定要经常用你的独立测试来补充供应商的测试。作为供应商,您可以实施强有力的 IAM 政策和控制,同时牢记最低特权原则,包括数据治理指南,以保护您的软件供应链中的数据和基础设施。

如何利用 CI/CD 实现供应链安全自动化

实现供应链安全自动化的最佳方式是拥有强大的持续集成和持续交付(CI/CD)管道。借助 CircleCI,您可以无缝集成应用安全(AppSec)和开发安全运营(DevSecOps)工具,以便在管道的构建、测试和部署阶段检查来自版本控制系统(VCS)的漏洞。

在 VCS 阶段,开发人员可能会犯以纯文本形式将秘密提交给存储库的错误,攻击者可以在 Git 历史中发现这一点。CircleCI 与您首选的 VCS 提供商集成,使您能够使用相关的orb,如 GitGuardian orb ,扫描提交的秘密。您可以使用静态加密环境变量或容器存储上下文在 CircleCI 中安全地存储和管理您的秘密,以便跨项目使用。

或者,您可以从第三方解决方案中动态获取存储的机密,或者提交加密版本并将解密工具保存在上下文中,以便您可以在 CI/CD 管道的构建和测试阶段执行解密作业来检索机密。

在 VCS 阶段,您还可以使用 Lightspin orb 扫描 IAM 错误配置、暴露的凭据和基础架构中不安全的配置,并提供建议,作为代码库中的代码(IaC)模板。

在管道的构建阶段,您可以使用相关的 orb 对您的代码和开源库运行静态应用安全测试(SAST) 作业。例如,你可以使用 Snyk orb扫描你的代码库寻找依赖漏洞。如果扫描揭示了软件供应链中的潜在威胁,您的构建将会失败,Snyk 将会输出提高代码安全性的建议。

在部署阶段,您可以运行动态应用程序安全测试(DAST)作业,以在生产运行时捕获漏洞。例如, DeepFactor orb 基于应用程序行为提供对应用程序代码、包依赖关系、web APIs 和合规性 CVEs 的优先洞察。

结论

软件供应链攻击日益猖獗。随着现代商业的相互联系,加上软件生态系统的变化速度越来越快,坏人有很多攻击点可以瞄准。这强调了尽可能防止成功利用漏洞的必要性。

做到这一点的一个方法是在 CI/CD 的软件交付过程的每一点添加自动化的供应链安全扫描。

使用 CircleCI,您可以创建带有作业的工作流,这些作业执行漏洞扫描并提供关于代码库、开源库、依赖项和其他第三方工具的建议。您可以检测基础架构中的 IaC 错误配置在基础架构中,安全地构建和部署工件,并使用各种自动管道触发器验证 CI/CD 管道中的合规性规定。

为了领先于攻击者并了解更多有关将自动化供应链安全添加到您的 CI/CD 渠道的信息,现在就开始使用免费 CircleCI 帐户

CI/CD 管道的自动密钥轮换- CircleCI

原文:https://circleci.com/blog/securing-ci-cd-pipelines-with-circleci-contexts-rest-api/

使用 CircleCI contexts API ,您可以节省团队的宝贵时间,同时增强安全性实践。我们知道维护贵组织的安全至关重要。有严格的合规要求要满足,比如 FedRAMP 和 GDPR,还有越来越多的安全威胁和供应链漏洞要防范。在 CircleCI 上,客户可以以多种方式主动管理安全需求,比如管理您组织的敏感密钥或机密。

机密用于构建、测试和跨多个项目部署。在维护它们的安全性的同时在您的团队中共享它们可能会很困难。在本文中,我们将介绍如何使用 contexts API 作为您团队安全策略的一部分,帮助您根据最佳实践自动执行秘密轮换,从而提供尽可能多的保护。

用 CircleCI 上下文管理秘密

为了充分利用 CircleCI 上的 CI/CD 工作流,开发人员连接到私有数据存储和访问受限服务。团队甚至可以直接连接到他们的基础设施来推动生产工件。CircleCI 使您能够以上下文的形式安全地存储凭证和其他秘密,以便在构建期间使用。

尽管 CircleCI 和您的组织投入了无数的时间和智慧来保护这些价值观的安全,但没有一个系统是完美的。良好的机密保健包括定期轮换机密以及在某人的访问权限被撤销时轮换机密。如果一名员工离开您的公司,或者如果您组织中的某人被调到另一个部门,他们有权访问的所有机密都应该轮换。

你不知道的秘密曝光怎么办?日志中的异常可能包含一个秘密值,将它暴露给不应该访问它的人。定期轮换静态机密有助于保护您的组织免受风险。

我们努力保护您的秘密,包括静态和传输中的加密、构建日志中秘密值的屏蔽,以及 CircleCI 员工对秘密数据存储的访问记录。我们是首款符合 FedRAMP 严格的安全和隐私 NIST 标准的 CI/CD 工具,此外还符合 SOC 2 Type II 标准。

秘密轮换的建议

CircleCI 建议自动进行秘密轮换,以防止人为错误,并确保定期进行。以下是在 CircleCI 中使用机密的一些准则:

  • 使用最小特权原则。只对您传递给 CircleCI 的秘密授予构建和部署所必需的权限。
  • 使用 CircleCI CLI 或 API 在 CircleCI 上下文中自动添加和更新机密。
  • 根据团队的独特需求和风险状况,安排定期秘密轮换。

本文中描述的指导方针尤其适用于大型组织,但即使是小型开源项目的维护者也可能希望提高他们的保密水平,以防止流行库遭到破坏。通过在设计和自动化方面的一些前期工作,无论您的组织规模如何,您都可以加强您的安全状况。

有关如何开始使用 API 或 CLI 自动执行秘密轮换和创建环境变量的更多信息,请查看我们的上下文文档

CI/CD - CircleCI 的安全最佳实践

原文:https://circleci.com/blog/security-best-practices-for-ci-cd/

欢迎使用 DevSecOps 和 CI/CD 安全指南。浏览每个部分,找到各种相关资源,以确保您的应用程序和基础架构的安全性。

什么是 DevSecOps?

DevSecOps 是从构思到部署安全开发应用程序和基础设施的理念。它要求在开发生命周期的所有阶段考虑安全风险。虽然 DevOps 团队过去一直专注于自动化他们的应用程序的构建、测试和部署,但 DevSecOps 包括自动化安全实践,以允许团队在不损失速度的情况下提高安全性。

使用 DevSecOps 保护 CI/CD 管道

安全的 CI/CD 管道是许多企业日常运营的一部分。这些过程,当设置正确时,通过自动化许多手工任务和显示软件是如何工作的,保持交付过程的一致性。

CI/CD 还是您的基础设施访问许多不同资源的技术堆栈,从开发和生产环境到分析密钥和代码签名凭证。管道可以访问的资源越多(安全机密、专有代码和数据库),保证 CI/CD 系统的安全就越重要。

在本文中,我们将讨论 CI/CD 管道安全性的不同部分,并重点介绍一些改进这些方面的方法。

CI/CD 的三种安全最佳实践

我们通常将安全实践分为三个部分,您可以使用不同种类的解决方案来解决。这些是:

  1. 安全管道配置
  2. 代码和 Git 历史分析
  3. 安全策略实施

这三个方面同等重要。让我们更深入地了解一下这些类别以及解决它们的方法。

安全管道配置

可以使用您的 CI/CD 管道配置来降低发生安全问题的可能性。

首先,将秘密安全地存储在连接到数据库和第三方服务的管道中。在 CircleCI 上,您可以使用静态加密环境变量,或者上下文特性。上下文提供了对跨项目的环境变量的访问。它们的使用也可以由组织的管理员限定为特定的安全组成员。

另一种选择是使用第三方解决方案,为您的工作从他们的安全存储中动态获取机密。为了确保您的凭证不会暴露给不应该看到它们的人,请不要以纯文本的形式将这些凭证签入到您的存储库中,即使存储库是私有的。

其次,对于像代码签名密钥这样的敏感文件,我们建议在加密文件和存储库之间增加一个额外的隔离层。这里看一个例子。在存储库中对它们进行加密,并将解密密钥存储在环境变量或上下文中。仅在需要时,在 CI/CD 作业中解密它们。这使得暴露或泄露这些高度敏感的文件变得更加困难。

第三,不要让您的 CI/CD 环境在没有监控的情况下运行。确保在需要敏感信息的作业完成后,销毁用于管道的容器和虚拟机。在 CircleCI 上,这在所有支持的平台上自动发生,包括 Docker 和 Machine 上的 Linux、macOS 和 Windows

第四,额外注意 CI/CD 系统如何处理来自存储库分叉的 pull 请求的构建。如果您的构建阶段需要秘密来工作,分叉的拉请求可能会通过您的管道获得对它们的访问。在 CircleCI 上,默认行为是从不将秘密传递给分叉的 pull 请求。

通过解决这四个方面的问题,您可以显著降低 CI/CD 管道配置带来的安全事故风险。接下来,代码和 Git 历史分析。

代码和 Git 历史分析

我们都喜欢 Git 全面的变更历史,能够用一个简单的 CLI 命令查看项目的历史,并看到谁最近更改了一行代码,还有其他好处。这也意味着,如果 Git 存储库被暴露,任何进入 Git 历史的敏感信息都可能被攻击者访问,即使存储库的最新状态不再包含机密。

松露猪和 GitLeaks 是帮助你识别已经提交给代码库的秘密的工具,这样你就可以停用和替换它们。这些工具扫描您的 Git 历史,寻找您的团队过去可能已经添加到存储库中的秘密的痕迹。

一旦您的 Git 历史没有秘密,您就可以进入下一个层次:确保您当前的修订版不包含任何易受攻击的依赖项。

静态应用安全测试(SAST) 技术可以检查您提交的应用并分析其依赖性。如果依赖项包含任何问题或已知的安全漏洞,您的提交将被标记为不安全,不会继续部署。

动态应用程序安全测试(DAST) 技术更进了一步,在您的 CI 作业中旋转生产环境的副本,以便您可以扫描容器和可执行文件。例如,动态方面帮助系统捕捉在启动时加载的依赖项,因为 SAST 不会捕捉这些依赖项。

安全策略实施

一些安全方面不能基于已知的漏洞进行静态检查,而是特定于您的特定公司。这些需要被编码为策略,可以是自动或手动的合规性检查。

某些任务,如审查有权访问您的存储库的所有帐户的列表,或确保您的入职和离职流程同步,将是手动的。我们建议您设置提醒来定期执行这些操作。

有些任务实际上可以很容易地自动化。例如,第三方服务可以提供一种便捷的方式来编纂一组规则,以匹配您的 CI 渠道。这些服务可以帮助您证明符合管理数据的法规。如果不匹配,构建将会失败。

用 CircleCI orbs 保护您的 CI/CD 管道

CircleCI orbs 是 CircleCI 配置的可共享包,可以在您的构建中使用。orb 定义了可重用的命令、执行器和作业,因此常用的配置可以压缩到一行代码中。orb 是 CircleCI config 的可重用、可共享的开源包,支持这些服务的即时集成。借助 orbs,您可以获得一个保护管道的开箱即用的解决方案。

寻找 orb 专用资源?查看以下博客帖子:

CircleCI 上的私人球体

虽然到目前为止,成千上万的开发人员已经享受了我们开源 orb 的体验,但是我们在大型组织中的许多客户需要一种方法来使他们的 orb 实例私有。所有 CircleCI 计划都提供私人球体,为开发人员提供更多隐私、效率和跨团队协作。这对于在医疗保健、金融和其他具有高治理和法规遵从性标准的行业中工作的团队尤其有用。

要了解更多关于私有 orbs 如何提高隐私、效率和组织内部共享的信息,请阅读我们关于如何在任何组织上构建私有 CircleCI orbs 的教程或阅读我们的 orbs 文档

了解如何使用 CAS 的 7 个概念编写安全代码

持续应用安全 (CAS)是一种帮助开发人员可靠地构建和运行安全应用和 API 的方法。CAS 将基于纸面的安全策略和指导转变为“安全即代码”。

通过基于工具的安全实施,CAS 使开发、安全和运营团队能够以现代软件开发的速度协同工作。在此了解如何使用 CAS 的概念。

采用 DevSecOps 方法

您的团队是您最有价值的安全研究人员。他们最了解您的应用程序,并会不断改进它们。以下是支持他们的方法:

  • 为您的团队成员实施清晰的安全问题报告流程
  • 花时间修复已发现的安全缺陷
  • 赞美那些公开报告问题的人。

想一想你团队中的某个人报告可疑活动需要多长时间。他们是否需要在多个闲置渠道发布帖子来吸引注意力,或者是否有一个简化的流程来调查这样的问题?你如何对待那些报告误报的人——你会责怪他们浪费团队的时间,还是会进行一次无可指责的事后分析,以便团队中的每个人都能从错误中吸取教训?所有这些问题都可能成就或破坏您注重安全性的开发文化。

查看将 DevOps 转变为 DevSecOps 的三条规则。

在本文中,我们介绍了实现 CI/CD 管道安全的标准安全措施的多种方法。如何使用 CI/CD 管道来发现和修补安全漏洞?在漏洞管理中了解更多信息,并使用 CI/CD 进行开发。

完成安全测试自动化周期| CircleCI

原文:https://circleci.com/blog/security-testing-automation-cycle/

DevOps、DevSecOps 和 CI/CD 是一个词的同义词——自动化。自动化他们的工作流使开发人员能够交付一致性、节省时间以及对他们的软件开发生命周期(SDLC)的有用见解。但是自动化的效率取决于您最薄弱的环节或最麻烦的瓶颈,有时可能是安全测试。

传统上,安全测试要么手动执行,要么在过程的后期执行。有时,安全测试依赖于遗留工具,这些工具速度慢,对开发人员不友好,并且不能有效地集成到持续集成(CI) 管道中。

为了保持快速的发布周期,您要么被迫等待直到手动安全检查和验证完成(这可能需要几个星期),要么接受风险并进行部署(不幸的是,这更常见)。这两种选择中的任何一种都可能导致生产中的重大漏洞,在发布完成(如果有的话)几个月后问题才得到补救,并导致不可逾越的技术和安全债务。

这个瓶颈抵消了自动化带来的好处。雪上加霜的是,瓶颈安全测试变成了一个棘手的问题,它经常被忽略,甚至完全从工作流程中删除。这篇文章将描述一些方法来使安全测试自动化成为你的管道中的一个活跃的部分,这样你就可以继续交付及时和安全的持续高质量的应用程序。

先决条件

为了遵循本文中的示例,您需要准备一些东西。

  • GitHub 帐户
  • 一个的账户
  • 云帐户(在本例中,我们将使用 AWS 帐户)
  • 一个 NexPloit.app 账户(免费)来运行安全测试

作为 CircleCI 工作流程一部分的自动化安全测试

要将安全测试自动化作为您工作流程的一部分,需要您的工具具有补充并跟上快速发展的属性:

  • 无缝集成到您的 CI 中
  • 专为开发者设计。为网络安全专业人员构建的工具具有复杂的测试设置和配置,对开发人员来说是行不通的
  • 直观的扫描优化和利用配置文件作为代码的能力
  • 快速测试每个构建和提交的能力,同时还能识别漏洞
  • 以不延迟(和烦扰)开发人员(和安全团队)的方式自动删除误报
  • 清晰有效的补救指南

有了这种工具,自动化测试就能发现真正的问题,这些问题可以尽早地、经常地得到补救,开发人员将开始信任结果。集成和运行这些测试以开始在每个构建中获得安全性遵从性是非常简单的,正如我将在本文接下来的几个部分中展示的。

集成和配置 circleci 和 exploit

神经区域有一个圆形球体。不过,对于这个例子,我们将使用 GitHub 存储库中的一个易受攻击的应用程序,并设置一个 CircleCI 工作流。

我们将使用 NexPloit 对目标运行初始安全扫描,CircleCI 将根据我们的配置中断构建,并且可以查看结果以进行补救。本例的代码库包含一个示例 CircleCI YAML 配置文件的公共可用库。它对一个名为破碎晶体的故意易受攻击的基准应用程序进行扫描。请随意使用这个目标作为测试项目。

这个 YAML 文件包含安全扫描的配置、管道本身(称为 nexploit),以及回购和执行步骤的详细信息。

安装 NexPloit CLI 实用程序,以便可以使用 Nexploit API 运行、轮询状态和停止扫描。

Output of NexPloit CLI

接下来,我们需要提供扫描设置的详细信息:

  • 将在目标上使用爬虫来自动检测攻击面
  • NEXPLOIT _ TOKEN–使用 Nexploit API 所需的令牌
  • 扫描和轮询间隔的长度以及超时
  • 断点,在本例中是检测到中等严重性问题时构建失败

Code for defining scan set up

那么,让我们看看如何在 CircleCI 中设置自动扫描。

首先,我们需要添加我们的集成。如果您还没有,请添加您的 GitHub 和 Nexploit.app 集成。

集成 GitHub 和 CircleCI

这可能是最简单的过程,不需要安装插件,集成真的是开箱即用。只需使用您的 GitHub 帐户登录,CircleCI 就会镜像您的 GitHub 团队权限和特权,因此您可以立即开始构建。每当您在 GitHub 中提交代码时,CircleCI 都会自动运行您的构建和测试流程。

将 NeuraLegion 的 Nexploit.app 与 CircleCI 集成

在 CircleCI 中,导航到您的项目,并选择您想要与自动化测试集成的 repo。

Select repo for automated testing

获取 Nexploit.app API 密钥

  1. 转到项目设置,然后转到环境变量,为 Nexploit repeater/CLI 添加 API 密钥和令牌。
  2. 在 nexploit.app 中,进入用户设置,点击创建新的 API 密钥
  3. 为 API 密钥添加“名称”
  4. 在“选择范围”下,选择全部。
  5. 创建并复制令牌。这可以在 CircleCI 平台中用来设置集成。

获取中继器 ID

  1. 转到中继器然后按 + 设置一个新的中继器。
  2. 添加一个“名称”

Newly-created Repeater ID

  1. 在中继器列表下,复制您生成的新中继器 ID。

List of Repeaters

新的 API 键和 Repeater ID 现在可以用来设置 CircleCI 环境变量:NEXPLOIT_TOKEN 和 Repeater。

Environment variables

您的下一次提交将启动一个工作流并使用 CircleCI 进行构建。NexPloit 将针对该提交运行安全性测试。

List of Repeaters

正如 config.yaml 中所指定的,环境已经启动,环境变量已经准备好,针对目标启动了新的 NexPloit 安全扫描,我们正在轮询结果。

CircleCI build

过不了多久,NexPloit 就会发现一个中等级别的问题。正如我们在配置文件中指定的,当遇到这个断点时,构建失败,扫描被设置为停止。在配置中删除此“停止扫描”步骤将导致构建失败,但会继续对目标运行扫描,以便可以检测到任何其他漏洞。

CircleCI build

现在,我们可以使用 NexPloit 仪表板检查状态,查看安全扫描状态和结果。

Scan summary

扫描器已经在目标上检测到许多自动验证的安全问题,准备修复,而不需要任何手动验证。

这些结果为开发人员提供了理解问题、如何重现问题、如何修复问题所需的信息,以及补救指南。

Remediation guidelines

提供了带有类似 diff 的视图(添加/删除了什么)的请求,以及响应、头和主体。

CircleCI build

在这个跨站点脚本 (XSS)的例子中,有截图形式的额外证据作为概念证明,所以你知道漏洞在那里。

Request view

Nexploit 与 JIRA 集成,因此每发现一个问题就可以自动开一张罚单,或者你可以用插件连接 JIRA 和 CircleCI

结论

NeuraLegion 的 DAST AppSec 扫描连接到您的 CircleCI 管道简单、直接,是您工作流程中的自动化步骤。你也可以继续使用你的其他集成,比如像 JIRA 这样的售票系统。NexPloit 可以扫描您的 web 应用程序或 API(SOAP、REST、GraphQL),以开发人员友好的方式组织和解释任何漏洞的结果,描述漏洞以及如何补救和修复它们。

使用 CircleCI 和 NeuraLegion,您可以在几分钟内创建端到端的部署管道,其中包括作为一个组成部分的安全测试,而不会因错误警报而导致构建失败。结果是部署了安全的产品,并配备了简化的 CI 流程。阅读关于浪子如何利用 CircleCI 和 NeuraLegion 的文章。

Oliver Moradov 是 NeuraLegion 的副总裁,也是 DevOps 和 DevSecOps 的应用安全测试(DAST)自动化方面的专家。NeuraLegion 提供了一个以开发者为中心的 DAST 平台。了解如何结合使用 CircleCI 和 NexPloit 在管道中自动进行 AppSec 测试,以确保设计的安全性。您可以马上开始,今天就注册您的 CircleCI 免费试用

CircleCI 工作流程| CircleCI 中的 Snyk 安全性

原文:https://circleci.com/blog/security-with-snyk-in-the-circleci-workflow/

今天的应用程序是使用现代 DevOps 流程构建的,每天都要进行几次频繁的集成和部署。为了实现速度和规模,应用程序安全性必须从开发人员开始,安全团队从审核和把关职能转变为授权和治理角色。

保护云原生应用

随着容器、Kubernetes 和 Terraform 等云原生技术用代码取代了基础设施,应用程序也发生了变化。这些元素现在由开发人员构建和定制,并存在于他们的存储库中。

保护云原生应用需要将这些应用组件的所有权(传统上是 IT 安全性的一部分)转移到应用安全模型中。随着开发人员掌握安全性的第一步,集成到他们的工作流中并以开发人员友好的方式呈现信息的安全测试工具对他们的工作效率变得至关重要。

Snyk 入门

Snyk orb 使得 DevSecOps 团队很容易将测试集成到 CircleCI 工作流中。使用 Snyk orb,您可以安装、运行测试,并使用 Snyk 监控项目。根据您的风险承受能力,您还可以为失败的构建设置一个阈值。扫描结果提供清晰的信息,帮助开发人员快速了解问题和任何可用的修复。

在本教程中,我们使用 Snyk orb 来演示如何通过将自动化 Snyk 测试添加到 CircleCI 工作流中来防止漏洞通过构建过程。

先决条件

要学习本教程,您需要:

  1. 一个的账户
  2. GitHub 的一个账户
  3. 一个帐户

您需要一个 Snyk API 令牌来使用 Snyk orb。创建一个 Snyk 令牌,然后用它的值在 CircleCI 中设置一个名为SNYK_TOKEN的环境变量。

当你在 CircleCI Academy 中完成基础设施代码模块后,本教程最有意义。您可以在不完成本模块的情况下完成本教程,但是 Terraform 工作流将会失败。

分叉演示项目

在本教程中,您将使用 CircleCI 学院模块中的存储库。在这里,您将创建一个工作流,该工作流使用 Terraform 创建一个 GKE 集群,并在其中部署一个应用程序,作为连续交付管道的一部分。

如果您没有完成该模块,请导航到Learn infra structure as Code repo并分叉。然后,进入 CircleCI 项目页面,点击设置项目,然后选择使用现有配置

确保依赖关系是安全的,并且符合 Snyk 开源

如果您完成了 Academy 模块,那么您部署了一个具有开源组件的应用程序,但是您没有检查它们的漏洞。将 Snyk 开源集成到 CI 工作流中,使安全性和合规性测试成为流水线的一部分,使您能够防止具有易受攻击的依赖关系的应用程序进入生产环境。

要添加 Snyk 开源,在编辑器中打开.circleci/config.yml文件,然后将 Snyk orb 添加到顶部,在 orbs 注册表中用最新版本的 Snyk orb 替换@x.y.z:

version: 2.1

orbs:
  snyk: snyk/snyk@1.0.1

jobs:
  run_tests:
    docker:
      - image: circleci/node:12 

添加 orb 会向您的工作流公开 Snyk 命令和作业。在选择将它们添加到工作流中的什么位置时,请考虑您的需求。对于这个例子,在运行单元测试之前,将snyk/scan命令添加到run_tests作业中,以检查漏洞和许可问题:

jobs:
  run_tests:
    docker:
      - image: circleci/node:12
    steps:
      - checkout
      - run:
          name: Install npm dependencies
          command: |
            npm install --save
      - snyk/scan
      - run:
          name: Run Unit Tests 

保存并提交您的更改。当工作流运行时,run_tests作业失败,因为package.json文件声明了易受攻击的依赖关系。

Open Source issues

还好你查了!但是,您可能不希望过早地将太多的中断引入您的管道。

可以通过向 orb 传递参数来定制这种行为。有关可用参数的列表,请查看 orb 注册表中的 Snyk orb。要让工作流不间断地继续,请将fail-on-issues参数添加到snyk/scan命令中,并提交您的更改:

- run:
          name: Install npm dependencies
          command: |
            npm install --save
      - snyk/scan:
          fail-on-issues: false
      - run:
          name: Run Unit Tests
          command: | 

下次管道运行时,它仍会发现问题,但会继续下一个作业。

Passed Open Source Scan

要了解如何解释 CLI 结果以修复开源漏洞,请访问 Snyk 文档。

使用 Snyk 容器扫描和保护容器图像

除了您的应用程序依赖性之外,您选择的容器基础映像也可能通过操作系统包和语言运行时引入易受攻击的开源组件。

Snyk 容器使用带有几个参数的相同的snyk/scan命令。因为需要构建映像来扫描它,所以通过分解现有命令,将命令添加到docker builddocker push步骤之间的build_docker_image任务中。请注意,使用了severity-threshold参数来报告仅高或严重的问题。

如果您没有完成 CircleCI 学院模块,您将需要设置一个名为DOCKER_LOGIN的环境变量,使用您的 Docker ID 或任何您想要的名称来命名图像,以使其工作:

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 .
      - snyk/scan:
          severity-threshold: high
          docker-image-name: $DOCKER_LOGIN/$CIRCLE_PROJECT_REPONAME
          target-file: "Dockerfile"
          fail-on-issues: true
      - run:
          name: Push Docker image
          command: |
            echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
            docker push $DOCKER_LOGIN/$IMAGE_NAME 

扫描完成后,Snyk 将由于高严重性漏洞而无法通过该阶段。与 Snyk 开源一样,可以通过向 orb 传递额外的参数来自定义这种行为。

Failed Container Scan

在容器扫描后使用 Snyk 来中断构建,如本例所示,这是防止有漏洞的映像被推送到容器注册表的一种方法。

访问 Snyk 文档,了解如何理解 Snyk 容器 CLI 结果,并确保浏览关于基础映像修复的部分。

使用 Snyk 基础设施作为代码来修复地形错误配置

正如我们上面讨论的,现代应用不仅仅是代码和开源组件;在云时代,基础设施是应用程序的一部分。虽然云提供商提供了管理云配置的工具,但云资源的错误配置仍然是最普遍的云漏洞,可以被利用来访问云数据和服务。这就是 Snyk 基础设施作为代码的用武之地,它提供了精选的安全信息,为开发人员提供了快速查找和修复配置问题所需的信息。

从 orb 版本1.0.0开始,Snyk 基础设施作为代码以job的形式提供,可以直接添加到workflow中。对于本例,在gke-create-cluster作业之前添加snyk/scan-iac作业,以在创建云基础设施之前检查 Terraform 文件是否配置正确。args参数指向要检查错误配置的文件,也可用于传递其他 Snyk CLI 参数:

workflows:
  build_test:
    jobs:
      - run_tests
      - build_docker_image
      - snyk/scan-iac:
          args: part03/iac_gke_cluster/
      - gke_create_cluster:
          requires:
            - run_tests
            - build_docker_image
            - snyk/scan-iac 

与所有其他 Snyk 扫描一样,默认行为是在出现任何错误配置的情况下中断构建。

IAC Issues

访问 Snyk 学院,了解如何在此工作流程中定制 IaC 扫描参数

增强开发者体验

如果你能走到这一步,恭喜你!现在,您的交付管道已经针对应用程序的开源依赖项、容器基础映像以及将应用程序部署到生产 GKE 环境中的 Terraform 文件所带来的问题进行了安全测试。我们可以通过集成 Snyk、IDE 和他们的源代码控制库,让开发人员更容易访问和操作这些信息。

要在 IDE 中使用 Snyk,下载任何可用的 IDE 插件或使用 Snyk CLI

要将 Snyk 集成到源代码控制管理(SCM)中,请使用 SCM 集成将 Learn 基础结构的分支作为代码报告导入 Snyk。访问 GitHub 集成的 Snyk 文档了解如何集成。一旦 fork 被导入,您将在 Snyk UI 中看到我们在本教程中测试的文件,以及它们的风险。这通过像修复拉取请求这样的特性提高了开发人员的生产力。请访问 SCM 集成最佳实践页面了解更多信息。

Repo Imported to Snyk

结论

通过完成本教程,您会更加意识到开发人员选择开放源代码、容器和基础设施配置所带来的安全风险。这种意识是在负责预防生产问题的安全团队、负责保持管道运行的运营团队和负责解决问题的开发人员之间建立协作文化的重要第一步。

想了解更多?访问 Snyk 文档,阅读将 Snyk 集成到 CI/CD 管道中的一些最佳实践

GitHub 检查-工作流| CircleCI | GitHub App + CircleCI 工作流检查

原文:https://circleci.com/blog/see-the-status-of-your-circleci-workflows-in-github/

CircleCI 现在与 GitHub Checks API 集成,因此您可以在 GitHub UI 的 Pull Request Checks 选项卡下看到您的 CircleCI 工作流的状态。以前在 GitHub UI 中,用户只能看到给定 pull 请求的单个 CircleCI 作业的状态——没有办法看到这些作业所属的工作流的状态。启用检查后,用户现在可以在 GitHub 的 checks 选项卡上查看 CircleCI 构建的完整层次和状态。

在 CircleCI 中启用 GitHub 检查

重要提示 -您无法从 GitHub UI 为 CircleCI 启用 GitHub 检查,您必须登录 CircleCI 并为您的组织启用检查。您必须拥有组织的管理员权限才能启用检查。

要启用检查:

  1. 登录 CircleCI
  2. 导航至仪表板
  3. 点击左侧导航栏上的设置
  4. 选择 VCS
  5. 点击管理 GitHub 检查

  1. 选择要启用检查的存储库,然后单击安装

一旦您为运行 CircleCI 构建的存储库安装了检查,您将看到 CircleCI“检查套件”出现在 checks 选项卡上,用于该存储库中打开的 pull 请求。在 CircleCI check suite 中,您可以看到工作流的状态以及每个工作流中的作业状态。

如果您需要重新启动任何工作流,您可以单击“重新运行”图标从头重新运行工作流。您还可以重新运行整个 CircleCI check suite,这将从头重新运行项目的所有工作流。更多高级功能,如从失败中重新运行工作流,或使用 SSH 运行作业,仍然只能通过 CircleCI 应用程序访问,而不是通过 GitHub 检查。您可以从 GitHub Checks 选项卡直接进入 CircleCI,方法是点击查看 CircleCI Checks 的更多详细信息。

自主跑步者为 CircleCI 增加手臂支撑

原文:https://circleci.com/blog/self-hosted-runners-add-arm-support-to-circleci/

今天早些时候,我们宣布将自主跑步者添加到我们的云产品中。这让我们新的规模计划的客户能够在他们自己的基础设施上运行 CircleCI 作业。我们也很高兴地宣布,这些跑步者有史以来第一次在 CircleCI 上支持 Arm 计算。

手臂的动量

最近, Arm 架构在边缘计算、台式机和笔记本电脑、数据中心服务器等领域获得了快速发展。Arm 处理器可以提供显著的性能和效率优势,因此在移动和物联网设备中几乎无处不在。随着 Arm 架构的发展,对基于 Arm 的 CI/CD 解决方案的需求也在增长。随着我们的自托管 runners 上增加了 Arm 支持,现在那些在 Arm 上开发的人可以在 Arm 资源上运行他们的 CircleCI 管道。

手臂环扣

有了自托管运行程序,CircleCI 客户现在就可以开始为基于 Arm 的设备构建、测试和部署应用程序。在发布时,CircleCI runner 正式支持 Linux 上的 arm64 架构。我们计划在不久的将来扩大支持平台的列表,以涵盖最流行的操作系统。

runner 只是 CircleCI 上的一种 Arm 支持,但它是我们致力于在基于 Arm 的设备上实现进一步软件开发的开始。除了自托管跑步者,CircleCI 还提供世界上最全面的 CI/CD 云托管计算车队。这些机器称为资源类,为 CircleCI 用户提供即时、按需访问干净、安全的执行环境,以构建和测试他们的代码。它们还让开发人员能够为管道中的每个作业定制操作系统、CPU、内存等,从而在云中提供无与伦比的灵活性。此外,在 2021 年初,CircleCI 将向我们已经庞大的设备群添加基于 Arm 的资源类,以便用户可以在 Arm 上构建,而无需管理自己的 CI/CD 基础设施。

如果你今天想在你自己的 Arm 计算上运行 CircleCI 作业,你可以在这里了解更多关于使用自托管运行器的信息。

自托管与基于云的移动应用测试| CircleCI

原文:https://circleci.com/blog/self-hosted-vs-cloud-mobile-testing/

测试是移动应用开发过程中至关重要的一部分。在向用户发布移动应用之前,您的团队可以使用测试来评估移动应用的质量、安全性和可靠性。希望他们的应用程序具有高性能和直观性的用户。

DevOps 团队可以通过两种方式对移动应用进行测试:内部(也称为自托管)或在云中。但是哪一个对你的团队是最好的选择呢?

本文比较了自托管和云托管的移动测试,以帮助您决定最适合您组织的应用测试选项。

什么是自托管移动测试?

自托管移动测试在专用物理位置设置的本地机器上运行。它包括在你的开发者测试环境中运行应用程序,在将应用程序发布到市场之前,检查它的可用性和功能是否满足所有要求。

对于自托管测试,您负责设置和维护服务器以及测试基础设施。您还必须监控机器并执行软件升级。为自托管移动测试创建配置和测试环境非常耗时,并且通常会产生很高的成本,但是它确实提供了一些好处。

自托管移动测试允许您设置并密切监控您的测试。它还能让您更好地控制配置以及软件和硬件的设置。数据保留在本地,安全合规性应用于本地。

虽然不太适合每个移动应用程序,但自托管移动测试在某些用例中是有益的。例如,一家专门从事医疗保健应用的开发公司可能会考虑在内部构建一个测试解决方案,以确保应用符合像 HIPAA 这样的法规。这些应用程序包含机密信息,测试人员必须确保它们能够安全地保存最高级别的数据。借助自托管测试解决方案,开发人员可以保持对应用、测试数据和测试环境的端到端控制。这种控制使解决潜在的安全问题变得更加容易,并使开发人员能够创建满足他们需求的测试,而无需涉及第三方。

什么是基于云的移动测试?

基于云的测试依靠第三方服务供应商提供云计算资源。这些资源对您的移动应用程序测试使用按使用付费的定价模式。提供者拥有测试基础设施,运行测试,并交付结果。云托管测试是实时进行的,因此结果可以立即获得。

基于云的测试是一种测试移动应用的虚拟方式。您可以使用托管在云或虚拟软件环境(如仿真器和模拟器)上的真实移动设备来模拟真实移动设备的条件。有了虚拟环境中的仿真器和模拟器,您可以更快地运行测试,而不需要实际的移动设备来进行测试。

基于云的移动测试的虚拟化允许您分配和扩展资源,而不影响整个测试系统。这不同于自托管测试,在自托管测试中,您必须扩展容量来处理越来越多的设备,并确保新添加的设备与基础架构的其余部分进行通信。

如果你正在大规模测试移动应用,你不能依赖自托管的顺序测试,因为它会减慢发布周期。基于云的测试允许您在短时间内测试许多配置——不管您的应用程序有多庞大。

例如,如果您正在测试一个应用程序的 UI 功能,您可以在云中用许多不同的配置、操作系统和环境来测试它。您可以同时运行无限数量的测试。大规模运行测试有助于您的团队更快地检测和修复错误,从而缩短应用的上市时间。

借助自托管移动应用测试解决方案,您的测试能力会受到基础设施的限制。在自托管解决方案上运行许多测试会耗费您的时间和金钱,并且可能需要基础设施升级。基于云的移动测试提供的灵活性让您可以在没有任何开销的情况下扩展您的移动应用程序测试。

基于云的移动测试解决方案的优势

除了上面概述的优势,使用基于云的移动测试还有许多其他优势,包括减少开销、灵活的计算选项和更快的测试执行。

低开销

有了基于云的测试,你不需要寻找服务器,获取测试工具和程序的许可,或者安装它们。云提供商让您能够访问虚拟测试实验室,该实验室拥有测试管理工具、执行工具和操作系统库。虚拟实验室还包含中间件、存储和创建测试环境所需的一切,以反映物理移动测试环境。

此外,基于云的测试工具让您免去了更新或修补测试基础设施以解决安全漏洞或获取最新软件版本的麻烦。云提供商为您管理这些流程,定期部署安全更新和更新虚拟机映像,以便您可以专注于向用户提供新功能,而不是维护复杂的基础架构。

多种计算选项

基于云的测试平台提供了范围的计算资源,允许您定制您的测试执行环境和计算能力,以满足您团队的需求。这种灵活性使得跨多个平台和软件版本测试您的应用程序变得更加容易,让您确信您的应用程序将按照最终用户的预期执行。

随着您的应用和测试需求的变化,云中的灵活计算产品允许您无缝扩展可用的 RAM 和 CPU,以处理增加的工作负载。一些云提供商还提供了在 GPU 和 ARM 等专业环境和架构中进行测试的选项,为您在类似生产的环境中测试移动应用提供了额外的灵活性。

速度

基于云的移动应用测试比自托管测试运行得更快,因为云支持缓存、并行化和测试拆分等优化。更快地运行测试有助于开发人员以更少的资源花费快速找到并修复 bug。

  • 缓存允许您重用之前计算或检索的数据。缓存暂时存储数据的子集,因此您不需要访问底层较慢的存储层来进行数据检索。从内存缓存中读取数据既快速又简单,这提高了测试移动应用的速度。
  • 并行化是一个主要在虚拟机上运行的自动化过程。测试人员在多种浏览器配置和设备组合中同时执行几项测试。并行化允许您利用虚拟化和云来执行更多的测试,比在您的基础架构上测试速度更快。这个过程是资源密集型的,在云中执行更好,在按需付费的模式下,您可以请求更多的资源。并行测试通常是自动化的,在云中以高并发性运行,这意味着更低的成本。
  • 测试分割允许你跨多个容器同时执行测试。如果您有一个包含几十个测试的测试套件,那么按顺序运行测试将会花费大量的时间。但是如果您将这些测试分成多个并发的过程,那么您可以很快收到反馈,并且等待时间更少。

您可以在以下资源中了解更多关于缓存、并行化和测试拆分的优势:

结论

自托管测试是测试移动应用的资本密集型选项。你必须花费在购买硬件、软件和执行维护上。在基于云的测试中,基础设施已经存在,测试环境已经准备好,服务提供商处理维护工作。

云是一个高度可扩展的环境,它允许您根据自己的需求来增加和减少测试资源。虽然在某些情况下自托管移动测试是有意义的,但大多数移动应用测试可以在云中更高效地完成。

CircleCI 为加速移动开发提供了许多选项,包括在全面的 CI/CD 管道中自动化软件构建和发布过程。要了解 CircleCI 和基于云的移动测试如何帮助您快速交付高质量的应用,请探索移动应用开发持续集成的优势。当你准备好利用基于云的测试自动化的速度和灵活性来优化你的移动应用测试体验时,从注册免费的 CircleCI 账户开始吧。

我们在 F 系列中筹集了 1 亿美元:这是我们下一步要做的——circle ci

原文:https://circleci.com/blog/series-f/

今天,我们宣布了由 Greenspring Associates 领投的 1 亿美元 F 轮融资,投资方包括 11 家 Prime、IVP、Sapphire Ventures、顶级资本合作伙伴、Baseline Ventures、Threshold、Scale、Owl Rock 和 Next Equity Partners。感谢我们的客户、社区、合作伙伴、投资者和团队。

这项最新投资让我们也可以投资;我们的产品、我们的社区和我们的员工。我们为数字时代的建造者建造:开发者。我们的目标是帮助开发人员快速交付高质量的代码。虽然我们的领先工程团队社区知道 CircleCI 在开发人员生产力、性能和快乐方面是一个游戏规则改变者,但我们仍然有巨大的机会帮助工程团队茁壮成长。

来自麦肯锡的研究表明同类最佳的工具是开发速度的主要驱动力,拥有强大工具的组织,尤其是 CI/CD,比排名垫底的公司多 65%的创新能力。虽然一流的工具是业务成功的最大贡献者,但只有 5%的高管将工具列为他们的三大软件促成因素之一。在使开发人员更有生产力,并最终使企业更成功的工具方面,显然投资不足。

有了这轮资金,我们将继续投资于我们的平台,构建我们的 Insights 仪表板,扩大我们的 20+计算选项的舰队,并使您更容易在您想要的任何地方托管您的 CI 作业——在云上的,在自托管运行程序上,或在私有服务器上。我们还计划继续创新,推出更多功能,如动态配置私有球体测试洞察。我们将在保持 CircleCI 为持续集成和交付提供最快和最高效平台的同时做到这一点。

我们还将继续投资于我们的员工。发展全球最佳 CI/CD 平台意味着继续发展我们的世界级团队;如今,我们在美国、加拿大、日本、爱尔兰、英国和德国有 70 多个职位空缺。我们期待继续为我们不断壮大的团队提供有竞争力的薪酬和福利:发展和健康应用程序和工具、教育和个人发展津贴、行业领先的过渡相关医疗保健、四个由员工领导和公司赞助的员工资源小组,以及每季度一次的全公司休息日。

作为今天公告的一部分,我们还欢迎 Stacey Epstein 成为我们董事会的第一位独立董事。Stacey 目前是 Freshworks 的 CMO,她为我们的董事会带来了她在企业软件方面的深度和丰富经验。欢迎你,史黛西。

最后,今天我们也欢迎 Vamp 的团队来到 CircleCI 。位于荷兰的 Vamp 是第一个发布流程编排平台,旨在使交付以用户为中心、无风险、回滚友好。我们收购了 Vamp,并带来了由 15 名员工组成的整个团队,以帮助我们将足迹扩展到发布管理,并将 CI/CD 的反馈回路连接到更远的生产中。

感谢我们的客户、社区、合作伙伴、投资者和团队。

无服务器与容器:哪个最适合你的应用?圆环

原文:https://circleci.com/blog/serverless-vs-containers/

为了保持领先,许多组织都在考虑如何发展他们的技术流程,以加速他们的 IT 基础设施开发。快速、稳健地部署到最新平台是实现这一变革所需的较短交付周期的关键。

托管这些部署的两种最广泛使用的技术是无服务器功能和容器。它们是什么,有何不同,如何决定哪一个最适合您的应用?在这个比较中,我们将看看无服务器计算和容器之间的一些重要区别,并概述一些您可以用来决定在您的下一个项目中使用哪个的标准。

什么是无服务器?

无服务器技术有点用词不当。使用无服务器计算,工作负载实际上运行在一个服务器上,该服务器在幕后托管功能。但是,服务器不是由开发人员管理的。说是开发者的 todo 列表无服务器可能更准确。

无服务器功能通常是小型、轻量级的编程功能,只有一个目的。这个单一的目的可以是任何事情,从从数据库中获取客户的详细信息到显示通知。

大多数云提供商提供无服务器功能,他们可能称之为功能即服务(FaaS)。领先的产品是 AWS LambdaAzure FunctionsGoogle Cloud Functions ,每一个都在相关的生态系统中有许多集成。它们是提供 API 端点或微服务的理想选择。

什么是容器?

理解集装箱的经典比喻是想象一艘集装箱船在大片水域上运输货物。

容器是一种独立的、轻量级的虚拟化技术。它们类似于虚拟机(VM),只是它们只虚拟化客户操作系统(OS)和应用程序,而不是整个计算机。容器比 VM 设置起来更快更容易。回到我们的船的比喻,船本身代表主机操作系统和硬件,而货物容器是客户操作系统和应用程序。

一旦有人构建了一个容器,他们就可以使用像亚马逊的弹性容器服务(ECS)或 Docker 这样的服务来部署和运行它。然而,DevOps 团队最常将容器部署到 Kubernetes 集群

容器是将现有的单一应用程序转变为云原生应用程序的最佳方式之一。为了尽可能高效,容器还应该将应用程序分成更小的部分。

无服务器与容器:相似与不同

无服务器应用程序和容器化应用程序有几个相似之处。也有一些重要的区别。

无服务器和容器有什么相似之处?

在大多数情况下,无论是无服务器功能还是容器都不需要开发人员担心服务器或托管其应用程序的基础设施。主机硬件和操作系统与客户应用程序和操作系统分开。DevOps 团队不需要考虑无服务器功能或容器使用什么硬件。

这两种托管选项都是可扩展的,只需提供更好的硬件,如更强大的 CPU、更多内存或更快的联网能力。

这种情况的例外是在使用具有内部基础设施的容器时。在这种情况下,硬件配置是一个手动过程,通常由专门的基础架构团队来处理。

还可以根据流量等需求进行扩展。开源编排系统 Kubernetes 可以在几秒钟内横向扩展容器。类似地,许多 FaaS 产品可以基于重要指标(如路由到应用程序的请求数量)自动扩展。

无服务器功能和容器都兼容顶级持续集成平台,包括 CircleCI。对于每个成功完成的构建,自动化工具可以为您的基础设施提供商部署新版本的容器映像或无服务器功能。

无服务器和容器有什么不同?

尽管这两种技术有相似之处,但基于一些固有的差异,每种技术都有独特的用途。

无服务器和容器都是有弹性的,所以它们可以在需要的时候伸缩。然而,使用容器的 DevOps 团队需要像 Kubernetes 这样的容器编排软件来根据给定的标准自动伸缩。与此同时,许多 FaaS 平台提供开箱即用的服务。

无服务器功能通常是小型的、独立的功能块,具有单一的职责。它们通常是短暂的,只运行几分钟,或者如果面向客户,只运行几秒钟。同时,容器最适合更广泛、长时间运行的应用程序或具有多重职责的应用程序。

对于以下情况,容器是很好的选择:

  • 远离传统的内部基础架构
  • 使现有的单一应用程序成为云原生的
  • 开发一个一次运行数小时的应用程序

一个显著的区别是,您的提供商将只对您的无服务器功能运行的时间收费。相比之下,您增加了容器实例,并保持其中至少一些实例全天候运行,这通常会导致更高的成本。如果您的应用程序很小,更适合无服务器应用程序,那么浪费的资源会很快产生不必要的成本,并导致更大的环境影响。

然而,这里有一些容器的无服务器产品的例子。AWS Fargate 是一个用于托管和运行容器的平台,您只需为容器完成运行的时间付费。

另一个区别是,供应商锁定更关注无服务器功能。由于与相关服务集成所需的代码,您很容易被捆绑到一个特定的生态系统中。

相反,容器让你保持供应商中立。这种中立性的一个副作用是容器支持任何语言,而无服务器应用程序仅限于少数几种语言。支持的语言列表因提供商而异。

如何为您的应用程序在无服务器和容器之间进行选择

当您决定无服务器还是容器最适合您的应用程序时,最好考虑上面列出的所有因素。然而,您的应用程序架构的大小和结构应该是影响您决策的主要因素。并且确保在你的决定中包括价格等其他因素。

您可以部署一个小型应用程序,或者一个我们可以轻松拆分为多个更小的微服务的无服务器应用程序。另一方面,更大、更复杂的应用程序可能更适合作为容器化的应用程序。紧密耦合的服务集,不容易分解成小的微服务,是容器的有力候选。

由于无服务器产品的局限性,对于某些应用程序来说,容器可能是更好的选择。例如,用不受支持的语言编写的应用程序,或者运行时间长的应用程序,比如机器学习应用程序。

但是你也不一定非要选择其中之一。无服务器和容器并不互斥。你可以在需要的地方使用容器,在有意义的地方结合无服务器,享受两个世界的好处。正如我们前面提到的,甚至有用于托管容器的无服务器产品,旨在弥合这两种选择之间的差距。

结论

无服务器和容器都是创建可伸缩的云原生应用程序的好选择,这将允许您更快地进行创新。要了解有关如何开始的更多信息,请查看这些教程:

无论您选择使用无服务器还是容器,您都可以使用 CircleCI 的持续集成平台来自动化您的构建、测试和部署。一旦你决定了如何使用无服务器和容器,了解更多关于如何使用 CI/CD 管道来优化和简化你的开发过程。

使用 Newman orb | CircleCI 运行邮差收集

原文:https://circleci.com/blog/set-up-a-circleci-pipeline-to-run-a-postman-collection-using-the-newman-orb/

软件业正越来越趋向于 API 优先的世界。今天构建的大型软件系统是由 API 组成的。 Postman 已经成为软件开发人员和测试人员设计、测试和发布 API 的可信工具。全球超过 600 万开发人员和 20 万以上的组织在其 API 开发生命周期的每个阶段都使用 Postman。

Postman 中的工作流围绕着收藏。集合是一组 HTTP 请求,它们可以有自己的测试、响应示例和文档。您可以直接从集合中生成 API 文档。保存的响应示例构成了为 API 创建模拟服务器的基础。您可以通过团队工作区在所有这些方面进行协作。

Postman 也有能力执行这些集合。运行集合会逐个触发集合中的所有请求。您还可以编写脚本来断言响应、控制执行顺序,以及在一次运行中在请求之间传输数据。当您想要测试 API、对 API 工作流执行端到端测试和/或对 API 进行健康检查时,这是非常有用的。你说吧。

在 CI 环境中执行集合

Postman 的一个非常常见的用例是在 CI 管道中运行集合。团队对他们的服务运行契约测试、端到端测试、安全检查或基础设施检查,作为他们构建或发布管道的一部分。这就是 Newman 命令行集合运行器有用的地方。

Newman 是一个 Node.js 应用程序,可以从本地文件系统或远程 URL 运行一个集合。纽曼还提供了多种报告选项。您可以生成 json、xml 或 html 输出形式的收集运行报告。当您将 Newman 与 CI 环境集成时,您可以根据您的 API 是否按预期执行来控制您的构建阶段。

一个 orb 来帮助简化您的收集运行

虽然 Newman 是作为 Docker 容器发布的,但是设置这些构建阶段可能非常耗时。借助 CircleCI 的 Newman orb ,我们已经构建了一个现成的集成,您只需进行一项配置,就可以在 CircleCI 管道中触发收集运行。

orb 将负责设置 Newman,执行您在配置中提到的集合,并将运行报告存储在适当的位置。

配置 orb

Newman orb 公开了可以在 CircleCI 配置中使用的newman/newman-run命令。您可以将您的集合从 Postman 导出为 JSON 并保存在您的存储库中,或者您可以使用您的集合 ID 和 API 键直接调用 Postman API,并对最新版本的集合运行 Newman。

假设您已经将您的收集文件导出到存储库根目录下的./collection.json,那么您的 CircleCI 配置应该是这样的:

version: 2.1
orbs:
  newman: postman/newman@0.0.2
jobs:
  newman-collection-run:
    executor: newman/postman-newman-docker
    steps:
      - checkout
      - newman/newman-run:
          collection: ./collection.json 

newman/newman-run命令接受许多传递给 Newman 的参数。以下是您应该知道的一些重要参数:

  • collection:如上例所示,这是唯一需要的参数。它采用一个指向集合的 JSON 文件的路径或一个可以访问集合的 URL。
  • environment:这需要一个从 Postman 导出的环境的路径,或者一个环境的 URL。
  • iteration-data:如果您需要在采集运行中使用 CSV 文件中的外部数据,此参数将获取 CSV 文件的文件路径或 URL。
  • folder:要在收藏中运行的文件夹的名称。当您想要将收集运行范围扩大到收集中的特定文件夹时,可以使用此选项。

除此之外,你可以在 orb 的文档页面上查看所有被接受的参数。

概括起来

将 CircleCI 的强大功能与 Postman 和 Postman API 结合起来,您可以为您的 APIOps 和 API 测试构建完美的工作流。我们希望这个 orb 能够帮助您在 API 自动化之旅中走得更远。

在我们将于 2019 年 5 月 14 日上午 10:00 PST/18:00 UTC 举行的网络研讨会“使用 CircleCI 和 Postman 简化端到端 API 开发”期间,了解更多关于 CircleCI 和 Postman 的信息。在这里报名

设置与 CircleCI 和 GitHub 的持续集成

原文:https://circleci.com/blog/setting-up-continuous-integration-with-github/

本教程涵盖:

  1. 创建一个简单的 Python 应用程序(使用 Flask)
  2. 为此应用程序编写测试
  3. 添加config.yml文件

持续集成(CI) 包括在特性分支被合并到项目中的主 Git 分支之前,它们的构建和测试自动化。这确保了代码库不会因为可能破坏某些东西的更改而更新。连续交付(CD)通过自动化这些分支或主分支的发布来扩展 CI。这允许小的增量更新更快地到达您的用户,符合敏捷软件开发哲学。

在本文中,我将指导您使用 GitHub 建立一个 CI/CD 流程。我们将使用一个 Python 应用程序来演示如何构建一个 CI 管道

我将引导您完成以下步骤:

  1. 创建一个简单的 Python 应用程序(使用 Flask)
  2. 为此应用程序创建测试
  3. 添加config.yml文件
  4. 推送至 GitHub
  5. 配置 CircleCI
  6. 用徽章更新README
  7. 创建一个公关,看看 CircleCI 的运作

先决条件

要按照教程进行操作,需要做一些事情:

  1. Python 安装在您的本地系统上
  2. 一个的账户
  3. GitHub 的一个账户

创建简单的 Python 应用程序

为了创建应用程序,我们将使用 Flask,这是 Python 的一个微框架。对于这个练习,对框架的最低限度的了解是必要的。你可以在这里找到的例子。

构建应用程序

首先,创建一个项目目录(文件夹)并将cd放入其中。在终端中键入以下内容:

mkdir python_app && cd $_/ 

接下来,打开你最喜欢的编辑器,创建一个hello.py文件。然后,将以下几行复制到该文件中:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!" 

Python 中的虚拟环境

在 Python 中工作时,强烈建议您使用虚拟环境。这允许您在一个抽象的环境中安装 Python 包,而不是整个本地机器。一些常见的方法是使用 virtualenv 或者更好的 virtualenvwrapper 。我们将使用模块venv,它是 Python3 的一部分。

创建虚拟环境:

python3 -m venv venv 

您可以为您的虚拟环境使用其他名称,如下例所示。

python3 -m venv Env 

激活环境:

source venv/bin/activate 

如果您为环境使用了另一个名称,请用该名称替换venv。您将注意到 shell 提示符前的环境名称,这告诉您虚拟环境是活动的。安装的任何 Python 包都将安装在此环境中。要停用此环境,只需运行:

deactivate 

运行应用程序

现在是时候在编辑器中创建一个requirements.txt文件了。将单词Flask添加到文件中并保存。

Flask 

然后,在虚拟环境中,通过运行以下命令来安装该软件包:

pip install -r requirements.txt 

运行该应用程序的最后一个命令是:

FLASK_APP=hello.py flask run 

在浏览器上转到http://localhost:5000/查看应用程序。

创建测试

在您的编辑器中,创建一个tests.py文件并将这些行粘贴到其中:

from hello import app
with app.test_client() as c:
    response = c.get('/')
    assert response.data == b'Hello World!'
    assert response.status_code == 200 

有关测试的更多信息,请参考这些资源

现在您可以运行测试了。打开终端并运行:

python3 tests.py 

不会有任何东西返回到您的终端。为什么?我们构建了 Flask 应用程序,这样它就不会在一次传递中输出到终端。没有终端输出意味着您的测试是成功的。前面提到的参考资料提供了终端如何处理通过和失败测试的更详细的例子。

创建 CircleCI 配置文件

创建一个.circleci文件夹。在那个文件夹里面,创建一个 config.yml 文件。然后,把这几行复制进去:

version: 2.1
jobs:
  build:
    docker:
      - image: cimg/python:3.10.1
    steps:
      - checkout
      - restore_cache:
          key: deps1-{{ .Branch }}-{{ checksum "requirements.txt" }}
      - run:
          command: |
            python3 -m venv venv
            . venv/bin/activate
            pip install -r requirements.txt
      - save_cache:
          key: deps1-{{ .Branch }}-{{ checksum "requirements.txt" }}
          paths:
            - "venv"
      - run:
          name: Running tests
          command: |
            . venv/bin/activate
            python3 tests.py
      - store_artifacts:
          path: test-reports/
          destination: python_app 

你可以在这里找到关于这个config文件的更多信息。

推送至 GitHub

使用尽早和经常提交代码的理念,我们会在这个过程中更早地初始化 Git,并且我们会有原子提交。因为这篇教程是关于 CircleCI 和 GitHub 的集成,所以我故意把它搁置到现在。

当前的代码结构如下所示:

.
├── .circleci
│   └── config.yml
├── hello.py
├── requirements.txt
└── tests.py 

打开编辑器,在工作目录中创建一个.gitignore文件。我们将使用这个文件来声明哪些文件和文件夹是我们不想提交给 Git 的。将以下几行复制到该文件中:

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# Virtualenv
venv/* 

通过运行以下命令提交代码:

git add . 
git commit -m "Initial commit" 

如果您还没有 GitHub 帐户,请到 GitHub 的主页创建一个。如果你是 GitHub 的新手,你可能想读一下关于向 GitHub 推送项目的教程。然后,通过去 github.com/new 的创建一个仓库。

我将使用名称python_app,保持其余的默认设置不变,并单击按钮创建存储库。(如果不可见,请向下滚动。)

GitHub create repo

在创建新的存储库之后,您将看到这样一个页面。

Newly created repo

选择第二个选项,推送一个现有的存储库。运行:

git remote add origin git@github.com:NdagiStanley/python_app.git
git branch -M main
git push -u origin main 

配置 CircleCI

既然 repo 已经在 GitHub 上,我们可以通过配置 CircleCI 来完成 CI。前往 CircleCI 注册页面。如果你还没有注册 CircleCI,请用你的 GitHub 帐户注册。

CircleCI

登录后,请确保您的个人 GitHub 帐户处于活动状态。如果你在几个 GitHub 组织中,其中一个可能是活跃的。只需点击下拉菜单(左上角)并选择您的 GitHub 用户名。然后,点击添加项目。最新的项目“python_app”就列在那里。

CircleCI project

点击包含您的项目的行右侧的设置项目

在重定向的页面上,您会注意到三个选项(最快、更快和快速),默认选择的是最快。我们将使用这个默认选项。注意另外两个的描述。

在 GitHub 分支的输入字段中输入 main (注意字段下方确认.circleci/config.yml文件存在的文本)并点击设置项目

2022-04-14-cci-config

构建很快就通过了。成功!

Passing build

熟悉可以为此项目更改的设置非常重要。我现在将谈一谈与我们相关的问题。

在右上角,点击项目设置(带有齿轮图标的按钮)。然后点击左边的高级

CircleCI settings

找到 Only build pull requests 卡片(如有必要,向下滚动)。默认情况下,它是关闭的。这意味着 GitHub 的每次推送都会在 CircleCI 上运行,包括 PRs。如果你认为有必要,请随意切换。有时,在一个大型团队环境中,建议适度调整构建时间。

自述文件-状态徽章

在您的本地机器上,通过运行以下命令签出到另一个 Git 分支:

git checkout -b add_readme 

打开编辑器并创建一个README.md文件。将以下行复制并粘贴到该文件中,相应地替换用户名NdagiStanley:

# PYTHON APPLICATION

This Python application repo was created to showcase the integration between GitHub and CircleCI.

[![CircleCI](https://circleci.com/gh/NdagiStanley/python_app.svg?style=svg)](https://circleci.com/gh/NdagiStanley/python_app) 

这将添加一个标题、简短描述和一个状态徽章

现在,运行以下命令:

git add .
git commit -m "Add README"
git push -u origin add_readme 

如果你去你的 GitHub repo,https://github.com/<< username >>/python_app,你会注意到我们有了一个新的分支:add_readme。点击比较和拉取请求

GitHub branch

打开拉取请求

这一部分包括我如何建立我的公关。

PR 的标题作为提交消息自动生成。我在描述中输入了以下内容,但这是可选的:

#### What does this PR do?

Adds README.md file and a CircleCI badge in it. 

点击创建拉请求,很快您就成功构建了!

GitHub PR

请注意,我单击了显示所有检查以显示成功的检查来自 CircleCI。(它现在显示了隐藏所有检查的选项。)甚至浏览器的标签页图标也显示了成功运行的绿色勾号。

如果您点击下面的细节,隐藏所有检查,您将被重定向到 CircleCI 上的构建:

The build on CircleCI

在顶部,点击 python_app 。您将被重定向到该项目的版本。

CircleCI builds

结论

你有它!我们已经将 GitHub 与 CircleCI 整合在一起。

总之,我们建立了一个 Python 应用程序,并为它创建了测试。然后,我们创建了一个 CircleCI 配置文件,并将代码库推送到 GitHub。最后,我们将我们创建的 GitHub 存储库连接到 CircleCI。

现在,您可以在 GitHub 中建立自己的项目,并在 CircleCI 上配置 CI 构建。祝贺您开始持续集成实践!让团队的其他成员参与进来,通过添加更多应用程序、更多测试和自动化部署来提升水平。



Stanley 是一名软件工程师和技术文案,他身兼数职,包括技术团队领导和社区参与。他把自己描述成一个数字人(在数字空间中有文化)。

阅读 Stanley Ndagi 的更多帖子

在 CircleCI 2.0 中设置复杂的容器

原文:https://circleci.com/blog/setting-up-tricky-containers-in-circle-2-0-multi-image/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


有些 Docker 容器非常适合 CircleCI 2.0。例如,Postgresql 只需传入几个变量就能完成你需要的一切:

version: 2.0
jobs:
  build:
    docker:
      - image: clojure:alpine
      - image: postgres:9.6
        environment:
          POSTGRES_USER: username
          POSTGRES_DB: db
          ... 

但是有时候你会遇到第三方容器,它玩起来不太好。您将需要访问一些资源,这些资源存在于容器内部,而不存在于其他地方。您将遇到的问题是 CircleCI 将您的执行环境作为您的主要映像。如上所述,虽然我可以访问 Postgres 公开的端口,但是psql不在我的$PATH中。只有 Clojure 的 Alpine 容器的内容是。哈希公司的金库就是这样一个容器。每次引导时,Vault 将选择一个新的根令牌,任何对它的访问调用都需要该令牌。现在,如果 Vault 是您的主映像,这真的没有问题,因为存储这个密钥的文件位于$HOME/.vault-token。但是如果这个图像是这样的:

version: 2.0
jobs:
  build:
    docker:
      - image: clojure:alpine
      - image: postgres:9.6
        environment:
          POSTGRES_USER: username
          POSTGRES_DB: db
      - image: vault:0.7.3 

你会过得很糟糕。

简单的变通方法

在对 Vault 进行一些测试时,我决定实际调用资源,而不是模仿对它的调用。当地的事情进展得或多或少很顺利,但是进入切尔莱西的vault-token被证明是一件苦差事。

Python 简单服务器拯救世界

如果您需要访问不透明容器中的资源,我强烈推荐使用用python -m SimpleHTTPServer包装容器的方法。SimpleHTTPServer通过 http 服务于它所在的文件系统,这意味着你可以通过一个端口遍历和访问你的容器上的文件系统。

最后,我这样做只是为了让我的测试正常工作(下面的所有代码都可以在这里找到):

Dockerfile

 FROM vault:0.7.3

RUN apk add --update python

ADD ./vault/ /vault
WORKDIR /vault

ENV VAULT_ADDR=http://127.0.0.1:8200
ENV SKIP_SETCAP=skip

EXPOSE 8201

ENTRYPOINT ["./docker-entrypoint.sh"] 

坞站-entrypoint.sh

python_http_server() {
  # we want to be able to server the VAULT_TOKEN for testing
  cd $HOME/
  python -m SimpleHTTPServer 8201
}

vault_server () {
  vault server -dev
}

vault_server & python_http_server 

将容器发布到 Dockerhub ,然后将其包含在我的多重图像层中:

。circi/config . yml〔t1〕

version: 2.0
jobs:
  build:
    docker:
      - image: clojure:alpine
      - image: postgres:9.6
        environment:
          POSTGRES_USER: contexts
          POSTGRES_DB: contexts_service
      - image: mannimal/vault-cci:latest 

现在在我的.circleci/config.yml中我有这样一句台词:

 - run:
          name: Setup Vault
          command: |
            VAULT__CLIENT_TOKEN=`curl localhost:8201/.vault-token`

            curl --fail -v -X POST -H "X-Vault-Token:$VAULT__CLIENT_TOKEN" -d '{"type":"transit"}' localhost:8200/v1/sys/mounts/transit
      - run:
          name: Run Tests
          command: |
            export VAULT__CLIENT_TOKEN=`curl localhost:8201/.vault-token`
            lein test 

然后,使用 CCI 将上述所有内容持续部署到 Dockerhub。

黑客快乐!

部署到谷歌 Kubernetes 引擎-部署到 GKE | CircleCI

原文:https://circleci.com/blog/simplifying-your-ci-cd-build-pipeline-to-gke-with-circleci-orbs/

本文试图解开 orbs 的使用之谜,以便快速入门 CircleCI 平台。orb 使您能够跨项目共享、标准化和简化配置。您可能还想使用 orb 作为配置最佳实践的参考。请参考此 orb 简介以获得高级概述。你也可以参考 CircleCI orbs 注册表获得可用 orbs 的完整列表。

使用的技术

本文假设您对以下技术和工具有基本的了解:

  • Docker——用于使用容器创建、部署和运行应用程序。Docker 提供了可重复的开发、构建、测试和生产环境。
  • CircleCI -用于持续集成和持续部署(CI/CD)。
  • Kubernetes——用于跨主机集群自动化部署、扩展和管理容器化应用。这降低了云计算的成本,简化了运营和架构。
  • Git 一个分布式版本控制系统,用于在软件开发过程中跟踪源代码的变化。
  • 谷歌 Kubernetes 引擎(GKE)——谷歌运行 Kubernetes 集群的云解决方案。

GKE 设置

Kubernetes 是一个独立于供应商的集群和容器管理工具,由 Google 于 2014 年开源。它提供了一个“跨主机集群自动部署、扩展和操作应用程序容器的平台。”最重要的是,这降低了云计算的成本,简化了运营和架构。要快速了解 GKE,请参见文档

如果你没有谷歌云平台账号,你可以在这里获得一个。一旦我们登录到我们的帐户,我们需要创建一个 id 为circle-ci-demo的项目。

接下来,我们需要下载并安装 Cloud SDK ,这是一个用于 Google 云平台产品和服务的命令行界面,其中包含了gcloud,我们稍后将会用到这个工具。

在命令行中,通过在终端中输入以下命令来确认项目确实存在:

gcloud projects list 

gcloud projects list

通过在您的终端中输入以下命令来初始化gcloud环境:

gcloud init 

通过输入以下内容对其进行身份验证:

gcloud auth login 

kubectl是一个命令行界面,用于对 Kubernetes 集群运行命令。让我们通过在您的终端中输入以下内容来安装它:

gcloud components install kubectl 

在 GKE 上创建一个名为circle-ci-cluster容器集群。您可以从 GCP 控制台执行此操作,但也可以通过在命令行中输入以下命令来执行:

gcloud container clusters create circle-ci-cluster 

我们可以使用以下命令确认集群创建成功:

gcloud container clusters list 

您的输出将类似于下面的输出:

gcloud clusters list

要从另一台计算机或项目的另一个成员对 GCP 控制台中创建的集群运行kubectl命令,您需要在您的环境中生成一个kubeconfig条目。通过运行以下命令生成一个kubeconfig条目:

gcloud container clusters get-credentials circle-ci-cluster 

要确保群集设置正确,请运行命令:

kubectl config current-context 

您的输出将类似于下面的输出:

Docker 设置

Docker 是一个开源工具,可以在软件容器中自动部署应用程序。使用容器来部署应用程序被称为容器化。集装箱化已经变得流行,因为集装箱灵活、轻便、可互换、便携、可升级和可堆叠。你可以在链接的网页中了解更多关于 Docker 的信息。

Dockerizing 一个简单的 Node.js 应用程序

我们的项目需要一个Dockerfile来对我们的简单 nodejs 应用程序进行 dockerize。一个 Dockerfile 是一个文本文档,它包含用户可以在命令行上调用的所有命令来组合一个图像。下面是项目中使用的 Dockerfile 文件:

# Set the base image to use for subsequent instructions
FROM node:alpine

# Add metadata to an image 
LABEL app="simple-node-application"

# Directive to set environmental variables key to value pair
ENV NPM_CONFIG_LOGLEVEL warn

# Set the working directory for any subsequent ADD, COPY, CMD, ENTRYPOINT, 
# or RUN instructions that follow it in the Dockerfile
WORKDIR /usr/src/app

# Copy files or folders from source to the dest path in the image's filesystem.
COPY package.json /usr/src/app/
COPY . /usr/src/app/

# Execute any commands on top of the current image as a new layer and commit the results.
RUN npm install --production

# Define the network ports that this container will listen on at runtime.
EXPOSE 3000

# Configure the container to be run as an executable.
ENTRYPOINT ["npm", "start"] 

我们现在可以使用以下内容构建和标记图像:

docker build -t circleci-gke:v1 . 

通过从终端运行以下命令,确认映像已成功创建:

docker image 

您的输出将类似于下面的输出:

List Docker Images

通过从终端运行以下命令,在本地测试映像:

docker run -p 3000:3000 circleci-gke:v1 

您现在可以通过访问 https://127.0.0.1:3000 在浏览器上访问该应用程序。

下一步是标记图像并将其推送到注册表中,以便于管理。开发人员、测试人员和 CI/CD 系统(如 CircleCI)需要使用注册表来存储应用程序开发过程中创建的映像。我们可以用以下命令标记图像:

docker tag circleci-gke:v1 gcr.io/circle-ci-demo/circleci-gke:v1 

然后,我们可以将图像推送到 Google 的容器注册中心(GCR ):

docker push gcr.io/circle-ci-demo/circleci-gke:v1 

既然我们已经将工作的容器化 Docker 映像推送到注册中心,我们就可以将应用程序部署到我们的circle-ci-cluster集群了。

为部署配置 Kubernetes 清单

Kubernetes 使用 YAML 进行配置。我们将需要一个类型为LoadBalancer的 Kubernetes 服务,以使我们的部署可以被外部世界访问。

Kubernetes 服务是一种抽象,它定义了一组逻辑单元和访问它们的策略。更多关于 Kubernetes 服务的信息请点击这里

Kubernetes 部署管理集群上运行的无状态服务。他们的目的是保持一组相同的 pod 运行,并以受控的方式升级它们——默认情况下执行滚动更新。更多关于 kubernetes 的部署在这里

创建一个文件夹来保存 Kubernetes 服务和部署的.yaml文件。对于这个例子,我将文件夹命名为admin。用下面的内容在文件夹中创建两个文件:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: production-circle-demo
  labels:
    app: simple-backend
spec:
  selector:
    matchLabels:
      app: ci-deploy
      tier: backend
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: ci-deploy
        tier: backend
    spec:
      containers:
        - image: gcr.io/circle-ci-demo/circleci-gke:v1
          name: rusty-pangolin
          ports:
            - containerPort: 3000
              name: ci-deploy 

应用程序部署. yaml

apiVersion: v1
kind: Service
metadata:
  name: circle-service
  labels:
    app: circle
spec:
  ports:
    - port: 3000
  selector:
    app: ci-deploy
    tier: backend
  type: LoadBalancer 

app-service.yaml

因为我们的 Kubernetes 清单在文件夹admin中,所以我们可以通过对文件夹admin运行以下命令来验证它们:

kubectl apply --validate=true --dry-run=true -f admin/ 

您的输出将类似于下面的输出:

现在我们已经准备好部署我们的应用程序了。使用以下命令部署它:

kubectl apply --validate=true   -f admin/ 

您的输出将类似于下面的输出:

要访问应用程序,请使用以下命令获取外部 IP:

kubectl get services 

您可以在http://<EXTERNAL-IP>:3000访问应用程序。对我来说,那是 http://35.246.104.239:3000/.

我们将如何自动从 GitHub 中签出应用程序,构建它,测试它,并更新 GKE 上的运行实例?我们将与 CircleCI 合作。

配置 CircleCI

现在我们已经有了一个到 GKE 的基本工作部署,我们将需要一种方法来更新应用程序,一旦更改被推送到我们选择的 VCS。我们将整合 CircleCI 和 GitHub 。CircleCI 配置.yml文件的形式存在于项目根文件夹的.circleci目录中,即配置的路径为.circleci/config.yml

使用 CircleCI 球体

我们不需要编写任何定制脚本来将我们的应用程序部署到 GKE。我们将通过将预构建的命令、作业和执行器导入到我们的配置文件中来节省大量时间。我们将使用orbs键调用这个项目中的以下 orb:

  • node: circleci/node@1.0.1 -用于安装所有的应用依赖项。

  • 与 GCR 一起工作的圆球。

  • 与 GKE 一起工作的球体。

orb 由以下元素组成:

  • 命令
  • 作业——一组可执行的命令或步骤。
  • 执行者——这些定义了一个作业的步骤将在其中运行的环境,例如 Docker、Machine、macOS 等。除了该环境的任何其他参数之外。

对于这个项目,config.yml文件包含以下代码行:

version: 2.1
orbs:
  node: circleci/node@1.0.1
  gcp-gke: circleci/gcp-gke@0.1.0
  gcr: circleci/gcp-gcr@0.0.2
jobs:
  build:
    description: Install npm
    # machine option runs your jobs in a dedicated, ephemeral VM that has the following specifications:
    machine: true
    steps:
      - checkout
      # Install node
      - node/install
      # Install npm
      - node/install-npm
      # Download and cache dependencies
      - node/with-cache:
          steps:
            - run:
                name: Install application dependencies
                command: npm install
          # Save cache
          cache-key: package.json
          # Ignore non-checksum cache hits
          use-strict-cache: true
  Build-Push-Image-Docker:
    description: Build and push image to Google Container Registry
    machine: true
    steps:
      - checkout
      - gcr/gcr-auth
      - gcr/build-image:
          image: circle-gke
          tag: "v2" #Change version number e.g to 'v3' when updating application
      - gcr/push-image:
          image: circle-gke
          tag: "v2" #Change version number e.g to 'v3' when updating application

  deploy:
    description: Deploy application to Google Kubernetes Engine
    machine: true
    steps:
      # Install `gcloud` and `kubectl` if not already installed.
      - gcp-gke/install
      # Initialize the `gcloud` CLI.
      - gcp-gke/init
      # Update a deployment Docker image.
      - gcp-gke/rollout-image:
          deployment: circle-ci-cluster
          container: dominic-backend
          image: gcr.io/circle-ci-demo/circle-gke:v2 # change version when updating
workflows:
  build_update_deploy:
    jobs:
      - build
      - Build-Push-Image-Docker:
          requires:
            - build
      - deploy:
          requires:
            - Build-Push-Image-Docker 

将 CircleCI 配置文件的更改推送到 GitHub。

如果您没有 CircleCI 帐户,请创建一个。你可以注册 GitHub。从 CircleCI 仪表板点击添加项目,从显示的列表中添加项目。

在我们对 CircleCI 做任何改变之前,我们需要一个和 GCP 沟通的方法。在我们的例子中,我们需要 CircleCI 将创建的映像推送到 GCR,并用新映像更新部署的实例。我们将使用 GCP 的服务帐户。使用这些指令创建服务账户。给服务账号编辑项目权限:

下面是一个服务帐户密钥结构:

注: 这是一个示例服务帐户(未使用)。你应该永远不要将服务账户提交给代码库,因为恶意攻击者可以获得对你的云平台资源的访问权。

复制服务帐户内容,并通过单击右上角的 cog 图标将它们添加为项目的环境变量

在构建设置下,点击环境变量

添加服务密钥为 GCLOUD_SERVICE_KEY 。您还需要添加谷歌计算区域。您可以通过运行以下命令来实现这一点:

gcloud container clusters describe circle-ci-cluster 

注意: 使用您创建的集群

对于我的集群,在底部,区域可以标识为europe-west2-a。根据您所在的位置,这可能会有所不同。

对于 GOOGLE_PROJECT_ID ,我们可以从控制台直接从项目信息卡中抓取:

现在我们有了 CircleCI 环境集。我们可以触发一个新的构建来测试我们的管道是否工作。您的工作流将类似于下面的工作流:

在 GKE 上访问应用程序

您可以通过运行以下命令来获取应用程序的外部 IP:

kubectl get services 

例如,我必须访问 http://35.246.118.41:3000/.上的应用程序

结论

使用 CircleCI orbs 通过简化我们编写 CircleCI 配置的方式来提高生产率。orb 也可以共享,这通过在我们的配置文件中使用预构建的命令、作业和执行器来节省时间。

orb 并不局限于 CircleCI + GKE 部署。您可以浏览注册表中可用的orb 列表,找到符合您选择的云平台、编程语言等的 orb。


Dominic Motuka 是 Andela 的 DevOps 工程师,在 AWS 和 GCP 支持、自动化和优化生产就绪部署方面拥有 4 年多的实践经验,利用配置管理、CI/CD 和 DevOps 流程。

阅读多米尼克·莫图卡的更多帖子

针对 CI 配置| CircleCI 的 6 个优化技巧

原文:https://circleci.com/blog/six-optimization-tips-for-your-config/

CircleCI 的客户工程团队帮助用户优化配置文件的设置。每天,他们都能为您的项目找到最有用的功能,平衡您的时间和信用消费。在对我们的 20 多家企业级客户进行了几十次配置审查后,我们的客户工程团队将这一切归结为一门科学。因为您和您的团队可能并不总是有时间与专家合作,所以我们整理了一份配置优化指南供您自己使用。本教程载有 CircleCI 客户工程师的最佳技巧和最有价值的建议。当您需要专家时,我们在这里,但我们也希望让像您这样的团队更容易优化您自己的配置。

从配置灾难到配置构建-更快

在这篇文章中,我们将介绍优化你的配置文件的六种最有效的方法,这样你就可以构建得更快。您将学习选择正确的执行器、并行化作业、缓存、使用工作区、秘密管理以及在配置中使用 orb 的最佳实践。

选择合适的执行者

许多 CI 渠道将受益于包括我们的闪电般快速的 Docker 便利图像舰队之一。使用docker yaml 键在 Docker 容器中运行将会以最快的速度提供基本功能。

我们将这些发布到 Docker Hub cimg profile 。如果你的应用需要其他工具,考虑运行一个定制 Docker 镜像。这里有一个例子,使用了一个旧的,笨重的映像固定到某个版本的 Node 上。这个执行器是通过指定 Docker 映像在每个作业下定义的,比如在这个test作业中:

test:
    docker:
      - image: circleci/node:9.9. 

有了下一代 CircleCI 节点图像,你可以剥离图层,获得更快的构建。更新到下一代执行器就像更新图像名称一样简单。

当前的配置是在 Node 9.9.0 中构建和测试的,但是我们希望它是使用 Node 的最新版本构建的。为此,我们将用于执行容器的图像替换为我们的下一代图像,如下所示:

docker:
  - image: cimg/node:latest 

如果你对跨多个环境的测试感兴趣,我们也有能力通过节点 orb 设置矩阵作业。这允许您在基本 Node Docker 层之上指定要测试的 Node 的不同版本。

并行的最佳实践

配置你的任务在多个容器中并行运行加速你的构建。例如,如果您有一个长时间运行的测试套件,其中有数百个独立的测试,那么可以考虑将这些测试分散到不同的执行器上同时运行。真正优化的配置意味着明智地使用并行性。您应该仔细考虑您运行了多少个并行执行器,以及拆分任务节省的时间是否值得多个容器的启动时间。还要确保您在这些执行器之间正确地分割了您的测试。
考虑下一个例子。这个测试工作的主要活动是使用npm run test命令运行测试:

test:
 …
 parallelism: 10
 steps:
    ...
     - run: CI=true npm run test 

虽然使用并行是朝着正确方向迈出的一步,但这并不是最佳的写法。这个命令将简单地在所有 10 个容器上运行完全相同的测试。为了跨多个容器分配测试,这个配置需要使用来自 CircleCI CLI 的 circleci tests split命令。当按文件名、类名或计时数据分割时,测试可以自动地在这些容器之间分配。通过计时数据进行分割对于并行化来说是理想的,因为它将测试均匀地分布在各个容器上,所以不会有运行速度更快的容器空闲地等待长时间运行的测试容器。

最后,考虑这是否是这个测试套件的正确并行级别。如果启动环境需要大约 30 秒,但是在每个容器中测试只需要 30 秒,那么可能值得考虑降低并行度,以便在所有作业运行中花费更少的时间。测试运行时间和加速时间之间没有黄金比例,但是对于一个最佳的构建应该考虑这个比例。下面是优化后的配置的样子,优化后的配置通过文件名和计时来分割测试,并在给定的容器中运行更多的测试:

test:
 …
 parallelism: 5
 steps:
    ...
     - run: |
            TESTFILES=$(circleci tests glob "test/**/*.test.js" | circleci tests split --split-by=timings)
            CI=true npm run test $TESTFILES 

缓存的最佳实践

使用缓存加速构建。这允许您重用耗时的获取操作中的数据。以下示例使用缓存从以前的作业运行中恢复其 npm 依赖关系。因为 npm 依赖项被缓存,npm install步骤将只需要下载在package.json文件中描述的新依赖项。这种依赖关系缓存通常与 npm、Yarn、Bundler 或 pip 等包依赖关系管理器一起使用,它依赖于两个特殊的步骤,即restore_cachesave_cache。该示例展示了如何在test作业中使用这些缓存步骤:

test:
  ...
  steps:
    …
    - restore_cache:
        keys:
          - v1-deps-{{ checksum "package-lock.json" }}
    - run: npm install
    - save_cache:
        key: v1-deps-{{ checksum "package-lock.json" }}
        paths:
          - node_modules 

注意到restore_cachesave_cache步骤都使用了键。关键字是定位缓存的唯一标识符。save_cache步骤指定在这个键下缓存哪些目录。在这种情况下,我们将保存node_modules目录,以便这些节点依赖项可以在以后的作业中使用。restore_cache步骤使用这个键来查找要恢复到作业的缓存。密钥是一个字符串,其中包含标识缓存的版本和编写为checksum “package-lock.json”的依赖项清单文件的内插散列。

虽然这是恢复和保存缓存的标准模式,但您可以使用回退键进一步优化它。回退键允许您标识一组可能的缓存,以增加缓存命中的可能性。例如,如果将单个包添加到该应用程序的package.json中,checksum 生成的字符串将会改变,整个缓存将会丢失。然而,添加具有更广泛的可能的键匹配集合的回退键可以识别其他可用的缓存。下面是一个添加了回退密钥的缓存恢复示例:

test:
  ...
  steps:
    …
    - restore_cache:
        keys:
          - v1-deps-{{ checksum "package-lock.json" }}
          - v1-deps- 

请注意,我们刚刚向键列表中添加了另一个元素。让我们回到我们的package.json中单个包发生变化的场景。在这种情况下,第一个关键字将导致缓存未命中。然而,第二个键允许恢复先前保存在旧的package.json文件中的缓存。依赖项安装步骤npm install将只需要获取改变的包,而不是对所有的包使用不必要的和昂贵的获取操作。访问我们的文档,阅读更多关于回退键和部分缓存恢复的信息。

有选择地坚持工作空间

下游作业可能需要访问前一个作业中生成的数据。工作区允许您在工作流的整个生命周期中存储文件。下一个示例配置说明了这个概念。build作业构建一个节点应用程序。工作流中的下一个作业部署应用程序。这个配置将整个工作目录持久化到build中的工作区,然后将目录附加到deploy中,因此 deploy 可以访问构建的应用程序。

 build:
    ...
    steps:
      ...
      - run: npm run build
      - persist_to_workspace:
          root: .
          paths:
            - '*'
  deploy:
    ...
    steps:
       ....
        - attach_workspace:
            at: . 

build作业中创建的应用程序目录可由deploy作业访问。有效果,但不理想。工作区本质上只是创建 tarball 并将它们存储在 blob 存储中,附加工作区需要下载和解压缩这些 tarball。这可能是一个耗时的过程。有选择地保存您以后的作业需要的文件会更快。本例中的npm run build步骤产生一个build目录,该目录可以被压缩,然后存储在工作区中以供部署。以下是此配置的优化版本:

 build:
    ...
    steps:
      ...
      - run: npm run build
      - run: mkdir tmp && zip -r tmp/build.zip build
      - persist_to_workspace:
          root: .
          paths:
            - 'tmp'

  deploy:
    ...
    steps:
       ....
        - attach_workspace:
            at: . 

包含构建工件的tmp目录现在将被挂载到项目的工作目录中。这个配置有选择地存储压缩的、构建的应用程序,而不是上传和下载整个工作目录,以节省归档、上传和下载工作空间所花费的时间。压缩文件可以存储在工作区的临时目录中。任何附加了工作空间的下游作业现在都可以访问这个 zip 文件。你可以在这个深入 CircleCI 工作区中了解更多。

机密管理的最佳实践

您不希望将您的秘密签入到版本控制中,并且不应该在您的配置中以纯文本的形式编写秘密。CircleCI 为您提供访问上下文的权限。这些允许您在组织中的项目间保护和共享环境变量。上下文本质上是一个秘密的存储,您可以将环境变量设置为在运行时注入的名称/值对。为了更好地理解这一点,请查看下一个代码示例,一个不安全的配置。这个配置包括一个用明文编写的 AWS 机密定义的deploy任务。

deploy:
 …
 steps:
    ...
     - run: 
            name: Configure AWS Access Key ID
            command: |
              aws configure set aws_access_key_id K4GMW195WJKGCWVLGPZG --profile default
        - run: 
            name: Configure AWS Secret Access Key
            command: |
              aws configure set aws_secret_access_key ka1rt3Rff8beXPTEmvVF4j4DZX3gbi6Y521W1oAt --profile default 

注意 : 这些是仅用于演示目的的虚假凭证。

在 CircleCI 上,所有能够访问您的项目的开发人员都可以看到这个文本。相反,这些秘密应该作为环境变量存储在上下文中。将密钥和访问 ID 作为键/值对添加到标题为aws_secrets的上下文中,可以将它们作为环境变量进行访问。然后,可以将此上下文应用于工作流中的作业。该配置的安全版本如下所示:

deploy:
 …
 steps:
    ...
     - run: 
            name: Configure AWS Access Key ID
            command: |
              aws configure set aws_access_key_id ${AWS_ACCESS_KEY_ID} --profile default
        - run: 
            name: Configure AWS Secret Access Key
            command: |
              aws configure set aws_secret_access_key ${AWS_SECRET_ACCESS_KEY} --profile default

workflows:
  test-build-deploy:
     ...
      - deploy:
          context: aws_secrets
          requires:
            - build 

请注意,机密已经从纯文本变成了环境变量,并且上下文应用于工作流中的作业。为了增加安全性,我们采用了秘密屏蔽来防止用户意外打印秘密值。

orb 和可重用的配置元素

因此,您已经为您的构建选择了正确的执行者,您正在适当地分割您的测试,并且您正在坚持工作空间以避免重复您的工作。现在你必须为你所有的其他项目这样做。多痛苦啊,我说的对吗?如果有一种方法可以在多次构建之间重用配置文件的共享元素就好了。好消息。

Circle CI 提供了一个名为 orbs 的特性,它允许您在一个中心位置定义配置元素,并快速方便地在多个项目中重用它们。不仅如此,你实际上可以传递参数到 orbs。您可以创建一个 orb,根据您传递给它的参数,它可以在不同的项目中做多种不同的事情。

使用我们的 2.1 配置版本,您还可以定义配置的可重用元素,以便在同一管道的多个作业中重用,从简单的作业步骤到重用整个执行器。您还可以将参数传递给这些可重用的元素。当您需要跨管道的多个不同部分重用配置文件的多个元素时,这很有用。

球体在实践中是什么样子的?这里有一个部署到 S3 存储桶的例子,完全写在配置文件中,没有使用我们的 AWS S3 部署 orb :

- deploy:
    name: S3 Sync
    command: |+
      aws s3 sync \
        build s3://my-s3-bucket-name/my-application --delete \
        --acl public-read \
      --cache-control "max-age=86400" 

那将完成工作。但这是使用 S3 球体后的样子:

- aws-s3/sync:
     from: bucket
     to: ‘s3://my-s3-bucket-name/my-application’
     arguments: |
       --acl public-read \
       --cache-control "max-age=86400" 

您不需要为 S3 部署声明单独的部署阶段。您可以简单地从 orb 调用 S3 同步,作为配置文件中的一个步骤。请注意,仍然包含了许多相同的信息,但是它现在表示为传递到 orb 中的参数,而不是配置文件中的脚本。这不仅更加简洁,而且还使得根据需要添加、删除或更改参数来更改 S3 部署变得非常容易。这个版本更容易一目了然,您可以通过更新 orb 来扩展多个项目。从这里提到的所有建议中,最重要的一点是,D.R.Y (不要重复自己)不仅仅是一件审美的事情。有了 orbs,跨项目复制的能力就是黄金。快乐的球体优化!

配置审查

这篇文章提供了 6 种方法来优化你自己的配置。如果您需要帮助,您应该知道我们在黄金和白金高级支持计划中提供配置审查高级服务。通过配置审查服务,CircleCI DevOps 客户工程师将与您的团队合作构建新配置或审查现有配置。我们将审查您的需求并提供建议,以充分利用 CircleCI 的功能。要了解更多关于优质服务和支持计划,请联系我们在 cs@circleci.com。


如果没有整个客户工程团队的共同努力,这篇文章是不可能完成的:Anna Calinawan、Johanna Griffin 和 Grant MacGillivray。

六件你不知道可以用 CircleCI Insights | CircleCI 做的事情

原文:https://circleci.com/blog/six-things-about-insights/

CircleCI 上有数百种功能,旨在为我们的用户创造最佳的 CI/CD 体验。但用户经常指出的平台上最有价值的一个功能是 CircleCI Insights 仪表板。

Insights dashboard 提供对工作状态、持续时间监控和管道优化解决方案等指标的全面了解。作为开发人员,它让您可以完全控制您的构建过程,提供快速反馈和更有效地利用资源。

洞察仪表板核心功能

仔细看看下面仪表板的核心功能。

管道优化

Insights 帮助在构建、测试和部署过程中确定关键的优化目标。在仪表板中,您可以通过识别趋势、查看什么在工作,以及哪些测试可能会失败来快速查看项目的健康状况。

测试

仪表板还可以让您鸟瞰测试洞察,密切关注端到端的片状测试检测。只需几次点击,您就可以确定您的测试是否失败,并快速找到解决方案。

工作流程

使用简单的 YAML 配置,您可以深入了解您的工作流,从而协调并发作业运行并高效地执行它们。这也可以缩短您的反馈循环,帮助您做出更好的总体工程决策。点击了解更多关于 CircleCI 工作流程的信息。

利用 Insights,您可以做很多事情来确保您从 CircleCI 中获得最大收益。请继续阅读,了解一些最具影响力的功能。

1.确保管道数据的准确性

使用 CircleCI 直接内置的专门算法,您可以确保您的管道始终如一地提供准确的数据。借助 Insights 异常值检测工具,您的团队可以快速、轻松地隐藏异常值,以平衡过大值的影响,为您提供更准确的数据视图。

Workflow runs with outliers hidden

Runs including outliers

2.使用资源类洞察力来节省使用成本

监控您的 CPU/RAM 使用情况有助于您和您的团队更好地了解您的资源消耗,以便围绕您的使用成本和管道优化做出最明智的决策。类似下图的报告允许您准确地确定您的使用情况。如果使用率高,您可以考虑升级您的资源类,如果使用率低,您可以通过转移到更低的资源类来节省资金。

Resource usage

3.通过片状测试扫描和检测提高您的效率

片状测试扫描是 Insights 仪表板的一项持续功能。仪表板显示了测试性能的高级概述,但也允许您的团队访问更细粒度的分析。通过薄片式的测试扫描和检测,您可以获得在您的活动或已完成的构建中哪些测试失败的近距离和个性化的视图。当检测到不稳定的测试时,Insights dashboard 会立即将测试标记为不稳定的,这样可以更容易地过滤掉构建中可能需要进一步关注的组件。

4.节省大量开发时间

在节省开发时间方面,并行性是最有益的洞察特性之一。该功能允许使用单个计算资源同时运行测试。尽管 CircleCI 中的测试拆分可以支持许多不同的框架,但并行性通过在您的测试套件中进行测试拆分节省了大量时间——甚至跨多个执行环境。

Parallel testing ROI

5.深入挖掘您的工作数据

您的职务数据包含大量有价值的信息。从 Insights dashboard 下载作业数据,以更全面地检查和理解您的数据,使用它进行自我修正,或与您的团队共享,以进一步优化您的管道。

Job data

6.确定哪些项目需要测试优化

CircleCI API 让您的团队有机会确定哪些项目可以从测试优化中受益。在 Insights 主仪表板中,构建自定义仪表板,供您和您的团队通过 API 使用。在这些仪表板中,添加特定的见解,如单个项目或整个组织的汇总指标和趋势。

Tests to optimize

软件交付中的一个常见挑战是缺乏对管道、构建过程和项目的全面了解。 CircleCI Insights 是帮助您和您的团队完全掌控开发流程的第一步。最棒的是,您可以对其进行定制,以便在您需要时准确地提供您需要的内容。

查看你自己的 CircleCI 见解

松散集成- CircleCI

原文:https://circleci.com/blog/slack-integration/

编者按 2020 年 10 月 9 日:你现在可以使用 CircleCI Slack orb 将 Slack 与 CircleCI 集成,支持 Slack Kit Builder ,用于在职通知。我们有一个关于如何设置它的教程。CircleCI Slack orb 是我们迄今为止最受欢迎的orb之一。有关 CircleCI orbs 的更多信息,请参见此处的公告。要查看所有可用的球体,请访问 CircleCI 球体注册表


上周,我们将构建通知支持扩展到了 Slack.com!Slack 是现代团队的实时消息、存档和搜索。

CircleCI 的聊天通知可以帮助团队中的每个人了解您的最新构建状态。当构建失败或通过时,您可以看到哪个提交触发了构建,以及谁负责将代码推送到 GitHub。这是一种很好的方式,可以让您随时掌握合作者正在进行的工作,并快速注意和修复有问题的构建。

设置集成非常简单:

当登录到您的 Slack 页面时,从 Slack 菜单转到配置集成,然后选择 CircleCI

hackpad.com_tc36SZAIBRY_p.124605_1398208464652_Add Service Integrations   Circle Slack

选择发布 CircleCI 通知的频道后,点击绿色的“添加 CircleCI 集成”按钮,然后复制提供的 webhook URL。

在您的 CircleCI 账户中,点击您想要监控的项目旁边的项目设置图标。

circleci_step1

在左侧栏中,点击聊天室。找到 Slack 部分并添加 webhook URL,然后在完成后按下保存通知挂钩按钮。

circleci_step2

就是这样!在你的下一个版本中,你会在你的 Slack 聊天室中看到 CircleCI 版本通知。

非常感谢 Slack 的朋友们与我们一起完成这个项目!

CI/CD 管道中的烟雾测试

原文:https://circleci.com/blog/smoke-tests-in-cicd-pipelines/

这里有一个困扰许多开发团队的常见情况。您通过 CI/CD 管道运行一个应用程序,所有测试都通过了,这很好。但是,当您将它部署到一个活动的目标环境中时,该应用程序并不像预期的那样运行。你不能总是预测当你的应用被实时推送时会发生什么。解决办法?冒烟测试旨在通过运行覆盖应用程序的关键组件和功能的测试用例来尽早揭示这些类型的故障。它们还可以确保应用程序在部署的场景中按预期运行。当实现时,冒烟测试通常在每个应用构建上执行,以在进入更广泛和耗时的测试之前验证基本但关键的功能通过。冒烟测试有助于创建对软件开发生命周期至关重要的快速反馈循环。

在这篇文章中,我将演示如何在 CI/CD 管道的部署阶段添加冒烟测试。冒烟测试将测试部署后应用程序的简单方面。

用于烟雾测试的技术

这篇文章将引用以下技术:

先决条件

这篇文章依赖于我上一篇文章中的配置和代码,使用基础设施作为代码从您的管道中自动发布。完整的源代码可以在这个回购中找到。

从烟雾测试中获得最大收益

冒烟测试非常适合暴露意外的构建错误、连接错误,以及在新版本部署到目标环境后验证服务器的预期响应。例如,一个快速、简单的冒烟测试可以验证一个应用程序是可访问的,并且使用预期的响应代码(如OK 200300301404等)进行响应。本文中的例子将测试部署的应用程序是否用一个OK 200服务器代码进行响应,还将验证默认页面内容是否呈现预期的文本。

运行 CI/CD 管道,不进行烟雾测试

让我们来看一个示例管道配置,它被设计来运行单元测试、构建和推送 Docker 映像到 Docker Hub。该管道还使用基础设施作为代码 ( Pulumi )来提供新的 Google Kubernetes 引擎(GKE)集群,并将该版本部署到该集群。这个管道配置示例没有实现冒烟测试。请注意,如果您运行这个特定的管道示例,将会创建一个新的 GKE 集群,它将一直存在,直到您手动运行pulumi destroy命令来终止它创建的所有基础设施。

注意: 不终止基础设施将导致意外成本。

version: 2.1
orbs:
  pulumi: pulumi/pulumi@2.0.0
jobs:
  build_test:
    docker:
      - image: cimg/python:3.8.1
        environment:
          PIPENV_VENV_IN_PROJECT: 'true'
    steps:
      - checkout
      - run:
          name: Install Python Dependencies
          command: |
            pipenv install --skip-lock
      - run:
          name: Run Tests
          command: |
            pipenv run pytest
  build_push_image:
    docker:
      - image: cimg/python:3.8.1
    steps:
      - checkout
      - setup_remote_docker:
          docker_layer_caching: false
      - run:
          name: Build and push Docker image
          command: |       
            pipenv install --skip-lock
            pipenv run pip install --upgrade 'setuptools<45.0.0'
            pipenv run pyinstaller -F hello_world.py
            echo 'export TAG=${CIRCLE_SHA1}' >> $BASH_ENV
            echo 'export IMAGE_NAME=orb-pulumi-gcp' >> $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
  deploy_to_gcp:
    docker:
      - image: cimg/python:3.8.1
        environment:
          CLOUDSDK_PYTHON: '/usr/bin/python2.7'
          GOOGLE_SDK_PATH: '~/google-cloud-sdk/'
    steps:
      - checkout
      - pulumi/login:
          version: "2.0.0"
          access-token: ${PULUMI_ACCESS_TOKEN}
      - run:
          name: Install dependencies
          command: |
            cd ~/
            pip install --user -r project/requirements.txt
            curl -o gcp-cli.tar.gz https://dl.google.com/dl/cloudsdk/channels/rapid/google-cloud-sdk.tar.gz
            tar -xzvf gcp-cli.tar.gz
            echo ${GOOGLE_CLOUD_KEYS} | base64 --decode --ignore-garbage > ${HOME}/project/pulumi/gcp/gke/cicd_demo_gcp_creds.json
            ./google-cloud-sdk/install.sh  --quiet
            echo 'export PATH=$PATH:~/google-cloud-sdk/bin' >> $BASH_ENV
            source $BASH_ENV
            gcloud auth activate-service-account --key-file ${HOME}/project/pulumi/gcp/gke/cicd_demo_gcp_creds.json
      - pulumi/update:
          stack: k8s
          working_directory: ${HOME}/project/pulumi/gcp/gke/
workflows:
  build_test_deploy:
    jobs:
      - build_test
      - build_push_image
      - deploy_to_gcp:
          requires:
          - build_test
          - build_push_image 

这个管道将新的应用程序版本部署到一个新的 GKE 集群,但是我们不知道在所有这些自动化完成之后,应用程序是否真正启动并运行。我们如何发现应用程序是否已经在这个新的 GKE 集群中部署并正常运行?冒烟测试是在部署后快速、轻松地验证应用程序状态的好方法。

如何编写冒烟测试?

第一步是开发测试用例,定义验证应用程序功能所需的步骤。确定您想要验证的功能,然后创建场景来测试它。在本教程中,我有意描述一个非常小的测试范围。对于我们的示例项目,我最关心的是验证应用程序在部署后是否可访问,以及提供的默认页面是否呈现预期的静态文本。

我更喜欢列出我想要测试的项目,因为这符合我的开发风格。大纲显示了我在开发此应用程序的冒烟测试时考虑的因素。下面是我如何为这个冒烟测试开发测试用例的一个例子:

  • 什么语言/测试框架?
  • 该测试应在何时执行?
    • 创建 GKE 集群后
  • 会考什么?
    • 测试:应用程序部署后是否可访问?
      • 预期结果:服务器以代码200响应
    • 测试:默认页面是否呈现文本“欢迎使用 CI/CD”
    • 测试:默认页面是否呈现文本“版本号:”
  • 测试后操作(无论通过还是失败都必须发生)
    • 将测试结果写入标准输出
    • 摧毁 GKE 集群和相关基础设施

我的测试用例大纲(也称为测试脚本)对于本教程来说是完整的,并且清楚地显示了我对测试感兴趣的内容。在这篇文章中,我将使用一个基于 bash 的开源冒烟测试框架来编写冒烟测试,这个框架由 asm89 开发,名为 smoke.sh 。对于您自己的项目,您可以用您喜欢的任何语言或框架编写冒烟测试。我选择了smoke.sh,因为它是一个易于实现的框架,并且是开源的。现在让我们探索如何使用smoke.sh框架来表达这个测试脚本。

使用 smoke.sh 创建冒烟测试

框架的文档描述了如何使用它。下一个示例代码块展示了我如何使用在示例代码的 repotest/目录中找到的smoke_test文件。

#!/bin/bash

. tests/smoke.sh

TIME_OUT=300
TIME_OUT_COUNT=0
PULUMI_STACK="k8s"
PULUMI_CWD="pulumi/gcp/gke/"
SMOKE_IP=$(pulumi stack --stack $PULUMI_STACK --cwd $PULUMI_CWD output app_endpoint_ip)
SMOKE_URL="http://$SMOKE_IP"

while true
do
  STATUS=$(curl -s -o /dev/null -w '%{http_code}' $SMOKE_URL)
  if [ $STATUS -eq 200 ]; then
    smoke_url_ok $SMOKE_URL
    smoke_assert_body "Welcome to CI/CD"
    smoke_assert_body "Version Number:"
    smoke_report
    echo "\n\n"
    echo 'Smoke Tests Successfully Completed.'
    echo 'Terminating the Kubernetes Cluster in 300 second...'
    sleep 300
    pulumi destroy --stack $PULUMI_STACK --cwd $PULUMI_CWD --yes
    break
  elif [[ $TIME_OUT_COUNT -gt $TIME_OUT ]]; then
    echo "Process has Timed out! Elapsed Timeout Count.. $TIME_OUT_COUNT"
    pulumi destroy --stack $PULUMI_STACK --cwd $PULUMI_CWD --yes
    exit 1
  else
    echo "Checking Status on host $SMOKE... $TIME_OUT_COUNT seconds elapsed"
    TIME_OUT_COUNT=$((TIME_OUT_COUNT+10))
  fi
  sleep 10
done 

接下来,我将解释这个 smoke_test 文件中发生了什么。

smoke_test 文件的逐行描述

让我们从文件的顶部开始。

#!/bin/bash

. tests/smoke.sh 

这个代码片段指定了要使用的 Bash 二进制文件,还指定了要导入/包含在smoke_test脚本中的核心smoke.sh框架的文件路径。

TIME_OUT=300
TIME_OUT_COUNT=0
PULUMI_STACK="k8s"
PULUMI_CWD="pulumi/gcp/gke/"
SMOKE_IP=$(pulumi stack --stack $PULUMI_STACK --cwd $PULUMI_CWD output app_endpoint_ip)
SMOKE_URL="http://$SMOKE_IP" 

这个片段定义了将在整个smoke_test脚本中使用的环境变量。以下是每个环境变量及其用途的列表:

  • PULUMI_STACK="k8s"由 Pulumi 用来指定 Pulumi 应用程序堆栈。
  • PULUMI_CWD="pulumi/gcp/gke/"是 Pulumi 基础设施代码的路径。
  • SMOKE_IP=$(pulumi stack --stack $PULUMI_STACK --cwd $PULUMI_CWD output app_endpoint_ip)是 Pulumi 命令,用于检索 GKE 集群上应用程序的公共 IP 地址。该变量在整个脚本中被引用。
  • SMOKE_URL="http://$SMOKE_IP"指定 GKE 集群上应用程序的 url 端点。
while true
do
  STATUS=$(curl -s -o /dev/null -w '%{http_code}' $SMOKE_URL)
  if [ $STATUS -eq 200 ]; then
    smoke_url_ok $SMOKE_URL
    smoke_assert_body "Welcome to CI/CD"
    smoke_assert_body "Version Number:"
    smoke_report
    echo "\n\n"
    echo 'Smoke Tests Successfully Completed.'
    echo 'Terminating the Kubernetes Cluster in 300 second...'
    sleep 300
    pulumi destroy --stack $PULUMI_STACK --cwd $PULUMI_CWD --yes
    break
  elif [[ $TIME_OUT_COUNT -gt $TIME_OUT ]]; then
    echo "Process has Timed out! Elapsed Timeout Count.. $TIME_OUT_COUNT"
    pulumi destroy --stack $PULUMI_STACK --cwd $PULUMI_CWD --yes
    exit 1
  else
    echo "Checking Status on host $SMOKE... $TIME_OUT_COUNT seconds elapsed"
    TIME_OUT_COUNT=$((TIME_OUT_COUNT+10))
  fi
  sleep 10
done 

这个片段是所有奇迹发生的地方。这是一个while循环,一直执行到条件为真或者脚本退出。在这种情况下,循环使用一个curl命令来测试应用程序是否返回一个OK 200响应代码。因为这条管道正在从头开始创建一个全新的 GKE 集群,所以在我们开始冒烟测试之前,需要完成 Google 云平台中的一些事务。

  • GKE 群集和应用程序服务必须启动并运行。
  • 用 curl 请求的结果填充$STATUS变量,然后测试200的值。否则,循环会将$TIME_OUT_COUNT变量增加 10 秒,然后等待 10 秒来重复curl请求,直到应用程序做出响应。
  • 一旦集群和应用启动、运行并响应,STATUS变量将产生一个200响应代码,其余的测试将继续进行。

smoke_assert_body "Welcome to CI/CD"smoke_assert_body "Version Number: "语句中,我测试了被调用的网页上的欢迎文本和版本号文本。如果结果为假,测试将失败,这将导致管道失败。如果结果为真,那么应用程序将返回一个200响应代码,我们的文本测试将产生TRUE。我们的冒烟测试将通过并执行pulumi destroy命令,该命令终止了为这个测试用例创建的所有基础设施。由于不再需要该集群,它将终止在该测试中创建的所有基础设施。

这个循环还有一个elif (else if)语句,用于检查应用程序是否超过了$TIME_OUT值。elif语句是异常处理的一个例子,它使我们能够控制意外结果发生时会发生什么。如果$TIME_OUT_COUNT值超过TIME_OUT值,则pulumi destroy命令被执行并终止新创建的基础设施。然后,exit 1命令会使您的管道构建过程失败。不管测试结果如何,GKE 集群都将被终止,因为除了测试之外,这个基础设施真的没有存在的必要。

向管道添加烟雾测试

我已经解释了冒烟测试的例子和我开发测试用例的过程。现在是时候将它集成到上面的 CI/CD 管道配置中了。我们将在deploy_to_gcp作业的pulumi/update步骤下添加一个新的run步骤:

 ...
      - run:
          name: Run Smoke Test against GKE
          command: |
            echo 'Initializing Smoke Tests on the GKE Cluster'
            ./tests/smoke_test
            echo "GKE Cluster Tested & Destroyed"
      ... 

这个片段演示了如何将smoke_test脚本集成并执行到现有的 CI/CD 管道中。添加这个新的运行块可以确保每个管道构建都将在一个活动的 GKE 集群上测试应用程序,并提供应用程序通过所有测试用例的验证。您可以确信,当部署到测试的目标环境(在本例中是一个 Google Kubernetes 集群)时,特定版本将正常运行。

包扎

总之,我已经讨论并展示了在 CI/CD 管道中使用冒烟测试和基础设施作为代码来测试目标部署环境中的构建的优势。在应用程序的目标环境中测试应用程序,有助于深入了解应用程序在部署后的行为。将冒烟测试集成到 CI/CD 管道中增加了应用程序构建的另一层信心。

如果您有任何问题、评论或反馈,请随时在 Twitter 上 ping 我 @punkdata

感谢阅读!

在 Jest 中创建快照以测试 React 应用程序

原文:https://circleci.com/blog/snapshot-testing-with-jest/

本教程涵盖:

  1. 设置 Jest 快照
  2. 使用快照来模拟测试的 UI 更改
  3. 使用快照测试来确定更改是否是有意的

自动化测试在有许多活动部件的大型应用程序中尤其重要。了解测试应用程序的许多方法是明智的,这样你就可以提供尽可能多的覆盖面。如果您不熟悉在测试中使用快照,请继续阅读。快照测试是作为前端测试自动化的一部分编写的。

在本教程中,我将带领您使用 JavaScript 测试框架 Jest 来创建测试简单的 React web 应用程序的快照。使用 Jest 快照将帮助您确保您的 UI 更改是确定性的,并且您知道何时进行了更改。使用这些信息,您可以确定这些更改是否是有意的。您将使用 Jest 创建的快照来模拟 React 应用程序中的变化。如果不断改变文本断言是痛苦的,您可能会发现快照测试是一种强大的解毒剂。

先决条件

要跟随本教程,您需要以下内容:

  • 本地安装的节点。
  • 一个 GitHub 账户。
  • 一个 CircleCI 账户。
  • JavaScript、React、Git 和 Jest 的基础知识。

我们的教程是平台无关的,但是使用 CircleCI 作为例子。如果你没有 CircleCI 账号,请在 注册一个免费的

开玩笑的快照测试

Jest 是一个 JavaScript 测试框架,它使得编写前端测试如快照、单元测试和组件测试变得简单高效。快照测试是一种输出比较测试。这种类型的测试确保您的应用程序符合开发团队的质量特征和代码值。

快照测试如何工作

当您想要确保您的 UI 不会意外更改时,快照测试非常有用。一个典型的快照测试用例呈现一个 UI 组件,获取一个快照,然后将它与测试旁边存储的一个参考快照文件进行比较。该测试将应用程序的当前状态与已建立的快照和预期行为进行比较。

可视化快照测试过程

下图说明了 Jest 中快照测试的过程。它显示了快照通过时、快照测试失败时的不同结果,以及发生了什么操作。

Snapshot testing

设置样本 React 应用程序

在本教程中,您的应用程序将由一个简单的 React 组件组成,该组件带有两个按钮,单击时会增加和减少计数。

通过在终端中运行以下命令来克隆存储库:

git clone https://github.com:mwaz/jest-snapshot-testing.git;

cd jest-snapshot-testing; 

接下来,您需要从 npm React 测试库JestReact 测试呈现器安装以下依赖项。

它们安装在您的package.json文件中。要在您的系统中安装它们,请打开您的终端并运行:

npm install 

安装完依赖项后,使用以下命令运行应用程序:

npm start 

这将启动您的应用程序。

Jest test application

您的测试将确定计数器值是否初始化为零,以及按钮是否工作。因为值根据所单击的按钮而变化,所以您可能想知道增量值或减量值保持不变,总是增加或减少 1。您现在可以开始编写测试了。

编写快照测试

Jest 使用正则表达式来查找扩展名为.test.js.test.jsx的文件。一旦遇到带有这些扩展名的测试文件,在执行 test 命令时,Jest 将自动运行这些文件中的测试。

为了编写您的第一个快照测试,您将使用renderer模块。该模块呈现文档对象模型( DOM )元素,该元素将被保存为文本快照:

import renderer from "react-test-renderer"; 

编写您的测试以确保它捕获了<App>组件的渲染,并将其保存为 Jest 快照。这是测试的结构:

import React from "react";
import renderer from "react-test-renderer";
import App from "./App";

describe("Jest Snapshot testing suite", () => {
  it("Matches DOM Snapshot", () => {
    const domTree = renderer.create(<App />).toJSON();
    expect(tree).toMatchSnapshot();
  });
}); 

该测试有一个domTree变量,它保存 JSON 格式的呈现组件的 DOM 树。这使得保存和比较快照更加容易。expect(domTree).toMatchSnapshot()如果快照不存在,则创建快照,保存它,并检查快照是否与以前存储的快照一致。如果存在现有的快照,Jest 会比较这两个快照。如果匹配,测试通过。不匹配的快照会导致测试失败。测试还使用了.toJSON()方法,该方法返回呈现的 DOM 树快照的 JSON 对象。

一旦你运行测试(使用命令npm test),就会有一个名为(__snapshots__)的新文件夹,里面有文件App.test.js.snap。该文件包含已保存的快照,应该类似于以下代码片段:

exports[`Jest Snapshot testing suite matches snapshot 1`] = `
<div
  className="App"
>
  <div
    className="counter"
  >
    <div
      className="buttons"
    >
      <button
        onClick={[Function]}
      >
        Increment
      </button>
      <button
        onClick={[Function]}
      >
        Decrement
      </button>
    </div>
    <p>
      0
    </p>
  </div>
</div>
`; 

这个快照文件显示了组件的 DOM 树,包括父选择器元素和子元素。

为了更好地理解快照,打开运行 React 应用程序的选项卡中的elements部分。与快照并排比较;它们应该是几乎相同的。快照的结构类似于 DOM,这使得识别 DOM 更改的过程无缝。

Application DOM structure

事实上,文本快照是从 DOM 创建的,这意味着只有当 DOM 发生变化或者内容与拍摄快照时不同时,它们才会失败。接下来,您将研究 DOM 中的变化是如何发生的,它们是如何触发快照变化的,以及如何处理这个过程。

处理快照更改

现在您已经知道了快照是如何创建的,是时候了解更多关于它们何时失败以及失败原因的信息了。为了演示这一点,使用前面的测试并对 DOM 树进行更改。您要做的更改是给组件引入一个标题<h1>COUNTER</h1>。该添加显示在文件Counter.js中:

return (
      <div className="counter">
        <h1>COUNTER</h1>
        <div className="buttons">
          <button onClick={this.increment}>Increment</button>
          <button onClick={this.decrement}>Decrement</button>
        </div>
        <p>{this.state.count}</p>
      </div>
    ); 

完成这一更改后,再次运行测试。测试应该会失败。

Failed snapshot

因为这些更改是意料之中的,所以您需要更新现有的快照,而不是更改代码来匹配以前的快照。选择 u 选项,当 Jest 在watch mode时更新快照。更新快照告诉 Jest,这些更改是有意的,您想要保留。在快照更新被触发之后,您的测试又回到了令人愉快的状态,并且它完美地通过了。

注意: 当 Jest 在watch mode时,应用程序正在被跟踪,因此任何更改都将触发测试的重新运行。要激活watch mode,用--watch参数在 Jest runs 中指定它。

 PASS  src/App.test.js (20.905 s)
  Jest Snapshot testing suite
    √ Matches Snapshot (64 ms)

 > 1 snapshot updated.
Snapshot Summary
 > 1 snapshot updated from 1 test suite.
   ↳ src/App.test.js

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   1 updated, 1 total
Time:        51.348 s
Ran all test suites related to changed files. 

添加更多快照测试

您可以添加更多的快照测试,以确保您的应用程序中所有重要的视觉元素都符合您的 UI 规范和 UX 准则,并且您的应用程序中的一切都正常工作。快照测试是全面前端测试的一部分,还应该包括单元和组件测试。

对于本教程,我将只添加一个快照测试。该测试检查增量功能是否按预期工作。这个代码片段可以在文件App.test.js中找到:

it("Should render 3 after three increments", () => {
    const component = renderer.create(<Counter />);
    component.getInstance().increment();
    component.getInstance().increment();
    component.getInstance().increment();
    expect(component.toJSON()).toMatchSnapshot();
}); 

在这个测试中,计数器组件结构被保存到一个组件变量中。然后,测试访问您的class-based componentincrement()方法,并调用它三次。目标是确保当Increment按钮被点击三次时,呈现的计数是三。此信息保存到快照中,应该会通过。

用 CircleCI 集成快照测试

考虑一下:为什么要运行别人不知道的成功测试?相反,花一点时间与团队的其他成员分享您的测试,这样他们也可以从他们提供的见解中受益。

在本教程中,我将带领您完成使用 CircleCI 执行快照测试的步骤。

在应用程序的根文件夹中,创建一个.circleci文件夹,并添加一个config.yml文件。该文件将包含运行 CircleCI 管道所需的所有配置。

在 CircleCI config.yml 文件中,添加以下配置:

version: 2.1
jobs:
  build:
    working_directory: ~/repo
    docker:
      - image: cimg/node:14.17.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: Jest snapshot tests
          command: npm test
      - store_artifacts:
          path: ~/repo/jest-snapshot-testing 

将您的更改推送到 GitHub

如果您克隆了存储库,那么更改已经存在,这是一个可选步骤。但是,如果您使用不同的存储库并使用相同的配置,您将需要将更改推送到存储库。

保存这个文件,提交并把你的修改推送到你的 GitHub 库。导航到 CircleCI 仪表盘后,点击库名旁边的设置项目

出现提示时,选择main,这是您的默认分支。然后点击设置项目。您的项目将开始在 CircleCI 上运行。

CircleCI branch configuration

CircleCI 仪表板上应该有一个绿色构建。单击它查看构建详细信息。

Successful build

太棒了。您的构建是绿色的,并且您的所有测试都成功执行。

结论

在本教程中,您了解了快照测试,以及它在确保您的 UI 外观和工作符合预期方面有多么有用。您学习了如何编写快照测试,并使用快照作为比较来确保所做的任何更改都是预期的。您还了解了如何在有意更改的情况下更新快照。最后,您集成了 CircleCI 来运行您的测试。我希望你喜欢本教程的项目工作。直到下次,继续编码!


Waweru Mwaura 是一名软件工程师,也是一名专门研究质量工程的终身学习者。他是 Packt 的作者,喜欢阅读工程、金融和技术方面的书籍。你可以在他的网页简介上了解更多关于他的信息。

阅读更多 Waweru Mwaura 的帖子

SOA 与微服务:超越整体

原文:https://circleci.com/blog/soa-vs-microservices/

现代软件开发越来越依赖于分布式、基于服务的架构模式来实现可伸缩性、可靠性以及快速构建、测试和发布周期。两种最流行的基于服务的方法是面向服务的架构(SOA)微服务。在本文中,我们将研究这两种方法,以确定它们的相似之处和不同之处,以及每种方法的一些用例。

由于 SOA 是公认的较老的风格,它可能不适合现代的云原生应用程序。但是它仍然提供了许多好处,使它成为许多不同场景中的最佳选择。通过了解 SOA 的哪些方面仍然相关,哪些部分已经被微服务所取代,您将能够在这两者之间做出选择,用于下一个基于服务的应用程序。

让我们先来感受一下 SOA 到底是什么。

什么是面向服务的架构?

全球研究和咨询公司 Gartner 最初在 20 世纪 90 年代中期创造了术语“面向服务的架构”。结构化信息标准促进组织(OASIS)——IT 行业标准委员会——将 SOA 定义为“一种组织和利用分布式功能的范例,这些功能可能处于不同所有权域的控制之下。”

SOA 是一种构建可以在开发中伸缩的后端的方法——换句话说,是一种可以快速增加新开发人员和附加功能的方法。请注意,SOA 是一种架构风格,而不是特定的实现。在这种架构风格中,一切都以服务为中心(因此得名)。这里,服务是业务领域的功能表示。

通常,服务通过服务总线进行通信,服务存储库识别它们。因此,服务以松散耦合的方式集成,只能在线使用。契约必须确定一个通信 API。这个契约建立了一个特定的接口来触发业务逻辑或访问数据。因此,它使用一些标准化的消息传递协议,这些协议通常比简单的 REST 要复杂得多。

因为通信是通过服务总线进行的,所以它会在需要时定位接收者——或者不需要。理论上,这允许请求排队和异步工作负载执行。但是这里大多数协议都是同步的,这意味着即使是异步工作负载也需要立即响应。

每个服务都应该足够隔离,以便有人可以独立开发它。然而,由于 SOA 完全是关于重用的,任何服务都可能共享或重用甚至是基本的组件,比如数据存储。服务没有框架或编程语言要求。这样,平台访问和开发普遍成为可能。

什么是微服务?

微服务架构是后端系统的另一种方法。UNIX 操作系统范式——“做一件事,并把它做好”——指导着这个体系结构。

事实上,微服务都是关于模块化和解耦后端能力的。小型开发团队创建并发布较小的实体,而不是创建一个大型服务。

虽然网飞在 2000 年代后期推广了微服务方法,但许多其他人在此之前已经尝试过这种模式。事实上,它的起源可以追溯到 2004 年,并且与 SOA 有很强的相似性。这可能就是开发人员最初将微服务方法描述为“细粒度 SOA”的原因

它对 DevOps 的严重依赖使微服务脱颖而出。在理想的微服务实现中,事实的全部来源在于代码——从开发到部署,再到运行时编排。因此,任何微服务都应该尽可能独立,提供自己的数据存储和通信协议。它还应该尽可能的轻便;DevOps 倾向于使用 REST 的简单 HTTP。

虽然微服务的目标是轻量级的,但它们仍然很复杂,并且需要大量资源。给每个团队他们自己的服务世界会导致重复和编排开销。结果,复杂性从构建一个大型应用程序的协调转移到这些小规模服务的编排。

虽然 DevOps 工程师仍然在激烈地讨论正确的微服务规模,但我们总是可以回到从根本上代表业务(子)领域的 SOA 定义。然而,虽然 SOA 最大化了重用,但是微服务试图清理有界的上下文,这最终导致了(想要的)重复。

基于服务和微服务架构解决了哪些问题?

尽管从外表上看非常相似,但 SOA 和微服务在许多方面都有所不同。根本的区别在于共享的代码和责任。虽然 SOA 试图将共享组件带到相互服务中并尽可能多地重用,但微服务方法尽可能少地共享。

一个完整的微服务可能有自己的日志系统、认证处理和其他纯技术功能。同时,SOA 肯定会将这些公共方面放到一个专用的服务中,以便尽可能地共享。

SOA 解决了大型企业应用程序中的一个常见问题,即公司需要将一组现有的应用程序(或服务)置于一个保护伞之下。他们经常使用诸如“企业服务总线”或“服务存储库模式”这样的词语 SOA 包含服务存储库和服务总线等元素并不奇怪。

另一方面,微服务方法是为云原生时代开发的,在这个时代,单个服务在内部和外部都是公开的。这种设置通常将第三方服务、编排平台和定制服务全部组合在云中。

微服务提供的 API 指导和规范不如 SOA 正式。如前所述,DevOps 团队通常使用 HTTP 和 RESTful 原则实现微服务 API,但这绝不是一成不变的。此外,微服务不需要 API 网关之类的组件——理想情况下,根本不使用 API 网关。SOA 范式需要一个中央服务来访问各个服务。

除了共享,我们发现这两种方法对服务实体的解释也不同。服务对于在微服务上下文中构建单个应用程序至关重要,而 SOA 使用服务来集成多个应用程序。

SOA 与微服务:您应该使用哪种方法?

如今,看到新的 SOA 项目相对来说并不常见。原因很简单:约束太多,知识太少。大多数开发人员要么更熟悉微服务方法,要么更喜欢它提供的一些自由。

所以,问题是,什么时候应该使用 SOA?如果您正在构建一个应该(并将永远)作为一个整体工作的平台,但需要强大的功能伸缩性并遵循预定义的业务领域,SOA 是一个很好的选择。基于这些考虑,SOA 是包含许多不同应用程序和服务的大规模企业平台的理想候选。

如果你追求独立的服务,那么微服务方法是更好的选择。这种方法将几乎所有特定的选择都留给了服务。通过这种方式,每个团队可以完全自治,并拥有从编码到部署和运行时行为的完全控制权。

SOA 诞生于经典的企业运营,这并不奇怪。即使在今天,SOA 仍然提供了更大的后端平台所需的许多方面。该架构处理数据一致性和治理,使公司能够完全集中控制平台。相比之下,一个真正的微服务平台有很多个人所有者,它无法从单个地方进行控制。

结论

如果您曾经构建过基于微服务的后端,那么您可能已经遇到了一些 SOA 可以为您解决的挑战。另一方面,SOA 也有自己的复杂性和限制。毕竟,人们如此迅速地加入微服务潮流是有原因的。

了解 SOA 和微服务之间的区别是为给定问题选择正确架构的关键。尽管 monoliths 在许多方面仍有其一席之地,但不要忽视 SOA 的分布式开发方面以及微服务等衍生架构模式。

问题本身应该决定你选择哪种方法,而不是当前的趋势。选择适合给定情况和可用资源的架构模式,您的项目将步入正轨。

通过自动化测试防止 SQL 注入攻击

原文:https://circleci.com/blog/sql-injection/

本教程涵盖:

  1. SQL 注入是什么,为什么它如此具有破坏性
  2. 添加和自动化暴露威胁测试
  3. 修补注射漏洞

SQL 注入攻击的目标是应用程序的数据库,这可能导致不可逆转的后果,导致金钱损失,并降低用户对您公司的信任。每天都有太多的应用程序数据泄露事件发生,通常是在恶意代理攻击数据库时。 SQL 注入是攻击应用程序最具破坏性的方式之一。该攻击诱使应用程序在数据库级别运行精心编制的恶意代码。SQL 注入攻击会导致数据暴露、损坏,甚至永久丢失。

在本教程中,我将演示如何使用自动化测试来检查易受数据库注入攻击的应用程序入口点。

先决条件

要遵循本教程,需要做一些事情:

  1. Javascript 的基础知识
  2. 您系统上安装的 Node.js (版本> = 11.0)
  3. 一个的账户
  4. GitHub 的一个账户

我们的教程是平台无关的,但是使用 CircleCI 作为例子。如果你没有 CircleCI 账号,请在 注册一个免费的

克隆演示项目

要开始这个练习,您需要克隆这个演示项目。该项目是一个简单的 Node.js 用户帐户 API 应用程序,具有以下端点:

  • GET : /users/fetch是获取所有用户的端点
  • POST : /users/fetch是一个POST端点,它接受一个用户id,并使用它来获取一个用户
  • POST : /users/create是一个POST端点,它使用参数nameemail来创建一个新的用户记录

这个项目运行在一个 SQLite 数据库上,该数据库将数据存储在内存中(用于演示目的),并包含一个名为users的表。

通过运行以下命令克隆项目:

git clone --single-branch --branch base-project https://github.com/CIRCLECI-GWP/sql-injection-testing.git 

克隆过程完成后,进入项目的根目录,通过运行以下命令安装依赖项:

cd sql-injection-testing
npm install 

接下来,使用以下命令运行应用程序:

npm run dev 

应用程序将开始监听默认端口3000。打开邮递员,使用http://localhost:3000/users/create端点添加一些用户。(一次只能创建一个用户。)

{
  "email": "john.doe@gmail.com",
  "name": "John Doe"
} 

Add Users - Postman

通过对http://localhost:3000/users/fetch端点进行GET调用来获取用户。

Get Users - Postman

您还可以通过调用/users/fetchPOST端点来检索用户,从而为第一个用户发送一个id参数。

Get User - Postman

添加测试以检查敏感数据暴露威胁

端点POST : /users/fetchPOST : /users/create是应用程序的两个入口点。它们接收应用程序处理后给出结果的数据。这两个端点的处理程序可以在项目根目录的user.js中找到。

...

function getUser(req, res) {

    let sql = `SELECT * FROM users WHERE id='${req.body.id}'`;

    db.all(sql, function (err, data) {
        if(err) throw err

        res.json({
            status : 200,
            data,
            message : "User record retrieved"
        })
    })

}

function createUser(req, res) {

    let sql = `INSERT INTO users(email, name) VALUES ('${req.body.email}', '${req.body.name}')`;

    db.run(sql, function (err) {
        if(err) throw err

        res.json({
            status : 200,
            message : "User successfully created"
        })
    })
} 

两个处理程序都运行直接从请求对象获取用户数据的 SQL 查询。我们可以攻击其中一个端点来暴露其 SQL 注入漏洞。

我们将测试POST : http://localhost:3000/users/fetch端点,它应该接受特定用户的id,并在data数组中返回该用户。我们将使用精心制作的 SQL 注入攻击,攻击该端点,使其暴露数据库中所有用户的数据。

首先,使用以下命令安装jest(测试运行程序)和supertest(用于测试 API 端点):

npm install -D jest supertest 

安装完成后,创建一个文件夹__tests__,并在这个文件夹中创建文件injection.test.js。添加以下代码:

const supertest = require("supertest");
const app = require("../app");
const request = supertest(app);

const db = require("../db");

let createTableSQL =
  "CREATE TABLE IF NOT EXISTS `users` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL , `email` VARCHAR(100) NOT NULL , `name` VARCHAR(240) NOT NULL)";

let insert =
  "INSERT INTO users (name, email) VALUES ('user1@test.com','User 1'), ('user2@test.com','User 2'), ('user3@test.com','User 3')";

test("Test malicious data exposure", (done) => {
  db.run(createTableSQL, function (err) {
    db.run(insert, function () {
      let maliciousUserId = `1' OR 1=1;--`;

      request
        .post("/users/fetch/")
        .send({ id: maliciousUserId })
        .set("Accept", "application/json")
        .expect(200)
        .expect("Content-Type", /json/)
        .end(function (err, res) {
          if (err) return done(err);

          //Should not return more than one record
          expect(res.body.data.length).toBeLessThan(2);
          done();
        });
    });
  });
}); 

这个测试用例从创建users表开始,并用 3 个测试用户播种它。然后,它会创建一个恶意用户id,拦截 SQL 查询并注入一个永远为真的条件。这是将结果限制为仅具有id的用户的条件的替代方案。结果是查询返回了users表中的所有用户。

我们可以测试 API 调用的结果,以确保它不包含一个以上的记录。表中已经有 3 个用户,如果请求返回 1 个以上的结果,它将失败,这表明攻击成功了。

为了完成测试设置,向package.json添加一个test脚本:

"scripts": {
    ...
    "test": "jest"
}, 

现在,您可以通过在项目的根目录下运行以下代码来运行测试:

npm run test 

一旦测试运行,您将得到一个失败的结果。

Test Failed - CLI

如果没有匹配,注入攻击不是返回单个值或根本不返回任何值,而是能够成功地迫使数据库暴露表中包含的超过2个用户。对于恶意用户来说,这种攻击是暴露数据库表中的敏感或私有数据(如电话号码、帐号和信用卡信息)的一种危险方式。

修补注射漏洞

那么,如何解决这个问题呢?SQL 注入通常通过调整代码来检查条件,以验证来自访问应用程序的用户的数据。例如,在上面的例子中,我们可以尝试检查用户提交的值是否为整数,因为id是作为整数存储的。任何恶意的 SQL 都无法通过检查,因为它们是字符串而不是整数。

然而,尽管该策略可以修复POST : http://localhost:3000/users/fetch端点中的漏洞,但在我们的应用程序中处理 SQL 语句还有另一个推荐的最佳实践:在getUser处理程序中的 SQL 命令。

let sql = `SELECT * FROM users WHERE id='${req.body.id}'`; 

将用户输入的值直接传递给我们的 SQL 查询通常被认为是不安全的。我们可以在查询中使用占位符,这些占位符映射到数组或对象中定义的一组值。大多数数据库驱动程序和对象关系映射器 (ORMS)都提供这种功能。对于我们的端点,用这段代码替换users.js文件中的getUser代码处理程序:

function getUser(req, res) {
  let sql = `SELECT * FROM users WHERE id=?`;

  db.all(sql, [req.body.id], function (err, data) {
    if (err) throw err;

    res.json({
      status: 200,
      data,
      message: "User record retrieved"
    });
  });
} 

更新后的处理程序在我们的查询中使用占位符?,然后将一个数组作为第二个参数传递给db.all(),其中包含用户的id。当您重新运行测试时,您将得到一个通过的结果。

Test Passed - CLI

我们的测试现在通过了,因为恶意代码被转换成了与任何用户id都不匹配的无害字符串。没有用户返回,使我们的数据免受不必要的暴露。

自动化测试过程

本教程的目的不仅仅是运行 SQL 注入测试。我们希望自动化整个过程,以便随时更新代码,这些测试运行,以确保我们的数据得到充分保护。

为了自动化 SQL 注入测试的运行,第一步是将项目推送到 GitHub

现在,点击 CircleCI 仪表板上的设置项目按钮。

Add Project - CircleCI

通过定义一个名为.circleci/config.yml的配置文件(可能需要指定分支),CircleCI 会自动检测到它。你可以点击设置项目开始构建。此构建将失败,因为您还没有设置您的配置文件,这是我们的下一步。

Add Config - CircleCI

在项目的根目录下创建一个名为.circleci的文件夹,并在刚刚创建的文件夹中添加一个名为config.yml的配置文件。在该文件中,输入以下代码:

version: 2.1
orbs:
  node: circleci/node@5.0.2
jobs:
  build-and-test:
    executor:
      name: node/default
    steps:
      - checkout
      - node/install-packages
      - run:
          command: npm run test
workflows:
  build-and-test:
    jobs:
      - build-and-test 

这个配置从定义 Node.js orb 开始。Node.js orb 包含一组预打包的 CircleCI 配置,您可以使用这些配置轻松安装 Node.js 及其包管理器(npm、yarn)。

build-and-test工作中,我们有检查/提取最新代码变更、安装包和运行测试套件的步骤。

注意:T3node/install-packages步骤是在 Node.js orb 中预定义的。

提交所有更改并推送到远程存储库,以运行构建管道。你应该得到一个成功的构建。

Build Successful - CircleCI

通过点击 build,您可以查看测试细节。

Build Details - CircleCI

您可以单击单个步骤来获取更多详细信息。

Test Step Details - CircleCI

测试如预期的那样通过了,现在我们有了测试 SQL 注入攻击的自动化管道。

注意 : 避免在生产数据库上运行注入测试;始终在试运行环境中运行这些测试。

结论

数据库是所有数据驱动的应用程序的心脏和灵魂(现在几乎是所有的应用程序)。对数据库的任何攻击都是不可容忍的,因此需要对应用程序代码进行彻底的 SQL 注入攻击测试。在本教程中,我们演示了为公开的端点创建一个简单的注入测试,并自动化测试过程。将你所学到的应用到你的团队正在进行的其他项目中是非常值得的。

编码快乐!


Fikayo Adepoju 是 LinkedIn Learning(Lynda.com)的作者、全栈开发人员、技术作者和技术内容创建者,精通 Web 和移动技术以及 DevOps,拥有 10 多年开发可扩展分布式应用程序的经验。他为 CircleCI、Twilio、Auth0 和 New Stack 博客撰写了 40 多篇文章,并且在他的个人媒体页面上,他喜欢与尽可能多的从中受益的开发人员分享他的知识。你也可以在 Udemy 上查看他的视频课程。

阅读 Fikayo Adepoju 的更多帖子

SQL 与 NoSQL 数据库| CircleCI

原文:https://circleci.com/blog/SQL-vs-NoSQL-databases/

应用程序开发人员可以在两大类数据库之间进行选择:SQL(结构化查询语言)和 NoSQL(不仅仅是 SQL)。SQL 数据库,也称为关系数据库,已经使用了 40 多年。尽管年代久远,SQL 数据库仍然非常受开发人员的欢迎。2021 年 9 月,在 DB-Engines 最受欢迎的数据库管理系统列表的前 10 个结果中,有 6 个是关系型的,或基于 SQL 的。

NoSQL 数据库,或者非关系数据库,在过去十年中已经获得了普及和广泛采用。使用最广泛的 NoSQL 数据库之一 MongoDB 在 DB-Engines 的列表中排名第五,并且是前十名中四个非关系数据库中排名最高的。

那么,SQL 和 NoSQL 之间有什么区别,您的下一个应用程序应该使用哪个数据库呢?在本文中,我们将探讨每种方法的优缺点。

SQL 数据库

SQL 语言是美国国家标准协会(ANSI)标准,有一些方言,如 PL/SQL(在 Oracle 中)和 T-SQL(在 Microsoft SQL Server 中)。编写 ANSI 兼容的 SQL 的优点是,您可以轻松地将脚本转移到另一个 SQL 数据库。

流行的 SQL 数据库包括 Oracle、MySQL、Microsoft SQL Server 和 PostgreSQL。下面,我们将看看使 SQL 成为开发人员普遍选择的一些特性。

SQL 是关系型的

一个常见的误解是,SQL 数据库被认为是关系数据库,因为您可以使用外键来定义数据库中记录之间的关系。事实上,这个名字是基于关系的数学概念,指的是唯一的元组的集合。元组只是值的有序集合。

在 SQL 数据库中,关系被表示为表,关系中的每个元组构成表中的一行(通常称为记录)。它就像一个大型的、正式的 Excel 电子表格,供软件使用。我们把表和行的定义称为模式。

在关系数据库中,您可以(并且通常应该)通过不冗余地存储数据来规范化数据。在过去,存储非常昂贵,规范化数据可以节省存储。

SQL 是健壮的

拥有一个模式有利也有弊。一方面,您总是知道应用程序期望的实体和值。另一方面,它不擅长处理动态数据。

拥有模式意味着您可以验证您的数据。例如,ID 字段必须是唯一的,并且不能为空。还可以强制外键关系,这意味着一条记录不能引用数据库中不存在的另一条记录。

拥有一个规范化的数据库和这些验证使您的数据变得可靠。有了其他 SQL 特权,比如事务,SQL 数据库通常是快速、可靠和健壮的。

NoSQL 数据库

关于 NoSQL 数据库的一个常见误解是“否”意味着数据库中不使用任何 SQL。如前所述,“不”代表“不仅”你可以在 NoSQL 的数据库中找到一些 SQL。

另一个混乱的来源是 NoSQL 数据库没有单一的定义。事实上,NoSQL 数据库有四大类:

  • 文档存储
  • 图形数据库
  • 键值存储
  • 宽列数据存储

一些数据库,比如 Cosmos DB,跨越不同的类别,但是 NoSQL 数据库很少可以互换,而且看起来完全不一样。它们的一个共同点是牺牲一些健壮性来获得速度和可伸缩性。

文档存储

最流行的 NoSQL 数据库类型是文档存储。文档存储看起来最像传统的 SQL 数据库,只是没有模式和规范化。不是列和行,你只是有一个你放进去的东西的集合。

向实体添加新字段很容易,但这意味着一些实体定义了这个字段,而另一些实体没有。您可以使用不同的值多次存储同一个实体。你也很容易把事情弄得一团糟!

然而,这些数据库在具有高度动态数据的环境中发展良好,并且比 SQL 数据库具有更好的伸缩性。文档存储通常可以在多台服务器上运行,而 SQL 数据库通常绑定到一台服务器上。由于文档存储没有所有这些烦人的字段验证,它们的速度非常快!

流行的文档存储包括 MongoDB、DynamoDB、Couchbase、Firebase 和 Cosmos DB。

图形数据库

图形数据库是 NoSQL 数据库的一种特殊类型,非常专业。

这种类型的数据库最常见的用例是“你可能认识的人”的例子。想象一下一些社交网站,比如脸书或 LinkedIn,向你展示你朋友认识的人。

在图数据库中,所有这些人都表示为节点,他们之间的关系表示为边。要找到你朋友的所有朋友,你可以从一个节点开始,简单地“走”边。你会先走到你朋友的边上,然后走到他们朋友的边上。假设你有 200 或 300 个朋友,每个朋友自己也有 200 到 300 个朋友(有一些重叠),你最终会找到 20,000 到 60,000 个节点。您可以通过简单地检查这些节点的所有边来深入了解。

有了足够大的数据集,一个图形数据库只需几秒钟就可以获取所有这些朋友的朋友。SQL 数据库很快就会陷入这样的困境。它需要匹配数百万用户,每个用户都有数百万用户,所有用户都有自己的数百万用户,最终过滤数十亿(双倍)用户。

如果你需要一个图形数据库,一些流行的有 Neo4j,ArangoDB,和 Cosmos DB。

键值存储

可能最简单的 NoSQL 数据库是键值存储。顾名思义,键值存储保存键值对的集合。该值可以是任何值,从数值到带有子对象的复杂对象。

它的应用并不广泛,但是键值存储非常适合缓存或存储会话数据这样的用例。

Redis、Memcached 和 Cosmos DB 是流行的键值存储。

宽列数据存储

宽列数据存储看起来有点像键值存储。但是,键拥有对列的访问,而不是单一的值。

一个值可以由数十亿列组成,并且可以是动态的。想象一个键值存储中的无模式 SQL 数据库或文档数据库。

宽列数据存储是可扩展的,可以容纳高达 Pb 的数据。它们的用例各不相同,例如时间序列数据(如多个服务器的 CPU 使用情况)、金融数据营销、物联网(IoT)数据和图形数据。

这种类型的流行数据库包括 Cassandra、HBase、Bigtable 和 Cosmos DB。

其他 NoSQL 数据库

NoSQL 包括其他类型的数据库,如以平面文本文件为中心的数据库。另外,请记住,我们可以将 SQL 出现之前的所有东西归类为 NoSQL。我们将要提到的一种特殊类型的数据库是搜索引擎。

搜索引擎是专门寻找数据内容的 NoSQL 数据库。它们通常支持复杂的搜索查询、全文搜索、结果排序和分组以及分布式搜索,以实现高可伸缩性。Elasticsearch、Solr 和 Splunk 是流行的搜索引擎。

你可能已经注意到了,运行在 Azure 上的云数据库 Cosmos DB 是一个几乎可以做任何事情的数据库。有各种多模型数据库,或者可以以多种方式存储数据的数据库,就像这样。亚马逊有自己的 DynamoDB,这是一个运行在 AWS 中的多模型数据库。

多模型数据库有一些限制。例如,您不能在一个数据库中使用不同的方法,但是您可以创建多个实例并在每个实例上使用不同的方法。

NewSQL 数据库

有时候,NoSQL 是你唯一的选择。然而,SQL 数据库已经赶上来了,现在在仍然是 SQL 的同时提供了一些 NoSQL 的额外好处。例如,Oracle 和 SQL Server 等数据库使您能够存储动态 JSON,甚至可以使用索引和过滤对这些值的查询。

一些数据库更进一步。例如,雪花是一个托管在云中的分散式 SQL 数据库。它解决了 SQL 不可伸缩的挑战,同时仍然完全保留 SQL。这些类型的数据库通常被称为 NewSQL。

为了让你了解 NewSQL 数据库有多受欢迎,Snowflake 在 2020 年 9 月至 2021 年 9 月的数据库引擎排名中上升了 107 位,攀升至第 21 位(领先 Cosmos DB 5 位)!

其他流行的 NewSQL 数据库包括 CockroachDB 和 Spark SQL。

SQL vs NoSQL:如何选择

有这么多数据库,可能很难选择一个适合你的。你会经常听到,“为正确的工作选择正确的工具。”然而,合适的工具可能只是您的团队已经知道的工具。一个最佳但不熟悉的数据库可能会对您的项目产生负面影响,而一个次优但熟悉的工具可能足以完成这项工作。

如果您决定使用一个新的数据库,无论是 SQL、NoSQL 还是 NewSQL,都要确保您的团队得到正确实现它所需的培训和指导。

对于大多数项目来说,SQL 通常是一个不错的选择,也是一个非常强大的全能工具。然而,对于更专业的工作,NoSQL 数据库可能是更好的选择。例如,Redis 已经成为缓存的流行选择。如果您正在寻找一个快速和可伸缩的数据库,并且不介意牺牲一些健壮性,MongoDB 可能正是您所需要的。

避免仅仅为了新奇而追求最新最好的。程序员可能喜欢新技术的想法,但今天热门的东西可能五年后就不再流行了。为停产的产品寻找人员或支持是一项挑战,在项目中期更换数据库通常成本高昂。

最终,对于下一个项目应该使用什么数据库的答案是:视情况而定。幸运的是,对于现代架构,如微服务,在 SQL 和 NoSQL 之间的选择不是非此即彼的。它们可以在同一个应用程序环境中并存。

结论

SQL 和 NoSQL 都在现代软件开发中占有一席之地。他们各有各的长处和短处。NoSQL 数据库可以包含 SQL 元素,而 SQL 数据库可以通过新功能和成熟的 NewSQL 数据库提供 NoSQL 的一些好处。

在选择数据库时,考虑您的需求以及什么对您的团队最有意义,无论是 SQL 还是 NoSQL。

SSH 访问-本地构建| CircleCI

原文:https://circleci.com/blog/ssh-vs-local-jobs-two-forms-of-debugging-and-when-to-do-them/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


“它在我的机器上工作,但在生产中中断了”是工程师们经常遇到的问题。CircleCI 1.0 用户发现通过使用 SSH 访问来调试这样的构建失败非常有用,我们现在已经为运行在我们的 2.0 平台上的构建启用了 SSH 访问。

此外,对于 2.0,我们引入了 CircleCI CLI,它可以帮助用户在本地重现 CircleCI 环境。该功能允许用户在其本地环境中快速迭代和调试问题。

我应该使用哪一个?

通过 CircleCI CLI 进行本地构建

如果您希望保持代码库的完整性,并且需要快速修复问题或调试配置语法,那么通过 CircleCI CLI 在本地运行构建可能是您的正确选择。

我们已经看到用户提交他们的配置并运行几次构建来验证他们的 YAML 语法。您可以使用circleci build在本地机器上快速试验和验证您的语法。一旦配置通过验证,您就可以提交它。

如果您需要一种简单的方法来解决容器配置问题,您可能还希望使用本地版本。本地构建允许您快速重试数据库配置或验证服务是否设置正确。

最后,对于测试项目,您可以克隆 repo,安装 CircleCI CLI 并运行circleci build。这是在与您的团队相同的环境中快速启动和运行的好方法。

SSH 访问

对许多 CircleCI 用户来说,通过 SSH 跨网络测试构建已经成为一个流行且有价值的特性。对构建的 SSH 访问允许您通过 SSH 会话访问执行环境,以排除构建故障、访问日志文件、分析正在运行的进程等等。您可以直接 SSH 到一个失败的构建中,看看哪里出错了。在每次构建之后,您可以选择启用 SSH 访问进行重建。当机器运行时,你将能够 SSH 到它,并发现问题。

我如何使用这些?

对于 SSH 访问您的作业,您可以遵循本指南

要在本地运行 CircleCI,请按照本指南中的安装 CircleCI CLI。

软件交付创业创始人指南

原文:https://circleci.com/blog/startup-founders-guide-to-software-delivery/

2 人团队的软件交付与 200 人团队的软件交付有很大不同。在创业公司的成长过程中,流程和工具的选择会自然发展——但要么过早优化,要么在没有明确方向的情况下让它们发展,可能会耗费你的时间和以后的敏捷性。这就是为什么我想和你谈谈如何有目的地改进你的交付过程。

软件交付的最佳方法依赖于您的软件架构,正如我们从康威定律中所知,这反过来又与您的组织结构相关。在整篇文章中,我将根据您在关键转折点上做出的决策,深入探讨这些因素是如何帮助您实现快速增长还是在扩展过程中设置障碍的。

完全披露:我是一家 CI/CD 公司的首席技术官,因此我将在本指南中鼓励您使用 CI 和 CD。在我将列出的许多其他策略中,我将解释为什么开始使用 CI/CD 永远不会太早,尽管它可能影响的领域将随着您的创业公司的成长过程而变化和发展。

你为什么要听我的?

作为一名 20 年的软件行业老手、四次创业的创始人和三次首席技术官,我知道时间是创业公司最宝贵的资源之一。过早地做出错误的选择,你会失去试图解决本可以避免的问题的宝贵时间。我已经看到了正确的选择如何能够带来巨大的回报,以及在哪里要对过度架构保持高度警惕以应对每一种可能发生的情况。在过去的 7 年里,我一直领导着 CircleCI 的工程团队,从成千上万使用我们的平台来简化和加快软件交付的组织中,我毫无疑问地看到了什么是有效的,什么是无效的:对于每个阶段的公司,以及每个行业。

创建阶段:1-10 名工程师

这是我最喜欢的公司发展阶段。你很兴奋。你有主意了。你最不想做的就是工具。在某一点上,您正在手工将您的第 20 个变更部署到您的初始环境中。你可能会想“这不可能是做这件事的正确方法。”你是对的。尽管从技术上讲,您可以这样操作,但现在是将 CI 和 CD 放在适当位置的时候了。在一个公司的早期阶段,你甚至可能没有进入正确的行业,但是快速的交付和对你的发布的信心将有助于你到达那里。

这个探索阶段要求简单。在这种规模下,协调成本很低,所以要充分利用这一点:一个团队,一个完整的代码库,一个基本的(自动化)部署。

你不想停下来问你的联合创始人:“嘿,提醒我一下 Capistrano 命令?”或者“我刚才是不是在你推的上面推了一把?”

你不希望你的笔记本电脑成为“构建笔记本电脑”,当有人想开发代码时,他们必须来找你。

只需点击一个按钮,您就可以拥有 CI/CD。别给自己添堵了。CircleCI 有一个免费计划。使用它。

仅仅这一步,即使你现在不“需要”(我会试着让你相信你需要),这一步也会让你在以后的道路上遥遥领先。当事情变得真实时,您遇到了一个让您措手不及的安全问题,或者您确实有客户并且正在解决大规模问题,您可以发布一个补丁。

这比“罗伯在吃午饭,我们不能部署”好多了

做什么

  • 现在就实施 CI/CD。您的应用变化很大,您想尽快了解。不要浪费时间试图记住如何安全部署。当您的系统开始变得更加复杂时(我将在后面解释为什么您应该尽可能推迟复杂性),您将拥有处理复杂系统的实践和工具。

  • 保持简单。CI 和 CD 很容易到位。获取谷歌分析并设置 CircleCI。在这个阶段,这可能就是你所需要的。但是没有它们是一个巨大的问题。没有这些基本工具,你就给了你的竞争对手巨大的优势。建立一家公司,关键在于执行力;获得可靠执行所需的工具,将其余的事情搁置。

  • 创建占位符实现。这基本上意味着推迟复杂性,但尽早启动支持工程组织复杂部分的流程。可追溯性是一个很好的例子,它是一个相对容易在一个整体中建立的过程,但是一旦你的应用程序已经扩展到包括服务时,就很难把它放进去了。何必呢?拥有可追溯性推动了某些行为和决策。因为甚至有一个简单的实现,你会在你构建的所有东西中考虑可追溯性。尽早实施这些实践实际上会改变您的编码方式。你将会在你的代码库中减少异步交付,或者你将会以一种你以后更容易理解的方式来设计它们。

  • 思考,不行动。知道你未来的限制是什么或可能是什么并不意味着你必须去适应它们。建筑贵,思想不贵。在做出决策之前,仔细考虑一下您的扩展路线图,这样您就可以有意识地权衡您要推迟到未来做什么。

  • 从一开始就优先考虑可操作性。创业公司的第一个开发者应该已经承担起思考软件运营的责任。这在第一行代码中应该很明显。例如,选择可以将输出重定向到中央系统的日志库的工作可以忽略不计。当您这样做时,构建您的日志记录,并在构建时删除或构建您用来调试的日志点。

红利投资者提示

Keep the architecture and tooling as simple as possible.

复杂性是杀手:它扼杀速度、文化和产品速度。一个好的构建工具可以帮助团队自动化过程,实践良好的卫生习惯,等等。虽然像 CircleCI 这样的工具确实可以扩展,但现阶段的初创公司应该专注于使用他们知道的最好的工具,这些工具可以轻松地集成到开发人员的工作流程中,让生活变得更轻松。发货和招聘速度是早期成功的最佳预测,所以使用 CircleCI 有助于这一点,因为它是开发工具中使用最广泛的 CI/CD 平台之一,人们理解它并喜欢使用它。这有助于加快运输速度。

Shomik Ghosh, Principal, Boldstart VC Automate core building blocks as much as possible.

滚动你自己的 CI/CD 工具对于你的创业公司的目标是无差别的,这是将建立在上面的业务逻辑。对于这样的事情,尽可能多地使用自动化流程。以后的扩展将意味着需要有一个真实的来源来帮助团队间的协调。CircleCI 也能帮上忙。然后,当你成为一家后期公司时,你需要一些能够轻松集成所有不同工具的东西,并消除新开发人员入职时的摩擦,因此任何有助于这一点的产品都是有用的(再次 CircleCI)。

Shomik Ghosh, Principal, Boldstart VC Have comprehensive and thoughtful test coverage from day one. Pay down tech debt from day one. Andy Vitus, Partner, Scale Venture Partners

不要做什么

  • 抄大玩家。你解决不了与网飞、谷歌或脸书相同的问题。他们所关注的进步甚至不太可能转化到你的环境中,更不用说作为直接的拷贝了。尽可能保持简单和灵活。希望有一天你会到达那里,但是今天不是那一天。

红利投资者提示

Don't let gross margin deteriorate and plan to ‘fix it later’. Don't add unnecessary complexity. Andy Vitus, Partner, Scale Venture Partners

非常早期的阶段:10-20 名工程师|寻找适合市场的产品

在您公司的这个阶段,您可能没有专门负责开发效率或工具的人。你可能会,但这并不常见。更有可能的是,每个人都在为这项事业做贡献。并且您的团队将从延迟创建专门的开发体验或工具团队中受益。

为什么?因为在这个阶段,你将通过推迟专门团队的工作,将这种心态融入你的文化中。让每个人都负起责任,比依赖一个人要走得更远。然后,人们正在创建可以跨团队共享的工具、过程和代码。

在 10-20 个工程师中,你有一些实践,你保持你的软件简单。你会发现产品符合市场。那是基准。一旦到了那里,就可以投资了。

在产品符合市场需求之前,你构建的所有东西都很有可能被抛弃——我们的目标是尽可能简单地构建,这样你就可以轻松地改变或抛弃这些东西。一旦你适应了,你就可以开始投资了。

这是一个尴尬的阶段,你试图管理你独立工作的能力,而不在你的操作环境中制造不必要的复杂性。你应该分成小组吗?对于一个团队来说,人太多了,但是你的优先级仍然在变化。但是做同样的事情确实感觉很麻烦。

在 10-20 个工程师的时候,你会被诱惑去建立服务,但是现在还为时过早。您将增加不必要的部署复杂性,这是您以后要付出的代价。

相反,用组件和库创建边界和稳定性。无论您最终使用的是库还是您的 monolith(即模块化 monolith)之间定义明确的边界,您仍然可以将它们整合在一起并以 monolith 的方式部署,这将为您提供更多独立操作的空间,同时保持您的部署简单。

将您的代码库分解成服务将增加一个全新的决策层:您应该使用哪种服务间通信?您应该如何进行服务发现?还是重试?你的第一次服务会带来巨大的成本和复杂性。

所以推迟吧。

您将想要改变您的持续集成(CI)过程而不改变您的持续交付(CD ),或者换句话说:改变您的构建模型而不改变您的部署模型。

做什么

  • 保持部署简单。让您的构建和部署尽可能简单。抵制创建服务或过度复杂化任何可以推迟的事情的冲动。

  • 保持效率和生产力随着你的成长超过两块披萨的数量。寻找更灵活的方法来创建独立的工作流,而不需要具体的团队定义。

红利投资者提示

Expose engineering to customers' feedback.

你需要一个工程团队来理解为什么客户需要一个给定的特性,以及它能解决什么问题。大型产品管理团队有时间和地点来管理信息,围绕核心原则关注团队,并保持产品组合的一致性,但不是现在(GTM 早期)。随着产品与市场契合度的提高,您可以通过优化客户和工程之间的直接沟通渠道并保持紧密的反馈循环来充分利用您的小团队。

Eric Anderson, Principal, Scale Venture Partners

不要做什么

  • 微服务。构建可以使用组件或库的服务会产生部署开销,这种规模的团队不应该处理。推迟。

早期:20-50 名工程师

在你成长的这个阶段,你可能已经有了产品团队。你开始寻找分解产品的方法,这样团队可以独立运作。当您到达这个阶段时,您将看到跨工具共享组件和实践的投资回报。能够跨团队共享模式不仅节省了前期实现时间,还将减少下游的维护负担。(这就是为什么我们建立 CircleCI orbs 的原因——因为在公司的这个阶段,跨团队共享配置是一个巨大的增值)

现在,你可能正在考虑解散你的团队以适应不同工程师的语言偏好。您可能想知道让每个开发人员用他们认为最好的语言工作有什么坏处。

不要这样做。

是的,看起来你今天会走得很快,但是明天你肯定会慢下来。工程师很容易将速度的定义与编写的代码量联系起来。我们可能会觉得我们走得很快(“我知道铁锈!我写了很多代码!”)但是如果你或你的工程师正在编写样板文件,那么这是你应该使用库的一个标志。

在这一点上,交付价值必须是最重要的,而不是代码行。

20-50 工程师阶段是你开始崩溃的时候。投资于对抗团队分裂的能力。devops 中有一个词叫做“铺路”这意味着,当然,你可以步行上山砍树,或者你可以开车上高速公路。您提供的工具(即 CI/CD 工具)应该使您的工程师能够容易地选择好的路径。如果你组织中的人没有,你可能没有设定正确的目标。

我试图使用 Rust,因为我看到了一个很酷的 YouTube 视频,这对我来说应该是痛苦的。

你如何铺一条路?构建共享组件。

如果是语言堆栈,就在一个整体中工作。在 CircleCI,我们有一个已经测试过的预罐装 Docker 容器。如果您需要在 CVEs 上保持最新,我们将这一点内置到 Clojure 管道中。我们有后端共享部分,人们可以用它在 Clojure 中构建服务。每个人在项目开始时做的所有事情,我们都有,你可以把你正在做的东西投入生产。如果有安全更新,你可以免费获得。但是:如果你想开发围棋,你的团队需要从头开始。

你签约的目的是为你的客户提供价值。不管你用什么语言写了多少代码,这仍然是底线。让你的团队轻松实现它。

做什么

  • 标准。和短绒。编码标准没什么意思。讨论编写代码的方法(从讨论缩进到括号)是人们可以转动轮子的又一件事情,但没有增加价值。选择一个标准,执行它。继续传递价值。

  • 自动化你的文化。将您对软件的所有描述放入 CI/CD 中。你关于如何做事的决定现在已经嵌入,并自动执行。这就是为什么你想一开始就使用 CI/CD——它给了你一个放置规则的地方。

  • 在需要的地方创建占位符。举个例子,有免费的漏洞扫描器;就用它们吧。构建编码标准和实践的文化。将它们全部添加到您的 CI/CD 中。以后,当你升级你正在使用的服务时,你就有了一个可以插入这些付费服务的地方。这一阶段很容易做到,但以后很难添加。

不要做什么

  • 完全重写。你可能在反思,作为一个团队,你现在比你第一次编写代码库时知道了多少,并且很想从头开始,用你所有来之不易的知识重写你的代码库。忍住冲动。事情会变得混乱,但是继续努力。作为一家企业,你正在大步前进。控制混乱,边走边清理,但是不要因为大量的重写而停止你的进程。

  • 假设共享上下文。最多 20 人左右,你还没有分成团队,所以你可以假设人们有上下文(那是一个黑客——不是一个策略)。再大一点,你就不能假设每个人都有相同的背景。投资共享环境。用工具来加强这一点,例如,如果您没有正确地注释,就会使 CI 中的构建失败。确保人们理解为什么要强制执行。它将使你免于编写没有人会阅读的无限文档。使用测试作为共享上下文的例子,测试是预期行为的表达。但是很多时候你阅读旧的测试,不明白为什么他们会那样。如果您破坏了一个测试,但是没有文档或错误消息,您如何知道它是否可信呢?

中期:50-150 名工程师

现在你长大了。

你不能再抗拒了:你可能会得到一些服务。

你的软件已经变得足够复杂,为了提高交付速度,你的管道数量也增加了。您可能想要分离工作单元。在你彻底分裂之前,集中精力找出你的交付管道和方法中的模式,这样你就不会制造混乱。

当您进入服务领域时,继续尽可能地管理压裂。挑一件事,把它做好。弄清楚单个服务的构建和部署管道是什么样子,而不是同时为十个服务构建和部署管道。一致性是游戏的名字。这里不是百花齐放的地方。相反:制定一个标准,然后复制。

像对待任何软件设计一样对待您的管道设计。构建一次,试用,再次构建,做一些复制粘贴。找出哪里出故障了。使用规则三:在第三种情况下,你可以建立一个抽象。在此之前,试图建立一个“完美的”抽象是浪费时间,因为你还不了解它。无论如何,你都会燃烧周期并出错。第一次尝试构建完美的抽象所花费的时间是浪费时间。

另一件事:加强结构和一致性的随机社会互动在这个规模开始失效。因此,人们只关注他们团队的效率,因为他们不知道其他团队在做什么。

你可能会拥有一个专门的开发经验和工具团队。与此同时,让一个团队提出一个解决方案,然后将它分发给所有其他团队,这可能是弥合差距的一种有效方法。这种方法也避免了建立一种交付是其他人的问题的文化的风险——分担这些责任使整个团队参与到结果中。

做什么

  • 投资于跨交付渠道的一致性。这里的诀窍是找到分配真正所有权的平衡,同时不让团队的其他成员认为这是其他人的责任。保持一种文化,在这种文化中,每个人都相信有效的软件交付是他们工作的一部分,然后给一些人所有权,使之成为现实。否则,每个人都在自己的团队中解决自己的问题,重复工作,失去了共同的经验教训。

不要做什么

  • 让团队自由支配。不要被让每个人独立运作所带来的错误的速度感所迷惑——几十个团队创造数百种当地效率的代价是长期的复杂性。

成长阶段:150-500 名工程师

在这个阶段,你创造的所有占位符和你在过程中实施的文化开始真正得到回报。

你正以惊人的速度成长,所以任何基于你之前的决定而轻易获得的东西都将是一个巨大的优势。起初,你有一个暗示,你想做一些事情,并为它留下了空间。然后你用一个免费工具取代了那个空间,然后是一个付费工具,然后是一个人,然后是整个团队,这一切都是因为你深思熟虑地给那个团队留了一个可以插电的地方。在这一点上,你可能有一个团队(SRE,开发者体验,发布工程,等等。)负责为您的工程组织的其余部分考虑工具,这是一个真正的转换点。

对此不要过于教条。

找到一致性带来最大价值的地方,以及它将创造更多开销而不是价值的地方。平衡强制一致性和让人们把事情做好。将您的共享工具专家放在您的团队最常使用的项目上,而不是放在某人做某件事两次的每个实例上。在某些时候,开销会超过一致性的价值。知道在哪里让这种情况发生是一个真正的挑战——永远要知道你的权衡。

做什么

  • 投资于系统和流程,而不是个人关系。你已经过了仅仅通过观察一切就能理解正在发生的事情的阶段。现在是系统在让事物运转,而不是人。

    当你正在完成从个人关系到系统的转变时,弄清楚你的操作工具将如何工作。在短期内,你会得到更好的反馈。不可否认,为设置这个工具构建业务案例可能很难,但是这也是早期做起来非常便宜并且越做越贵的事情之一。从小处着手。每天做一点。继续走。

    清晰的团队边界是必不可少的,这样你就可以部署你的服务,而不管系统中其他地方发生了什么,也不管你在和谁打交道。这就是自主的真正价值所在:你可以专注于完成工作,而不需要与特定的人打交道来铺平道路。

红利投资者提示

Make time to address and work down technical debt.

技术债务是你无法避免的。如果你不解决这些问题,它们迟早会复合并成为一个更大的问题。技术债务通常以几种不同的方式出现。4-5 年后编写的任何软件中的架构和技术债务。鉴于云原生技术的快速变化率,使用传统技术的软件在架构上也会有缺点,这使得它难以扩展和伸缩。

随着时间的推移,软件积累了代码债务。开发人员并不总是用定义良好的接口和 API 来编写干净的代码。软件也积累了测试和文档债务。很少有开发人员充分地记录他们的代码或编写具有足够覆盖率的测试。

鉴于你无法避免技术债务,最好的方法是积极主动地解决它。大多数优秀的软件团队每 4-5 年就会领先预算 6 个月,这时他们会停止添加任何新功能,只专注于解决技术债务。

Jai Das, President & Managing Director, Sapphire Ventures

不要做什么

  • 奖励英雄。你再也找不到专家让他们修理东西了。在这一点上,如果你没有工具来管理某些东西,你就有麻烦了。这看起来像是好的文档、清晰的工具和处理问题的清晰过程。不要再修补船了;您需要健壮的基础设施和操作工具。不要让你的团队依赖个人关系来完成工作。

  • 尝试解决所有问题。现在你有 150 多名工程师在一个系统上工作。自从你有 10 个工程师以来,你的代码的某些部分将保持不变。别人每天都会主动被感动。先担心那些。你更有可能打破遗留的(也就是传说中的)代码,而不是改进它。如果人们每天都在处理它,那么先解决它

关闭思路——随着成长不断创新

由 500+工程师,恭喜你:你不再是一个创业公司。你做得太棒了。你已经成为一个了不起的软件交付组织,所以现在不要放弃。要知道每一个比你大的软件组织都试图像你一样运作,所以不要陷入他们缓慢的遗留过程的世界;继续做模特。

随着您的不断成长,具体的实现将会发生变化,但是不要忘记让您实现这一目标的方法。不要放弃你的 CI/CD 文化。是的,你需要更多的系统和过程,但是你会用你一直应用于任何新工具或过程的同样的眼光来采用它们:实现速度。

随着规模的不断扩大,像审计员这样的新的利益相关者会对你这个组织提出要求。理解他们需要的结果,但是不要认为你必须按照他们的建议去做。他们并不处于软件开发的前沿。作为一家成功的初创公司,你已经取得了这么多的增长。构建工具,使之成为一个令人敬畏的软件团队成为可能。不要因为担心以你的体型操作而掉下火车。

感谢阅读。如果你能够实现我在这里分享的一些想法,你将成为世界上最高性能的软件交付组织之一——这是非常值得骄傲的事情。

好奇度量如何让你的团队专注于价值交付?在我们对 CircleCI 上超过 44,000 个组织和 160,000 个项目的超过 5,500 万个数据点的分析中,我们发现了我们平台上最高绩效团队共享的 4 个关键基准。立即下载 2020 年软件交付状态了解更多信息。

借助 MobX | CircleCI 为 Flutter 应用提供 CI/CD 和状态管理

原文:https://circleci.com/blog/state-management-for-flutter-apps-with-mobx/

MobX 是一个可扩展的库,旨在简化前端应用程序中的状态管理过程。在本教程中,您将学习如何使用 MobX 管理您的 Flutter 应用程序的状态,然后使用 CircleCI 为您的应用程序设置一个持续集成/持续部署(CI/CD) 管道。您可以在GitHub 资源库中找到为本教程开发的示例项目。

先决条件

在开始之前,你需要一个关于 Flutter 的工作知识。如果需要帮助入门,可以关注 Flutter 网站上的 codelabs。

您需要在计算机上安装以下项目:

  • Flutter SDK ,1.0 或更高版本。(这附带了一个 Dart SDK 安装)。
  • 开发环境。选择以下选项之一:

无论你选择哪个 IDE,你都需要安装 Dart 和 Flutter 插件。这些插件对于编辑和重构你的 Flutter 应用程序是至关重要的。

MobX 简史

根据mobx.js.org的说法,MobX 是一个经过战斗测试的库,它通过透明地应用函数式反应式编程( TFRP )使状态管理变得简单和可扩展。最初开发 MobX 时考虑的是 React 应用程序,现在它已经支持用其他 JavaScript 库构建的应用程序,最近还支持 Flutter 应用程序。

在 Flutter 应用中使用 MobX 进行状态管理

使用 MobX 管理状态依赖于该库的三个主要概念:

  • 可观察状态
  • 行动
  • 计算值

可观察的状态是应用程序的那些易受变化影响的属性。这些状态是用@observable注释声明的。例如,待办应用程序中的可观察状态包括所有待办事项的列表。该列表还包括其值可以更新的所有其他属性。

动作是旨在改变可观察状态的值的操作。动作用@action注释声明。在运行一个动作时,MobX 处理应用程序中使用被该动作修改的可观察对象的更新部分。待办事项应用程序中的动作的一个例子是用新的待办事项更新待办事项列表的功能。

计算出的值类似于可观察的状态,并用@computed注释声明。计算值不直接依赖于动作。相反,计算值取决于可观测状态的值。如果计算值所依赖的可观察状态被一个动作修改,则计算值也被更新。在实践中,开发人员经常忽略计算值的概念,而是经常无意地使用可观察值来代替它们。

比较 MobX、BLoC、Redux 和 setState()中的范例

MobX 建立在一个简单的理念上,即任何可以从应用程序状态派生的东西都应该从派生出来。这意味着 MobX 覆盖了应用程序状态中的所有属性,这些属性被定义为有可能改变。MobX 仅在此类属性改变时才重建 UI。这种方法不同于 BLoC、Redux 和 setState 使用的方法。BLoC 使用流来传播更改,而 Redux 基于一个应用程序,该应用程序拥有一个真实的来源,其小部件从该来源继承。提供与 MobX 类似的简单性,需要您自己处理状态传播。凭借其抽象状态变化细节的能力,MobX 相对于其他方法提供了更平滑的学习曲线。

建立一个颤振项目

要创建新的 Flutter 项目,您将使用 Flutter CLI 工具。打开您的终端,导航到您的项目目录并运行以下命令:

$ flutter create reviewapp 

CLI 工具会生成一个模板项目,让您在几秒钟内就可以开始工作。项目生成后,您可以在 IDE 中打开它。

安装项目依赖项

您的新项目需要五个主要依赖项:

  • mobx 是 mobx 的 Dart 端口,用于编写状态修改逻辑。
  • flutter_mobx 是 mobx 的 flutter 集成,它提供了Observer小部件,可以根据可观察状态的变化自动重建。
  • shared_preferences 是一个本地持久性库。
  • mobx_codegen 是 mobx 的代码生成库,允许使用 MobX 注释。
  • build_runner 是一个运行代码生成操作的独立库。

在 IDE 中打开项目后,导航到您的/pubspec.yaml文件以添加依赖项。用以下代码片段替换dependencies部分:

dependencies:
  flutter:
    sdk: flutter
  mobx: ^0.3.5
  flutter_mobx: ^0.3.0+1
  shared_preferences: ^0.5.3+4 

然后用这段代码替换dev_dependencies部分:

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^1.6.5
  mobx_codegen: ^0.3.3+1 

现在,在项目的根目录下运行这个命令来下载依赖项:

$ flutter packages get 

你在建造什么

在本教程中,您将构建一个简单的审阅应用程序,该应用程序允许用户添加评论和星号,如下图所示:

如何构建样本颤振项目

您正在构建的内容一节中描述的示例项目工作如下:

  • 启动应用程序
  • 从本地首选项获取评论
  • 用检索到的评论更新 UI
  • 添加评论
  • 更新应用状态中的评论列表
  • 在首选项中保留更新的评论列表

在开始之前,通过在项目的/lib目录中运行该命令,创建/widgets/screens/models文件夹:

$ mkdir widgets screens models 

创建数据模型

首先,通过在/lib/models/目录中创建一个reviewmodel.dart文件,为评审定义一个数据模型。将以下代码片段添加到其中:

import 'package:meta/meta.dart';
class ReviewModel {
  final String comment;
  final int stars;
  const ReviewModel({@required this.comment, @required this.stars});

  factory ReviewModel.fromJson(Map<String, dynamic> parsedJson) {
    return ReviewModel(
      comment: parsedJson['comment'],
      stars: parsedJson['stars'],
    );
  }

  Map<String, dynamic> toJson(){
    return {
      'comment': this.comment,
      'stars': this.stars,
    };
  }
} 

创建用户界面

我们正在构建的示例应用程序需要一种用户与之交互的方式。该应用程序将包含一个评论表单,显示现有评论的列表,评论的总数,以及每个评论的平均星级数。该表单还允许用户添加新的评论。

首先在/lib/screens目录下创建一个review.dart文件。添加以下代码片段:

import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import '../widgets/info_card.dart';

class Review extends StatefulWidget {
  @override
  ReviewState createState() {
    return new ReviewState();
  }
}
class ReviewState extends State<Review> {
  final List<int> _stars = [1, 2, 3, 4, 5];
  final TextEditingController _commentController = TextEditingController();
  int _selectedStar;

  @override
  void initState() {
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    Size screenSize = MediaQuery.of(context).size;
    double screenWidth = screenSize.width;
    return Scaffold(
      appBar: AppBar(
        title: Text('Review App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            SizedBox(height: 12.0),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                Container(
                  width: screenWidth * 0.6,
                  child: TextField(
                    controller: _commentController,
                    decoration: InputDecoration(
                      contentPadding: EdgeInsets.all(10),
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10.0),
                      ),
                      hintText: "Write a review",
                      labelText: "Write a review",
                    ),
                  ),
                ),
                Container(
                  child: DropdownButton(
                    hint: Text("Stars"),
                    elevation: 0,
                    value: _selectedStar,
                    items: _stars.map((star) {
                      return DropdownMenuItem<int>(
                        child: Text(star.toString()),
                        value: star,
                      );
                    }).toList(),
                    onChanged: (item) {
                      setState(() {
                        _selectedStar = item;
                      });
                    },
                  ),
                ),
                Container(
                  child: Builder(
                    builder: (BuildContext context) {
                      return IconButton(
                        icon: Icon(Icons.done),
                        onPressed: () {},
                      );
                    },
                  ),
                ),
              ],
            ),
            SizedBox(height: 12.0),
            //contains average stars and total reviews card
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                InfoCard(
                    infoValue: '2',
                    infoLabel: "reviews",
                    cardColor: Colors.green,
                    iconData: Icons.comment),
                InfoCard(
                  infoValue: '2',
                  infoLabel: "average stars",
                  cardColor: Colors.lightBlue,
                  iconData: Icons.star,
                  key: Key('avgStar'),
                ),
              ],
            ),
            SizedBox(height: 24.0),
            //the review menu label
            Container(
              color: Colors.grey[200],
              padding: EdgeInsets.all(10),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.start,
                children: <Widget>[
                  Icon(Icons.comment),
                  SizedBox(width: 10.0),
                  Text(
                    "Reviews",
                    style: TextStyle(fontSize: 18),
                  ),
                ],
              ),
            ),
            //contains list of reviews
            Expanded(
              child: Container(
                child: Text("No reviews yet"),
              ),
            ),
          ],
        ),
      ),
    );
  }
} 

创建自定义小部件

在这段代码中,有一个对InfoCard的引用。InfoCard是一个自定义小部件,显示评论总数和平均星级数:

Infocard custom widget for review app

要创建InfoCard小部件,在/lib/widgets目录中创建一个名为info_card.dart的文件。添加以下代码片段:

import 'package:flutter/material.dart';

class InfoCard extends StatelessWidget {
  final String infoValue;
  final String infoLabel;
  final Color cardColor;
  final IconData iconData;
  const InfoCard(
      {Key key,
      @required this.infoValue,
      @required this.infoLabel,
      @required this.cardColor,
      @required this.iconData,
      })
      : super(key: key);
  @override
  Widget build(BuildContext context) {
    Size screenSize = MediaQuery.of(context).size;
    double screenWidth = screenSize.width;
    return Container(
      height: 100,
      width: screenWidth / 2,
      child: Card(
        color: cardColor,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(5.0),
        ),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: <Widget>[
            Icon(
              iconData,
              size: 28.0,
              color: Colors.white,
            ),
            Text(
              "$infoValue $infoLabel",
              style: TextStyle(color: Colors.white),
            ),
          ],
        ),
      ),
    );
  }
} 

尽管在本教程的后面部分您还不需要它,但是创建一个ReviewWidget类。这个类将被用来显示一个单独的评论项目。首先在项目的lib/widgets目录下创建一个review.dart文件。添加以下代码片段:

import 'package:flutter/material.dart';
import '../models/reviewmodel.dart';
import '../models/reviews.dart';
import '../widgets/review.dart';
import '../widgets/info_card.dart';

class ReviewWidget extends StatelessWidget {
  final ReviewModel reviewItem;

  const ReviewWidget({Key key, @required this.reviewItem}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Padding(
          padding: EdgeInsets.all(10.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              Expanded(
                child: Text(
                  reviewItem.comment,
                ),
              ),
              Row(
                children: List(reviewItem.stars).map((listItem) {
                  return Icon(Icons.star);
                }).toList(),
              ),
            ],
          ),
        ),
        Divider(
          color: Colors.grey,
        )
      ],
    );
  }
} 

实现 MobX

要在应用程序中实现 MobX,您需要在应用程序状态中定义观察值、动作和计算值。

在应用程序中的任何时间点,评论列表、平均星级数和评论总数必须是最新的可用值。这意味着它们必须用注释声明,以便 MobX 可以跟踪对它们的更改。

为此,在项目的/lib/models目录中创建一个文件reviews.dart。添加以下代码片段:

import 'dart:async';
import 'dart:convert';
import 'package:mobx/mobx.dart';
import 'package:shared_preferences/shared_preferences.dart';
import './reviewmodel.dart';
part 'reviews.g.dart';
class Reviews = ReviewsBase with _$Reviews;
abstract class ReviewsBase with Store {
  @observable
  ObservableList<ReviewModel> reviews = ObservableList.of([]);

  @observable
  double averageStars = 0;

  @computed
  int get numberOfReviews => reviews.length;

  int totalStars = 0;

  @action
  void addReview(ReviewModel newReview) {
    //to update list of reviews
    reviews.add(newReview);
    // to update the average number of stars
    averageStars = _calculateAverageStars(newReview.stars);
    // to update the total number of stars
    totalStars += newReview.stars;
    // to store the reviews using Shared Preferences
    _persistReview(reviews);
  }

  @action
  Future<void> initReviews() async {
    await _getReviews().then((onValue) {
      reviews = ObservableList.of(onValue);
      for (ReviewModel review in reviews) {
        totalStars += review.stars;
      }
    });
    averageStars = totalStars / reviews.length;
  }

  double _calculateAverageStars(int newStars) {
    return (newStars + totalStars) / numberOfReviews;
  }

  void _persistReview(List<ReviewModel> updatedReviews) async {
    List<String> reviewsStringList = [];
    SharedPreferences _preferences = await SharedPreferences.getInstance();
    for (ReviewModel review in updatedReviews) {
      Map<String, dynamic> reviewMap = review.toJson();
      String reviewString = jsonEncode(ReviewModel.fromJson(reviewMap));
      reviewsStringList.add(reviewString);
    }
    _preferences.setStringList('userReviews', reviewsStringList);
  }

  Future<List<ReviewModel>> _getReviews() async {
    final SharedPreferences _preferences =
        await SharedPreferences.getInstance();
    final List<String> reviewsStringList =
        _preferences.getStringList('userReviews') ?? [];
    final List<ReviewModel> retrievedReviews = [];
    for (String reviewString in reviewsStringList) {
      Map<String, dynamic> reviewMap = jsonDecode(reviewString);
      ReviewModel review = ReviewModel.fromJson(reviewMap);
      retrievedReviews.add(review);
    }
    return retrievedReviews;
  }
} 

这段代码声明了两个变量:

  1. reviews是所有用户评论的列表
  2. 是从所有评论中计算出的可观测恒星的平均数量。它们被计算为可观察的,因为它们的值被期望响应于动作而改变。然后,代码定义了addReview()函数,它向评论列表中添加了一个新的评论。它还添加了一个initReviews()函数,用来自共享偏好的现有数据初始化评论列表,作为更新可观察状态的动作。

虽然也可以将numberOfReviews变量声明为可观察变量,但是可以使用计算值,因为其值的变化取决于动作的结果(更新的可观察状态),而不是直接取决于动作本身。就当是余波效应吧。最后,声明一个totalStars变量和函数_calculateAverageStars()_persistReview()_getReviews()。这些参数没有注释,因为它们是不直接更新状态的辅助参数。

运行 CodeGen

由于 MobX 专注于抽象高级实现细节,库处理生成数据存储的过程。相比之下,Redux 甚至要求存储都是手工编写的。MobX 通过使用其mobx_codegen库和 Dart 的build_runner库来执行代码生成,并在搭建存储时考虑所有带注释的属性。

转到项目的根目录,运行命令:

$ flutter packages pub run build_runner build 

在您生成存储之后,您将在您的/lib/models目录中找到一个review.g.dart文件。

使用观察者

即使实现了 MobX 存储,在应用程序的 UI 中反映状态变化也需要使用来自flutter_mobx库的观察者。observer 是一个小部件,它包装了一个可观察值或计算值,以将它们值的变化呈现给 UI。

添加每个新评论时,平均星级、评论数和评论总数的值都会更新。这意味着用于呈现值的小部件被包装在一个Observer小部件中。要使用 observer 小部件,请导航到您的/lib/screens/review.dart文件。使用以下代码修改ReviewState类:

class ReviewState extends State<Review> {
  final Reviews _reviewsStore = Reviews();
  final TextEditingController _commentController = TextEditingController();  
  final List<int> _stars = [1, 2, 3, 4, 5];
  int _selectedStar;
  @override
  void initState() {
    _selectedStar = null;
    _reviewsStore.initReviews();
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    Size screenSize = MediaQuery.of(context).size;
    double screenWidth = screenSize.width;
    return Scaffold(
      appBar: AppBar(
        title: Text('Review App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            SizedBox(height: 12.0),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: <Widget>[
                Container(
                  width: screenWidth * 0.6,
                  child: TextField(
                    controller: _commentController,
                    decoration: InputDecoration(
                      contentPadding: EdgeInsets.all(10),
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(10.0),
                      ),
                      hintText: "Write a review",
                      labelText: "Write a review",
                    ),
                  ),
                ),
                Container(
                  child: DropdownButton(
                    hint: Text("Stars"),
                    elevation: 0,
                    value: _selectedStar,
                    items: _stars.map((star) {
                      return DropdownMenuItem<int>(
                        child: Text(star.toString()),
                        value: star,
                      );
                    }).toList(),
                    onChanged: (item) {
                      setState(() {
                        _selectedStar = item;
                      });
                    },
                  ),
                ),
                Container(
                  child: Builder(
                    builder: (BuildContext context) {
                      return IconButton(
                        icon: Icon(Icons.done),
                        onPressed: () {
                          if (_selectedStar == null) {
                            Scaffold.of(context).showSnackBar(SnackBar(
                              content:
                                  Text("You can't add a review without star"),
                              duration: Duration(milliseconds: 500),
                            ));
                          } else if (_commentController.text.isEmpty) {
                            Scaffold.of(context).showSnackBar(SnackBar(
                              content: Text("Review comment cannot be empty"),
                              duration: Duration(milliseconds: 500),
                            ));
                          } else {
                            _reviewsStore.addReview(ReviewModel(
                                comment: _commentController.text,
                                stars: _selectedStar));
                          }
                        },
                      );
                    },
                  ),
                ),
              ],
            ),
            SizedBox(height: 12.0),
            //contains average stars and total reviews card
            Observer(
              builder: (_) {
                return Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    InfoCard(
                      infoValue: _reviewsStore.numberOfReviews.toString(),
                      infoLabel: "reviews",
                      cardColor: Colors.green,
                      iconData: Icons.comment
                    ),
                    InfoCard(
                      infoValue: _reviewsStore.averageStars.toStringAsFixed(2),
                      infoLabel: "average stars",
                      cardColor: Colors.lightBlue,
                      iconData: Icons.star,
                      key: Key('avgStar'),
                    ),
                  ],
                );
              },
            ),
            SizedBox(height: 24.0),
            //the review menu label
            Container(
              color: Colors.grey[200],
              padding: EdgeInsets.all(10),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.start,
                children: <Widget>[
                  Icon(Icons.comment),
                  SizedBox(width: 10.0),
                  Text(
                    "Reviews",
                    style: TextStyle(fontSize: 18),
                  ),
                ],
              ),
            ),
            //contains list of reviews
            Expanded(
              child: Container(
                child: Observer(
                  builder: (_) => _reviewsStore.reviews.isNotEmpty
                      ? ListView(
                          children:
                              _reviewsStore.reviews.reversed.map((reviewItem) {
                            return ReviewWidget(
                              reviewItem: reviewItem,
                            );
                          }).toList(),
                        )
                      : Text("No reviews yet"),
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
} 

这段代码通过从/lib/models/reviews.dart创建一个Review类的实例来追加第一次修改,作为访问存储的一种方式。然后,它用一个 observer 小部件包装显示平均星级和总评论数据的Row。然后它使用Review类的reviewStore实例来引用数据。

接下来,当商店中的评论列表为空时,将显示占位符“no reviews”Text小部件。否则,ListView显示列表中的项目。最后,修改“完成”按钮的onPressed()功能,向商店添加新的评论。

至此,您的应用程序差不多完成了。下一步是将审查屏幕导入到您的main.dart文件的导入部分。打开文件,并添加以下代码片段:

$ import './screens/review.dart'; 

/lib/main.dart中,修改MyApp类的build()方法中的home属性。将home属性从MyHomePage()更改为Review()。代码如下:

@override
Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Flutter Demo',
    theme: ThemeData(
      primarySwatch: Colors.blue,
    ),
    home: Review() //previously MyHomePage(),
  );
} 

最后,使用flutter run命令运行应用程序。

编写样本测试

为了理解测试如何适应 CI/CD 管道,您将需要创建一个简单的单元测试和小部件测试。

为了编写单元测试,在项目的/test目录中创建一个名为unit_test.dart的文件。添加以下代码片段:

import 'package:flutter_test/flutter_test.dart';
import '../lib/models/reviewmodel.dart';
import '../lib/models/reviews.dart';

void main() {
  test('Test MobX state class', () async {
    final Reviews _reviewsStore = Reviews();

    _reviewsStore.initReviews();

    expect(_reviewsStore.totalStars, 0);

    expect(_reviewsStore.averageStars, 0);
    _reviewsStore.addReview(ReviewModel(
      comment: 'This is a test review',
      stars: 3,
    ));

    expect(_reviewsStore.totalStars, 3);
    _reviewsStore.addReview(ReviewModel(
      comment: 'This is a second test review',
      stars: 5,
    ));

    expect(_reviewsStore.averageStars, 4);
  });
} 

接下来,通过用以下代码片段完全替换项目的test目录中现有的widget_test.dart文件的内容来添加小部件测试:

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../lib/main.dart';

void main() {
  testWidgets('Test for rendered UI', (WidgetTester tester) async {
    await tester.pumpWidget(MyApp());
    Finder starCardFinder = find.byKey(Key('avgStar'));

    expect(starCardFinder, findsOneWidget);
  });
} 

通过执行项目根目录中的flutter test命令来运行测试。

带 CircleCI 的 CI/CD

除了为您提供一个构建项目的环境之外,持续集成(CI)工具还为运行自动化测试和自动上传部署工件提供了一个可靠而稳定的环境。在本节中,您将学习如何使用 CircleCI 为您的颤振项目设置和利用 CI/CD 管道。

首先,通过执行项目根目录中的git init命令来初始化项目中的本地 Git 存储库。用git add .把你的文件添加进去。把那些文件交给git commit -m "First commit"。然后,在 GitHub 上为您的项目创建一个在线存储库。将 GitHub 存储库添加为本地存储库的远程引用,然后通过在项目根目录下运行以下命令将更改推送到远程存储库:

$ git remote add origin https://link_to_repo && git push -u origin master 

创建配置文件

通过运行项目根目录中的mkdir .circleci命令创建一个名为.circleci的文件夹。创建一个配置文件,这样文件路径的结构就是这样的/your_project_path/.circleci/config.yml

然后,用下面的代码片段填充/.circleci/config.yml文件:

version: 2
jobs:
  build:    
    docker:
      - image: cirrusci/flutter:v1.5.8 

    branches:
      only: master

    steps:
      - checkout

      - run:
          name: Run Flutter doctor
          command: flutter doctor

      - run:
          name: Run the application tests
          command: flutter test

      - run:
          name: Build the Android version
          command: flutter build apk 

      - store_artifacts:
          path: build/app/outputs/apk/release/app-release.apk 

在这个配置文件中, Docker 被用作 executor 。Flutter 没有官方的 CircleCI Docker 图片,但是 DockerHub 上有一个很大的 Flutter 图片列表。最突出的是 cirrusci/flutter 图像。这张图片的使用频率超过 100 万次。

配置文件中可选的branches部分用于过滤部署流程运行的分支。当没有明确定义时,CircleCI 假设master为要处理的分支。

配置定义了使用哪个 Docker 映像。它还固定了一个镜像版本,该版本与运行项目本地副本的 Flutter 版本相匹配(在我的例子中是 v1.5.8)。

在步骤部分中,配置文件定义了每次在项目的存储库上运行部署时要执行的每个过程,按照它们的执行顺序。

最后,在上面代码片段的store-artifacts部分,引用了我们的构建工件的路径。这使得工件能够自动上传到 CircleCI 仪表板的工件选项卡。工件可以部署到 AWS S3 存储桶或任何其他托管服务。对于生产就绪的 Flutter 应用程序,您可以将应用程序商店的部署添加到该配置中。

设置 CircleCI

要将 CircleCI 与您的项目集成,请转到 CircleCI 仪表板,然后单击添加项目。它位于仪表板页面的最左侧。接下来,导航到页面的最右侧,点击设置项目

Add Projects Screen

在下一页,点击开始建造

Project setup interface

您的构建将开始。

Building project

现在,新代码的每一次提交都将为您的 Flutter 应用程序触发一个自动化的构建、测试和部署管道。

结论

在这篇文章中,我们介绍了如何使用 MobX 状态管理库管理 Flutter 应用程序的状态。我们还介绍了如何使用 CircleCI 为您的应用程序设置 CI/CD 管道。

尽管在为颤振项目(尤其是大型项目)选择状态管理方法时需要考虑权衡,但 MobX 为中型项目提供了可靠的选择。图书馆为你做最难的工作,同时给你权力在必要的地方负责。当您决定下一个 Flutter 项目的状态管理,甚至可能重写当前的项目时,这是一个很大的好处!

开发团队继续在他们的颤振项目中采用 MobX。查看图书馆的 GitHub 知识库,了解有助于其发展的方法。

我真的希望你喜欢这个教程。祝编码愉快!


Fabusuyi 是 Busha 的一名软件工程实习生,在那里他与移动开发团队一起工作。他目前是计算机科学专业的学生,对计算理论有研究兴趣。

阅读更多 Fabusuyi Ayodeji 的帖子

用 Cypress | CircleCI 测试 Web 应用程序

原文:https://circleci.com/blog/streamlined-web-application-testing-with-the-cypress-circleci-orb/

我们公司 Cypress.io 开发了一个开源的、麻省理工学院许可的、免费的端到端测试运行程序,可以测试任何在浏览器中运行的东西。test runner 使有效地测试复杂的现代 web 应用程序变得容易,而且它安装简单,易于学习,并且运行良好。

问题是

Cypress 测试运行器可以用于在开发人员的机器上本地运行测试,或者在持续集成服务器上运行测试。我们坚信每一个提交都应该被测试,因此我们记录了如何设置 CI 测试,甚至在大量 CI 提供者上运行我们自己的示例项目。但是设置 CI 套件从来没有想象中那么容易。当然,开发人员可以从我们的示例中复制代码并粘贴到他们的配置中,但是他们通常必须运行项目并调整设置,直到项目的构建和测试通过。我们的团队意识到,整个过程比想象的要困难得多,而且常常令人沮丧。我们,这个工具的作者,没有办法帮助开发人员配置他们的 CI 套件。

球体

也就是说,直到我们看到 CircleCI Orb 提案和示例。哇——现在 Cypress 团队可以制作小的可重用 CI 配置,并与我们的用户共享它们!下面是我们在 CircleCI 上运行端到端测试的用户可以使用的最简单的例子:

version: 2.1
orbs:
  cypress: cypress-io/cypress@1.0.0
workflows:
  build:
    jobs:
      - cypress/run 

七行,十个单词:它正确地检查代码,安装 npm 依赖项以及 Cypress 应用程序,缓存需要缓存的所有内容,并运行所有端到端测试。美丽而优雅。

对于想要构建应用程序并在 Cypress 仪表板上记录测试结果和工件的用户来说,这里有一个更复杂的例子。不需要编写任何定制代码——因为cypress/run作业接受这些常见用例的参数:

version: 2.1
orbs:
  cypress: cypress-io/cypress@1.0.0
workflows:
  build:
    jobs:
      - cypress/run:
           build: "npm run build"
           record: true 

参数被静态检查,并且至少防止一些配置错误。即使是我们的用户通常需要的复杂工作流,如“在一台机器上安装和构建应用,然后在 10 台机器上运行负载平衡的所有测试”,也可以通过cypress/run作业的参数轻松完成:

version: 2.1
orbs:
  cypress: cypress-io/cypress@1.0.0
workflows:
  build:
    jobs:
      - cypress/install:
           build: "npm run build"
      - cypress/run:
           record: true
           parallel: true
           parallelism: 10 

最少的配置,但最高的效率。

自定义配置

Cypress Orb 提供的工作足够全面,可以覆盖大多数典型用例。最终,您的项目可能会达到真正需要自定义设置的程度。当您达到这一点时,您可以“弹出”并使用完全解析的版本替换本地circle.yml配置文件,该版本公开了所有 Cypress Orb 命令及其完整的 YAML 文本。为此,您需要安装 CircleCI CLI 工具并运行:

$ circleci config process config.yml > config.yml 

通过运行这个命令,项目的 config.yml 文件将被完全解析的配置所替换,而不使用 Cypress Orb。现在,您可以根据项目需求调整配置。⚠️警告:没有自动的方法从“被驱逐”的配置回到使用 orb。

最后的想法

自从 Docker 出现并使建立可重复构建变得超级简单以来,我从未对持续集成服务如此兴奋过。orb 给了我们,测试工具的作者,创建可重用的、版本化的 CI 配置的能力,这立即使我们用户的生活变得更加简单。

如果您想了解更多信息,请务必注册参加 12 月 5 日的特别 Cypress + CircleCI 网络研讨会

请务必在on.cypress.io/circleci-orb查看我们的 orb 文档,并查看 repocypress-io/circle ci-orb


Gleb Bahmutov 是 JavaScript 忍者,图像处理专家,软件质量狂热分子。白天,作为 Cypress.io 的工程副总裁,格莱布让网络变得更好。晚上,他在 https://glebbahmutov.com/blog/的与软件错误作斗争,并写博客。微软开源软件 MVP。你可以跟踪他和他的作品 @bahmutov 并找到他在https://slides.com/bahmutov会议上的演讲幻灯片。

CircleCI 1.0 于 2018 年 8 月 31 日停产

原文:https://circleci.com/blog/sunsetting-1-0/

TL;DR:2018 年 8 月 31 日之后,CircleCI 1.0 将不再面向 Linux 和 macOS 用户。你可以在这里找到从 1.0 过渡到 2.0 的指南以及计划中的变化的完整时间表

我们在 2017 年 7 月推出了 CircleCI 2.0,为用户提供了更高的灵活性、功能和控制力。从那以后,我们的 Linux 和 macOS 机群的构建时间都大大减少了。我们已经能够处理越来越多的用户和工作,同时减少我们所服务的每种语言的平均工作时间。2.0 中工作流的增加也使您的管道与团队需求相匹配成为可能。

虽然 2.0 提供了更多的功能和灵活性,但我们知道开始构建并不像过去那样简单。为此,我们承诺在 8 月 31 日前实现以下改进和更新:

对于我们的云托管客户:

  • 增强工作流功能:增加自动取消冗余作业、从分叉的 PRs 触发工作流以及通过 API 触发工作流作业的能力
  • 改进命令行界面:我们计划添加额外的命令,并使 CLI 更容易使用
  • 简化配置语法,使您的作业更容易运行,并对它们进行微调以满足您的需求

如果您目前是使用我们的服务器托管选项的客户,您的客户团队将会向您提供有关即将推出的功能和时间表的更多信息。

我们知道您的工具有多重要,但更重要的是,保持您的交付渠道平稳运行而不中断是多么重要。在接下来的 6 个月里,我们的目标是尽可能无缝地从 1.0 过渡到 2.0。我们已经整合了资源和指南快速提示来帮助您进行转换,我们的团队随时准备帮助您启动并运行 2.0。

我们在 2018 年有一个很大的使命要完成,我们不能在支持云、服务器和 macOS 两个平台版本的同时完成它。我们的计划包括帮助您更智能地构建的新方法:利用我们系统中的数据为我们的用户创建智能自动化。我们将在 CircleCI 中建立主动警报和重新路由,这样我们可以在问题发生之前告诉您,并帮助您的团队找到不同的方法。我们迫不及待地想和你一起创造伟大的事业。

数字化转型停滞的三个原因

原文:https://circleci.com/blog/supporting-digital-transformation-while-avoiding-common-stalls/

在我的职业生涯中,观察技术在几乎每个行业的传播是我见过的最迷人的事情之一。15 年前,公司认为设置内部存储、防火墙和虚拟专用网络是一项技术工作,而现在,整个工程团队都致力于开发应用和服务。无论是制鞋公司使用应用程序推动电子商务,百货公司开发技术以促进店内和网上销售,还是重型设备制造商开发技术服务和自动驾驶拖拉机,我都看到许多老牌公司进行了巨大的数字化转型,以便能够生存和竞争。

然而,改变是艰难的——尤其是数字化转型所需要的大规模系统性改变。在我 20 年的科技生涯中,我曾与几家公司合作过,这些公司一开始就有变革的计划,但最终结果却乏善可陈。这很少是彻底的失败——团队或部门中通常存在小部分成功——但是当大项目停滞时,我几乎总是看到一组常见的模式出现。这里有三个最大的障碍,以及如何绕过它们。

团队不接受这个计划

管理任何重大的结构性变革都必须自下而上,而不是简单地从高层获得授权。任何组织中的领导者在开始结构变革之前首先关注的事情——无论是安装新的电子邮件系统或安全工具,还是围绕 DevOps 重组整个业务部门——是他们如何在这些努力中带动每个人。如果有一群愤世嫉俗的员工认为一个重要的项目只是下一个时尚,我向你保证,它会的。

领导者必须意识到这些项目需要文化变革以及程序变革。员工是否了解他们所扮演的角色在他们进入的新世界中是如何变化和发展的?他们想成为其中的一部分吗?

应对这些问题的一个好策略是,确保参与任何重大变革的每个组织都有可以团结在周围的内部拥护者。然而,冠军不能简单地被任命。关键是找一个真正理解为什么一个项目是重要的,并且有动力和灵感去完成它的人。理解一个项目如何以及为什么会让他们的工作和周围的人变得更好的员工会。

拥有或管理大型项目的一部分是知道是什么激励人们完成项目。我见过几次这样的情况,一家公司将一个新项目投入到一个巨大的生产中,然后有人站起来,试图通过告诉员工这将对每股收益产生多大影响来团结员工。嗯,我可能没有太多的股票,所以这对我来说并不重要,所以这怎么能让我去完成这件事呢?想一些简单的方法来激励伟大的工作。也许成绩最好的团队会得到一个假期,或者为另一个项目获得更多的预算。

公司未能标准化

我曾经在一个在分布式环境中使用五种不同操作系统的地方工作过。。一开始并不是这样,但我们的文化是这样的,如果一群人决定使用新的操作系统,他们就会这样做,这最终会成为我们做事方式的一部分。对于应用程序团队来说,这种负担是最小的,因为他们有一种运行特定应用程序的标准方式。然而,对于系统团队来说,有 n+1 种方法来做事。每个涉及操作系统的流程现在都有了另一个分支。这意味着审核、安全控制、监控、备份和资源调配现在在流程中有了另一个分支—另一个 if 语句。每个“如果”语句都增加了复杂性。有两条途径可以验证。随着时间的推移,你会意识到你在给组织制造阻力,而一门新的语言或多或少是永恒的。

这造成了巨大的成本——不仅是花费的金钱,还浪费了大量的时间和精力——并为其他问题打开了一扇巨大的窗户。最终,该公司花了九年时间从五个主要操作系统发展到两个。这巩固了标准化对我的重要性,因为最终,为他们的应用程序选择操作系统的人没有看到他们决策的痛苦。

当工具、实践或流程中的变化减少时,变化会更快。对于大型企业来说,实施新标准是一项巨大的工作,可能需要数年时间。这通常意味着有人将不得不放弃重要的东西,这将是痛苦的,但这也意味着随着变化的发生,发展和前进的东西越来越少。艰难的选择总是发生在这里,但没有捷径。如果他们不这样做,一个可变性的拖累将永远跟随项目,在最坏的地方设置路障。标准必须是关于全局优化的,每个人都需要理解,有时全局优化是局部次优化。这并不意味着它是错的。

标准是减少差异的工具。更少的变化需要更少的适应、更少的校正和更少的争论,因为更少的“如果”陈述。

不愿意改变

这个我见过很多次了。我和很多公司合作过,他们想变得更好,但不愿意改变任何事情。有一次,我和一家大型银行的几位高管开会,讨论工程策略,我一直听到人们说,“他们不会让我们这么做的。”我听了几次,最后问道:“他们是谁?我还以为你是他们呢!”

一个组织的发展速度没有速度限制,只有他们颠覆常规的意愿。公平地说,这并不容易——人们不喜欢颠倒他们的工作。他们知道帮助他们成功的工具和策略,并且通常反对改变它们。然而,变革通常要求他们这样做。

变化是一个持续的过程,领导者既需要理解它,也需要时刻意识到它。

结论

当我看到一个停滞不前的项目时,我会从寻找这三个缺陷中的一个开始,并深入研究如何修复它。我还试图放大成功,以表明推出只是停滞不前,并没有被放弃。每个项目都会遇到困难,如果一个团队看到他们如何前进,无论多么微小,他们都会从中获得一些灵感。

最后——也是非常重要的——请注意,在几乎所有情况下,我谈到的一切都是文化性的,而不是技术性的。在任何技术组织中,经理的主要工作是管理团队的动力和士气。对于技术头脑来说,战略性地思考如何激发人们的内在动机并不总是第二天性,但这是一项需要学习的基本技能。

改变从来都不容易,在我看来,得不到团队的认同、未能标准化以及对改变的不安是阻止改变发生的最常见的障碍。如果公司投入必要的时间、精力和意志力去克服它们,最终总会有回报。

用 Docker Compose - CircleCI 驯服您的测试环境

原文:https://circleci.com/blog/tame-your-test-environment-with-docker-compose/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


Kevin_Docker_Meetup

我昨晚在 Heavybit 俱乐部会所的 Docker meetup 上做了一个有趣的小演讲。演讲的视频应该很快就会在网上发布,但在此之前,我们先来回顾一下内容。

复合坞站

即使您还没有在生产中使用 Docker,Docker Compose 在您的开发和测试环境中也会非常有用。Docker Compose 基本上是一个工具,可以一次运行任意数量的 Docker 容器,这些容器相互链接,或者以简单的 YAML 文件中指定的任何方式进行配置。

服务依赖关系

service dependencies

Docker Compose 在您的开发/测试环境中的一个用例是管理服务依赖。例如,如果您需要一个 LESS 编译器、一个 linter、一个 DB 和一个应用服务器都在您的开发环境中运行,您可以简单地使用一个 docker-compose.yml 来管理它们。如果您熟悉 Foreman 之类的工具来管理您的开发环境中的多个进程,docker Compose 可以做非常类似的工作,但远不止启动每个进程,它在自己的 Docker 容器中运行每个服务,这意味着所有依赖项都是非常独立的。这里有一个视频示例,是我在昨晚的演讲中提到的一个用例(视频中没有声音):

复杂的测试服务

test services

除了管理应用程序复杂的服务依赖关系,Docker Compose 还可以帮助运行复杂的测试过程。分布式压力测试或复杂的安全扫描浮现在脑海中,但即使是普通的浏览器测试也涉及一组相当复杂的交互过程,设置和考虑起来都很复杂。Docker Compose 有助于为这种设置提供一些清晰的结构,以及开发和测试/CI 环境之间的对等性。这是另一个快速的视频演示(同样,没有声音):

这就是在您的开发和测试环境(包括 CircleCI)中使用 Docker Compose 的基本情况。以上视频中使用的所有代码都可以在 GitHub 上找到:

昨晚演示的幻灯片也可以在 SlideShare 上获得

感谢阅读!别忘了查看 Docker 上的 CircleCI 文档,了解如何使用 CircleCI 满足您的所有 Docker 需求!

用 Postman | CircleCI 测试 API

原文:https://circleci.com/blog/testing-an-api-with-postman/

本教程涵盖:

  1. 设置邮递员环境
  2. 为 API 请求编写测试
  3. 使用 Newman orb 自动化测试

从 cURL 是唯一可用的工具开始,测试 API 已经走过了漫长的道路。 Postman 通过允许开发人员从一个用户友好的界面轻松地提出请求,改善了端到端的测试体验。您可以使用 Postman 作为 API 开发和测试的全功能协作平台。

在本教程中,您将学习如何使用 Postman 的命令行实用程序 Newman 对您的 API 执行和自动化测试。

先决条件

要完成本教程,需要做一些事情:

  1. JavaScript 的基础知识
  2. 邮差桌面安装在您的系统上。你可以在这里下载。本教程使用版本 9.30.3。
  3. 一个的账户
  4. GitHub 的一个账户

我们将在这个帖子中测试构建和部署的 API。这是一个简单的 Node.js API,由创建和获取用户帐户的端点组成。这个 API 被部署到地址https://my-adonis-api-app.herokuapp.com。你可以在这里找到源代码,并按照文章中的步骤部署它(上面有链接)。

随着我们需要的一切安装和设置,是时候开始教程了。

设置邮递员环境

要为您的 API 测试建立一个自动化测试管道,您需要在 Postman 中创建一个环境。设置环境的范围将帮助您避免变量在全局范围内或与其他环境发生冲突。

打开 Postman desktop,从左侧菜单栏中选择环境。你的环境将会显示在这里,如果你有,否则,你将会有一个链接来创建一个新的。点击创建环境或使用加号图标。

Create environment - Postman

注意: 你的邮递员用户界面可能看起来与本教程中的截图略有不同,但你需要的工具将会可用。

接下来,输入环境的名称。输入环境变量api_url。用https://my-adonis-api-app.herokuapp.com填充 API 基 URL(不带尾随斜杠)。INITIAL VALUE中的值在CURRENT VALUE中重复。在本教程中保持它们的一致性。

点击保存添加新环境。从界面右上角的下拉列表中选择新环境。

Create environment variable - Postman

为 API 测试创建 Postman 集合

下一步是为我们正在测试的 API 的用户端点创建一个 Postman 集合。邮递员集合充当 API 请求的容器。邮递员集合通常用于对与特定实体相关的请求进行分组。

要创建新的收藏,请单击界面左侧栏上的收藏选项卡。然后点击创建收藏

Create collection button - Postman

填写您收藏的名称(Users)。您还可以为该集合添加描述,以提供更多信息。您的新收藏列在左侧栏的收藏标签下。

Collection List - Postman

向集合中添加请求

现在,您可以将请求添加到您的集合中。我们的 API 由两个端点组成:

  • (GET):获取用户配置文件列表
  • {{api_url}}/user/create (POST):创建新的用户配置文件

要添加新请求,请单击集合旁边的菜单图标。

Add Request - Postman

点击添加请求。为{{api_url}}/user/get端点创建一个请求,点击保存Users是我们之前创作的系列。

Add Request - Postman

将列出一个新的“请求”选项卡。使用您为当前环境创建的api_url变量,在地址栏({{api_url}}/user/get)中输入请求的端点。把它命名为Fetch the list of users

Add Request - Postman

Postman 返回用户数组。点击保存

创建另一个请求,这次是针对{{api_url}}/user/create端点的。这是一个POST请求,需要一个usernameemailpassword

Add Request - Postman

请确保保存此请求。

为 API 请求编写测试

现在设置已经完成,是时候添加一些测试了。

左侧栏应该显示{{api_url}}/user/get请求已被选中。如果不是,请单击选择它。点击测试选项卡并输入:

pm.test("Request is successful with a status code of 200", function () {
  pm.response.to.have.status(200);
});

pm.test("Check that it returns an array", function () {
  var jsonData = pm.response.json();
  pm.expect(jsonData).to.be.an("array");
}); 

Add Test 1 - Postman

你刚才加的邮差测试是断言。

在上面的代码中,第一个测试检查请求是否完成,成功状态代码为200。第二个测试检查从请求返回的数据是否是一个数组;在这种情况下,预期的用户配置文件数组。

保存并再次运行请求。关于已通过测试的详细信息列在测试结果选项卡上。

Run Tests 1 - Postman

接下来,向{{api_url}}/user/create端点添加对POST请求的测试。此端点的测试比前一个测试更复杂。这是因为 API 对usernameemail请求参数进行了重复检查。使用硬编码参数将导致请求在第一次尝试后失败。

幸运的是,你可以建立一个邮递员的Pre-request Scripts。这些脚本在触发请求之前运行。使用此脚本为请求动态生成随机用户名和电子邮件。

单击请求以加载它,然后单击预请求脚本选项卡。添加以下脚本:

let random = +new Date();

pm.globals.set("username", `${random}-user`);
pm.globals.set("email", `${random}-user@test.com`); 

Pre-request scripts - Postman

在上面的脚本中,当前时间戳用于为每个触发的请求随机创建用户名和电子邮件。随机变量usernameemail被设置为请求实例的全局变量。用下面显示的动态值替换请求体中的usernameemail

Dynamic parameters - Postman

现在,每个请求将使用动态的usernameemail参数。将以下内容添加到该请求的Tests窗口中:

pm.test("User creation was successful", function () {
  pm.expect(pm.response.code).to.be.oneOf([200, 201, 202]);
});

pm.test("Confirm response message", function () {
  var jsonData = pm.response.json();
  pm.expect(jsonData.message).to.eql("User Successfully created");
}); 

在上面的代码中,第一个测试通过检查状态代码200201202来断言一个成功的响应。这些代码用于指示成功的POST请求。第二个测试检查在json响应中返回的消息,以确保它与预期的成功消息匹配。

运行请求,测试将通过,如下所示:

Test Results 2 - Postman

设置测试自动化项目

到目前为止,我们已经使用了 Postman GUI 来发出请求,这很好,但是本教程的目标是自动化测试和生成测试结果。这使得我们的下一个任务是为测试自动化建立一个项目。

首先,在本地计算机上的任意位置为项目创建一个文件夹:

mkdir postman-api-testing 

确保您已经保存了您的请求。然后从集合上下文菜单中单击导出

Export Collection - Postman

名为User.postman_collection.json的文件将被下载。

我们还需要导出为本教程创建的 Postman 环境。点击侧边栏中的环境选项卡,确保选择了my-remote-api-testing。单击菜单图标,从弹出对话框中导出您的环境。

Export Environment - Postman

名为my-remote-api-testing.postman_environment.json的文件将被下载。

注意: 如果你使用不同的文件名,一定要保持一致,或者使用教程中的例子来重命名文件。

将这两个文件放在项目文件夹的根目录下。

使用 Newman orb 自动化测试过程

本教程的最后一步是编写测试自动化脚本。这个脚本将使用 Postman 的 CLI 兄弟 Newman,使用导出的环境运行集合。幸运的是,CircleCI 为纽曼准备了一个Orbs 是抽象了大量样板代码的包。orb 为开发人员提供了一个友好的 API,用于调用常见命令和使用 Newman、Heroku 和 environments 等工具。

因为newman是第三方 orb,你的 CircleCI 账号必须设置成允许。要更改此设置,请转到您的 CircleCI 帐户的组织设置。在左侧工具条中,点击安全。在安全设置页面上,选择以允许第三方 orb 进入您的构建。

Security Settings - CircleCI

创建配置文件

在项目的根目录下创建一个名为.circleci的文件夹,并在文件夹中添加一个名为config.yml的配置文件。在该文件中,输入以下代码:

version: 2.1
orbs:
  newman: postman/newman@1.0.0
jobs:
  build:
    executor: newman/postman-newman-docker
    steps:
      - checkout
      - newman/newman-run:
          collection: ./User.postman_collection.json
          environment: ./my-remote-api-testing.postman_environment.json 

通过调用newman/newman-run命令,上面的脚本加载 Postman 的Newman orb,并使用指定的environment运行collection。就是这么简单。

提交您的更改并将您的项目推送到 GitHub

将项目连接到 CircleCI

现在,登录你的 CircleCI 账户。如果你注册了你的 GitHub 账户,你所有的库都可以在你项目的仪表盘上看到。

点击postman-api-testing项目旁边的设置项目

Add Project - CircleCI

这将触发管道构建并成功运行它。

Build Successful - CircleCI

要查看测试结果,单击构建并展开测试选项卡。

Test Results - CircleCI

正如预期的那样,Newman 运行了集合,然后生成了一个报告,详细说明了测试是如何运行的。

厉害!

结论

手动测试过程会很快变成一个麻烦的、容易出错的例行程序。Postman GUI 已经是每个 API 开发者的必备工具包。正如我们在本教程中所展示的,CircleCI 自动化管道和newman orb 可以让你的 API 测试更进一步。

编码快乐!


Fikayo Adepoju 是 LinkedIn Learning(Lynda.com)的作者、全栈开发人员、技术作者和技术内容创建者,精通 Web 和移动技术以及 DevOps,拥有 10 多年开发可扩展分布式应用程序的经验。他为 CircleCI、Twilio、Auth0 和 New Stack 博客撰写了 40 多篇文章,并且在他的个人媒体页面上,他喜欢与尽可能多的从中受益的开发人员分享他的知识。你也可以在 Udemy 上查看他的视频课程。

阅读 Fikayo Adepoju 的更多帖子

测试 Commander.js 命令行应用程序

原文:https://circleci.com/blog/testing-command-line-applications/

本教程涵盖:

  1. CLI 应用程序如何工作
  2. 为 CLI 应用程序编写测试
  3. 使用 CircleCI 自动化命令行应用程序测试

生产中的突破性变化是不方便的,而且修复起来可能会很昂贵。使用类似于git clone < some GitHub repository >的命令,在您的终端上执行是一种常见的做法,称为使用命令行。这种做法比使用 GUI 更快更有效。在本教程中,我将带您了解测试命令行应用程序git的过程,解释您为什么需要命令行应用程序,并详细描述它们是如何工作的。

先决条件

完成本教程需要以下项目:

我们的教程是平台无关的,但是使用 CircleCI 作为例子。如果你没有 CircleCI 账号,请在 注册一个免费的

设置应用程序

为了帮助你完成本教程,我创建了一个 Github 库供你克隆。您需要通过在终端中运行以下命令来安装项目的依赖项:

git clone https://github.com/CIRCLECI-GWP/palatial-cakes-cli-app.git

cd palatial-cakes-cli-app

npm install 

为什么要使用 CLI 应用程序?

复杂的图形用户界面(GUI)带来的技术进步是惊人的,但我们不能否认命令行应用程序(CLI)引发了软件革命。

那么,到底什么是CLI应用呢?命令行界面(CLI)应用程序是一种程序,它允许通过计算机终端输入文本命令,然后主机将这些命令解释为正在执行的程序。

让我们看看为什么在某些情况下 CLI 应用程序比 GUI 应用程序更好。

  1. 了解 CLI 命令及其工具可以提高开发人员的工作效率。使用命令行实现自动化(重复任务)比使用 GUI 更快更有效。
  2. 与 GUI 应用程序相比,CLI 应用程序需要更少的内存和机器处理能力。
  3. 由于其基于文本的界面,CLI 应用程序可以在低分辨率显示器上完美运行,并受到大量操作系统的支持。

CLI 应用程序如何工作的描述

使用 CLI 应用程序需要在主机终端输入并执行文本命令。一些 CLI 应用程序允许用户从提供的默认options中进行选择,而其他应用程序除了提供的选项之外还需要手动文本输入。

让我们来看看执行fetch命令时,git命令行应用程序的命令行接口结构和语法示例。

Git fetch CLI command

在这种情况下,Program关键字是实际程序的名称,通常是一个名词。command(一个动词)描述了程序做什么或者我们指示程序做什么。Arguments允许 CLI 接受正在处理的值。修改命令行为的一种有记录的参数类型被称为option。这些字符以连字符为前缀输入。

概述我们的示例 CLI 应用程序如何工作

在本教程中,我创建了一个实践 CLI,它将帮助您学习如何测试命令行应用程序。CLI 应用程序允许您订购蛋糕,并创建所有可订购蛋糕的列表。

运行我们的 CLI 应用程序有两个不同的命令:一个用于订购蛋糕,另一个用于获取可以订购的蛋糕列表。

订购蛋糕

要运行命令行蛋糕订单:

npm run order-cake 

列出所有蛋糕

要获取所有可用蛋糕的列表,请运行以下命令:

npm run list-cakes 

下面是使用 CLI 订购和列出蛋糕的两种不同工作流的图示。

Palatial cakes application flows

为我们的 CLI 应用程序编写测试

既然您已经看到了我们的 CLI 应用程序是如何工作的,那么最好仔细检查添加更多的更改不会破坏代码。测试还有助于您深入了解潜在的易出错区域。为此,您将向现有的 cake 应用程序逻辑添加测试。然后您将使用 Jest 来测试您的应用程序。

理解蛋糕订购逻辑

处理蛋糕订单的逻辑是这样实现的:

/** lib/order.js **/
const inQuirerOder = async () => {
  const answers = await inquirer.prompt(orderDetailsQuestions);
  return Object.keys(answers).map(
    (key, index) => `\n${index + 1}:${key} => ${answers[key]}`
  );
}; 

使用Inquirer.jsprompt()方法,您为用户提供一个选项列表。对于每个问题,您使用其答案检索用户选择的答案。

根据 Inquirer.js 的文档,prompt 方法接受一个包含问题对象和另一个参数的数组:answers,一个包含以前回答的问题的值的对象。默认情况下,answers 对象为空:

inquirer.prompt(questions, answers) -> promise 

为了测试这个逻辑,我们将提供问题和预期的答案,而不仅仅是提供问题,这将通过 CLI 提示输入。这导致询问者避免提示回答。

因为这个方法返回一个承诺,所以我们可以使用[async-await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)模式来获取响应对象。

接下来,您将测试订购各种蛋糕,因此将相关的测试分组到一个测试套件中是一个很好的实践。下面提供了使用describe()方法对几个相关测试进行分组的语法,以及三个测试块,为您的蛋糕提供不同类型的测试。

/** Order.spec.js **/

describe('Order different types of cakes', () => {
 test('order cake: type A', async () => {
   ...
 });
 test('order cake: type B', async () => {
   ...
 });

 test('order cake: type C', async () => {
   ...
 });
}); 

断言入门

从文件顶部的order.js文件和inquirer模块导入所有内容。

/** __tests__/order.spec.js **/

const order = require("../lib/order");
const inquirer = require("inquirer"); 

现在您已经拥有了所需的模块,您可以在第一个测试块中快速创建一个 cake 对象;这将是我们的答案。您将从导入的订单文件中得到您的问题。

/** __tests__/order.spec.js **/
cakeA = {
  Coating: "Butter",
  type: "Strawberry",
  "Cake Size": "Medium",
  Toppings: "Fruit",
}; 

第一个 cake 对象包含key-value对,这些键来自原始的 question 对象。其他两个测试块之间的唯一区别是 cake 对象和对象名称的值。

如前所述,我们将为询问者提示方法提供各种问题和答案:

const order_cli = await inquirer.prompt(order.orderDetailsQuestions, cakeA); 

返回的承诺是一个对象,包含用户在使用 CLI 时可能已经选择的答案。因此,我们可以断言这些被嘲笑的反应。

expect(order_cli).toMatchObject(cakeA); 

如果你想回顾整个order.spec.js测试文件,你可以在这里找到它

就像这样,您现在能够测试蛋糕订单命令的各种输入和输出。

列出蛋糕

测试处理蛋糕渲染的逻辑将会很简单。您将提供一个蛋糕列表,然后根据您的 CLI 呈现的蛋糕列表断言它。

全面实施:

/** __tests__/cakes-list.spec.js **/
const renderCakes = require("../lib/cakes-list");

const results = [
  "\n1 => Strawberry",
  "\n2 => Vanilla",
  "\n3 => Mint",
  "\n4 => White Chocolate",
  "\n5 => Black Forest",
  "\n6 => Red Velvet",
  "\n7 => Fruit Cake",
];

test("renders cakes list", () => {
  expect(renderCakes()).toEqual(results);
}); 

这个代码片段导入了cakes-list模块,然后使用renderCakes方法断言蛋糕列表与之前在命令行应用程序中声明的列表相同。

设置错误处理

您可能希望确保在您的 CLI 应用程序中检测和处理错误,以便在错误发生时修复错误。您将在这里编写的测试将涵盖unknown optionscommands,以及undefined选项的使用。在这个测试中,您将不会测试输出,而是测试是否检测到错误。

检查分隔符后选项的检测

您可以从发现当参数包含未定义的选项时会发生什么开始。在之后提供了可能的选项。

在声明程序变量之前,您需要使它可用:

const { Command } = require("commander"); 

Commander.js建议创建一个本地命令对象以在测试时使用。稍后,您将在每个测试块中这样做,就像这样:

const program = new Command(); 

考虑下面的测试片段:

/** handle-errors.spec.js **/
test("when arguments includes -- then stop processing options", () => {
  const program = new Command();
  program
    .option("-c, --coatings [value]", "cake coatings to apply")
    .option("-t, --type <cake-type>", "specify the type of cake");
  program.parse([
    "node",
    "palatial-cakes-cli",
    "--coatings",
    "--",
    "--type",
    "order-cake",
  ]);
  const opts = program.opts();
  expect(opts.coatings).toBe(true);
  expect(opts.type).toBeUndefined();
  expect(program.args).toEqual(["--type", "order-cake"]);
}); 

第一个非选项参数应该被接受为指示选项结束的分隔符。即使它们以字符-开始,任何后续的参数都应该被视为操作数。

因为—-coatings选项出现在--之前,所以它被视为有效选项,您可以执行断言来查看这是否正确。接下来的-–type应该被视为一个undefined选项。

Valid and invalid CLI options

program.parse(arguments)将处理参数,程序没有接受的任何选项都将留在program.args数组中。因为–type选项被忽略了,所以可以断定它属于program.args数组。

检查未知选项的检测

/** handle-errors.spec.js **/
test("unknown option, then handle error", () => {
  const program = new Command();
  program
    .exitOverride()
    .command("order-cake")
    .action(() => {});
  let caughtErr;
  try {
    program.parse(["node", "palatial-cakes-cli", "order-cake", "--color"]);
  } catch (err) {
    caughtErr = err;
  }
  expect(caughtErr.code).toBe("commander.unknownOption");
}); 

上面的测试块没有什么异常。它只是将一个未知选项–-color传递给program.parse()方法,并检查它是否被检测为错误。你可以确认这个错误是commander.unknownOption的一个实例,正如预期的那样。

然后,通过将错误整齐地记录到终端来处理错误。如果您在handle-errors.spec.js文件中运行上面的代码块npm test,您应该得到一个通过测试的错误消息:error: unknown option '--color'

检查未知命令的检测

就像您测试一个未知选项一样,您可以检测一个未知命令,然后在终端上整齐地记录一条错误消息。

下面是测试片段的实现:

/** handle-errors.spec.js **/
test("unknown command, then handle error", () => {
  const program = new Command();
  program
    .exitOverride()
    .command("order-cake")
    .action(() => {});
  let caughtErr;
  try {
    program.parse(["node", "palatial-cakes-cli", "make-order"]);
  } catch (err) {
    caughtErr = err;
  }
  expect(caughtErr.code).toBe("commander.unknownCommand");
}); 

这提供了program.parse()方法中预期参数的列表,但是带有一个未知的 CLI 命令make-order。您可以预期抛出的错误是commander.unknownCommand的一个实例,因此您可以针对该错误断言它。在 CI 环境中设置测试之前,运行您的测试应该验证它们都在本地通过。

在下一节中,您将把您的 GitHub 帐户连接到 CircleCI。然后,您将把您编写的所有测试推送到您的 Github 帐户,以便您可以配置它们在 CI 环境中运行。

配置 CircleCI

要配置 CircleCI,创建一个名为.circleci的目录并添加一个名为config.yml的文件。在.circleci/config.yml文件中,添加以下配置:

# .circleci/config.yml
version: 2.1
jobs:
  build:
    working_directory: ~/palatial-cakes-cli-app
    docker:
      - image: cimg/node:10.16.3
    steps:
      - checkout
      - run:
          name: update npm
          command: "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 test
      - store_artifacts:
          path: ~/palatial-cakes-cli-app/__tests__ 

现在您可以提交并将您的更改推到存储库中。然后在 CircleCI 仪表盘上设置你的项目。

在 CircleCI 仪表板上,转到项目。将列出与您的 GitHub 用户名或组织相关的所有 GitHub 存储库。本教程的存储库是palatial-cakes-cli-app。在“项目”面板上,选择设置项目的选项。使用分支main中现有配置的选项。

Setting up CircleCI

瞧啊。在检查 CircleCI 仪表板并展开构建细节时,您可以验证您已经成功运行了 CLI 应用程序测试并将其集成到 CircleCI 中。

Successful test execution

现在,任何时候你对你的应用程序进行更改,CircleCI 都会自动运行你的测试,并验证所做的更改是否会破坏你的应用程序。

结论

在本教程中,您了解了 CLI 应用程序及其工作原理。您设置了一个简单的 CLI 应用程序,了解了它的工作原理,并为它编写了测试。然后,您配置 CircleCI 来运行您的测试,并验证这些测试是否成功。

虽然已经创建并配置了示例应用程序,但是浏览这些文件可能是值得的。这可以帮助您更好地理解当 CLI 应用程序由commander.js运行时会发生什么。

一如既往,我很高兴为您创建本教程,我希望您会发现它很有价值。直到下一个,继续学习,继续建设!


Waweru Mwaura 是一名软件工程师,也是一名专门研究质量工程的终身学习者。他是 Packt 的作者,喜欢阅读工程、金融和技术方面的书籍。你可以在他的网页简介上了解更多关于他的信息。

阅读更多 Waweru Mwaura 的帖子

坞站图片-gos |圆形

原文:https://circleci.com/blog/testing-docker-images-with-circleci-and-goss/

测试是任何 CI/CD 管道的关键部分。大多数团队在应用程序级测试方面做得很好,并且有大量的框架(JUnit、RSpec 等。)来支持。但是服务器级测试——服务器配置和服务的验证——经常被忽略。在这篇博文中,我们将探索一种方法来使用 Goss 对我们的自定义 Docker 图像执行测试,作为 CircleCI 管道的一部分。

首先, Goss 是什么?

Goss 是一个位于 YAML 的 serverspec 替代工具,用于验证服务器的配置。

在这篇文章中,我们将只涉及几种类型的测试,所以值得探索一下 Goss 手册来学习所有可用的操作。该项目还包括dgoss,它是一个面向goss的 Docker 包装器。它包括两个基本操作:editrun

先决条件

为了在本地运行测试,您需要根据您的操作系统使用适当的步骤安装 dgoss 。

您还需要一个正在开发的 Docker 图像。在这篇文章的剩余部分,我将引用我的示例项目,你应该分叉并跟随它!

创建测试

正如 Goss 手册所建议的,最简单的开始方式是通过使用goss add <TYPE> <ARGUMENTS>在你想要测试的服务器/容器内部运行。因为我们在容器的侧面运行,所以我们需要首先构建它。

docker build . -t my-image:test 

然后使用上面的标记名,我们可以通过 dgoss 运行安装了 Goss 的容器。dgoss edit支持启动图像所需的任何 Docker 参数。在我的示例项目中,这包括一个修改的入口点。

dgoss edit --entrypoint=/test/gossEntrypoint.sh my-image:test 

您可以运行test/editTests.sh来作为重复这些命令的快捷方式。CDGossDocker_edit1.gif

一旦进入正在运行的 Docker 映像,您就可以探索不同的测试,Goss 命令会自动将这些测试附加到 goss.yaml 文件中。退出后,该文件将被复制到您的本地工作站。

验证文件是否存在

我们的第一个测试非常简单:确保我们的 entrypoint.sh 文件在文件系统中。

/goss # goss add file /entrypoint.sh 

CDGossDocker_file1.png

您可以看到输出指定了位置、类型、所有者和其他关键属性。您现在可以cat goss.yml看到初始的测试结构,但是一个健康的映像不仅仅包括文件的存在,所以让我们再添加一些测试。

验证命令输出

我们的 Docker 映像非常简单,entrypoint 只是根据安装在我们的生产环境中的配置文件运行一些简单的逻辑。我们希望确保它按照预期处理这个配置文件。

对于我们的第一个测试,我们希望确保如果没有找到文件,脚本退出时显示一个读取警告。由于这是默认状态,我们可以运行以下命令:

goss add command "/entrypoint.sh /config.txt /schedule.txt" 

引用参数很重要,否则 Goss 会将它们视为单独的命令。

CDGossDockercommand1.png

您可以看到 Goss 期望退出状态为 1,这是一条打印到 stderr 的消息。

我们还有一些更复杂的测试,因为它们修改文件来模拟生产中可能出现的某些条件。对于这些测试,我发现将逻辑封装到它们自己的脚本中,并在文件名中指明明确的意图,更具可读性和可维护性。一个例子可见于 testNonEmptyScheduleModifiesScalerConfig.sh 。该测试将确保非空调度修改我们的配置。

该目录中的每个测试都设置一些预期的状态,然后像我们上面做的那样调用/entrypoint.sh /config.txt /schedule.txt

模式匹配

有时候输出会有动态内容,幸运的是 Goss 支持一些基本的模式匹配。例如,上面的测试在测试中使用了当前的日期和时间,并将在输出中打印出来。因为这将根据我们执行测试的时间来改变输出,所以我们使用一个正则表达式来处理这个问题。

/test/testNonEmptyScheduleModifiesScalerConfig.sh:
  exit-status: 0
  stdout:
  - /Matching rule - Day:\s[1-7], Hour:\s[1-2]?[0-9], Type :\sdocker, Count:\s5/
  - Updated docker preallocation count to 5
  - /Matching rule - Day:\s[1-7], Hour:\s[1-2]?[0-9], Type :\smachine, Count:\s5/
  - Updated machine preallocation count to 5
  - schedule updated
  stderr: []
  timeout: 10000 

注意:需要使用\s,这样 yaml 就不会将冒号解析为 yaml key: value。

最终规范(goss.yaml)

当您在运行完所有的goss add步骤后键入exit时,您将看到 Goss 在停止实例之前将生成的 goss.yaml 复制回您的本地机器。

/goss # exit
INFO: Copied '/goss/goss.yaml' from container to 'test'
INFO: Deleting container 

该文件是我们已经看到的各个输出的集合,按照它们的类型分组。

file:
  /entrypoint.sh:
    exists: true
    mode: "0755"
    size: 1530
    owner: root
    group: root
    filetype: file
    contains: []
  /schedule.sh:
    exists: false
    contains: []
command:
  /test/testEmptyScheduleIgnored.sh:
    exit-status: 1
    stdout: []
    stderr: []
    timeout: 10000
  /test/testNonEmptyScheduleModifiesScalerConfig.sh:
    exit-status: 0
    stdout:
    - /Matching rule - Day:\s[1-7], Hour:\s[1-2]?[0-9], Type :\sdocker, Count:\s5/
    - Updated docker preallocation count to 5
    - /Matching rule - Day:\s[1-7], Hour:\s[1-2]?[0-9], Type :\smachine, Count:\s5/
    - Updated machine preallocation count to 5
    - schedule updated
    stderr: []
    timeout: 10000
  /test/testNonExistentScheduleIgnored.sh:
    exit-status: 1
    stdout: []
    stderr:
    - 'cat: can''t open ''/schedule.txt'': No such file or directory'
    timeout: 10000 

运行我们的测试

现在我们应该有了一个工作的 Docker 映像(我们希望如此)和一些在 goss.yaml 中定义的测试,我们想对一个新的映像执行我们的测试。dgoss 希望当前目录中有一个名为 goss.yaml 的文件。因为我把我们的放在了test文件夹中,所以我们需要包含GOSS_FILES_PATH参数。

docker build . -t my-image:test
GOSS_FILES_PATH=test dgoss run --entrypoint=/test/gossEntrypoint.sh my-image:test
# OR provided wrapper for this tutorial
test/runTests.sh 

上面的命令将构建一个新的 Docker 映像,mount Goss 和 goss.yaml,并执行我们的测试。

CDGossDocker_test1.gif

这个输出不是很令人兴奋,但是通过测试应该是这样的。所以,让我们打破一些东西!

#!/bin/bash
#
# Any changes are written to the SCALING_FILE
#
set -euo pipefail

#this won't work..
CMD=`./nonexistentScript.sh`

SCALING_FILE=$1
SCHEDULE_FILE=$2 

当你执行test/runTest.sh时会发生什么?Goss 将打印预期的而不是实际的输出,并包括一个错误摘要。它还以非零状态存在,这对我们的下一步很重要:与我们的持续集成管道集成!

在每次提交时运行我们的测试

我们在本地运行测试很好,但这里的想法是将 Docker 级别的测试集成到我们的 CI/CD 管道中,这样我们就不会发布糟糕的映像。

CDGossDocker_passingtests.png

CDGossDocker_failingtests.png

CircleCI 不提供预装 Goss 的映像,但安装它只需要一秒钟。您可以查看 sample config.yml 了解完整设置。我在这里只包括相关的片段。

jobs:
  test:
    docker:
      - image: circleci/python:2-jessie
    steps:
      - checkout

      - setup_remote_docker:   # (2)
          docker_layer_caching: true # (3)
      - run:
          name: Install goss
          command: |
            # rather than give internet scripts SU rights, we install to local user bin and add to path
            mkdir ~/bin
            export GOSS_DST=~/bin
            export PATH=$PATH:~/bin
            curl -fsSL https://goss.rocks/install | sh
            goss -version
      - run:
          name: Test
          command: |
            # Don't forget path!
            export PATH=$PATH:~/bin
            # Important, change from mount to work on remote docker, see https://github.com/aelsabbahy/goss/pull/271
            # If using machine image you do not need this.
            export GOSS_FILES_STRATEGY=cp
            test/runTests.sh junit
      - store_test_results:
          path: goss 

试验结果

注意:对于 CI 执行,我们将参数junit传递给我们的测试运行程序。这会将输出格式转换成~/goss/report.xml,并包含在 CircleCI 的测试摘要中:

CDGossDocker_testresults.png

摘要

就这样吧!随着这个基本结构的运行,您可以添加更成熟的测试,并将基于 Goss 的映像测试作为核心步骤包含在您的 CI/CD 管道中,以保持您团队的代码库从服务到服务器都得到测试。

一定要把那个快乐的绿色状态徽章加到你的回购上,让全世界都知道!

Passed_Build.png

航运愉快!

使用 Pytest | CircleCI 测试烧瓶框架

原文:https://circleci.com/blog/testing-flask-framework-with-pytest/

本教程涵盖:

  1. 在 Flask 应用程序上设置 Pytest
  2. 编写 Pytest 测试
  3. 分组测试

用任何编程语言编写测试都可能很困难,但也不尽然。在本教程中,我将向您展示如何使用 Flask 和 Pytest 轻松编写和运行测试。

作为奖励,我们还将集成一个 CI/CD 管道,使用 CircleCI 在 Flask 应用程序上运行测试。

请务必查看我们的其他 Flask 教程,了解应用程序日志认证装饰者自动化 Flask 部署

先决条件

为了从本教程中获得最大收益,您需要:

  • Python 编程语言的基本理解
  • 了解烧瓶框架
  • 对测试的基本理解

您还需要进行以下安装和设置:

  1. 您的计算机中安装的 Python 版本> = 3.5
  2. GitHub 账户;您可以在处创建一个
  3. CircleCI 账户;您可以在处创建一个
  4. 来自 GitHub 的项目资源库;在这里克隆它

我们的教程是平台无关的,但是使用 CircleCI 作为例子。如果你没有 CircleCI 账号,请在 注册一个免费的

一旦项目被克隆,转到项目的根文件夹。使用命令pip install -r requirements.txt安装依赖项。

为什么要用烧瓶?

Flask 是一个用 Python 编写的轻量级微 web 框架。Flask 预装了核心特性,因此您可以根据项目的需求进行定制。Flask 并不决定使用哪个数据库或者默认的解析器是什么。所有这些都可以通过修改来改变,这使得它对于开发团队的新需求来说是可扩展的和灵活的。

在这个项目教程中,我们将使用一个用 Flask 构建的简单的book-retrieval API。我们将使用 API 来展示如何在 Flask 应用程序中编写测试。为了保持本教程的简单,我们将把重点放在测试 API 上,而不是构建它。API 应用程序可以在克隆存储库的项目根目录下的api.py文件中找到。

现在你知道了 Flask 是什么以及如何使用它,你可以通过学习Pytest以及如何在 Flask 应用上设置它来建立这些知识。

什么是 Pytest,我如何使用它?

Pytest 是一个 Python 测试框架,旨在帮助开发人员编写更好的系统并满怀信心地发布它们。用 Pytest 编写小型的、可伸缩的测试很简单。该代码片段显示了 Pytest 测试的基本布局:

from api import app # Flask instance of the API

def test_index_route():
    response = app.test_client().get('/')

    assert response.status_code == 200
    assert response.data.decode('utf-8') == 'Testing, Flask!' 

测试 Flask 要求我们首先从我们的api(在我们的应用程序中创建)导入一个 Flask 实例app,如前面的代码片段所示。然后,导入的实例从 Flask 公开一个test_client()方法,该方法包含向被测应用程序发出HTTP请求所需的特性。在本例中,它指向我们默认的(/ ) API 端点。然后,我们断言test_client()收到的响应是在解码字节对象后预期的。在下面的步骤中,您将使用这种格式来编写您的测试。

注意: 默认情况下,Pytest 使用utf-8编解码器对 API 响应进行编码。您需要使用decode()方法将从测试客户端接收的byte对象转换成可读的字符串响应,如前面的代码片段所示。

在烧瓶上设置 Pytest

安装 Pytest 很简单。如果您克隆了存储库,那么它已经安装好了,您可以跳过这一步。如果尚未克隆存储库,请在终端上运行以下命令:

pip install pytest 

注意: 如果你使用的是虚拟环境在安装前激活。最佳实践是使用虚拟环境来隔离不同的应用程序及其相关的 Python 包。

导入 Pytest 模块

安装完成后,导入 Pytest 模块。这个片段可以放在任何测试文件上:

import pytest 

测试的命名约定

对于本教程,将您的测试保存在应用程序根文件夹的tests目录中。Pytest 建议测试文件名以格式test_*.py开始,或者以格式**_test.py结束,使用这些格式可以使 Pytest 很容易地自动发现测试文件,并且在运行测试时最大限度地减少混淆。当创建测试:test_index_route():时,在test methods前加上test一词。

用 Pytest 编写测试

现在您已经知道如何在 Flask 应用程序上设置 Pytest,您可以开始为图书检索 API 编写测试了。你的第一个测试将是/bookapi/books路线。

# get books data

books = [
    {
        "id": 1,
        "title": "CS50",
        "description": "Intro to CS and art of programming!",
        "author": "Havard",
        "borrowed": False
    },
    {
        "id": 2,
        "title": "Python 101",
        "description": "little python code book.",
        "author": "Will",
        "borrowed": False
    }
]

@app.route("/bookapi/books")
def get_books():
    """ function to get all books """
    return jsonify({"Books": books}) 

这个路径所做的只是返回一个硬编码到books变量中的图书列表。您将在下一步中测试这个端点。使用相同的格式,定义您的测试函数,并断言test_client()收到的响应是您所期望的。

import json
from api import app

def test_get_all_books():
    response = app.test_client().get('/bookapi/books')
    res = json.loads(response.data.decode('utf-8')).get("Books")
    assert type(res[0]) is dict
    assert type(res[1]) is dict
    assert res[0]['author'] == 'Havard'
    assert res[1]['author'] == 'Will'
    assert response.status_code == 200
    assert type(res) is list
    .... 

这个测试片段确保您能够接收一个图书列表。通过断言test_client()收到的响应确实是您期望的图书列表,您可以做到这一点。您还断言响应包含一个dictionarieslist,它是定义的 book 对象中的单本书。您还断言列表中的第一本书有作者Havard,第二本书有作者Will。这就是编写一个从/bookapi/books端点获取所有书籍的测试所需的全部内容。

使用 Pytest 运行测试

要运行 Pytest 测试,您可以在终端中使用py.testpytest。您也可以通过在 Pytest 命令后明确指定文件名来运行单个文件:pytest test_api.py。当执行这些命令时,Pytest 会在根目录或指定的单个文件中查找所有的测试。

当没有显式定义测试文件时,Pytest 在根目录中执行任何遵循标准命名模式的测试,而不需要指定文件名或目录。

Executing your first test

您已经验证测试已成功执行!Pytest 用绿点.标记通过的测试;它用红色的F标记测试失败。计算点或 f 的数量,以确定有多少测试通过和失败,以及执行的顺序。

注意: 如果您正在调试控制台的测试,并且您需要打印出一个响应,您可以使用$ pytest -s test_*.py来登录到 stdout。当 Pytest s选项被定义时,您可以在测试中控制消息,并在测试执行期间调试输出。

既然您已经成功地执行了您的第一个测试,那么当您推进到main分支时,您可以集成 CircleCI 来自动运行测试。

设置 CircleCI

在根目录下创建一个.circleci目录,然后在那里添加一个config.yml文件。配置文件包含每个项目的 CircleCI 配置。使用 CircleCI Python orb执行测试,如下所示:

version: 2.1
orbs:
  python: circleci/python@2.1.1
jobs:
  build-and-test:
    docker:
      - image: cimg/python:3.11.0
    steps:
      - checkout
      - python/install-packages:
          pkg-manager: pip
      - run:
          name: Run tests
          command: pytest
workflows:
  sample:
    jobs:
      - build-and-test 

CircleCI orbs 是 YAML 代码的可重用包。Orbs 将多行代码压缩成一行。在这个例子中,这一行代码是:python: circleci/python@2.1.1。您可能需要启用组织设置以允许在 CircleCI 仪表板中使用第三方 orb,或者向您组织的 CircleCI 管理员请求权限。

设置好配置后,提交您的更改,然后将您的更改推送到 GitHub。

登录 CircleCI 并转到项目仪表板。从与您的用户名或组织相关联的 GitHub 存储库列表中,找到您想要在 CircleCI 中设置的特定存储库。这种情况下是testing-flask-with-pytest

Select project

接下来,点击设置项目开始在 CircleCI 上构建项目。这将显示一个模式弹出窗口,其中有一个默认选项,可以使用项目存储库中的配置文件。输入存放配置文件的分支的名称。

Select configuration

点击设置项目完成该过程。

瞧啊。在 CircleCI 仪表板中,单击构建以查看详细信息。您可以验证运行第一个 Pytest 测试并将其集成到 CircleCI 中是成功的。

Pipeline setup success

既然您已经成功地设置了持续集成,下一步就是对测试进行分组,并运行分组测试的批次。

分组和运行测试批次

随着应用程序功能的增加,您需要增加测试的数量,以确保一切正常。很容易被大量可用的测试脚本淹没。不过您不需要担心,Pytest 已经解决了这个问题。Pytest 允许您通过对测试进行分组,从一个文件中运行多个测试。

Pytest 提供了markers,您可以使用它来设置测试函数的属性和特性。

使用 Pytest 测试标记

我们可以使用标记赋予测试不同的行为,比如跳过测试,运行测试的子集,甚至失败。Pytest 附带的一些默认标记包括xfailskipparameterize。对于这个项目,您将创建一个定制标记,它将对所有执行GET请求到/bookapi/books/bookapi/book/:id端点的测试进行分组。

以下是您将用于创建自定义标记的结构示例:

@pytest.mark.<markername>
def test_method():
  # test code 

要在 Pytest 中使用自定义标记,请将其定义为pytest命令中的一个参数:

$ pytest -m <markername> 

-m <markername>是您将用于测试的自定义标记名称。

您需要在您的测试文件中导入pytest来使用 Pytest 标记。还需要注册标记,以便 Pytest 可以抑制关于它们的警告。将此添加到您的pytest.ini文件中:

[pytest]
markers =
    <markername>: Marker description 

使用标记对测试进行分组

对于本教程,您想要将向/bookapi端点发出GET请求的测试分组。

创建一个名为get_request的客户标记,并将其添加到测试中:

import pytest
...
# Other imports here

@pytest.mark.get_request
def test_get_book_by_id():
    response = app.test_client().get('/bookapi/books/1')
    res = json.loads(response.data.decode('utf-8')).get("Book")
    print(res)
    assert res['id'] == 1
    assert res['author'] == 'Havard'
    assert res['title'] == 'CS50'
    assert response.status_code == 200 

pytest -m get_request参数运行测试会执行测试文件中所有用@pytest.mark.get_request装饰器标记的测试。您也可以通过在终端中运行它来验证这一点。

现在,将您的更改提交并推送到 GitHub,并检查管道是否成功执行。

Passing Tests

太棒了。您的所有测试都已成功执行。

结论

在本教程中,您已经使用 Pytest 设置了测试,执行了测试,学习了使用 Pytest 标记对测试进行分组。您学习了如何使用 Pytest 命令行参数来执行测试,以及如何使用test_client()方法来发出HTTP请求。您知道如何在测试中使用收到的响应。我希望你喜欢这个教程。如果你有,与你的团队分享你所学到的。没有什么比教别人更能巩固学习了。直到下一个,享受你的编码项目!


Waweru Mwaura 是一名软件工程师,也是一名专门研究质量工程的终身学习者。他是 Packt 的作者,喜欢阅读工程、金融和技术方面的书籍。你可以在他的网页简介上了解更多关于他的信息。

阅读更多 Waweru Mwaura 的帖子

云中测试如何为开发团队带来价值

原文:https://circleci.com/blog/testing-in-the-cloud/

测试是软件开发过程中不可或缺的一部分,也是开发团队更好地理解应用程序如何运行的关键方法之一。测试还可以防止对代码库进行影响代码其他部分的更改,使您能够测量软件的质量,并在用户与软件交互之前消除任何错误。

大多数开发团队使用单元和集成测试来评估他们的软件。这种测试通常发生在开发人员和测试人员的本地机器上。虽然本地测试有助于将开发人员的注意力集中在软件组件的行为上,但是在云中测试可以使整个开发团队更加有效,并帮助您的组织更快地交付更好的软件。

云测试使用持续集成平台来自动测试代码库每次变更时软件的完整性。这些平台产生即时结果,并提供给整个组织,快速发现问题,改善团队协作。云测试还提供了对大范围计算资源的访问,允许您在没有任何硬件限制的情况下运行测试。

本文讨论了为什么您的团队应该在云中进行测试。它探讨了云测试的好处,以及从测试数据中获得的洞察力如何能够显著地改进整个软件开发过程。

在云中测试相对于本地测试的优势

开发团队从任何软件测试中受益,无论是在云中还是在本地机器上。但是云提供的一些好处超过了本地测试提供的好处。您的一些基于云的新能力:

  • 减少发现的错误数量,并最大限度地降低其严重性
  • 可视化和量化测试优势
  • 加强决策
  • 提供实时见解
  • 加速反馈

减少错误的数量和严重性

云中的测试确保测试总是在每次提交后运行,以保证没有人将错误带入产品。

基于云的持续集成平台可以在每次提交每个源代码控制分支和合并请求后触发一个测试套件。这给了代码评审者信心,在接受合并请求之前,所有的测试都已经通过了。

持续集成的一个基本原则是开发人员应该在一天中频繁地进行小的提交。通过对每个变更进行粒度提交和运行测试,您可以更好地了解代码的状态,并在测试失败时更快地采取行动。这使您对开发过程有了更多的控制,使您的团队更加敏捷和灵敏,并提高了代码的整体质量。

可视化和量化测试优势

云中的测试使得测试的影响更容易可视化和量化。

离线执行,测试看起来像是消耗开发人员和测试人员时间的隐性成本,没有可测量的投资回报。相比之下,持续云测试使得经常运行测试变得容易,跟踪测试防止 bug 扩展到生产的频率,以及跟踪随着时间的推移您为了响应新特性和 bug 报告而添加了多少测试。

加强决策

在云中测试有助于您做出更好的决策。如果您必须关注并在某个特定区域执行更多的测试来响应 bug 报告,那么您可能希望在将来更仔细地研究那个区域和特性。这有助于团队解决与容量相关的问题,并保持对新出现的错误或挑战的响应。

提供实时见解

借助云测试,您可以在线访问计算资源,并在实时环境中监控测试。这提供了增强的移动性,帮助团队响应任何与容量相关的问题,并收集关于软件性能和服务可用性的信息。

加速反馈

云测试的另一个好处是,它比本地测试更快地产生数据,在本地测试中,资源限制通常意味着您必须一次运行一个测试。

通过持续云测试,您可以并行或同时运行多个测试。这意味着您可以更快地从测试中收到可操作的信号,从而提高生产率和效率。您可以轻松捕获测试输出,并使用 CircleCI 的 Insights dashboard 等可视化工具来改进开发过程。

如何从测试数据中获取有价值的见解

对团队来说,在云中进行测试的最大好处之一是能够从测试数据中快速获得洞察力。获得这些洞察的最佳方式是跟踪有意义的指标和行业基准,例如在 2022 年软件交付状态报告中引用的那些指标和基准。一旦您捕获了这些数据,通过将测试结果上传到一个所有团队成员都可以访问的中心位置来与团队的其他成员共享这些数据也是同样重要的。

跟踪有意义的指标

您可以使用四个常用指标来衡量您的团队相对于行业基准的绩效:

  • 吞吐量
  • 接通率
  • 持续时间
  • 平均恢复时间(MTTR)

下面,我们将看看这些度量标准中的每一个度量标准是什么,以及如何改进您的开发过程来优化每一个度量标准。

吞吐量

吞吐量是每天运行工作流的平均次数。开发人员对共享存储库中代码库的更新会激活工作流。

如果开发团队的目标是增加吞吐量,更频繁地进行较小的提交可以减少错误的威胁,并使快速减少错误变得更容易。您应该根据需要经常进行这种测量。

接通率

对于产品的主要分支,你的成功率应该总是很高(90%或以上)。该比率是一段时间内通过运行的次数除以运行总数。

衡量当前工作流成功率的能力对于建立发展目标至关重要。例如,功能分支,即开发人员试验客户问题的新解决方案的地方,可能比主分支的成功率低得多,主分支应该总是反映产品的最新稳定版本。如果主分支上的成功率不够高,您应该通过增加测试来解决问题,以确保在提交到主分支之前捕获到 bug。

持续时间

持续时间是工作流运行所需的时间长度。十分钟的持续时间是标准工作流长度的一个很好的目标。在此期间,您可以更快地行动,并从通过 CI 渠道生成的信息中获益。

为了改善您的工作流持续时间,您可以利用 CI/CD 提供者提供的节省时间的特性,例如依赖缓存Docker 层缓存,以及并行运行您的测试。

平均恢复时间

平均恢复时间(MTTR)是测试失败后获得通过的构建信号所需的平均时间。MTTR 是要跟踪的最重要的度量,因为它度量了当您的开发团队遇到失败时,他们能够多快地恢复。快速响应错误的能力是保持竞争力和向最终用户交付价值的关键部分。

理想情况下,你的平均恢复时间应该少于 60 分钟。MTTR 与您的工作流持续时间直接相关(毕竟,您修复 bug 的速度不可能快于您测试 bug 的速度),因此,如果您的团队正在努力及时解决 bug,请尝试通过提高基于云的测试工作流的运行速度来“更快地失败”。进行更小的、更多的增量提交也可以使测试运行得更快,代码中的缺陷更容易解决。

将您的测试结果上传到云端

将所有的测试结果和度量上传到 CI/CD 平台,可以让您的团队——以及整个组织——更好地理解产品在每个阶段的状态。

许多持续集成平台支持将您的测试结果存储一段时间,允许您与利益相关者共享您的结果,并分析您的测试数据中的趋势。使用 CircleCI,您可以使用store_test_results步骤从您的测试运行中上传数据,然后可以在 web 应用程序的 Tests 选项卡中查看这些数据长达 30 天。对于测试结果的长期存储,您还可以使用store_artifacts步骤将测试结果存储为一个可共享的工件

有关存储持续集成测试结果的好处的更多信息,请参见以下视频:

https://www.youtube.com/embed/3sC_EzK0V-U

视频

上传您的测试结果不仅为您提供了一个所有团队成员分析和检查测试数据的集中位置,而且还使您能够更容易地通过数据可视化工具来访问这些数据。一些平台提供了内置的仪表板来分析您的测试运行。例如,CircleCI 的 Insights 仪表板提供了工作流持续时间和成功率等重要指标的详细数据,并且可以自动识别不稳定的测试以帮助提高测试套件的可靠性。许多平台还提供了与 Datadog、Honeycomb、Katalon、PractiTest 等工具的集成,这些工具可以提供高度可定制的报告,说明您的团队相对于重要基准的表现。

如何应用测试洞察力来改进您团队的开发过程

使用这些测试见解来改进您的开发过程。请记住,数据只有转化为实际的见解才有价值。

例如,好的测试可以识别高价值的用户旅程或模拟错误来测试系统的响应。通过从测试中获得的洞察力,您可以记录那些高价值的旅程,开发处理模拟错误的流程,并走在用户需求的前面。

为了应用从测试中获得的见解,您的团队应该养成一种习惯,审查测试数据,总结见解,提出建议,并与能够从中受益的团队分享结果。这看起来如何取决于你的团队结构。您可能会有一个专门的测试人员来做这件事,或者在较小的团队中,这个责任可能会在团队成员之间逐个 sprint 地轮换。

结论

在云中测试是有价值的——不仅仅是为了应用程序的成功,也是为了整个开发工作流程。如果您已经在使用基于云的 CI,那么您所使用的平台将已经内置了测试运行程序,这意味着只需很少的成本就可以开始使用并获得收益。

从测试中获取输出并做出有效改变的关键是获得详细的见解。 Insights 仪表板让 CircleCI 成为开始云测试的好地方。借助 Insights,CircleCI 可帮助您监控和优化工作流程,使您能够对开发和测试流程做出明智的决策。

要进一步了解如何使用 CircleCI 来获得对您的开发工作流程的有价值、可操作的见解,立即开始使用免费计划

所有开发人员都应该知道的 15 种测试方法

原文:https://circleci.com/blog/testing-methods-all-developers-should-know/

在软件中,有许多技术概念和定义。当学习新的话题时,甚至当在使用不同术语的公司之间切换时,这可能是令人难以置信的。

测试就是这样一个话题。随着现代技术公司通过采用持续集成实践,沿着他们的 DevOps 之旅走向成熟,测试和测试自动化越来越受到重视。不要迷失在所有不同方法的混乱中。这里是对最常见的软件测试类型的高级参考。

1.单元测试

单元测试是一种集中在审查单个“单元”或代码片段的测试方法。

单元测试的主要目标是确定逻辑完整性——一段代码做了它应该做的事情。

一般来说,人们会把单个的方法或函数作为一个单元来测试,根据代码的大小和复杂程度,也会测试类。它们被隔离测试,随后任何典型的依赖关系都被剔除或嘲笑

例如,如果您有一个从数据库中处理数据的函数。然而,由于这是一个单元测试,您不会使用真正的数据库:您会调用一个存根端点,它返回您通常期望从数据库中得到的数据。这样,唯一被测试的功能就是这段代码,或者单元。

大多数语言至少有一个推荐给自己的单元测试框架(如 Java → JUnit ,Python → PyUnitPyTest ,JavaScript → MochaJestKarma 等)。).

2.集成测试

集成测试是一种集中审查多个组件的测试方法。

集成测试的主要目标是确保组件或单元之间的关系完整性和数据流。

通常,人们会首先运行单元测试来测试单个单元的逻辑完整性。然后,他们将运行集成测试,以确保这些单元之间的交互行为符合预期。继续上面的例子,在这种情况下,集成测试将对真实的数据库运行相同的测试。对于真实的数据库,您需要考虑额外的场景和行为。

“集成测试”是一个宽泛的术语,包括任何涉及多个组件的测试。随后,可以使用各种各样的技术和框架,包括上面在单元测试中使用的相同的技术和框架,或者单独的、基于行为的框架(例子在下一节中列出)。

3.端到端测试(E2E,系统)

系统测试,或端到端(E2E)测试,集中于从端到端审查系统的行为。

端到端测试的主要目标是确保整个应用程序或系统作为一个单元按照我们期望的方式运行,而不考虑内部工作方式。

本质上,单元和集成测试通常是“白盒”(例如,内部是已知的),而 E2E 测试通常是“黑盒”(例如,我们只验证输入和输出的组合)。E2E 测试的一个例子可能是一个普通的用户故事,比如“获取用户的数据”输入可以是对特定路径的简单 GET 请求,然后我们验证返回的输出是我们所期望的。系统如何获取底层数据并不重要。

正如你所看到的,E2E 测试只能检查整体行为,所以这就是为什么单元测试和集成测试是必要的。可能是虽然输出是正确的,但内部获得结果的方式是不正确的,E2E 测试不会发现这一点。

对于 E2E 测试,你通常使用基于行为的框架。你可能会使用类似于黄瓜邮递员肥皂泡空手道柏树卡塔隆等框架。请注意,许多 API 测试框架用于 E2E 测试,因为 API 通常是您以编程方式与应用程序交互的方式。

4.验收测试

验收测试通常是开发周期的一个阶段。

验收测试的主要目标是验证给定的产品或功能是根据客户或内部利益相关者(如产品经理)制定的规范开发的。

在验收测试中,也可以有多个阶段,例如α测试或β测试。随着软件开发世界向敏捷过程发展,用户验收测试变得不那么严格,更具协作性。

重要的是要注意,虽然验收测试可以验证应用程序的行为是否符合用户的要求,但它不能验证系统的完整性。用户接受度测试的另一个警告是,一个人能想到的极限情况和场景是有限的——这就是为什么以前的自动化测试方法是重要的,因为每一个用例和场景都是被编码的。

5.白盒测试(结构、透明盒)

白盒(也称为结构或透明盒)测试描述了测试或方法,其中被测试软件的细节和内部工作是已知的。

因为您知道函数、方法、类、它们是如何工作的,以及它们是如何联系在一起的,所以您通常能够更好地检查代码的逻辑完整性。

例如,您可能知道某种语言处理某些操作的方式有些古怪。您可以为此编写特定的测试,否则您不知道在黑盒场景中编写这些测试。

单元测试和集成测试通常是白盒。

6.黑盒测试(功能、行为、封闭盒)

相反,黑盒(也称为功能、行为或封闭盒)测试描述了任何测试或方法,其中被测试软件的细节和内部工作是未知的。

因为你不知道任何细节,你不能真正地创建测试用例来针对特定的利基场景或者强调系统中特定的逻辑。

您唯一知道的是,对于一个请求或给定的输入,某种行为或输出是预期的。因此,黑盒测试主要测试系统的行为。端到端测试通常是黑盒。

7.灰盒测试

灰盒测试只是黑盒和白盒的混合组合。

灰盒测试继承了黑盒测试的简易性(如输入→输出),并针对白盒测试中与特定代码相关的系统。

灰盒测试存在的原因是因为黑盒测试和白盒测试本身会遗漏重要的功能。

  • 黑盒测试只测试对于给定的输入,你能得到一定的输出。它不测试内部组件的完整性——您可能完全是偶然得到正确的输出。
  • 白盒测试关注单个单元的完整性以及它们如何一起工作,但是它有时不足以发现系统范围或多组件的缺陷。

通过将这两种类型结合在一起,灰盒测试可以包含更复杂的场景,以真正验证应用程序在结构和逻辑上是否合理。

8.人工测试

不言自明——手动测试是指用户手动指定输入或与系统交互的测试。他们也可以手动评估结果。

这种测试方法通常很慢并且容易出错。随着敏捷原则的采用,许多软件行业已经转向自动化测试。

如今,用户可能会手动测试测试版产品,以检查接受度、边缘案例和利基场景。

9.静态测试

静态测试描述了不执行实际代码的任何方法或测试方法。

这实际上包括与其他人一起审查代码,手动验证函数、类等的逻辑和完整性。

就像手工测试一样,静态测试可能很慢并且容易出错,通常静态测试是作为捕捉非常明显的问题的第一道防线来完成的。

很多公司在一个工程师的工作合并到主分支之前,都会进行代码评审。这些代码审查是为了节省时间,抓住低挂的果实。

10.动态试验

动态测试描述了代码实际执行的任何方法或测试方法。

一般来说,前面提到的所有测试方法都是动态的,除了手工测试和有时的验收测试。您通常运行自动化脚本或使用框架来执行系统输入。

11.用户界面/视觉测试(浏览器测试)

UI 或浏览器测试描述了专门检查用户界面组件的完整性和行为的测试。

通常在使用网站时,某些动作会导致某些状态。UI 测试验证这些都是正确发生的。例如,你实现某些 CSS 的方式可能会在 Firefox 中崩溃,但不会在 Chrome 中崩溃。浏览器测试可以检查这一点。

有很多流行的浏览器测试框架如 SeleniumCypressTestCafeSauceLabsKatalon StudioBrowsersyncRobot 等。

12.烟雾测试

冒烟测试只是指合理验证系统工作的较小检查子集。

它只是选择并运行一组非详尽的测试来检查核心功能。

这方面的一个例子可能只是测试几个用户流,比如上面的“获取用户数据”。这并不详尽,但是由于您的应用程序的大部分都包括用户登录、发出请求和从某处获取数据,这一个或几个测试可以让您有理由相信您的系统正在运行。

通常,当用户期望更改不会对整体逻辑和功能产生任何重大影响时,就会运行冒烟测试。每次运行全套测试既昂贵又耗时,因此冒烟测试被用作一种廉价的安全措施,可以更频繁地运行。

13.回归测试

回归测试是一种测试方法,用于验证任何之前的功能特性是否突然中断(或回归)。

这通常包括运行所有单元、集成和系统测试的整体,以确保没有功能发生意外变化。众所周知,有时候软件有最奇怪的破解方式。

回归测试通常很耗时,而且可能非常昂贵,这就是为什么有时人们会运行冒烟测试,特别是如果最近的更改在逻辑上不会影响整个系统。

通常,当人们设置 CI/CD 时,他们会在几乎每次提交时运行冒烟测试,而回归套件可能会以设定的时间间隔或在大型功能上运行,以确保无问题的持续集成。

14.负载测试

负载测试是指测试应用程序对不断增长的需求的响应。

这包括测试突然涌入的请求或用户,它们可能会给系统带来意想不到的压力。负载测试通常作为安全测试的一部分,以确保应用程序及其系统不会被 DDOS 攻击。

负载测试还用来验证系统在任何给定时间可以处理的最大数据量。它对于帮助团队确定有效的 HA(高可用性)实施和扩展公式是不可或缺的。

15.渗透测试

渗透测试(或 pen 测试)是安全测试的一种形式,涉及验证应用程序安全性的健壮性。

应用程序可能受到危害的每一种方式(跨站点脚本、未经确认的输入、缓冲区溢出攻击等)。)来检查系统如何处理它。Pen 测试是确保公司不会成为严重违规行为的受害者的重要组成部分。

结论

正如你所看到的,在软件测试中使用了很多不同的术语,其中很多经常重叠或者互换使用(无论多么不正确)。

通过准确理解这些术语的含义,了解它们包含的内容,你将能够理解人们在谈论什么,并根据你的需要更深入地挖掘。你现在甚至可以纠正它们。要进一步阅读关于从整体上有效地思考测试的文章,请查看来自战壕博客的测试策略。

如果你对自动化测试感兴趣,今天就注册一个免费账户,看看 CircleCI 如何帮助你更快地发现问题。

快乐工程。

NestJS GraphQL 项目的持续集成| CircleCI

原文:https://circleci.com/blog/testing-nestjs-graphql/

本教程涵盖:

  1. 克隆示例项目
  2. 添加单元和集成测试
  3. 自动化端到端测试

NestJS 正迅速成为 NodeJS 项目事实上的框架。与旧的框架不同,NestJS 是用 TypeScript 构建的,这在 JavaScript 社区中已经很常见了。采用 TypeScript 的团队似乎更喜欢 NestJS 这样的框架。

NestJS 支持在 REST 和 GraphQL 中构建 API。作为 REST 的替代方案, GraphQL 允许您以完整而准确的方式描述数据,为更改客户端命令提供足够的灵活性。它为响应客户端查询返回数据提供了一个运行时。GraphQL 提供了许多有用的开发工具,有助于简化 API 的维护。

本教程的目标是展示如何向 NestJS GraphQL 项目添加单元和集成测试,并使用 CircleCI 自动化测试过程。

先决条件

要完成本教程,您需要准备一些东西:

我们的教程是平台无关的,但是使用 CircleCI 作为例子。如果你没有 CircleCI 账号,请在 注册一个免费的

克隆一个示例项目

本教程使用现有的 NestJS 应用程序。这是一个小的发票应用程序,演示了如何使用 NestJS 构建一个 GraphQL API。

首先,克隆[ 项目。它已经包含了一些我将解释的测试。然后,我们将使用 CircleCI 自动化运行测试的过程。该项目有两个模块:客户和发票。每个模块包含一个服务和一个解析器文件。示例测试将集中在客户模块上。

单元测试客户服务

客服有三种方式:createfindAllfindOne。我们将为每个方法添加一个测试。customer.service.spec.ts文件已经包含了一个测试样板,我们可以用它来测试客户服务。在添加每个测试之前,您需要配置测试模块,为它提供所有必需的依赖项。测试配置被添加到beforeEach模块中。以下是客户服务的配置:

// src/customer/customer.service.spec.ts

import { Test, TestingModule } from "@nestjs/testing";
import { getRepositoryToken } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { CustomerModel } from "./customer.model";
import { CustomerService } from "./customer.service";
type MockType<T> = {
  [P in keyof T]?: jest.Mock<{}>;
};
describe("CustomerService", () => {
  let service: CustomerService;
  const customerRepositoryMock: MockType<Repository<CustomerModel>> = {
    save: jest.fn(),
    findOne: jest.fn(),
    find: jest.fn(),
  };
  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        CustomerService,
        {
          provide: getRepositoryToken(CustomerModel),
          useValue: customerRepositoryMock,
        },
      ],
    }).compile();
    service = module.get<CustomerService>(CustomerService);
  });
}); 

有两个提供者:CustomerServiceCustomerRepository。因为这是一个单元测试,我们对CustomerRepository使用了一个模拟值。以下是对每种方法的完整测试:

// src/customer/customer.service.spec.ts

import { Test, TestingModule } from "@nestjs/testing";
import { getRepositoryToken } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { CustomerModel } from "./customer.model";
import { CustomerService } from "./customer.service";
type MockType<T> = {
  [P in keyof T]?: jest.Mock<{}>;
};
describe("CustomerService", () => {
  let service: CustomerService;
  const customerRepositoryMock: MockType<Repository<CustomerModel>> = {
    save: jest.fn(),
    findOne: jest.fn(),
    find: jest.fn(),
  };
  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        CustomerService,
        {
          provide: getRepositoryToken(CustomerModel),
          useValue: customerRepositoryMock,
        },
      ],
    }).compile();
    service = module.get<CustomerService>(CustomerService);
  });
  it("should be defined", () => {
    expect(service).toBeDefined();
  });
  describe("create", () => {
    it("should create a new customer", async () => {
      const customerDTO = {
        name: "John Doe",
        email: "john.doe@email.com",
        phone: "3134045867",
        address: "123 Road, Springfied, MO",
      };
      customerRepositoryMock.save.mockReturnValue(customerDTO);
      const newCustomer = await service.create(customerDTO);
      expect(newCustomer).toMatchObject(customerDTO);
      expect(customerRepositoryMock.save).toHaveBeenCalledWith(customerDTO);
    });
  });
  describe("findAll", () => {
    it("should find all customers", async () => {
      const customers = [
        {
          id: "1234",
          name: "John Doe",
          email: "john.doe@email.com",
          phone: "3134045867",
          address: "123 Road, Springfied, MO",
        },

        {
          id: "5678",
          name: "John Ford",
          email: "john.ford@email.com",
          phone: "3134045867",
          address: "456 Road, Springfied, MO",
        },
      ];
      customerRepositoryMock.find.mockReturnValue(customers);
      const foundCustomers = await service.findAll();
      expect(foundCustomers).toContainEqual({
        id: "1234",
        name: "John Doe",
        email: "john.doe@email.com",
        phone: "3134045867",
        address: "123 Road, Springfied, MO",
      });
      expect(customerRepositoryMock.find).toHaveBeenCalled();
    });
  });
  describe("findOne", () => {
    it("should find a customer", async () => {
      const customer = {
        id: "1234",
        name: "John Doe",
        email: "john.doe@email.com",
        phone: "3134045867",
        address: "123 Road, Springfied, MO",
      };
      customerRepositoryMock.findOne.mockReturnValue(customer);
      const foundCustomer = await service.findOne(customer.id);
      expect(foundCustomer).toMatchObject(customer);
      expect(customerRepositoryMock.findOne).toHaveBeenCalledWith(customer.id);
    });
  });
}); 

测试客户解析程序

还有测试解析器的样板代码:customer.resolver.spec.ts。解析器的测试类似于维修测试。我们将为CustomerServiceInvoiceService依赖项提供模拟值。

// src/customer/customer.resolver.spec.ts

import { Test, TestingModule } from "@nestjs/testing";
import { InvoiceService } from "../invoice/invoice.service";
import { CustomerDTO } from "./customer.dto";
import { CustomerResolver } from "./customer.resolver";
import { CustomerService } from "./customer.service";
const invoice = {
  id: "1234",
  invoiceNo: "INV-01",
  description: "GSVBS Website Project",
  customer: {},
  paymentStatus: "Paid",
  currency: "NGN",
  taxRate: 5,
  taxAmount: 8000,
  subTotal: 160000,
  total: 168000,
  amountPaid: "0",
  outstandingBalance: 168000,
  issueDate: "2017-06-06",
  dueDate: "2017-06-20",
  note: "Thank you for your patronage.",
  createdAt: "2017-06-06 11:11:07",
  updatedAt: "2017-06-06 11:11:07",
};
describe("CustomerResolver", () => {
  let resolver: CustomerResolver;
  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        CustomerResolver,
        {
          provide: CustomerService,
          useFactory: () => ({
            create: jest.fn((customer: CustomerDTO) => ({
              id: "1234",
              ...customer,
            })),
            findAll: jest.fn(() => [
              {
                id: "1234",
                name: "John Doe",
                email: "john.doe@email.com",
                phone: "3134045867",
                address: "123 Road, Springfied, MO",
              },

              {
                id: "5678",
                name: "John Ford",
                email: "john.ford@email.com",
                phone: "3134045867",
                address: "456 Road, Springfied, MO",
              },
            ]),
            findOne: jest.fn((id: string) => ({
              id: id,
              name: "John Doe",
              email: "john.doe@email.com",
              phone: "3134045867",
              address: "123 Road, Springfied, MO",
            })),
          }),
        },
        {
          provide: InvoiceService,
          useFactory: () => ({
            findByCustomer: jest.fn((id: string) => invoice),
          }),
        },
      ],
    }).compile();
    resolver = module.get<CustomerResolver>(CustomerResolver);
  });
  it("should be defined", () => {
    expect(resolver).toBeDefined();
  });
  describe("customer", () => {
    it("should find and return a customer", async () => {
      const customer = await resolver.customer("1234");
      expect(customer).toEqual({
        id: "1234",
        name: "John Doe",
        email: "john.doe@email.com",
        phone: "3134045867",
        address: "123 Road, Springfied, MO",
      });
    });
  });
  describe("customers", () => {
    it("should find and return a list of customers", async () => {
      const customers = await resolver.customers();
      expect(customers).toContainEqual({
        id: "1234",
        name: "John Doe",
        email: "john.doe@email.com",
        phone: "3134045867",
        address: "123 Road, Springfied, MO",
      });
    });
  });
  describe("invoices", () => {
    it("should find and return a customer invoice", async () => {
      const customer = await resolver.invoices({ id: "1234" });
      expect(customer).toEqual(invoice);
    });
  });
  describe("createCustomer", () => {
    it("should find and return a customer invoice", async () => {
      const customer = await resolver.createCustomer(
        "John Doe",
        "john.doe@email.com",
        "3134045867",
        "123 Road, Springfied, MO"
      );
      expect(customer).toEqual({
        id: "1234",
        name: "John Doe",
        email: "john.doe@email.com",
        phone: "3134045867",
        address: "123 Road, Springfied, MO",
      });
    });
  });
}); 

自动化端到端测试

端到端测试不使用模拟值,因为目标是测试每个组件(模型、解析器和服务),并确保它们一起正常工作。

测试数据库的 TypeORM 配置与主数据库的配置略有不同。我创建了一个config并添加了一个config.database.ts来基于环境(测试或开发)导出 TypeORM 配置。

import dotenv from "dotenv";
dotenv.config();
const database = {
  development: {
    type: "postgres",
    host: "localhost",
    port: 5432,
    username: "godwinekuma",
    password: "",
    database: "invoiceapp",
    entities: ["dist/**/*.model.js"],
    synchronize: false,
    uuidExtension: "pgcrypto",
  },
  test: {
    type: "postgres",
    host: "localhost",
    port: 5432,
    username: process.env.POSTGRES_USER,
    password: "",
    database: process.env.POSTGRES_DB,
    entities: ["src/**/*.model.ts"],
    synchronize: true,
    dropSchema: true,
    migrationsRun: false,
    migrations: ["src/database/migrations/*.ts"],
    cli: {
      migrationsDir: "src/database/migrations",
    },
    keepConnectionAlive: true,
    uuidExtension: "pgcrypto",
  },
};
const DatabaseConfig = () => ({
  ...database[process.env.NODE_ENV],
});
export = DatabaseConfig; 

注意dropSchema被设置为true;这将在测试后删除您的数据,以便将它们与之前的测试和运行隔离开来。在每次测试运行之后,删除连接中注册的每个实体的所有数据是一个很好的做法。在测试文件夹中创建一个connection.ts文件,并添加以下代码:

// /test/connection.ts
import { createConnection, getConnection } from "typeorm";

const connection = {
  async create() {
    await createConnection();
  },

  async close() {
    await getConnection().close();
  },

  async clear() {
    const connection = getConnection();
    const entities = connection.entityMetadatas;

    const entityDeletionPromises = entities.map((entity) => async () => {
      const repository = connection.getRepository(entity.name);
      await repository.query(`DELETE FROM ${entity.tableName}`);
    });
    await Promise.all(entityDeletionPromises);
  },
};
export default connection; 

connection导出两个方法closeclear。为了关闭到数据库的连接,在所有的测试都被执行之后,调用close。在每次测试之前,clear被调用以在下一次测试运行之前删除所有数据。

添加测试:

// /test/customer.e2e-spec.ts

import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import request = require("supertest");
import { AppModule } from "../src/app.module";
import connection from "./connection";
import { getConnection } from "typeorm";
import { CustomerModel } from "../src/customer/customer.model";

describe("CustomerResolver (e2e)", () => {
  let app: INestApplication;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await connection.clear();
    await app.init();
  });

  afterAll(async () => {
    await connection.close();
    await app.close();
  });

  const gql = "/graphql";

  describe("createCustomer", () => {
    it("should create a new customer", () => {
      return request(app.getHttpServer())
        .post(gql)
        .send({
          query:
            'mutation {createCustomer(name: "John Doe", email: "john.doe@example.com", phone: "145677312965", address: "123 Road, Springfied, MO") {address name phone email}}',
        })
        .expect(200)
        .expect((res) => {
          expect(res.body.data.createCustomer).toEqual({
            name: "John Doe",
            email: "john.doe@example.com",
            phone: "145677312965",
            address: "123 Road, Springfied, MO",
          });
        });
    });

    it("should get a single customer by id", () => {
      let customer;
      return request(app.getHttpServer())
        .post(gql)
        .send({
          query:
            'mutation {createCustomer(name: "John Doe", email: "john.doe@example.com", phone: "145677312965", address: "123 Road, Springfied, MO") {address name id phone email}}',
        })
        .expect(200)
        .expect((res) => {
          customer = res.body.data.createCustomer;
        })
        .then(() =>
          request(app.getHttpServer())
            .post(gql)
            .send({
              query: `{customer(id: "${customer.id}") {address name id phone email}}`,
            })
            .expect(200)
            .expect((res) => {
              expect(res.body.data.customer).toEqual({
                id: customer.id,
                address: customer.address,
                name: customer.name,
                phone: customer.phone,
                email: customer.email,
              });
            })
        );
    });

    it("should retrieve all customer data", async () => {
      const data = [
        {
          name: "John Doe",
          email: "john.doe@example.com",
          phone: "145677312965",
          address: "123 Road, Springfied, MO",
        },
        {
          name: "Jane Doe",
          email: "jane.doe@example.com",
          phone: "145677312900",
          address: "456 Road, Springfied, MO",
        },
      ];
      const connection = await getConnection();
      data.map(async (item) => {
        await connection
          .createQueryBuilder()
          .insert()
          .into(CustomerModel)
          .values(item)
          .execute();
      });

      request(app.getHttpServer())
        .post(gql)
        .send({
          query: `{customers() {address name phone email}}`,
        })
        .expect(200)
        .expect((res) => {
          expect(res.body.data.customers.length).toEqual(data.length);
          expect(res.body.data.customers[0]).toEqual(data[0]);
        });
    });
  });
}); 

将 CircleCI 配置添加到项目

要开始自动化您的测试,您首先需要构建一个 CircleCI 将运行的持续集成管道。管道将:

  • 检查您的存储库
  • 恢复现有缓存
  • 安装项目依赖项
  • 保存缓存
  • 运行单元测试
  • 运行端到端测试

首先,在项目的根目录下创建一个名为.circleci的文件夹。在其中创建一个名为config.yml的文件。现在将这段代码粘贴到里面:

version: 2.1
workflows:
  build:
    jobs:
      - test
jobs:
  test:
    docker:
      - image: cimg/node:10.23
      - image: cimg/postgres:10.17
        environment:
          POSTGRES_USER: circleci
          POSTGRES_DB: circleci
          POSTGRES_HOST_AUTH_METHOD: "trust"
    environment:
      NODE_ENV: test
      POSTGRES_USER: circleci
      POSTGRES_DB: circleci
      POSTGRES_HOST_AUTH_METHOD: "trust"
    steps:
      - run:
          name: Waiting for Postgres to be ready
          command: dockerize -wait tcp://localhost:5432 -timeout 1m
      - checkout
      - restore_cache:
          key: v1-deps-{{ checksum "package.json" }}
      - run: yarn install
      - save_cache:
          paths:
            - node_modules
          key: v1-deps-{{ checksum "package.json" }}
      - run: yarn test
      - run: yarn test:e2e 

这个脚本指定 Docker 作为执行环境。它将导入两个 CircleCI Docker 映像,一个用于 Nodejs,另一个用于 Postgres。运行端到端测试需要 Postgres。

提交您的代码并将其推送到您的 repo

接下来,将项目设置为在推送代码更改时在 CircleCI 上运行。

将项目连接到 CircleCI

转到 CircleCI 项目仪表板。如果你用 GitHub 或 Bitbucket 注册了 CircleCI,你所有的库都会在那里列出。点击教程项目的设置项目按钮。

CircleCI project dashboard

出现提示时,点击 Let's go 。您的项目将连接到 Circle CI,并且您的第一个构建将开始运行。

Select CircleCI confiq file

您的构建应该会成功运行。

Project Pipeline overview

单击 build 链接查看管道如何运行,并确认测试成功通过。

Project pipeline detail

结论

在本教程中,我们使用 customer 模块向您展示了如何在 NestJS GraphQL API 中执行单元测试和端到端测试。这里使用的相同方法可以应用于示例应用程序中的其他模块,或者任何 NestJS 应用程序。与您的团队合作,尝试对您的 NestJS 和 GraphQL 应用程序进行自动化测试。

如果你有兴趣了解 app 是如何构建的,可以参考这篇帖子:如何用 NestJS 构建 GraphQL API。

为无服务器应用程序构建自动化单元测试管道

原文:https://circleci.com/blog/testing-pipeline-for-serverless-apps/

本教程涵盖:

  1. 使用 Jest 框架为无服务器应用程序设置单元测试
  2. 在本地运行测试
  3. 构建管道来运行测试和部署应用程序

无服务器框架是用 Node.js 编写的开源框架,简化了 AWS Lambda 功能的开发和部署。它让您不必担心如何将应用程序打包和部署到云中,因此您可以专注于您的应用程序逻辑。

无服务器应用程序是按设计分布的,所以良好的代码覆盖率是至关重要的,并且应该包括单元测试。单元测试用例使得在开发阶段早期检测 bug 和防止回归问题变得更加容易。它们改进了应用程序的整体设计和质量,并允许您满怀信心地重构代码。

因为您的应用程序可能依赖于许多其他 AWS 服务,所以很难在本地复制云环境。单元测试允许你独立地测试你的应用程序逻辑。使用基于事件的架构,您可以模拟事件来添加测试用例,并根据接收到的事件断言预期的行为。

在本教程中,您将学习如何使用 Jest 测试框架为无服务器应用程序构建自动化单元测试管道。它建立在从部署无服务器应用博文中获得的经验之上。

先决条件

对于本教程,您需要设置以下项目:

我们的教程是平台无关的,但是使用 CircleCI 作为例子。如果你没有 CircleCI 账号,请在 注册一个免费的

创建新的无服务器应用程序

为您的项目创建新目录。运行:

mkdir circleci-serverless-unit-testing
cd circleci-serverless-unit-testing 

转到circleci-serverless-unit-testing目录。从这里,您将使用aws-nodejs模板创建一个新的无服务器应用程序。(确保您的系统上设置了无服务器框架。)运行以下命令:

serverless create --template aws-nodejs 

执行这个命令会创建一个带有基本 Lambda 处理程序的handler.js文件和一个带有应用程序配置的serverless.yml文件。

创建 Node.js Lambda 函数

接下来,您需要使用 Node.js 定义一个 AWS Lambda 函数。该函数生成一个虚拟 CSV 文件,将其上传到 AWS S3,并向 DynamoDB 表添加一个条目。在本教程的后面,您将使用 Jest 测试框架为 Lambda 处理程序编写单元测试用例。

在无服务器应用程序的根目录下创建一个新的package.json文件。您将需要它来添加 Lambda 函数所需的依赖项。在package.json文件中,添加以下代码片段:

{
  "scripts": {

  },
  "dependencies": {
    "csv-stringify": "^6.0.5",
    "fs": "0.0.1-security",
    "uuid": "^8.3.2"
  },
  "devDependencies": {

  }
} 

接下来,用下面的代码片段更新现有的handler.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

module.exports.uploadCsvToS3Handler = async (event) => {
  try {
    const uploadedObjectKey = await generateDataAndUploadToS3()
    const jobId = 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,
      body: JSON.stringify(
        {
          "status": "success",
          "jobId": jobId,
          "objectKey": uploadedObjectKey
        },
        null,
        2
      ),
    };
  } catch (error) {
    throw Error(`Error in backend: ${error}`)
  }
};

const generateDataAndUploadToS3 = async () => {
  var filePath = '/tmp/test_user_data.csv'
  const objectKey = `${uuidv4()}.csv`;
  await writeCsvToFileAndUpload(filePath, objectKey)
  return objectKey
}

const uploadFile = async (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;
};

async function writeCsvToFileAndUpload(filePath, objectKey) {
  var data = getCsvData();
  var output = stringify(data);

  fs.writeFile(filePath, output, function (err) {
    if (err) {
      console.log('file write error', err)
    }
    uploadFile(filePath, objectKey);
  });
}

function getCsvData() {
  return [
    ['1', '2', '3', '4'],
    ['a', 'b', 'c', 'd']
  ];
} 

以下是该片段包含的内容:

  • uploadCsvToS3Handler是由 AWS Lambda 调用的处理函数。它调用generateDataAndUploadToS3方法来生成一个虚拟 CSV 文件。
  • 然后使用uploadFile方法将虚拟 CSV 文件上传到 AWS S3。
  • 然后,generateDataAndUploadToS3方法返回生成的文件键,该键在处理程序中用于在 AWS DynamoDB 表中插入一个新项。
  • 处理程序返回一个带有uploadedObjectKey的 HTTP JSON 响应。

注意: 处理程序从环境变量中接收 AWS S3 桶名和 AWS DynamoDB 表名。在所有 AWS 区域的所有 AWS 帐户中,AWS S3 时段名称应该是唯一的。

更新无服务器配置

您之前定义的 Lambda 处理程序使用环境变量,并与 AWS S3 和 AWS DynamoDB 交互。您可以通过对serverless.yml文件进行以下更改来进行设置:

  • 通过创建新的 AWS S3 存储桶和新的 AWS DynamoDB 表来调配新的云资源。
  • 更新 Lambda 函数的处理程序名,并将桶名和表名作为环境变量传递。
  • 更新 IAM 角色,将 Lambda 函数权限授予 S3 存储桶和 DynamoDB 表。

注意 : 确保将 AWS S3 存储桶名称更新为唯一的名称。存储桶不能共享相同的名称,如果 AWS 上已经存在同名的存储桶,您的部署可能会失败。

要应用这些更改,用以下代码更新serverless.yml:

service: circleci-serverless-unit-testing
frameworkVersion: '3'

provider:
  name: aws
  runtime: nodejs12.x

  # you can overwrite defaults here
  region: us-west-2

  # you can add statements to the Lambda function's IAM Role here
  iam:
    role:
      statements:
        - Effect: "Allow"
          Action:
            - "s3:ListBucket"
          Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ]  }
        - Effect: "Allow"
          Action:
            - "s3:PutObject"
          Resource:
            Fn::Join:
              - ""
              - - "arn:aws:s3:::"
                - "Ref" : "ServerlessDeploymentBucket"
                - "/*"
        - Effect: "Allow"
          Action:
            - "s3:ListBucket"
          Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ]  }
        - Effect: "Allow"
          Action:
            - "s3:PutObject"
          Resource: { "Fn::Join": ["", ["arn:aws:s3:::circle-ci-unit-testing-bucket", "/*" ] ] }
        - Effect: "Allow"
          Action:
          - dynamodb:Query
          - dynamodb:Scan
          - dynamodb:GetItem
          - dynamodb:PutItem
          - dynamodb:UpdateItem
          - dynamodb:DeleteItem
          Resource: "arn:aws:dynamodb:us-west-2:927728891088:table/circle-ci-unit-testing-table"

functions:
  UploadCsvToS3:
    handler: handler.uploadCsvToS3Handler
    environment:
      TABLE_NAME: circle-ci-unit-testing-table
      BUCKET_NAME: circle-ci-unit-testing-bucket

# you can add CloudFormation resource templates here
resources:
 Resources:
    S3Bucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: circle-ci-unit-testing-bucket
    DynamoDB:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: circle-ci-unit-testing-table
        AttributeDefinitions:
          - AttributeName: jobId
            AttributeType: S
        KeySchema:
          - AttributeName: jobId
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1

 Outputs:
    S3Bucket:
      Description: "S3 Bucket Name"
      Value: "circle-ci-unit-testing-bucket"
    DynamoDB:
      Description: "DynamoDB Table Name"
      Value: "circle-ci-unit-testing-table" 

更新后的配置将负责提供云资源并设置所需的 IAM 权限。

设置单元测试

对于本教程,您将使用 Jest 测试框架为 AWS Lambda 函数编写单元测试用例。Jest 适用于大多数 JavaScript 项目,并为测试提供了很好的 API。您可以在不添加任何附加配置的情况下生成代码覆盖率报告。它不仅支持模仿,还为模块提供了手动模仿实现。这些特性使得 Jest 成为测试无服务器应用程序的绝佳选择。

注意: 如果你对使用 Mocha/Chai JS 进行单元测试感兴趣,这篇教程将向你展示如何设置。

为单元测试添加库依赖项

通过运行以下命令添加jest NPM 依赖项:

npm install --save-dev jest 

为单元测试创建目录结构

作为最佳实践,您应该为您的测试保持与应用程序文件布局相同的目录结构。例如,如果您在您的无服务器应用程序中定义了controllersviewsmodels,您可以在您的__tests__目录中为测试用例添加类似的文件夹。以下是无服务器应用程序的布局示例:

.
├── __tests__
│   └── controllers
│   |   └── myAppController.test.js
│   └── models
│   |   └── user.test.js
│   └── views
        └── userView.test.js
├── controllers
│   └── myAppController.js
├── handler.js
├── models
│   └── user.js
├── views
│   └── userView.js
├── node_modules
├── package-lock.json
├── package.json
└── serverless.yml 

为无服务器函数编写单元测试

要为无服务器应用程序添加单元测试用例,在无服务器应用程序的根目录下创建一个__tests__目录:

mkdir __tests__ 

接下来,添加一个handler.test.js文件。该文件将包含对handler.js中定义的处理函数的测试。

  1. __tests__目录中创建一个空的handler.test.js
  2. handler.test.js中为aws-sdk添加一个手动模拟实现。Jest 允许你为模块定义部分模仿,这样你就不需要模仿模块的每一个功能。
  3. 为 DynamoDB 的putItem和 S3 木桶的putObject函数创建模拟实现。

将以下代码片段添加到文件中:

jest.mock("aws-sdk", () => {
  return {
    config: {
      update() {
        return {};
      },
    },
    DynamoDB: jest.fn(() => {
      return {
        putItem: jest.fn().mockImplementation(() => ({ promise: jest.fn().mockReturnValue(Promise.resolve(true)) })),
      };
    }),
    S3: jest.fn(() => {
      return {
        upload: jest.fn().mockImplementation(() => ({ promise: jest.fn().mockReturnValue(Promise.resolve(true)) })),
      };
    }),
  };
}); 

您已经定义了模拟实现来成功解析Promise。接下来,为 Lambda 处理程序添加一个单元测试。用测试 ID 调用 Lambda 处理程序,并断言响应体中的status被设置为success

const handler = require('../handler');

describe('uploadCsvToS3Handler', () => {
  beforeEach(() => {
    jest.restoreAllMocks();
  });

  test('test uploadFile', async () => {
    const response = await handler.uploadCsvToS3Handler({
      jobId: 'test-job-id',
    });

    let body = JSON.parse(response.body);

    expect(body.status).toBe('success');
    expect(body.jobId).toBe('test-job-id');
  });
}); 

您可以从终端运行npm run test命令来确保测试通过。

接下来,为失败场景添加另一个测试用例。因为您需要为失败场景提供不同的模拟实现,所以在__tests__目录中添加一个名为handler-fail-putItem.test.js的新文件。将以下代码片段添加到其中:

jest.mock("aws-sdk", () => {
  return {
    config: {
      update() {
        return {};
      },
    },
    DynamoDB: jest.fn(() => {
      return {
        putItem: jest.fn().mockImplementation(() => {
          throw new Error();
        }),
      };
    }),
    S3: jest.fn(() => {
      return {
        upload: jest.fn().mockImplementation(() => ({ promise: jest.fn().mockReturnValue(Promise.resolve(false)) })),
      };
    }),
  };
});

const handler = require('../handler');

describe('uploadCsvToS3Handler', () => {
  beforeEach(() => {
    jest.restoreAllMocks();
  });

  test('test uploadFile', async () => {
    expect(handler.uploadCsvToS3Handler({
      jobId: 'test-job-id',
    })).rejects.toThrow(new Error('Error in backend'))
  });
}); 

注意,当在 AWS SDK 的 DynamoDB 模块上调用putItem方法时,我们抛出了一个异常。因为putItem方法抛出一个异常,测试处理程序也会抛出一个异常。通过将错误字符串与预期错误进行匹配来断言异常。

应用程序结构应该如下所示:

.
├── README.md
├── __tests__
│   ├── handler-fail-putItem.test.js
│   └── handler.test.js
├── handler.js
├── jest.config.js
├── package-lock.json
├── package.json
└── serverless.yml 

在本地运行测试

现在您已经为您的应用程序添加了单元测试,接下来看看如何执行它们。首先,将scripts添加到package.json文件中。

"scripts": {
  "test": "jest",
  "coverage": "jest --coverage"
} 

添加这些脚本后,您可以使用npm run test命令运行所有测试用例,并使用npm run coverage命令生成代码覆盖报告。在本地运行这些命令,以确保所有测试用例都通过,并检查代码覆盖率报告是否按预期生成。

一旦您运行了npm run test命令,它将执行所有的测试套件并显示结果,如图所示。

Jest test results

使用 CircleCI 自动化无服务器应用程序部署

既然您已经能够在本地部署和测试,那么自动化工作流,这样就可以在每次部署时生成代码覆盖率报告。

添加配置脚本

转到包含 CI 管道配置文件的项目根目录。添加一个.circleci/config.yaml脚本。对于本教程,您将使用 OIDC 令牌向 AWS 进行身份验证,而不是使用静态访问密钥和密码。OIDC 令牌是为每个作业新颁发的短期令牌。要实现此添加:

version: 2.1

orbs:
  aws-cli: circleci/aws-cli@3.0.0
  serverless-framework: circleci/serverless-framework@2.0
commands:
  aws-oidc-setup:
    description: Setup AWS auth using OIDC token
    parameters:
      aws-role-arn:
        type: string
    steps:
      - run:
          name: Get short-term credentials
          command: |
            STS=($(aws sts assume-role-with-web-identity --role-arn << parameters.aws-role-arn >> --role-session-name "circleciunittesting" --web-identity-token "${CIRCLE_OIDC_TOKEN}" --duration-seconds 900 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text))
            echo "export AWS_ACCESS_KEY_ID=${STS[0]}" >> $BASH_ENV
            echo "export AWS_SECRET_ACCESS_KEY=${STS[1]}" >> $BASH_ENV
            echo "export AWS_SESSION_TOKEN=${STS[2]}" >> $BASH_ENV
      - run:
          name: Verify AWS credentials
          command: aws sts get-caller-identity

jobs:
  build:
    executor: serverless-framework/default
    steps:
      - checkout
      - aws-cli/install
      - aws-oidc-setup:
          aws-role-arn: "${CIRCLE_CI_WEB_IDENTITY_ROLE}"
      - run:
          name: Test AWS connection
          command: aws s3 ls
      - run:
          name: Install Serverless CLI and dependencies
          command: |
            sudo npm i -g serverless
            npm install

      - run:
          name: Run tests with code coverage
          command: npm run coverage

      - run:
          name: Deploy application
          command: sls deploy

workflows:
  build:
    jobs:
      - build:
          context:
            - "aws-context" 

您需要在 CircleCI 组织设置中定义一个上下文,否则构建将会失败。

这个脚本使用 CircleCI 的 aws-cli orb 来安装 AWS CLI。它使用无服务器框架 orb 将应用程序部署到使用无服务器框架的 AWS。

这个管道运行所有的单元测试用例,并在将应用程序部署到 AWS 之前生成代码覆盖报告。提交更改并将其推送到 GitHub 存储库。

为应用程序创建 CircleCI 项目

接下来,使用 CircleCI 控制台将存储库设置为 CircleCI 项目。在 Circle CI 控制台上,点击项目,搜索 GitHub repo 名称。为您的项目点击设置项目按钮。

Circle CI set up project

代码库已经包含一个config.yaml文件,CircleCI 将检测到该文件。点击走吧继续。

Circle CI project configuration

点击设置项目按钮触发管道。这次管道将失败,因为您尚未设置环境变量。接下来您可以设置它们。

设置环境变量

在项目页面,点击项目设置,进入环境变量选项卡。在下一个表单中,点击添加环境变量。添加这些环境变量:

  • CIRCLE_CI_WEB_IDENTITY_ROLE设置为从 AWS 控制台的 IAM 角色页面获得的角色 ARB。确保您按照指南中描述的步骤设置 AWS 以使用 OpenID 连接令牌并创建 IAM 角色。

一旦您添加了环境变量,它应该在仪表板上显示键值。

Circle CI set up environment variables

创建 CircleCI 上下文

因为 OpenID 连接令牌仅可用于至少使用一个上下文的作业,所以请确保每个需要 OIDC 令牌的作业都使用一个上下文(该上下文可能没有环境变量)。

转到组织设置

Organization Settings

创建在 CircleCI 工作流中使用的上下文。称之为aws-context

Create context

既然已经创建了上下文并配置了环境变量,那么再次触发管道。这一次构建应该会成功。

Circle CI pipeline builds successfully

结论

在本教程中,您了解了如何为无服务器应用程序构建自动化单元测试管道。无服务器框架简化了将应用程序部署到云的过程。为了放心地部署,拥有包括单元测试在内广泛的代码覆盖率是至关重要的。使用 Jest 框架,您可以轻松地为您的应用程序添加表达式单元测试,包括模仿、监视和存根。同样在本教程中,您使用了 OIDC 短期令牌来部署 AWS,而不是使用静态访问密钥和密码。你可以在 GitHub 上查看本教程中使用的完整的源代码


Vivek Kumar Maskara 是 JP 摩根的一名软件工程师。他喜欢写代码,开发应用程序,创建网站,并写关于他的经历的技术博客。他的简介和联系方式可以在maskaravivek.com找到。

阅读更多 Vivek Maskara 的帖子

CircleCI 工程师使用的 10 大开发工具| CircleCI

原文:https://circleci.com/blog/the-best-dev-tools-used-by-circleci-engineers/

如果说我看得更远,那是因为我站在巨人的肩膀上。

— 艾萨克·牛顿

软件工程师总是在优化。我们修补、尝试新工具,并转变我们的开发过程。每台电脑都是有趣实验和插件的真正游乐场。

出于好奇,我与 CircleCI 的一些工程师谈论了他们用来提高生产率的工具。结果是软件宝石的宝库。很难将这个列表缩减到十个,但是这十个工具是最划算的。我希望这篇文章能帮助 CircleCI 内外的其他工程师,让他们的日常工作更快捷、更有趣。

特别感谢亚历克斯·恩格尔伯格、德文·布朗、格伦·梅勒和杰奎琳·加西亚与我会面,讨论他们的设置。另外感谢 CircleCI 的其他人在我们(许多)Slack 频道中提供的各种工具建议。

通用生产力工具

浏览器的一个选项卡

OneTab 是 Chrome 和 Firefox 浏览器的一个便捷插件,可以聚集和存储你的标签。更具体地说,它存储了你在其中打开的链接,允许你关闭标签页并释放内存。

</blog/media/2020-05-26-OneTab-Open.mp4>

Opening a OneTab grouping

选项卡很容易分组和命名:您可以保存整个选项卡组,也可以恢复选项卡组。我有一组客户演示所需的页面,可以立即打开。完成后,我可以为下一个演示保存这些相同的选项卡。

在我看来,这就是书签应该有的样子。保存和打开新书签是一件麻烦的事,而在 OneTab 中,它们是简单的一键式体验。试用 OneTab,看看它能为您的浏览器选项卡管理做些什么!

动量仪表板

Momentum 是 Chrome、Firefox 和 Edge 的浏览器扩展,帮助用户找到焦点。

Momentum

它用漂亮的背景图片、激励性的引语和用户可配置的其他项目取代了浏览器上的新标签。有大量方便的小工具——倒计时,世界时钟,当地天气,你可以在浏览器中快速记笔记…

链接下拉菜单可以完全取代书签栏,因为它速度更快,可读性更强。甚至还有键盘快捷键!

</blog/media/2020-05-26-Momentum.mp4>

Opening a link in Momentum

在付费计划中,你可以设置与 Todoist、Asana、Trello、Bitbucket、GitHub 等应用程序的集成。结合其“自动对焦”功能,将一个项目集中在屏幕上,您可以专注于手头的任务,保持一天的清晰。

动力已经迅速成为我们中一些人选择的仪表板。

谷歌日历顺时针方向

顺时针是 GSuite 和 Chrome 的日历助手,智能管理你的日历。

Clockwise

它可以重新安排你的会议(称为“自动驾驶”),以创造最大的专注时间,或不间断的高质量工作时间。

此外,还有一些方便的集成,如与 Slack 的集成,可以根据您的日历自动打开勿扰模式和同步状态。这个插件甚至可以给你的会议标上颜色。

Clockwise Slack

顺时针方向有助于创造不间断的时间。作为工程师,避免过于频繁的上下文切换和干扰我们的流程是很重要的。通过自动驾驶某些会议,我们可以毫不费力地移动会议来创建这些区块。

矩形

矩形是窗口管理器,允许用户使用键盘快捷键移动和平铺窗口。

</blog/media/2020-05-26-Window-Tiling.mp4>

Tiling windows

现代技术工作的一个常见问题是管理许多应用程序和窗口。矩形使管理它们变得容易;您可以使用直观的键盘快捷键来移动东西(例如,Ctrl+Alt+→向右平铺窗口)。

本来 Devin 推荐的是眼镜。然而,眼镜不再保持,它的用户指向矩形,一个开源的替代品,具有本质上相同的功能。

看看用矩形移动你的窗口有多容易!

克利皮

Clipy 是一个 Mac 剪贴板扩展,可以保存拷贝项目的更长历史。

</blog/media/2020-05-26-Clipy.mp4>

Fetching a value from the Clipy clipboard

它支持包括图像和文本在内的各种格式,它的历史很长——你可以去找一个你几个动作前复制的小东西。

此外,它甚至支持代码片段。有可能经常打字的长东西吗?你可以存储它,只需一个快捷键就可以调用它。

开发工具

GitHub 内嵌建议

GitHub 提供了一个强大的功能,叫做建议的修改,它允许 PR 评论者提出内嵌建议,作者直接提交它们。

Suggestions

开发人员知道,令人厌倦的游戏——打开 PR、接收建议、返回 IDE、进行更改、推送更改、返回 PR……所有这些都可以通过内联提交建议的能力来消除。这对于微小的变化特别有用。

总的来说,这个特性帮助我们很多人简化了 PRs 的修复,从而加快了合并的速度。

奔向苹果电脑

Dash for macOS 是一个 API 文档管理器和代码片段管理器。

</blog/media/2020-05-26-Dash.mp4>

Browsing and Searching Documentation on Dash

有了 200 多个可用的离线文档集,您不用上线就可以找到最流行语言的文档。

无论是浏览 Go 的strconv包,还是查找 PSQL 命令,都可以使用一个单一、快速、统一的应用来完成。它甚至有一个 snippets 特性,允许你输入一个别名并扩展到一些更大的文本——对于更大的重复输入的文本非常有用。

Dash 对于跨多种语言和项目以及离线查找文档非常有用。

可宽延时间的提取提醒

拉动提醒是一个 Slack 应用,通知用户 PRs 上未完成的任务。

Pull Reminders

它非常适合通过 Slack 通知用户所有的公关事件和行动项目,有效地取代了 GitHub 的通知。用户可以在 Slack 中看到他们需要的一切,他们可以关闭电子邮件通知来清理他们的收件箱。

VS 代码的 GitLens

GitLens 是一个在 VS 代码中扩展 Git 功能的插件。

</blog/media/2020-05-26-GitLens.mp4>

Seeing line by line authors & commit messages inline

通常在项目协作时,很容易丢失特定代码的上下文。有了 GitLens,可以一目了然地访问这些信息:从内联错误到差异,甚至是特定文件和代码行的历史记录。

此外,GitLens 可以在“倒带”提交历史的同时显示并排的差异,允许用户逐行、逐提交地查看更改。

超级有用。

VS 代码的实时共享

VS Code Live Share 是一个插件,允许用户在 VS Code 中进行远程协作和实时编辑。

</blog/media/2020-05-26-Code-Live-Share.mp4>

Seeing Jacque work in a live share session

过去,远程协作通常是通过tmux完成的,它运行在终端上,共享基于文本的会话。

但从那时起,功能和技术已经扩展到涵盖完整的 IDE 和工作区共享,如在 Live Share 插件中看到的。

CircleCI 的许多工程师都使用 VS 代码,因为它有很棒的插件和可扩展的特性。Live Code 插件经常用于工作中的配对。

结论

有这么多伟大的工具,这是不可能在这篇文章中列出他们。然而,以上只是 CircleCI 工程师使用的众多工具中的一小部分。

在我认识他们的时间里,他们做了令人难以置信的工作,我们希望这些工具可以帮助你更有效地做你自己的令人难以置信的工作。

实践中 3000 万个工作流揭示了 DevOps 的哪些特性

原文:https://circleci.com/blog/the-data-driven-case-for-ci-what-30-million-workflows-reveal-about-devops-in-practice/

高绩效的技术交付团队是什么样的?你怎么知道你的团队做得好不好?虽然有许多关于技术交付团队行为的广泛报道和共享的调查,这些调查定义了高绩效者的衡量标准(2019 年傀儡开发状态报告2019 年加速开发状态),但在 CircleCI,我们很荣幸能够获得有利位置,能够审查关于技术交付团队在野外如何表现的真正大量数据。我们的云持续集成和持续交付平台每天为 40,000 多家组织和 150,000 多个项目处理超过 160 万个作业。我们分析了来自 3000 万个工作流*的数据,以了解观察到的行为与报告的行业标准相比如何。

我们的结果表明持续整合 (CI)为成为高绩效团队提供了一条清晰的道路:

  • 使用 CI 的团队非常快:80%的工作流在不到 10 分钟内完成。
  • 使用 CI 的团队保持流畅并保持工作进展:50%的恢复发生在不到一个小时内。
    • 25%在 10 分钟内恢复。
    • 50%的组织在一次尝试中恢复。
  • 高效的 CI 工具服务于您的业务需求,而不是相反。管道是灵活的,对它们的变更是成功的:在 90 天的观察中,当管道变更被执行时,50%的项目从未失败过。
  • 如果你正在寻找一条通往工程成功的道路,专注于 CI 将会带来成果。

使用 CircleCI 的团队正在以高速度、高信心交付产品。这就是成为高绩效开发团队的定义。 CI 是信心,自动化

尽管 CircleCI 是否能让团队变得高绩效,或者高绩效团队选择 CircleCI 的问题尚未确定,但我们可以清楚地看到,致力于 CI 实践的团队可以始终如一、自信地、以高速度开发出最好的软件

我们知道实施一个强健的、被广泛采用的 CI 实践并不容易。交付一个伟大的软件产品并不容易。但是我们的数据有很好的消息:使用 CI 的普通团队可以取得将他们放在技术交付团队最高级别的结果。您的团队不一定要有世界上最好的 DevOps 实践者才能成为最好的开发团队之一。您不必每天部署一千次,也不必每月在基础设施上花费一百万美元。简单地开始并致力于 CI/CD 实践是工程成功的可靠途径

点击阅读完整报告

反馈循环:如何适应不断的变化

原文:https://circleci.com/blog/the-feedback-loop-how-to-adapt-to-constant-change/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


我们生活在一个加速发展的世界。

信息正以前所未有的速度被创造、传播、处理和反馈。但是我们的大脑不适应这种危险的节奏;随着事物数量的增加,我们的注意力持续时间一直在缩短,以保护自己免受源源不断的信息冲击。

世界也在改变,以弥补我们缺乏专注。脸书和推特源源不断地提供微小的内容,用多巴胺轰击大脑——博柏利在展示设计后的第二天就发布了新的设计——表情符号和迷因这两种艺术形式将我们的想法提炼成图像。最好的组织了解我们对新事物的迷恋:亚马逊、谷歌、脸书——这些公司满足了我们对即时满足的需求。他们通过缩短推动其业务的反馈循环来做到这一点,这样他们就可以给我们带来更多我们想要的东西。速度最快的公司是科技公司,这并不奇怪;软件开发本质上是迭代和自适应的。

但问题就在这里:现在,每家公司都涉足科技领域。这是必然的。代码已经渗透到每一个行业,现代公司现在面临着达尔文式的困境:适应或者死亡。当然,这不是一个新的比喻;托马斯·赫胥黎在 19 世纪 60 年代就已经在谈论“达尔文主义”,不久之后,人们开始将这个术语应用于像资本主义这样的非生物过程。

但是新的是变化的速度;比赛的节奏;迭代的速度。这不在于你有多快做出特有的改变;这是关于你能多快地装备你的公司来应对不断的变化。你公司的活力是其速度的直接反映。速度是整个组织中紧密反馈循环的结果。

识别和缩短组织的反馈循环是保持业务反应最有效的方法。当反馈仅限于一台计算机上的一名工程师时,缩短反馈循环是很简单的。但是在规模上,这变得更具挑战性,也更重要。DevOps 运动的核心是致力于减少传统开发人员和运营工程师之间的反馈回路。

什么是反馈回路?

反馈回路有两个组成部分:一个动作和一个反应。让它成为一个“循环”的是当反应的结果反馈到过程中的时候。在动作和反应之间也可能有几个步骤,但是,为了简单起见,我们将这些视为原始动作的一部分。

当我们识别反馈回路时,有两件事我们应该关心:1)动作和反应是什么,以及 2)整个反馈回路的周期时间。周期时间包括:

  • 选择操作
  • 执行操作
  • 等待反应
  • 测量反应

Diagram-01.jpg

在我们讨论如何在您的组织中定位反馈环之前,让我们看一些具体的例子,看看它们是如何真正工作的。

生物学喜欢反馈循环

体内平衡是生物系统通过不断适应内部和外部变化来维持一系列条件的趋势。

反射也是非常短(和有限)的反馈回路。当你触摸一个热锅(动作)时,热量会触发你皮肤中的感觉神经元。这些信号作为电信号传输到你的脊髓,在那里被中间神经元处理(反应)。这些中间神经元向你手中的运动神经元传递信号,刺激肌肉,使你把手拿开(动作)。当你把手拿开时,感觉神经元停止尖叫,中间神经元停止告诉你手的运动神经元移动。这就是为什么你的手只会向后拉一小段距离。

所有这些都发生在你没有意识到的情况下——因为那需要太长时间。如果你着火了,你的身体想要尽快脱离那种状态。没有必要通过大脑的官僚机构来做决定;这只会增加循环的周期时间。

这不是人类独有的。任何可以繁殖的东西都是如此。所有的生命都依赖于忙碌的小反馈回路,使它们的宿主适应外部输入,并优化它们以进行复制。

如果我们缩小范围,我们可以看到跨物种运作的巨大反馈回路:在数百万年的时间里,数十亿种生物对外部刺激做出了反应。一些生命形式,如细菌,比其他生命形式的反馈回路更短;这就是为什么抗生素耐药性越来越引起的关注。或者是流感病毒,它每年都像博柏利病毒一样自我改造。

作为人类,我们倾向于认为自己在所有方面都是最好的。但是我们的生殖反馈循环非常缓慢:婴儿出生需要九个月,甚至需要才能繁殖。我们的优势是我们的智慧:我们已经能够用我们的工具创建更快的反馈循环。通过语言和文字,我们交流知识,改变行为——这一切都发生在一代人的时间里。

通过以这种方式缩短反馈循环,我们已经能够创造人类最重要的三项发明——诗歌、粒子物理和 Soylent。

作为自我调节有机体的系统

很好,我已经漫谈了一些关于生物反馈循环的问题,但是这些与产品开发或者更普遍的商业有什么关系呢?让我们来看看短反馈循环提高产品生命力的一些领域。

沟通

两个人之间的(好的)对话是一个光荣的反馈循环的产物。你说了什么,对方回应了什么,你的下一句话就要被那个人说的话所告知。聪明的说话需要积极的倾听。

这就是为什么当有人主导谈话时会让人不舒服:不允许其他人发言,讨论就失去了与其他参与者的相关性。谈话狂忽略了有利于他们自己行动的反应,这让人感觉被忽视了。

这就是为什么高质量的单口喜剧演员不只是死记硬背他们的套路;他们观察他们的观众并适应这个房间。最好的老师不会给学生讲课 75 分钟;这甚至不是一个完整的反馈循环——它只是一个长期的行动。相反,他们用苏格拉底式的方法提问并创造出对话。每说一句话,学生就会暴露理解上的漏洞,给老师提供纠正的机会。

媒体

一周可以创作一集《南方公园》。这种制作时间在动画行业很少见,大多数节目都需要几个月的工作。该节目的创作者认为,这种短暂的转变使创作过程更具反应性,从而导致一个持续的主题节目。就生命力而言,这是一种制胜策略。自 1975 年以来,周六夜现场每周都有演出,而没有停止的迹象。

音乐家们也认识到,如果他们发行月度单曲而不是年度专辑,观众会更加投入。这种音乐滴定煽起了歌迷的热情,帮助艺术家衡量观众的参与度。基于这些反应,音乐家可以进行调整,将风格或声音的反馈纳入未来的决策中。

最后,最近直播的激增(特别是在游戏行业)似乎是这个世界对短反馈循环的痴迷的合乎逻辑的结论。还有什么比实时看到事情发生更短暂的呢?Streamers 可以立即响应观众,根据观众的反馈修改表演。这让观众感觉参与到产品的创作中。即使是少量的代理也能增加情感投入和忠诚度。

*### 软件

软件工程师对短反馈循环非常熟悉,这并不奇怪。编写代码的行为本身就是一个巨大的反馈循环:

Diagram-02.jpg

软件的美妙之处在于它通常很容易改变。如果你有一个性格难相处的孩子,你不能更新他们的源代码。迭代产品的唯一方法是再生一个孩子,并更新你的育儿方式。

另一方面,发现软件中的 bug 并不需要召回整个产品;只要修复漏洞,继续你的生活。软件工程师天生了解高效反馈回路的力量;这就是为什么我们花这么多时间优化日常任务。

视频游戏行业的一个增长趋势是提供对标题的“早期访问”。这使得开发者可以将真实用户的反馈整合到设计决策中。数字市场 Steam 上一些最成功的游戏是持续开发周期的结果。简短反馈循环的价值应该是显而易见的:开发人员和用户之间的公开对话会产生更令人愉快的产品。

DevOps 由反馈回路供电

软件正在吞噬世界。而且,像大多数过程一样,软件开发受益于短的反馈循环。那么你应该从哪里开始呢?我反问的答案是“DevOps”。这个运动关注的是改进软件开发和交付的方式。通过优化开发者和操作者之间的反馈循环,公司降低了发布代码和衡量用户反应的成本。

这不是你需要担心的唯一的反馈回路。但这是一个可以自动化的反馈循环,让您专注于与您的业务相关的反馈循环。但是,在优化它们之前,您必须找到它们。你必须…

优化前识别

在你的组织中寻找反馈循环从识别你给客户带来的价值开始。使用反馈循环的四个部分来产生关于您的流程的问题:

  • 我们如何做出产品决策?
  • 我们如何改变产品?
  • 我们如何将产品交付给用户?
  • 我们如何知道用户对产品的看法?

Diagram-03.jpg

问这些问题应该会把你引向你业务核心的一个反馈回路。这可能还不存在!如果是这种情况,您应该立即将重点放在构建缺失的功能上。这个“主要”反馈回路定义了你的任务,并突出了你的弱点。虽然肯定会有其他的反馈循环,但是你应该自动化这些循环,直到你优化了你的主要反馈循环。

虚构的例子

假设你经营一家回形针公司。你的工作是以合理的价格生产高质量的回形针。你的主要反馈循环包括生产回形针(行动)和衡量顾客的反应(反应)。

比方说,你通过生产一百万个绿色 1⅛塑料回形针开始创业。当你把这些带到市场上,你会意识到每个人都讨厌绿色,喜欢洋红色。但是你仍然需要出售这些额外的库存,所以你开始了一个社交媒体活动,将公众的偏好从洋红色转移到绿色。

你的公司精通色彩理论,通过机器学习算法运行了几个世纪的艺术,以测量光谱分布随时间的变化。你会发现切换颜色偏好是出了名的困难——尤其是当颜色互补的时候。所以你歇业了。

这显然是愚蠢的

但混乱中有真相:人们会被他们不需要解决的问题分心。如果你用每种主要颜色做了 100 个回形针,并测量了哪些卖了,你就会更好地了解人们想要什么。您可以帮助成千上万的人保持他们的文件在一起,同时仍然保持盈利!

你应该问一些关于主要反馈回路的问题:

  • 做一个回形针需要多长时间?
  • 将它们运送给供应商需要多长时间?
  • 供应商多长时间进货一次?
  • 你怎么知道哪些回形针受欢迎?

这些问题中的每一个都会引出一个值得优化的指标。您可能会发现您甚至缺少测量任何东西的工具!但是这没关系:即使意识到您的过程也是一项有价值的任务。

回避这些问题是可以理解的;它们背后往往隐藏着一个严酷的事实,有时这个残酷的事实就是你正在开发一个没有人需要甚至喜欢的产品。但是如果你想你的生意成功,你必须问和回答这些问题。

优化也是一个反馈循环

一旦你确定了你的反馈回路,你就需要开始考虑优化它们。正如我们所讨论的,投资于你的组织的适应性是你能为你的公司健康所做的最好的事情之一。

但是改变不会在一夜之间发生!我们都是人,依靠模式和结构来度过我们的日子。流程优化需要根本性的改变,即使是那些希望 T1 这么做的人也必须克服一些严重的惰性。

这里的技巧是根据 DevOps 最佳实践逐步引入变更。例如,如果你试图培养一种测试文化,要求每个人从今天开始写测试可能会受到一些抵制。相反,要求工程师为他们发现的每个 bug 编写一个测试。每一个新的测试都暴露了代码覆盖率中的差距,告知代码库的哪些区域应该获得下一个优先级。

在冲刺或中断之后,运行回顾以明确地捕捉来自涉众的反馈。通过这样做,你可以从错误中走出来,并获得如何在将来防止错误的信息。

最后,真正的用户反馈应该比其他任何东西都重要。你的“专家”不会像普通消费者一样思考,因为他们是创造者。

彻底的改变很少像预期的那样起作用;人太固执,会想方设法抗拒来自高层的命令。但是当你的文化改变时,对你的过程进行小的、渐进的改变会给你的团队一些喘息的空间。这允许改变有机地发生,同时仍然优先考虑你的产品中最脆弱的部分。

最快的生存者

软件交付可能不是你的主要反馈环。而且不应该;那是我们的工作。CircleCI 花时间想办法将代码从工程师的头脑中取出,送到用户手中——而不牺牲速度或安全性。

我们认为人们在知情的情况下会做出更好的决定。变得有见识需要做决定——在你确信成功之前执行。我们将帮助您更频繁地做出更多决定。我们无法预见未来,但我们非常确定它会与现在不同。随着世界发展速度的加快,您的组织需要跟上步伐。

跟上意味着增加你的产品对变化的适应力。这种变化可能是政治的、环境的、技术的或文化的——关键是你不会知道小行星什么时候会导致灭绝事件。不要让你的公司走上恐龙的老路,就像 T2 一样,因为它像 T3 一样移动;成为在冲击中幸存下来的小哺乳动物,因为它能够适应。

确定你的反馈循环并缩短它们。你的后代会感谢你的。*

运营的未来| CircleCI

原文:https://circleci.com/blog/the-future-of-operations-6-shifts-worth-noting/

在过去的几年里,“DevOps”这个总称发展得如此之快,以至于现在这个词对所有人来说都意味着同样的事情。这与其说是一个语义问题,不如说是软件交付自动化范围不断扩大的反映。这种增长的掌舵人是那些管理工具和实践的操作者,这些工具和实践使得今天的软件交付方法成为可能。他们的角色已经发生了巨大的变化,甚至在过去两年 CircleCI 一直在他们的防火墙后运行。这些操作者的责任是多方面的:他们负责保持开发人员每天使用的工具链正常运行,同时对业务的安全性、成本控制和合规性负责。在幕后,他们正在阻止不可避免的技术火灾,同时面临来自高级管理层的越来越大的压力,要求他们实现数字化转型。

在 CircleCI 内部,我们很幸运地坐在前排,了解世界各地成千上万个团队的模式、实践和工具的变化。这些转变使运营商和 DevOps 从业者能够继续创新,加快发展速度,并创建可重复的流程,从而保持领先并避免灾难。

当我们看到我们的客户在软件创新方面领先时,我们看到了以下六种模式:

1。过程才是王道。最好的团队总是推动尽可能多的软件交付管道的标准化和自动化。每个人现在都承认软件是一种战略投资。但是对于这些组织中的开发人员来说,实际的代码通常被视为一种负债,或者至少是一种快速贬值的资产。增值资产是公司创建的可重复和可扩展的过程,用于将代码转化为运行的软件,从而在其软件交付管道中驱动更高的吞吐量和质量。这些过程包括:自动化团队如何交付他们的工作软件,他们如何管理他们的操作效率,以及他们经济有效地启动新项目的能力。

2。管道的分散化:我们已经看到一种趋势,即远离集中的管道,将权力推向边缘。公司希望通过给开发人员自动提供执行管道的资源,使他们能够控制自己的管道逻辑。对于行业领导者来说,维护需要一次性供应和独立安全控制的独立构建系统已不再是可容忍的。随着运营商寻求消除自身的瓶颈,团队正在转向他们可以集中运营的系统,但这些系统为团队提供分布式电源,以便他们自己进行自我供应和自动扩展。

3。抽象比比皆是:devo PS 领域中许多最棘手的问题越来越多地被抽象出来。常见的挑战包括环境的虚拟化、基础架构的编排,以及在过去需要手动干预或通过脆弱的脚本粘在一起的复杂流程集上安排执行。今天,我们有像 Docker、Kubernetes、Terraform 和许多其他工具来模拟和可靠地自动化这些问题。最好的 IT 团队总是希望缩小深奥风险的范围。这就是为什么他们选择行业标准和/或供应商支持的常见问题抽象,而不是尝试在定制系统上创建、维护和创新。高绩效的软件组织将尽可能多的精力集中在专有价值创造上,而不是在他们的同行中普遍存在的支持系统上。他们喜欢开放的标准,这些标准提供了表达性的代码到通用的语法和工具中。

4。持续的法规遵从性:随着软件交付管道中自动化的发展,法规遵从性努力已经很难跟上步伐。将软件交付吞吐量作为战略资产进行投资的公司不希望这种投资受到业务流程瓶颈的阻碍。运营商希望向他们的各种利益相关者保证,他们拥有强大的实践,如职责分离、谨慎的安全密钥管理、高可用性、灾难准备等。在不影响团队产量的情况下,将这些问题纳入核心运营模式的系统之所以成功,是因为它们保护了运营商最宝贵的资产:系统的稳定性和责任性。

5。自动化的自动化:远离单一架构的趋势使得运营商需要支持越来越多的活动软件项目。即使是那些维护巨石柱的人也倾向于拥有越来越多的其他辅助系统。这种复杂性的激增需要新的抽象来避免管理和运营开销的困境。我们看到脚本和其他工具的使用越来越多,它们加速回购、注入各种常见的依赖关系、生成 CI 脚本、注册各种集成点以及其他以前的手动任务。通过减少创建新计划和维护现有计划的税收,团队开始看到软件创新吞吐量的巨大改进。开发运维实践的可重复性成为团队成功的关键因素。

6。可用性和恢复:我们越来越多地看到公司以生产质量的服务水平对待他们的 CI/CD 系统。这些公司知道成功的 DevOps 实践实际上将软件交付管道转化为生产系统,因此他们正在梳理他们的系统以消除所有单点故障。我们看到了提供自动化备份和恢复、全球联合基础架构以及无缝扩展以满足需求高峰的需求。这些投资是由严重的规模问题和雄心勃勃的企业弹性和可恢复性目标共同推动的。

对软件团队的需求将继续增长。我们知道,管理这些团队所依赖的系统的操作员需要迎接未来的挑战:管理日益增加的自动化及其带来的开销,通过允许团队更快、更自主地移动来实现创新,同时保持他们的系统安全合规。

CI | CircleCI 中的速度与决定论问题

原文:https://circleci.com/blog/the-issue-of-speed-and-determinism-in-ci/

在 CI 中,我们花了很多时间讨论速度和决定论。持续集成的存在是为了帮助团队更快、更高质量地构建和发布软件。我们通过在代码库中添加小的改进来做到这一点,并采用动态测试来确保我们不会破坏任何东西。确保质量的一个关键因素是一个叫做决定论的概念。本质上,决定论意味着同样的输入,你会得到同样的输出。一个测试运行一次就失败了,如果没有其他变化的话,下一次就不会通过了。

在过去的几个月里,我们一直致力于解决和修复一个问题,这个问题影响了我们对某些项目速度的确定性。在这篇文章中,我将谈论我们发现了什么,以及我们如何修复它。结果是,在 CircleCI 上使用 Docker executor 的项目现在可以配置 RAM 磁盘,作为在内存中而不是在 SSD 上执行 I/O 繁重操作的一种方式。

问题是:同样的工作,不同的结果

关于决定论,需要注意的一件重要事情是,没有决定论,就很难衡量其他任何东西;一个度量,任何度量(例如,“这个构建需要运行多长时间?”)只能给你有意义的信息,如果你可以依赖它是一致的。

我是 CircleCI 的解决方案工程师,因此我与潜在客户一起解决他们评估平台时出现的问题。我们最近与一位潜在客户合作,该客户因缓存恢复而体验到不一致的性能。

对于有问题的客户,我们偶尔会看到缓存恢复花费两倍甚至三倍于其通常持续时间的时间(长达 90 秒)。解决这个问题尤其重要,因为客户正在从传统的 Jenkins 安装进行迁移,而提交部署时间 CircleCI 传统上擅长的领域——是任何新 CI/CD 设置的关键成功指标。缓存恢复时间的这种变化使得很难掌握这一关键指标。

这种差异的影响在并行运行的作业中尤其明显。CircleCI 让用户能够一次在一系列不同的机器之间分割一个大型测试套件。然而,这些并行构建只和运行时间最长的作业一样快。任何不必要的变动都可能要求用户在继续其工作流之前等待单个作业完成。

determinism-cache-times.png

虽然速度的差异令人沮丧,但作为一名解决方案工程师,我更担心这一点,因为这使我无法向潜在客户提供准确的报告,说明 CircleCI 可能为他们的构建提供的任何速度改进。我决定更深入地研究这个问题,以帮助这个潜在客户,但最终找到了一个解决方案,可以帮助任何在我们的平台上运行 node.js 的人。

深入研究

我决定探究可能导致这种情况的原因。我们如何加快提交部署时间?最终我们发现,我们看到的所有不一致都是在恢复 Docker 容器中运行的 Javascript 项目的缓存和工作空间时发生的。问题似乎源于 CircleCI 同时在同一个虚拟机上运行多个客户的 Docker 容器:如果一台机器的所有租户都在大量使用它们之间共享的硬盘驱动器,硬件可能会陷入困境。

我们注意到这些不一致的所有项目都是基于 Node.js 的,并且都缓存了它们的 node_modules 文件夹。节点模块是一个有趣的例子,因为它不仅仅是一个需要缓存的大型工件(我们看到了 150-300mb 范围的项目),而是大量的小文件。我们检查的一个项目有一个 node_modules 文件夹,包含超过 150,000 个文件。此外,对于其他非 Node.js 项目中的大型缓存,我们似乎没有体验到相同的性能变化。

第一个实验

解决这个问题的第一步是研究用于压缩缓存档案的 tar 命令。压缩指定的目录相当简单。一个直接的观察结果是,tar 不是多线程的,因此增加 CPU 对任务的可用性不会产生任何影响。这导致了第一个实验,它将使用一个替代的 tar 实现,特别是pigz(GZip 的并行实现),以便减少压缩缓存归档所花费的时间。在本地,这种方法效果很好,表现出极大的性能提升。作为我们测试项目的一部分,在 CircleCI 上的测试产生了一个改进的最佳情况,但是在多次运行中仍然显示了同样令人沮丧的变化。

探索基于内存的解决方案

因为 CPU 似乎不是瓶颈,我们得出了一个新的假设,磁盘本身可能是问题所在。我们在构建软件时经常采用的一种传统折衷方法是用硬盘空间换取 RAM,因为内存运行速度更快,限制更少。对于我们的客户来说,解决这个内存问题意味着他们不仅能够使用资源来缓存或恢复工作区,还能够使用 node.js 在其 CI 环境中进行任何类型的操作。

在 CircleCI 的情况下,我们的 Docker 执行器通过在底层 AWS EC2 VM 上托管 Docker 容器来工作,根据请求为每个任务分配必要的 CPU 和 RAM,但容器都共享一个本地 SSD。我们如何测试它的一个想法可能是利用一个 ramdisk——并完全避免写入这个 SSD。方便的是,我们的容器已经在/dev/shm 安装了一个 ramdisk,这有助于快速试用这种方法。我们看到原始性能和一致性都得到了立即的提高,尽管不幸的是/dev/shm 被挂载为 NOEXEC,如果它被用作工作目录,就会立即出现问题——用户通常需要调用可执行脚本来安装依赖项或运行测试。

我们如何解决这个问题

最终,我们能够在平台上运行的每个容器中添加一个新目录。容器中的所有内容都将存在内存中。随后,我们对挂载 Docker 容器的方式做了一点小小的改变,以便在/mnt/ramdisk 中公开一个单独的 ramdisk。这个 ramdisk 可以利用用户配置中指定的资源类中可用的内存,并且没有上面详述的 NOEXEC 限制。对于拥有大型 git 存储库、缓存、工作区或其他 I/O 繁重工作负载的用户,一种简单的入门方法是通过配置中的 working_directory 设置使用新的/mnt/ramdisk 位置作为他们的工作目录。

RAM 磁盘现在提供了一种在内存中而不是在 SSD 上执行 I/O 繁重操作的便捷方式,同时仍然能够利用基于容器的 CI/CD 作业的便利性、确定性、低成本和快速启动时间。

这种方法还有其他一些优点。因为内存是如此快速和灵活,它给了客户一个额外的理由来利用我们的平台提供的更大的资源类。除了缓存之外,这一改变也很有用,它可以用于客户工作流中的任何 I/O 密集型操作。

这一变化导致了我们产品的两次重大升级。首先,也是最明显的,我们发现一致性问题的工作的工作流程时间减少了 40%。以下是修复前后运行的工作流示例。

determinism-before-after.png

然而,更重要的是,这些时间现在在每次运行中都是一致的——更具确定性。在这一关键性能指标没有变化的情况下,我们现在能够更容易、更准确地进行性能基准测试,同时也减轻了客户在遇到这一问题时的挫折感。

这一功能现已在我们的产品中投入使用,因此,如果您的团队正在运行带有缓存或工作区的 Javascript 作业,您现在可以获得这一改进,这将有助于提高性能和一致性。

如果你喜欢发现和解决这样的重要问题,加入我们的团队

可能的小遗留代码:软件所有权的寓言

原文:https://circleci.com/blog/the-little-legacy-code-that-could-a-fable-of-software-ownership/

在角落里,有一些代码。

它厌倦了被人骂。它厌倦了被最后选中。毕竟这个代码是经营业务的。但是,对于它处理的所有交易、它创造的价值和它帮助的用户,它都被取笑了。

这段代码很悲伤。“我是业务骨干!”它在虚空中呼喊。人们不玩它,甚至不想与它互动。代码听到了最糟糕的名字。就在上周,有人被征召去修改代码,那些言论是不真实的。

“我不想碰那个代码。太丑了。这不符合我们目前的做法。当我真的搞砸了,我就会收到传呼,糟糕的事情就会发生,”一位团队领导说。

代码坐着哭它的小 1 和 0。然后代码重新考虑它的智慧,并决定,真的,一个品牌运动是它所需要的。它开始与其他代码对话。这段代码与其他代码并不真正友好,但是其他代码似乎总是从它那里拿东西。

“你好微服务先生,为什么人类喜欢在你身上工作?”

“哦,你好,”微服务回应道,眼睛飞快地转着,拼命想从对话中脱身。“我想是因为我的目的很容易定义,我有测试覆盖率,而且我很容易部署。”

“哦,”代码说。“谁在你身上工作?”

“整个团队一直和我一起工作。我被编写、重构、放到 Kubernetes 上、更新和自动缩放。”

“哇,听起来很有趣。什么是 kubernetes?”暗号说。“你如何适应生产?”

“生产?”微服务回答道。“那是什么?”

“哦,不。”代码回答道。它意识到自己在和“最好的”代码对话——技术上完美但对用户没有帮助的代码。我们的小代码得到了很多这样的反应。有很多新的系统和新的想法,但是这些代码只是不停地运行,运行着一个业务。

我们被打败的朋友,代码,遍历了几个管道,试图与另一个代码库交互。

“你好。你知道为什么人们不想和我一起工作吗?”

这个代码库更大,至少有几天没刮了。它看起来有点凌乱,但总而言之,这增加了它的智慧。

衣冠不整的代码简单地回答道:“嗯,当然没有人愿意和你一起工作。你是遗产。”

“没必要当着我的面骂我,”代码说。

“不,不,遗产意味着你经营企业。你有巨大的影响力。你做了各种各样重要的事情……但这也意味着和你一起工作的最好方法被那些伟大的老方法所取代。人类已经转向新的模式、实践和工具。我也遇到了这种情况。”

“是吗?”回答我们现在稍微不那么沮丧的代码。

Fable_dude.jpg

“当然可以。还有遗产是怎么变成一个不好的词的?遗产意味着它起作用了。它工作了很长时间,经受住了大肆宣传。像你和我这样的代码,我们继续经营生意。我们一直在交付我们应该交付的东西。领导者想要遗产,为什么代码库不应该呢?”

“但这些微服务总是在谈论每个人如何与他们合作。他们称我为数字贱民。”

“他们当然知道。是嫉妒。这些服务中有多少会被公开?没有你或我,有多少人能真正做好自己的工作?当然,他们应该能够做到这一点,但人类往往不能非常正确地抽象代码。这些服务最终往往成为我们的接口。我们是传统,没关系。我们传递价值。其他代码库希望他们拥有我们所拥有的事务、延迟和健康。”

“那么,为什么我们没有专门的团队与我们合作呢?”

“嗯,人类喜欢重组他们的工程团队。他们给它们分配域或微服务或子系统。很多时候,我们在那些人类构造分裂存在之前就存在了。我们被留在地板上,最后被挑选,或者不知何故只跟随那些非常小心的人,不管他们是哪个队的,”明智的代码说。

“嗯。所以因为我们工作得很好,而人类想做其他事情,我们就被遗忘了?”

“我不会这么说。人们知道我们的存在。他们知道我们做好事。他们只是想用更新的方式工作。有时他们想花更多的钱购买新的工具和技术。我通常只是想知道这些项目中的哪些会进入生产阶段。请记住,生产是我们展示我们有多有用的地方。”

“我一直喜欢参与制作……”我们的朋友 codebase 沉思道。“谢谢你的帮助。”

“没问题。”

他停下来,转过身。“你知道,我们不是遗产…我们是传奇,”现在微笑的代码库说。

“就是这种精神。我们传递价值,走在前面。我们可以为此感到自豪。”


这是一个虚构的故事。但它所说明的问题是非常真实的。现在我已经引起了你的注意,让我们来看看软件是如何失去它的看护者的,以及防止没人想接触的悲伤、孤独的代码的方法。

缺乏软件所有权导致的问题

每当有一部分代码没有明确的所有者时,就有问题了。现在这可能不是问题,但事情开始恶化只是时间问题。

有一些迹象表明您可能存在所有权缺口:

  • 系统中有没有没人想碰的组件?
  • 代码库中是否有大多数新员工都没有接触过的地方?
  • 在代码或系统中是否有需要英雄主义和来自伟大的老一代人的部落知识的地方?

如果这些听起来很熟悉,你可能正在处理一些遗留的…咳咳…传奇的代码。

如果代码被交付,那么一旦交付,它就是“别人的问题”,这不是所有权;更像是租约到期的出租。一旦某个特性、子系统或应用程序发布,谁来维护它?一个运营团队,或者其他不那么幸运的团队,在他们所处的结构中,事情越来越糟糕,他们可能不适合维护一个代码库,因为他们几乎没有领域知识,并且优先维护基础设施的稳定性、升级和改进。

什么时候代码所有权是必要的?

生产中的所有代码都必须有人拥有吗?

在我们的行业中,SRE 团队通常会负责处理任何问题。当软件没有一个明确的所有者时,SRE 就成了零零碎碎的数字垃圾抽屉——确保火车不出轨。这是不公平的,也不是对这个团队技能的谨慎使用。但是怎么会变成这样呢?

开发团队通常以新特性交付或速度来衡量。它们并不总是因为可操作性、可追溯性、保持库更新…基本上,不是特性的东西而被衡量和奖励。因为这种责任被忽视了,所以它通常会由某个运营领域的人来承担。Ops 是根据稳定性和可靠性来衡量的。他们可能会使用系统管理的牛顿第三定律来接近它,该定律说“一个未被触及的系统将保持在线。”我们知道在某个时候这不是真的,但在那之前,全世界的运营团队都会遵循这一点。

为什么会出现权属不清的情况?

一个软件的所有权要么模糊不清,要么不存在,主要有四个原因:

  • 写它的人已经不在了;
  • 曾经有一个所有者,但是团队重组了,所有权转移/分散了;
  • 旧软件被弃用,但一个新的东西并没有完全取代它;
  • 从来没有一个定义明确的所有权模型。

这听起来可能是显而易见的,但我还是要说:你不能只是建立一些东西,然后停止工作。但是人们总是这样做。企业对事物进行测试,然后事物不可避免地发生变化,出现新的优先事项。该软件现已投入生产,人们正在使用和依赖这些功能。他们仍然希望它们发挥作用,并且通常期望它们有所改进。然而,很多时候,一个产品或功能推出后就被忽视了。

这可能是 MVP 开发模型的结果——希望快速发货,并获得快速反馈。问题是,“让我们看看”模型往往是不完整的;它是有计划的,直到收集到有价值的数据并完成反馈循环。但是经常被忽视的是用来收集反馈的软件;现在发生了什么?“让我们看看”应该从一开始就有一个完成步骤,这可能是把事情拆掉。

规划软件的持续维护是构建软件的首要责任之一。编写和发布代码可能感觉像是一项成就,确实如此,但实际上您永远不会完成您编写的代码。真正的工作是对代码的持续维护和保养。

与永久运营成本相比,软件的初始开发成本趋近于零。

如何鼓励软件所有权

或者,当某样东西没有主人时,你会怎么做?

许多组织已经尝试了一种重叠的矩阵模型,比如 Spotify guilds 或 T2 Valve 的 cabals,来将所有权分配给孤立的代码项目。我们在这里也试过了。说实话,我从没见过它效果超级好的。就其本质而言,你的公会工作对于你的主要目标来说是次要的。因此,你的表现永远不会以你在公会的表现来衡量。所以这并没有创造任何真正的所有权,也没有解决所有权问题。不说别的,它稀释了责任。

然而,有一些事情确实有效。

  • 有些公司做团队维护。假设你在一个负责计划和支付的团队中。有一个孤立的项目被发布,虽然与计划和支付无关,但也成为了您团队的责任。这往往比行会更有效——它的优势在于可追踪和可衡量。有些人不喜欢这种方法,因为它不干净,而且分派的职责与核心领域无关。人们喜欢完美的领域,但我们并不是生活在一个完美的世界里。任何期望在生产中执行的代码都需要有人直接负责维护。即使是次优的所有者也比没有所有者好。

  • 另一个策略是在早期建立跨代码库的通用实践。如果一个开发者在几个相似的代码库上工作,那么在它们之间转移的成本必然更低。在这种环境中,测试、构建、部署和更新都以同样的方式运行。任何维护一个代码库的人都更有可能介入另一个代码库。虽然使用的语言或领域模型可能不总是相同的,但是您已经得到了相同的工具。通过使用抽象,开发人员可以专注于运行测试。抽象为你提供了公共的入口点。您总是以同样的方式构建、测试和部署。

最终,优化所有权涉及到你设计和构建软件的方式。具体来说:仪器、测量和监控的设计。正确编写软件之外的一个层次是验证这一点的能力。考虑诊断进行设计所需的远见需要开发人员的意图。通常他们不会有这种意图,除非他们被分配了行动任务(或者在半夜被传呼)。您可以在验收标准中构建跟踪点、调试点。

软件所有权是关于工程的非功能部分。这是一个人的问题,那些人花时间在操作、测量和可维护性上。记住,开发一个新特性的成本在运行、更新和维护该特性的几年或更长时间内会趋近于零。

缺乏软件所有权不是一个简单的问题,但是我有一些建议:

1。仅仅因为你不再想做某件事,并不意味着它就消失了。为此做好计划。在团队重组、维护计划和前进的道路中考虑它。
2
。向新员工传授经营业务的代码库。**一个常见的问题是,新人从事新事物,这意味着能够从事传奇系统工作的工程师的数量或比例减少了。也滋生了英雄文化,不成规模。
3。下一个处理代码库的人不会知道所有的上下文。那个人可能就是两年后的你。记录做出决策的原因,而不仅仅是它们是什么。创建良好的提交消息。**

让你走到这一步的代码,也可能让你走到那一步,这需要与匆忙丢弃或替换功能系统的行为相平衡。但是,请记住,我们的朋友遗留的代码库以及这些年来它能够提供的所有功能、事务、用户、资金和吞吐量。它需要一点爱,它会走得很远。

漏洞扫描程序- DAST -安全 CI/CD | CircleCI & Probely

原文:https://circleci.com/blog/three-rules-for-turning-devops-into-devsecops/

为什么是 DevSecOps?

人们普遍认为,我们的开发和部署方法越快,就越容易出现安全问题。当我们到达一个开发敏捷、快速、实用的点时,我们开始问自己:这些都是安全的吗?更重要的是,如何提高我们项目的安全性?

随着 DevOps 实践在行业中的成熟,安全性被置于边缘,直到现在我们才开始看到改进。随着敏捷开发和快速代码部署,我们也看到了一波应用程序的快速漏洞引入。由于大多数 DevOps 栈优先考虑功能和速度而不是安全,从业者通常不愿意通过安全检查,因为这会减慢过程。然而,像 2018 年英国航空公司的违规事件这样令人瞠目结舌的违规事件证明了改善我们的 DevOps 实践和整合尽可能多的安全检查是多么重要。尤其是在看到法律后果和英国航空公司最近面临的 GDPR 罚款(约 2.3 亿美元)之后。

DevOps 与 DevSecOps

开发和运营的合并是非常自然的。开发团队和运营团队一直有着相同的目标:尽快向客户发布功能性应用。然而,安全似乎是这一进程中的一个障碍。开发和运营的目标是快速和功能性的发布。另一方面,由于所有的审计和耗时的测试,安全性似乎减缓了发布的速度,从而阻碍了开发运维过程。了解了这一点,许多开发人员将安全性视为软件开发生命周期(SDLC)的一个障碍而不是一个必要和补充的部分就不足为奇了。这就是“DevOps vs. DevSecOps”辩证法的来源。然而,最近我们看到许多安全从业者开始理解,如果安全不在 SDLC 中左移,它将在 DevOps 时代被忽略。安全性越早进入 SDLC,对敏捷开发人员的阻碍就越小。尽管在 DevOps 中加入安全性可能会稍微减慢开发过程,但它通常会缩短上市时间,并改善您公司的状况。

那么,DevOps 和 DevSecOps 是对立的吗?

尽管看起来是这样,但这两者并不矛盾。我们知道必须以某种方式关注安全性,除了在早期培养开发、运营和安全性之间的协同作用之外,还有什么更好的方式呢?让安全性成为工作流的重要组成部分并不一定意味着脱离开发运维,而是旧系统的进步。

那么,如何成功地把 DevOps 变成 DevSecOps 呢?

以下是一些规则或最佳实践,可帮助您实现这一目标:

规则#1:从简单开始并自动化

当试图将安全性整合到 DevOps 实践中时,我们会遇到两个问题:速度和缺乏安全意识。这就是为什么当你第一次开始将 DevOps 转变为 DevSecOps 时,你必须确保这个过程对于开发者来说是快速而顺利的。您希望将安全性“融入”您的开发生命周期。要做到这一点,重要的是要避免像误报、难以理解的安全工具和冗长的代码审查这样的麻烦困扰开发人员。同时,您希望在工作流中尽可能多地自动化安全性,以便可以尽早发现安全性问题。最重要的是,由于是开发人员来修复这些漏洞,您需要确保他们知道如何正确地修复它们,或者他们有正确的相关指导。

那么,你来这里的第一步是什么?

您必须确保开发人员能够轻松部署安全的应用程序,并且能够快速完成。要做到这一点,您将需要一个对开发人员来说可以理解和直观的工具,同时自动化您的安全测试过程。

这就是应用安全测试(AST)工具发挥作用的地方。有两种主要类型的 AST(有两种以上,但这些是最常见的)😄(动态)AST 和 S(静态)AST。两者的区别在于,SAST 工具扫描代码的漏洞,而 DAST 工具在应用程序运行后扫描它。由于 DAST 工具试图像黑客一样攻击应用程序并发现安全问题,他们报告了更多相关的发现,并提供了关于漏洞的大量证据。SAST 扫描仪在这里看起来可能是正确的选择,因为他们在工作流程的早期扫描代码。然而,事实是 SAST 扫描仪报告了更多的误报,无关的发现可能会拒绝开发者。另一方面,开发人员更容易理解 DAST 结果,因为它们显示了漏洞的后果,开发人员可以了解风险有多严重。大多数好的 DAST 扫描器也有插件和强大的 API,可以让你将它们与流行的 CI 工具集成,比如 CircleCI。其中一些,比如 Probely ,将为你的开发者提供定制的指导,告诉他们如何修复发现的问题。

DAST 扫描仪是将 DevOps 转变为 DevSecOps 的良好开端。它们使开发人员在处理漏洞扫描时不那么沮丧,并且更容易理解安全风险。DAST 扫描仪可以无缝集成到您的 CI/CD 管道中。

规则 2:分清主次

对于 DevSecOps 来说,速度是关键,而优先级是速度的关键。当涉及到将安全性集成到您已建立的 DevOps 实践中时,自动化和速度不应受到影响。为了实现这一点,你必须分清主次。您必须将每个应用程序和每个漏洞与风险对应起来。例如,您希望更加关注处理敏感客户信息的应用程序的安全性。此外,您需要了解每个漏洞的背景,并将其与您的企业或公司面临的潜在风险对应起来。当您这样做时,优先考虑具有高风险评分的漏洞。或许,作为一个 DAST 扫描仪,自动为你做部分工作。它根据上下文将漏洞分为三类:高、中、低。

优先级有助于你专注于重要的事情,并避免给你的开发人员带来太多的负担。这将有助于你与开发证券协同作用。

规则 3:示范和教育

当将安全性集成到您的公司中时,仅仅合并 DevSecOps 实践、设置规则并要求人们遵守它们是不够的。DevSecOps 更多的是关于公司文化,而不是一套程序实践。这是一种组织和思考的方式。您可以从对开发人员和其他员工进行安全教育开始。您可以从向他们提供这个 web 应用程序安全清单开始。通常,您公司的开发人员和员工不会理解安全性的重要性,并且会低估许多风险。为了对他们进行适当的教育,你需要演示这个过程和其中的风险。

对开发人员进行安全编码和安全意识的教育是不够的。贵公司的文化应该从整体上拥抱安全。除了向技术人员展示您的 DAST 工具提供的漏洞和利用证据之外,您还应该进行公司范围的演示。针对技术和非技术人员的伪造但精心制作的网络钓鱼和社会工程攻击带来了大量的可见性和参与度。因此,教育和提高对此的认识至关重要。您可以通过自己进行“模拟”网络钓鱼攻击来演示网络钓鱼攻击的含义。发一封恶意邮件,看看你公司有多少员工点击了链接,泄露了机密数据。这将有助于您展示此类攻击的风险,并教育非技术人员。

奖励规则:规则就是用来被打破的

DevOps 和 DevSecOps 的美妙之处在于,您可以变通现有的规则,也可以制定自己的规则。所以,如果这些规则在你的公司内部不兼容,不要担心,总有办法解决。请随意调整 DevSecOps 实践以适应您自己的情况。尝试不同的工具,看看你是否能负担得起 pentest 或 bug 奖金,在网络安全上投入时间和金钱,等等。用一套标准化的安全规则做事没有“正确的方法”。

我应该使用什么 DAST 工具?

有许多好的 DAST 工具和漏洞扫描器,但是您应该使用哪一个呢?答案取决于您的个人偏好、预算和公司规模,但一般来说,您会想要一个快速、易于与 CI 工具和其他应用程序集成的 DAST 工具,具有强大的 API,并为您的开发人员提供直观的用户界面和有关其发现的漏洞的相关信息(如何修复、证据、描述)。如果你正在使用 CircleCI,你会希望有一个 DAST 工具,有一个匹配的 orb 。这样,您将能够轻松地将安全性集成到您的 CircleCI 工作流中。

为此,你可以使用 Probely 的球。Probely 允许您扫描您的 web 应用程序的 1000 多个漏洞,它具有上述所有功能。开发人员很欣赏 Probely,因为它很直观,可以显示相关信息。除了显示漏洞是真实存在的证据,Probely 还会检测用于构建应用程序的语言或技术,并在此基础上为用户提供定制的如何修复漏洞的指导。


这篇文章是我们制作的关于 DevSecOps 的系列文章的一部分。要阅读本系列的更多文章,请单击下面的链接之一。

优化 Docker 版本的技巧| CircleCI

原文:https://circleci.com/blog/tips-for-optimizing-docker-builds/

Docker 图像被用作 Docker 执行器中的主要图像。它们是容器的蓝图,为容器的产生提供了指导。在这篇文章中,我将阐述一些经常被忽视的概念,这些概念将有助于优化 Docker 映像开发和构建过程。

如何建立 Docker 形象?

让我们从 Docker 构建过程的简要描述开始。它是通过使用 Docker CLI 工具运行 docker build命令触发的过程。

docker build命令基于名为 Dockerfile 的文件中指定的指令构建 Docker 映像。Dockerfile 是一个文本文档,包含用户在命令行上调用的所有有序命令,以组合一个图像。

Docker 图像由只读层组成。每一层代表一个 Dockerfile 指令。这些层是堆叠在一起的,每一层都是前一层变化的增量。我认为这些层是缓存的一种形式。只对改变的层进行更新,而不是在每次改变时更新每个层。

以下示例描述了 Dockerfile 文件的内容:

FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py 

该文件中的每个指令代表 Docker 映像中的一个单独的层。以下是每个指令的简要说明:

  • FROMubuntu:18.04 Docker 图像创建一个层
  • 从 Docker 客户端的当前目录添加文件
  • 使用 make 构建您的应用程序
  • CMD指定在容器中运行什么命令

在构建过程中执行这四个命令时,它们将在 Docker 映像中创建层。

如果你有兴趣了解更多关于图像和图层的信息,你可以在这里阅读

优化映像构建流程

现在我们已经介绍了一些关于 Docker 构建过程的内容,我想分享一些优化建议来帮助高效地构建映像。

短暂的容器

docker 文件定义的图像应该生成短暂的容器。在这个上下文中,临时容器意味着可以停止和销毁的容器,然后使用绝对最小的设置和配置重新构建并替换为新产生的容器。短暂的容器可以被认为是一次性的。每个实例都是新的,与之前的容器实例无关。在开发 Docker 映像时,您应该尽可能多地利用短暂的模式。

不要安装不必要的软件包

避免安装不必要的文件和软件包。Docker 图像应尽可能保持简洁。这有助于提高可移植性、缩短构建时间、降低复杂性和减小文件大小。例如,在大多数情况下,不需要在容器上安装文本编辑器。不要安装任何非必要的应用程序或服务。

实施。dockerignore 文件

.dockerignore 文件排除了与您在其中声明的模式相匹配的文件和目录。这有助于避免不必要地将大型或敏感的文件和目录发送到守护程序,并可能将它们添加到公共映像中。

要排除与构建无关的文件而不重构您的源存储库,使用一个.dockerignore文件。该文件支持类似于.gitignore文件的排除模式。

对多行参数排序

只要有可能,通过按字母数字顺序对多行参数进行排序来简化以后的更改。这有助于避免包的重复,并使列表更容易更新。这也使得 PRs 更容易阅读和审查。在反斜杠前加一个空格也有帮助。

这里有一个来自 Docker Hub 上 Docker 的buildpack-deps图像的例子:

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \ 
  subversion \
  && rm -rf /var/lib/apt/lists/* 

分离应用程序

依赖于其他应用程序的应用程序被认为是“耦合的”在某些情况下,它们托管在同一主机或计算节点上。这在非容器部署中很常见,但是对于微服务,每个应用程序都应该存在于它自己的容器中。将应用程序解耦到多个容器中使得水平伸缩和重用容器变得更加容易。例如,一个解耦的 web 应用程序堆栈可能由三个独立的容器组成,每个容器都有自己唯一的映像:一个用于管理 web 应用程序,一个用于管理数据库,一个用于内存缓存。

将每个容器限制为一个进程是一个很好的经验法则。使用你最好的判断来保持容器尽可能的干净和模块化。然后,如果容器相互依赖,您可以使用 Docker 容器网络来确保这些容器可以通信。

尽量减少层数

只有RUNCOPYADD指令创建层。其他指令创建临时中间映像,最终不会增加构建的大小。在可能的情况下,只将您需要的工件复制到最终图像中。这允许您在中间构建阶段包含额外的工具和/或调试信息,而不会增加最终映像的大小。

利用构建缓存

在构建映像的过程中,Docker 遍历 Docker 文件中的指令,按顺序执行每个指令。在每条指令中,Docker 都会在其缓存中搜索一个现有的映像来使用,而不是创建一个新的副本映像。这是 Docker 遵循的基本规则:

从已经在高速缓存中的父映像开始,将下一条指令与从该基础映像派生的所有子映像进行比较,以查看它们中是否有一个是使用完全相同的指令构建的。否则,缓存将失效。

在大多数情况下,只需将 docker 文件中的指令与子映像之一进行比较就足够了。但是,某些说明需要更多的检查和解释。

对于ADDCOPY指令,检查映像中文件的内容,并为每个文件计算校验和。这些校验和不考虑文件的最后修改时间和最后访问时间。在缓存查找期间,校验和会与现有映像中的校验和进行比较。如果文件中的任何内容(如内容和元数据)发生了更改,缓存将失效。

除了ADDCOPY命令,缓存检查不查看容器中的文件来确定缓存匹配。例如,当处理一个RUN apt-get -y更新命令时,不检查容器中更新的文件来确定是否存在缓存命中。在这种情况下,命令字符串用于查找匹配项。

一旦缓存失效,所有后续的 Dockerfile 命令都会生成新的图像,并且缓存不会被使用。利用您的缓存包括将您的图像分层,以便只有底层经常变化。您希望您的RUN步骤在 docker 文件的底部变化频繁,而变化不频繁的步骤应该在顶部排序。

优化 CI 管道中的 Docker 映像构建

到目前为止,我一直专注于优化 Docker 映像构建,同时从代码和 Docker CLI 构建的角度介绍了与此过程相关的概念。将这些优化策略实施到 CI 管道中并没有太大的不同,CircleCI 有一个特定的 Docker 构建优化,它将显著加快您的自动化 Docker 构建工作。

首先,前面几节中提到的所有优化概念都适用于在您的 CI 渠道中实现。尤其是缓存。如果 docker 文件有变化,利用缓存仍然是减少构建时间的最佳方式。

作为您 CI 渠道的一部分,这是如何运作的?当使用 Docker executor 作为构建作业的运行时,您可以利用一个名为 Docker 层缓存(DLC) 的特性来加速这些构建。

当构建 Docker 映像是 CI 流程的常规部分时,DLC 是一个很好的功能。DLC 将保存在您的作业中创建的图像层。DLC 缓存作业期间构建的任何 Docker 图像的各个层,然后在后续 CircleCI 运行中重用未更改的图像层,而不是每次都重建整个图像。

您的 docker 文件从提交到提交的变化越少,您的映像构建步骤将运行得越快。DLC 可以与机器执行器和远程 Docker 环境(setup_remote_docker)一起使用。值得注意的是,DLC 仅在使用docker builddocker compose或类似的 Docker 命令创建您自己的 Docker 映像时有用,它不会减少所有构建启动初始环境所需的挂钟时间。如果你有兴趣了解更多关于 DLC 的知识,你可以在我们的文档中阅读。

欲了解更多 Docker 内容,请参见CI/CD 管道 Docker 使用指南

摘要

在这篇文章中,我介绍了构建 Docker 映像的优化技术。提供的构建建议将作为高效开发 Docker 映像的指南。这些构建建议极大地加快了 CI 管道的速度。

大多数人不需要构建自己的自定义映像。在 CircleCI,我们建立了一个由 CI 优化 Docker 映像组成的车队,用于您的 CI 管道。在宣布我们的下一代便利图像中,阅读我们关于图像开发的决定:更小、更快、更确定的

感谢你关注这篇文章,希望你觉得有用。请随时在 Twitter @punkdata 上寻求反馈。

编写第一个 orb | CircleCI 的技巧

原文:https://circleci.com/blog/tips-for-writing-your-first-orb/

在我的上一篇文章中,我回顾了我构建第一个 orb 的过程,并解释了为什么它对于那些想为开源社区做贡献的人来说是一个如此好的选择。在这篇文章中,我想总结一下我在编写 orb 时学到的最重要的东西,给那些想尝试一下的人一些指导(并且我真的推荐你做)!

那么,准备好为那个可爱的开源贡献酱写一个牛逼又受欢迎的 orb 了吗?请记住这些注意事项:

1。解决问题:与编程、开发和发明中的任何事情一样,主要目标是解决问题或改善用户体验。有时,这可能仅仅意味着将“这个东西”连接到“那个东西”(更好的说法是集成)。我们的 AWS CLI 和 S3 orb 是将服务实现为 orb 的一个很好的例子。

通过 orbs,您最喜欢的 CLI 工具或 API 可能会变得更容易安装和使用。您甚至可以实现原本不存在的功能(目前还没有!)不带宝珠。

在我的案例中,我们的愿望是提供更加模块化和更好集成的 Slack 通知。我们的许多用户依靠 Slack 通知来接收 CircleCI 上运行的成功或失败的作业的更新,这是一个重要的工具,可以了解是否有任何事情阻碍了您的开发。

2。给用户自由:一个伟大的球体的美丽在于简单实用。在创建自己的 orb 时,重要的是要在使工具易于使用和保持足够通用以适应大多数用户需求之间找到平衡。终端用户有一种很好的方式来使用工具和产品,这种方式你可能做梦也想不到。使你的 orb 通用和可扩展意味着它可以为更多的人使用。

再次指向 Slack orb,其中一个参数是发送的消息的颜色。默认情况下,邮件将包含一个灰色的外边框。改变颜色的选项允许用户定制他们的体验:他们可以选择区分不同类型的问题,或者使用不同的颜色来表示不同的项目或团队。这完全取决于用户。

3。创建一个开发管道:利用 CircleCI 中的工作流不仅可以像前面提到的那样 lint 和发布 orb,还可以在多个用例场景中测试它。就像任何其他软件一样,我们希望能够在进行变更时测试我们的代码——因为变更有时意味着破坏。在启动之前知道您的代码工作正常,这为您提供了创建一个受欢迎的、值得信赖的 orb 的最佳机会,它将为用户提供价值而不会导致任何错误。

远程团队有效结对编程的工具| CircleCI

原文:https://circleci.com/blog/tools-for-effective-pairing/

今年我已经做了大约 150 个电话屏幕。把你的工程组织翻倍意味着要和很多人谈,我是说很多。我和他们谈论他们的工作历史,他们的软件哲学,以及帮助实现高质量软件交付的过程。这经常求助于过程来帮助实现协作编程范例,并最终配对。不可避免地,我会听到这样的话,“但是,你是完全遥远的!”以及“你如何共享键盘?”

这类问题总是让我困惑。我是一个从未在同处一地的团队中工作过的人,我看到过结对在一个大多数人都在偏远地区的工程部门中取得成功。通过我的谈话,很明显,有一批开发人员认为结对是有效的,但是在远程团队中无法实现。因此,我认为分享 CircleCI 的计划和定价团队成功配对的技巧和诀窍是有益的,circle ci 是一个 100%远程团队,几乎 100%的时间都进行配对。

工具作业

作为远程配对的抑制因素,最常提到的是工具。"没有连接到显示器上的两个键盘,你怎么配对?"人们问我。事实证明非常容易。

当我问我们的计划和定价团队什么工具曾经有效时,答案非常简单:主要是屏幕共享,有时是终端共享的 tmate 。对于这个问题,我最喜欢的回答是“等等,除了屏幕共享,你还有什么?”?比如全 AR 体验?Lol。”

团队能够有效地配对,一个人编码,而另一个人跟随,给出建议,讨论实现,查找资料,并且是一个参与的副驾驶。屏幕共享是一个非常合适的工具。

Tmate 可以很好地实现远程终端共享,但它对浏览器开发不是很有用,需要每个参与者熟悉其他人的环境,并且排除了使用 IntelliJ 或草书等 ide 的人。因此,虽然它在某些情况下很有用,但是作为一个由 vim、emacs 和草书用户组成的团队,做大量的前端工作,它的效用最终相当有限。

配对范例

在工具化之后,我最感兴趣的是使结对最有效的范例。是驾驶员<>导航员吗?一个人写测试,另一个人让他们通过吗?答案再一次比那更简单:做对双方都有意义的事情。

很多时候,只是一次交谈。两个忙碌的队友之间关于最佳前进路径、命名函数的最有利方式、是否重构的争论。有时这对搭档做 TDD,有时不做。有时一个人开车,而另一个人导航,有时这更像是一个学习机会,因为更有经验的人带着另一个人参观代码库,帮助他们获得上下文和熟悉度。它是关于什么最适合那一对。

总的来说,在我收到的所有回答中,我始终认为只有一点:这是关于参与度的。结对不是一个人编码,而另一个人浏览 Reddit,或者回应 Slack。它是关于两个人一起合作解决问题。只要这两个人都致力于解决这个问题,他们所使用的特定范例就远没有他们所创造的协作环境重要。

然而,即使我们有良好的意愿,有一个高度积极的团队来配对和创造一个协作的环境,我们仍然经历了一些困难和问题,迫使我们提出创造性的解决方案。

异步配对

在完全远程的组织中,配对的最大障碍之一是处理时区。在大约 6 个月的时间里,Plans 团队让人们处于 UTC+7、UTC+8 和 UTC-9 时区,总共有 17 个小时的时差。然而,该团队仍然设法配对。他们是怎么做到的?

每天,团队都会在我们在美国的队友和我们在东京的队友之间进行交接。这两个人会在西海岸一天的最后几个小时里配对,把上下文转移到我们的日本同事身上。交接完成后,他会继续工作,推起他的树枝,并在最后留下详细的笔记。第二天早上,这两个人会从他停下的地方继续工作。

这种实践允许团队继续保持高度协作的范例,甚至跨越海洋。

配对随机化

团队经历的一个问题是团队中不均衡的配对。这是由于不知道谁可以自由配对,以及“配对婚姻”,即两个人一张接一张地连续配对。为了防止这种情况,研究小组实施了配对随机化。

每天开始时,在站立时,团队通过各种过程(如自动脚本和“幸运轮”)随机挑选配对团队遵循一些约束,以确保这不会造成干扰:

  • 每张票至少要有一个人保持一致,所以上下文会从一天延续到下一天。
  • 如果有一个好的理由不改变这一对,团队就不改变。这方面的一个例子是研究票,其中某些人有必要的背景来完成任务。

然后,两人一组协调制定一个对他们来说有意义的时间表。这降低了结对的障碍,并增加了团队间的知识共享。

这也有一些有趣的副作用,比如团队使用的配对脚本,如下图所示:

结论

成为一个远程工程组织并不意味着你必须是一个人单独工作的组织。配对在远程环境中与在同处一地的环境中一样有效,并且具有同处一地的配对所没有的一些额外的好处,例如对抗社会孤立。最后,如果你想做点什么,只需要接受和一些创造性的思考。如果你想让远程配对工作,希望这些实践能给你提供一个有用的跳板。

5 大 CI/CD 最佳实践| CircleCI

原文:https://circleci.com/blog/top-5-ci-cd-best-practices/

利用这些 CI/CD 最佳实践优化您的渠道

对于工程团队来说,CI/CD 是改善他们开发周期的关键。CircleCI 致力于帮助我们的客户优化他们的管道,以简化生产交付。

软件开发团队的顶级 CI/CD 策略

https://www.youtube.com/embed/Is3U2a_TOF4

视频

如果你的团队重视加快你的上市时间,承诺尝试这 5 个最佳实践。这些是 CircleCI 为减少你的开发周期时间和总体上改进你的 CI/CD 过程而提出的建议。

CI/CD 最佳实践

  1. CI/CD 并不新奇——它是所有开发团队的需求
  2. 充分利用开发人员宝贵的时间
  3. 保护您的管道
  4. 维护跨管道的测试覆盖率
  5. 让受欢迎的球进入你的舵手室

CI/CD best practices

CI/CD 最佳实践 1: CI/CD 并不新奇——它是所有开发团队的需求

组织发现他们的开发周期对于提高代码生产质量是多么重要。在我们的 2022 年软件交付状态报告中,我们看到一些组织脱颖而出:与行业中的其他组织相比,他们正在更快、更成功地进行规模化建设。通过我们的分析,我们发现这些表现最佳的团队在 4 个关键基准上都有所提高。

CI/CD 最佳实践 2:充分利用开发人员宝贵的时间

既然您已经知道如何使用度量基准来优化您的管道,我们的下一个建议是最好地利用宝贵的开发时间。您可以通过以下方式做到这一点:

  • 重用配置
  • 使用缓存策略
  • 实现测试拆分

重用配置

一个很好的优化方法是永远不要写两次相同的配置。使用 CircleCI 的 orbs ,YAML 配置的可重复使用的软件包,它自动化重复的过程并加速项目设置。

私有 orb 允许您在组织的范围内跨多个项目共享配置。

还有许多开源 orb 可供使用:

  1. 开发者中心(Developer Hub)——扫描我们的公共注册表,寻找开源 orb,这些 orb 是可重用的配置包,可以自动执行重复的过程,加快项目设置。
  2. orb 开发套件-orb 开发套件有助于简化工程师创建自己的 Orb 的整个 Orb 创作过程。开发人员现在可以从 CLI 生成一个全功能的 orb 示例项目。

使用缓存策略

优化项目构建和工作流的一种简单方法是实现特定的缓存策略,允许重用以前构建和工作流中的现有数据。无论是选择包管理应用程序还是手动改进缓存,缓存都是提高作业速度的最有效方法之一。

在我们的文档中了解有关如何优化构建和工作流的更多信息。

实现测试拆分

最后,开发人员可以通过利用测试分割来加速他们的项目构建。自动测试分割通过将一套测试自动分割到同一工作的多个实例上——或者更确切地说,是一系列并行运行的测试环境上,从而缩短了反馈循环。

了解如何根据您的应用需求在此显著减少您的测试时间。

CI/CD 最佳实践 3:保护您的管道

使用符合行业安全标准的 CI/CD 平台与有意采取措施保护您的管道一样重要。本建议旨在确保您使用我们在 CircleCI 平台上提供的附加产品安全功能来保护您的管道:

私人环境

私有环境变量使开发人员能够安全地存储秘密,即使是他们的公共项目。秘密屏蔽增加了另一层安全性,因为它在作业输出中隐藏了项目和上下文中的环境变量。

每个工作流有多个上下文

为了进一步限制访问,每个工作流拥有多个上下文允许开发人员通过让环境变量包含特定域中的值而不是给定的工作流来限制对环境变量的访问。当需要访问工作流中的特定上下文时,这提供了更大的粒度,从而提高了组织的安全性。

灵活的管理控制

灵活的管理控件允许您管理跨项目的用户访问。通过在整个组织内标准化 CI 平台的可访问性和使用,您可以维护应用程序的安全性。保护您的管道与确保您的工作流程快速运行同样重要。

要了解更多有关 CircleCI 产品特性的信息,请查看我们的CI/CD 安全性终极指南和 DevSecOps 博文。

CI/CD 最佳实践 4:维护整个管道的测试覆盖率

使用 CircleCI,开发人员可以以合作伙伴 orb 的形式集成一些最好的开发工具。

orb 是可重用的配置包,有助于加快项目设置。CircleCI 的合作伙伴提供了多个类别的 orb,从代码分析到部署,但我们希望找出那些有助于保持整个管道测试覆盖率的 orb。

考虑将这些流行的合作伙伴 orb 集成作为您开发周期的一部分:

  1. Codecov 将您的覆盖报告上传到 Codecov,无需处理复杂的配置。快速获得您的应用程序的覆盖率结果,这样您就可以更轻松地呼吸,并满怀信心地提交您的代码。
  2. Cypress 在 Cypress 仪表板上记录结果,并以并行模式进行负载平衡测试。运行端到端浏览器测试,无需花费时间配置 CircleCI。
  3. Sonarcloud 在问题出现之前,检测您的存储库中的错误和漏洞。
  4. 工作包将您的测试套件的 LCOV 覆盖数据发布到工作包. io 中,用于分析、变更跟踪和通知。在运行拉式请求构建时,将在 PR 中添加一条注释,详细说明如果合并将如何影响覆盖率。
  5. Snyk 查找、修复并监控您的应用程序依赖项和 Docker 映像中的已知漏洞。

CI/CD 最佳实践 5:让这些受欢迎的球进入你的舵手室

我们想把最好的留到最后:我们最好的圆形球体。包含这些 orb 是增强您的 CI/CD 实践策略的一个很好的方法:

  1. Node 轻松安装 Node.js 及其包管理器(npm,yarn)。最重要的是,安装软件包时默认启用缓存。
  2. Slack 跨所有 CI/CD 渠道实施基于事件的通知。利用内置的消息模板或 Slack 的可视块工具包构建器,根据您组织的需求创建和定制通知。加速反馈循环,以便更快地修复错误和交付产品。
  3. AWS-CLI 安装并配置 AWS 命令行界面(awscli)。
  4. Python 为 Python 编程语言启用常见的 CircleCI 任务。
  5. 轻松地自动缓存和安装您的 Ruby Gems,运行并行 Rspec 测试,或者直接安装 Ruby。

阅读我们的 2022 年软件交付状态报告,了解我们从全球软件、医疗保健、零售、金融、房地产和媒体等领域的 50,000 多个组织中收集到的关于构建优秀软件开发团队的最佳实践。

软件即服务的六大优势| CircleCI

原文:https://circleci.com/blog/top-6-adv-of-saas/

在现代企业面临的诸多挑战中,管理远程工作人员几乎是重中之重。在当今高度竞争和全球化的商业环境中,让分散的团队保持组织、参与和快乐是至关重要的。提供可靠的、安全的、有成本效益的软件工具只是这个日益复杂的难题的一部分。幸运的是,软件即服务(SaaS)模型为 IT 团队提供了一种远程友好的替代方案来实现和维护内部企业软件。

软件即服务通过云随时随地向用户交付软件应用。实现、集成、维护和升级软件的所有难题都属于 SaaS 提供商,而不是用户的 IT 组织。这可以极大地提升承受着过多需求和过少资源负担的 IT 部门。

使用 SaaS 来满足您的企业软件需求有很多好处。在本文中,我们考虑前六名。

缩短受益时间

在实施和集成新的 IT 解决方案时,时间可能会拖延。小 IT 项目变成大项目。甚至在您宣布上线日期之前,应用程序通常需要复杂的定制。

SaaS 应用程序通常基于云,随时可用。与内部应用不同,SaaS 应用驻留在云中,员工可以立即访问和使用它们。

使用 SaaS,可以节省大量时间。企业应用不再需要几天、几周甚至几个月才能上线,现在只需几个小时就可以上线。除了更少的幕后准备和过渡工作之外,如此短的可用时间的好处是显而易见的:等待新应用产生影响并开始产生回报的时间更少。

降低成本

SaaS 在许多方面降低了你的成本。拥有许多订户的供应商可以提供现成的 SaaS 应用程序。庞大的用户群有助于 SaaS 供应商保持低成本。另一方面,独立软件安装在内部,会导致硬件和软件成本显著增加。基本软件成本是不分摊的,这意味着它们会完全转嫁到您身上。

除了规模经济之外,SaaS 还能帮助您避免其他维护成本,如维护、补丁安装和配置控制,这些都是 SaaS 供应商的责任。此外,您的组织不需要花费时间和金钱来升级功能和修补软件的易受攻击区域,以抵御紧急恶意软件威胁和其他安全漏洞。

随着您需求的增长,SaaS 产品可以无缝地容纳多个位置的更多用户,而不会超出您的预算。

可扩展性和集成

在更多位置容纳更多用户的能力表明了使用 SaaS 的另一个关键优势:可伸缩性和集成

随着越来越多的组织雇佣员工,越来越多的人开始参与商业运作。基于云的 SaaS 应用程序简化了对重要软件和数据的访问。它是可扩展的,并且能够容易地集成到组织的新的和扩展的领域中。

替代方案(内部安装的模型)不太容易扩展。IT 必须提前建立新的服务器、软件、布线、培训和连接,这使得可扩展性和完全集成变得更加困难。

SaaS 提供了一个战略优势。它使组织能够轻松、快速地扩展,并且没有非基于云的企业级软件带来的难题。

不再升级

升级可能是软件管理的致命弱点。DevOps 现在是 IT 中的一种常见做法,它允许组织在扩展或变更运营的同时实施开发。然而,从一个软件版本升级到另一个版本仍然是一个棘手的过程。对于 SaaS,这一负担通常落在 SaaS 提供商身上,而不是贵组织的 IT 部门。

SaaS 还减少了与解决恶意软件和其他安全漏洞相关的工作量。SaaS 提供商的 IT 团队可以快速识别和修补新出现的安全威胁。此外,提供商引入和开发新功能以跟上技术进步的步伐,将用户从这一负担中解放出来。

在 SaaS 模式中,配置控制成为 SaaS 提供商的责任。这将解放您的组织,使其专注于运营和战略目标,而不会在管理和实施软件升级方面陷入困境。

易用性

用户在使用新软件应用程序时遇到的一个常见挑战是它的学习曲线。熟悉新的界面和特性需要时间,并且经常会导致挫败感。

此外,开发人员必须做出许多调整来满足用户的需求。用户经常提交帮助台票证来修改软件。

有了 SaaS 模型,供应商的开发人员已经解决了使用该软件所需的许多挑战和调整。当多个企业订阅一个 SaaS 产品时,开发人员已经收到了常见的投诉和反馈并采取了行动。因此,当您的组织插入一个已建立的 SaaS 产品时,您可以确信它已经可以使用了。如果您在本地购买并安装软件,则不一定如此。

除了调整功能以满足用户需求,还有培训。大多数 SaaS 型号都有一个培训组件,它伴随着许可证订阅,并解决用户将会遇到的许多问题。SaaS 提供商提供教程、视频和帮助台,因此用户可以从 SaaS 提供商处获得自助帮助,而不是自己组织内的 IT 部门。

将培训方面与 SaaS 提供商维护的久经考验的软件应用程序结合起来,您会发现大多数 SaaS 产品对于您的团队成员来说都很容易融入他们的工作流程。

买之前先试试

每当你采用一个新的软件应用程序,都会有一些风险。用户可能不喜欢。特征可能被错误地传达。应用程序的实用性可能并不完全符合用户的预期。

大多数 SaaS 产品都有一段时间的免费试用。这样您就可以进行试驾,看看它的属性和功能是否符合您的需求。

即使应用程序没有免费试用期,SaaS 也很容易在小范围内进行尝试。如果一个概念验证项目进展顺利,你可以更有信心,它将为更大的推广工作。

结论

云计算和在云中运行的 SaaS 模式是新的常态。分布式团队可以利用基于云的 SaaS 产品在世界各地进行交流和协作。点击鼠标和几个按键,用户立即受益于 SaaS 的产品。IT 团队可以自由地专注于更重要的运营目标,而不是对有问题的内部安装进行故障排除。

用户可以在购买之前试用 SaaS 的应用程序。他们还可以快速上手,避免仅仅为了入门而提交帮助台票证。系统错误已经被检查过了,用户也不会因为升级或者担心什么时候可以访问软件而陷入困境。有了 SaaS,扩展和集成变得又快又容易。

由于基于云的模式的效率,为 SaaS 产品签署许可席位的大型组织降低了总体成本。他们的用户可以在几个小时内,而不是几天、几周或几个月内利用所有的软件功能。

现在,您对 SaaS 产品的优势有了更多的了解,在购买新工具或升级旧软件时,您可以更好地考虑这种模式。

云计算的 8 大用途| CircleCI

原文:https://circleci.com/blog/top-8-uses-cloud-computing/

云正在被广泛采用。对于许多组织来说,云计算已经成为跨分布式团队进行交流和协作的不可或缺的工具。无论你是在亚马逊网络服务(AWS),谷歌云,还是 Azure。云可以降低成本,提高灵活性,优化资源。

如果你的职业生涯是在满是电缆窝的嗡嗡作响的服务器机房度过的,你可能会想知道这些大惊小怪是为了什么。在本文中,我们将研究云计算的八大用途,供您考虑。一旦你更多地了解了云计算的功能,你可能也会成为它的粉丝。

1.基础设施即服务(IaaS)

云的一个最明显的优势是,您不需要为云中托管的应用程序配备自己的硬件。您可以启动虚拟机Kubernetes 集群、域名系统(DNS)服务、存储、队列、网络、负载平衡器和大量其他服务,而无需将另一台巨型服务器拖到您的数据中心。

降低成本是转向 IaaS 最常见的理由。你不需要自己的服务器;您只需为您需要的云服务付费。当您不安装和维护自己的硬件时,还可以节省开销。

更好的是,您可以使用脚本或基础设施作为代码(IaC) 来创建和配置这些云服务。您可以自动创建完整的环境,并通过填充一些变量轻松地克隆环境。虽然在某种程度上您可以在本地服务器上做到这一点,但在云中更容易。

一个潜在的陷阱是这些服务中的大部分仍然需要维护。例如,您仍然负责安装软件,并保持您的云虚拟机和软件都是最新的。

此外,虽然您可以完全自定义自己的虚拟机,但您只能在云中选择一组预定义的虚拟机。虽然你有一些选择,但你可能找不到你需要的。

总而言之,IaaS 可以节省您的时间和金钱,但是如果您只是将一些服务器迁移到云虚拟机,那么您的钱可能不会有这么大的价值。在决定 IaaS 是否适合您时,请检查您的需求(如可扩展性)和费用(如开销)。

2.平台即服务(PaaS)

PaaS 是 IaaS 的升级。IaaS 将您的硬件迁移到云中,PaaS 则更进一步,还将您的大部分维护工作迁移到云中。

例如,假设您需要运行一个服务和一个数据库。VM 可以代替服务器,但是您仍然需要自己安装服务和维护数据库。PaaS 解决方案创建了运行您的服务的资源,以及运行数据库的资源。只需点击几个按钮(或 IaC ),您就拥有了一个服务和数据库。没有必要安装或更新它,因为 PaaS 会为您处理这些。

有了 PaaS,您对运行服务的系统和硬件的控制就更少了。不过,仍有大量计划可供选择,因此计算能力可能不是问题。

但是,请记住,一些应用程序需要低级别的系统访问,这在大多数 PaaS 解决方案中是不可能的。毕竟,您无法访问系统或硬件。此外,如果您的应用程序依赖于安装在服务器上的特定程序,PaaS 不适合您。

虽然开始使用 PaaS 解决方案很快也很容易,但是对您可以运行的内容有一些限制。当您需要在运行您的应用程序的服务器上安装特定软件时,也可以考虑切换到 PaaS 解决方案。

3.无服务器

一种内部不存在的云产品是无服务器产品。无服务器有点用词不当,因为它肯定涉及到服务器。然而,从一个用户的角度来看,就像程序员或管理员一样,没有服务器可以与之交互或管理。

您只需创建一些带有触发器的(短期运行的)代码,比如计时器、HTTP 请求或队列中的消息。每当代码触发时,它就被放在由云提供商管理的服务器上。代码执行后,虚拟服务器消失。

无服务器具有成本效益(通常是免费的),并且易于扩展。因此,如果您需要处理包含数百万条消息的队列,您可以在不同的、自动启动的实例上并行处理这些消息。

当然,缺点是您完全无法控制代码如何以及何时执行。无服务器通常也有一些启动延迟,但它仍然是你的云工具箱中的一个强大工具。

4.混合云和多云

大多数严重的环境,尤其是在成熟的企业中,仍然需要访问本地资源,如文件或服务。借助混合云模型,您可以允许云资源通过虚拟网络等方式安全地访问您的本地服务器。这使得云成为您内部系统的延伸。

扩展云的另一个选择是多云环境。维护多云环境有两种方法:

  1. 在一个云平台(比如 Azure)上运行一些服务,在另一个云平台(比如 AWS 或者 GCP)上运行其他服务
  2. 在多个云中同时运行相同的服务

在多个云平台上运行相同服务的好处是,当一个云出现问题时(不幸的是,这种情况仍然会发生),另一个云可以接管。这样,您的服务就高度可用。多云的另一个好处是可以避免供应商锁定。

当然,缺点是运行两个云会使您的成本翻倍。在考虑多云时,您需要考虑冗余需求和控制预算需求。

5.测试和开发

云计算非常适合快速启动开发、测试、验收和生产(DTAP)环境。您可以使用 IaC 快速创建和配置云资源。要创建一个新环境,您只需要更改一些变量。

您可以从您的 DevOps 环境中自动运行您的脚本。您不再需要为额外的环境创建更多的虚拟机(或者更糟,购买服务器),然后希望它们完全反映生产环境。

您的云环境将始终保持不变。即使您为某些服务选择了较便宜的层,您也可以确信一切都会按预期运行。

6.大数据分析

云为大数据分析提供了大量解决方案。您可以使用各种数据仓库、数据湖以及提取、转换和加载(ETL)工具来存储和处理结构化或非结构化数据。

即使你还没有准备好对你的数据做任何事情,云平台也提供了无限的存储来收集你现在可能需要的一切。这包括客户购物习惯、搜索和网站导航路线、传感器数据、图像等等。随着源源不断的数据流入,云服务也随之扩展,因此您可以收集所有数据。

人工智能(AI)和机器学习(ML)的各种云产品可以帮助您在数据中找到模式,并从大量数据中产生商业价值。这些见解有助于推动业务增长、设备和成本优化、客户体验和营销改进。云服务上可用的计算能力可以帮助您实时分析数据,以快速做出业务决策。

云上可用的无限存储和计算能力是一个优势;然而,随着成本的上升,这也会变成一个不利因素。监控有助于控制这些成本。

7.云存储

你可以把数据和文件存储在云中,让它们在任何地方都可以被任何人使用。这尤其有助于员工和承包商在与健康相关的停工期间在家工作,以及全球办事处在全球范围内协调他们的工作。

云提供商通常会冗余地存储文件,因此您可以自动进行一些备份。您可以从本地环境中同步文件,确保在设备发生故障、火灾或自然灾害时始终有备份。您只需为您使用的存储付费,并且您的存储会随着业务的增长而扩展。

云存储快速、可访问且安全。大多数云漏洞是由云用户在设置服务时犯的错误引起的,例如未能锁定具有用户权限的容器,而不是云平台不安全。

8.数据备份和灾难恢复

备份数据可能是一件痛苦的事。默认情况下,云提供商备份许多资源,您可以相对容易地添加额外的备份服务。

您可以为各种服务选择备份策略,并将这些备份保存在您所在地区、洲甚至世界各地的不同地方。例如,您可以将关键文件存储在位于三个不同大洲的三个数据中心的三个服务器上,从而为您提供 3 x 3 x 3 个备份,或同一文件的 27 个副本。

恢复备份可能就像选择要恢复的备份并单击一个按钮一样简单。

这使得灾难恢复快速且经济高效。通过切换到另一个区域的另一个数据中心,您可以快速缓解一个数据中心甚至整个区域的灾难。一些服务,如数据库,有默认的故障转移区域,通常在主主干上。这使得数据传输很快。这些故障转移实例甚至可以用作只读副本,以最大限度地减少请求延迟。

结论

云在很多方面都很棒。您可以使用 IaaS、PaaS 和 IaC 快速增加额外的资源并完善环境。您只需为您使用的东西付费,这可能比您的内部环境更便宜。

云的真正力量不在于它的资源可能更便宜。它减少了开发时间和环境之间的错误,它对 DevOps 环境有很好的 IaC 支持,并且它需要更少的维护,特别是对于 SaaS 解决方案。

云可能无法满足您团队的所有需求。对于一些组织来说,混合解决方案效果最佳。但是无论您的用例是什么,既然您已经了解了云的潜力,那么您就已经准备好为您自己的组织利用云计算的力量了。

要了解如何使用云计算来加速工程团队的构建、测试和部署流程,请立即注册参加 CircleCI 免费试用

使用 Rollbar 和 CircleCI 进行部署后跟踪错误

原文:https://circleci.com/blog/tracking-errors-after-deployments-with-rollbar-and-circleci/

今天的博文是由 Rollbar 技术内容的首席编辑杰森·斯科沃龙斯基撰写的两部分系列文章的第一部分。Jason 的职业生涯始于亚马逊的一名软件开发人员,现在他乐于成为最新技术的开发者倡导者。

应用程序错误会给客户带来令人沮丧的问题,最终可能导致失去他们的信任和业务。有经验的开发人员知道一个关键的产品问题是什么样的,他们会花几分钟甚至几个小时来诊断一个棘手的问题。当几个开发人员同时进行变更和部署时,就更难诊断了。

Rollbar 是一个错误监控解决方案,它可以告诉您在部署后发生了什么错误,并向您显示可能导致这些错误的部署和代码更改。它与您的持续集成和交付(CI/CD)系统集成,以跟踪部署何时提升到生产环境。当一个新的错误发生时,它会像 GitHub 一样在您的源代码库中查找已部署的版本,以确定哪些代码被更改以及是谁更改了它。这将帮助您更快地缩小由于代码错误导致的错误。

在本帖中,我们将通过一个例子展示 Rollbar 如何与 CircleCI 和 GitHub 集成来实现这一点。只需点击几下鼠标,您就可以看到可能导致错误的代码更改。

rollbar2.png

简单的例子

Rollbar 集成了大多数主流编程语言、框架、CI/CD 解决方案和源代码库。在这个例子中,我们将使用 CircleCI 和 Angular,这样我们可以演示 JavaScript 源代码映射。我们的例子是免费提供的,运行或修改都很简单。源代码可以在 Github 上的 angular-circleCI 库中找到。

如果您的应用程序使用不同的语言或框架,您仍然可以遵循这些说明,但是最相关的部分是将部署 API 调用添加到滚动条。这个 API 调用对于任何语言都是一样的。

打开您的 CircleCI 配置

首先,我们将向您展示如何为我们的示例应用程序设置 CircleCI。然后,我们将向滚动条发送部署通知,以便它可以跟踪何时进行部署。

我们将假设您已经拥有 CircleCI 的帐户。如果没有,注册然后配置源代码库比如 GitHub 或者 BitBucket。

rollbar3.png

CircleCI 在名为。项目目录下的 circleci。它应该包含一个名为 config.yml 的文件。在我的例子中,配置文件部署到一个 S3 桶,因为 Angular 示例是一个静态站点。根据您部署应用的位置,您的应用会有所不同。

# Deploy our app to the S3 by copying the files
- run:
     name: Deploy to S3
     command: |
     aws --region us-east-2 s3 sync dist s3://rollbar-example/ --delete     --acl public-read 

向滚动条发送部署通知

现在我们可以在成功部署后通知滚动条,这样它就可以跟踪已部署的版本。在部署脚本后插入下面的滚动条部署通知。确保用您自己的访问令牌、用户名等替换示例变量。

- run:
     name: Deployment notification to Rollbar
     command: |
    # Deployment script
      …
    # Notify rollbar
     curl https://api.rollbar.com/api/1/deploy/ \
     -F access_token=2a208f30fa1b4f0183adb694c4432038 \
     -F environment=production \
     -F revision=$CIRCLE_SHA1 \
     -F rollbar_username=RollbarExample \
     -F local_username=$CIRCLE_USERNAME \
     -F comment='Deploy with bug' 

access_token: 滚动条上的目标项目令牌。这个令牌是在滚动条上创建项目时生成的。

环境:部署服务的部署环境。我们可以配置不同的环境,例如开发、试运行和生产。

版本:部署的应用版本。这应该是存储库组件 ID。如果提供的版本是提交 ID,滚动条将创建一个到存储库提交源代码的链接。

local_username: 将构建部署到服务器的用户。

现在可以在滚动条上看到部署版本历史。它还可以识别每次部署所做的代码更改。稍后,我们将向您展示 Rollbar 如何使用它来识别这个部署中发生了哪些错误。

rollbar4.png

将滚动条 SDK 添加到您的应用程序中

在前面的章节中,我们学习了如何设置连续交付作业以及与 Rollbar 的集成。现在我们将学习如何集成 Rollbar 的 SDK 来监控应用程序错误。如果抛出了已处理或未处理的异常,应用程序将捕捉它们并通知滚动条错误细节。

第一步:

先决条件:

创建一个 roll bar https://rollbar.com 帐户。创建您的项目,并从通告程序列表中选择 Java。选择创建项目时生成的服务器端访问令牌。

第二步:

为项目中的 Rollbar-Angular 添加 package.json 文件中的依赖项。正如我们之前提到的,Rollbar 为许多其他语言提供了SDK。

"dependencies": {
   "rollbar": "2.2.8"
 } 

第三步:

用强制参数配置 app.module.ts 中的滚动条。你可以在 Javascript SDK 文档中了解更多关于这些参数的信息。记得在配置中添加您自己的访问令牌。

const rollbarConfig = {
  accessToken: 'f627d5e044a24b9987a23e54c5df352e',
  captureUncaught: true,
  captureUnhandledRejections: true,
  enabled: true,
  source_map_enabled: true,
  environment: 'production',
  payload: {
	server: {
  	   branch: 'master',
  	   root: 'webpack:///./'
	},
	client: {
  	   javascript: {
    	       code_version: versions.versions.revision
  	   }
	}
  }
}; 

代码版本应该与您正在部署的版本相匹配,并且通常设置为 git 提交的 SHA。服务器根帮助滚动条定位源代码的基本路径。它们允许 Rollbar 匹配您回溯到已部署版本的源代码中报告的错误。我们将在本系列的第二部分对此进行更深入的讨论。

第四步:

我们将使用rollbar包来通知滚动条错误。让我们从产生错误的 app.component.ts 开始。为了跟踪滚动条中的错误,我们需要用我们刚刚创建的访问令牌来配置它。为了测试它,我们可以用一个方法 sendError()触发一个错误,如下所示。当抛出异常时,manualHandle()方法将通过 rollbar.error(e)方法调用通知 Rollbar。

export class AppComponent {

 constructor(private rollbar: Rollbar) {

 }
 title = 'Angular 4 with Rollbar';
 errorMessage = 'Hello World, Error Message';

 sendError() {
   console.log('Introducing an error code here');
   throw new Error(this.errorMessage);
 }
} 

第五步:

现在,我们将测试刚刚组装好的所有部件。我们将在 CircleCI 上执行构建,运行应用程序,然后生成一个测试错误。

找到错误的根本原因

菜单选项卡上检查滚动条仪表板的错误记录,它将显示错误细节以及导致错误的源代码文件的链接。该错误将显示可疑的部署版本以及导致错误的代码。

rollbar5.png

调查错误原因的第二种方法是使用可疑部署选项卡。它显示了首次发现错误的修订,以及自上次部署以来对代码所做的更改。我们可以看到提交消息,表明引入了一个错误代码。

rollbar6.png

单击带有修订签名的链接会向我们显示一个差异,在这里我们可以看到可能导致此错误的源代码更改。

rollbar7.png

总之,我们已经成功地生成了一个错误,用 Rollbar 跟踪了错误,并用 Rollbar 快速地确定了问题的根本原因。

最近在其测试版特性实验室中推出的一个新特性 Rollbar 能够看到部署后发生的所有错误。这与从错误开始,然后识别部署是相反的。这有助于在部署后监控应用程序,以查看哪些新错误正在发生,哪些已重新激活,以及哪些已解决。下面,我们可以看到这个部署是在 2 小时前进行的,并生成了我们的“Hello World”错误。

rollbar8.png

结论 Rollbar 是一种跟踪生产中出现的错误的好方法。对于每个部署,它会告诉您发生了哪些错误,以及哪些代码更改是导致错误的原因。这可以大大加快您的故障诊断时间,这将使您的团队和您的客户都更高兴。

继续阅读我们博客系列的第二部分,了解如何自动识别哪些代码变更导致了错误。我们将深入研究源代码集成是如何工作的,包括 JavaScript 源代码图。

从 GitHub 操作工作流| CircleCI 触发您的 CircleCI 管道

原文:https://circleci.com/blog/trigger-circleci-pipeline-github-action/

如果您已经是 GitHub 用户,您可能知道 GitHub Actions 为您提供了强大的工具来提高软件交付生命周期的效率。行动可以对团队协作和流程简化产生影响。例如,您可以自动完成构建容器、欢迎新用户加入您的开源项目、管理分支或对问题进行分类等工作。

对正确的工作使用正确的工具很重要。将 GitHub 动作与 CircleCI 一起使用,可以让您自动化版本控制系统的各个方面,同时还可以受益于只有在 CircleCI 管道中才有的特性。想象一下,扩展你的 VCS 工作流,使之包括高级功能,如 SSH 调试测试分割、健壮的 CPU 和 RAM 选项以及 GPU 和 Arm 支持

例如,您可以使用动作进行团队协作和流程简化,同时使用 CircleCI 加速构建、测试和部署阶段。

A Workflow using both GitHub Actions and CircleCI

为了让 GitHub 上的任何事件都能更容易地启动 CircleCI Pipeline,我们的团队构建了 Trigger CircleCI Pipeline action,它现在可以在 GitHub Marketplace 上获得。

Trigger CircleCI Pipeline action listing in GitHub Marketplace

在本文中,我们将介绍几种方法,您可以将触发 CircleCI 管道操作合并到您的工作流中,以充分利用您的 GitHub automation 信用,同时受益于 CircleCI 世界级的持续集成能力。

触发 CircleCI 管道:一个 GitHub 动作,用于触发 CircleCI 中的工作流

使用 Trigger CircleCI Pipeline 操作,可以从给定分支或标记上的任何事件启动 CircleCI 管道。该操作会传递一组包含元数据的管道参数,这些元数据可用于控制 CircleCI 管道和工作流的流动。

触发 CircleCI 管道操作的一个潜在用例是每当开发人员打开一个 pull 请求时触发 CircleCI 管道。在 pull 请求上触发允许您测试 PR 中的更改,即使最后一次提交是在 PR 打开之前进行的(或者在任何时候将更改推送到打开 PR 的分支时触发构建和测试循环)。

Trigger CircleCI Pipeline 动作的另一个用例是每当在 GitHub 中创建、编辑或发布一个版本时,从 CircleCI 触发一个构建和部署。当 repo 所有者指定应用程序的新发布版本时,触发发布允许您构建和部署应用程序,例如将 Dockerized 微服务部署到 Kubernetes 集群或将二进制文件发布到 app store。

您可以在触发器 CircleCI 管道库的示例目录中查看这两个用例的完整实现,包括您的 CircleCI 和 GitHub 操作配置文件的模板。接下来,我们将探索一个使用触发器 CircleCI 管道操作在发布新版本时启动 CircleCI 工作流的示例。

如何使用 Trigger CircleCI Pipeline 操作从新版本中触发管道

设置 GitHub 操作以使用 CircleCI 的第一步是为所需的 CircleCI 管道创建 GitHub 操作工作流。您可以通过向./.github/workflows添加一个工作流 YAML 文件(我们将使用main.yml)来做到这一点。

在下面的例子中,您使用ontypes语法来指定在发布新版本时触发trigger-circleci动作。为该步骤选择一个定制的nameid,以便在 CircleCI 管道中提供额外的上下文元数据:

on:
  release:
    types: [published]
jobs:
  trigger-circleci:
    runs-on: ubuntu-latest
    steps:
      - name: <customize name>
        id: <customize id>
        uses: circleci/trigger_circleci_pipeline@v1.0
        env:
          CCI_TOKEN: $ 

接下来,创建一个名为CCI_TOKEN加密秘密,其中包含将用于触发管道的个人 API 令牌。我们建议将此设为机器用户

现在您已经设置了 GitHub 工作流,您可以修改 CircleCI 配置文件,以便在 GitHub 动作启动时运行。首先将管道参数定义添加到您的 CircleCI 配置中。当 GitHub 动作被触发时,该数据将被输入。

将以下内容添加到您的.circleci/config.yml文件的顶部。确保您指定了版本 2.1:

version: 2.1
parameters:
  GHA_Event:
    type: string
    default: ""
  GHA_Actor:
    type: string
    default: ""
  GHA_Action:
    type: string
    default: ""
  GHA_Meta:
    type: string
    default: "" 

该操作会生成几个管道参数:

  • GHA_Actor表示哪个用户触发了管道。
  • GHA_Action是工作流配置中分配的id
  • GHA_Event是触发管道的 GitHub 事件的类型。
  • GHA_Meta是可选的附加元数据参数,允许您使用输入参数指定附加元数据。

您可以使用此管道参数数据来有条件地运行工作流。在本例中,GHA_Event参数将用值release填充,您可以在 CircleCI 配置中使用一个 when子句来指定仅在 GitHub 工作流触发时运行的工作流:

jobs:
  release:
    docker:
      - image: cimg/node:lts
    steps:
      - run: npm install
      - run: npm build
      - run: npm publish

workflows:
  release:
    when:
      equal: [ "release", << pipeline.parameters.GHA_Event >>]
    jobs:
      - release 

这个配置片段指定了一个发布 npm 包的release作业和一个调用release作业的release工作流。在when子句中,您指定release工作流仅在 GitHub 工作流运行时运行,并用值release填充GHA_Event参数。

虽然这个例子演示了如何设置释放触发器,但是您可以使用相同的技术从任何可用的 GitHub 事件中启动 CircleCI 管道来触发工作流。要查看从 pull 请求的事件中触发 CircleCI 的完整设置,请访问示例 GitHub repo

如何防止 GitHub 和 CircleCI 中的作业重复执行

需要强调的是,GitHub Actions 与本地 CircleCI 集成并行运行。默认情况下,当存储库连接到 CircleCI 时,如果该项目配置中的工作流没有指定任何会阻止执行的条件或过滤器,则默认情况下工作流将在每个推送事件时执行。这意味着一个作业可能会意外运行两次,一次是在 CircleCI 的 push 事件上,另一次是在 GitHub 动作触发的其他事件上。

如果您依赖 GitHub 操作来提供所有 API 触发器,请确保您的每个 CircleCI 配置工作流都包含一个条件,限制其仅执行 GitHub 操作触发器,如下例所示:

workflows:
  # This workflow is set to be triggered conditionally, only when
  # the GitHub Action is triggered.
  # With no other workflows, normal push events will be ignored.
  when: << pipeline.parameters.GHA_Action >>
  test:
    jobs:
      - test 

在您的工作流声明中使用when子句允许您指定 CircleCI 应该只在指定的GHA_Action被触发时运行工作流,而不是在正常的推送事件之后。

结论

GitHub Actions 是一个非常有用的工具,可以自动执行各种版本控制过程,从检查特定版本的 Git 存储库到自动创建或合并 pull 请求,再到根据代码的更改更新 README 文件。在 Trigger CircleCI Pipeline 操作的帮助下,您可以将版本控制自动化节省时间的优势与只有最快、最安全、功能最丰富的持续集成平台才能提供的强大工作流加速相结合。

要将触发 CircleCI 管道操作添加到您的存储库中,请访问 GitHub Marketplace ,并将工作流语法复制到您的workflow.yml文件中。如果你的团队还没有使用 CircleCI 来快速自信地验证并向你的用户发布变更,今天就注册一个免费账户

触发数据库清理作业| CircleCI

原文:https://circleci.com/blog/triggering-jobs-for-db-clean-up/

随着时间的推移,大型应用程序会生成“陈旧”的数据。一年前的请求日志或已交付订单的派送附件的坐标不再相关。删除冗余或过时的数据可以保持数据库干净整洁。这些类型的数据应该定期存档或从数据库中删除。

在本教程中,我将演示一种清理数据库的策略。我们将使用 CI 管道中的自动调度作业清除 Node.js 应用程序中的日志数据。

先决条件

要完成本教程,您需要:

  1. JavaScript 的基础知识
  2. 系统上安装的 Node.js
  3. Postman for desktop 安装在您的系统上
  4. Heroku 或其他应用托管平台
  5. 一个的账户
  6. GitHub 的一个账户

当这些项目被安装和设置后,我们就可以开始教程了。

克隆日志演示项目

首先,您需要克隆 Node.js 项目。我们将使用一个简单的 Node.js API 应用程序,它有一个根端点和另外两个端点,用于获取和清理日志数据。通过运行以下命令克隆项目:

git clone --single-branch --branch base-project https://github.com/coderonfleek/db-cleanups.git 

克隆过程完成后,转到项目的根目录并安装依赖项:

cd db-cleanups
npm install 

接下来,运行应用程序:

npm start 

应用程序将开始监听默认端口3000。打开 Postman 并向http://localhost:3000/logs/fetch/50端点发出一个GET请求。这将返回一个包含 50 个日志数据的数组,每个数据包含一个事件及其发生日期。

Get Logs - Postman

请注意,有些日志已经超过一年了。

在本地测试logs清理

记住,这个项目还包含一个清理过时数据的端点。下面是server.js中清理端点的代码:

app.get("/logs/clean", async (req, res) => {
  try {
    let d = new Date();
    let currentYear = d.getFullYear();
    let today = new Date(`${currentYear}-01-01`);

    let total_records_to_delete = await req.db
      .collection("logs")
      .find({
        date: {
          $lt: today
        }
      })
      .count();

    const removedResult = await req.db.collection("logs").deleteMany({
      date: { $lt: new Date(today) }
    });

    res.status(200).send({
      message: `There are ${total_records_to_delete} to delete`,
      result: removedResult
    });
  } catch (error) {
    console.log(error);
    return res.status(500).send(error);
  }
}); 

清理端点的处理程序脚本获取当前年份。然后,它会删除早于当前年份的所有日志记录。

在 Postman 中运行这个端点(http://localhost:3000/logs/clean)。

Clean Logs - Postman

清除操作的成功消息显示,根据设置的标准,找到了 21 个要删除的过时行(该数字可能会有所不同)。删除操作的返回值在响应数据的result属性中被捕获。

为了最有效,应该按照对您的团队有意义的时间表定期调用像这样的清理端点。本教程的下一部分是使用计划作业自动清理。

创建项目的 Heroku 应用程序进行托管

我们需要部署应用程序,然后才能调度一个作业来调用它的清理端点。对于本教程,我们将创建一个 Heroku 应用程序。

转到您的 Heroku 仪表板,创建一个新的托管应用程序。点击新建,然后新建 app

New App - Postman

设置好应用程序后,记下应用程序的名称(在本例中为db-cleanups)。从账户设置页面获取 Heroku API 密钥。稍后我们将使用 API 键在 CircleCI 上设置环境变量

将项目连接到 CircleCI

将你的项目推送到 GitHub 开始。

注意 : 克隆的项目可能会抛出一个关于已经被初始化为 git repo(或者已经包含远程 repo)的错误。如果发生这种情况,运行rm -rf .git删除任何现有的git工件。然后用git init 重新初始化

在您提交了所有更改之后,转到 CircleCI 仪表板上的添加项目页面来添加项目。

Add Project - CircleCI

点击设置项目

Add Config - CircleCI

在设置页面上,点击 Use Existing Config 以指示您正在手动设置配置文件,并且不使用显示的示例。接下来,您会得到一个提示,要么下载管道的配置文件,要么开始构建。

Build Prompt - CircleCI

点击开始建造。这个构建将会失败,因为我们还没有设置配置文件。我们将在下一步中这样做。

在离开 CircleCI 控制台之前,您需要设置环境变量,以便将应用程序部署到 Heroku 上的两个环境(登台和生产)中。

管道页面,选择您的应用程序,然后点击项目设置

Project Settings - CircleCI

项目设置侧菜单中,点击环境变量,然后点击添加环境变量

Add Environment variable - CircleCI

添加这些变量:

  • HEROKU_APP_NAME:Heroku 应用名称项目(在本例中为db-cleanups)
  • 你的 Heroku API 密钥

现在,您已经准备好将项目部署到 Heroku 托管平台。

编写部署和计划清理管道脚本

我们已经达到了本教程的主要目标,即编写一个脚本来定期调用清理端点来清除过时的日志数据。

首先在项目的根目录下创建一个名为.circleci的文件夹。在刚刚创建的文件夹中添加一个名为config.yml的配置文件。输入以下代码:

version: 2.1
orbs:
  heroku: circleci/heroku@0.0.10
jobs:
  build:
    executor: heroku/default
    steps:
      - checkout
      - heroku/install
      - heroku/deploy-via-git

  clean:
    docker:
      - image: circleci/node:10.16.3
    steps:
      - run:
          name: Clean Database
          command: "curl https://[YOUR_HEROKU_APP_NAME].herokuapp.com/logs/clean"

workflows:
  deploy:
    jobs:
      - build
  clean_old_logs:
    triggers:
      - schedule:
          cron: "* * * * *" # use cron syntax to set the schedule
          filters:
            branches:
              only:
                - main
    jobs:
      - clean 

在此脚本中,有两个作业执行以下操作:

  • build:使用 CircleCI Heroku orb 将日志项目部署到 Heroku
  • clean:运行一个curl命令,调用已部署应用程序的清理端点。

注意: 确保将YOUR_HEROKU_APP_NAME替换为您为 Heroku 应用程序输入的名称。

创建部署和运行清理端点的作业后,脚本定义了两个工作流:

  • deploy:运行build任务,将应用程序部署到 Heroku
  • clean_old_logs:定义一个触发器,该触发器使用cron语法来设置时间表,并每分钟运行一次clean任务

在本教程中,我们每分钟触发一次clean作业,以演示作业如何定期运行。实际上,您应该每年运行一次这种类型的作业。使用适当的cron语法来实现正确的时间表。为了找出最有效的方法,您可以修改这个 cron 时间表表达式

提交您的所有更改,并将代码推送到远程存储库,以运行脚本并触发部署管道。

管道页面显示了deploy工作流的运行。它还显示,每一分钟,预定的clean_old_logs工作流都在运行。

Scheduled Jobs - CircleCI

单击第一个clean_old_logs操作中的clean作业,查看对清理端点的调用响应。

Clean up response - CircleCI

响应从数据库中删除了 18 条记录(实际数字可能不同)。点击clean任务的下一次运行。响应显示有 0 个过期记录。

Clean up response - CircleCI

第一个clean操作删除了今年之前的所有记录。

结论

在本教程中,您已经使用脚本来安排清理数据库中的过时数据。定期的数据库清理是喜欢 CI/CD 管道和流程自动化的另一个原因。

编码快乐!


Fikayo Adepoju 是 LinkedIn Learning(Lynda.com)的作者、全栈开发人员、技术作者和技术内容创建者,精通 Web 和移动技术以及 DevOps,拥有 10 多年开发可扩展分布式应用程序的经验。他为 CircleCI、Twilio、Auth0 和 New Stack 博客撰写了 40 多篇文章,并且在他的个人媒体页面上,他喜欢与尽可能多的从中受益的开发人员分享他的知识。你也可以在 Udemy 上查看他的视频课程。

阅读 Fikayo Adepoju 的更多帖子

通过从其他管道触发管道来管理复杂的开发项目| CircleCI

原文:https://circleci.com/blog/triggering-pipelines-from-pipelines/

众所周知,软件开发正变得越来越复杂。软件的单个元素,如应用程序、库和服务,是相互联系的,并依赖于许多其他元素。开发团队处理他们开发、维护或依赖的整个服务生态系统,而服务生态系统又依赖于由独立团队维护的其他软件生态系统。维护这个生态系统就像你想象的那样复杂。确保整个系统正常运转,并按照预期保持运转,是一项极具挑战性的工作。

从其他管道触发管道是众多技术之一,可以帮助您管理复杂的相互连接和相互依赖的开发项目。在本教程中,我将介绍一些使用这种技术的用例,以及为什么它会有帮助。我将展示一个如何使用 CURL 实现管道到管道触发器的例子,您可以跟着我做。

先决条件

本中级到高级教程需要对 CircleCI 及其管道有所了解。如果您使用 CircleCI 并且有一些已经配置好的项目,这将特别有用。本文涵盖的所有内容将适用于所有的 CircleCI 计划。

管道到管道触发器的用例

考虑从现有管道触发新管道有几个原因。微服务编排就是一个典型的例子。一种常见的模式是在每个版本控制存储库中都有微服务,每个微服务都需要一个单独的 CircleCI 管道配置。随着一个服务的部署,您可以重新部署依赖于这个新版本的其他服务。或者你可以触发一个扩展的集成测试套件,独立于更广泛的测试和部署流程运行。

如果不使用微服务,可以使用管道到管道触发器来发布和测试与下游使用者集成的库。

实现管道触发器

触发管道的一个工作流程遵循以下步骤:

  • 执行第一个管道,进行构建,运行一些测试,并触发部署。
  • 该过程成功完成后,第一个管道对 CircleCI 进行 API 调用,以触发不同项目中的另一个管道。
  • 第二个管道执行构建、运行测试并触发部署。

在这种情况下,第一个管道还可以传递一些管道参数,如部署的名称和版本,以便新触发的管道有一些上下文。

使用管道触发器协调单独的服务

要编排两个独立的服务,可以从单个作业中调用 CircleCI API。您需要知道项目的名称,以及您希望从该作业中触发的管道。要用 API 处理管道,您需要组织名称(zmarkan)和项目名称(pinging-me-softly,因为我有古怪的幽默感)。

接下来,获取个人 API 令牌。您必须拥有对这两个项目的推送访问权限才能工作。您可以将 API 令牌存储在这个项目环境变量中。为了增加安全性,将其存储在环境中。在我的例子中,我使用了一个名为circleci-api的上下文,环境变量的名称为CIRCLE_API_TOKEN

要触发管道,您需要向项目的管道端点发送 POST 请求。这是我在本教程中使用的 URL:

https://circleci.com/api/v2/project/gh/zmarkan/pinging-me-softly/pipeline 

有效负载必须包含您想要用来触发管道的分支或标记的详细信息,以及您想要传递给它的任何管道参数。

以下是一个作业示例:

 trigger-new-pipeline:
      docker: 
        - image: cimg/base:2021.11
      resource_class: small
      steps:
        - run:
            name: Ping another pipeline
            command: |
              curl --request POST \
                --url https://circleci.com/api/v2/project/gh/zmarkan/pinging-me-softly/pipeline \
                --header "Circle-Token: $CIRCLECI_API_KEY" \
                --header "content-type: application/json" \
                --data '{"branch":"main","parameters":{"image-name":"'$DOCKER_LOGIN"/"$CIRCLE_PROJECT_REPONAME":"0.1.<< pipeline.number >>'"}}' 

该作业使用了cimg/base图像和small资源类,这对于发送一个 HTTP 请求来说已经足够了。唯一的依赖是所有 CircleCI 图像附带的卷曲工具,包括cimg/base

要触发管道,只需运行一个命令:curl。API 密钥在Circle-Token头中传递,在包含令牌的环境变量中传递。有效载荷主体是一个 JSON 对象,它包含一个branch和参数:

 { 
    "branch":"main",
    "parameters": {
        "image-name":"'$DOCKER_LOGIN"/"$CIRCLE_PROJECT_REPONAME":"0.1.<< pipeline/ number>>'"
    }
  } 

image-name参数的值是由三个变量构成的字符串:

  • $DOCKER_LOGIN是在该项目或上下文中设置的环境变量。
  • $CIRCLE_PROJECT_REPONAME是 CircleCI 中指定这个项目的内置环境变量。
  • pipeline.number是另一个内置的环境变量。

总的来说,这个值提供了我们在上一步中构建的 Docker 映像的名称。我在中使用了zmarkan/demo-full:0.1.119这个例子

处理管道触发器

CircleCI 会自动为您处理管道触发器,就像它处理存储库中新的 git 提交一样。默认情况下,您的所有工作流都将运行,并且将执行您在 API 调用中指定的分支或标记的最后一次提交。对于本教程,我指定了main

您可以在您的作业和工作流中处理您传递的任何参数(在我的例子中是 T0)。只需确保在配置的parameters部分声明它们:

 version: 2.1

  parameters:
    image-name:
      type: string
      default: "Not a real image name"

  jobs:
    print-image-name:
      docker: 
        - image: cimg/base:2021.11
      steps:
        - checkout
        - run:
            name: Echo Image name
            command: echo << pipeline.parameters.image-name >>

  workflows:
    run-workflow:
      jobs:
        - print-image-name 

这个管道示例只有一个工作流。它打印出我们在前面的管道中构建并通过 API 传递给这个管道的图像的名称。

结论

在本文中,我演示了如何使用 CircleCI API 在第一个项目的管道完成时触发另一个管道。这是一个简单的过程,只需要从正在运行的作业中调用一个 API,所有 CircleCI 用户和组织都可以使用。

这种技术为编排复杂的工作流开辟了新的可能性,包括微服务架构,并将昂贵的集成测试工作从构建和部署场景中分离出来。对于你和你的团队来说,还有很多很多的可能性可以尝试。

如果你对这篇文章有任何问题或建议,或者对未来的文章和指南有什么想法,请通过 Twitter - @zmarkanemail 给我联系我。

在不受信任的分叉| CircleCI 上触发受信任的 CI 作业

原文:https://circleci.com/blog/triggering-trusted-ci-jobs-on-untrusted-forks/

在公共代码库中,通常允许任意用户进行分叉和发出拉请求(我们称之为“分叉 PRs”)。这些用户可能来自您的组织之外,通常被认为是不可信的,无法运行自动化构建。如果您希望一个持续集成服务运行需要访问凭证或敏感数据的测试,这会导致一个问题,因为恶意开发人员可能会提出将凭证暴露给 CI 日志的代码。即使对于私有存储库,您也可能希望组织中的一些用户能够查看存储库,而不会触发泄露机密的构建。

之前,在当您有来自外部贡献者的拉请求时管理秘密中,我们讨论了如何隐藏来自分叉的 pr 的凭证并跳过需要它们的构建。但是这假设了有凭证的构建是为了准备构建工件,并且只有合并真正需要掌握。如果在合并之前,一个可信作业的输出对于变更的适当审查是必需的,那么我们不能简单地跳过这个作业。我们想要的是一种方法,让我们团队中受信任的成员检查分叉的 PR,验证它不会引入可能泄露机密的更改,然后启动我们需要的受信任的 CI 工作,以确保更改的完整性。在这篇文章的前半部分,我们将讨论如何使用 Git 本身作为将代码标记为可信并启用该工作流的手段。第二部分将全面演示如何将这些概念应用于特定的存储库主机(GitHub)和 CI 提供者(CircleCI)。

通过向上游推送将代码标记为可信

将拉请求合并到存储库中的行为可以部分地看作是将代码标记为有效和可信的行为。分叉可以被看作是一种隔离用户更改的方式,直到它们被验证。外部贡献者通常不能将代码推送到主存储库的任何分支,而只能推送到他们自己的分支。

像 GitHub 这样的提供商已经以支持这种模型的方式建立了他们的安全控制:通常,只有一组特定的指定用户被允许对存储库进行任何更改。CI 提供者在设置他们的权限时也考虑到了这一点:CI 权限通常是这样配置的,CI 系统将只向推送至主存储库时触发的作业公开凭证。

如果构建是在创建 PR 时自动触发的,那么任何将构建配置直接放入存储库中的系统都有运行不可信代码的风险。但这也意味着将代码推送到上游分支的行为可以被用作信任的表达,从而允许受信任的作业运行。如果来自分叉 PR 的代码被推送到上游分支,则上游分支和分叉分支的 Git 引用(“refs”)将是相同的,指向相同的提交。GitHub 知道它们是相同的代码,许多 GitHub 的 API 端点只关心提交引用,而不关心引用所关联的分支。在 CI 作业的情况下,GitHub 通过提交进行索引,因此作为将代码推送到上游分支的结果而运行的可信作业也将与指向相同提交的分叉 PR 相关联。

实施触发可信配置项作业的工作流

现在,让我们通过构建一个示例项目将上述理论付诸实践。我们将有一个在任何 PR 上运行的基本test作业,即使它来自一个分支,但我们也将有一个更全面的test-with-data作业,它需要 AWS 凭证来从亚马逊 S3 拉一些私人数据。我们希望避免触发后一个作业,直到对存储库具有提交访问权限的人审查 PR 以确保它不会泄漏凭证。

创建项目

让我们从我们在管理秘密中创建的同一个小型 Java 项目开始,当您有来自外部贡献者的拉请求时。使用 Apache Maven 作为构建工具,我们可以通过以下方式生成项目:

mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=managing-secrets -DarchetypeArtifactId=maven-archetype-quickstart -Dversion=1.3 -DinteractiveMode=false 

现在,我们将创建一个简单的 CircleCI 工作流来运行一个test任务:

version: 2.1

jobs:
  test:
    docker:
      - image: circleci/openjdk:8u171-jdk
    steps:
      - checkout
      - run: mvn clean test

workflows:
  version: 2
  build:
    jobs:
      - test 

一旦我们将它提交到 GitHub 并在 CircleCI 中启用它作为一个项目,每次推送都会触发 CircleCI 中的build工作流的运行。从主存储库的任何分支发出的 PRs 将显示test作业的状态。

为分叉的拉请求启用 CI

现在,我们想要为来自分叉存储库的拉请求启用相同的工作流。这不仅允许没有提交权限的贡献者提出变更,而且对喜欢从自己的分支工作的提交者也有帮助。

要为分叉拉取请求启用 CI,我们在 CircleCI 中进入项目的设置页面,选择构建设置 > 高级设置,并启用“构建分叉拉取请求选项。

当我们在那里时,注意下一个选项,“从分叉的拉请求向构建传递秘密”。这是默认禁用的,这正是我们在这里想要的。下一步,我们将上传 AWS 凭证,我们不想意外地将它们暴露给组织外的用户。

添加秘密

我们通过将 AWS 凭证设置为特定于项目的环境变量,使它们对可信构建可用。注意,创建一个由多个项目共享的上下文也是可能的。

我们现在将移动到 CircleCI 中项目配置的构建设置 > 环境变量部分,并添加AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY变量,这些变量包含允许从亚马逊 S3 的一个位置读取的凭证,我们已经在该位置存放了我们的私有测试数据。这些变量将只针对由具有提交访问权限的人发起的对主存储库上的分支的推送进行设置,而不针对由分叉的 pull 请求触发的 CircleCI 作业。

添加不为分叉 PRs 运行的可信作业

我们现在准备设置一个额外的作业,该作业访问凭证并基于私有数据运行测试。让我们来看看更新后的配置:

version: 2.1

jobs:
  test:
    docker:
      - image: circleci/openjdk:8u171-jdk
    steps:
      - checkout
      - run: mvn clean test
  test-with-data:
    docker:
      - image: circleci/openjdk:8u171-jdk
    steps:
      - checkout
      - run: |
          if [ -z "$AWS_ACCESS_KEY_ID" ]; then
            echo "No AWS_ACCESS_KEY_ID is set! Failing..."
            exit 1;
          else
            echo "Credentials are available. Let's fetch the data!"
          fi

workflows:
  version: 2
  build:
    jobs:
      - test
      - test-with-data:
        filters:
          branches:
            # Forked pull requests have CIRCLE_BRANCH set to pull/XXX
            ignore: /pull\/[0-9]+/ 

这份新工作有点虚假;为了保持这篇文章的简短,我们还没有实现任何真正的数据获取或者使用这些数据的测试。相反,我们只是通过检查凭证的存在来避免这种情况。如果该作业是通过推送到主存储库来启动的,那么环境变量应该是可用的,并且该作业应该通过。

我们使用过滤器将test-with-data作业放入我们的工作流中,确保作业在被分叉的 PR 触发时不会运行。在上一篇文章中,我们实现了一种在 PR 分叉的情况下提前返回的技术;过滤器完成了几乎相同的事情,但是不太详细。

测试分叉 PRs 的审核工作流

现在让我们对我们的存储库做一个小的配置更改,以使测试被跳过变得更加明显;毕竟,如果我们正在经历添加一个可信作业来用真实数据运行我们的测试的麻烦,那么确保这些测试通过可能是确保我们项目的完整性的核心。

在我们的 GitHub repo 中,我们将转到设置 > 分支,我们将添加一个分支保护规则。在我们的特定例子中,目标分支是triggering,我们希望启用“合并前需要通过状态检查”的切换,并确保我们根据需要选择了两个 CI 作业(显示为ci/circleci: testci/circleci: test-with-data)。

现在,当有人从 fork 发出 PR 时,test作业将运行并且(希望)在成功时变成绿色。test-with-data作业不会运行(因为这是一个分叉的 PR),但因为我们已将其标记为必需,它仍会显示在 PR 页面的“状态检查”部分,状态为“预期—等待报告状态”,请审核者注意该作业需要在合并前运行。更好的是,我们可以为评审者创建一个带有检查表的PULL_REQUEST_TEMPLATE.md,明确地指导他们在开始可信构建之前寻找潜在的安全问题。

让我们考虑一个在这个州的公关例子-jklukas/管理秘密#4 。查看此 PR 的审查者将对代码进行初步扫描,发现 PR 在将凭证打印到日志的 CI 作业中引入了一个更改,然后请求更改或关闭 PR;test-with-data从不运行,因此凭据永远不会暴露。

相反,如果变更是良性的,并且评审者决定运行它是安全的,他们可以通过将 PR 的提交推送到主存储库的一个分支来开始可信的工作。这可以通过直接的git调用手动完成,但是有点繁琐。一般的工作流程是将 PR 的 fork 作为远程添加,下拉 PR 的分支,将该分支推到主存储库,然后进行清理。您可以从jklukas/git-push-fork-to-upstream-branch安装一个小型 bash 脚本,使它成为一个更方便的一行程序:

git-push-fork-to-upstream-branch upstream <fork_username>:<fork_branch> 

一旦代码被推送到上游分支,分叉 PR 上的test-with-data状态检查将进入执行状态,并有希望让我们进入完全绿色状态,准备合并。

进一步阅读

管理来自外部贡献者的 pull 请求时的秘密中,我们深入讨论了 CI 凭证管理的一些选项,并探索了一种允许分叉的 pr 在需要凭证来暂存工件的 CI 作业中提前返回的方法。

在这篇文章中,我们改进了这种方法,并探索了一种方法,通过将提交推送到上游分支,让存储库提交者在分叉的 PR 上触发可信构建。你可以在Mozilla-services/Mozilla-pipeline-schemas看到一个更真实的例子。该存储库包含 JSON 模式,Mozilla 使用这些模式来验证传入数据管道的有效负载,这是一个非常有价值的用例,可以根据真实客户端数据的样本测试所有更改,以捕捉任何可能导致管道开始拒绝以前有效负载类的回归。虽然进入该存储库的 pr 通常是由 Mozilla 员工提议的,但是这些员工通常没有对主存储库的写访问权限,因此从 forks 发出他们的 pr,这使得支持提议和测试分叉 pr 的愉快过程变得很重要。

一些新的探索是 CircleCI 最近宣布的受限上下文,它包括将秘密注入工作流图的一部分的可能性,由手动批准步骤触发。这可能是让评审者触发可信构建的更方便和灵活的方法的基础。


杰夫·克鲁卡斯有实验粒子物理学的背景,他既是教师又是帮助发现希格斯玻色子的研究人员。他现在在俄亥俄州哥伦布市的 Mozilla 公司的 Firefox 数据平台上远程工作,之前是 Simple 公司数据平台的技术负责人,Simple 是一家云端无分支银行。

阅读更多杰夫·克鲁卡斯的文章

排除无法解释的构建失败:神秘的“什么都没有改变”案例

原文:https://circleci.com/blog/troubleshooting-unexplained-build-failures-the-mysterious-case-of-nothing-changed/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


在支持的第一线,我们经常收到用户询问,为什么他们的构建在什么都没有改变的情况下突然开始失败了。在回答了近两年的这类问题后,我现在可以充满信心地说,这些案例中的每一个都有所改变。

明确地说,“我的代码中没有任何变化”可能是一个非常有效的陈述。但是“一切都没变”的想法几乎总是错误的。将持续集成引入到您的软件开发工作流中的本质就是测试无论发生什么变化都不会破坏您的应用程序。在本帖中,我们将探讨一些最常见的变化,这些变化是我们在排除意外构建故障时可能没有想到的。

变化是唯一不变的。–未知

我最近得知,人们一直错误地将这句古老的名言归因于一位名叫赫拉克利特的希腊哲学家。换句话说,既然我们不能确定到底是谁先说了这句话,甚至这句话的出处也在不断变化。)

CircleCI 构建映像

我们一直在更新我们的构建映像。我们尽最大努力对一系列 canary 项目进行彻底测试,并在我们社区网站的公告部分提前通知我们的用户即将到来的更新。然而,尽管我们尽了最大努力,有时我们所做的改变会破坏你的构建。这个问题没有很好的解决办法,因为我们一直试图在一个“一刀切”的世界里创造一个“一刀切”的形象。好消息是:在我们即将发布的 CircleCI 2.0 平台中,用户将可以完全控制他们的执行环境。我们希望这意味着在未来,我们将永远无法打破你的建设了。

取消固定的相关性

依赖性管理是艰难的。每种语言和框架都试图用自己的方式解决依赖管理的问题。这导致了各种工具之间的一些不一致。此外,现代框架具有相当大的依赖链,使得开发人员几乎不可能跟踪它们。这是当“什么都没有改变”时构建失败的最常见原因。

如果你选择的语言使用了DependencyList.lock模式,那么你的状态通常很好。如果没有(我正在看你的 pip 和 npm)或者鼓励使用相对版本(比如软件> =1.2),那么不幸的是,你的依赖将导致你的构建失败只是时间问题。当你今天运行pip install foo,一个月后运行pip install foo,除非你明确地说pip install foo==1.2.3,那么foo的版本将会不同。只有当foo拥有 10 个其他 Python 模块的依赖图时,这个问题才会变得更加复杂,其中一些模块可能也是您正在安装的 bar 包的依赖项。这被称为依赖地狱,作为依赖地狱的幸存者,我可以告诉你这不是一个好地方。

这里没有简单的解决办法。处理这个问题的一些策略是:清理你的circle.yml(和任何脚本)以确保你没有任何悬而未决的非固定依赖。

仔细检查你的依赖:你真的需要它们吗?(也就是说,您在 9 个月前安装的节点模块看起来非常整洁,但是您还没有在您的代码中使用它)。

花点时间定期清理和更新你的依赖关系。

第三方服务

如果你的集成测试依赖于第三方服务,请记住这些事情一直在变化。这些变化范围很广,从你没有听说过的重大的、突破性的 API 变化,因为不知何故时事通讯出现在了你的垃圾邮件文件夹中,到那天一些服务的简单的剥落。让您的构建始终可重复的最好方法是减少对第三方的依赖。嘲笑请求和外部系统是一个好策略。如果你不能用嘲讽来完成你的目标,那么你必须在你的测试套件中建立一些容错机制。

更改凭据和环境变量

如果 Heroku CLI 告诉您,您的用户不再有权上传新版本的应用程序,那么很可能您的用户不再有权上传新版本的应用程序。

在这个话题上没有什么可说的了。错误消息很少欺骗你。这个问题最大的罪魁祸首是你的团队中有人改变了一些事情,却没有告诉你。

"忽略这张票,看起来有人改变了我们昨晚的部署方式."

在团队中工作可能很难

随着你的产品和公司的成长,跟踪所有的活动部分变得越来越有挑战性。还记得上周你对那份有 120 个新文件的公关做代码审查,还说“LGTM”吗?这是 120 个刚刚改变的东西,可能没有与之相关的测试。现在一个星期过去了,那些虫子都长大了,也不开心了。

这些通常是我们团队最容易解决的问题,也是最让用户沮丧的问题。我唯一的建议是做一个体贴的队友,向人们传达突破性的变化。如果你在一个分布式团队中工作,这一点尤其正确。

找出构建意外失败的原因可能会非常令人沮丧。好消息是 CircleCI 支持团队会在这种情况发生时提供帮助。请记住,下一次你的构建自然地失败,那是因为某些东西已经改变了。幸运的是,CircleCI 提供了像 Rollbar 这样的解决方案,它可以跟踪何时进行部署,并且可以自动识别哪些代码更改导致了错误

CircleCI webhooks | CircleCI 故障排除

原文:https://circleci.com/blog/troubleshooting-webhooks/

本教程涵盖:

  1. 设置 CircleCI webhooks
  2. 使用 Hookdeck CLI 隧道传输 HTTP 请求
  3. 常见错误疑难解答

CircleCI webhooks 开辟了各种令人兴奋的用例,从数据记录和与第三方监控和可观察性解决方案的集成到建立自己的定制仪表板来监控管道健康状况。为了确保您可以正确地监视事件、解决身份验证错误,以及访问事件中包含的信息,您需要一个可靠的过程来调试您可能遇到的任何错误。

在本教程中,我将向您展示如何在 CircleCI 上设置 webhook,以及如何排除 webhook 连接的常见错误,这样您就可以对您和您的团队所做的工作充满信心。

先决条件

首先,您需要准备一些东西,包括:

  • 一个 CircleCI 账户和关联项目
  • Node.js 安装在您的系统上以运行示例项目
  • API 端点的可公开访问的 URL
  • 用于编辑代码的文本编辑器

有了这个设置,您就有了一个可以方便地对 CircleCI webhooks 进行故障诊断的环境。

克隆和运行演示 API

首先,您将克隆一个样本 Node.js API。这个 API 接收并在内存数据库中记录您的 CircleCI webhook 信息的子集。我故意在这个应用程序中引入了一个身份验证错误和一个not found错误。这些是您在学习本教程时将要调试和修复的错误类型。

通过运行以下命令克隆项目存储库:

git clone --single-branch --branch base-project https://github.com/coderonfleek/circleci-webhooks-api 

通过运行以下命令,导航到项目的根目录并安装所需的依赖项:

cd circleci-webhooks-api
npm install 

安装完成后,使用以下命令运行 Node.js 服务器:

npm start 

这将启动 API 应用程序,并在屏幕上打印一条消息,指示 API 正在运行并监听端口1337上的连接。

我们在这个项目中使用两个端点:

  • /log-circleci-webhook是将接收 CircleCI webhook 并将其记录到内存数据库中的端点。它记录了一个简单的对象,该对象包含来自 webhook 有效负载的信息子集。
  • 可以调用/fetch-webhooks-logs端点来检索记录的 webhook 数据的集合。

使用 Hookdeck CLI 排除 CircleCI webhooks 故障

对 webhooks 进行本地故障排除需要您有一个可公开访问的 URL。但是,本地运行的 API 没有可公开访问的端点。您需要一种方法使您的本地端点可以公开访问,这可以通过使用 HTTP 请求隧道系统来实现。

HTTP 请求隧道系统有助于将您的 webhooks 隧道到您的本地开发环境中,以您的本地 API 上的端点为目标。

有许多开源工具可以帮助您实现这一点。我发现最方便的是 Hookdeck CLI 。Hookdeck CLI 不仅为您提供了一个指向本地 API 端点的可公开访问的 URL,而且它还提供了报告、一个查看您的头文件和有效负载的事件页面,以及其他使调试 webhooks 不那么令人沮丧的工具。

您可以运行以下命令来安装 macOS 的 CLI 工具:

brew install hookdeck/hookdeck/hookdeck 

如果您使用的是 Windows 操作系统,请使用以下命令安装 CLI 工具:

scoop bucket add hookdeck https://github.com/hookdeck/scoop-hookdeck-cli.git
scoop install hookdeck 

对于 Linux 用户,你可以按照这里的说明在 Linux 上安装该工具

获取 CircleCI webhook URL

下一步是使用 CLI 生成指向正在运行的 API 应用程序的 webhook URL。运行以下命令:

hookdeck listen 1337 

此命令会启动一个交互式会话,在该会话中,CLI 会收集有关您将要创建的端点的信息。回答下表中记录的问题。确保在每次回答后按下回车

  • 问:你的新源标签应该是什么?
  • 问:webhooks 应该被转发到什么路径(即:/webhooks)?
  • 问:什么是连接标签(即:我的 API)?
    • 答:我的 CircleCI Webhooks 服务器

CLI 使用您输入的信息来生成 URL。当该过程完成时,URL 被打印到屏幕上。然后,CLI 指示它已准备好接收请求。

fikayo $ hookdeck listen 1337
🚩 Not connected with any account. Creating a guest account...
? What should be your new source label? CircleCI
? What path should the webhooks be forwarded to (ie: /webhooks)? /log-circleci-w? What's your connection label (ie: My API)? My CircleCI Webhooks Server

Dashboard
👤 Login URL: https://api.hookdeck.com/signin/guest?token=13cpjezttzt1ll5w0jb1403lj8sd8t5j2dbfv2di6k1kpshgop
Sign up in the dashboard to make your webhook URL permanent.

👉 Inspect and replay webhooks: https://dashboard.hookdeck.com/cli/events

circleci Source
🔌 Webhook URL: https://events.hookdeck.com/e/src_A345Rx6enuPRpUFuc526wiua

Connections
my-circleci-webhooks-server forwarding to /log-circleci-webhook

> Ready! (^C to quit) 

注意: 您需要使用控制台中的访客链接来访问仪表板。

Login URL复制并粘贴到您的浏览器中,开始访客登录会话。

确保侧菜单上的连接状态显示Connected

设置 CircleCI webhook

有了你的 webhook URL,你现在可以在 CircleCI 上设置一个 webhook 了。转到你的任何 CircleCI 项目,导航到项目设置> Webhooks 。在 Webhooks 页面,点击添加 Webhook

像这样填写表单上的字段:

  • Webhook 名称:输入 Webhook 的描述性名称。使用类似Log API Webhook这样简单的东西。
  • 接收者 URL: 将 Hookdeck CLI 输出中的 webhook URL 粘贴到此处。
  • 秘密令牌:这是一个安全特性,允许你验证你的 webhook 来源(稍后会详细介绍)。在此输入数值ABCD123
  • 证书验证:如果选中此项,则验证您的 API 上的 SSL 证书是真实的和最新的。单击此框。
  • 事件:检查工作流完成事件。

Add a webhook in CircleCI

点击添加网页挂钩创建你的网页挂钩。它将显示在可用网页挂钩列表中。

Webhook has been created - CircleCI

接下来,确认您现在可以从 CircleCI 项目接收 webhooks 了。要运行项目构建,请执行以下操作之一:

  • 进行新的提交
  • 点击从开始处重新运行工作流程

构建完成后,转到运行 Hookdeck CLI 会话的终端。您应该有一个新条目。

Authentication error

CLI 上的条目确认您正在成功接收 webhook。但是有一个500服务器错误。

CLI 上的 webhook 条目按顺序由 4 个元素组成:

  1. HTTP 状态代码(500)
  2. 请求方法(POST)
  3. 您配置 CLI 以将 webhooks 路由到的端点(/log-circleci-webhook)
  4. 活动页面 URL,您可以在此查看您的网页挂钩的详细信息

继续下一节,修复500错误。

身份验证错误故障排除

在做出任何假设之前,使用事件的页面检查服务器响应。从 Hookdeck CLI 会话中复制事件页面链接,并将其加载到您的浏览器中。

Event page view

在这个页面上,您可以查看您刚刚收到的 webhook 的所有详细信息。花点时间查看一下标题部分。

Event headers

查看主体部分中的 webhook 有效载荷。

Event body

向下滚动到尝试部分。单击红色状态代码标记,查看服务器响应的详细信息。

Authentication error details

响应消息显示我们没有通过 webhook 有效负载的安全检查。因为我们定义了一个 API secret,CircleCI 发送circleci-signature头。该报头包含真实有效载荷的加密版本,并且必须对照通过使用秘密密钥接收的未加密有效载荷进行验证。

这是一项安全检查,目的是防止攻击者用可能破坏您的 API 的恶意有效负载替换实际有效负载。

我们知道我们正在接收来自 CircleCI 的实际有效载荷,因为我们设置好了一切并触发了 webhook。我们知道导致身份验证失败的不是攻击者的有效负载。我们的设置一定是出了什么问题。

我们的下一步是回顾 API 上的验证逻辑。这可以在server.js文件中找到。

//Validate payload
function validatePayload(req, res, next) {
  if (req.method == "POST") {
    if (!req.rawBody) {
      return next("Request body empty");
    }

    const sig = Buffer.from(req.get(sigHeaderName) || "", "utf8");
    const hmac = crypto.createHmac(sigHashAlg, secret);
    const digest = Buffer.from(
      "v1=" + hmac.update(req.rawBody).digest("hex"),
      "utf8"
    );

    if (sig.length !== digest.length || !crypto.timingSafeEqual(digest, sig)) {
      return next(
        `Request body digest (${digest}) did not match ${sigHeaderName} (${sig})`
      );
    }
  }

  return next();
}
app.use(validatePayload); 

代码中使用的三个变量值得检查,以确认它们引用了正确的值。这些是:

  • sigHeaderName,代表 CircleCI 发送的签名头名称。
  • sigHashAlg,用于加密的算法。
  • secret,这是我们 CircleCI webhook 上设置的 API secret。

这些值设置在server.js文件的第 10-12 行。以下是这些定义的代码:

const sigHeaderName = "circleci-signature";
const sigHashAlg = "sha256";
const secret = "XXX-XXX"; 

你发现错误了吗?如果这个秘密与我们在 webhook 表单中设置的不一样,那么验证总是会失败。

CircleCI 不允许您再次查看您在 webhooks 中设置的 API 秘密;你只能重置它。这就是为什么你需要确保你记住这个值或者把它存放在安全的地方。

对于本教程,我们知道我们将秘密设置为一个简单的ABCD123字符串。在真实的应用程序中,您会想要设置一个更复杂的秘密,并从代码中的环境变量引用它。

secret的值更改为正确的值,保存文件,并重启服务器。

要测试修复,您需要触发一个新的 webhook。您可以在 CircleCI 项目上触发另一个构件。如果不想等待新的构建,可以使用重试按钮来重新运行 webhook。重试按钮在书签按钮旁边的活动页面顶部。

在您点击重试之后,一个新的请求将被列在事件页面的尝试部分。

Webhook attempts list

CLI 上将会有一个新的 webhook 条目。

Not found error

干得好!我们已经清除了500错误,但是现在我们有了一个新的错误。这个404 not found 错误表示找不到我们指定的端点(/log-circleci-webhook)。

排除“未找到”错误

我们的第二次 webhook 尝试在目的地端点上导致了一个404错误。这告诉我们,即使我们成功地接收了 webhooks,端点也不在指定的位置,或者它不存在。大多数情况下,可以通过检查指定路线名称中的拼写错误来修复错误。最坏的情况是,路由确实不存在,需要创建。

点击尝试部分的红色 404 徽章,查看服务器对此错误的响应。

404 error

这显示了来自正在运行的服务器的实际响应,并确认无法找到指定的端点。

为 webhook 指定的要命中的端点是/log-circleci-webhook,可以在项目目录根目录下的routes.js文件中找到。

router.post("/log-circleci-hook", async function (req, res) {
  //console.log(req.body);

  const payload = req.body;

  let webhook_info = {
    name: payload.webhook.name,
    project: payload.project.name,
    workflow: payload.workflow.name,
    status: payload.workflow.status
  };

  const save_webhook = await req.db
    .collection("webhooks")
    .insertOne(webhook_info);

  res.status(201).send({
    message: "Webhook Event successfully logged"
  });
}); 

仔细检查路由处理程序发现了一个错别字。第 10 行的webhook被错误地拼写为hook。纠正拼写错误并保存文件。

您需要重新启动 Node.js 服务器,以使更改生效。使用Ctrl + C关闭服务器,然后使用npm start命令重启。

再次点击重试按钮,查看新 webhook 请求的尝试部分。成功的 webhook 将由201状态代码指示。

Successful webhook attempt

在您的 Hookdeck CLI 会话中也会有一个 webhook 条目。

Successful attempt - CLI

我们的 API 操作成功。

尝试部分,单击绿色状态标记以显示服务器响应。服务器返回成功消息。

Webhook successful log response message

随着对我们的 API 的成功操作,我们的 webhook 应该被记录。访问端点/fetch-webhooks-logs以查看集合。

Webhook logs

结论

就像应用程序的任何其他架构组件一样,webhooks 可能会出错。开发应用程序或设置应用程序基础结构时,调试是工作流的一部分。错误是不可避免的,因此知道当某个错误发生时要注意什么,并拥有正确的工具来调试它们,可以使故障排除更快、更容易。

在本文中,我们已经演示了如何调试 CircleCI webhooks,以便清楚地了解正在发生的错误。

编码快乐!


Fikayo Adepoju 是 LinkedIn Learning(Lynda.com)的作者、全栈开发人员、技术作者和技术内容创建者,精通 Web 和移动技术以及 DevOps,拥有 10 多年开发可扩展分布式应用程序的经验。他为 CircleCI、Twilio、Auth0 和 New Stack 博客撰写了 40 多篇文章,并且在他的个人媒体页面上,他喜欢与尽可能多的从中受益的开发人员分享他的知识。你也可以在 Udemy 上查看他的视频课程。

阅读 Fikayo Adepoju 的更多帖子

基于主干与基于特性的开发

原文:https://circleci.com/blog/trunk-vs-feature-based-dev/

当您是构建软件项目的唯一开发人员时,您可以根据个人偏好创建和修改您的代码。当您向团队运行的项目贡献代码时,您需要遵循一组标准化的指导原则,并与其他团队成员进行精确的协调。标准的指导方针和协调的工作对于每个基于团队的软件开发项目的成功都是至关重要的。

为了满足这一需求,世界各地的工程团队设计了许多开发工作流程。最近,大多数团队使用 Git 来版本化和管理他们的软件代码。基于 Git 的两种最流行的开发工作流是基于主干的开发和基于特性的开发。脸书、谷歌、网飞和许多其他科技企业的团队都使用这些工作流程。

在本文中,我将讨论基于主干和基于特性的开发,目的是帮助您决定哪种工作流适合您的团队。

什么是基于主干的开发?

基于主干的开发工作流是开发团队中最流行的开发框架之一。在此工作流中,只有一个分支(主干)被视为主要分支。它保存了项目的可部署代码。

开发人员可以直接将变更推送到主分支,但是如果编码活动需要更多的时间——可能需要几天——他们可以从主分支中签出一个分支,将变更移动到其中,然后在开发完成时将它合并回去。

然后,在将检出的分支与主分支合并之前,其他开发人员必须根据公司的指导方针执行代码审查。检出分支的关键在于它们是短暂的,最多持续两到三天。

在基于主干的工作流中,主分支应该总是生产就绪。错误的代码会破坏整个构建,并导致复杂的开发历史。这意味着团队应该在将代码变更推到主分支之前彻底测试它们。短的开发周期和自动化测试使团队能够识别缺陷并从失败的构建中快速恢复,从而降低风险。

使用基于主干的工作流要求团队中有经验丰富的开发人员。初级或无经验的成员需要对工作流程有充分的了解,才能参与项目。

什么是基于特性的开发?

基于特性的开发工作流——或 GitFlow —是软件工程的经典方法。开发单个特征是基于特征的工作流的主要焦点。它与基于主干的工作流的主要区别之一是,它从不将代码变更推送到主分支。

在开发一个特性之前,开发人员检查一个“特性”分支,并在那里修改所有的代码。当特性的开发完成时,开发人员创建一个与主分支的合并请求。根据公司的政策,在将功能分支合并到主分支之前,可能会进行代码审查。最重要的是,开发人员从不将代码变更直接推送到基于特性的工作流程的主分支。

基于特性的工作流的优点是数百名开发人员可以在任何给定的时间处理数百个独特的特性。他们可以分阶段进行特性开发,这样多个合并请求就不会相互冲突。

基于功能的工作流有利于经验较少或资历较浅的团队成员。他们可以开发自己的功能,而不用担心破坏产品代码。技术领导、QA 或任何开发团队成员都可以测试每个合并请求的构建。

然而,管理基于特性的开发可能是棘手的,尤其是当多个拉请求排队合并到主分支时。团队领导或高级软件工程师负责快速审查这些请求,并将它们与主代码合并。这种情况有时会导致冲突,使新开发人员感到沮丧,他们可能需要等待更长时间才能获得合并批准。较慢的集成还会导致部署频率较低,失败构建的恢复时间较长,因此产品创新可能会落后。

基于主干的工作流在您的 CI/CD 实践中的重要性

基于主干的工作流的最大好处之一是,它们可以与现有的持续集成和持续交付(CI/CD) 服务很好地集成。当您将每个提交推送到主分支时,您可以运行自动化测试(CI 的一部分)来验证新的更改不会破坏主分支。然后,在所有测试成功终止之后,您可以配置一个管道,以便在登台分支上为 QA 团队创建部署。

基于 QA 团队的反馈,发布经理——负责创建新发布的人——可以通过将主分支代码推送到发布分支来发布新的发布。然后,发布经理可以将最新的版本部署到生产环境中。

这种工作流确保了发布分支保持不变,因为每个发布都是唯一的。如果存在生产问题,高级开发人员有时可以创建一个补丁来修补发行版中的错误。

在创建了一个新的版本之后,通过将代码推送到主分支,开发工作将继续到下一个版本。随着时间的推移,旧的发布变得陈旧,团队可以安全地删除相应的发布分支。

大多数团队使用持续集成来测试和构建他们的软件。基于主干的工作流方法与 CI/CD 系统集成得很好。它允许快速迭代,并保持代码随时可以部署。

另一方面,基于特性的开发有很长的开发周期和混乱的集成。审查过程会大大增加实时预览新变更所需的时间。

如何在您的项目中实现基于主干的开发

尽管基于主干的开发工作流非常适合许多应用程序,但也有例外。如果您正在进行一个新项目,这个项目要么是一个最小可行的产品,要么是一个概念验证,那么基于主干的工作流可能是一个不错的选择。

团队中的开发人员可以定期推送更改,而无需等待其他人审查和合并拉取请求。唯一的要求是团队中有一些有经验的开发人员,他们可以确保团队不会将任何构建失败的变更提交给主分支。或者,更好的是,自动化 CI/CD 工具可以在推送代码之前检查新代码的错误。

您的开发人员在决定使用基于主干的开发时必须和您一样有信心。但是,在某些情况下,您和您的团队可能需要考虑基于特征的工作流。

例如,有时您必须维护多个软件版本。在这些情况下,基于主干的工作流可能很难实现。Linux 内核是基于特性的开发的经典用例,并且使用长期存在的发布分支。在这种情况下,基于特性的工作流是最明智的解决方案,因为用户可能必须维护 Linux 内核的每个发布版本好几年——甚至可能几十年。

要在项目中实现基于主干的开发,您需要:

  1. Git 版本控制的在线服务,如 GitHub T1。
  2. 建立一个主要(主干)分支、一个临时分支和一个生产分支。开发人员将他们的代码推送到主分支,发布经理通过将主分支与试运行或生产分支合并来创建新的发布。
  3. 持续集成(CI)系统,用于测试开发人员在发布之前提交的新变更。
  4. 连续部署(CD)系统,用于构建项目并将其部署到试运行或生产环境中。

拥有健壮的 DevOps 系统和文化对于任何实现基于主干的开发的组织来说也是至关重要的。随着越来越多的客户使用您的产品,您将会收到更多的错误报告和功能请求。这些通知会让您的开发人员很忙,所以您需要一个系统来测试新的变化,然后再让软件准备好发布。

查看一些最佳 CI/CD 实践,了解关于建立自动化测试和部署管道的想法。

摘要

基于主干和基于特性的开发工作流都有优点和缺点。决定哪种解决方案最适合您的团队取决于团队的集体偏好和经验,以及对团队工作流程的整体适应性。对于采用持续集成最佳实践的团队来说,基于主干的分支模型允许快速迭代和反馈,并且可以在长特性开发周期结束时消除昂贵和耗时的合并冲突。

无论您的团队喜欢哪种开发工作流程,您都将从使用 CI/CD 系统测试和构建软件中受益。注册一个免费的 CircleCI 计划开始设置您项目的持续集成和部署管道。

可信的图像现在在公共测试版 CircleCI 中

原文:https://circleci.com/blog/trusty-image-public-beta/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


我们很自豪地宣布可靠形象的公开测试。如果你是 CircleCI 的测试程序“内部圈子”的一员,你已经有权限了!

什么是可信的图像?

Ubuntu 云映像是由 Ubuntu engineering 定制的预装磁盘映像,可在 Amazon EC2、Openstack、Windows 和 LXC 等云平台上运行。测试版程序使你能够在 Ubuntu 14.04(可信的)映像上运行构建。目前,构建运行在 Ubuntu 12.04 (Precise)上。

使用可信图像的好处?

以下是为什么您应该使用 Trusty 的一些好处:

  • 你现在可以安装更新的 Debian 软件包了
  • 最新主要和次要版本的语言支持

如何启用可信图像?

要启用可信的图像,导航到:“项目设置”->“调整”->“实验设置”->“Ubuntu 14.04 可信容器”

运营重点

由于可信图像仍处于测试阶段,可能会有某些性能问题。在某些情况下,您可能会遇到很长的构建队列。然而,我们的团队将尽最大努力确保所有构建的成功完成。

预安装的软件包

新的可靠映像包括大多数语言的更新版本,具有最新的路径级别。如需完整列表,请点击此处。例如:Python 2.7.11 excludes2.7.10

您仍然可以使用未预安装的版本,但是安装将在构建时进行。以前,如果你想使用新版本的软件包/语言,你需要手动安装它们,有了新的可信任的镜像,很多都是预装的。

频繁更新

您的反馈对我们非常重要。当我们收到反馈时,我们将继续更新可靠的图片。请随时通过beta@circleci.com联系我们。

更多详情,您可以访问我们的主题为的讨论论坛。

使用构建缓存| CircleCI 加速您的 Android Gradle 构建

原文:https://circleci.com/blog/turbocharging-your-android-gradle-builds-using-build-cache/

Gradle 构建缓存旨在通过重用先前构建产生的输出来帮助您节省时间。它的工作方式是存储(本地或远程)构建输出,并允许构建在确定输入没有改变时从缓存中获取这些输出。构建缓存使您能够避免重复工作以及重新生成耗时且昂贵的流程的成本。

使用构建缓存可以为您带来以下好处:

  • 使用本地缓存加速开发人员构建
  • 在 CI 构建之间共享结果
  • 通过重用 CI 结果加速开发人员构建
  • 将远程结果与本地缓存相结合以获得复合效果

构建缓存允许您在团队中共享和重用未更改的构建和测试输出。这加速了本地和 CI 构建,因为不会浪费周期来重新构建不受新代码更改影响的组件。

注意 : Gradle 企业构建缓存支持 Gradle 和 Maven 构建工具环境。

先决条件

要遵循本教程,需要做一些事情:

  1. Gradle 构建工具的基本理解
  2. 如何在 CircleCI 上建立 Android 项目的知识
  3. 了解 Android 构建流程

构建应用程序

为了节省时间,我们将使用本系列前一篇教程中的一个入门项目 Gradle build 扫描 Android 项目:本地和 CI 构建。在这里下载启动项目。我们还将使用 Gradle 的构建缓存文档

要开始,请单击 Android Studio 上的运行按钮。

</blog/media/2020-07-19-circle-notes.mp4>

注意 : 如果 app 不在 Android Studio 上运行,通过从文件菜单中选择无效缓存/重启来使缓存无效并重启。

设置分级构建缓存

对于本教程,我们使用的是 Gradle 版本 6.7。这是对前一教程的更改,因此将提示您更新项目文件。

首先,将文件gradle/wrapper/gradle-wrapper.properties中的最后一行改为:

distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 

另一个变化是我们将使用com.gradle.enterprise作为插件 ID。这个插件必须应用在项目的settings.gradle文件中。

更新settings.gradle文件:

plugins {
    id "com.gradle.enterprise" version "3.4.1"
}
include ':app' 

作为最后的改变,移除build.gradle中的plugins块(线 24-26)。这种格式在 Gradle 6.7 中已被弃用。

如果你愿意,你可以使用项目的替代设置分支。在这个 GitHub pull 请求中可以看到上面概述的对项目文件的更改。

这样一来,我们可以继续了。

启用分级构建缓存

默认情况下,不启用构建缓存。您可以通过几种方式启用构建缓存:

  1. 在命令行上,运行带有--build-cache标志的任务。Gradle 将只为这次构建使用构建缓存。

  2. org.gradle.caching=true放到你的gradle.properties文件中。Gradle 将尝试在所有构建中重用以前构建的输出。您可以通过使用--no-build-cache标志来防止 Gradle 重用任何文件的输出。

对于本教程,我们将使用第二个选项。打开gradle.properties文件并新建一行:

org.gradle.caching=true 

Gradle 构建缓存的工作原理

让我暂停一下,给你一些关于构建缓存如何工作的背景知识。这将有助于教程,你可以与你的团队分享信息。Gradle 支持本地和远程构建缓存。每个都可以单独配置。当两个构建缓存都启用时,Gradle 首先尝试从本地构建缓存加载构建输出。如果没有找到构建输出,Gradle 会尝试远程构建缓存。如果在远程缓存中找到输出,那么它们也存储在本地缓存中,所以下次它们将在本地被找到。

How Build Cache Works

Gradle 有 3 层重用,防止不必要的执行潜在的昂贵任务。这些层使您在以下 3 个目标场景中的构建速度更快:

  1. 在开发人员连续运行一个 Gradle build 的过程中,很多东西保持不变是很常见的。Gradle 增量构建特性只执行自上次执行以来发生了变化的任务。

  2. 开发人员通常在许多分支上维护许多工作区,以执行逻辑上不同的任务。本地缓存允许跨工作区和分支机构快速重用输出,而无需通过任何网络。

  3. CI 节点和开发人员通常使用相同的变更集运行相同的任务。远程缓存允许输出在用户和构建代理之间重用,这样您的团队就不必两次构建相同的东西。

注意 : Gradle 在任何enabled并且BuildCache.isPush()设置为 true 的构建缓存中存储(“推送”)构建输出。

配置梯度构建缓存

下一步是通过使用settings.gradle文件的Settings.buildCache(org.gradle.api.Action)块来配置构建缓存。我们将从配置本地缓存开始,然后转到远程缓存。

配置内置的本地构建缓存

内置的本地构建缓存DirectoryBuildCache,使用一个目录来存储构建缓存工件。默认情况下,该目录存储在 Gradle 用户主目录中,但其位置是可配置的。

Gradle 将通过删除最近没有使用的条目来定期清理本地缓存目录。

有关配置选项的更多详细信息,请参考 DirectoryBuildCache 的 DSL 文档。

将这段代码添加到settings.gradle文件中:

...

buildCache {
    local {
        enabled = true
        directory = new File(rootDir, 'build-cache')
        removeUnusedEntriesAfterDays = 30
    }
    remote(HttpBuildCache) {
        enabled = false
    }
} 

同步项目。

Local Build Cache Settings

从 Android Studio 工具栏中,选择构建,然后选择重建。这将在build-cache目录中生成工件。

Local Build Cache Settings

build-cache文件夹添加到.gitignore中。这种添加阻止您将工件提交到源代码控制。

将以下内容添加到.gitignore文件中:

...
# Local build cache
build-cache 

接下来,在您的本地机器上运行以下步骤序列。这些步骤将使您的构建在本地运行时完全可缓存,无论项目位于何处:

  1. 删除存储在build-cache目录中的本地构建缓存
  2. 在命令行上运行./gradlew clean test Gradle 任务
  3. 重新运行./gradlew clean test,以便它使用您在上一步中生成的本地构建缓存
  4. 确保两个版本都成功,并访问它们的版本扫描链接

单击链接打开每个构建扫描。

如果这是您第一次使用,系统会提示您输入电子邮件地址,以便您可以将构建扫描链接发送给您。输入您的电子邮件地址,单击“Go”。检查您的电子邮件中的版本扫描通知,然后单击链接打开您的版本扫描。

如果您已经激活了构建扫描,Android Studio 的链接会将您重定向到一个没有“激活构建扫描”和“发送电子邮件”步骤的页面。

通过查看性能选项卡的构建缓存部分,确保缓存配置正确。

Local Build Cache Build Scan 1

Local Build Cache Build Scan 2

注意: 注意,截图中的第二个链接有 100%的输出请求来自缓存。

从第二个构件的构件扫描中,单击时间轴。确保没有可缓存的任务做任何工作。在您的构建扫描 URL 的末尾,添加:

/timeline?cacheableFilter=cacheable&outcomeFilter=SUCCESS

Local Build Cache Build Scan 3

您还可以观察还不可缓存的构建任务。在您的构建扫描 URL 的末尾,添加:

/timeline?cacheableFilter=any_non-cacheable&outcomeFilter=SUCCESS

Local Build Cache Build Scan 4

使用远程 HTTP 构建缓存

Gradle 内置了对通过 HTTP 连接到远程构建缓存后端的支持。使用以下配置,本地生成缓存用于存储生成输出,而本地和远程生成缓存用于检索生成输出。

注意 : 对于这个实现,您需要一个构建缓存节点。如果你还没有创建一个构建缓存节点,你可以使用这个

更新settings.gradle文件的内容:

plugins {
    id "com.gradle.enterprise" version "3.4.1"
}
include ':app'

boolean isCiServer = System.getenv().containsKey("CI")

buildCache {
    local {
        enabled = false
        directory = new File(rootDir, 'build-cache')
        removeUnusedEntriesAfterDays = 30
    }
    remote(HttpBuildCache) {
        url = 'http://34.75.139.200:5071/cache/'
        allowUntrustedServer = true
        enabled = true
        push = !isCiServer
    }
} 

这个片段使我们的项目能够从HttpBuildCache加载工件。将url(第 15 行)追加到/cache/,更新到您的build cache node链接。请注意结尾的斜线。在这种情况下,我使用了之前分享的 URL。

同步项目:

Remote Build Cache Settings

远程构建缓存配置有几个有用的属性:

  • url是共享远程构建缓存后端的位置
  • 如果您不使用自签名或不受信任的证书,请使用allowUntrustedServer参数。
  • enabled激活或禁用远程构建缓存
  • 如果您的持续集成服务器使用干净的构建来填充远程构建缓存,而开发人员从远程构建缓存中提取构建,并将其推送到本地构建缓存,则使用push

在本地计算机上运行下一系列步骤。完成这些步骤将使您的构建在本地运行时完全可缓存,无论项目位于何处。这些步骤类似于我们之前对本地缓存所做的。

  1. 删除存储在build-cache目录中的本地构建缓存
  2. 在命令行上运行./gradlew clean test Gradle 任务
  3. 再次运行./gradlew clean test Gradle 任务
  4. 确保两个版本都成功,并访问它们的版本扫描链接
  5. 在构建扫描中,转到性能选项卡上的构建缓存部分,以确保缓存配置正确

Remote Build Cache Build Scan 1

现在,您已经确认了远程构建缓存正在按预期工作。恭喜你!

现在,更新设置,以确保它按照 CI 构建的预期工作。因为我们已经验证了可以从本地项目推送至远程缓存,所以我们应该将这种推送限制在 CI 环境中。

为 CircleCI 设置

下面是一个更新的settings.gradle文件,展示了 CI 推送用例的推荐设置:

plugins {
    id "com.gradle.enterprise" version "3.4.1"
}
include ':app'

boolean isCiServer = System.getenv().containsKey("CI")

buildCache {
    local {
        enabled = true
        directory = new File(rootDir, 'build-cache')
        removeUnusedEntriesAfterDays = 30
    }
    remote(HttpBuildCache) {
        url = 'http://34.75.139.200:5071/cache/'
        allowUntrustedServer = true // Allow untrusted cache server
        enabled = true
        push = isCiServer
    }
} 

清除您的remote build cache node,以便当我们检查 CI 推送机制是否工作时,不存在任何工件。

Build Cache Node Purge

在您进行代码更改之后,将它们提交到源代码控制并推送到您的远程分支。

在成功的 CI 构建之后,在 Circle CI 仪表板上的 Run Tests 作业下找到构建扫描。

Circle CI Build Scan

在浏览器中打开构建扫描。在构建扫描的性能选项卡上,查看构建缓存部分以确保缓存配置正确。

Remote Build Cache Build Scan 2

这是上一张截图上显示的项目的关键。

  • (1)来自本地和远程构建缓存的命中
  • (2)本地缓存已启用
  • (3)本地缓存路径
  • (4)远程缓存已启用
  • (5)远程缓存 URL 路径

检查远程构建缓存节点,确保缓存工件被保存。

Build Cache Node

结论

在本教程中,您学习了如何:

  • 在我们的项目中启用构建缓存
  • 为本地和 CI 生成配置生成缓存。
  • 向你的朋友和团队成员解释构建缓存是如何工作的
  • 查看构建扫描信息以确认缓存按预期工作

这样,您就可以有意义地改进您的构建性能并提高开发人员的生产率。对于下一个相关教程,请参见使用 GCP 部署您的 Gradle build 缓存节点或者在 Android 项目中使用 Gradle Build 扫描


Taracha 是一个创造者,他的使命是帮助公司用最简单的方式解决复杂的问题。他关心寻找他热爱的愿景&事业,并利用技术帮助这些公司解决他们的挑战。

他有超过 6 年的本地移动应用程序开发经验&大约 4 年的 web 应用程序开发经验。

他目前是 Premise Data 的高级软件工程师,负责新产品功能的开发和部署,对开发人员生产力工程领域特别感兴趣。

弃用 Ubuntu 14.04 和 16.04 映像:使用现代 Ubuntu | CircleCI 保持安全

原文:https://circleci.com/blog/ubuntu-14-16-image-deprecation/

CircleCI 的 Ubuntu 14.04 和 16.04 Linux 虚拟机(VM)镜像将于 2022 年 3 月下旬、2022 年 4 月下旬和 2022 年 5 月中旬暂时不可用,并且它们将于 2022 年 5 月 31 日永久不可用。如果你正在使用这些镜像,在 2022 年 5 月底 14.04 和 16.04 镜像被移除之前,用一个更新的 Ubuntu machine镜像更新你的配置。

Ubuntu 14.04 和 Ubuntu 16.04 映像现在已被弃用

截至 2022 年 2 月 15 日,与我们的 machine执行程序一起使用的 Ubuntu 14.04 和 16.04 Linux VM 镜像已被弃用。这意味着将不会有新的版本或对这些图像的支持。当这些映像在 2022 年 5 月 31 日达到寿命终止(EOL)时,任何利用 Ubuntu 14.04 或 16.04 映像的管道都将失败。

如果您的任何项目目前使用受影响的映像,我们将直接联系您的团队,鼓励您迁移到更新的 Ubuntu 映像。但是,我们意识到电子邮件通知很容易被错过,因此在通知期结束后,我们还会对受影响的映像实施偶尔的限电或短暂的不可用,直到停产日期。

Ubuntu 图像弃用时间表

请参考下表,了解关于弃用旧 Ubuntu 映像的重要日期:

| 日期 | 发生了什么 |
| 2022 年 2 月 15 日 | 通知电子邮件发送给受影响的团队。CircleCI 的 Ubuntu 20.04 映像已经对想要立即迁移的团队可用。 |
| 2022 年 3 月 29 日 | CircleCI 发布首张 Ubuntu 22.04 测试版图片。 |
| 2022 年 3 月 29 日 | 首次限电。被弃用的图像将在以下时间段内每次 10 分钟不可用:

-UTC 00:00—00:10&UTC 01:00—01:10
-UTC 10:00—10:10&UTC 11:00—11:10
-UTC 15:00—15:10&UTC 16:00—16:16 |
| 2022 年 4 月 25 日 | CircleCI 发布 Ubuntu 22.04 稳定镜像。 |
| 2022 年 4 月 26 日 | 第二次限电。不推荐使用的映像将在以下时间段内每次 60 分钟不可用:

-UTC 00:00—01:00
-UTC 10:00—11:00
-UTC 15:00—16:00

(时区助手)

任何引用这些映像的作业都将收到一个failed to create host: Image <image-name> is not supported错误。 |
| 2022 年 5 月 19 日 | 第三次限电。不推荐使用的映像将在以下时间段内一次不可用 3 小时:

-UTC 00:00—03:00
-UTC 08:00—11:00
-UTC 16:00—19:00

(时区助手)

任何引用这些映像的作业都将收到一个failed to create host: Image <image-name> is not supported错误。

Ubuntu 22.04 成为machine: true的新默认图像 |
| 2022 年 5 月 31 日 | Ubuntu 14.04 & 16.04 生命终结。这些图像永久不可用。 |

受影响图像的第一次限电将于 2022 年 3 月 29 日进行,随后将于 2022 年 4 月 26 日进行另一轮限电,第三次将于 2022 年 5 月 19 日进行。任何依赖于这些图像的管道运行将在指定的限电期间失败。14.04 和 16.04 映像将于 2022 年 5 月 31 日永久删除,使用受影响映像的管道将永久失败,直到指定更新的映像。

为什么 Ubuntu 14.04 和 16.04 镜像被弃用?

CircleCI 致力于为您和您的团队提供最稳定、最安全的体验。放弃我们的 Ubuntu 14.04 和 16.04 映像而支持新版本有几个好处,包括改进上游支持和增强基于 Linux VM 管道的安全性和兼容性。

上游支持

简单明了,Ubuntu 14.04 和 16.04 镜像老了!Ubuntu 背后的企业赞助商 Canonical 于 2019 年停止支持 Ubuntu 14.04,2021 年停止支持 16.04。这可以在下面的图表中看到,Ubuntu 的标准支持窗口(CircleCI 和大多数提供商提供的图片)是橙色的。

Ubuntu release cycle

安全性和兼容性

缺少对这些图像的上游支持意味着缺少错误修复和安全更新。这意味着没有新版本的包含软件,如 Node.js、Python、OpenJDK 等。但更重要的是,当安全漏洞影响到行业时,我们无法为您提供更新的图像。

诸如 log4j CVEs 、GitHub 的 OpenSSH 协议弃用让我们加密证书过期等安全问题在这些映像上要么极其困难,要么几乎不可能缓解。随着年龄的增长,这个问题会越来越严重。

Ubuntu 14.04 和 16.04 镜像弃用对我有影响吗?

如果您的任何项目使用了一个或多个受影响的图像,我们将通过电子邮件直接通知您或您团队中的其他人。由于电子邮件有时会被忽略,我们也将对受影响的图像实施限制。这些将是一个准确的指标,当这些图像在 5 月底被永久删除时,项目是否会受到影响。提醒一下,请查看上表了解确切日期。

要自行检查项目,您可以参考以下受影响图像的完整列表。

以下基于 Ubuntu 14.04 的图像将被移除:

  • machine: true(见下面的注释)
  • circleci/classic:201703-01
  • circleci/classic:201707-01
  • circleci/classic:201708-01
  • circleci/classic:201709-01
  • circleci/classic:201710-01
  • circleci/classic:201710-02
  • circleci/classic:201711-01
  • 圆形/经典
  • circleci/classic:最新
  • circle ci/经典:边缘

注意:使用默认镜像意味着你在你的配置中使用了machine: true,因此得到了一个基于 Ubuntu 14.04 的镜像。如果你运行的是 CircleCI 服务器CircleCI 运行程序,那么这不适用于你。否则,这种反对意见适用,您需要切换到受支持的映像。

此外,以下基于 Ubuntu 16.04 的图像将被删除:

  • circleci/classic:201808-01
  • ubuntu-1604:201903-01
  • ubuntu-1604:202004-01
  • ubuntu-1604:202007-01
  • ubuntu-1604:202010-01
  • ubuntu-1604:202101-01
  • ubuntu-1604:202104-01

这是其中一个图像在您的配置中的外观示例:

jobs:
  my-job:
    machine:
      image: ubuntu-1604:202010-01 

如何迁移到受支持的 Ubuntu 映像

根据您的技术需求,有几种方法可以将您的项目迁移到受支持的 Ubuntu 映像。两种最常见的方法是使用 Docker 便利映像而不是 VM,以及更新您的配置以使用更新的 Ubuntu machine映像。

试试我们的便利图片

首先要考虑的是您的团队是否真的需要首先使用 Linux 机器/VM 镜像。我们有一批基于 Docker 的图像,称为便利图像,快速灵活,涵盖大多数用例。《T2 如何选择遗嘱执行人》可以帮你做出决定。

切换到更新的 Ubuntu 机器镜像

认定machine执行人是一个要求?太好了。我们有一个受支持的 Ubuntu 20.04 映像,您可以从今天开始使用。关于这个和任何其他虚拟机镜像的信息可以在开发者中心找到。有关迁移到较新映像的信息可以在以下两个 CircleCI 文档页面中找到:

注: Canonical 将在 4 月发布下一个 Ubuntu 长期支持(LTS)版本 Ubuntu 22.04。CircleCI 将于 3 月 29 日发布该图像的测试版,并定期更新,直到 4 月 25 日正式发布,即 Canonical 提供其正式发布版本的几天后。有关日期,请参见上面的时间表。

结论

如果您的任何项目当前使用 Ubuntu 14.04 和 16.04,请计划尽快迁移过时的映像。如果您在 2022 年 3 月、4 月和 5 月的限电日期之前迁移,您的管道将不会受到影响。在 5 月 31 日截止日期之前迁移这些映像是至关重要的,因为在此日期之后,所有仍然使用基于 Ubuntu 14.04 或 16.04 的机器映像的构建都将失败。

如果您有任何关于您的项目是否受到弃用影响或者如何迁移到受支持的 Ubuntu 映像的问题,请联系支持

单元测试与集成测试

原文:https://circleci.com/blog/unit-testing-vs-integration-testing/

软件是为实际应用而发明的最复杂的工具之一。一个放错位置的字符可能会破坏整个应用程序。因此,在发布任何代码之前,仔细的测试是一个基本要求。在本文中,您将了解两种基本的软件测试类型,单元测试集成测试,以及您的团队如何在您的 CI/CD 管道中实现它们,以快速验证您的代码,并满怀信心地向您的用户交付新功能。

这些年来,DevOps 团队和开发人员已经引入了几种软件测试的方法。这些方法中的大多数已经找到了进入测试金字塔的一个或另一个变体的方法。在大多数情况下,单元测试和集成测试构成了金字塔的两个基础层,强调了它们在全面测试策略中的重要性:

The testing pyramid shows complementary test categories

虽然这形成了一个很好的图表,但实际上在单元测试、集成测试和许多其他类型的测试之间没有清晰的界限。不同的测试类别不是排他的,而是互补的。因此,虽然开发人员可能会争论单元测试和集成测试的相对重要性,但是在您的持续集成管道中找到使用这两种测试的理想位置对您的团队最有利。但是,在这样做之前,您需要了解这两种测试的基础。

什么是单元测试?

单元测试集中在整个应用程序的一个单独的部分,通常是一个单独的类或函数。理想情况下,被测组件没有副作用,因此尽可能容易隔离和测试。

实际上,DevOps 不可能总是达到这种隔离级别。这时测试开始变得更具挑战性。

其他因素会限制单元测试的能力。例如,在带有访问修饰符(如 private 或 public)的编程语言中,不能测试私有函数。特殊的编译器指令或标志有时有助于避开这些限制。否则,您需要应用代码更改,以使这些受限的帮助器可用于单元测试。

使单元测试成为好选择的一个关键因素是它的执行速度。因为这些测试应该是无副作用的,所以您会想要直接运行它们,而不需要任何其他系统的参与。理想情况下,这不包括对底层操作系统的依赖,例如文件系统访问或网络功能。实际上,可能存在一些依赖性。其他的依赖关系可以被交换出来,以便进行独立的测试。这个过程叫做嘲讽

单元测试也是高级软件开发过程的核心,称为测试驱动开发。在测试驱动的开发过程中,DevOps 专业人员和开发人员在实际实现之前编写测试。目标是在实现之前推出单个单元的规范。

尽管执行这种合同可能很有吸引力,但也有明显的不利之处。规范需要精确,测试人员至少需要从概念的角度了解部分实现。这个需求与一些敏捷原则相矛盾。

既然我们已经详细探讨了单元测试,我们可以了解集成测试的不同之处。

什么是集成测试?

我们已经了解到,在实践中,单元测试的隔离属性对于某些功能可能是不够的。在这种情况下,一个解决方案是测试应用程序的各个部分如何作为一个整体协同工作。这种方法被称为集成测试。

与单元测试不同,集成测试从一开始就考虑副作用。这些副作用甚至可能是可取的。

例如,集成测试可以使用到数据库的连接(单元测试中的依赖项)来查询和改变数据库,就像它通常会做的那样。您需要准备数据库,然后正确地读出它。DevOps 经常“嘲笑”这些外部资源,就像单元测试中使用嘲笑一样。这导致掩盖了由 API 引起的超出其控制的故障。

集成测试有助于通过检查应用程序或特定单元的实现来发现不明显的问题。集成测试在几个应用程序部分的相互作用中发现缺陷。有时,这些缺陷很难跟踪或重现。

虽然各种测试类别之间的界限很模糊,但是集成测试的关键属性是它处理应用程序的多个部分。虽然单元测试总是从单个单元中获取结果,比如一个函数调用,但是集成测试可能会从不同的部分和来源聚集结果。

在集成测试中,没有必要模仿应用程序的各个部分。您可以替换外部系统,但是应用程序以集成的方式工作。这种方法可以用于 CI/CD 管道中的验证。

CI/CD 中的单元测试和集成测试

测试需要运行才能有效。自动化测试的最大优势之一是它们可以无人值守地运行。根据大多数 DevOps 原则,在 CI/CD 管道中自动化测试被认为是一种最佳实践,即使不是强制性的。

当系统能够并且应该触发测试时,有多个阶段。首先,测试应该在有人将代码推到一个主要分支时运行。这种情况可能是拉请求的一部分。在任何情况下,您都需要保护代码到主分支的实际合并,以确保所有的测试在代码被合并之前通过。

设置 CD 工具,以便仅当所有测试都通过时才部署代码更改。这种设置可以应用于任何环境,或者只应用于生产环境。这种故障保护对于避免在没有正确检查副作用的情况下发布问题的快速修复是至关重要的。虽然额外的检查可能会让你慢一点,但通常额外的时间是值得的。

您可能还希望针对生产环境或其他环境中的资源定期运行测试。这种做法让你知道一切都还在运行。服务监控对于保护您的生产环境免受不必要的中断更为重要。

因为您的 CI/CD 管道应该很快,所以尽可能快地运行大多数测试是有意义的。通常,最快的选择是使用许多单元测试,但是总的关键度量是覆盖率和相关性

开发团队必须为他们的项目创建一个有效、可靠的测试设置,一个覆盖所有相关代码路径的测试设置。使在您的 CI/CD 管道中自动运行这些测试成为您的团队的一个高优先级。测试方法的结合提高了测试覆盖率,并使你的软件尽可能地没有错误。

结论

单元测试和集成测试都是成功软件开发的重要部分。虽然它们服务于不同但相关的目的,但一个不能取代另一个。他们很好地互补。

虽然编写单元测试通常更快,但是集成测试的可靠性往往会为关键的利益相关者建立更多的信心。使用这两种测试类别来确保您的应用程序今天工作,明天继续工作。

使用您的 CI/CD 工具来自动运行您团队的测试,当事情发生变化时触发,定期地,或者按需地。更多的测试意味着更多的数据,以及更多的方法来确保您团队的软件应用程序在生产中保持稳定。要了解自动化测试策略如何提高您团队的开发速度,并消除昂贵且低效的手动过程,今天就注册一个免费的 CircleCI 帐户

持续构建、测试和部署 Golang 应用程序| CircleCI

原文:https://circleci.com/blog/use-circleci-orbs-to-build-test-and-deploy-a-simple-go-application-to-aws-ecs/

在本教程示例中,我们将向 Amazon EC2 容器服务(ECS)部署一个简单的 Go 应用程序。然后,我们将使用 CircleCI 自动构建、测试和部署应用程序的后续版本。为了确保很好地掌握所使用的技术,我们将逐步做到这一点,主要步骤如下:

  1. 创建安全组
  2. 创建一个包含 1 个实例的 ECS 集群
  3. 创建 ECS 任务定义
  4. 创建运行任务定义的服务
  5. 创建并配置一个 Amazon 弹性负载平衡器(ELB)和目标组,它将与我们集群的 ECS 服务相关联
  6. 使用我们的 ELB 上的 DNS 名称来访问应用程序(测试它的工作)
  7. 使用circleci/aws-ecr@6.2.0 orb 配置 CircleCI,以构建一个更新的映像并将其推送到 Amazon 弹性容器注册中心(ECR)
  8. 使用circleci/aws-ecs@0.0.11 orb 配置 CircleCI,将更新后的映像部署到我们之前创建的集群中

要充分体会亚马逊 ECS 的好处,首先需要了解 Docker 。在本教程中,我们假定您对码头和集装箱化有所了解。此外,您需要对持续集成和持续部署(CI/CD)有一个初步的了解。在下一节中,我们将介绍一些我们将使用的技术和术语。

使用的技术和术语概述

  • ECS 概述 : ECS 是亚马逊网络服务 (AWS)中管理容器的云计算服务。它使开发人员能够通过应用程序编程接口(API)调用和任务定义来部署和管理运行在服务器组(称为集群)上的可伸缩应用程序。本质上,它是一个任务调度器。它创建的任务映射到正在运行的 Docker 容器。它根据可用的资源确定在集群中的资源上运行任务的位置。而其他集装箱技术存在(LXC,rkt 等。),因为 Docker 的大量采用,ECS 被设计为本机使用 Docker 容器。

  • 任务定义 :把它看作是描述如何运行你的容器的配方。它包含诸如要在容器上公开的端口、要分配的内存和 CPU,以及从中启动容器的 Docker 映像等信息。在我们的例子中,它是一个容器、要使用的映像、要分配的 CPU 和内存以及要公开的端口。

  • 任务:ECS 任务是从任务定义实例化的运行容器的单元。它们是在同一实例上一起运行的 1 到 N 个容器的逻辑分组,N 由您在 1 到 10 之间定义。根据需要,一个任务定义可以创建多个任务。

  • 服务:ECS 服务用于保证您始终有一些任务在运行。如果任务的容器由于错误而退出,或者底层 EC2 实例失败并被替换,ECS 服务将替换失败的任务。我们创建集群,以便服务在 CPU、内存和网络端口方面有足够的资源可以使用。对我们来说,任务在哪个实例上运行并不重要,只要它们能够运行。服务配置引用任务定义。服务负责创建任务。

  • 集群:一个 ECS 集群是一组(容器)实例(或者 Fargate 中的任务),它们位于一个区域内,但是可以跨越多个可用性区域。ECS 处理调度、维护和处理对这些实例的扩展请求的逻辑。它还消除了根据您的 CPU 和内存需求寻找每个任务的最佳位置的工作。一个集群可以运行许多服务。如果您的产品中有多个应用程序,您可能希望将其中几个放在一个集群中。这有效地利用了可用资源,并最大限度地减少了设置时间。

  • 容器实例:这是一个运行 Amazon ECS 容器代理的 Amazon 弹性计算云(EC2)实例。它有一个明确定义的 IAM 策略角色,并且已经注册到一个集群。当您使用 Amazon ECS 运行任务时,您的任务(使用 EC2 启动类型)被放置在您的活动容器实例中。

  • CircleCI orb:orb 是 circle ci 配置的包,可以跨项目共享。orb 允许您创建一个作业、命令和执行器的捆绑包,它们可以相互引用,可以导入到 CircleCI 构建配置中,并在它们自己的名称空间中调用。

一个简单的围棋程序

以下是我们项目的目录结构:

.
├── .circleci
│   └── config.yml
├── Dockerfile
├── README.md
├── ecs-service.json
├── main.go
└── task-definition.json

1 directory, 6 files 

完整的申请可在本报告中找到。要跟进,在您的终端中将它克隆到所需的位置:

$ git clone https://github.com/daumie/circleci-ecs.git 

如果你只对 Dockerfile 感兴趣,你可以在这里找到。Go 应用的main.go文件可以在这里找到。

让我们把它们放在一起。首先,创建并激活一个 AWS 账户。然后,在本地机器上安装并配置 AWS CLI 。我们将使用它从命令行界面与 AWS 进行交互。

接下来,我们将使用创建 AWS 帐户时自动创建的默认虚拟私有云(VPC)。如果它不可用,您可以通过运行以下命令来创建默认 VPC:

$ aws ec2 create-default-vpc 

通过运行以下命令,确认我们有一个可以使用的 VPC:

$ aws ec2 describe-vpcs 

在确认我们有一个默认的 VPC 后,让我们创建一个稍后将使用的安全组:

$ aws ec2 create-security-group --group-name circleci-demo-sg --description "Circle CI Demo Security Group" 

接下来,我们将创建一个 ECS 集群和相关的 EC2 实例。我们将称这个集群为circleci-demo-cluster。我们需要附加我们在前面创建的circleci-demo-sg安全组。

  • 集群名称 : circleci-demo-cluster
  • EC2 实例类型 : t2.medium
  • 联网:对其所有子网使用默认 VPC
  • 安全组 : (circleci-demo-sg)您将使用它的 id
  • 容器实例 IAM 角色 : ecsInstanceRole

等待几分钟,然后确认容器实例已经成功注册到circleci-demo-cluster。您可以通过单击 Clusters/my-cluster 下的 ECS Instances 选项卡进行确认。

创建应用程序映像并将其推送到 AWS ECR

在本地创建 Docker 映像,并将其推送到 ECR:

$ docker build -t circleci-ecs:v1 .

Step 1/14 : FROM golang:latest as builder
 ---> be63d15101cb
... 

按照以下说明在 ECR 上创建一个图像存储库。将其命名为circleci-demo:

AWS 帐户有唯一的 ID。适当修改以下命令中的 634223907656 。获得存储库名称后,我们现在可以相应地标记图像:

$ docker tag circleci-ecs:v1 634223907656.dkr.ecr.eu-west-2.amazonaws.com/circleci-demo:latest 

您可以使用 credential helper 对 Docker CLI 的 AWS ECR 存储库进行身份验证。让我们使用下面的命令进行身份验证(根据需要更改区域)。

$ aws ecr get-login --no-include-email --region eu-west-2 | bash 

然后,将映像推送到 ECR 存储库:

$ docker push 634223907656.dkr.ecr.eu-west-2.amazonaws.com/circleci-demo:latest 

现在我们在 ECR 注册中心有了一个映像,我们需要一个任务定义,它将成为我们启动 Go 应用程序的蓝图。项目根目录中的task-definition.json文件包含以下几行代码:

{
    "family": "circleci-demo-service",
    "containerDefinitions": [
        {
            "name": "circleci-demo-service",
            "image": "634223907656.dkr.ecr.eu-west-2.amazonaws.com/circleci-demo:latest",
            "cpu": 128,
            "memoryReservation": 128,
            "portMappings": [
                {
                    "containerPort": 8080,
                    "protocol": "tcp"
                }
            ],
            "command": [
                "./main"
            ],
            "essential": true
        }
    ]
} 

注意 : 记得把image改成你推到 ECR 的那个。

让我们从命令行界面使用以下命令注册任务定义:

$ aws ecs register-task-definition --cli-input-json file://task-definition.json 

确认任务定义已在 ECS 控制台中成功注册:

创建一个 ELB 和一个目标组,以便以后与我们的 ECS 服务关联

我们正在创建一个 ELB,因为我们最终想要跨多个容器负载平衡请求,并且我们还想要将我们的 Go 应用程序公开到互联网上进行测试。为此,我们将使用 AWS 控制台。进入 EC2 控制台 > 负载均衡 > 负载均衡器,点击创建负载均衡器,选择应用负载均衡器

配置负载平衡器

  • 将其命名为circleci-demo-elb,选择面向互联网。
  • 在侦听器下,使用带有 HTTP 协议和端口 80 的默认侦听器。
  • 在“可用性区域”下,选择在群集创建期间使用的 VPC,并选择所需的子网。

配置安全设置

  • 跳过警告,因为我们不会使用 SSL。

配置安全组

  • 创建一个名为circleci-demo-elb-sg的新安全组,打开端口 80 和 source 0.0.0.0/0,这样外界的任何东西都可以通过端口 80 访问 ELB。

配置路由

  • 使用端口 80 创建一个名为circleci-demo-target-group的新目标组。

注册目标

  • 通过选择 ECS 实例注册现有目标。

回顾

  • 查看负载平衡器的详细信息

circleci-demo-elb-sg安全组向外界开放circleci-demo-elb负载平衡器的端口 80。现在,我们需要确保与 ECS 实例相关联的circleci-demo-sg安全组允许来自负载平衡器的流量。要允许所有 ELB 流量到达容器实例,请运行以下命令:

$ aws ec2 authorize-security-group-ingress --group-name circleci-demo-sg --protocol tcp --port 1-65535 --source-group circleci-demo-elb-sg 

确认已通过 EC2 控制台将规则添加到安全组:

inbound rules

outbound rules

使用这些安全组规则:

  • 只有 ELB 的 80 号港口对外界开放。
  • 允许从 ELB 到带有circleci-demo-target-group组的容器实例的任何流量。

创建服务

下一步是创建一个运行circleci-demo-service任务定义(在task-definition.json文件中定义)的服务。我们的ecs-service.json文件在我们项目的根目录下有这几行代码:

{
    "cluster": "circleci-demo-cluster",
    "serviceName": "circleci-demo-service",
    "taskDefinition": "circleci-demo-service",
    "loadBalancers": [
        {
            "targetGroupArn": "arn:aws:elasticloadbalancing:eu-west-2:634223907656:targetgroup/circleci-demo-target-group/a5a0f047c845fcbb",
            "containerName": "circleci-demo-service",
            "containerPort": 8080
        }
    ],
    "desiredCount": 1,
    "role": "ecsServiceRole"
} 

要找到创建circleci-demo-elb负载平衡器时创建的targetGroupArn,请转到 EC2 控制台 > 负载平衡 > 目标组并单击circleci-demo-target-group。复制并替换ecs-service.json文件中targetGroupArn的文件。

现在,创建circleci-demo-service ECS 服务:

$ aws ecs create-service --cli-input-json file://ecs-service.json 

从 ECS 控制台进入集群>circle ci-demo-cluster>circle ci-demo-service并查看任务选项卡。确认容器正在运行:

测试一切是否正常

使用 curl 验证 ELB 公开可用的 DNS 端点:

$ curl circleci-demo-elb-129747675.eu-west-2.elb.amazonaws.com; echo

Hello World! 

同样可以通过浏览器确认:

配置 CircleCI 来构建、测试和部署

在成功地将我们的 Go 应用程序部署到 ECS 之后,我们现在想要在每次更新时重新部署该应用程序。通过使用 CircleCi orbs ,我们将通过将预先构建的命令、作业和执行器导入到我们的配置文件中来节省大量时间。这也将通过消除 AWS 部署所需的大量 bash 脚本来大大减少我们配置中的代码行。我们将使用orbs键调用这个项目中的以下 orb:

  • circleci/aws-ecr@6.2.0:一个 orb,用于与 Amazon 的 ECR 一起构建、推送和更新图像
  • circleci/aws-ecs@0.0.11:一个 orb,用于与 Amazon 的 ECS 一起工作,将更新后的映像部署到之前创建的集群中

orb 由以下元素组成:

  • 命令
  • 作业:一组可执行的命令或步骤
  • 执行者:它们定义了一个作业的步骤将在其中运行的环境,例如 Docker、Machine、macOS 等。除了该环境的任何其他参数

要使用 CircleCI,我们需要一个配置文件,CircleCI 将使用它来安排构建、测试和部署操作。对于这个项目,config.yml文件包含以下代码行:

version: 2.1

orbs:
  aws-ecr: circleci/aws-ecr@6.2.0
  aws-ecs: circleci/aws-ecs@0.0.11

workflows:
# Log into AWS, build and push image to Amazon ECR
  build_and_push_image:
    jobs:
      - aws-ecr/build-and-push-image:
          account-url: AWS_ECR_ACCOUNT_URL
          aws-access-key-id: AWS_ACCESS_KEY_ID
          aws-secret-access-key: AWS_SECRET_ACCESS_KEY
          create-repo: true
          # Name of dockerfile to use. Defaults to Dockerfile.
          dockerfile: Dockerfile
          # AWS_REGION_ENV_VAR_NAME
          region: AWS_DEFAULT_REGION
          # myECRRepository
          repo: '${MY_APP_PREFIX}'
          # myECRRepoTag
          tag: "$CIRCLE_SHA1"
      - aws-ecs/deploy-service-update:
          requires:
            - aws-ecr/build-and-push-image
          aws-region: AWS_DEFAULT_REGION
          family: '${MY_APP_PREFIX}-service'
          cluster-name: '${MY_APP_PREFIX}-cluster'
          container-image-name-updates: 'container=${MY_APP_PREFIX}-service,tag=${CIRCLE_SHA1}' 

我们将使用 GitHub 和 CircleCI。如果您没有 CircleCI 帐户,请创建一个 circle ci 帐户。注册 GitHub。从 CircleCI 仪表板点击添加项目,从显示的列表中添加项目。

添加以下环境变量:

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS _ 默认 _ 区域
  • AWS_ECR_ACCOUNT_URL(本例中为“634223907656 . dkr . ECR . eu-west-2 . Amazon AWS . com”)
  • MY_APP_PREFIX(本例中为“circle ci-demo”)

让我们在main.go文件中修改这一行:

html := "Hello World!" 

html := "Hello World! Now updated with CircleCI" 

将您的更改提交并推送到 GitHub。

您可以通过运行以下命令来确认从终端应用了更改:

$ curl circleci-demo-elb-129747675.eu-west-2.elb.amazonaws.com ; echo

Hello World! Now updated with CircleCI 

从浏览器中也可以确认这一点:

结论

我们已经构建了一个简单的 GO 应用程序,并将其部署到 ECR。现在,我们可以向应用程序添加测试,以确保在更新 ECS 实例之前通过这些测试。虽然本教程使用了一个基本的应用程序,但这是一个成熟的部署管道,适用于许多现实情况。

此外,使用 CircleCI orbs 通过简化我们编写 CircleCI 配置的方式提高了生产率。orb 也可以共享,这通过在我们的配置文件中反复使用预构建的命令、作业和执行器来节省时间。orb 不限于 CircleCI + ECS 部署。您可以浏览 Orb 注册表中可用 Orb 的完整列表,找到符合您选择的云平台、编程语言等的 Orb。


Dominic Motuka 是 Andela 的 DevOps 工程师,在 AWS 和 GCP 支持、自动化和优化生产就绪部署方面拥有 4 年多的实践经验,利用配置管理、CI/CD 和 DevOps 流程。

阅读多米尼克·莫图卡的更多帖子

使用这些指标充分利用您的工程团队| CircleCI

原文:https://circleci.com/blog/use-these-metrics-to-get-the-most-out-of-your-engineering-team/

我领导软件团队已经超过 20 年了,我学到的关于度量的一件事是,领导者倾向于过于强调工程度量,而没有考虑更大的图景。

在与一系列工程行业领导者交谈并仔细研究了全球软件团队处理的数百万份工作后,我们发现最具洞察力和相关性的指标分为三类:

  • 速度指标
  • 士气指标
  • 业务指标

对你的团队来说,衡量哪些指标是有意义的?哪些不是?听一听,找出答案。

速度指标

工程速度度量测量软件交付管道的速度和效率——这是管理者通常最关注的度量类别。虽然我会解释为什么不是跟踪的唯一重要类别,但是速度指标在帮助团队识别减速并找到优化其整体性能的方法方面是至关重要的。

一些最常见的速度指标包括:

  • 吞吐量
  • 更改提前期
  • 冲刺速度
  • 持续时间
  • 平均恢复时间
  • 接通率

根据您的工作结构定制吞吐量

数据处理和审查平台 Zapproved 的前工程主管摩西·门多萨(Moses Mendoza)使用吞吐量来了解他的团队的工作节奏。

“吞吐量帮助我们识别和理解速度,但系统的吞吐量也受到其主要约束的限制,”Mendoza 说。“吞吐量将向您显示事件链中最慢的问题是什么,但它不会向您显示如何修复它以加快您的工作。”

我团队中的一位工程经理 Graeme Harvey 强调,对所有工程师来说,为他们的团队定制吞吐量测量是非常重要的。

“因为我们的团队实行结对编程,所以测量吞吐量并不能与个人的生产力联系起来,”Harvey 说。

当谈到生产能力时,他的工程师们为团队而不是个人进行优化。结对编程和互相帮助可能感觉像是在阻碍个人的进步,但实际上,它将精力重新集中在对团队和最终业务最重要的事情上。

虽然吞吐量是一个有价值的指标,可以帮助您跟踪输出,但是没有一种通用的方法来衡量它。准确地测量吞吐量需要您评估团队的结构和您的工作方式。

衡量交付时间以确定沟通问题和管道质量

根据软件配置工具 Puppet 的前增长副总裁 Alex Bilmes 的说法,有两种方法可以衡量变更的交付时间。“衡量变革准备时间的一种方法是,看一下提出一个想法和这个想法实现完整周期需要多长时间。另一种方法是查看部署交付周期,它衡量开发人员将变更推向生产后,需要多长时间才能投入生产。”

完整的变更准备时间将指出沟通和理解中的问题,以及您积压工作的深度。部署交付时间更有可能显示您的管道和工具的质量。

使用 sprint velocity 来规划工作负载

冲刺速度衡量一个团队在一次冲刺中可以完成的工作量,可以用来计划和衡量团队绩效。

视频会议平台 Livestorm 的首席技术官 Tom Forlini 在测量速度时更深入地研究了三个更小的指标:

  • 问题和故事点的数量
  • 已完成的问题数量与计划的数量
  • 根据类型分配的问题百分比

“Livestorm 工程师进行为期两周的冲刺,每次冲刺有 50 个故事点,”Forlini 说。“我们跟踪已完成问题的数量与计划的数量,因为这为我们提供了产品和技术之间 sprint 计划质量的良好指标。”

然后,他的团队按类型查看问题的百分比。“当一个 sprint 只包含新的特性问题时,我们从一开始就知道这可能是一个非常具有挑战性的 sprint,”Forlini 补充道。"理想情况下,你应该尽可能通过 sprint 来平衡问题的类型."

士气指标

士气度量可能是工程中最容易被忽视的度量类别。他们告诉你工程师对他们的工作质量和工作快乐的感觉,这是一个主要的保留因素。保持高保留率意味着保持高昂的士气。

一些常见的士气指标包括:

  • 个人和团队士气
  • 代码质量置信度

通过直接询问工程师来衡量士气

在 Zapproved,门多萨追踪士气以监控员工的忠诚度。“我们通过调查、对话以及让经理在一对一的会议中深入了解员工的感受,来衡量工作中的士气。”

如果对调查的回应非常积极,你会想知道什么在起作用,以及如何复制这种积极的工作环境。同样,如果回答是否定的,直接从你的团队中找出他们为什么会有这种感觉,以及你能做些什么来解决这个问题是很有帮助的。

关注信心而非覆盖面

zap 的门多萨通过与团队经理和他们的 scrum 大师一起回顾每一次冲刺,证明了他的自信。门多萨说:“当我们在两三次冲刺中测量代码质量信心时,如果我们看到代码质量下降,这意味着团队如何计划他们对工作的个人投资有问题。”

我领导的工程经理也通过信心来衡量工作。

Harvey 补充道:“把信心放在覆盖率上作为一个度量要求重点不在代码覆盖率上。”“关键是要打破对 80%或 90%代码覆盖率的依赖,然后交付时才发现代码已经损坏。测试覆盖率是代码可信度的部分代理。如果你知道 95%的代码都经过了全面测试,而只有 20%,那么你会非常有信心,如果你的测试通过了,你的代码就是合法的。”

Harvey 的团队专注于快速交付小的迭代。这提供了团队正在构建高质量的东西的信心,没有任何东西被破坏,并且他们在为仪表板构建工具方面做出了正确的选择。

业务指标

工程师做的每一件事都应该推动公司前进。这就是为什么跟踪业务指标也很重要。

一些常见的业务指标包括:

  • 公司增长漏斗指标
  • 最终用户价值

跟踪您的业务扩展速度

跟踪业务指标是您的团队有效适应用户增长的方式。来自的朱一新(音译)认为,虽然考察工程执行指标很重要,但了解企业目标和衡量公司增长也很重要。

随着的指数级增长,为了朱的工程团队取得成功,跟踪业务指标变得异常重要。“当你谈论每六个月翻一番时,你必须跟踪它,以了解你需要构建什么,预计会有什么降级,你需要多少数据中心,多少机箱,等等。”,朱说道。

简而言之,工程师必须密切关注实时业务指标,以便准确地进行规划和计划。“你必须积极主动,”朱补充道。

充分利用有意义的指标

以下是一些帮助你从工程工作中获得最大价值的建议:

  • 定义业务目标。你的工程师应该知道你的企业想要完成什么。当工程师了解公司的发展方向时,他们就明白自己在朝着什么方向努力。
  • 经常检查指标。每次会议开始时都要回顾指标,这样团队就能确定你做得对的地方和需要改进的地方。
  • 从小处着手。你只需要一到三个指标就可以开始了。如果您有意跟踪简单的指标,尝试不同的方法,并观察这些指标的发展,您可以随着时间的推移添加指标。
  • 不要只看执行指标。工程团队的成功不仅仅取决于工程指标。将工程度量与业务度量配对将帮助您理解为什么您每天都在构建您正在构建的东西。

本文原载于VentureBeat

使用 CircleCI webhooks | CircleCI 创建集成、仪表板、通知等

原文:https://circleci.com/blog/using-circleci-webhooks/

Webhooks 允许服务和 API 之间的通信,这使它们成为我们互连的、基于云的应用环境的粘合剂。如果你熟悉 API,你可以学习使用 webhooks。CircleCI 为我们的 CI/CD 平台提供了一个 webhooks 特性,允许您订阅和响应 CircleCI 事件,例如工作流和作业完成。

本教程展示了 webhooks 特性,并给出了入门步骤。它是为那些已经在使用 CircleCI 进行项目建设的人设计的。还有一个免费的样本 NodeJS webhook 消费者项目

webhooks 和 API 有什么不同?

webhooks 和 API 的主要区别在于,对于开发者来说,API 是一种体验,而 webhooks 是一种体验。客户端从 API 请求数据。如果客户端需要更多数据,或者需要检查数据是否已经更新,它会发出另一个请求。使用 webhook,一个服务(客户端)被设置为 webhook 端点,您将该端点交给另一个服务。另一个服务(在我们的例子中是 CircleCI)在您感兴趣的事件发生时调用您的 webhook 端点。webhooks 有很多用途,从 GitHub 集成到 Discord 聊天机器人、Slack 通知等等。有成百上千种可能性。

CircleCI 允许您为项目中的工作流和作业完成事件设置 webhooks。当每个事件成功或不成功完成时,都会触发这些事件。

如果没有 webhooks,这只能通过重复轮询 CircleCI APIs 来实现。为了以这种方式使用 API,服务必须重复请求以获得最新的数据。这种方法对于开发团队来说并不理想,因为他们必须维护一个每天发出数百个(如果不是数千个)请求的服务。重复的 API 请求对 CircleCI 来说也不是什么好事,因为我们必须满足所有成千上万的请求。

webhooks 解锁的用例有哪些?

在 CI/CD 环境中,webhooks 允许您构建各种集成,如仪表板、数据记录和项目管理工具。Webhooks 使您的团队能够自动通知下游客户,例如,通知他们需要更新的新版本。您可以在单个项目或多个项目上设置 webhooks,然后聚合它们的事件。然后,您可以在后端以适合您的用例的方式处理它们。

设置 CircleCI webhook

要使用 CircleCI web 界面在单个项目上设置新的 webhook,请转到您的 CircleCI 项目的项目设置。选择 Webhooks 部分,然后点击添加 Webhook 按钮。

Setting up a webhook interface

创建 webhook 时可以传递的选项有:

  • 名字
  • webhook 将发送到的端点的 URL
  • 要发送哪些事件
  • 证书验证
  • 秘密价值

在示例图像中,我们使用 Ngrok 隧道从本地服务器生成的临时 URL 和一个秘密令牌super-secret-1234。当然,这只是举例说明一个观点;你的秘密将会不同。

点击保存网页挂钩将其打开。CircleCI 会将所有工作流完成事件发送到您在 URL 字段中输入的端点。

处理有效负载并响应 webhook

webhook 将有效负载作为 POST 请求主体发送到您在设置它时指定的端点。

处理 webhook 的服务器必须发送一个 200 状态码作为响应,否则 webhook 可能会被重新发送多次,这可能会导致您这边出现重复事件。最佳实践是将所有传入的 webhook 有效负载传递给一个消息队列,比如 RabbitMQ 或亚马逊 SQS,并异步处理它们。

webhook 有效负载中有什么?

有效负载包含有关已完成工作流的信息,包括其名称、状态、计时、项目和管道、与之关联的提交以及开始管道执行的触发器。对于 VCS 提交请求,流水线触发器的值将是webhook。另外两个选项是:apischedule

以下是完整工作流有效负载的示例:

{
  type: 'workflow-completed',
  id: 'f85a2fd5-108d-38e2-a990-fb49e7001515',
  happened_at: '2021-06-01T11:23:14.146Z',
  webhook: {
    id: '1154a973-e434-407c-8051-9259d8a53e99',
    name: 'Workflow Completed'
  },
  workflow: {
    id: '89b02ea5-7fca-4017-a902-747e68235ffd',
    name: 'do-all-the-things',
    status: 'success',
    created_at: '2021-06-01T11:23:07.441Z',
    stopped_at: '2021-06-01T11:23:13.942Z',
    url: 'https://app.circleci.com/pipelines/github/zmarkan/very-simple-non-app/26/workflows/89b02ea5-7fca-4017-a902-747e68235ffd'
  },
  pipeline: {
    id: 'ac0cc08f-67a2-4adb-9fa6-db633e390670',
    number: 26,
    created_at: '2021-06-01T11:23:07.371Z',
    trigger: { type: 'webhook' },
    vcs: {
      provider_name: 'github',
      origin_repository_url: 'https://github.com/zmarkan/very-simple-non-app',
      target_repository_url: 'https://github.com/zmarkan/very-simple-non-app',
      revision: 'f1e37011b5ff2d1703d4de9143c52ba650acae53',
      commit: [Object],
      branch: 'streamline'
    }
  },
  project: {
    id: 'e5c5dce3-7ef8-4227-9888-6b2b97b0a5ab',
    name: 'very-simple-non-app',
    slug: 'github/zmarkan/very-simple-non-app'
  },
  organization: { id: '8995ddc0-b07a-4bc2-8eb6-816c67a512fa', name: 'zmarkan' }
} 

webhook 中的所有对象都包含唯一的id字段。如果您需要来自 CircleCI 的更多上下文,您可以使用一个id来查询 CircleCI APIs。

验证 webhook 签名

你的 webhook 端点可以从 web 上的任何地方接收事件,而不仅仅是 CircleCI,不管是谁知道端点地址。您无法控制请求来自何处,因此验证事件发送方的完整性非常重要。你需要确保你没有让恶意的请求进来。

为了确保请求的安全性,webhooks 允许秘密验证。当你建立一个新的 webhook 时,你可以传入一个secret值。在示例中,我们使用了super-secret-1234。CircleCI 可以使用它来创建 webhook 请求体的 HMAC SHA256 摘要。当 webhook 有效载荷被接收时,秘密摘要在一个circleci-signature报头中。该值将是形式为circleci-signature: v1=YOUR_HMAC_SHA256_DIGESTv1参数。

您可以通过使用相同的密钥和相同的算法创建您自己的请求主体的摘要来验证请求,然后将它们与circleci-signature头中的内容进行比较。如果两个值相同,您可以假设它确实来自 CircleCI。不匹配的签名可能意味着它是由恶意参与者发送的。

下面是来自样本 NodeJS Express 应用程序的示例片段:

// req, res are Express objects

let signature = req.headers["circleci-signature"].substring(3)
const key = "super-secret-1234" // Same string as used in webhook setup
let testDigest = crypto.createHmac('sha256', key).update(JSON.stringify(req.body)digest('hex')

if (testDigest !== crypto.sign) {
    console.log("Webhook signature not matching")
    console.log(`Signature: ${signature}`)
    console.log(`Test digest: ${testDigest}`)
    res.status(403).send("Invalid signature")
} 

使用 webhooks 创建仪表板集成

一旦您能够接收和处理 webhooks,您就可以用它们构建许多类型的集成。我已经创建了一个示例仪表板,它显示了使用 Vue 和 Express 进入的工作流的状态。

Sample webhooks dashboard

你可以在 GitHub 上找到源代码。我用来测试 webhooks 的项目在 GitHub 上也有

结论

CircleCI webhooks 是将 CircleCI pipelines 与您的其他业务相集成的强大方法。从仪表板到项目跟踪再到通知,webhooks 为创建获取和共享信息的新方法提供了许多可能性。在本教程中,您已经学习了如何开始为项目设置 webhook,以便在工作流完成时通知您。既然您已经了解了一些处理 webhooks 和确保系统安全的最佳实践,我希望您和您的团队能够找到许多方法在自己的项目中使用它们。

如果你对我接下来要讨论的话题有任何反馈或建议,请通过 Twitter - @zmarkan 联系我。

使用 CircleCI 工作流复制 Docker Hub 自动构建| CircleCI

原文:https://circleci.com/blog/using-circleci-workflows-to-replicate-docker-hub-automated-builds/

发布者声明 : 支持工程师 Nick Bialostosky 于 2020 年 10 月 9 日更新了此内容,以反映 Docker Hub 对访问令牌支持的变化。


CircleCI workflows 是一项强大的功能,可用于使您的部署过程简单直观。在本文中,我们将学习如何使用它们将图像自动推送到 Docker registry,就像 Docker Hub 自己的自动构建过程一样,但是具有您自己的构建过程提供的所有定制功能。

CircleCI 工作流程

一个工作流是一组规则,用于定义一组作业及其运行顺序。工作流使用一组简单的配置密钥支持复杂的作业编排,帮助您更快地解决故障。- CircleCI 工作流程文件

在我们的工作流中,将提交推送到master分支将运行一个特定的作业,该作业在 Docker Hub 上发布带有latest标签的图像。每次提交还会向图像添加一个 Git 标记作为图像标记,并发布带有该标记的图像。最后,我们将研究如何使用 CircleCI 的环境变量来自动发布图像的增量版本,而不是使用 Git 标签。

建立我们的码头工人形象

让我们从一个基本的 Docker 映像开始,它只打印运行时传递的参数。docker 文件看起来就像这样简单:

FROM alpine:3.8

ENTRYPOINT [ "echo" ] 

在本地构建和运行映像应该向我们显示它工作正常:

$ docker build -t building-on-ci .
Sending build context to Docker daemon  4.608kB
Step 1/2 : FROM alpine:3.8
 ---> 196d12cf6ab1
Step 2/2 : ENTRYPOINT [ "echo" ]
 ---> Running in fe2151b22bc1
Removing intermediate container fe2151b22bc1
 ---> edc0ca6e654a
Successfully built edc0ca6e654a
Successfully tagged ci-workflows:latest

$ docker run building-on-ci "Hello!"
Hello! 

现在我们有了我们的图像,我们需要把它推送到我们的 GitHub 库。

为了开始在每次提交时构建我们的映像,我们将用下面的配置文件引导我们的项目。在项目的根目录中,创建一个.circle目录,并将下面几行保存为config.yml:

version: 2
jobs:
  build:
    environment:
      IMAGE_NAME: jonathancardoso/building-on-ci
    docker:
      - image: circleci/buildpack-deps:stretch
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Build Docker image
          command: docker build -t $IMAGE_NAME:latest .
workflows:
  version: 2
  build-master:
    jobs:
      - build:
          filters:
            branches:
              only: master 

我们使用 CircleCI 提供的名为circleci/buildpack-deps的预构建 Docker 映像来运行我们的构建。CircleCI 在 Docker Hub 上有许多自定义映像,它们通过在 CI/CD 环境中有用的额外工具扩展了官方映像。查看 CircleCI 图像文档了解更多信息。

还有一个名为IMAGE_NAME的环境变量,我们将使用它来设置 Docker 图像的名称。您必须将其更改为您的用户名/组织名,而不是使用我的(jonathancardoso)。否则,当您试图将该图像发布到 Docker Hub 时,将会出现一个错误。

目前,我们的配置只有一个名为build-master的工作流,当我们向master分支提交时,它运行一个build任务。这份工作只是建立我们的码头工人形象,仅此而已。

我们的一个步骤叫做setup_remote_docker,是在 CircleCI 上构建 Docker 图像时最重要的步骤之一。因为我们的作业步骤是在 Docker 映像中运行的,所以它们不能构建其他 Docker 映像。setup_remote_docker命令告诉 CircleCI 分配一个新的 Docker 引擎,与运行我们作业的环境分开,专门执行 Docker 命令。查看建筑 Docker 图像文档了解更多信息。

执行示例:https://circleci.com/gh/JCMais/docker-building-on-ci/1

将映像发布到 Docker Hub,并在每次提交到主机时使用:latest 标记

既然我们在每个对master的提交上构建了我们的映像,我们需要创建一个新的作业来将该映像发布到 Docker Hub。

Docker Hub 没有可用于访问您的帐户的访问令牌,因此我们需要使用自己的用户名/密码登录 Docker Hub 注册表。这通常通过运行以下命令来完成:

docker login -u "username" -p "password" 

注意 : Docker Hub 现在支持访问令牌,因此如果您愿意,您可以使用令牌代替密码来执行以下指令。

然而,像这样以明文形式保存您的凭证是一种不好的做法,因为它会暴露您的密码。我们需要用我们的用户名和密码创建新的 CircleCI 环境变量。在 CircleCI 中有多种方法可以做到这一点,但是如果多个项目要将图像推送到 Docker Hub,推荐的方法是使用上下文。否则,我们可以直接在项目设置中设置它们。对于本文,我们将采用后一种方法。

在设置了环境变量DOCKERHUB_USERNAMEDOCKERHUB_PASS之后,让我们创建我们的作业:

version: 2
jobs:
  build:
    environment:
      IMAGE_NAME: jonathancardoso/building-on-ci
    docker:
      - image: circleci/buildpack-deps:stretch
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Build Docker image
          command: docker build -t $IMAGE_NAME:latest .
  publish-latest:
    environment:
      IMAGE_NAME: building-on-ci
    docker:
      - image: circleci/buildpack-deps:stretch
    steps:
      - setup_remote_docker
      - run:
          name: Publish Docker Image to Docker Hub
          command: |
            echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
            docker push $IMAGE_NAME:latest
workflows:
  version: 2
  build-master:
    jobs:
      - build:
          filters:
            branches:
              only: master
      - publish-latest:
          requires:
            - build
          filters:
            branches:
              only: master 

我们向build-master工作流添加了一个新的publish-latest任务。这项工作试图推动我们刚刚建立的形象到 Docker 枢纽。然而,如果我们提交变更并检查构建日志,我们会看到它失败了,因为 Docker 找不到映像jonathancardoso/building-on-ci

这是因为工作流中的作业相互隔离,我们在build作业上构建的映像对publish-latest不可用。

请注意我们在配置文件中的重复。我们用环境变量IMAGE_NAME的定义和将用于运行我们的作业的 Docker 映像重复了两次。

解决重复问题的一个方法是使用 YAML 锚。然而,CircleCI 已经发展了,在版本2.1中,我们现在可以通过指定一个可重用执行器来直接重用一些配置。在文件的顶部,我们将把版本从2更改为2.1,并添加一个新的executors密钥:

version: 2.1
executors:
  docker-publisher:
    environment:
      IMAGE_NAME: building-on-ci
    docker:
      - image: circleci/buildpack-deps:stretch 

现在,对于每个作业,我们将指定一个executor键,而不是声明dockerenvironment键:

# ...
jobs:
  build:
    executor: docker-publisher
    # ...
  publish-latest:
    executor: docker-publisher
    # ... 

重复已解决。是时候解决我们的 Docker 图像在publish-latest作业中不可用的问题了。我们将使用工作流的工作区在两个作业之间共享图像。

让我们在build任务的末尾添加一个新的运行步骤。该运行步骤将图像保存为 tar 归档文件:

 # ...
      - run:
          name: Archive Docker image
          command: docker save -o image.tar $IMAGE_NAME
      # ... 

现在,我们可以通过添加另一个名为persist_to_workspace的步骤将该文件持久化到工作流工作区中:

 # ...
      - run:
          name: Archive Docker image
          command: docker save -o image.tar $IMAGE_NAME
      - persist_to_workspace:
          root: .
          paths:
            - ./image.tar
      # ... 

为了检索publish-latest作业中的image.tar文件,我们将在其他步骤之前添加一个新步骤attach_workspace:

 # ...
   steps:
      - attach_workspace:
          at: /tmp/workspace
      # ... 

在我们试图将存档的图像推送到 Docker Hub 注册表之前,用docker load加载它:

 # ...
      - run:
          name: Load archived Docker image
          command: docker load -i /tmp/workspace/image.tar
      # ... 

我们的完整配置文件如下所示:

version: 2.1
executors:
  docker-publisher:
    environment:
      IMAGE_NAME: jonathancardoso/building-on-ci
    docker:
      - image: circleci/buildpack-deps:stretch
jobs:
  build:
    executor: docker-publisher
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Build Docker image
          command: |
            docker build -t $IMAGE_NAME:latest .
      - run:
          name: Archive Docker image
          command: docker save -o image.tar $IMAGE_NAME
      - persist_to_workspace:
          root: .
          paths:
            - ./image.tar
  publish-latest:
    executor: docker-publisher
    steps:
      - attach_workspace:
          at: /tmp/workspace
      - setup_remote_docker
      - run:
          name: Load archived Docker image
          command: docker load -i /tmp/workspace/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
workflows:
  version: 2
  build-master:
    jobs:
      - build:
          filters:
            branches:
              only: master
      - publish-latest:
          requires:
            - build
          filters:
            branches:
              only: master 

现在一切都应该是绿色的!

现在,我们可以从 Docker Hub 注册表中检索并运行我们的映像:

$ docker run jonathancardoso/building-on-ci "Workflows are awesome!"
Unable to find image 'jonathancardoso/building-on-ci:latest' locally
latest: Pulling from jonathancardoso/building-on-ci
4fe2ade4980c: Already exists
Digest: sha256:f719ca403b0fc6a5214823c61c750a5e48ce5809a24b882b3a45d6d119870366
Status: Downloaded newer image for jonathancardoso/building-on-ci:latest
Workflows are awesome! 

发布 Git 标签作为坞站中心上的坞站图像标签

依赖带有标签的图像很容易引起混乱,因为我们无法控制运行的图像版本。推荐的方法是使用特定的标签。

假设我们将使用 Git 标签来发布 Docker 图像的新版本,并且我们希望 CircleCI 在 Docker Hub 上发布带有每个 Git 标签的图像作为图像标签。以下是我们的做法。

首先,我们将添加一个新的工作流,build-tags:

# ...
  build-tags:
    jobs:
      - build:
          filters:
            tags:
              only: /^v.*/
            branches:
              ignore: /.*/
      - publish-tag:
          requires:
            - build
          filters:
            tags:
              only: /^v.*/
            branches:
              ignore: /.*/ 

这两个作业共享相同的过滤器,基本上就是说它们必须为每个以v开头的标签运行,而不应该为分支执行。

build作业是相同的(我们可以在工作流之间重用作业),但是publish-tag作业将如下所示:

# ...
  publish-tag:
    executor: docker-publisher
    steps:
      - attach_workspace:
          at: /tmp/workspace
      - setup_remote_docker
      - run:
          name: Load archived Docker image
          command: docker load -i /tmp/workspace/image.tar
      - run:
          name: Publish Docker Image to Docker Hub
          command: |
            echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
            IMAGE_TAG=${CIRCLE_TAG/v/''}
            docker tag $IMAGE_NAME:latest $IMAGE_NAME:$IMAGE_TAG
            docker push $IMAGE_NAME:latest
            docker push $IMAGE_NAME:$IMAGE_TAG
# ... 

这个作业和publish-latest的区别在于,这个作业使用了 CircleCI 自动设置的环境变量CIRCLE_TAG。我们检索该值并删除v前缀,例如v0.0.1变成了0.0.1。然后,我们继续用该版本标记我们的映像,并将其推送到 Docker Hub 注册中心。我们还要确保按下latest标签,这样两者就能保持同步。如果我们提交一个标签,我们可以看到它的工作:

如果我们访问图像的 Docker Hub 页面,我们可以看到标签:

现在,我们可以使用我们的标签运行我们的图像:

docker run jonathancardoso/building-on-ci:0.0.1 "Tags are working!"
Unable to find image 'jonathancardoso/building-on-ci:0.0.1' locally
0.0.1: Pulling from jonathancardoso/building-on-ci
4fe2ade4980c: Already exists
Digest: sha256:1a3a6c74860832704df55acdcbd875f662957d757d69fa340a48b41b890469bc
Status: Downloaded newer image for jonathancardoso/building-on-ci:0.0.1
Tags are working! 

每个构件的自动图像标签

如果我们有一个不经常使用的图像或者一个我们不想使用 git 标签的图像怎么办?一个我们想要严格控制使用哪个版本的图像,所以我们不能依赖于latest标签?

CircleCI 允许我们这样做:它公开了一个名为CIRCLE_BUILD_NUM的环境变量,这个变量的数量一直在增加。使用它,我们可以让 CircleCI 为每个构建发布一个新版本的映像。让我们修改一下我们的publish-latest作业来做到这一点:

 # ...
      - run:
          name: Publish Docker Image to Docker Hub
          command: |
            echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
            IMAGE_TAG="0.0.${CIRCLE_BUILD_NUM}"
            docker tag $IMAGE_NAME:latest $IMAGE_NAME:$IMAGE_TAG
            docker push $IMAGE_NAME:latest
            docker push $IMAGE_NAME:$IMAGE_TAG
      # ... 

我们使用CIRCLE_BUILD_NUM创建图像标签,并将其与latest标签一起发布到 Docker Hub 注册中心。如果我们将它提交给 master 并等待工作流完成,我们可以看到它已发布:

摘要

这些版本控制技术只是构建模块。您可以根据需要将图像版本的标记变得复杂或简单。对于特定的标签,您甚至可以使用这些知识将图像上传到您自己的私有注册表,而不是 Docker Hub。

撰写本文时使用的资源库可以在 GitHub 上找到:https://github.com/JCMais/docker-building-on-ci


Jonathan Cardoso 热衷于 DevOps、开源和游戏。他有六年的经验,帮助世界各地的公司通过技术解决方案推进他们的目标。他目前在巴西远程工作,是 HelloMD 的全栈开发人员和 DevOps 专家。

阅读乔纳森·卡多佐的更多帖子

充分利用 Docker 和工作流,第 1 部分:在 CircleCI 上有效地使用 Docker

原文:https://circleci.com/blog/using-docker-effectively-on-circleci/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


作为一名成功的工程师,我经常在 CircleCI 上看到很多关于使用 Docker 的困惑。在 CircleCI 2.0 发布之前,无需考虑 Docker 就可以构建和测试应用程序:我们所有的构建都在 LXC 容器中运行,这些容器由我们的推理引擎配置,以相当自动的方式包含任何必要的工具和依赖项。

然而,随着 CircleCI 2.0 的发布,我们将 Docker 放在了构建过程的前端和中心:任何 Docker 映像,公共的或私有的(我们现在也支持托管在亚马逊 EC2 容器注册表上的私有映像!),可以是生成的主容器。这增加了构建过程的能力和定制,特别是当与工作流结合时,它允许多组作业(构建、测试、部署等)。)串在一起,有时并行运行,每个都有自己的基本 Docker 映像。为了帮助从 CircleCI 1.0 过渡到 2.0,我们甚至提供了广泛的便利图像来满足常见的语言和依赖需求,调整以最大限度地兼容 CircleCI 平台。

然而,正如他们所说,权力越大,责任越大。使用 Docker 可能会令人生畏。对于一些开发人员来说,它似乎有很高的学习曲线。其他人担心维护或保养他们创造的任何图像。此外还有隐私问题——毕竟,Docker 主注册中心 Docker Hub 上的许多图片是完全公开的。但是当你在 CircleCI 2.0 上构建和测试项目时,Docker 会是一个有价值的盟友。下面,我们概述了一些人们在使用 CircleCI 2.0 时遇到的常见问题,以及 Docker 如何帮助解决这些问题。

问题 1:构建时间太长

在 CircleCI 上运行一个构建可能需要很长时间的原因有很多。但是一个常见的因素是安装依赖项。当我帮助客户将项目迁移到 CircleCI 2.0 时,我通常会从添加一些缺失的包开始。不过,很快,这可能会变成一系列的run步骤,而且,即使启用了缓存,也会给大型项目的构建时间增加几分钟。缓解这种情况的一种方法是将这些依赖项卸载到 Docker 映像中。

您的 Docker 映像可能感觉像是与您的 CircleCI 构建分开的东西,但将它视为构建过程的第一步会有所帮助。如果每次运行构建时都要安装相同的几个包,那么没有理由不将它们安装到 Docker 映像中。这样,它们将在您每次开始构建时被预安装。作为一个例子,假设我们使用 CircleCI 便利图像。从这里开始,创建 docker 文件并添加几个简单的步骤来安装我们的依赖项并不困难。您的 docker 文件可能如下所示:

FROM circleci/ruby:2.4.1-node-browsers

USER root

# pulling in commands from 'install os packages'
RUN apt-get update
RUN apt-get install -y qt5-default libqt5webkit5-dev gstreamer1.0-plugins-base gstreamer1.0-tools gstreamer1.0-x postgresql-client

USER circleci 

这告诉 Docker 从我们的circleci/ruby:2.4.1-node-browsers便利图像开始,切换到root安装几个依赖项,然后切换回circleci(我们所有便利图像的默认用户)。

假设您已经创建了 Docker ID 并在您的计算机上安装了 Docker,那么您需要做的就是在本地构建您的映像并将其推送到 Docker Hub:

docker build -t my-docker-id/my-image:my-tag /path/to/Dockerfile docker push my-docker-id/my-image:my-tag

现在,您可以使用您的新映像进行构建,并从您的config.yml文件中删除那些apt-get install命令,节省宝贵的构建时间,同时保持您的config.yml代码简洁明了。更多信息请参见我们的流程演练,或者查看码头工人指南

问题 2:我需要一个特定的[X]遗留版本

通过我们的便利图片,我们试图涵盖大多数语言和软件开发环境的最流行的版本。但是有时候你可能需要一个特定的版本,比如说,Node.js 如果我们不提供呢?

这是我们在 CircleCI 2.0 上与客户合作时遇到的一个相当常见的问题。不访问他们的源代码,很难知道使用特定依赖项的不同版本是否会破坏他们的构建,所以我通常宁可小心谨慎,也尽量复制他们的 1.0 执行环境。在这种情况下,所需要的只是 Dockerfile 中的几行文本来获取我们的便利 Node.js 图像并降级到不同的版本:

FROM circleci/node:6.11

USER root

# delete node 6.11 files
RUN rm -f /usr/local/bin/node /usr/local/bin/nodejs /usr/local/bin/npm /usr/local/share/man/man1/node.1 /usr/local/share/systemtap/tapset/node.stp
RUN rm -rf /usr/local/lib/node_modules /usr/local/include/node /usr/local/share/doc/node

# download node 6.8.1 from here: https://nodejs.org/en/download/releases
RUN wget https://nodejs.org/download/release/v6.8.1/node-v6.8.1.tar.gz
RUN tar xvf node-v6.8.1.tar.gz

WORKDIR node-v6.8.1

# install node 6.8.1 as described here: https://github.com/nodejs/node/blob/master/BUILDING.md#building-nodejs-on-supported-platforms
RUN ./configure
RUN make -j4
RUN make doc
RUN make install

USER circleci 

这里我们从节点 6.11 版本开始,删除所有相关的节点 6.11 文件和目录,下载/编译/安装节点 6.8.1 版本。在这种情况下,有几个节点版本管理工具可能会使这项任务变得更容易( nnvm 是流行的选择),或者我们可以尝试使用 Ubuntu 的apt-get包管理系统,但对我来说,从源代码开始重新编译总是更安全。

一个简单的谷歌搜索可以帮助识别你正在使用的任何语言或工具的升级/降级过程,并且在一天结束时,你仍然可以受益于我们在 CircleCI 上运行的便利图像的优化。从那里开始,过程是相同的:构建您的映像,将其推送到 Docker Hub,并放入您的config.yml

问题 3:但是等等,我不想在我的本地机器上构建 Docker 映像

这实际上很好,因为有了 CircleCI 2.0,您不必这样做。相反,你可以使用我们的 machine执行程序为你的构建启动一个完整的虚拟机,并在 CircleCI 中随心所欲地创建 Docker 映像。或者将docker执行器与 setup_remote_docker一起使用——我们实际上在六月份发布了一篇关于这种方法的整篇博文

您甚至可以建立一个完整的存储库来构建 Docker 映像— 这就是我们的工作方式。随着计划作业的即将发布,您将能够定期使用我们的资源构建您的映像,无需维护。

问题 4:我不希望我的 Docker 图片被公开

您的 Docker 图像可能包含大量私人或敏感数据,您可能不希望这些数据被公众访问。

幸运的是,Docker Hub 支持私人 Docker 图像——这就像在您的 Docker Hub 帐户设置中更改图像的默认可见性一样简单。你甚至可以更进一步,使用完全私有的 Docker 注册表来代替 Docker Hub。


准备好了吗?阅读本系列的第二篇博文,了解如何利用工作流将你新学到的码头工人技能提升到一个新的水平。或者参见高级客户成功工程师 Ryan O'Hara 的这篇精彩讨论文章,更深入地了解如何打造 Docker 形象。

使用洞察力发现不可靠的、缓慢的和失败的测试

原文:https://circleci.com/blog/using-insights-to-discover-flaky-slow-and-failed-tests/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


测试,再测试

在 CircleCI,我们非常重视测试。在运行了超过 6500 万次构建之后,我们学到了一些东西。让团队能够经常更好地测试是我们工作的驱动力。运行测试使您的代码更加可靠。

但是你从你的测试中得到最大的收获了吗?除了简单地实现它们,你是否充分地测试了你的测试?毕竟,测试本身是可靠的。

如果你不能信任失败的测试,为什么要信任通过的测试呢?

不可靠的测试(当代码没有改变时,有时通过,有时失败)并不比根本没有测试好多少。在 Insights 选项卡中,您可以快速查看哪些测试可能是不可靠的,以及哪些是您最失败和运行最慢的测试。虽然我们不能为您重写测试,但我们可以帮助您准确地看到哪些测试是缓慢的、易出问题的和失败的,这样您的团队就可以在支持您的测试时集中精力。

你只能管理你能衡量的东西

但是不要相信我们的话!我们自己的测试并不总是很好。他们过去很慢,这很令人沮丧。但是我们不知道为什么,也不知道具体是哪一个慢。工程师 Marc O'Morain 知道他不能加快每个人的测试速度,但他可以做下一件最好的事情:羞耻鼓励他们自己做。

他对构建页面做了一个小小的修改,这样它将默认显示最慢的测试所花费的时间。他的希望是让人们感到内疚,从而加速他们的测试。而且成功了。发货后,我们都可以看到我们最慢的测试花了 2 分 20 秒,没多久就有人解决了。

现在我们的团队意识到了我们测试操作中的弱点,我们能够改进最糟糕的违规测试并加快我们的测试。

我们希望帮助您从测试中获得最大收益,因此以下是我们最喜欢的工具,现已提供给所有 CircleCI 用户:

失败的测试:你的哪些测试最失败?Failed Tests

最慢的失败测试:在你的失败测试中,哪些是最慢的?当试图加速你的测试时,从这些开始。Slowest Failed Tests

成功率:一个分支上有多少构建是成功的?在 master 上,测试应该 100%通过,任何更低的百分比都可能表明你的武器库中的测试可能是不可靠的。Success Rate1.png

使用 CircleCI 转轮进行本地测试| CircleCI

原文:https://circleci.com/blog/using-runner-for-local-testing/

在本文中,您将学习如何在本地机器上设置 CircleCI runner 代理来运行测试。您还将学习如何配置 CircleCI 管道,以便正确调用运行器。

许多开发团队从运行他们测试的本地构建盒(或者六个)开始他们的 CI/CD 之旅。例如,在我工作的几个移动团队中,我们有几个插有物理设备的 Mac Mini 盒子,用于运行本地 UI 和单元测试。最终,我们迁移到了基于云的解决方案,这给我们带来了更好的稳定性和许多新功能。但是迁移到云也意味着我们的本地硬件过时了。我们不得不依靠仿真,或者选择另一个基于云的设备场来实现。

有了 CircleCI 的 runners,你可以继续使用自己的设备进行测试,同时受益于 CircleCI cloud 中的其他任何东西。您还可以使用运行器进行测试,这些测试需要在您自己的基础设施上运行,用于安全关键的场景,或者当您正在开发自己的硬件时。

随着 CircleCI 免费计划的推出,像跑步者这样的功能现在每个人都可以使用。在本文中,您将学习如何在本地机器上设置 CircleCI runner 代理来运行测试。您还将学习如何配置 CircleCI 管道,以便正确调用运行器。

先决条件

本教程假设您对 CircleCI 有所了解,并且了解如何配置管道来构建和测试软件项目。

我已经创建了一个显示流道配置的示例项目。

您还可以在示例项目的 CircleCI 仪表板中找到它的构建

注意 : 这个例子展示了一个 Android 应用程序和测试,使用了 macOS 上的 runner。然而,其背后的原则可以普遍适用:适用于 iOS 应用程序、定制硬件或不同基础设施上的 runner,如 Docker 容器、Windows、Linux 机器等。

跑步者如何工作

runner 是安装在基础设施上的一个进程,它与 CircleCI 通信。运行者作为他们自己的资源类。您的config.yml仍然定义管道、作业和工作流,但是您没有使用 CircleCI 的基于云的资源,如容器和虚拟机,而是自己执行。

首先,在本地安装 runner 代理。这是一个后台运行的守护进程,检查来自 CircleCI 的提示。当 CircleCI 发出必须在运行程序上触发作业的信号时,代理将执行工作负载。它还可以访问与 CircleCI 云版本相同的凭证,因此它可以无缝运行,并可以从您的存储库中获取代码。

一旦作业完成,结果和任何工件都被发送回云服务。只有实际的作业执行是在您的基础设施上完成的。

履行

实施由以下过程组成:

  • 在本地设置运行程序代理
  • 从你的圈子中调用跑步者config.yml

设置跑步者代理

安装 runner 代理取决于您使用的平台。请参考 CircleCI 文档中的设置说明。

对于我的示例应用程序,我在 Mac 计算机上安装了 runner 代理。

注意 : 这些步骤只是一个简要的概述,并不是完整的指南。

设置流道的主要步骤是:

  1. 在您的组织中注册一名新的跑步者
  2. 按照适用于您的平台的说明安装运行程序代理
  3. 启动 runner 守护进程,并确保它在系统启动时运行

使用circleci CLI 工具在您的组织中注册新的跑步者。如果这是您的第一个跑步者,您可能需要在该组织中创建一个名称空间。您将使用完全限定名引用 CircleCI 配置中的特定 runner。对我来说,那就是zan_demos_namespace/mac_runnermac_runner是跑步者的名字,zan_demos_namespace是我使用的名称空间。

以下是一个命令示例:

$ circleci runner resource-class create zmarkan-demos-namespace/local-mac-runner "Local Mac OS Runner" --generate-token 

这个命令还将为这个 runner 设置一个新的 API 令牌,以便与组织中的 CircleCI API 一起使用。因为有了--generate-token标志,这才成为可能。请确保存储该令牌以备后用。

向 CircleCI 注册转轮后,按照文档中的说明安装转轮代理。设置需要预装curlsha256sumtargzip等软件。可能需要其他软件。

设置一些守护程序脚本,并传入这些值:

  • 在上一步中创建的 API 令牌
  • 跑步者名称空间
  • 您指定的名称

这些项目的细节因您使用的平台而异。例如,在 Mac 上你需要创建一个launchd.plist并用launchctl启动它。在 Linux 上,这将是一个以systemctl开始的新服务。

从管道调用运行程序

一旦 runner 代理开始运行,您就可以从.circleci/config.yml文件中的一个作业调用它。确保用您的运行程序的名称空间和运行程序名称指定machine执行器类型和资源类。这里有一个例子:

 runner-test:
    machine: true
    resource_class: zmarkan-demos-namespace/local-mac-runner
    environment:
      JAVA_HOME: /Users/zmarkan/libs/jdk-11.0.2.jdk/Contents/Home
      ANDROID_SDK_ROOT: /Users/zmarkan/Library/Android/sdk
    steps:
      - checkout
      - run:
          name: Run connected check on local runner
          command: |
            ./gradlew connectedDebugAndroidTest 

在我的例子中,我有一些 Android 设备连接到我的 Mac 上,所以跑步者可以访问本地硬件来运行测试。

在使用 CircleCI 自己的基础设施时,我还需要传入一些现成的特定值,比如JAVA_HOMEANDROID_SDK_ROOT。这是因为它们在 CircleCI runner 使用的特定 shell 中不容易访问。在大多数现代 MAC 电脑上,使用的外壳是 ZshCircleCI 默认运行 Bash。

剩下的步骤很简单,取决于您的应用程序和您想要执行的内容。在我的情况下,就是从回购中检查代码并运行测试。您还可以使用store_artifactsstore_test_results来处理测试并保存输出。然后,您可以在 CircleCI 云仪表板中看到输出。

结论

在本文中,您了解了如何设置一个本地 CircleCI 运行程序,以便在本地基础设施上执行 CircleCI 云工作负载。Runner 有许多使用案例,从安全性到在定制硬件上的执行,都具有基于云的 CircleCI 提供的相同 CI/CD 体验。

如果你对我接下来要讨论的话题有任何反馈或建议,请通过 Twitter - @zmarkan 联系我。

计划管道入门| CircleCI

原文:https://circleci.com/blog/using-scheduled-pipelines/

CircleCI 的预定管道让你定期运行管道;每小时、每天或每周。如果您使用过计划的工作流,您会发现用计划的管道代替它们会给您更多的能力、控制和灵活性。在本教程中,我将引导你了解计划管道是如何工作的,描述它们的一些很酷的用例,并向你展示如何开始为你的团队设置它们。我将演示如何使用 API 和 UI,以及如何从头开始设置预定的管道或从预定的工作流迁移到它们。

先决条件

以下是您学习本教程所需的内容:

您可以在完成这些步骤时使用示例项目

https://www.youtube.com/embed/x3ruGpx6SEI

视频

CircleCI 开发者倡导者 Zan Markan 解释了计划管道的工作原理、常见用例以及入门方法。

安排定期构建和测试

有许多理由安排 CI/CD 工作,而不仅仅是在将代码推送到存储库时执行。计划的第一个也是最明显的原因是定期构建软件。开发团队之外的人(产品经理、QA 工程师和其他利益相关者)经常需要访问您和您的团队正在开发的软件的最新版本。当然,您总是可以根据需要手动触发构建,但是这会分散您和团队中其他开发人员的注意力。自动化这一过程并向所有涉众指出他们可以在哪里找到最新的构建要容易得多。这适用于一切,从网络应用和移动应用,到后端服务,图书馆,以及两者之间的任何东西。

预定的构建在晚上或者下班时间没有开发的时候自动构建软件。这些每夜构建(正如他们有时被称为的那样)从您的主分支中取出最新的版本,并产生软件的一个阶段或测试版本。

您可以使用预定的管道来运行整个测试套件,这样每夜的构建也可以验证您的软件工作正常,并且在您开始下一个工作日的时候已经准备好了。调度允许您在每次提交时运行那些您不想运行的昂贵且耗时的功能或集成测试。构建不需要在夜间运行。您可以以适合您团队的节奏运行它,每隔几个小时,甚至每小时几次。

安排其他种类的工作

但是为什么要停留在构建阶段呢?您还可以安排不一定是构建但需要定期发生的工作,如批处理、数据库备份或重新启动服务。如果可以将它添加到能够访问您的环境的机器上的脚本中,那么它可以在 CI/CD 系统中完成。

传统的计划方式—计划的工作流

对 CircleCI 来说,安排工作的能力并不新鲜。开发团队已经能够将调度作为配置的一部分,使用 cron 语法在工作流级别定义调度。不过,这种方法也有一些缺点:

  • 它需要开发工作来对时间表进行任何更改,甚至审查已经到位的时间表。
  • 在 CircleCI 中,触发的是管道,而不是工作流。
  • 微调权限是很困难的,因为并不总是清楚是谁调度了被触发的工作。

预定管道如何改善开发者体验

以下是使用预定管道的一些优点:

  • 可以调度整个管线,包括要传递的任何管线参数。
  • 通过在配置文件之外处理调度,您可以使用 API 和 UI 来查询和设置调度。这种能力为谁来管理调度和执行提供了更大的灵活性。
  • 调度管道与上下文一起工作。上下文可以让您更好地控制谁有权执行和安排某些作业。您可以将您的部署凭证只授予具有足够权限的工程师,这样其他人就无法设置这些时间表。上下文还可以与动态配置一起使用,为您的 CI/CD 设置提供更多的灵活性。

现在我们已经介绍了一些关于调度管道的基本事实,我们可以实现一个了。

实现预定的管道

首先,我们需要一个管道来调度。幸运的是,我有一个使用预定工作流的项目。这是一个开放源代码的 Android 库项目,运行夜间部署,所以它是调度用例的理想选择。我建议你在 GitHub 上分叉这个项目,在 CircleCI 上设置成一个项目。

对于我们的示例时间表,我们希望每天晚上运行一次构建,并将其部署到 Sonatype 快照存储库中。这个版本使得任何人都可以使用这个库并获得最新的代码。

在我们的.circleci/config.yml - nightly-snapshot中已经为其定义了一个工作流:

 parameters:
    run-schedule:
        type: boolean
        default: false

  nightly-snapshot:
    when: << pipeline.parameters.run-schedule >>
    jobs:
      - android/run-ui-tests:
          name: build-and-test
          system-image: system-images;android-23;google_apis;x86
          test-command: ./gradlew assemble sample:connectedDebugAndroidTest
      - deploy-to-sonatype:
          name: Deploy Snapshot to Sonatype
          requires:
            - build-and-test 

工作流有两个任务:一个运行测试,另一个部署快照。除了检查run-schedule管道参数的when语句之外,管道本身没有调度逻辑。我们的调度程序将在触发管道时设置参数。

使用 API 调度管道

要开始使用 API 进行调度,您需要 API 令牌。要获得令牌,请登录 CircleCI 并点击左下角的头像。点击你的头像打开用户设置。导航到个人 API 令牌,创建一个新令牌,并将其保存在安全的地方。在示例项目中有一个build-scheduling目录,其中有一个名为.env.sample的文件。您可以将该文件复制到.env,并用您的替换占位符令牌。你应该对.env文件的其他部分做同样的操作:PROJECT_IDORG_NAME

CIRCLECI_TOKEN=YOUR_CIRCLECI_TOKEN
PROJECT_ID=YOUR_PROJECT_ID
ORG_NAME=YOUR_VCS_USERNAME
VCS_TYPE=github 

设置环境变量后,我们可以进行 API 调用。预定管道使用 CircleCI API v2 。要发布新的时间表,您需要向端点发出发布请求。端点由您的 CircleCI 项目、您的用户名和您的 VCS 提供者组成。这里有一个例子:https://circleci.com/api/v2/project/github/zmarkan/android-espresso-scrollablescroll/schedule

这个来自setup_nightly_build.js脚本的 POST 调用示例使用了 Axios JavaScript 库:

const token = process.env.CIRCLECI_TOKEN

axios.post("https://circleci.com/api/v2/project/github/zmarkan/android-espresso-scrollablescroll/schedule", {
    name: "Nightly build",
    description: "Builds and pushes a new build to Sonatype snapshots every night. Like clockwork.",
        "attribution-actor": "system",
        parameters: {
          branch: "main",
          "run-schedule": true
        },
        timetable: {
            per_hour: 1,
            hours_of_day: [1],
            days_of_week: ["TUE", "WED", "THU", "FRI", "SAT"]
        }
    },{
        headers: { 'circle-token': token }
    }
) 

正文有效负载包含计划名称(必须是唯一的)和可选描述。它包括一个 attribution actor,可以是中立 actor 的system或获取当前用户权限的current(根据您使用的令牌)。有效负载还包括参数,如触发管道时使用哪个分支,以及您设置的任何其他参数。本教程在配置文件中使用了run-schedule。主体的最后一部分是timetable,在这里我们定义何时以及多频繁地运行我们的调度管道。这里使用的字段是per_hourhours_of_daydays_of_week。注意,这没有采用 cron 表达式,这使得它更容易被使用 API 推理的人解析。

在消息头中,我们将使用之前在 CircleCI UI 中生成的令牌传递一个circle-token

在时间表中,我们将一切都设置为在周二到周六的凌晨 1:00(UTC)运行,即工作结束后的第二天晚上。我们不需要在周日和周一运行,因为在我们的示例项目中,没有人在周末工作。代码库在那几天不会改变。

除了 POST 方法,API 还公开了其他方法,如 GET、DELETE 和 PUT,用于检索、删除和更新计划。存储库中有get_schedules.jsdelete_schedule.js的样本。

使用图形用户界面

除了使用 API,您还可以在 CircleCI 仪表板中设置预定的管道。在 CircleCI 的项目中,进入项目设置,从左侧菜单中选择触发器

点击添加调度触发器打开页面,可以设置新的调度管道。该表单具有与 API 相同的选项:触发器名称、星期几、开始时间、参数、属性用户等。

点击保存触发器将其激活。触发器将准备好在您指定的日期和时间开始调度您的管道。

Trigger set up in the GUI

从计划的工作流迁移

到目前为止,我们已经探索了如何使用 API 和 GUI 来设置和检查管道。现在,我们可以专注于将您现有的计划工作流迁移到更有利的计划管道。

此示例显示了一个计划的工作流,其中所有内容都在配置文件中定义。它包括工作流定义的触发器配置,通过 cron 表达式传入时间表:

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 assemble sample:connectedDebugAndroidTest
      - deploy-to-sonatype:
          name: Deploy Snapshot to Sonatype
          requires:
            - build-and-test 

在我们的例子中,它在每天午夜运行,我们希望只在主分支上触发它。

要进行迁移,需要完成几个步骤:

  1. 触发计划管道中现有的nightly-snapshot工作流。
  2. 引入一个名为run-schedule的新管道变量,就像我们在第一个例子中所做的那样。
  3. 对于所有工作流,添加表示当run-scheduletrue时运行它们并且除非run-schedulefalse否则不运行其他工作流的when表达式。
parameters:
  run-schedule:
    type: boolean
    default: false

workflows:
  build-test-deploy:
    when:
      not: << pipeline.parameters.run-schedule >>
    jobs:
      ...

  nightly-snapshot:
    when: << pipeline.parameters.run-schedule >>
    jobs:
      ... 

其余过程与从头开始设置时相同:

  1. 解析 cron 表达式;你可以像这样使用一个在线工具。
  2. 使用带有新时间表配置和run-schedule管道参数的 API 或 GUI 设置时间表。

结论

预定管道是 CI/CD 工具包的一个多功能组件,允许您:

  • 重复触发您的构建
  • 利用管道参数
  • 微调控制谁可以设置管道
  • 使用 CircleCI 上下文清楚地定义它们运行时所需的权限

在本教程中,您了解了预定管道在 CircleCI 中的工作方式以及如何设置它们,无论是从头开始还是从预定工作流移植。我们还讲述了如何使用 API 和 web UI,并回顾了它们的一些用例来帮助您入门。

请告诉我们您和您的团队在计划管道、移植您的计划工作流方面的进展,或者您是否希望看到 UI 或 API 功能的任何补充。

如果你对我接下来要讨论的话题有任何反馈或建议,请通过 Twitter - @zmarkan 联系我。

无可指责的文化的价值——从集成电路到高级管理人员

原文:https://circleci.com/blog/value-of-blameless-culture/

在 CircleCI,CI 还有第二层含义:持续改进。我们不断寻求反馈,不仅是为了改进我们的代码,也是为了改进我们的过程,并在工作中做得更好。这种持续改进始于一个重要的公司价值观:无可指责的文化。我们无可指责的文化延伸到我们运营的各个方面。它让我们能够在不同的工程团队之间建立信任,这对于我们如何处理事故、从中吸取教训以及防止事故再次发生至关重要。无可指责的文化意味着我们将责任归咎于过程,而不是人。

为了帮助你从内部理解一个无可指责的文化是什么样的,我们希望你能听到来自组织各个层面的员工的声音。

工程师的视角——泰勒·麦高芬

最近,我的团队对代码库所做的更改在 CircleCI 平台上引发了一个事件。但我从不担心会受到指责。我们优先考虑的无可指责让我能够专注于最重要的事情——解决事件并确保它不再发生。

在事故发生之前

无可指责让我完成我的工作,而不用担心错误的改变会给我带来不好的影响。有一种理解是,如果我引入了一个导致事件的变更,那么该事件发生只是时间问题。很可能是另一个团队成员捡起了票,并介绍了突破性的变化。这种心态让我可以把工作做到最好,不用担心出了问题会被训斥。

调试事件

当出现问题时,最重要的优先事项是减轻对我们的客户和同事的影响。如果我的突破性改变唤醒了欧洲的队友,我们都专注于通过快速解决问题和减轻对客户的影响来尽快让他们回到工作岗位,而不是找出是谁造成了问题。

通过专注于解决问题,而不是责备,每个人都有动力投入进来,帮助解决问题。没有人会犹豫抛出可能导致问题的想法,或者潜入兔子洞寻找更多的背景。我从来没有因为一个坏主意而受到责备,也没有因为追逐一条红鲱鱼而受到任何恶意。参与的每个人都关注最重要的事情—让我们的系统恢复运行。

事件发生后

现在有趣的部分来了。我们已经缓解了事故,是时候弄清楚到底发生了什么。一旦发现了根本原因,我们不会停止在那里提问。很容易将责任归咎于有罪的行为,命令它得到解决,并在我的职业发展中记录下是我让这一切发生的。但那会把责任推到人身上。我们更深入地探究原因。

  • 为什么这个提交会破坏应用程序?这暴露出了什么弱点或局限性吗?除了修改提交,我们还需要修改更大的系统吗?
  • 为什么我们没有用 CI 抓到这个?是否有测试或警报可以预防这种情况?
  • 为什么以前没有发现这些弱点?这真的是未知事件,还是之前的事件回顾中有什么可以解决这一问题?

一次事故意味着我们对这个系统失去了一些信任,但这种信任是可以恢复的。事件回顾会创建可操作的项目,这样我就可以相信流程会涵盖我,知道我们已经从情况中吸取了教训,并改进了我们的流程和系统。每个人都尽了自己当时所拥有的知识。

这就是我们关注过程而不是人来解决事件的方式,它使我在工作的每一部分都成为一名更自信的工程师。

工程经理的视角——郄佳朝·普罗克多

作为一名经理,用演示文稿和文件来表达有抱负的文化是一回事;你实际执行、允许和鼓励的完全是另外一回事。

作为 CircleCI 的一名工程经理,我帮助强化我们无可指责的文化。我确保我们关注流程和改进,而不是特定的个人或行动。在事件缓解或回顾过程中,我们确保主旨和主题集中于为什么和如何,而不是什么或谁,即使当我们回顾过去找出发生了什么。

我们这样做的一个方法是用五个为什么技术进行回顾。个人行为和错误几乎总是更深层次的基础问题的结果,但谈话自然倾向于围绕表面错误。我帮助我的团队深化讨论,并使用“五个为什么”框架找出根本原因。

这里有一个简单的例子:

“数据库出现故障,我们的应用程序失败了。”为什么?“我们最近提交的代码关闭了数据库。”为什么?“它通过一个糟糕的循环使我们的数据库超出了容量。”为什么?“我们在提交之前没有编写测试,所以我们没有在它投入生产之前发现它。”为什么?“我们不得不在一个非常紧迫的截止日期前发布这项功能,所以不得不走捷径。”为什么?“我们就是没有足够的人手。”**

这是一个简化的例子,但你可以看到反复问“为什么”并进一步推动讨论如何帮助我们越过表面的结果,找到更系统的根本原因。

这也迫使讨论超越个人或个人行为。个人没有责任去解决远远超出其权限或专业知识范围的系统性问题。我们帮助团队深入挖掘这些基本问题,然后我们作为经理,与高层领导一起,采取行动解决这些问题,使每个人受益。

首席技术官的视角— Rob Zuber

作为一个领导者,你的目标是建立一个高绩效的组织,高绩效的团队只有在每个人的福祉都得到优先考虑的情况下才能存在。这在谷歌的项目 Aristotle 和书 Accelerate 的数据中得到了证明。该数据将团队绩效与心理安全直接关联起来。

关于如何改进一个组织的最丰富的信息来自失败。当人们觉得公开谈论失败是安全的时候,他们会暴露那些需要改进的地方,并采取行动让事情变得更好。没有这种安全性,这些问题永远不会浮出水面。

这不是偶然事件

事件是显微镜,我们倾向于在显微镜下检查清白的文化,因为它们通常是尖锐的,非常明显的,并且是最优先的。他们也非常有压力。在事件发生期间和之后,表现出明显的行为升华。

但是,在您的事件响应中表现出来的文化将会直接反映您每天在最平凡的情况下通过每个行动建立的文化。例如,如果每次招聘不按计划进行时你都责怪招聘团队,你就无法走进事后审查,称之为“安全空间”,也无法让你的员工诚实地说出哪里出了问题。相反,如果你对招聘团队关于一个任性的雇佣的回应是,“有什么额外的信息可以帮助你在这里做出不同的决定?”很明显,你是在努力解决问题,而不是责备那个人。你的团队会注意到的。

检查系统

当我们检查一个事件以确定需要改进的地方时,我们最常问的一个问题是,“系统在哪些方面让我们失望了?”它可以有许多不同的表达方式,但重点是明确的:同样的人在不同的系统中运作会有不同的结果。

这就是我们与责备断绝关系的地方。如果同样的人在不同的系统中会得到不同的结果,那么让我们来谈谈这个系统。

作为一名领导者,如果你没有准备好,走进这一刻会很不舒服。责备一个人很容易,也很安全。也超级懒。当您开始检查系统时,您会发现系统的设计——随着时间的推移,创建您当前环境的显性或隐性价值、优先级和决策的集合——不是事件中涉及的个人的工作。这是组织领导的工作,包括你自己。

你如何选择处理这一事实是建立一个无可指责的文化的核心。公开讨论让你进入该系统的背景和心理模型,以及可以改进的方法,发出一个非常重要的信号,表明你愿意做改进工作。

我最喜欢的关于系统的资源之一是 Donella Meadows 的《系统中的思考》,她在书中说,“系统不能被控制,但是它们可以被设计和重新设计。”

系统按照其设计运行。对你在系统设计中的角色负责,并找到一种方法来修改设计以产生更好的结果…不要责备。

底线

无可指责的文化让 CircleCI 的工程师可以更快地解决问题,因为不用担心遭到报复。当我们的工程师通过错误发现我们系统中的弱点时,他们从这些错误中学习,这有助于我们不断迭代和改进我们的过程和产品。

收购发布编排工具 Vamp - CircleCI

原文:https://circleci.com/blog/vamp-and-circleci/

CircleCI 和 Vamp 联手了

我们自豪地宣布,CircleCI 已经收购了第一个云原生版本编排平台 Vamp 。这为 CircleCI 客户拥有一流的版本编排和持续验证铺平了道路,所有这一切都在 CircleCI 平台中进行。

在 CircleCI,我们的目标是树立信心,让您相信您正在生产的软件已经准备好了,可以呈现在您的客户面前。现在,我们很高兴能够将这种信心一直延伸到代码在生产中的运行方式。通过将 Vamp 引入 CircleCI,我们将能够提供一流的 CI 和 CD,以及版本编排和持续验证,这意味着您将明确知道您的代码正在按预期工作。如果不是这样,你可以很容易地恢复。

跟上不断加速的变化

在 CircleCI,我们认识到软件开发的世界已经发生了变化,我们也在随之变化。敏捷方法和更小的变更单元,以及共享库和微服务,导致生产前和生产环境中的管理更加复杂。

由于应用程序构建在第三方服务和平台的堆栈上,而这些服务和平台本身也在不断更新,因此生产是一个移动的目标,无法复制。古老的格言:永远不要两次踏入同一条河?这就是你的现代生产环境。

这意味着能够持续验证您的代码比以往任何时候都更加重要。作为当今最大的共享 CI 平台,我们处于将真正的持续验证推向市场的理想位置。

“Vamp 平台的加入扩大了 CircleCI 在软件交付供应链中的范围,并使 CircleCI 更接近最终的软件最终用户。这进一步巩固了 CircleCI 作为当今领先的云 CI/CD 平台的地位,”IVP 合作伙伴 Cack Wilhelm 说道。

DevOps 的未来

对于今天的许多软件团队来说,如果生产中出现了问题,开发团队就无法获得可靠的信息来了解是什么破坏了构建、为什么以及从哪里开始修复它。这是一个真正的大问题,不仅影响这些企业的交付渠道,还影响它们的底线。事实上,麦肯锡最近的一项研究发现,优先为其开发团队提供同类最佳工具的组织比同行的创新能力高 65%,股东回报高 60%。

优秀的开发人员工具——能够提高生产力、可见性和协调性——对于任何希望在当今软件领域取得成功的企业来说都是至关重要的。通过收购 Vamp,CircleCI 正在构建一个平台,该平台能够为各种类型和规模的软件公司提供无与伦比的高性能 DevOps。

这意味着您可以更快、更好地控制从制造到发货的过程。

下一步是什么?

我们非常高兴将 Vamp 加入我们的团队,以及它将使我们能够为世界软件团队提供更强大、更灵活的支持。作为此次收购的一部分,Vamp 的所有员工都将加入 CircleCI 团队。由于 Vamp 总部位于荷兰,随着我们新的荷兰实体 CircleCI Netherlands B.V .的成立,这也扩大了我们的地理足迹,并推动了我们的欧洲扩张。在我们努力将 Vamp 与 CircleCI 集成的同时,Vamp 客户可以继续使用该产品。

对于 CircleCI 的客户,在此注册,我们将在何时向平台集成新的部署和发布功能。

如果你是 CircleCI 的新手,现在就注册看看持续集成如何帮助你今天发布更好的软件。

CircleCI 可视化配置编辑器| CircleCI 简介

原文:https://circleci.com/blog/visual-config-editor/

CircleCI 可视化配置编辑器(VCE) 现在作为一个开源项目普遍可用。开发团队现在可以在可视化的拖放、低代码环境中创建和修改 CircleCI 配置文件。

VCE 是一个节点图编辑器,可以用来修改 CircleCI 配置元素和生成配置文件。它提供了一种无摩擦的方式来构建 CI/CD 管道,并以高效、用户友好的可视化界面与 CircleCI 的平台进行交互。

我们的目标不是取代作为代码语言的YAML,我们事实上的配置,而是提供一种替代方案,在其中你可以可视化地编排你的管道开发。在本帖中,我们将触及可视化配置编辑器背后的动机,浏览其元素和支持的功能,解释常见用例,并演示如何使用 VCE 建立管道。

可视化配置编辑器的用例

可视化配置编辑器为配置编辑提供了一体化的环境,包括创建组件定义和用法。视觉生态系统用 YAML 换取了低代码(或者,当使用正确的球体时,甚至没有代码!)开发经验。

CircleCI 的新用户会发现可视化配置编辑器在以下方面很有用:

  • 无需学习或编写 YAML 即可生成配置
  • 只需轻轻一点,即可访问 CircleCI orbs ,快速集成流行服务
  • 可视化管理标签和分支过滤器
  • 实时验证输入
  • 简化配置构建体验,因此您可以满怀信心地协调您的管道

更有经验的用户会发现可视化配置编辑器在以下方面很有用:

  • 无需通过 YAML 即可快速更改
  • 预览工作流程编排
  • 提供一个可视化的解释器,用于在你的项目的 CI 管道上教育同行
  • 轻松访问 orb 的高级功能

可视化配置编辑器功能

创建管道配置文件所需的一切都是预先提供的。VCE 分为三个焦点窗格:可视化工具、检查器和输出

VCE user interface

可视化窗格由一个节点图组成,其中工作流被创建和组织。

组件内置在检查器面板中,在这里您可以创建、查看和编辑组成配置的组件。

输出窗格是一个集成的代码编辑器,提供查看您的配置的 YAML 输出的能力。

如何使用可视化配置编辑器设置管道

让我们看看它的实际效果吧!在这一节中,我们将演示如何使用 VCE 来建立一个管道来测试和部署一个用 Vue.js 构建的示例前端应用程序。您可以派生出连续食品交付示例项目并跟随,或者使用您自己的(最好是基于节点的)项目。

入门指南

为了跟随演示并学习如何使用 VCE 来管理自己的管道,你还需要注册一个免费的 CircleCI 账户Heroku 账户,如果你还没有的话。登录 Heroku 后,进入仪表盘,点击新建,然后选择创建新应用。请记下您在此处选择的应用程序名称,因为您稍后会用到它。目前,这是 Heroku 需要的所有设置。

要访问 VCE 并开始构建您的配置文件,请访问 VCE 主页

将测试作业添加到管道中

好消息!我们实际上不需要做太多来让 CI 参与这个项目。VCE 具有 orb 支持,提供对各种预打包配置的快速访问,以便与您喜欢的所有服务集成。要从 VCE 向您的管道添加 orb,只需单击 orb 旁边的按钮,并选择您想要使用的 orb。

在这个例子中,我们将使用 Node.js orb 向我们的工作流添加测试。因为 Node.js 是我们最受欢迎的 orb 之一,所以它是菜单中第一个可用的 orb。单击圆形图标查看管道中可用的作业、命令和执行器。要添加测试任务,将node/test任务拖到可视化窗格中。依赖项安装和缓存将为我们处理。然而,在连续食品交付应用程序的情况下,我们需要添加一些额外的属性来告诉节点测试作业我们想要运行哪个测试。

单击您刚刚放入工作流阶段的node/test作业,以配置其设置。样本库中的文件 package.json 包含了我们需要的关于这个项目脚本的信息。

{
  "name": "sample-javascript-cfd",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "build": "vue-cli-service build",
    "test:unit": "vue-cli-service test:unit",
    "test:e2e": "vue-cli-service test:e2e",
    "lint": "vue-cli-service lint",
    "start": "vue-cli-service serve"
  } 

在连续食品交付示例应用程序中,有两种不同类型的测试:端到端测试(test:e2e)和单元测试(test:unit)。对于我们的node/test工作,我们将把run-command参数设置为test:unit,因为 e2e 测试将需要进一步的配置。

Sample application tests

添加部署作业

现在我们已经在管道中添加了测试,我们可以放心地部署到 Heroku。是时候再次搜索球体并为认证的 Heroku 球体导入一个任务了。就像前面一样,导航到检查器面板的 Orbs 部分并点击导入。在搜索栏中输入Heroku来查找 Heroku orb。

接下来,将heroku/deploy-via-git任务拖到可视化窗格中。要将测试作业标记为部署作业的需求,通过长按测试作业旁边的节点并将鼠标拖到部署作业上,绘制一个从node/test作业到heroku/deploy-via-git作业的连接。

Add test to deploy job

为了让 Heroku 部署我们的项目,需要使用您为 Heroku 项目选择的应用程序名称来配置部署作业。确保在作业属性的app-name字段中使用相同的名称。

当连接像 Heroku 这样的第三方服务时,通常需要一个 API 密钥。在我们开始项目之前,让我们记下将我们需要的键添加到上下文中。尽管还没有配置,我们将告诉 Heroku 作业使用cfd-deploy上下文。

还有一点:大多数开发团队不想在每次提交时都进行部署。为了确保部署作业仅在对存储库的主分支进行更改时运行,请将作业属性菜单中的分支参数设置为main

Set branch to main

使用过滤来控制作业何时在管道中运行还有许多更高级的方法。您可以使用屏幕左上方的预览工具箱来直观预览不同的过滤器如何影响您的工作流程。

VCE filtering

下面是完成所有步骤后最终生成的配置。

# This configuration has been automatically generated by the CircleCI Config SDK.
# For more information, see https://github.com/CircleCI-Public/circleci-config-sdk-ts
# SDK Version: 0.9.0-alpha.14
# VCE Version: v0.10.0
# Modeled with the CircleCI visual config editor.
# For more information, see https://github.com/CircleCI-Public/visual-config-editor

version: 2.1
setup: false
jobs: {}
workflows:
  build-and-deploy:
    jobs:
      - node/test:
          run-command: test:unit
          pkg-manager: yarn
      - heroku/deploy-via-git:
          context:
            - cfd-deploy
          requires:
            - node/test
          filters:
              branches:
                  only:
                    - main
          app-name: cfd-sample
orbs:
  node: circleci/node@5.0.2
  heroku: circleci/heroku@1.2.6 

您也可以在 VCE 中打开这个例子,查看生成的配置文件和工作流程图。

验证您的管道构建绿色

现在配置完成了!要在 CircleCI 管道中使用它,请将新生成的配置复制并粘贴到样例项目 fork 中预先存在的config.yml文件中。确保推动这些变化

最后,是时候去 CircleCI 了。在添加项目之前,您需要配置我们的上下文,以便安全地将 Heroku 凭证传递到您的作业。首先,在 Heroku 应用程序页面中生成一个新的 Heroku 令牌。然后,转到 CircleCI web 应用程序中的组织设置>上下文。使用您之前选择的名称cfd-deploy创建一个上下文,并添加HEROKU_API_KEY环境变量。

Create contexts

现在,该项目已准备好添加到 CircleCI 中。转到项目仪表板,单击示例项目的设置项目按钮。选择使用项目中现有的 config.yml。输入总分行名称,点击继续。这将触发您设置的管道的构建。

如果一切按预期进行,您的管道将导致一个成功的构建,并且应用程序现在将在 Heroku 上可用。恭喜你!您已经使用可视化配置编辑器在 CircleCI 上创建了一个自动化测试和部署管道。

Circle CI set up project

结论

可视化配置编辑器为任何级别的 CircleCI 用户提供了编排和维护 CI 管道的替代方法。从构建您的第一个管道到新团队成员入职,再到促进复杂的配置审查,VCE 让您可以访问 CircleCI 云平台的所有强大功能,而无需管理复杂的 YAML 带来的额外开销。

VCE 项目是开源的,我们欢迎社区所有成员的贡献。要投稿,请访问 GitHub 上的投稿指南或在我们的社区讨论上评论该主题。我们还有一个社区Discord——来和我们在#visual-config-editor频道聊聊这个项目吧。

寻找更高级的配置即代码解决方案的用户可能也会对使 VCE 成为可能的底层技术感兴趣,即 CircleCI config SDK 。要了解关于 config SDK 以及如何使用它从本地 JavaScript 或 TypeScript 生成 CircleCI 配置文件的更多信息,请访问 GitHub 上的开源配置 SDK 项目。

VPN 和为什么他们不工作- CircleCI

原文:https://circleci.com/blog/vpns-and-why-they-don-t-work/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


请注意:这篇文章专门介绍了我们在 SaaS 的服务。如果您的团队使用 VPN,我们的企业产品将更适合您。给我们一行多聊聊。

用户经常向支持部门询问的一个问题是,如何在他们的构建中创建一个 VPN 隧道来安全地访问网络资源。这在我们的平台上不工作有几个原因。一个是由于 VPN 如何创建 TUN/TAP 设备,另一个与无特权的 LXC 容器有关。这篇文章将解决使用 CircleCI 和 VPN 的常见问题。

调谐器/TAP 设备

什么是调谐器/TAP 设备?

调谐器/TAP 设备是为 VPN 连接创建和使用的特定设备。它充当以太网设备,但不是直接从物理设备接收数据,而是从用户空间程序接收数据(可以是您正在使用的任何 VPN 类型,如 L2TP、PPP、Cisco 等)。).然后,它将数据发送回用户空间程序,用户空间程序再将数据发送到加密的“设备”,以便传输到目的地。

这和我的构建有什么关系?

VPN 连接软件使用特定设备节点(如/dev/net/tun)创建设备,并将其注册为 tunX 或 tapX。创建 VPN 连接的用户空间程序需要有创建设备的适当权限。使用该设备的驱动程序也需要编译到内核中才能正确使用。虽然用户确实可以访问他们的构建,但这并不适用于您的构建是有原因的,这就是 LXC 发挥作用的地方。

无特权的 LXC 集装箱

什么是 LXC 集装箱?

当用户运行一个构建时,我们创建一个 LXC 容器。linux 内核为不同的运行进程提供了不同的系统“视图”。这允许物理资源,如 I/O、CPU、RAM 等。在逻辑上被“划分”。内核还允许使用两个主要组件:控制组和 POSIX 文件功能。控制组允许内核将一个或多个进程组合在一起,然后指定要分配给该控制组的资源。POSIX 文件功能只是允许这些进程使用的文件比标准的“根”和“用户”权限分离具有更细粒度的安全性。

什么是无特权 LXC 容器?

无特权的 LXC 集装箱是 LXC 集装箱的一个更安全的版本,并且是我们在 CircleCI 使用的。在特权 LXC 容器中,主机和容器的根 UID 是相同的(0)。这意味着,如果用户能够通过/proc 或/sys 文件系统,或者通过某个 syscall 突破容器,他们将是主机上的 UID 0(或 root ),从而对主机以及该主机上的所有其他容器/资源拥有 root 访问权限。

非特权 LXC 容器使用用户名称空间为用户分配主机上未使用的用户和组 ID 范围(通常为 100,000 到 165,000)。这意味着如果用户能够突破容器,容器的 UID 0 将映射到主机上的 UID 100,000。这实际上授予了他们与 nobody 用户相同的权限,并且极大地限制了他们在主机上的权限。

为什么这意味着我不能创建 VPN?

当容器中的“root”用户试图制作一个 TUN/TAP 设备时,它在主机上需要比它所拥有的有限访问权限更多的权限。这限制了多项操作,包括但不限于:

  • 挂载某些文件系统
  • 创建设备节点(如创建 TUN/TAP 设备所需的节点)
  • 或者任何需要分配给-- LXC 分配给容器的 UID/GID 范围(100,000 - 165,000 范围)之外的权限的操作。

如果不能在非特权容器上创建 TUN/TAP 设备,使用非特权 LXC 容器的任何平台上的任何用户都不能使用 VPN 服务。这意味着,只要我们使用无特权的 LXC 容器,就不会支持创建 TUN/TAP 设备,除非 LXC 对上游进行了更改,允许我们允许用户创建这些设备。

这是否意味着你无能为力?不,当然不是。还有一些其他的选择。我们将发布一篇后续文章,概述这些内容,并展示如何在未来实现它们。

等待好代码- CircleCI

原文:https://circleci.com/blog/waiting-for-good-code/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


waiting for good code 图片来源

毁坏
弗拉迪米尔

开放的办公室。循环依赖。
晚上。

埃斯特雷根坐在一个低矮的球上,正试图重启他的电脑。他气喘吁吁地用双手按下电源按钮。他放弃了,筋疲力尽,休息,再试一次。和以前一样。

进入弗拉基米尔。

埃斯特拉贡:
(再次放弃)
无计可施。

弗拉基米尔:我开始接受那个观点了。我一生都在试图调试它,说弗拉基米尔,理智点,你还没有尝试所有的东西。我继续奋斗。

他沉思着,沉思着这场斗争。转向埃斯特拉贡。)

你又来了。

我是吗?

弗拉基米尔:很高兴看到你回来。我以为你永远离开了。

我也是。

终于又在一起了!我们得庆祝一下。但是怎么做呢?
(他反映。)
跟我一对。

埃斯特拉贡:
(性急)
现在不行,现在不行。

弗拉基米尔:
(伤害,冷冷)
请问殿下在哪里过夜?

在棒球场里。

弗拉基米尔:
(钦佩地)
一个棒球场!在哪里?

飞龙:
(无手势)
那边。

弗拉基米尔:
他们没有打你?

埃斯特拉戈:
打我?他们当然打败了我。

弗拉基米尔:
和往常一样的操作团伙?

埃斯特拉戈:
一样?我不知道。

弗拉基米尔:
当我想起…这些年…如果不是我…你会在哪里…
(果断地。(T4)毫无疑问,此刻你只不过是一堆白骨。

埃斯特拉戈:
那又怎么样?

弗拉基米尔:
(闷闷不乐)
一个人吃不消。
(停顿。愉快地。)
另一方面,现在灰心有什么用,我就是这个意思。我们早在 100 万年前,也就是 90 年代,就应该想到这一点。

啊,别废话了,帮我弄这个该死的东西。

你在做什么?

正在重启。你没遇到过这种事吗?

弗拉基米尔:电脑必须每天重启,我已经厌倦了告诉你这些。你为什么不听我说?

埃斯特拉戈:
(无力)
救救我!

弗拉基米尔:
(愤怒)
除了你,没有人会受苦。我不算。我想听听如果你有我有的东西,你会说什么。

你在期待什么,你总是等到最后一刻才合并你的代码。

弗拉基米尔:
(沉思)最后一刻… (他沉思。)法典延期会使某些东西生病,谁说的?

你为什么不帮我?

弗拉基米尔:
无计可施。(埃斯特雷根尽了最大的努力成功关闭了他的电脑。他凝视着屏幕,把它倒过来,摇晃它,看着地面看是否有什么东西掉了出来,什么也没找到,茫然地看着前方。)
嗯?

没什么。

给我看看。

没什么可展示的。

试着再打开它。

埃斯特拉贡:
(审视自己的双手)。
我出去透透气。

弗拉基米尔:有一个人完全是为了你,责怪他的手指的错误重新启动。这越来越令人担忧。

人是该死的无知的猿。我们走吧。

弗拉基米尔:我们不能。

为什么不呢?

弗拉基米尔:我们在等待好的代码。

埃斯特拉戈:
【绝望】
啊!
(停顿。)
你确定是这个拉请求?

弗拉基米尔:
什么?

那是我们要回顾的。

他在树旁说。
(他们看着饭桶树。)
你还看到其他人吗?

埃斯特雷根:
树枝在哪里?

它一定是死了。

在我看来,它更像一丛灌木。

弗拉基米尔:
答--。你在暗示什么?我们提出了错误的请求?

应该在这里。

VLADIMIR:
他没有肯定地说这会是好代码。

如果他不解决呢?

弗拉基米尔:我们明天会回来。

埃斯特拉戈:
然后是后天。

有可能。

埃斯特拉贡:
以此类推。

弗拉基米尔:
重点是—

ESTRAGON:
直到代码好。

弗拉基米尔:
你是无情的。

我们昨天已经讨论过了。

啊,不,你弄错了。

我们昨天做了什么?

弗拉基米尔:我们昨天做了什么?

是的。

弗拉基米尔:
为什么……
(愤怒地。当你在的时候,没有什么是确定的。

依我看,我们在这里。

弗拉基米尔:(滚动差异)你认得这个吗?

我没那么说。

弗拉基米尔:嗯?

你确定是今天晚上?

弗拉基米尔:
什么?

那是我们要回顾的。

他说星期六。
(停顿。)我觉得。

你认为。

弗拉基米尔:我一定是记下来了。
(他一边摸索着 JIRA,一边鼓鼓囊囊地塞着杂票。)

埃斯特拉贡:
(非常阴险)。
但是什么星期六?今天是星期六吗?今天不是星期天吗?
(停顿。)
还是周一?
(停顿。)还是周五?

弗拉基米尔:(疯狂地点击着铬合金标签,仿佛日期就刻在其中一个标签上)
这不可能!

埃斯特拉戈:
还是周四?

我们该怎么办?

ESTRAGON:
如果他昨天推码,我们没看到,你可以确定他今天不会再推了。

但是你说我们昨天在这里。

我可能弄错了。
(停顿。)
我们暂时停止说话,你介意吗?

弗拉基米尔:(无力地)。
也好。
(埃斯特雷根把头放在他站立的书桌上。弗拉基米尔激动地来回踱步,不时停下来凝视着拉请求。埃斯特雷根睡着了。他的电话开始响起传呼值班警报。埃斯特雷根被惊醒了。)

埃斯特拉贡:
(恢复了他的恐怖处境)
我睡着了!(绝望地,对着他的电话。)
为什么你永远不让我睡觉?

弗拉基米尔:
随叫随到。

这是一次不可靠的测试。

不要告诉我!

埃斯特拉戈:
我没有修理—

不要告诉我!

埃斯特拉戈:
你真不好。如果我不能告诉你,我又能告诉谁我自己的噩梦呢?

让他们保持隐私。你知道我不能忍受。(沉默。)
我们现在怎么办?

埃斯特拉戈:
等等。

是的,但是在等待的时候。

ESTRAGON:
安装一个节点包怎么样?

来自 NPM?我不会相信它。

我们总是可以尝试。

去吧。

你先请。

不不,你先。

让我们什么都不做。这样更安全。

让我们等着看他怎么说。

好主意。

弗拉基米尔:
我很好奇他能提供什么。那我们要么接受要么放弃。

我们到底向他要求了什么?

你不在那里吗?

这是一个很长的会议。我不可能一直在听。

哦……没什么非常确定的。

一种祈祷。

弗拉基米尔:准确地说。

埃斯特拉戈:对变革的含糊恳求。

弗拉基米尔:没错。

他回答了什么?

弗拉基米尔:
他会看到的。

他不能承诺任何事。

弗拉基米尔:那他得好好想想。

在安静的家中。

弗拉基米尔:咨询他的经理。

他的同龄人。

他的直接下属。

他的书。

他的家人。

他的星座。

在做决定之前。

这很正常。

不是吗?

我想是的。

弗拉基米尔:我也这么认为。

(沉默。)

我们没有打平?

弗拉基米尔:
你说的平手是什么意思?

埃斯特拉戈:
下来。

但是对谁呢?被谁?

ESTRAGON:
为了好的代码。

弗拉基米尔:
要好代码?绑好代码!多好的主意!毫无疑问。
(停顿。
)暂且不提。

有趣的是,你编码越多,情况就越糟糕。

弗拉基米尔:
对我来说,正好相反。

换句话说?

弗拉基米尔:
我一路走来,已经习惯了这种肮脏。

埃斯特拉贡:
(经过长时间的反思。)
是相反的吗?

弗拉基米尔:
气质问题。

埃斯特拉贡的性格。

弗拉基米尔:你对此无能为力。

合并没有用。

一个人就是一个人。

部署是没有用的。

弗拉基米尔:本质不会改变。

埃斯特拉戈:
无计可施。

Alek 是 CircleCI 的一名开发者倡导者,在这里他说的比他应该说的多,写的也差不多,都是关于持续集成和交付——你在等待好代码时所做的事情。

如果你喜欢这个,你可能会喜欢他的其他科技小说:连续(分解)集成连续交付:一个圣诞故事如何用你梦想的配色方案激发嫉妒

哦,当然还有《T1》原著《T2》。😃

CircleCI 筹集了 5600 万美元的 D 轮融资:客户的下一步是什么

原文:https://circleci.com/blog/we-raised-a-56m-series-d-what-s-next-for-circleci-customers/

今天,我们宣布了由 Owl Rock Capital 和 NextEquity Partners 领投的 5600 万美元的 D 轮投资,我们现有的投资者还有 Scale Venture Partners、Top Tier Capital、Threshold Ventures(前 DFJ)、Baseline Ventures、Industry Ventures 和 Heavybit。这使得我们迄今为止筹集的资金总额达到 1.155 亿美元。感谢我们的客户、我们的团队和我们的合作伙伴,感谢你们对我们缩短创意和交付之间距离的愿景的支持。

自从我们在 2018 年 1 月宣布 C 系列以来,我们已经看到了巨大的增长:

  • 我们的客户名单不断扩大,现在包括世界一流的工程组织,如三星、美国小企业管理局、福特汽车公司、HashiCorp、Chewy、PagerDuty、加拿大国家银行、德勤日本公司、BuzzFeed、VMWare、Twitch 等等。

  • 我们在东京开设了第一个国际办公室,并在波士顿和丹佛设立了中心。

  • 我们的团队已经从 120 名员工发展到 250 名,我们预计今年还会增加 50 多名员工。

  • 我们的执行团队增加了五名新成员:朱恩·柯,总法律顾问;客户工程副总裁 Thomas Enochs 平台工程副总裁 Michael Stahnke 业务发展主管汤姆·特拉汉;和日本区经理 Kensuke Morimoto。

  • 我们推出了用于模板化配置的 orbs,淘汰了 CircleCI 1.0,成为第一个(也是唯一一个)FedRAMP 认证的 CI/CD 工具,引入了管道,并发布了受限上下文以提高管道安全性。

  • 我们的月度就业人数从 2018 年 1 月的 700 多万增长到现在的 3000 多万。

开发人员工具领域正以令人难以置信的速度增长,对于能够同时满足开发人员和大公司需求的公司来说,这是一个巨大的机会。我们正计划这样做。

以下是我们的方法,以及您接下来可以从我们这里期待的内容:

继续我们的激光聚焦 CI/CD

最好的团队专注于他们独特的价值主张,并让其他人完成该领域之外的工作。我们是外包无差别重物的忠实信徒:我们专注于 CI/CD,因此我们的客户可以构建他们的下一个大东西。

你应该期待看到我们的团队在持续集成和持续交付的核心问题上加倍努力。一个团队的 CI/CD 管道是他们软件交付的心脏和控制中心。这个问题太重要了,不能部分解决。我们致力于成为市场上最好的平台,这样我们的客户就可以尽可能快速、自信地进行迁移。

虽然专注是成功的关键因素,但在我们的领域有一种趋势,那就是从工作规划到监控,尝试并满足工程团队流程的每个角度和可能的需求。虽然所有这些领域都很重要,但我们没有看到一个工具可以预测、构建和满足复杂工程团队需求的世界。虽然用一个工具来运行您的整个软件开发过程在理论上听起来不错,但我们知道全力投入一家公司是一个冒险的赌注,通常以客户痛苦而告终。

大量投资于不可知论和可扩展性

虽然 CI/CD 是我们的核心竞争力,但我们知道我们无法想象团队可能想要的每一种可能的交付模型。当我们在 2018 年推出 orbs(用于配置的包管理器)时,我们迈出了成为平台的第一步。不到一年后,我们的注册簿上有来自 45 个以上行业合作伙伴的 900 多个 orb。我们将继续关注 orb,将其作为平台可扩展性的手段,这样您就可以将所有您喜欢的工具连接到您的 CI/CD 管道。我们还将继续投资于我们的 CLI 和 API。如果你想知道这项工作的发展方向,你可以现在预览我们的 v2 API。

接下来,我们计划增加我们的 VCS 和执行环境不可知论。除了 Linux、Docker 和 macOS 之外,我们已经在为客户运行 Windows 作业。我们计划继续开放对不同 Git 提供商的支持,,包括 GitLab

利用我们的规模来展现数据和对价值交付的见解

CircleCI 每月运行超过 3000 万个独立的工作,并且还在增长,它对语言、架构模式、代码测试模式、交付成功率、团队实践等有着丰富和无与伦比的理解。我们正在积极投资,将这些见解提供给你们,我们的开发者客户,以帮助改善所有人的价值交付状况。

今天,很难回答这个问题,“我的工程团队表现好吗?”我们相信,像速度和恢复时间这样的表面指标可以帮助团队快速发现和修复问题,并最终向他们的客户交付巨大的价值。

借助企业控制,继续我们的开发人员授权战略

迄今为止,我们的成功一直基于不懈地专注于提供开发人员所需的功能和灵活性,同时确保我们也拥有企业所需的控制和检查。虽然有些人认为这两个优先事项需要权衡,但我们知道团队值得并需要这两者。

我们目前正在采取措施,通过目前正在开发的新 UI 以及对入职流程的改进,使我们的工作更加直观。虽然我们致力于支持公共和私人项目的免费使用,但我们也将继续扩大可用计算选项的范围。我们正在预览一个新的基于使用的定价模型,该模型可以让团队根据工作控制他们的资源类别。我们认为人类永远不应该等待机器,我们希望给予团队灵活性和控制力,以最符合他们需求的方式驱动他们的工作流程。

最后,我们将继续建设我们的全球客户和支持团队,扩大我们在 APAC 和 EMEA 的业务。我们知道 CI/CD 是至关重要的,我们的客户期待并依赖我们的专业支持和成功工程师团队、客户成功经理和客户经理的专业知识。

再次感谢我们的团队、合作伙伴,最重要的是,感谢我们的客户。大规模支持世界上最优秀的工程团队是一次非常值得的冒险,我们从你们每天的建设中学习并受到鼓舞。

我们为迄今为止取得的成就感到自豪,但仍有更多的工作要做。如果你想帮助塑造软件交付的未来,来加入这个团队:我们正在招聘

CircleCI 筹集了 3100 万美元的 C 轮融资

原文:https://circleci.com/blog/we-raised-our-series-c-what-s-next/

今天,我们宣布了一笔 3100 万美元的 C 轮投资,投资方包括顶级资本合伙人、行业风险投资公司和 Heavybit,以及现有投资方 Scale Venture Partners、Baseline Ventures、Harrison Metal 和 DFJ 风险投资公司。在这里的发布中有更多的细节,但是我想分享我们的新资金计划,以及你对 CircleCI 未来的期望。

当 CircleCI 在 2011 年推出时,世界要简单得多。Rails monoliths 风靡一时,Docker 还不存在,最新的 iPhone 是 4s。今天,世界要复杂得多。团队在一个产品中运行数以千计的微服务,用多种语言构建,并且全部在云中支持。

很明显,我们的世界只会继续变得更加复杂。我们需要我们的工具和系统不仅要进化来满足这种需求,而且要变得更加智能。对工程团队的需求没有显示出放缓的迹象:每个团队都被问及如何更快、更安全、更高效地构建高质量的产品。我们相信我们可以帮助团队解决这些问题,我想分享一点我们今年将如何开始解决这个问题的计划。今天,我们每月运行 700 多万个版本,比不到 6 个月前的 450 万有所增加。2017 年的重点是在从 1.0 到 2.0 的过渡中重建我们的平台,这一过渡取得了回报。我们的 Linux 和 macOS 机群的构建时间已经大大减少。我们已经能够处理越来越多的用户和工作,同时减少我们所服务的每种语言的平均工作时间。

2018 就是规模。我们的使命是尽可能简化从创意到交付的过程。我们相信,我们可以利用每天发生的大量构建中的数据,为我们所有的用户创建智能自动化。例如,今天,当您依赖的工具、框架或库出现故障时,我们会看到一个接一个的构建由于您无法控制的原因而失败。我们的第一个计划是在 CircleCI 中建立主动警报和重新路由,这样我们可以在问题发生之前告诉您,并帮助您的团队找到不同的方法。

除了为智能自动化开发更多的工具,我们还将致力于使 CircleCI 对新用户来说更容易上手。随着 CircleCI 2.0 变得更加强大,我们也变得更加复杂。我们计划让用户体验回归简单。我们还将继续投资于在全球最大企业内运营 CircleCI 的运营商的体验。我们将继续发展,让我们最大的客户和团队更容易、更快地交付更高质量的代码。

我们为迄今为止所做的工作感到自豪,但我们还有很多工作要做。如果你想帮助塑造软件交付的未来,来加入这个团队:我们正在招聘

加入 CircleCI 在欧洲的工程团队| CircleCI

原文:https://circleci.com/blog/we-re-growing-circleci-s-engineering-teams-in-europe-join-us/

在 CircleCI,我们一直致力于在我们发现的地方雇佣最优秀的人才。从一开始,我们就是一个分布式工程团队,我们为继续在全球分布而自豪。我们的工程师现在分布在旧金山和东京之间,横跨四大洲,三大洋,17 个小时的时差。我们计划今年进一步发展我们的工程组织,并决定将大部分扩张集中在一个地区:欧洲。

为什么是欧洲?

我们希望确保公司的可持续发展。这种增长的一个重要因素是解决时区给我们带来的挑战。过去,我们的团队遍布全球,这给团队内部以及跨团队协作带来了困难。限制每个团队内的时区分布,并在我们的日本和美国时区之间增加更多工程师,将有助于我们更轻松地进行日常协作、交流和社交。

此外,伦敦、都柏林和柏林等欧洲城市是充满活力的科技中心,拥有充满活力的社区,在这些知名大城市之外,也有蓬勃发展的小型科技社区。这里有很多优秀的工程师,引进更多的欧洲人才给我们提供了一个很好的机会,让我们可以与那些为我们的团队带来不同观点和背景的人一起丰富我们的工程文化。作为一家生产全球通用产品的公司,我们相信我们需要不同的观点,以确保我们的组织能够代表我们用户的观点和需求,并为我们用户面临的所有挑战制定适当的解决方案。

可持续发展

我们感兴趣并投资雇佣那些希望与我们长期合作,并希望与我们共同成长的人。我们努力让新的团队成员成为全职员工;爱尔兰、英国和德国是我们目前可以做到这一点的国家,我们将在未来扩大这一名单。

虽然我们从一开始就是一个在欧洲拥有强大影响力的分布式组织,但去年我们迎来了我们在欧洲的第一位工程总监(就是我!).在这里拥有这种级别的代表性和对工作文化的理解,使我们能够在招聘实践中学习和发展,以实现更大的文化包容性。这就是为什么我们在过去的几周里与我们的招聘团队和面试官一起努力提高这种包容性,讨论工作文化的差异,并相应地调整我们的流程和候选人沟通,随着我们在这里的不断发展,我们很高兴能够继续学习。

加入我们吧!

我们正在寻找对协作和学习感兴趣的好奇的人,并希望加入我们塑造软件工程的未来,支持我们的内部团队,以及数以千计的使用我们产品的组织。

我们目前正在爱尔兰、英国和德国招聘员工。

我们将在接下来的几个月里开放更多的职位供申请,期待您的申请。

使用 CircleCI webhooks 和 Airtable Automations 开发管道构建测井系统

原文:https://circleci.com/blog/webhooks-airtable/

本教程涵盖:

  1. 在 air table 中设置 webhook 自动化
  2. 在 CircleCI 上创建 webhook
  3. 测试构建日志系统

自从 CircleCI 推出 webhooks 以来,我一直对这种新的集成方式向开发人员开放的可能性感到兴奋。我决定尝试在 webhooks 文档中描述的用例之一。这个用例包括将关于构建管道工作流的信息传输到一个 Airtable 数据库中。通过管道传输到 Airtable 的数据会形成一个日志,供您监控工作流,您还可以设计图表和其他可视化工具来分析构建数据。

在本教程中,我将指导您使用 CircleCI webhooks 和 Airtable Automations 来构建这个设置。

先决条件

要完成本教程,您需要:

  1. 一个 CircleCI 项目
  2. 飞行表账户

有了这些,你就可以开始了。

创建航空表数据库

第一步是创建一个 Airtable 数据库。前往您的 Airtable 帐户,在工作区内创建一个新的 Airtable Base

Create base - Airtable

单击底部以打开数据库表格。在表格上,使用Single line text字段类型创建以下字段:

  • ID:web hook 的 id
  • Project:触发 webhook 的 CircleCI 项目的名称
  • Status:项目构建的状态
  • Time:触发 webhook 的时间

注意: 您可以编辑或删除默认创建的字段,并创建我刚才列出的新字段。

这是您将在 Airtable 中捕获的 webhook 有效负载的子集。

Configure Table - Airtable

在 Airtable 中设置 webhook 自动化

要在 Airtable 中捕获 webhook 有效负载,您需要在刚刚创建的基础上设置一个自定义的 Airtable 自动化。点击屏幕右上角的自动

Click Automations - Airtable

在自动化页面上,点击创建定制自动化

Create custom Automation - Airtable

这将打开“自定义自动化”页面。在此页面上,将自动化重命名为Builds Automation(或任何您喜欢的名称)。点击添加触发器,选择When webhook is received选项。

Add Trigger - Airtable

这将在右侧显示一个面板,向您显示 Airtable 将接收 webhooks 的端点。点击复制按钮,复制该 URL 供下一步使用。

现在,您需要测试这个 webhook 端点,以确保 Airtable 可以在其上成功接收您的 CircleCI webhooks。要在自动化设置中使用 webhook 有效负载,您需要使用您期望的有效负载结构来测试这个端点。

要发送测试 webhook 请求,您将使用 ReqBin 。将 Airtable webhook 端点粘贴到地址栏中,并从请求方法下拉列表中选择 POST 。点击内容标签,粘贴此样本请求:

{
  "id": "3888f21b-eaa7-38e3-8f3d-75a63bba8895",
  "type": "workflow-completed",
  "happened_at": "2021-09-01T22:49:34.317Z",
  "webhook": {
    "id": "cf8c4fdd-0587-4da1-b4ca-4846e9640af9",
    "name": "Sample Webhook"
  },
  "project": {
    "id": "84996744-a854-4f5e-aea3-04e2851dc1d2",
    "name": "webhook-service",
    "slug": "github/circleci/webhook-service"
  },
  "organization": {
    "id": "f22b6566-597d-46d5-ba74-99ef5bb3d85c",
    "name": "circleci"
  },
  "workflow": {
    "id": "fda08377-fe7e-46b1-8992-3a7aaecac9c3",
    "name": "build-test-deploy",
    "created_at": "2021-09-01T22:49:03.616Z",
    "stopped_at": "2021-09-01T22:49:34.170Z",
    "url": "https://app.circleci.com/pipelines/github/circleci/webhook-service/130/workflows/fda08377-fe7e-46b1-8992-3a7aaecac9c3",
    "status": "success"
  },
  "pipeline": {
    "id": "1285fe1d-d3a6-44fc-8886-8979558254c4",
    "number": 130,
    "created_at": "2021-09-01T22:49:03.544Z",
    "trigger": {
      "type": "webhook"
    },
    "vcs": {
      "provider_name": "github",
      "origin_repository_url": "https://github.com/circleci/webhook-service",
      "target_repository_url": "https://github.com/circleci/webhook-service",
      "revision": "1dc6aa69429bff4806ad6afe58d3d8f57e25973e",
      "commit": {
        "subject": "Description of change",
        "body": "More details about the change",
        "author": {
          "name": "Author Name",
          "email": "author.email@example.com"
        },
        "authored_at": "2021-09-01T22:48:53Z",
        "committer": {
          "name": "Committer Name",
          "email": "committer.email@example.com"
        },
        "committed_at": "2021-09-01T22:48:53Z"
      },
      "branch": "main"
    }
  }
} 

这个示例负载的结构与 CircleCI 将为一个workflow-completed事件发送给 Airtable 的负载结构相同。

Sample Request - Reqbin

你的要求应该和前面的截图差不多。

现在,点击发送来测试你的 webhook 端点。成功的测试应该返回成功消息。

Successful test - Reqbin

返回到 Airtable,并转到您复制 webhook 端点的屏幕。Airtable 可能已经自动检测到您的 webhook 请求,并显示了一条成功消息。如果没有成功消息,按下测试触发器按钮。

Successful test - Airtable

Airtable 现在理解了 webhook 有效负载的结构,您可以在自动化中使用它。

添加自动化操作

下一步是添加从 CircleCI 接收到 webhook 时要执行的操作。转到自动化页面。点击运行动作选项卡上的 +添加动作按钮。从下拉菜单中选择 Create Record ,为接收到的每个 webhook 在 Airtable 中创建一个新条目。在屏幕右侧的属性面板上,为表格字段选择Table 1(这是使用您的 Airtable base 创建的表格的默认名称)。

在表单的字段部分,选择ID字段。这将在字段下方显示一个文本框。点击加号 + 图标。然后点击Webhook (from Step 1: When webhook received)选项上的继续。点击body然后点击插入选择id属性。

使用相同的方法将下列表格字段映射到其各自的有效负载属性:

  • 项目 : body > project > name
  • 状态 : body > workflow > status
  • 时间:body>

您的字段应该类似于下一个屏幕截图中的字段。

Automation fields

使用名称旁边的切换按钮打开自动化。

Turn on automation - Airtable

返回到表格视图。您的自动化将在屏幕右侧列出。描述应该说:“当一个网页挂钩收到创建一个记录”

New automation - Airtable

在 CircleCI 上创建 webhook

下一步是在 CircleCI 上创建 webhook。当您的项目构建或工作流完成时,会向 Airtable 发送一个 webhook 请求,以记录关于构建过程的信息。

准备好 webhook URL 后,现在可以在 CircleCI 上设置 webhook 了。转到你的任何 CircleCI 项目,导航到项目设置> Webhooks 。在 Webhooks 页面,点击添加 Webhook 按钮。

这将显示一个表单,您可以使用它来创建您的 webhook。按照下一个屏幕截图所示填写表单。给它一个名称,并从 Airtable 中输入您的 webhook 端点 URL。

Create Webhook - CircleCI

点击添加网页挂钩按钮创建你的网页挂钩。一旦创建,它将显示在网页挂钩列表中。

测试构建日志系统

现在,您已经完成了将构建信息记录到 Airtable 中的所有设置。考验它的时候到了。运行几次构建,直到完成。一旦您的项目完成了几个构建,请检查您的 Airtable 基础上的表格以查看条目。

Webhook Logs - Airtable

结论

现在你有了,一个使用 CircleCI webhooks 和 Airtable automations 为你的 CircleCI 构建的日志系统。当然,您可以通过向表中添加更多的字段并将它们映射到各自的有效负载属性来扩展捕获的信息量。您还可以使用捕获的数据在 Airtable 中创建不同的视图。只需一点努力,您就可以使用该系统执行分析操作,从您的数据中获取可操作的信息。

编码快乐!


Fikayo Adepoju 是 LinkedIn Learning(Lynda.com)的作者、全栈开发人员、技术作者和技术内容创建者,精通 Web 和移动技术以及 DevOps,拥有 10 多年开发可扩展分布式应用程序的经验。他为 CircleCI、Twilio、Auth0 和 New Stack 博客撰写了 40 多篇文章,并且在他的个人媒体页面上,他喜欢与尽可能多的从中受益的开发人员分享他的知识。你也可以在 Udemy 上查看他的视频课程。

阅读 Fikayo Adepoju 的更多帖子

欢迎,GitHub Actions:为什么 CircleCI 仍然是团队的最佳工具

原文:https://circleci.com/blog/welcome-github-actions-why-circleci-remains-best-tool-for-teams/

今天 GitHub 宣布他们正在扩展行动以包括 CI/CD。我们知道这一天的到来已经有一段时间了,我们欢迎我们的长期合作伙伴 GitHub 进入 CI/CD 领域。

持续集成和持续交付对于软件团队来说是至关重要的。您的 CI/CD 管道是交付管道的心脏和控制中心。虽然工程团队正在接受开发运维及数字化转型,但世界正变得越来越复杂、越来越互联。安全性向左转移到开发,测试向右转移到生产。无服务器、无部署、GitOps、NoOps 的兴起,以及交付中令人眼花缭乱的变化,对任何团队来说都是一个挑战。)。

我们也知道开发者和创新在开放的生态系统中茁壮成长。没有一个供应商能够模拟今天将要构建的内容的复杂性或可能性,更不用说明天了。当一切都变得更快时,你必须灵活应变。当团队陷入工具链时,没有人会赢。

CI/CD 是一个很难解决的问题。我们在帮助团队从想法到交付方面拥有超过九年的经验,在此期间,我们看到许多团队进入和离开 CI/CD 市场,以及所有大型云公司的进入者。5 亿多的构建之后,我们将在这个领域长期发展,我们将为我们的客户打造最好的产品、团队和服务。

两周前在我们价值 5600 万美元的 D 系列产品发布会上,我们为 CircleCI 和我们的客户预览了我们的计划。以下是我们概述的四个领域:

  • 继续关注 CI/CD。我们的首要任务是使 CI/CD 流程具有可扩展性、高性能和智能化。除了之前对 Docker、Linux、Android 和 macOS 的支持,我们昨天刚刚推出了 Windows 执行环境。我们还在资源类、管道和工作流灵活性方面进行了大量投资,以便团队对他们的 CI/CD 过程拥有尽可能多的权力和控制。

  • 投资使 CircleCI 灵活地与您喜欢的工具一起使用,并通过 orb 和我们的 API 继续我们的可扩展性之路。最好的开发人员不应该在孤岛中工作,你的工具也不应该。我们将继续围绕您最喜欢的代码存储选项构建集成,并通过 orb 创建开放、可扩展的端点和配置共享机会。连接到您的团队快速、安全移动所需的所有工具和服务。

  • 展现我们 9 年多的数据,为您的交付渠道的健康状况提供更多分析和见解。我们有大量关于代码如何从想法到交付的数据,我们计划让它成为现实。我们计划利用这些数据,使访问更多构建指标成为可能,并最终为您的管道创建智能自动化。

  • 成为开发人员的最佳工具,拥有企业团队所需的控件。开发人员是我们产品的核心,我们希望提供无缝的交付体验。随着我们继续与客户一起成长,我们还将投资于大型团队所需的控制,以及您期望从 pro tools 获得的支持、指导和专业性。

如今,我们每月在 Windows、Docker、Linux 和 macOS 上运行 3000 多万个作业,并且还在不断增长,无论是在云中还是在防火墙后面。我们在旧金山、波士顿、丹佛、多伦多、东京和欧洲有一个 250 多人的全球产品、工程、支持和客户管理团队,我们团队的 30%在世界各地远程工作。我们认为 CI/CD 以及您的团队从创意到交付的成功非常重要,这就是我们所做的一切。

差点不及格的 GRE 教会了我什么是入学障碍

原文:https://circleci.com/blog/what-almost-failing-the-gre-taught-me-about-barriers-to-entry/

大学毕业后,我在洛斯阿拉莫斯国家实验室(LANL)做了一年的学士后工作。在大学和实验室,我和一个团队一起研究卡西尼飞船上仪器的数据,卡西尼飞船在土星轨道上运行。在和 LANL 的团队一起工作时,我也在做申请研究生项目所需的事情:进行和展示研究,获得推荐信,以及参加考试。我想 100%通过 GRE(美国研究生入学考试)的数学部分,所以我花了几个月的时间每天花几个小时练习数学题。

考试那天,我去了考试中心。在开始考试之前,我最不需要的就是一份笔迹样本。有人递给我一张纸,上面有一段打字稿,我要用草书抄写。高中毕业后我没有马上去上大学。我有点老了,已经很久很久没有用草书写东西了。我的焦虑增加了。我低头看页面,段落以“我(你的名字),做……”开头,我愣住了。我不知道如何用草书写大写的 I。我想得越久,我的焦虑就越多。我开始怀疑我是否应该去那里。我想知道我是否是研究生院的材料。心怦怦直跳,我终于决定跳过我,继续前行。到最后,我想起了如何写一篇论文,我填了,但是由于这个不吉利的开头,我考得不好。我确实再次参加考试并且我得到了我的 100 分。直到今天,只要看到草书就让我毛骨悚然。

在上面的故事中,GRE 组织有意识地决定使用笔迹样本作为防止欺诈的策略。这是基于一个假设,即一个从大学毕业并准备读研的学生将能够写草书。这个假设似乎是合理的。然而,仅仅合理是不够的,而且还制造了一个意想不到的进入壁垒。任何时候你在创作别人会使用的材料时,你都应该考虑进入壁垒。做出合理的假设比批判性地考虑用户的需求和他们可能拥有或可能不拥有的特定能力更容易,但这通常会导致意想不到的后果,即遗漏了其他方面合格的人。

提供可访问性通常被认为是“伸手扶某人起来”在上面的例子中,我根本不需要帮助——我能够 100%通过考试!让其他人也能接触到东西并不是提供慈善。这是关于消除你可能没有意识到的障碍。

对于一些人来说,要求一套专业技能来完成练习中的一个步骤可能是一个不可逾越的障碍。如果我们自己拥有这种技能,就很难识别这些特殊的技能。

因此,下一次当你考虑起草入职内容或为用户设计新体验时,问问自己:我能删除需要特殊技能的步骤吗?如果你的入职经历中有任何步骤需要用草书书写,那就把它们去掉!

关于高绩效团队,变更失败率告诉了我们什么?圆环

原文:https://circleci.com/blog/what-does-the-change-fail-rate-tell-us-about-high-performing-teams/

正如我们在关于 CircleCI 的新报告的博客系列文章中所讨论的,CI 的数据驱动案例:实践中 3000 万个工作流揭示了 DevOps 的什么,速度并不是 DevOps 的一切,至少不是它本身。正如速度在真空中不是一个好的衡量标准,更多的绿色构建不一定意味着高性能。在这篇文章中——我们探索 DevOps 高绩效指标系列的第四篇也是最后一篇——我们将关注变更率失败(也可参见我们系列中的交付时间部署频率平均恢复时间文章)。与速度测量非常相似,变化率故障度量比看起来要复杂得多。

如 DevOps 2019 报告的状态所述,变更失败率或变更失败率定义如下:

对于您使用的主要应用程序或服务,生产或向用户发布的更改中有多少百分比会导致服务降级(例如,导致服务受损或服务中断)并随后需要补救(例如,需要热修复、回滚、向前修复、修补)

变更失败率允许 DevOps 团队衡量他们在高性能之旅中的进展——这是通过采用持续集成和持续交付(CI/CD)来辅助的。当我们检查工作流数据以分析标准行业指标实际上如何匹配时,我们发现大量证据表明 CI/CD 提供了成为高绩效团队的清晰途径:

  • 使用 CI 的团队非常快:80%的工作流在不到 10 分钟内完成。
  • 使用 CI 的团队保持流畅并保持工作进展:50%的恢复发生在不到一个小时内。
  • 25%的组织在 10 分钟内恢复。
  • 50%的组织在一次尝试中恢复。

如果一个团队产生了没有错误的代码,这并不总是意味着他们是一个高性能的团队。事实上,对于每个技能水平的团队来说,红色构建是开发过程中的一个日常部分。关键是能够尽快对失败采取行动,并从失败中收集信息以改进未来的工作流。(红色版本是有原因的,你需要找出这些原因。)

为什么主题分支是改善信号的最佳地方

根据 DevOps 2019 报告的状态中探索的那些指标,最高性能的团队很少将坏代码推送到他们的默认分支。但是不要认为这意味着这些团队从不写错误的代码。这些团队在单独的分支上执行测试和安全检查;只有当一切都通过时,才允许发生到默认分支的合并。

这是一个很好的 DevOps 实践——起源于 Vincent Driessen 的 Git-flow 模型。团队应该知道他们的代码在合并到默认分支之前工作良好。主题分支是获得最快信号的最佳地方,也是最容易失败的地方。主题分支上的变更失败率更高,因为这是大部分工作完成的地方,并且因为这些分支上的失败不会导致默认分支停止。这些分支上的失败只会影响在相同分支上工作的人,而不是整个代码库或产品。

我们还观察到其他团队使用基于主干的开发,这是另一种常见的开发策略,团队成员直接在默认分支上开发。该策略针对恢复时间进行了优化——当一个构建为红色时,每个人都在努力恢复。然而,我们在数据中并没有看到这种方法被如此频繁地使用。

数据告诉我们的是

为了了解观察到的开发行为与行业标准相比如何,我们查看了 2019 年 6 月 1 日至 8 月 30 日期间观察到的超过 3000 万个工作流的 CircleCI 数据。工作流代表:

  • 每天运行 160 万个作业
  • 超过 40,000 个组织
  • 超过 150,000 个项目

以下是我们的发现:

  • 总体而言,CircleCI 上 27%的工作流都失败了。
  • 主题分支的平均失败率为 31%。
  • 如果我们只看默认分支,变更失败率下降到 18%。
  • 当编排 CI/CD 流程的 circle.yml 文件发生配置更改时,50%的项目从未失败过。

一些 DevOps 专家认为每个分支都应该是绿色的。但是在我们看来,只要团队能够快速恢复,红色构建是好的。一个失败的构建不一定意味着一个问题:它意味着您的 CI 系统正在工作,并为您提供有效的数据。

如果我们按分支类型来看失败,我们可以更深入地了解失败的真正含义。例如,违约分支的失败率更低就不足为奇了。我们的数据发现,在我们观察的 90 天时间内,超过 30%的活动项目分支从未失败过——这支持了我们的信念,即在主线集成之前,主题分支是大多数变更集被提交和验证的地方。在对主题分支进行了大量的准备工作之后,当您合并到默认分支时,您会对代码将要产生的变化有更好的理解。

我们发现默认分支机构的变更失败率为 18%,这与 DevOps 2019 报告中针对高绩效者的状态一致,显示精英团队的变更失败率为 0-15%。

当对 circle.yml 文件进行配置更改时,50%的项目从未失败,这一发现让我们感到惊讶。一种可能的解释是配置被重用。例如,一个拥有许多相同配置的项目的组织将更新一个项目,并在变更通过测试后复制它。另一种解释是使用 CircleCI orbs,这是一种可重用、可共享的配置包,团队可以使用它来添加功能,而不用担心失败,因为它们经过了作者的测试和社区的验证。不管这种低失败率是如何产生的,它对 CircleCI 的客户来说都是非常重要的,因为它反驳了广泛持有的配置更改很难并且需要频繁更新的观念。

采用 CI/CD 的增量价值

我们知道优化单个 DevOps 指标或选择特定的工具不会让您成为高绩效团队:事实上,当您在整个组织中应对这些变化时,采用 CI/CD 会增加失败率。至少在短期内是这样。如此大规模的改变并不容易!如果你能够降低变更失败率,这是朝着正确方向迈出的一步。随着你的进步,你将沿着生产最好的软件的道路前进,持续地,高速地。简单地采用 CI/CD 原则会让您踏上提高性能的道路。

什么是 CI/CD 管道?圆环

原文:https://circleci.com/blog/what-is-a-ci-cd-pipeline/

管道介绍

CI/CD 管道是自动化软件开发的最基本的组件。虽然该术语已被用于描述计算机科学的许多不同方面,但在 CircleCI 和 DevOps 行业的许多地方,我们使用“管道”来说明持续集成(CI) 中涉及的行为和过程的广泛应用。

CI 是一种软件开发策略,它可以提高开发速度,同时确保所部署代码的质量不受影响。使用 CI 工具,开发人员不断地以小增量提交代码,有时一天多次,然后在与共享存储库合并之前自动构建和测试代码。现代软件交付管道可以根据您的业务需求构建、测试和部署应用程序。

点击观看 CI/CD 101 视频。

什么是 CI/CD 管道?

CI/CD 管道是在您触发项目工作时运行的一整套流程。管道包含您的工作流,这些工作流协调您的作业,这都在您的项目配置文件中定义。

持续集成自动化了软件的构建和测试。连续部署是这种自动化的扩展,它允许在通过测试套件的每个代码提交之后部署您的软件。最成功的开发团队经常部署他们的软件。有关更多信息,请阅读我们的持续集成(CI)与持续部署(CD) 页面。

CI/CD 管道的组件

持续集成自动化了软件的构建和测试。连续部署是这种自动化的扩展,它允许在通过测试套件的每个代码提交之后部署您的软件。

最成功的开发团队经常部署他们的软件。有效地交付高质量的软件意味着使用 CI/CD 最佳实践来构建、测试和部署代码。

构建阶段

在构建阶段,多个开发团队将他们自己机器上开发的代码贡献给一个共享的存储库。这听起来很简单,但很快就引入了复杂性。除了版本控制,还会出现一些问题,包括开发人员和生产环境、工具和代码质量的细微差异。在您的管道中包含构建过程的优点是,它自动化了开发人员的贡献,并提供了标准化软件质量和环境的工具。

检验阶段

开发团队经常直接进入部署阶段。这是一个错误,因为测试阶段是 CI/CD 的主要优势所在。测试是一个复杂且重复的过程,您的 CI/CD pipeline 可以帮助您实现自动化。

有几种不同类型的测试,所有这些都可以在自动化持续集成管道中一起使用。您可以将单元测试与集成测试结合起来,以提供尽可能大的测试覆盖范围。测试还提供了关于软件性能的重要数据,这些数据可以立即集成到代码中。测试的结果是高质量的软件,错误越来越少。

部署阶段

在部署阶段,您可以将软件发布编排到生产或其他环境中。您的管道可以配置为按计划部署代码,向所有客户或仅一个选定的组推出软件,甚至在出现问题时回滚发布。您可以决定向客户提供更新软件的最佳策略。这些都可以作为 CI/CD 渠道的一部分实现自动化。

配置 CI 管道

持续集成的 DNA 是配置。 CI 管道是配置文件中最高级别的编排。

CI 工作流

工作流允许您单独运行作业并进行故障排除,因此您可以实时查看失败的构建。如果工作流中的一个作业失败,您可以单独重新运行该作业,而不是重新运行整个作业集。

An image of workflows from CircleCI's UI

CI 作业

作业是在单个单元中执行的步骤的集合,工作流是定义该组作业及其运行顺序的规则集。

An image of jobs from CircleCI's UI

配置步骤

在作业中,步骤是单个键值对的列表。该键指示步骤的类型,值可以是配置映射或字符串。当步骤是运行时,您可以将要执行的命令指定为字符串值。

An image of steps from CircleCI's UI

带有 CircleCI 的 CI/CD 功能

在 CircleCI 上通过管道构建代码时,您将遇到的一些特性是使用多个资源类、并行测试分割、orb、矩阵作业、环境变量/上下文和批准。

  • 资源类 : CircleCI 提供了大量的资源类,可以让你为每个任务优化 CPU 和 RAM 资源。

  • 并行测试分割:为了减少时间,并行运行测试,将它们分布在多个独立的容器中。

  • orb:orb是 YAML 配置的可重用包,它将重复的配置压缩成单行代码。

  • 矩阵作业:矩阵作业允许你用不同的参数多次运行一个参数化的作业。

  • 参数 : 管道变量环境变量上下文是允许用户存储和重用数据以及保护敏感信息的参数。

  • 批准:工作流程可编程为等待手动批准后再继续。对您的存储库具有推送访问权限的任何人都可以批准作业以继续工作流。

在 CircleCI 开始建造管道

如果您不熟悉持续集成,理解管道的作用和能力将有助于您理解 CI 的真正价值。要了解更多信息,请访问 CircleCI 资源页面。首先,查看我们的文档

软件材料清单:它是什么,为什么你需要一个

原文:https://circleci.com/blog/what-is-a-software-bill-of-materials/

大多数制作软件的组织——从小初创公司到数十亿美元的巨擘——都使用第三方库和工具来开发他们的应用程序。现代应用程序依赖许多外部组件来构建软件并交付给客户。这些库和工具统称为软件供应链

典型 web 应用程序的软件供应链可能包括以下组件:

  • 正在运行的 Linux 实例
  • 全系统共享库,如 libcrypt 和 glibc
  • 通过 npm、NuGet 和 Maven 等包存储库安装的开源框架和库
  • 从其他应用程序复制的大量源代码(或堆栈溢出)
  • 支持数据库和内存缓存等应用
  • 像 jQuery、D3 和 React 这样的 JavaScript 库
  • 底层支持服务,如 Apache、IIS 和 nginx

当这些组件中的任何一个出现安全缺陷时,其影响会扩展到所有使用受损组件的应用程序。这会使任何被访问或处理的数据面临风险。威胁参与者利用这些供应链漏洞来渗透目标网络。供应链漏洞利用的两个著名例子是 2020 年的网络安全管理软件产品黑客攻击和 2021 年的 Log4j 漏洞。

减少软件供应链漏洞的一种方法是维护一个软件材料清单

什么是软件材料清单?

使用软件材料清单(SBOM)是减少软件供应链网络攻击的高效策略。就像菜谱列出了做一顿美味佳肴所必需的配料一样,SBOM 列出了软件应用程序的所有组成部分。

SBOM 包括用于创建特定软件应用程序的所有库、代码包和其他第三方组件。SBOM 名单还包括:

  • 每个组件的许可类型(共享、开源或商业)
  • 组件版本
  • 补丁状态
  • 软件供应链中这些组件之间的依赖关系

SBOM 的重要性远远超出安全范畴。它还表明符合主要的数据隐私法规,确保软件应用程序中涉及的所有组件都是透明和可跟踪的。

SBOM 中包含的信息对于防止软件供应链网络攻击至关重要,以至于美国商务部已经发布了各种相关文件。

国家电信和信息管理局(NTIA)提出了三种格式用于生成识别软件实体及其相关元数据的 SBOM 清单:

  • 软件包数据交换(SPDX) 是一个开放标准,用于创建包含所有软件组件、组件许可证、版权和安全参考的 SBOM 清单。
  • OWASP CycloneDX 是一个轻量级的 SBOM 标准,用于为风险分析创建第一方和第三方软件组件的完整清单。CycloneDX 可以记录组件类型,包括应用程序、容器、库、文件、固件、框架和操作系统。
  • 软件识别标准(SWID) 由国际标准化组织(ISO)和国际电工委员会(IEC)制定。SWID 是一个 XML 文件,包含软件组件及其许可证、修补程序状态和安装包的列表。

为什么 SBOM 很重要

软件供应链——用于编写、构建或运行应用程序的每个工具、框架或库——已经成为软件漏洞和违规的主要来源。软件开发人员和组织越来越多地在许多应用程序之间重用代码、软件包和库。许多第三方供应商也出售商业代码包,可以与 Office 365 等广泛使用的生产力应用程序集成在一起。

构建软件应用程序的复杂性使得盘点用于创建每个应用程序的所有组件变得至关重要。编目使组织能够在恶意行为者获得对敏感资源的未授权访问之前检测并解决安全漏洞。

使用 SBOMs 也是有益的,因为它:

  • 避免重用软件项目中易受攻击的组件
  • 帮助发现当前应用中易受攻击的部分
  • 通过了解软件应用程序中使用的所有组件(及其依赖关系),更好地管理软件供应链风险
  • 通过提供所有应用程序组件及其相关特征的列表,帮助组织更好地遵守各种数据保护法规
  • 帮助组织选择提供其应用软件的首选软件供应商
  • 帮助组织维护其 IT 环境中所有软件应用的清单
  • 通过在软件开发生命周期(SDLC)的早期设计阶段检测易受攻击的部分,节省组织的时间和资源
  • 引起对与软件供应链相关的安全风险的必要关注

美国政府发布了一项行政命令,指示联邦机构制定改善软件供应链安全的计划。因此,许多商业企业纷纷效仿。

如何生成 SBOM

组织或个人可以手动或通过自动化技术生成 SBOM。手动方法对于较小的项目可能是正确的,但是对于其他项目可能不太理想。

手动方法

开发人员可以在电子表格或类似文件中手动列出所有软件组件,以及每个组件的版本、许可证、依赖关系和任何其他相关信息。

这种方法适用于组件很少的小型项目。但是,对于大型项目来说,这还远远不够理想。根据 ZDNet 的说法,大多数 GitHub 项目有大约 700 个依赖项。企业级系统肯定会超过这个数字。试图为一个具有数千个直接和可传递依赖项的项目手动生成 SBOM 清单是不现实的。

手动方法也容易出现人为错误。更新一个依赖项,然后忘记更新 SBOM,这太容易了。

自动化方法

对于大多数项目来说,自动化 SBOM 是最好的方法。自动化允许您维护所有组件的更新列表,包括语言、库和 Docker 映像。

您可以将自动化工具整合到您的持续集成和持续交付(CI/CD)管道中,以扫描您的所有软件项目,并根据需要更新它们的相关组件。流行的依赖扫描器包括 SnykAnchore

大多数 SBOM 生成工具也能很好地与 CI/CD 工具集成。这些工具自动扫描您的软件项目,列出专有和开源软件组件以及相关属性,如许可证和对第三方库的依赖。

摘要

SBOM 为合规性、安全性和管理每个应用程序许可协议的各个部分提供了许多好处。自动 SBOM 有助于确保您组织的全面覆盖和最新信息。

使用 SBOM 并将其组件库存与网络威胁情报源联系起来的组织可以更好地跟踪其应用程序中的零日漏洞。然后,他们可以在对他们的软件完整性造成重大损害之前修复它们。

了解更多关于如何在 CI/CD 管道中集成依赖扫描和 SBOM 生成工具以增强软件安全性的信息。

什么是 AIOps?圆环

原文:https://circleci.com/blog/what-is-aiops/

AIOps 是一种通过应用人工智能(AI)来管理 IT 运营指数级增长和新技术复杂性的方法。IT 基础设施越来越依赖于复杂的部署、多云架构和海量数据。传统上,科技行业通过运用额外的脑力来解决问题,引入更多的工程师、开发人员和管理人员来应对复杂性。但是随着对软件团队需求的持续增长,增加更多的员工显然不是一个可扩展的解决方案。IT 行业没有用人工智能来解决这些问题,而是求助于人工智能解决方案来帮助团队处理软件令人难以置信的规模和无尽的创新。

在这一集的《自信的承诺》中,CircleCI 的首席技术官 Rob Zuber 与 Modzy 的工程总监 Nathan Mellis 坐下来讨论 ML 和 AI 的所有事情。

以云错误配置为例。当团队为云数据保护建立的设置不充分时,就会发生这些事件。但是这些错误的配置并不是智力不足的产物。相反,在分布式计算和一切即服务的世界里,它们几乎是不可避免的(XaaS)。

一个复杂的 IT 系统可能会产生足够多的此类事件,甚至超过我们最聪明、最高效的团队的维护能力。每一次事故都需要我们投入时间和精力,否则我们的基础架构就很容易受到攻击,我们的数据(以及我们客户的数据)也会暴露给攻击者。

进入 AIOps 的世界。

AIOps 简史

研究公司 Gartner 在 2016 年首次创造了 AIOps 这个术语。Gartner 将“人工智能”和“IT 运营”这两个词结合在一起,为 IT 行业的一支发展力量命名,将 AIOps 定义为 AI 在 IT 运营中的应用。

AIOps 利用机器学习和大数据来辅助 IT 运营。从这个意义上说,这种做法几乎肯定要早于 Gartner 对该术语的定义——处于人工智能研究前沿的公司很可能多年来一直在应用算法流程来帮助他们的内部运营。

在这个领域曾经是稀罕物的东西正在迅速成为现代 IT 环境中的必需品。在全行业向容器和微服务的转变中,系统的组件成倍增加。在错综复杂的 IT 领域工作的开发人员越来越坚持想要人工智能工具的帮助。

在一个充斥着人工智能软件的市场上,人们可能很容易将 AIOps 视为又一个短暂的趋势,因为各公司都在寻求营销其机器学习工具的方法。然而,大规模的大数据和软件带来的安全挑战是实实在在的。如果没有某种形式的 AIOps,IT 组织肯定会发现许多阻碍其发展的障碍。

与 ai 的合作

AIOps 对软件开发最重要的贡献是在人工智能和人类智能的交叉点上。

发现并诊断错误和效率低下是任何 IT 运营部门面临的持续挑战。公司通常会大规模地构建、部署和运行软件,为数百万甚至数十亿人提供服务。这些庞大的系统及其庞大的用户群产生了大量的数据,没有一个组织希望通过手动方式对其进行分类。机器学习的优势在于其分析和处理海量数据的能力。

有效的 AIOps 充当所有这些数据的第一线解释者。AIOps 使组织能够通过监控日志、过滤信息和使用机器学习来发现模式和见解,从而充分利用他们的开发时间。在 AIOps 的帮助下,团队可以减少花费在繁琐任务上的时间,例如仔细研究日志和尝试隔离复杂系统中的问题来源。

对于任何 IT 运营来说,检测和诊断缺陷和瓶颈都是一项持续的挑战。AIOps 筛选原始数据,帮助开发人员快速解决他们的 AI 工具识别的问题。人工智能可以通知团队异常活动,提供错误警报,并作为早期预警系统,在问题变得严重之前发现问题。这不仅有助于调解内部错误,还有助于识别和规避恶意的外部攻击。

人类没有能力处理现代软件的规模。AIOps 通过执行人类智能不适合处理的安全功能来节省时间和麻烦。

DevOps vs AIOps

任何从事 IT 行业的人都可能会忧虑地观察 AIOps 的发展。然而,AIOps 并不是要取代 DevOps,甚至不是要与之竞争。AIOps 的主要应用是监控和报警——这是对现有 IT 部门的一个决定性的补充。

DevOps 团队不断处理来自不同来源的数据,处理来自任意数量的云平台、Kubernetes 集群、无服务器功能以及部署中的所有其他东西的数据。所有这些信息都必须经过整理,并与重要变量相关联。这是一个可以从自动化中获益的挑战。

您的典型 AIOps 被配置为数字看门狗,向传统 DevOps 团队发出错误和异常警报。更复杂的 AIOps 可以承担分析角色,处理重要指标的数据,为改进软件或优化部署提供建议。

虽然不可能很快取代 DevOps,但 AIOps 无疑是未来的一个预兆。DevOps 从业者需要适应自动化程度不断提高的环境,并找到将机器学习和其他人工智能工具集成到工作流程中的方法。如果不愿意接受这些新工具,软件行业的扩展能力将会停滞不前,对应用程序安全性的潜在威胁将会出现。

如何将 AIOps 整合到您的 DevOps 实践中

大多数组织开始尝试 AIOps 时都是扮演监视角色。人工智能不断扫描系统,并标记任何潜在的问题。这种类型的持续异常检测有利于先发制人地减轻停机,并在它们发展成灾难之前发现警告信号。

AIOps monitoring 可以作为一个安全层,监视您的整个系统。它在几毫秒内处理大量信息的能力使它能够先发制人地识别安全缺陷或检测可能表明安全漏洞的模式。您可以进一步将 AIOps 的安全潜力与其他可用的重要工具相结合,以保护大规模应用程序,确保我们的数据在多层冗余背后是安全的。

在将 AIOps 用于监控和安全之后,对于大多数团队来说,将 AIOps 工具集成到他们的 DevOps 策略中是一个自然的过程。除了提供立竿见影的效果,如自动化重复任务和使关键数据更容易获得,AIOps 系统还可以根据它们得出的结论采取行动。

组织也可以采用 AIOps 进行诊断。作为诊断工具,AIOps 是根本原因分析的有力助手。当失败不可避免地发生时,AIOps 在提醒团队注意问题并引导他们找到问题的根源方面扮演着积极的角色。随着 AIOps 的能力越来越强,它开始在补救问题方面发挥作用。

通过持续分析和建议对资源使用等指标的精确调整,机器学习算法还可以微调系统性能,并识别新的机会来加强安全性或优化架构和部署。

拥抱 AIOps 的未来

对于大多数团队来说,将 AIOps 工具集成到 DevOps 策略中是一个合乎逻辑的发展。DevOps 程序受益于卸载重复性任务,转而致力于改进软件。AIOps 还为团队提供了更好的访问软件性能信息的途径,以推动这些改进。

我们认为 AIOps 不是一种威胁,而是一种超级力量,它使 DevOps 团队能够管理比人类能力更多(更复杂)的数据集。正确的 AIOps 策略可以帮助 DevOps 团队克服扩展软件所固有的巨大的、相互关联的挑战。大型组织能够以最小的成长痛苦进行扩展,而小型团队可以通过利用人工智能而不是增加更多的员工来避免膨胀。而且,随着其功能的扩展,AI 可能会对 DevOps 和 IT 变得更加重要。自动化在补救问题和提供微调建议方面也将变得更加智能和积极。

如果您是 DevOps 实践者,希望您的团队的策略经得起未来考验,那么 AIOps 应该是您研究的方法之一。拥抱自动化并将人工智能和机器学习工具集成到您的工作流中,将为您提供安全可靠地扩展企业的机会。

获取电子书 6 应用安全之路了解更多关于如何在开发过程中实现 AIOps 以及 AI 如何帮助您维护应用安全的信息。

什么是云爆?管理混合云| CircleCI 上的零星工作负载

原文:https://circleci.com/blog/what-is-cloud-bursting/

DevOps 领域正在进行一场伟大的向云的集体迁移。企业正在分散他们的应用程序和数据库,将它们托管在云中,使它们不受地理位置或用户设备的影响。

一些组织选择在私有服务器上托管他们的应用程序,但是在高需求时期,通过将溢出流量导向云服务器来利用公共云。这种方法叫做云爆发

您可以使用云爆发来保持您的服务不受中断,同时有效地分配资源。您可以将多余的计算工作卸载到公共云提供商,并将敏感或低延迟工作保留在您的私有云及内部基础架构上,如物理上位于数据中心的服务器和任何关联的虚拟机(VM)。一些公司选择外包他们的私有云托管。

如果您提供的服务经历了短暂的流量显著增加,计算需求可能会超出您的私有云的容量,并影响您的用户体验。结合了公共和私有基础设施的混合云方法使您能够处理假期期间电子商务增长的季节,或者导致重要发布或发货日期的开发工作。它还解决了您的组织没有确切模型的任何意外工作负载。

每种方法都提出了不同的挑战,因此了解每种部署类型可能对您的优先级产生的影响是有好处的。例如,混合云引入了公共云固有的安全挑战和体系结构考虑因素。需要隔离且高度安全的网络的组织可能会在内部运行其应用程序。

在本文中,我们将探讨什么是云爆发,了解如何以及何时实施云爆发,并讨论如何确定云爆发是否适合您的组织。

什么是云爆?

云爆发是一种混合云部署技术,其中私有云上的应用程序被配置为将溢出流量重定向到公共云中。这种重定向是为了应对超过私有云容量的计算资源需求,使应用程序或网络能够处理通常会淹没公司私有服务器的零星工作负载。

您可以在公共云中配置额外的计算资源,就像配置计量工具一样。通过购买时间和容量,您只需为您在公共云中使用的内容付费。当需求低于最大容量时,应用程序或数据会移回您的私有云。

跨越这个阈值的接近和切换是平滑的。随着对计算资源的需求接近您的系统可以处理的程度,应用程序会自动切换到公共云,而不会中断应用程序的服务和用户体验。

您可以使用这种技术来管理关键和非敏感应用程序,为关键和敏感应用程序释放私有云上的资源。

云爆发策略

您可以将云爆发配置为手动部署或根据您在软件中指定的策略进行部署。

手动爆破

手动突发允许您在遇到或预计流量增加时手动分配对云资源的访问。在手动分配突发容量之前,无需等待负载平衡器发送通知。

例如,假设你为一家公司工作,该公司计划在一个受欢迎的体育赛事期间投放广告。你应该提前几天提供足够的云爆发能力,并对其进行负载测试,以确保它能够满足潜在需求。

通过创建大型临时云部署,您可以为业务关键型应用程序释放空间或本地资源。例如,您组织的人工智能团队可以在内部训练和测试他们的模型,然后购买额外的容量来在大型数据集上训练大型模型。

手动拆分让您可以在部署之前测试云拆分项目,但是这种技术在关键点需要人力。在手动突发的每一种情况下,当需求下降时,您都需要手动取消所有这些云资源的供应。这让你容易犯人为错误。毕竟,云容量是一种昂贵的商品,一旦你提供了它,你就承诺了交易——即使你不使用容量。

自动爆裂

自动云爆发涉及设置指导软件如何处理爆发的策略,它将在不需要人工监督的情况下完成。

当您预计流量会出现峰值,但不知道峰值会在何时出现或有多突出时,这种技术尤其适用。例如,一个电子商务网站预计在假期期间会出现流量高峰,可以使用自动爆发来处理额外的负担,即使他们不确定他们的营销工作在那一年是否成功。

云爆发用例

到目前为止,所描述的大多数用例都展示了云爆发如何最适合峰值或不可预测的工作负载,或者两者兼而有之。让我们更深入地探索这些用例。

峰值负载

在重要的发布或发货日期之前,云爆发在持续集成和持续交付(CI/CD) 中可能是至关重要的。您可能有大型开发团队同时处理大型项目并同时运行许多构建,这需要比您的内部服务器所能容纳的更多的资源。

定期发布大量产品更新的大型组织可能会在内部处理类似这样的峰值。尽管如此,这样做的公司通常接近公共云提供商的规模。

不可预测的需求

您可以使用云爆发来处理不可预测的流量高峰。例如,您的组织可能不确定在新的营销推广期间流量峰值会有多大。手动拆分使您能够在活动期间准备好计算资源。

银行也受益于假日期间的云爆发。节日期间交易的急剧增加可能会耗尽银行的内部服务器资源。

即便如此,在这一年的剩余时间里拥有基础设施也是不值得的。将银行的一些非关键和非敏感应用程序分散到公共云中,可以为业务敏感应用程序释放内部资源。

云爆炸的优势和挑战

尽管云爆发提供了许多优势,但它也带来了一些新的挑战,对使用爆发有着重要的影响。云爆发提供的成本效益和灵活性也使维护数据和应用程序的完整性变得更加困难。它还会增加基础设施的复杂性。

爆破最大的好处是它的成本效益。您不需要放弃您的内部基础架构,以及它对您的底线的极小影响。您只需为高峰需求期间使用的公共计算资源付费,这是您不想在正常使用期间支付的容量。

在严格控制的条件下,访问私有云和公共云的优势,为您的部署提供了灵活性。您不会受限于内部拥有的计算资源。您可以利用新的研究、开发或其他业务机会,尤其是在大型数据集上测试应用程序或模型时。

不过,小心您的基础架构如何投射到公共云中是一个好主意。将您组织的一些资源从内部硬件移动到公共云可能会在意想不到的地方增加复杂性,例如给服务用户带来延迟。您可能会发现在您的内部服务器和公共云之间建立要求低延迟和高带宽的冗余连接非常困难。

将数据暴露给公共云也意味着,如果有人攻击公共云,数据可能会受到威胁。尽管成本高且相对不灵活,但组织使用私有云模式的主要原因是保护他们的数据和应用程序。因此,请仔细考虑是否以及如何配置您的云爆发,以处理您需要保留在本地的敏感和关键数据。

如何实施云爆发策略

要为云爆发配置您的服务,请确保您的基础架构已准备好进行部署,并且您的云是完全匹配的。

仔细选择适合您需求的云服务提供商。提供商是一个多样化的群体,他们的云产品适合不同的用途。要获得最佳的云爆发体验,请选择最能满足您的应用需求的云提供商。例如,您需要访问特定类型的服务器或 GPU,这可能是选择云提供商的一个因素。

你还必须准备好你的申请。选择正确的云提供商后,确保您的应用程序已准备好部署到公共云,并具备所需的所有依赖关系。实施保护应用程序及其数据所需的任何安全措施。

然后,设置负载平衡。设置您的本地服务器可以处理的预定义流量阈值。当本地工作负载达到此阈值时,您的应用程序会通过在本地服务器和公共云基础架构之间拆分请求来自动切换到公共云。

如何决定云爆炸是否是您组织的正确方法

云爆发使组织能够扩展其业务,而无需在内部安装和维护额外的资源。这些资源是一种间接能力,你不太可能经常使用,以使投资合理。

请记住,云爆炸还会带来安全挑战,因为它可能会将敏感数据部署到公共云中。在实施云爆发之前,权衡经济高效的需求和维护数据完整性的需求。

如果您的组织运行不太敏感的服务或应用程序,或者如果您在公共云提供商受到攻击时基本不受影响,您可以使用云爆发来确保自己免受需求高峰导致的服务中断。

但是,如果安全性比成本效益更重要,那么爆发可能不是一个可行的解决方案。一些安全性敏感的工作负载必须只能在内部运行。

当您准备在软件交付策略中实施云爆发和混合云方法时,自动化 CI/CD 工具可以提供帮助。当你的应用遇到暂时的流量高峰,或者当你的开发者需要更多的资源来应对即将到来的发布日期时,CI/CD 管道可以将代码推送到云端。要了解更多关于 CI/CD 如何帮助您的团队管理高峰工作负载的信息,请阅读混合云 CI/CD,并从免费 CircleCI 账户开始。

代码签名:防范供应链漏洞| CircleCI

原文:https://circleci.com/blog/what-is-code-signing/

在创建应用程序时,开发人员通常依赖于许多不同的工具、程序和人员。参与软件开发生命周期(SDLC)的代理和参与者的集合被称为软件供应链。软件供应链是指在开发、生产和部署过程中接触或影响应用程序的任何东西,包括开发人员、依赖关系、网络接口和开发运维实践。

因为您正在处理几个移动的部分——包括开源材料、API 等等——所以了解您的软件供应链的每个组件有多安全是至关重要的。加强软件供应链的安全性可以确保源代码在供应链中传播时,不会给网络攻击者或其他恶意攻击者留下渗透的空间。这确保了发布的应用程序的安全性和真实性。

加强安全性的一个重要方法是应用代码签名。代码签名确保最终发布的软件来自原始发布者,并且不包含来自未授权方的篡改。

本文探讨了代码签名,它是如何工作的,以及它在加强应用程序安全性方面的重要性。然后,讨论了对应用程序进行代码签名时的一些最佳实践。

什么是代码签名?

代码签名使用应用程序、脚本、驱动程序和其他软件组件的数字签名来确认其原始作者。每当软件的作者进行修改时,他们都会在代码上签名,以确保没有未经授权的行为。

组织通常会签署代码,以确认所有的变更都是真实的和有文档记录的。这种做法保证了用户不会得到有漏洞或安全风险的应用程序。

在团队环境中工作时,代码签名也很有帮助。在整个 SDLC 中交换源代码时,您可以使用代码签名来确保双重身份验证,防止攻击,甚至防止名称空间冲突。

代码签名遵循三步过程:创建公钥-私钥、哈希、描述和验证。

第一步:创建唯一的公私密钥

代码签名从创建一个唯一的公钥-私钥对开始,这个公钥-私钥对可以通过 OpenSSL 等工具在本地生成。之后,您将公钥发送到受信任的证书颁发机构(CA ),验证您和您的组织的身份。在 CA 验证这些身份之后,它发回公钥和数字签名的代码签名证书。

第二步:散列

在返回代码签名证书和公钥之后,代码经过一个哈希函数。哈希函数是一种单向加密方法,它不可逆地将文本转换为混合值。然后,私钥对散列函数的输出(摘要)进行加密,并防止外部篡改。之后,散列函数、其摘要和代码签名证书被捆绑到签名块中并发送给消费者。

第三步:解密和验证

下载软件后,用户的计算机检查代码签名证书以验证其真实性。在验证之后,来自公钥-私钥对的公钥解密来自捆绑签名块的摘要。散列函数然后作用于软件代码,并将结果输出(摘要)与您解密的摘要进行比较。如果两个摘要匹配,则确认该软件可以安全安装。

加强应用程序安全性的重要性

在整个 SDLC 中,确保软件安全性仍然是一项持续的任务。通过代码签名等实践来加强应用程序安全性有助于减少攻击面并降低安全漏洞的几率。

最常见的是,SDLC 使用依赖关系来实现最佳功能。这些依赖关系通常还需要其他依赖关系,从而增加了 SDLC 中出现漏洞的途径。

为了帮助降低将恶意软件和其他漏洞引入源代码的风险,您应该只下载来自可信供应商的第三方软件和依赖项。依赖混淆和简单的配置错误会迅速破坏你的代码签名实践。因此,在实现依赖关系时,一定要小心谨慎。

攻击者也可能使用域名仿冒。攻击者使用这种技术来锁定在 web 浏览器中输入错误或拼写错误的 URL 的互联网用户。访问这些网站可能会将恶意软件下载到计算机系统中,或者允许访问敏感信息。

苹果应用商店和谷歌 Play 商店的代码签名

作为软件分发平台,苹果的 App Store 和谷歌 Play 商店要求开发者在向潜在用户进行商业分发之前对应用进行代码签名。要了解代码签名的实际工作方式,并强调其安全性优势,请考虑苹果应用商店和谷歌 Play 商店如何在其移动应用版本和更新系统中实现代码签名。

谷歌 Play 商店上发布应用程序之前,您必须签署您的代码。为此,您必须生成一个密钥,由每个用户的设备包管理器和谷歌 Android 包套件(APK)进行检查。这个密钥是 Google 验证应用程序及其更新的真实性和来源的方式。

以前,开发人员负责管理这些密钥的安全性。现在,谷歌提供了 Play 应用签名,它将用于将您的工件上传到 Google Play 的上传密钥与您的应用签名密钥(有助于将 apk 签名到分布式设备)分开。通过 Play 应用程序签名,Google 将应用程序签名密钥存储在其安全基础架构中,从而提高了安全性。

苹果的应用商店采用非对称加密技术(公钥和私钥)来确保发布到其平台的应用程序的安全性,用于其代码签名过程。首先,您(作为应用程序发行者)必须在本地机器上创建一个证书签名请求(CSR)。一旦您制作了 CSR,本地设备就会生成一个公钥-私钥,将公钥附加到 CSR,并下载一个用于向 Apple 申请证书的 certSigningRequest。请记住,证书只能使用为其生成的私钥。

然后,您下载证书并将其推送到钥匙串,钥匙串与他们的私钥配对。证书和私钥共同构成了用于发布 iOS 应用程序的代码签名身份。

代码签名最佳实践

这里有一些代码签名的最佳实践,以确保您的应用程序代码是安全的。

确保私钥的安全

当代码签名私钥暴露、丢失或泄露时,就会出现严重的安全威胁。泄露的密钥赋予黑客和恶意用户很大的权力。暗网上也需要这些代码签名证书密钥。

避免这种情况的一些常见方法包括:

  • 仅授予授权人员访问密钥的权限
  • 通过采用物理安全控制和限制可以访问私钥的计算机数量来限制对私钥的访问
  • 使用加密硬件产品来保护密钥

使用严格的访问角色和权限

实施严格的策略和角色,确保只有获得授权的人才能访问私钥,以限制风险暴露。此外,如果您使用日志来执行事件报告的审计,那将是最好的。

使用 CI/CD 自动化签名流程

自动化代码签名流程涉及一种端到端的集中式方法来执行代码签名工作流,同时实施安全策略。这一自动化流程使用粒度访问控制,并与 CI/CD 管道集成,而不会降低 SDLC 的速度。

通过使用连续签名(CS)通过 CI/CD 管道自动进行代码签名,可以确保端到端的安全性。借助 CS,当代码通过 SDLC 的不同阶段时,集中式证书管理器会自动部署代码签名证书。此过程消除了停止开发周期以生成代码签名证书的需要,从而提高了开发速度。

您可以使用 CI/CD 工具来应用 CS,这些工具在管道的每个阶段执行安全检查,例如 CircleCI runner ,它有助于在 CS 期间保持证书的安全和签名。通过使用 CS,您可以有效地提高开发生命周期的速度,并确保您发布的产品是安全的,同时维护自动化、高效的 CI/CD 管道。

对代码使用时间戳

当您使用代码签名证书对代码进行签名时,它会附加附加数据(即时间戳)来记录签名时间。这个时间戳有助于在使用时向用户保证代码签名证书的有效性,提高整体安全性,并使用户在使用您的应用程序时感到安全。

签名前扫描代码中的病毒和漏洞

虽然自动化有助于提高总体生产力,但这也意味着审查实际代码的工作量减少了。在某些情况下,这为漏洞留下了空间。为了防止这种情况,您可以在提交源代码之前仔细扫描软件代码中的病毒和其他漏洞。这种实践有助于检测潜在的风险并提高代码质量。例如,你可以使用像 Snyk orb 这样的工具来扫描你的代码库的漏洞

结论

您的软件供应链很可能是复杂的、相互关联的、冗长的。因此,它很容易受到攻击和利用。漏洞可能是无辜的,也可能是无意的,就像错误配置或意外输入错误一样。它们可能是严重的,有针对性的,因为攻击者将恶意代码注入到您的软件中,在发布后导致严重的网络攻击。

代码签名是确保软件供应和分发链安全的一个重要组成部分,也是向用户保证已发布应用程序安全的一种方式。此外,您可以将代码签名用于与安全相关的目的,例如防止易受攻击的依赖项的恶意软件攻击。

为了确保您的应用程序是安全的,并使您的代码签名工作彻底而有效,请遵循最佳实践,例如维护安全密钥、扫描漏洞以及在签名时使用时间戳。当您准备好加速和扩展您的软件构建、测试和代码签名实践时,将持续集成工具整合到您的开发工作流中。你可以从今天注册一个免费的 CircleCI 账户开始。

什么是容器编排?圆环

原文:https://circleci.com/blog/what-is-container-orchestration/

容器化是一种虚拟化,在这种虚拟化中,软件应用程序或服务与在任何计算环境中运行所需的所有组件打包在一起。通过使应用程序更加可移植、高效和可伸缩,容器与现代云本地开发实践紧密结合。随着应用程序架构变得越来越复杂,维护分布式系统稳定性所需的容器数量也在增加,软件团队可以通过容器编排来简化其容器基础设施的管理。

容器编排工具管理和自动化容器的整个生命周期,包括供应、部署和扩展。这使得组织能够大规模获得集装箱化的好处,而不会产生额外的维护开销。然而,在采用新技术之前,团队应该考虑一些与采用容器编排工具相关的挑战。

本文涵盖了容器编排的优点和挑战,以及一些要考虑的流行容器编排工具。

容器编排的好处

容器编排的好处包括:

  • 高级安全性
  • 成本效率
  • 简化部署
  • 改进的应用程序开发
  • 高效的资源管理

高级安全性

在容器中运行应用程序及其进程可以降低传统的安全风险,因为容器中的应用程序可以独立运行。此外,容器编排工具只能在用户之间共享特定的资源,从而降低了数据泄露和其他安全漏洞的风险。

成本效率

容器是轻量级的,比虚拟机需要更少的资源来运行。无论环境如何,在一台主机上容纳多个容器都变得更加容易,当通过容器编排工具进行大规模部署时,可以显著节约成本。与手动部署相比,容器编排系统需要更少的时间和人力来管理,从而节省了额外的成本。

简化部署

有了容器编排工具,构建和发布软件的速度加快了。应用程序部署更易于管理,因为与应用程序相关的一切都存在于容器中。一些容器编排工具提供了部署控制器,进一步简化了部署过程,包括部署和回滚、pod 管理等等。

改进的应用程序开发

容器使得开发、测试和交付软件的整个过程更快、更有效和可重复。编排允许团队轻松推出和回滚新版本或功能,使生产测试和更复杂的部署策略更容易完成。

高效的资源管理

容器使得管理资源更加容易,因为它们不包括操作系统(OS)映像。这使得容器比传统应用程序更加高效和轻量级。

容器编排工具

有许多容器编排工具可用。本节回顾了一些最流行的。

库伯内特斯

在所有开源容器编排工具中,Kubernetes——也称为 K8s——可能是最著名和最受欢迎的。Kubernetes 专注于管理容器的整个生命周期,并提供一系列托管服务来帮助团队获得所有好处,而不会带来复杂性。开发人员喜欢它的灵活性、独立于供应商的特性、稳定的版本发布以及围绕它构建的开源社区。

Kubernetes 帮助管理复杂的应用程序,这些应用程序由几个必须大规模托管的独立服务组成。由于 Kubernetes API,这变得很容易,它允许与供应和管理相关的几个任务的自动化。

这一系列服务简化了容器自动化和管理流程,并简化了交付云服务的流程。

一些托管的 Kubernetes 服务包括:

  • 红帽 OpenShift
  • 谷歌 Kubernetes 引擎(GKE)
  • 蓝色库柏服务(AK)
  • 亚马逊弹性库柏服务(EKS)

OpenShift 是 Red Hat 基于 Docker 和 Kubernetes 开发的云开发平台,由开源的 Kubernetes 引擎提供支持。这个平台即服务(PaaS)使用 Kubernetes 作为容器编排引擎,使开发人员能够在任何地方运行和构建应用程序。它有助于开发、部署和管理基于云的应用程序,允许开发人员以自助方式工作。

Google Kubernetes 引擎(GKE)是一个基于 Kubernetes 的管理系统,用于管理 Google 公共云服务中的 Docker 容器和容器集群。

Azure Kubernetes Service (AKS)是 Azure 的完全托管的容器服务,简化了在 Azure 中部署 Kubernetes 集群。Azure 完全管理集群,使得无需编排技能或专业知识就可以轻松部署容器化的应用程序。

亚马逊弹性 Kubernetes 服务 (EKS)是一项托管服务,简化了在 AWS 中运行和部署 Kubernetes。这是一个容器即服务(CaaS),它为 AWS 用户提供了 Kubernetes 的功能,而无需管理 Kubernetes。

码头工人群

Docker 提供的 Docker Swarm 是一个开源的容器编排工具,也是 Docker 的原生集群引擎。它通过将 Docker 实例和主机池转换为单个虚拟主机,实现了对部署在众多机器上的多个容器的有效管理。

Docker Swarm 提供了分散的访问,使得分散的团队可以更容易地有效地工作和管理环境。要了解更多关于 Docker Swarm 的信息,以及它与其他流行的容器管理工具的比较,请阅读 Docker Swarm vs Kubernetes:如何选择容器编排工具

梅索斯

Apache Mesos 是另一个开源集群管理服务。它在应用程序层和操作系统之间工作,使得在大型集群环境中部署和管理应用程序更加简单高效。

Mesos 是第一个开源集群管理服务,它使用动态资源共享和隔离(将程序与其他正在运行的进程隔离)来处理分布式环境中的工作负载。Mesos 为应用程序提供集群中所有机器上的可用资源,并频繁更新以包含已完成的应用程序所释放的资源。这使得应用程序可以做出最佳决策,决定在哪个机器上执行哪个任务。

流浪者

由 HashiCorp 于 2015 年首次发布,Nomad 最初被设计为一个通用的管弦乐队。现在,Nomad 是一个容器编排工具,它提供了灵活的集群管理以及在集群中的工作节点之间调度和部署任务。Kubernetes 集群中的工作节点执行 Kubernetes API 提供的操作。它们可以用来运行容器化的应用程序,也可以处理集群内外的应用程序之间的网络。

Nomad 在现场或云环境中的各种基础设施上部署和管理容器和非容器化应用程序。除了容器之外,Nomad 还处理任何应用程序的编排,并提供装箱功能,以实现有效的空间管理和作业调度。

苏斯牧场主

SUSE Rancher 是一个为方便管理、组织和管理任何基础设施上的数千个 Kubernetes 集群而构建的服务。

Rancher 允许开发人员使用 Rancher Kubernetes 引擎(RKE)或其他云 Kubernetes 服务(如 GKE 和 EKS)创建 Kubernetes 集群。使用 SUSE Rancher,开发人员可以从任何 Kubernetes 发行版导入和管理现有的 Kubernetes 集群。

容器编排挑战

对于以前没有什么容器经验的组织来说,设置容器编排可能会令人望而生畏。自学这些工具及其管理的内容将有助于您应对挑战,例如:

  • 选择正确的工具
  • 安全性
  • 建立工作关系网
  • 文化变迁

选择正确的工具

有了这么多可用的容器编排工具,可能很难理解每个工具提供了什么功能,为什么应该使用它们,以及它们是否适合您的组织的需求。

选择工具时需要注意的一些基本特性包括:

  • 安全性
  • 缩放比例
  • 所需技能
  • 效率
  • 组织特定的需求

在选择工具为您的决策过程提供信息时,请记住这些。

安全性

容器管理和编排可能比其他基础设施更复杂。整个容器编排过程必须是安全的,因为攻击者可以利用任何错误配置、错误或其他漏洞。停机时间、金钱损失和敏感数据被利用只是容器编排中安全问题的一些副产品。

让具有安全意识的开发人员管理和维护整个过程以降低安全风险是至关重要的。通过在持续集成管道中实施自动化漏洞扫描等安全最佳实践,您可以显著降低风险。

建立工作关系网

使用微服务开发的组织需要每个服务与其他服务进行通信,以实现轻松的工作流。如果组织预期容器之间的高流量,这可能会导致一些问题。然而,一个好的负载均衡器可以轻松地解决流量问题。

文化变迁

容器编排是一个需要开放性和责任性的领域。如果这不是一个组织文化中已经存在的一部分,那么实现容器编排可能会很困难,不管规模有多大。为了有效地实现容器编排、通信和协作,您必须鼓励所有级别的最佳 DevOps 实践。

容器编排和 CI/CD

您可以将容器编排集成到持续集成和持续开发(CI/CD)管道中。将 CI/CD 与容器编排结合使用将自动完成基本任务。本节讨论其中的一些。

自动化容器构建

CI/CD 管道可以自动化构建容器的整个过程,允许开发人员专注于提供新功能和响应客户需求。您还可以自动将容器部署到 Kubernetes 集群或其他容器编排工具,这样您的用户可以快速接收更新,减少等待时间。

安全扫描

在应用程序的开发过程以及编排过程中,可能会出现错误和安全问题。为了减少您在容器化应用程序中暴露于供应链漏洞和其他潜在攻击媒介的风险,实现全面的自动化测试过程非常重要。

基础架构配置和部署

配置和部署是 DevOps 管道的重要方面。您可以使用 CI/CD 自动化您的容器集群配置过程,减少代价高昂的错误配置和其他错误的机会。

结论

本文讨论了容器编排的好处,包括高级安全性、成本效率、简化的部署、改进的应用程序开发和高效的资源管理。它调查了几种工具,讨论了容器编排带来的一些挑战以及如何应对这些挑战,并解释了 CI/CD 管道如何通过自动化来简化容器编排。

容器化和容器编排工具将继续存在。像 Kubernetes、Docker Swarm 和 Nomad 这样的工具使得复杂的容器化应用程序的开发到部署过程变得更加简单,使组织免于昂贵的手动基础设施管理。但是,在选择编排工具之前,考虑您的组织需求和能力是很重要的。尽管容器编排有其挑战,但好处远远大于问题,这些问题可以通过适当的规划得到缓解。

当您的组织准备好通过容器编排扩展容器的使用时,您可以通过全面的 CI/CD 管道进一步自动化和保护构建、测试和部署过程。CircleCI 与 Kubernetes 和其他容器编排工具集成,使您更快、更轻松地过渡到基于容器的部署。首先,今天就注册一个免费账户

到底什么是持续部署?-切尔莱西

原文:https://circleci.com/blog/what-is-continuous-deployment-anyway/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


CD

根据 CircleCI 开发人员布道者 Kevin Bell 的说法,“有很多方法可以理解持续部署,但是我现在最喜欢的是将持续部署理解为敏捷开发和交付实践在持续集成之外的自然延伸。”

持续集成只是持续部署道路上的一步。这是 CircleCI 的凯文·贝尔和雨林 QA 首席执行官兼联合创始人弗雷德·斯蒂文斯-史密斯实现持续部署最近网络研讨会的主题

该网络研讨会探讨了各种规模的公司如何实现持续部署的基础,包括:

对于感兴趣的人,你可以点播网上研讨会,幻灯片可在这里获得。

如果您想亲自听听 Kevin Bell,现在听听他给出的关于使用 Bash 的提示和技巧还为时不晚!立即注册 CircleCI 办公时间:2 月 17 日星期三下午 6 点:点击此处回复。

什么是持续集成?-切尔莱西

原文:https://circleci.com/blog/what-is-continuous-integration/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


你好!我叫 Lev,是 CircleCI 的技术服务主管。我领导着一个由令人难以置信的支持和成功工程师组成的团队,他们每天都在帮助我们的用户从 CircleCI 平台中获取最大价值。在这个系列中,我将分享来自支持前线的故事,这些故事将鼓舞、通知和激励你。

几周前,我在我们的社区网站上看到一个帖子,这个帖子激励了我。不知何故,一名开发人员偶然发现了 CircleCI,并决定在不知道 CircleCI 用途的情况下尝试一下。作为一个为 CI 公司工作的人,并且显然想传播关于持续集成和持续部署的承诺的好消息,我喜欢与其他可能不熟悉这些概念的开发人员交谈,并且当他们意识到他们能够用 CircleCI 解决的问题范围时,看着他们睁大眼睛。这个特别的用户激励了我,因为在公共论坛上露面并承认你不知道一些事情需要很大的勇气。我希望更多的人,包括年轻时的我,经常这样做,因为这是学习和成长的最好方式之一。

很多年前,当我第一次开始使用 Linux 时,我不知道自己在做什么。藏在 2004 年各种论坛的档案中,你可能会发现一个 14 岁的怪胎提出的愚蠢问题。我坚持下去,因为有人没有翻白眼不理我,而是花时间向我解释 cd 的意思是改变目录。我很感激有机会通过简要概述什么是持续集成和持续部署以及 CircleCI 如何融入其中来回报这些人。考虑到这一点,下面解释一下 CI 和 CD 是什么,以及 CircleCI 如何适应这一过程。

连续累计

基本上,持续集成意味着多个开发人员将小的、频繁的变化推到一个共享的存储库或“主”上。他们不断地整合变化,而不是周期性地,因此–哒哒!–持续集成。有很多关于 CI 最佳实践的东西你可以遵循,但是我想说最重要的是测试你对你的代码库做出的所有改变。您可以通过以下方式实现这一点:

总的来说,软件测试是一个包含大量概念的巨大话题。您编写测试的方式根据您使用的语言和框架而有所不同。

持续部署

如果您的测试通过了,那么您就可以将您的代码部署到开发、试运行、产品化等等。,自动。具体的实现方式取决于您要部署的基础设施的类型。我认为 Heroku 是最容易理解的,CircleCI 的默认集成很容易设置。

如果您不喜欢持续部署的想法,您可以选择使用手动门或触发器来进行部署(也称为“持续交付”)。但是你需要先走后跑,所以我会努力确保你在进入 CD 之前做 CI。

CircleCI 如何帮助您?

CircleCI 集成了一个 VCS,并在每次检测到存储库发生变化时自动运行一系列步骤。

CircleCI 构件由一系列步骤组成。一般来说,它们是:

  • 属国
  • 测试
  • 部署

如果您使用标准项目的最佳实践,我们有一些可以自动检测这些的推断。您也可以手动配置每个阶段。

感谢每一个曾经在论坛帮助过一个完全迷茫的新手的人,感谢每一个完全迷茫的新手不怕提问的人!我希望这个简短的概述能够帮助那些不熟悉这些概念的其他开发人员。这绝对是我第一次创业时希望拥有的东西。

什么是端到端测试?圆环

原文:https://circleci.com/blog/what-is-end-to-end-testing/

端到端测试,也称为 E2E 测试,是一种用于确保应用程序按预期运行,并为各种用户任务和流程维护数据流的方法。这种类型的测试方法从最终用户的角度出发,模拟真实世界的场景。例如,在注册表单上,您可以期望用户执行以下一个或多个操作:

  • 输入空白的电子邮件和密码
  • 输入有效的电子邮件和密码
  • 输入无效的电子邮件和密码
  • 单击注册按钮

您可以使用端到端测试来验证所有这些操作是否如用户所期望的那样工作。

端到端测试听起来可能很全面,但是有许多其他的测试方法你应该使用它们来创建一个健壮的持续集成实践。您应该考虑的一些其他测试类型:

  • 单元测试确保系统中的每一个组件都按预期工作。
  • 功能测试确保系统对特定的输入给出正确的输出。
  • 集成测试将单个的软件模块组合起来,作为一个组一起测试。

这些类型的测试不同于端到端测试,后者侧重于完整的用户工作流。

为什么端到端测试很重要?

端到端测试已被广泛采用,因为它:

  • 通过添加比其他测试方法(如单元测试和功能测试)更详细的测试用例,帮助团队扩展他们的测试覆盖范围。
  • 通过基于终端用户的行为运行测试案例,确保应用程序正确运行。
  • 通过允许发布团队自动化关键用户路径,帮助他们缩短上市时间。
  • 通过减少测试软件所需的时间来降低构建和维护软件的总成本。
  • 帮助预测和可靠地检测错误。

端到端测试吸引了包括开发人员、测试人员、管理人员和用户在内的多学科团队。开发人员从中受益,因为他们可以将大部分测试和质量保证工作交给 QA 团队,从而将开发人员从为应用程序添加功能的工作中解放出来。测试人员更容易编写端到端的测试,因为它们基于用户的行为,这可以在可用性测试过程中观察到,并记录在标签中。端到端测试使得在将软件发布给最终用户之前发现问题变得更加简单。通过识别工作流对现实世界用户的重要性,端到端测试帮助经理们在开发积压中区分任务的优先级。

端到端测试也改善了用户体验。特别是对于需要大量用户交互的应用程序,比如 web、桌面和移动应用程序,用户期望是测试用例的基础。

端到端测试的挑战

端到端测试是测试软件的一个很好的方法,但是它也带来了一些挑战。挑战不断涌现,因为端到端测试:

  • 很费时间。
  • 必须被设计成重现真实世界的场景。
  • 需要很好地理解用户目标。

耗费时间的

端到端测试可能非常耗时,因为编写测试用例需要对产品有完整的理解。在一个大型软件产品中,用户可以遵循许多路径,并且很少值得去测试每一条路径。相反,公司通常更频繁地使用单元测试、快照测试和集成测试,并对最高优先级的用户工作流使用端到端测试。

难以设计测试

因为端到端测试模拟了用户的真实行为,所以在设计这些测试时有许多组件需要考虑。

例如,您可以在许多浏览器上运行一个 web 应用程序,每个浏览器都有不同的规范。这意味着您必须编写特定于这些浏览器的测试。这项艰巨的任务可能会导致预算超支。在测试驱动的开发中,您正在寻找对代码的快速反馈,依赖端到端测试不是一个好方法。

了解用户目标

用户不是在寻找功能:他们在寻找解决他们特定问题的方法。端到端测试应该关注应用程序如何有效地解决用户的问题。

问题是,并不是所有的开发团队都对用户意图有详细的了解。因此,他们必须在软件开发的早期部署方法,以收集用户对软件功能的观点。用户研究可能非常昂贵,因此团队通常依赖同一组用户作为应用程序的“beta 测试人员”。

如何实现端到端测试

既然您知道您想要向您的开发过程添加一些端到端的测试,那么您从哪里开始呢?设计端到端测试用例是很好的第一步。然后,您可以从手动测试开始,直到开始自动化您的端到端测试有意义为止。

设计端到端测试用例

因为端到端测试需要一些准备工作,所以最好熟悉实现这些测试的步骤。以下是典型的端到端测试过程的步骤:

  • 审查需求以验证端到端测试结果。
  • 设置测试环境和需求。
  • 定义系统和子系统的所有过程。
  • 描述每个系统和子系统的角色和职责。
  • 概述测试工具和框架。
  • 列出设计测试用例的需求。
  • 列出每个系统的输入和输出数据。

一旦您完成了这些步骤,您就可以实现端到端测试了。

手动端到端测试

人工测试是由测试人员直接与测试软件交互来执行的。在编写测试计划时,这些测试人员可以很快了解到什么可行,什么不可行。手动测试有助于识别测试用例,并帮助测试人员发现系统中隐藏的用户交互路径。这给了测试人员他们需要的信息,以便在将来开始自动化这些测试用例。手动构建的测试计划和测试用例可以成为自动化测试。

手工测试有两种方式:水平和垂直。

水平端到端测试覆盖了整个应用程序。它要求软件开发团队拥有定义良好的工作流和已建立的测试环境。单个测试工作流可以跨越多个子系统。同时测试 UI、数据库和电子邮件集成的计划是水平端到端测试的一个例子。

垂直端到端测试将应用程序分解成可以单独测试的层。由于这种粒度,垂直端到端测试通常先于水平测试。例如,对用户界面子系统执行垂直的端到端测试,可以让您轻松地识别和修复 bug。

自动化端到端测试

随着项目的增长,手动执行所有的端到端测试将变得更难管理。对于测试用户界面来说尤其如此,因为用户界面中的一个动作可以导致许多其他动作。这种复杂性使得测试自动化至关重要。端到端测试可以帮助自动化用户交互测试,节省宝贵的时间。

一旦你决定了测试用例,你就可以将它们写成代码,并与自动化测试工具集成。例如,您可以使用一个 CI/CD 管道来自动化软件的端到端测试。

随着软件获取新特性的速度,自动化软件测试是唯一可行的选择。自动化使您能够更快地捕获 bug,因为每次您添加新代码时,整个代码库都会自动对照测试用例进行检查。

结论

本文回顾了什么是端到端测试,并描述了它的好处和挑战。涵盖了端到端测试的实现技术,包括水平测试、垂直测试以及手动和自动测试之间的区别。

手动测试是一个很好的起点,也是构建自动化测试的良好基础。自动化不仅节省了时间并防止了复杂性,它还解放了您的团队去做他们最擅长的事情——开发应用程序。

为了有效地实现端到端测试,可以使用 CircleCI 这样的 CI/CD 平台。首先,今天就注册一个免费的 CircleCI 账户

什么是平台工程?快速介绍

原文:https://circleci.com/blog/what-is-platform-engineering/

平台工程是一门新兴学科,致力于通过降低现代软件交付的复杂性和不确定性来提高开发人员的生产力。它解决了大规模开发运维面临的一些最大挑战,包括使开发实践与业务优先级保持一致,以及减轻在整个应用生命周期中管理复杂的工具和基础架构网络的负担。

https://www.youtube.com/embed/E10MHY0AbJA

视频

平台团队实现了基础设施管理的自动化,并使开发人员能够从集中管理的技术平台中自助使用可靠的工具和工作流。通过减少开发团队的认知负荷,平台工程成为云原生软件交付的一个重要发展。

平台工程的价值

平台工程通过降低操作复杂性和消除开发过程中的摩擦而使软件组织受益。它为在云中构建大规模分布式应用的团队提供了一个稳定的替代方案,避免了你构建它,你运行它的混乱和低效。

平台工程团队的工作体现在四个关键领域:

  1. 构建内部开发者平台
  2. 标准化和保护关键交付流程
  3. 设置和维护内部服务水平协议
  4. 监控团队绩效指标

让我们仔细看看平台工程的这些特性以及它们给开发过程带来的价值。

内部开发者平台(IDP)

平台工程团队最重要的角色之一是构建和维护内部开发人员平台(IDP ),它是工具、服务和自动化工作流的集中集合,支持整个组织软件产品的快速开发和交付。IDPs 提供了一个服务层,它抽象了应用程序配置和基础架构管理的复杂性。

重要的是,IDPs 允许开发人员自助获取所需的资源,以最小的摩擦快速构建、测试、部署和监控应用。开发人员无需等待运营团队调配基础设施或解决问题,而是可以轻松启动云环境,触发 CI/CD 管道进行自动化测试和部署,实施回滚,访问日志和构建工件,以及从单个 API 或 GUI 执行更多操作。

为标准化流程和减少认知负荷铺平道路

平台工程为组织提供了一个统一的系统来管理、标准化和扩展典型的开发运维流程和工作流。通过审查和管理资源目录,平台团队创建了简化和加速开发的“铺平道路”,同时也给予开发人员在必要时使用他们自己的首选工具的自主权。

在拥有数百甚至数千开发人员的大型组织中,过多的自主权会导致工具无序蔓延、知识孤岛、成本失控和精疲力竭。铺设的道路提供了一条成熟可靠的交付途径,减少了开发团队的操作复杂性负担,同时允许组织实施更强的安全性、合规性和预算控制。

设置内部服务水平协议(SLA)

平台团队将 IDP 视为他们的产品,将开发者视为他们的客户。为了提高 IDP 的采用率,他们对 IDP 的可靠性和性能设定了很高的期望,并通过服务水平协议对自己负责。

维护一个稳定安全的内部平台是平台工程师工作的重要部分。内部系统的失败不仅使得开发人员不太可能遵循已铺好的生产道路,而且还会导致价值流向外部客户的中断。平台团队积极监控 IDP 的运行状况和性能,以确保开发人员保持高效,组织的客户保持满意。

监控关键绩效指标

平台团队跟踪重要的工程性能指标,如吞吐量、工作流持续时间和事件恢复时间,以消除瓶颈并根据开发人员的需求定制平台。例如,如果恢复时间开始超过基线,平台团队可能会增加管道中自动化测试的数量,或者改进 IDP 中提供的监控和警报功能。

通过跟踪开发人员生产力和性能的数据,平台团队可以确保内部平台适当地支持组织的开发模式和业务目标。

我需要一个平台团队吗?

假设您有许多团队在从事需要复杂工程的大型分布式项目,或者您有监督应用程序开发、操作和基础设施的跨职能团队。在这种情况下,你应该考虑一个平台团队——你可能已经在做某种形式的特别平台工程了。

另一个表明你的组织已经为平台团队做好准备的标志是,你有一个成熟的产品,对它的发展方向有一个清晰的愿景,并且你已经准备好开始扩大运营。

如果您的工程团队正在从事云集成和基础设施运营,这可能表明您需要一个平台团队。一个为工程团队定义良好的工作流程,清楚地了解每个团队成员的角色和职责,是一个重要的先决条件。它不一定与你的工程团队的规模有关,而是与你的产品的成熟度有关——随着你规模的扩大,你必然需要平台团队。

另一方面,如果你有一个小组织,由少数开发人员构建一个单一的应用程序,你不太可能从一个平台团队中获得太多的好处。首先专注于实现产品与市场的契合,自动化任何重复的任务让开发者专注于创新。一旦您准备好开始将您的应用拆分为独立的服务,多个工程团队提供不同的价值流,平台团队可以帮助您实现速度和稳定性的最佳平衡。

CI/CD 平台工程

健壮的持续集成和交付管道是任何平台团队工具链的关键部分。它提供了一个自动化引擎,可以触发模板化的工作流,在开发、QA、试运行和生产环境中构建、测试和部署代码。在更高的层次上,一个成熟的 CI/CD 管道可以作为一个控制平面,用于编码、执行和度量开发中的业务优先级。

在 CircleCI,我们提供了许多功能来帮助平台团队自动化和简化关键的开发任务。CircleCI orbs 是可移植的、可重用的配置代码片段,您可以使用它们轻松地将流行的工具和服务集成到您的工作流中,或者跨项目共享通用流程。借助配置策略管理,平台团队可以设定全球规则,以确保组织中的每个管道都遵循相同的惯例和安全策略。

Insights 仪表板为平台工程师提供了性能的图形视图,因此他们可以快速确定需要改进的领域,并优化管道以实现最高效率。它提供了关于长期运行和经常失败的工作流、资源使用和信用消耗的时序数据,以便平台团队可以确保他们的管道能够提供开发人员要求的速度和当今企业要求的成本效益。

CI/CD 节省了开发人员的时间,并改进了开发过程。它构成了平台工程工具包的核心,支持自助服务、可重用性和平台工程方法的其他关键原则。

结论

平台工程是一个强大的工具,用于在不牺牲质量、安全性或效率的情况下扩大软件交付过程。通过简化和自动化资源供应和管理,它使开发人员能够在更短的时间内向客户交付更多的价值。

组织可以使用平台团队来释放和扩大 DevOps 的优势,通过组织的可靠性、可见性和信心来增强开发人员的敏捷性。如果你准备好通过平台工程来提高你的组织的性能,从注册一个免费 CircleCI 账户开始,看看同类最佳的持续集成如何帮助你实现更快的迭代周期和更好的交付结果。

什么是 YAML?初学者指南| CircleCI

原文:https://circleci.com/blog/what-is-yaml-a-beginner-s-guide/

什么是 YAML?

YAML 是一种易于理解的数据序列化语言,常用于用任何编程语言创建配置文件。

YAML 是为人类交互而设计的,是另一种数据序列化语言 JSON 的严格超集。但是因为它是一个严格的超集,它可以做 JSON 能做的一切,甚至更多。一个主要的区别是,换行符和缩进在 YAML 中实际上是有意义的,与 JSON 相反,JSON 使用括号和大括号。

这种格式有助于指定配置,这就是我们在 CircleCI 使用它的方式。

What is YAML diagram

如何写 YAML

YAML 文件的基本结构是地图。你可以称之为字典、散列或对象,这取决于你的编程语言或情绪。

非常普遍的是,键和值一直向下:

key: value 

YAML 示例:标量类型

您可以使用各种标量类型作为值:数字、布尔值和字符串(带引号或不带引号)。例如,config.yml的第一行通常是:

version: 2 

键中的单词可以用下划线、破折号或空格分隔。在 CircleCI,我们使用下划线

如果键值是多行字符串,您可以使用带有“|”字符的“文字块”样式。这在定义 shell 命令时特别有用:

command: |
    if [ "${CIRCLE_BRANCH}" == "master" ];
      then ansible-playbook site.yml -i production;
    fi 

请注意,多行字符串的前导缩进将被去除。

YAML 示例:集合类型

创建收藏所要做的就是使用缩进:

environment:
    TEST_REPORTS: /tmp/test-reports 

如果你有一个事物列表(比如图像),你可以用破折号来表示这个序列:

docker:
    - image: ubuntu:14.04
    - image: mongo:2.6.8
      command: [mongod, --smallfiles]
    - image: postgres:9.4.1 

注意,序列中的第二项有两个键:imagecommandcommand键使用 JSON 风格的序列,因为(记住!)YAML 是 JSON 的超集。

最后,YAML 不允许制表符,所以如果你使用这些来缩进,让你的文本编辑器把它们转换成空格。你的 YAML 中的语法错误有时会导致 CircleCI 构建挂起,但是通过让你的circle.yml/config.yml运行一个在线验证器也很容易避免。

进一步阅读

本指南中的内容应该是您轻松编写 CircleCI 配置文件所需的全部内容。YAML 确实支持额外的功能,你可以在官方网站上读到这些功能,或者在不那么吓人(但不那么详尽)的 T2 网站上读到这些功能。

阅读更多信息:

远程优先与远程友好循环意味着什么

原文:https://circleci.com/blog/what-it-means-to-be-remote-first-vs-remote-friendly/

嗨!我是 Rose Jen,CircleCI 的产品经理。我在 CircleCI 进行了一些采访,我被问到的第一个问题是“你对 CircleCI 的分布式文化有什么体验?”

https://www.youtube.com/embed/XdPe-0am5E4

视频

我以前在允许远程工作的公司工作过。他们为自己的团队提供工具,使远程工作成为可能。我也曾在一些团队中工作过,这些团队开始时位于同一地点,但随着团队成员的移动,他们不得不分散到不同的地方。通常在这些情况下,远程工作的人变得越来越不受重视,与团队的其他成员越来越疏远。

鉴于这些经历,我最初对在分布式团队中工作犹豫不决,特别是担心沟通会很困难,或者更糟,我可能会感觉与团队脱节。然而,当我在 CircleCI 工作的时候,我注意到公司在建立一个包容性的分布式文化上的深思熟虑的工作减轻了我的许多恐惧。通过这次经历,我看到了这些努力带来的变化,并了解到仅仅允许员工远程工作是不够的。

虽然 CircleCI 从一开始就是一个分散的团队,但我们在旧金山有一个正式的总部。为了支持我们遍布全球的队友感受到完全的联系、包容和授权,我们每天都在努力构建“远程优先”的文化。允许远程工作的团队和努力做到远程优先的团队之间的区别可能听起来像是一个技术问题,但实际上对我们的团队成员来说是一个不同的世界,不管他们在哪里。远程优先意味着远程员工不是事后才想到的。“远程优先”意味着不仅要对所使用的工具有意识,还要对我们如何规划公司文化以尽可能包容远程员工,同时让每个人都发挥最大的生产力有意识。

对于任何考虑加入我们的人,或者那些希望在自己的组织中培养远程优先文化的人,这里有 5 种方法可以让它发挥作用:

默认视频会议

在 CircleCI,我们非常依赖视频会议——在这里,没有某种视频会议是不可能度过一个工作日的(我们使用 Zoom )。为了最大限度地包容,我们对如何使用视频会议工具非常具体。首先,作为一家公司,我们在硬件和软件上进行了大量投资,这使得我们对视频会议的依赖得以实现。我们办公室的每个会议室都完全支持视频。另一方面,我们希望我们的员工能够使用高速互联网连接,并在能够清晰听到和看到他们的地方工作——听不到别人打电话也不会影响工作。这种战术的成功依赖于每个人的参与。我们还在早期决定授予每位员工 Zoom 帐户的完全访问权限,这样每位团队成员都会觉得自己有权召集会议,无论是大型头脑风暴还是临时聊天。

此外,我们有关于如何通过视频会议传播信息的实践。我们在每个活动邀请中都包含一个缩放链接,这样无论参与者在哪里,他们都可以加入。在 CircleCI,我们有一条通用规则,如果不同时区的团队成员无法参加会议,我们会对会议进行录制,供以后查看。这让所有时区的人们都可以灵活地以最适合自己的方式管理自己的一天。

可访问的、结构化的和记录的团队会议

在 CircleCI,大多数产品开发团队每周都有各种聚会:计划会议、每日站立会议、头脑风暴、设计回顾和回顾。这些都是通过视频会议完成的。根据会议的类型,我们还利用每个人都可以实时访问的数字工具( Google Docs ),通过这些工具我们可以促进讨论并记录决策。例如,我们使用 Google Docs 来概述头脑风暴或回顾性讨论主题。对于计划会议和站立会议,我们去我们的共享吉拉板,那里显示我们的交付成果和进展,这样当讨论发生时,每个人都实时跟进。对于设计评审,我们使用视觉,一个设计原型工具,允许设计在团队中共享,并允许团队成员提供评论。这些帮助我们在团队所在的任何地方召开富有成效的会议。我个人并不怀念将整个团队塞进同一个会议室,我当然也很高兴摆脱了寻找可用会议室的挣扎。

文档文档文档

谁不喜欢好的文档呢?在远程优先的环境中,要特别注意可能对不在同一地点的团队成员不利的物理工件。当几个人在同一个房间时,总会有实时发生的事情(快速草图,便利贴),但我们努力记录并与整个团队分享这些 IRL 头脑风暴的结果,并拍摄大量图表,路线图和其他结果的照片,以便每个人都可以在事后访问它们。想办法确保远程团队成员不只是在听,并寻找他们可以实时参与的方式。Zoom 提供协作白板,您可以在电话会议中使用。即使有最好的工具,这个过程通常也是不完美的,因此需要每个人都有意识和创造力。

没有走廊谈话

这并不意味着你不能和你的同事面对面或在饮水机旁交谈。但这确实意味着,如果你与工作相关的对话没有被数字化记录,它们应该在事后以某种方式被记录下来:在聊天中做一个总结,写一个简短的文档,或者找到另一种方法来确保信息可以被远程团队成员引用。我们还鼓励我们的团队成员通过视频进行即兴的“走廊”对话,无论是问问题还是迅速让团队就特定主题达成一致。不管在什么地方,有些事情只有一对一的快速聊天才能弄清楚。

一起计划时间

只要有可能,就把分散的团队召集到一起!在现实生活中,能够与远方的同事共度时光真的很棒。在 CircleCI,我们计划团队每年至少在一个地点聚会一次。各部门也是如此。这将需要认真的后勤规划和资金预算,但作为团队建设的投资,这是非常值得的。

RemoteFirst-Infographic-v4.jpg

我所分享的并不是运营分布式公司的权威指南——只是我在 CircleCI 注意到的一些事情,这些事情让我和我的同事在分布式团队中工作时有所不同。值得一提的是:我们总是在重新评估我们的流程,并寻找改进的地方。事情会改变的。例如,现在我们正在向全球扩展,这改变了分布的真正含义(例如,召集一个成员相距 1 小时而不是 8 小时的团队,可能意味着旧金山的人在一天中更早安排会议,以适应北美/南美和欧洲团队成员的时间表)。这些变化促使我们改变使用工具的方式,并给了我们继续迭代和改进的机会。总的来说,如果你所在的组织希望变得更加远程友好,我们鼓励你保持开放的心态,尝试找到最适合你的方式。

远程工作| CircleCI

原文:https://circleci.com/blog/what-to-expect-as-a-remote-circleci-employee/

我们在面试中听到的一个常见问题是“在 CircleCI 远程工作是什么感觉?”嗯,这是很多事情,包括美好的,支持,有时,挑战。今年,随着我们的工程组织在欧洲的发展,我们希望更多的人会对在我们的分布式团队中工作感到好奇。我们联系了我们的一些工程师,了解他们的经历,这样你就可以直接从一些有朝一日可能与你共事的人那里听到。

团队的观点

你喜欢远程工作的什么?

“灵活性。灵活的工作时间意味着我可以花时间和家人在一起,否则我会花时间在通勤上,我的孩子可以在日托中心呆更长时间。”-Marc O'Morain,高级参谋工程师

“我很感激团队能够在我们有些人睡觉的时候继续工作。我们团队的时区分布意味着我可以在晚上和清晨随时待命。”-丹·卡利,SRE

“我很感激我的团队(以及我们公司作为一个整体)努力思考异步通信的意义。人们意识到思考我们互动的方式有多重要,人们开始做这项工作。-Liene Verzemnieks,二级支持工程师

“我很高兴我们都能拥有灵活的生活!我们的团队对我们的工作和使命充满热情,我喜欢看到每个人在工作之外都在做些什么(旅行、健身、可爱的宝宝等等)。).我觉得受到了大家极大的支持和关心。”-雅克·加西亚,软件工程师

“能够制定自己的时间表。“早上/晚上工作,中午打个盹,享受健身午餐,这已经改变了游戏规则。”-工程经理贾斯汀·郭伯伟

作为一个分布式团队,是什么让你的团队成功?

“我们都非常理解彼此的目标——我们定期交流一天中我们在做什么和完成了什么,并一起完成了困难的任务。我们的团队对我们的工作和使命充满热情。”雅克

“分布式沟通意味着把事情写下来,这对每个人都有帮助:病假或假期回来的人,以及加入公司的新人。我们使用我们的团队 Slack 渠道,以便每个人都可以为回答我们知道的事情或学习我们不知道的事情做出贡献。”-丹

“随着时间的推移,我的团队对实验持开放态度(例如,每周两次团队会议,这样每个人都可以至少与队友进行一次面对面的实时对话,即使不是所有人都同时进行)。我们高度重视善待他人,尽管我们并不总是这样谈论,我们也高度重视心理安全(例如,问一些“愚蠢”的问题,或者不知道答案,或者有一些感受)。各种形式的定期对话,加上小手的关键粘合(定期的面对面团队聚会),真的会产生神奇的效果。”-琳恩

你会给新的远程员工什么建议?

“开诚布公。人们真的很好,也很支持我。”-丹

“确保你计划好休息时间,并有意创造与同事的社交互动(因为这可能不会有组织地发生)。”贾斯汀

“刚开始工作时,尽可能多去办公室,时间越长越好。尽可能多地亲自去见一些人。”马克

“过度沟通:宁可问问题也不要伸出手(你不能无意中进入饮水机旁的谈话,所以你能做的任何创造沟通机会的事情都是非常有价值的)。此外,定义工作/不工作,无论是基于时间的(例如,我 8:00 - 17:00 工作,然后我就停下来)还是基于计算机的(例如,这是我的工作计算机,这是我的个人计算机)。”-琳恩

“应对不确定性可能很有挑战性。有疑问时,让人们参与进来。”雅克

分布式团队的提示

从一开始,我们就是一个分布式组织。随着我们的成长,我们已经学到了不少经验(例如,关于异步通信的,远程配对,以及分布式入职)。对于那些考虑加入我们团队的人,以及那些正在发展自己的分布式团队的人,我们想分享一些我们最喜欢的分布式团队技巧:

  • 在跨时区工作时,尽可能使用异步通信,并充分利用您拥有的任何同步通信时间。
  • 在共同的团队渠道而不是直接消息中交谈。
  • 记录会议。
  • 每天向你的团队问好并道别,以表明你何时加入/退出。
  • 让你的工作时间在你的日历上可见,这样你什么时候有空和工作就一目了然了。
  • 在团队讨论或会议后,将总结留在松散的线程中。
  • 经常结对。

在分布式团队中工作的最佳方法,无论是在 CircleCI 还是在其他地方,都会因团队、团队中的个人以及您要做的工作而异。我们鼓励你和你的队友交流,一起测试,并且要有耐心。建立一个快乐和成功的团队,尤其是一个地理上分散的团队,需要时间,但是拥有有效的过程是值得投资的。

转移到 DevOps:您真正需要什么工具?圆环

原文:https://circleci.com/blog/what-tools-do-you-need-to-do-devops/

DevOps 是一系列问题解决过程中的最新一个,每个过程都带有一个充满工具的数字车库:CI/CD 系统、测试框架、监控工具和安全审计工具等等。您需要这些工具中的哪一个?哪一项将解决您的组织面临的问题、困难和减速?在我帮助各种规模的团队时,我不断听到同样的两个问题:“我们需要什么样的组织结构?我们应该实现哪些工具?”

这两个问题都不错,但孤立地问,它们没有抓住要点。当你直接面对这些问题时,在你真正评估你试图在你的组织中解决的问题之前,你正在考虑解决方案。

组织通常认为自顶向下的模型(“使用这个!这样做!”)会让他们的团队更快地创新。热切的团队领导将带来一个新的 CI/CD 工具,并让每个人都开始使用它。为个体贡献者提供适当的工具来遵循所采用的实践是重要的,但是当团队领导在没有完全理解工具的价值或者他们为什么这样做的情况下引入工具时,问题就来了。通常,即使是下令改变的人也会忘记他们当初为什么要改变,或者他们想从中得到什么。可悲的事实是,一旦最初的推理变得模糊,就很容易开始滥用工具,从而创造出损失的甚至是负面的价值。

我们要 DevOps!

我接触过无数的组织,他们相信 DevOps 是他们所有问题的解决方案,只要他们能够快速启动并运行它。

如果你发现自己处于这种境地,问问自己,“为什么我们希望 DevOps 在我们的组织中?我们认为它会带来什么价值?”

至此,我们简单说一下 DevOps 是什么,又不是什么。

DevOps 是开发者和运营团队之间非常具体的协作。本质上,它表明您已经在文化上采用了基础设施中的开发实践,以及开发周期中的操作实践。这在实践中是什么样子的?这可能意味着将基础设施作为代码来维护,或者通过构建可重用的组件来创建不可变的基础设施,以便您可以随时拆除或拆除,为您提供版本控制和所做更改的历史记录。这也意味着让所有的产品贡献者更关心他们工作的最终结果——它在这个世界上是如何运作的?用户是如何与之互动的?让人们真正关心质量意味着同时关心商业价值和可用性。当构建产品的每个人都关心质量的两个方面,关心当产品被部署并暴露给最终用户时会发生什么。这是真正采用 DevOps 的结果。

根据我的经验,这种广泛的认同对于软件团队来说尤其难以实现,因为它需要具有不同技能和领域专长的人的大量合作。实现这一点需要跨职能团队结构和深思熟虑的沟通技巧。例如,如果一名工程师需要与业务人员讨论数据库问题,她不仅需要展示她正在处理的数据,还需要给出必要的上下文,并让该人员关注他们应该关注的内容和原因。

那么,您应该引入新工具吗?答案是:有可能。工具有时看起来像是快速解决方案,但它们不是万能的解决方案。根据我的经验,在引入新的 DevOps 工具之前,将这些考虑牢记在心会增加你成功的机会。

在开始转变之前:

1。确保每个人都在同一页上关于您试图通过这种转变实现的目标。每个人都应该在你试图解决的问题上达成一致,并且在痛点上保持一致。

2。总是从小处着手。不要试图一夜之间将整个组织打造成一个 model DevOps 模型团队。相反,从一个团队开始,看看流程改变在你的组织中是否有效。如果你看到进步,继续渐进地前进。

3。做对你有用的事。知道 DevOps 可能不适合您的组织。一些公司在没有 DevOps 的情况下已经成功了很长时间,考虑到他们的文化或产品需求,这可能不适合他们。我个人看到瀑布在一些非常成功的组织中工作得非常好。例如,如果保密是你公司产品策略的一个重要部分,那么通过增量发货来获得反馈对你来说是不合适的,因为你需要把所有的产品细节锁起来,直到一个大的发布。在那种环境下,很难建立 DevOps 文化。

4。始终测量。在您开始任何改进计划之前,获取您当前所处位置的准确指标(即我们的开发周期需要 X 时间)。在邀请 SRE 人加入开发团队之前,先这样做。然后过一段时间,你就能看到它是否有效。在你做出改变之前和之后进行衡量,并且持续地衡量你是否有所进步。举个例子:当许多敏捷转型正在发生时,许多公司采用了站立,没有真正理解为什么,或者衡量它是否对他们的团队有积极的影响。这可能浪费了更多的时间,而不是节省。

5。不要试图自动化一切。至少不是一下子。关于开发运维的一个误解是,所有的基础架构配置和配置管理都必须自动完成。这被称为“基础设施即代码”但是有些东西手动的时候效果更好;自动化不是万能的解决方案。此外,想想你要运行这个自动化脚本多少次,自动化它要花多少时间。你会用上千次还是只用三次?此外,有时您不得不开始手动方式,甚至找出自动化的最佳解决方案。还渴望自动化吗?将你的应用程序归档是一个很好的自动化方式,因为你所做的工作很可能会被重用。自动化生产前环境创建是实现自动化的另一个好方法。再举一个例子:你在尝试自动化防火墙设置吗?鉴于当前许多防火墙软件缺乏 API 支持,这可能不值得。尽管为灾难做准备是件好事,但你可能会把更多的价值投入其中,而不是从中获得。

下一步是什么?

如果您的组织正在考虑 DevOps 转型,请开始考虑您的交付速度和产品质量。你今天遇到了什么事?了解这些问题的答案将有助于组织中的每个人了解您的痛点是什么,因此您将处于开始改进它们的最佳位置。

在以后的文章中,我将通过一些练习来帮助您确定您的组织的问题区域在哪里,并让您有信心评估哪些工具或过程在解决这些问题时最有价值。

我们在 CircleCI 学到的关于雇佣工程经理的知识

原文:https://circleci.com/blog/what-we-ve-learned-about-hiring-engineering-managers/

注意(2020 年 8 月):自从发表这篇博文以来,我们已经从几个方面更新了下面概述的招聘流程。我们取消了“协作练习”面试阶段,将“产品面试”与“产品思考&工作分解”合并。这些变化意味着流程更短,有助于我们提高招聘流程的效率。我们重视基于经验教训的持续改进,并将在需要时继续改进流程。

在过去的一年里,我们对 CircleCI 优秀工程经理的理解发生了一些重大转变,并在此过程中了解到了很多关于我们组织的需求和价值观。我们已经从根本上改变了我们的招聘流程,通过这些努力,我们已经聘用了一些优秀的人才,使我们的工程管理团队增加了一倍。今天,我们想与你分享所有这些知识。

雇佣工程经理就像我们雇佣工程师一样

去年,我们分享了“如何面试工程师”。这是我们当时的流程,工程师和工程经理都用。总体而言,在 2018 年,我们有数百名候选人通过了我们的工程招聘流程。我们招聘工程师的流程如下:

Hiring Process Before

申请人审查>招聘经理电话筛选>形>宏观技能>最后一轮(协作练习、首席技术官对话、产品面试)关于这一过程的更多细节,请参见这篇博文

2018 年初,我们对工程师和工程经理采用了相同的招聘流程。当时,我们对工程经理的招聘主要集中在他们的技术背景和他们团队的技术工作之间的一致性上。正如我们在最初的帖子中所写的,“我们包括一个编码问题,因为发现候选人的价值观和优先事项是否与他们将管理的团队的价值观和优先事项一致是如此重要。”我们与一些工程经理申请人一起经历了招聘过程,并意识到我们经常到现场阶段,只是在结对时才发现他们的管理技能与我们的期望不符——发现这一点为时已晚。

这揭示了一个更大的问题:它让我们意识到,我们试图调整所有招聘流程的努力,导致我们关注并优化了错误的技能组合。曾经有一段时间,我们认为拥有非常技术化的经理是一件好事。我们打算改编 Spotify 模式,让交付团队和经理在不同的团队中按专业分工合作。随着我们对这种组织模型的接受发展到与我们作为分布式工程组织的需求相一致,我们意识到我们想要更多地分配领导。

我们引入了额外的角色来支持我们不同领域的团队:团队领导,负责他们团队的交付,以及技术领导,负责促进技术工作。通过我们新的工程能力矩阵,我们还将我们对所有级别的所有工程师的领导力期望整理成文。我们从这些变化中见证的早期成功鼓励我们继续发展我们对优秀工程经理的理解。

为我们的工程师提供更好的支持

管理不是工程。工程管理是一项根本不同的工作,需要不同的技能,我们需要这样对待它。CircleCI 的工程经理现在致力于人员管理:专注于一系列工程师、技术领导和团队领导的发展。他们定期与向他们汇报的工程师进行一对一的职业发展对话,并负责为他们设定目标、提供反馈、指导和辅导。他们还跨一组团队工作,以确保团队健康、知识共享、商业价值交付和跨团队的一致性。这意味着我们的工程师拥有对他们的个人和专业成长有极大兴趣和投资的经理,团队有专人在产品交付过程中指导他们。

使我们的流程适应我们重新关注的价值观

由于了解了工程管理中对我们真正重要的是什么,我们调整了我们的开放职位,使其同时关注人员和业务价值交付。我们还改变了招聘流程和每个面试阶段的设计,以便在面试过程的早期探索这些领域。与此同时,我们对所有申请人采取了更有条理、基于行为的面试流程。

这是我们现在招聘工程经理的流程:

Engineering Manager Hiring Process After

申请人审查>招聘经理电话筛选>配对练习>产品思考&工作分解讨论>最后一轮(工程团队成员面试、管理技能深度挖掘、产品面试) 注意:我们一直在寻找改进流程的方法,因此请注意这些阶段可能会发生变化

使用强调人员管理和业务价值交付的新流程,我们对这些技能的评估如下:

1。招聘经理电话筛选:该职位的招聘经理会与候选人谈论他们在不同领域的管理经验。
2。合作练习:这是一次合作面试,候选人与我们的一名工程经理配对。他们共同应对了我们过去作为一个组织所面临的两个挑战,这两个挑战今天仍有可能再次发生。就像我们的技术配对练习一样,我们希望确保反映我们正在进行的实际工作,并且作为工程管理团队一起讨论挑战和开发解决方案对于我们的日常工作非常重要。这次面试帮助我们更多地了解候选人应对组织挑战的方法,以及他们如何与同事合作。
3。产品思考和工作分解讨论:在这次面试中,候选人和我们工程管理团队的另一名成员讨论一些与他们理解工作和交付能力相关的问题,并从客户价值的角度引导讨论。
4。工程团队成员面试:这是一段与候选人将要共事的高级工程师的对话。他们一起讨论我们的团队过去面临的协作挑战。我们希望候选人能够指导技术讨论并为其增值,同时了解自己的局限性,支持技术决策,而不是充当决策者。
5。管理技能深度挖掘:这种对话通常是与工程管理团队的一名高级成员进行,对重要的领导技能和候选人的管理经验进行更深入的讨论。
6。产品面试:最后一次面试是与我们产品团队的一名成员进行的,重点是候选人对产品、流程和建立健康的工程团队的看法。

下一步是什么

以这种方式改变我们的流程有助于我们筛选正确的价值观和技能,并尽早确定哪些候选人符合这些价值观和技能。最终,它使我们能够雇用关心我们的优秀经理,他们的价值观与我们的组织一致,并且能够帮助我们的工程组织进入下一个增长阶段。

目前,我们正在努力确保这一流程与我们新采用的工程管理能力矩阵完全一致(该矩阵尚未公开,但我们的目标是尽快分享)。

如果我们的价值观和工程经理的角色听起来像是你喜欢的东西,请联系我们——我们正在招聘欧洲和北美的工程经理!如果你对与一个伟大的团队和一个投资于你成长的经理一起工作感兴趣,加入我们成为一名工程师

您对我们的流程有反馈吗?很想听听大家的意见。给我发邮件:lena@circleci.com

阅读更多信息:

模拟自动扩展构建集群第 3 部分:当自动扩展构建机群工作时

原文:https://circleci.com/blog/when-auto-scaling-build-fleets-work/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


注意:这篇文章是系列文章的一部分。见第一部分第二部分点击此处当另一个帖子被添加到此系列时会得到通知。

到目前为止,在这个系列中,我已经介绍了一个自动扩展构建车队的计算模型,并使用它来针对随机变化的构建负载尝试自动扩展。最终目标是找出如何最好地在 CircleCI Enterprise 的设备上使用自动扩展,这些设备在与 circleci.com T2 非常不同的流量模式下运行。在上一篇文章中,当我们意识到虚拟机启动时间太慢,无法响应现实世界流量的随机波动时,我们遇到了一点困难。这一次,我们将考虑全天的流量波动,看看自动缩放是否能帮到我们。

启动大数据 Hadoop 云

好吧,更像是启动 Postgres DB 和 Python 脚本…无论如何,让我们看看一些使用 CircleCI 的实际团队一天中构建的频率。查看单个公司的任何一天都是非常随机和尖锐的,但是合计几天内给定时间窗口内的所有构建会产生一些很好的平滑曲线。请记住,如果构建在一天中以恒定的速率随机发生,这些曲线将是平坦的线,但显然不是这样。

builds over time for various organizations

随着时间的推移,构建的情节看起来大多像拳击手套,在午餐时间略有下降。除了组织 4,它要么不吃午餐,要么可能分布在全球各地。如果你把 CircleCI 上所有组织的所有建筑加起来,你会得到一个类似这样的图:

all circleci builds over time

这是一种正弦波,有几个午餐时间和几个位于欧洲的员工。有理由假设一个非常大的公司,其团队遍布世界各地,共享一个中央构建集群,看起来会像这样。

固定到达模式已经过时了

为了模拟这种时变流量,我们只需对模拟进行一项更改。我们将保留之前的所有随机尖峰,因为自动缩放过程仍然需要处理它,但我们将添加一个时变函数,在采样之前乘以我们的随机分布。

为了简单起见,我将使用一个正弦函数,从午夜的 0,到中午的 1,再到午夜的 0。我将用这个函数乘以上次的相同流量模式,因此过去的恒定流量现在将是峰值流量。

大揭秘

测试这种新流量模式的具体细节与上次基本相同,只需一次快速的 c4.8xlarge 操作,即可运行所有可能的自动缩放参数。在进一步分析结果之前,我将分享一个在这种流量模式下任意一天的模拟时间的快速示例:

autoscaling fleet utilization

耶!这就是我所说的自动缩放!请注意尖峰信号有多猛烈,并且模型被优化为只能勉强吸收它们。另请注意,在一天当中,紫色的大块实心区域表示大量设备的持续利用率,这对机器效率很有好处。

看了这张图之后,这张图显示了新交通模式下固定规模车队的成本节约情况,这并不令人惊讶:

autoscaling savings

首先,与恒定流量的情况相比,我们为每个流量级别节省了更多。第二,也是更重要的一点,节省的费用不会随着流量减少而是增加。当我们试图针对随机流量峰值进行自动扩展时,更多的流量意味着更多的随机性和峰值。当与一个移动更慢的基本流量率相比时,更多的流量意味着我们有一个坚实的使用基础(上图中紫色的大块),同时仍然能够在晚上关闭大多数机器。

那么,我该如何设置我的 ASG 呢?

当我开始这个练习时,我的主要目标是确定自动扩展对于 CircleCI Enterprise 客户的实际启动时间和流量模式是否有意义,如果有意义,什么自动扩展参数可以产生最佳性能?

根据这篇文章和上一篇文章的结果,问题第一部分的答案似乎很清楚了。针对构建流量中每分钟的随机波动进行自动扩展对于实际的启动时间和流量模式来说没有意义,但是根据流量每小时的变化进行扩展肯定有意义。

问题的第二部分,即对于任何给定的交通模式,什么参数是最好的,要困难得多。我的强力方法有助于确定自动缩放为日常流量模式节省大量资金的参数确实存在,但这不足以为任何情况指定一套完整的参数,例如 AWS 自动缩放组(ASG)参数。即使在我的理想化模型中,这也是一个复杂的多维建模问题,在现实世界中更加复杂。

也就是说,我对优化工作中出现的参数做了一些简单的分析,发现了一些明显的趋势。我在一个简单的公式中捕获了这些信息,为给定的负载推荐 ASG 参数(CloudWatch 警报计时、阈值等)。该公式只依赖于构建的总容量(每小时构建分钟数),而不依赖于构建的长度,因此希望它能够简化我的模型中构建时间是恒定的。该公式似乎在推荐参数方面做得相当好,即使是在我的强力方法的搜索空间之外的流量:

autoscaling parameter model

请注意,该公式只是相对于我在它所涵盖的领域中通过强力搜索找到的最佳参数而言一般,这是意料之中的,因为我没有在我的公式中捕捉到每一个可能的良好性能预测。然而,该公式在高容量的情况下表现得非常好,这也是意料之中的,因为这些情况应该有一个更大的运行构建的基础来扩展。如果不从客户群中收集更多真实世界的数据,而只是根据我的模型粗略地评估推荐的参数,那可能是愚蠢的。

后续步骤

关于自动扩展优化的系列文章到此结束,但是我们改进 CircleCI 企业自动扩展体验的工作还远远没有结束。下面是我们接下来要做的一些事情:

  1. 从客户那里获得反馈:这种模拟业务很酷,但没有什么比来自现场的数据更能测试模型的实用性并指导我们的建议。
  2. 测试更多流量模式和参数:使用简单的正弦波流量是一个很大的简化。测试更真实的交通模式也是一件好事。从客户那里获取流量数据可以让我们将预测的自动缩放性能与现实进行比较。到目前为止开发的简单参数预测公式也可以用于缩小参数空间,并以更高的分辨率搜索更好的公式。
  3. 探索替代的自动扩展指标和模型:circle ci 的经验表明,根据可用容量进行扩展是抢先确保流量得到处理的好方法。然而,如果流量非常低,基于 CloudWatch 警报阈值的固定数量可能会导致夜间过度配置。我们可能会发布其他指标或技术来缓解这一问题。

同样,这个模拟在 bellkev/asg-sim 是开源的,我鼓励任何 CircleCI Enterprise 的客户、其他集群运营商或者只是好奇的市民去看一看!

注意:这篇文章是系列文章的一部分。见第一部分第二部分点击此处当另一个帖子被添加到此系列时会得到通知。

漏洞检查器-安全 CI/CD | CircleCI & WhiteSource

原文:https://circleci.com/blog/whitesource-launches-free-open-source-vulnerability-checker-orb-for-all-circleci-users/

已知的开源漏洞是我们开源使用的最大威胁。如果我们的产品得不到补救,就会被人利用。这些漏洞已被安全研究人员发现,并报告给一系列安全资源,如国家漏洞数据库、各种咨询和问题跟踪器。黑客知道,他们可以从这些资源中免费获取如何利用许多产品中使用的易受攻击的开源组件的信息,从而很容易找到潜在的目标。

未能掌握易受攻击的开源组件可能会带来巨大的代价,正如我们在 2017 年 Equifax 的案例中看到的那样。当黑客利用客户门户中使用的 Apache Struts 的一个易受攻击的版本,窃取了大约 1.47 亿多用户的个人身份信息(PII)时,信用评级机构被攻破。Struts 组件中的漏洞至少在两个月前就已经为人所知,但该公司的开发人员并不知道他们正在他们的产品中使用它,因此在漏洞被发现之前修补它的速度太慢了。

不幸的是,当我们考虑在我们的产品中使用多少开源软件时,至少可以说,跟踪我们所有的开源使用是一个挑战。面对这一挑战,了解您正在使用的组件和易受攻击的组件是重要的第一步。

知道是成功的一半

应对易受攻击组件威胁的第一步是识别哪些组件出现在您的回购、产品等中。然而,除了已经包含在已部署产品中的组件之外,在您使用的所有开源组件通过您的 CI/CD 管道时,保持对它们的关注远不是一个简单的过程。通常情况下,一个开源组件可能看起来没有任何漏洞,但是它可能在它的依赖项中隐藏了一些令人讨厌的漏洞,这些漏洞可能会被利用。除此之外,您还需要不断地在所有可用的安全资源中寻找新的漏洞,这实际上是一项艰巨的任务。

为了使这项任务更加可行, WhiteSource 与 CircleCI 合作,为所有 CircleCI 用户提供免费工具,自动扫描他们的产品,寻找最新和最常见的易受攻击的开源组件。WhiteSource 每个月都会汇编一份清单,列出 50 大开源漏洞,可以对照您的清单进行检查,同时还有一份更全面的常见漏洞清单,如果不进行检查,这些漏洞可能会威胁到您的产品。

如果您的产品需要补救,或者希望没有列表中的任何已知漏洞,扫描结果将实时通知您。

WhiteSource 漏洞检查器 orb 入门

整合白源宝珠又快又简单。只需将下面的.yml文件中的相关行复制到 GitHub repo 中项目的配置文件中,然后点击 commit changes 开始扫描。

version 2.1

orbs:
  vulnachecka: whitesource/vulnerability-checker@19.5.5

workflows:
  test:
    jobs:
      - vulnachecka/scan 

在我们的 CircleCI 环境中,我们可以看到扫描正在进行。当扫描完成后,我们可以在我们的工件选项卡中看到项目,并通过点击一个易于理解的 HTML 页面看到下面的结果。

我们可以在这里看到我们是否完全清楚,或者是否从最常见或前 50 名列表中发现了漏洞。

拥有安全性的开发者

为了跟上当今软件开发的飞速发展,我们需要找到新的和创新的方法来更有效地处理我们产品的安全性。自动化工具,如 WhiteSource 提供的工具,可帮助组织将其安全性向左转移,从 SDLC 的最早阶段避免其产品中的漏洞,以满足紧张的发布时间表。

无需注册这项免费服务,现在正是 CircleCI 用户利用 WhiteSource 行业领先的开源安全解决方案来控制您的开源漏洞管理的大好时机。


这篇文章是我们制作的关于 DevSecOps 的系列文章的一部分。要阅读本系列的更多文章,请单击下面的链接之一。

为什么开发商要转向 Yarn - CircleCI

原文:https://circleci.com/blog/why-are-developers-moving-to-yarn/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


随着 Npm@5 和 NodeJS V8 的发布,下面的一些信息已经过时。

JavaScript 世界每秒都在变化。说过去一年发生了很多事情是轻描淡写。这些变化包括 Angular2 发布Node.js 7.0.0 发布,以及 ECMAScript 2016 (ES7) 的特性集最终确定。2016 年 10 月,脸书、Exponent、谷歌和 Tilde 发布了一个意想不到的东西,一个被他们称为npm 替代品。

Npm 问题和纱线解决方案

Yarn 不是 npm 的一个分支,而是它的一个重新想象。大型项目——比如脸书和谷歌的项目——放大了开发人员在使用npm时可能面临的问题。

国家预防机制的潜在问题

  • 嵌套依赖关系(在 npm 3 中已修复)
  • 程序包的串行安装
  • 单包注册表(npmjs.com 有没有为你倒下过?)
  • 需要网络来安装软件包(虽然我们可以创建一个临时的缓存)
  • 允许包在安装时运行代码(不利于安全)
  • 不确定的包状态(您不能确定项目的所有副本都将使用相同的包版本)

纱线解决方案

  • 多个注册表——Yarn 从npmjs.comBower 读取并安装包。如果其中一个发生故障,您的项目可以继续在 CI 中构建,不会出现问题
  • 平面依赖结构——更简单的依赖解析意味着纱线完成得更快,并且可以被告知使用特定包的单一版本,这使用更少的磁盘空间
  • 自动重试-单个网络请求失败不会导致安装失败。失败时会重试请求,从而减少由于临时网络问题而导致的红色构建
  • 并行下载——Yarn 可以并行下载包,减少构建运行的时间
  • 与 npm 完全兼容-从 npm 转换到纱线是一个无摩擦的过程
  • yarn.lock -将依赖关系锁定到特定版本,类似于 Ruby 世界中的 Gemfile.lock

纱线锁

Yarn 引入了一个锁文件yarn.lock,用来管理 JavaScript 包。对于团队工作的开发人员来说,这可能是 Yarn 最有用的特性。在package.json中,包版本可以被指定为一个范围,或者根本没有版本。这可能会导致一个问题,即同一个团队的不同开发人员使用同一个包的不同版本。任何接受过 CI 培训的工程师都知道,用完全相同的依赖关系复制一个环境的能力,对于有效的调试和新团队成员的加入是至关重要的。Yarn 从包管理器如 Bundler 那里借用,创建了一个yarn.lock文件,记录你在项目中使用的每个包的确切版本。将这个锁文件提交到您的 VCS 可以确保所有从事该项目的开发人员,如果他们使用 Yarn,将会使用每个包的相同版本。

在圈圈上使用纱线

从 npm 切换到 Yarn 是没有痛苦的,因为它们是兼容的。我们第一次写关于 Yarn 的文章是在12 月,在那里我们展示了下载和安装 Yarn 用于您的构建的最佳方式。从那以后,我们继续展示我们对 Yarn 的支持:Yarn 现在已经预装在我们的 CircleCI Ubuntu 14.04(可信)映像中。通过简单地使用yarn命令,Yarn 可以像您的本地环境一样在 CircleCI 上使用。缓存说明可在我们的纱线文档中找到。

npm 至纱线备忘单

| npm | 故事 |
| npm 安装 | 故事 |
| -从 package.json 安装项目依赖项 |
| npm 安装[包] | 不适用的 |
| -安装特定的包,但不将其作为依赖项保存在 package.json 中 |
| npm 安装-保存[包] | 纱线添加[包装] |
| -安装一个包,并将其作为依赖项保存在 package.json 中 |
| npm 安装-保存-开发[包] | 纱线添加[包装] [ - dev/-D] |
| -安装一个包,并将其专门作为开发依赖项保存在 package.json 中 |
| 不适用的 | 纱线添加[包装] [ - peer/-P] |
| -安装一个包并将其保存在 package.json 中,专门作为一个 Yarn 对等依赖项 |
| npm 安装-保存-可选[软件包] | 纱线添加[包装] [ -可选/-O] |
| -安装一个包,并将其保存在 package.json 中,作为可选的依赖项 |
| npm 安装-保存-精确[包] | 纱线添加[包装] [ -精确/-E] |
| -安装软件包的特定版本,而不是安装最新版本的默认行为 |
| 不适用的 | 纱线添加[包装] [ -颚化符/-T] |
| -安装具有指定主要版本的软件包的最新次要版本(即 yarn add foo@1.2.3 -T 将安装与 1.2.x 匹配的最新版本) |
| npm 安装-全局[软件包] | 纱线全局添加[包] |
| -在您的本地计算机上全局安装一个包,通常用于开发人员工具 |
| npm 重建 | 纱线安装力 |
| -重建所有软件包,即使已经下载 |
| npm 卸载[软件包] | 不适用的 |
| -卸载软件包,但不将其从 package.json 中删除 |
| npm 卸载-保存[包] | 纱线去除器[包装] |
| -卸载一个包并将其从 package.json 中删除(不考虑 Yarn 中的依赖类型) |
| npm 卸载-保存-开发[包] | 不适用的 |
| -卸载软件包,并将其作为开发依赖项从 package.json 中移除 |
| npm 卸载-保存-可选[软件包] | 不适用的 |
| -卸载软件包,并将其作为可选依赖项从 package.json 中删除 |
| rm -rf 节点模块和 npm 安装 | 纱线升级 |
| -将软件包升级到最新版本 |

摘要

Yarn 解决了诸如不确定的依赖性、网络问题/npmjs 关闭以及并行下载等问题,以便提供比 npm 更大的价值。然而,新政治机制是自身成功的受害者。随着越来越多的人转向 Yarn 和其他注册中心,npm 服务器将变得更加可用。两个包经理都很棒,最终会互相提高。

好玩的事实: 在你的本地机器上,npm 可以安装 Yarn!npm install --global yarn。纱线团队不推荐这种安装方法。

限制功能使用:为什么公司故意下线

原文:https://circleci.com/blog/why-brownouts/

限电是一种有意但暂时的服务中断。为什么一个公司会故意造成停电,什么时候有意义?在这篇文章中,我将解释什么和为什么限电,并提供一些最近的例子。

什么是限电?

限电一词来自公用事业行业。对于电力公司及其客户来说,停电(或完全断电)可能是毁灭性的。电力公司的工作人员没有让事情发展到完全中断的地步,而是密切关注停电即将来临的迹象。然后,公用事业公司可以降低每个人的电压,或者在有限的时间内切断某些地区的服务。这种影响与停电有很大不同。例如,电压较低的旧房子中的照明可能会变暗,颜色从正常的黄色逐渐变暗,变成倾斜的棕色。这个想法是,这种“限电”的后果将没有影响每个人的停电严重。

科技行业已经采用了限电概念,并将其应用于服务器和运行在服务器上的服务。如果硬件出现故障,或者如果流量涌入威胁到整个网络,科技公司可能会选择性地限制对其服务的访问。科技公司发现,当一项服务或功能必须逐步淘汰时(也称为“日落”),限电可以缓解客户的中断。

为什么要实施限电?

精心规划和有意识的限电是提醒用户注意关键变化、收集数据和防止未来服务损失的重要工具。以下是 CircleCI 等 SaaS 提供商在计划限电时可能会考虑的一些事情:

  • 展示影响的范围
  • 了解对间接用户的影响
  • 吸引客户的注意力,以便他们能够应对变化

展示影响的范围

2022 年 2 月,CircleCI 宣布弃用我们的 Ubuntu 14.04 和 16.04 图像。这些图片将于 2022 年 5 月 31 日删除。这里有一个假设的场景需要考虑:

CircleCI 的客户 SavvyTech 偶然看到了我们的博文,看到了标题。他们查看了他们项目的配置,没有看到任何这些被否决的图像。太好了!他们可能会想“这不会影响我们,我们很好。”

快进到三月底,这些被否决的图像第一次出现限电。由于缺少 Linux 映像,一些项目的 SavvyTech 管道会失败 10 分钟。

  • 这里发生了什么?
  • 他们为什么会受到影响?

原来他们使用的是旧版本的 CircleCI orb ,它使用的是一张废弃的图片。这在他们的配置中没有显示出来,但显然仍然会影响他们。一旦 10 分钟时间到了,服务恢复了,他们就开始在日常工作的同时测试一个更新的 orb。

这种情况下的限电向 SavvyTech 展示了图像废弃的全部影响。事实上,影响比他们最初预期的要大。停电造成了 10 分钟的停机时间,而不是几个小时甚至几天,如果他们直到后来才意识到这些图像已被永久删除,这可能会发生。

了解对间接用户的影响

技术在许多方面将我们联系在一起。对于科技公司来说,这意味着如果 AWS 出现故障,尤其是us-east-1,无论我们是否是 AWS 的客户,我们都会感觉到。涓滴效应可能会影响 Slack、脸书甚至 Spotify,这可能会影响到从开发团队到临时社交媒体用户的每个人。

这种现象使得限电成为提醒非公司直接客户的用户的有效方式。

这里有一个例子。2021 年,JFrog 决定终止他们的 BinTray 托管服务。他们在博客上宣布了这一计划,向客户发送了电子邮件,并采取了其他传播策略。JFrog 没有做的是联系德国的一名开发人员(我将称她为 Molly ),她在 MacBook Pro 上运行一个旧的自定义自制程序。他们为什么要这么做?他们会怎么辨认她?JFrog 还在 BinTray 上运行了一次限电。

Molly 不是顾客,她甚至不知道 JFrog 是什么,但她仍然受到 BinTray 被日落的影响。当莫莉试图brew install时,她发现了这个变化,并得到了一个关于 BinTray URL 的错误。原来,家酿使用 BinTray 存储瓶子(二进制)。JFrog 运行的 BinTray 灯火管制提醒了 Molly 和成千上万的其他人,BinTray 即将日落。Molly 接触了家酿社区,并了解到她需要将她的家酿叉子升级到更现代的版本,并且何时需要这样做。

吸引顾客的注意力

从本质上讲,灯火管制是一种吸引某人注意力的非破坏性工具。有时电子邮件不会被发送或保持未打开状态,博客帖子不会被阅读,应用内的横幅广告会被忽略。在 DevTools 领域,你试图接触到那些把大部分时间花在终端或 VS 代码上的开发人员,那么你如何接触到他们呢?如果你幸运的话,也许有一个命令行界面可以使用。但有时你甚至没有。

这里有一个我比较熟悉的具体空间:持续集成。通常,开发人员除了他们的编辑器和 GitHub PR 页面之外不会查看任何东西,除非出现故障。

他们可能会跳到 web 应用程序上,看看哪些测试或下载失败了。由限电引起的故障可能是联系一个埋头苦干的开发者的唯一途径,他只是一张接一张地敲掉吉拉的门票。这位开发人员可能太热衷于实现零收件箱,以至于一封夕阳西下的电子邮件在他们看到之前就被删除了。

当谈到贬低重要或广泛使用的功能时,吸引注意力是关键。

评估限电风险

当用户的服务被中断时,肯定会有压力。这会引起周围人的不满,尤其是支持团队。但是有什么选择呢?

中断用户服务 10 分钟或 1 小时并不理想。但是,让用户的服务在停产日期前无限期关闭是非常非常糟糕的。取消服务或功能通常是不可逆的,用户所需的操作有时需要时间。在服务被永久移除之前引起客户的注意,让他们有时间制定计划,然后成功执行该计划。

科技限电的真实例子

以下是一些科技公司最近限电的例子。即使从这一小组例子来看,明确的限电长度和频率仍在测试和完善中。

我们已经弃用了非常旧且不安全的 Ubuntu 14.04 和 16.04 版本。为了提醒用户这些变化,我们计划在 2022 年的 3 月、4 月和 5 月进行限电。关于图片、时间线等更多信息可以在我们的弃用公告帖子中找到。

GitHub-2021 年 9 月 GitHub 宣布他们正在放弃旧的不安全的 OpenSSH 密码。他们继续对这些密码进行了一些限制,最终在 2022 年 3 月停止使用。

JFrog -正如本文前面提到的,JFrog 在 2021 年初弃用了他们的托管服务 BinTray。这导致了该服务在 2021 年 5 月被永久关闭之前的几次限电。

Codecov -经过反复试验,Codecov 为他们的服务推出了一个新的改进的 Bash 上传器。这意味着贬低(和淘汰)旧的 Bash 上传器。他们在这个博客帖子中解释了原因并提供了计划的细节。

PyPI 不像这个名单上的其他公司,所以没有一个集中的地方让人们去了解细节。尽管如此,PyPI,一个开源项目和 Python 生态系统的一个重要部分,运行他们自己的弃用和限制。这一点在很多地方都得到了传达,包括邮件列表和几个社区成员博客

结论

虽然限电不是功能弃用的唯一可用工具,但它们是有效的。限电在以后的弃用过程中特别有用。在一个通知超载的时代,它们不仅是获得你自己的客户的注意和认知的方法,也是获得集成商和下游用户的注意和认知的方法。

正如我在这篇文章中多次提到的那样,CircleCI 最近弃用了旧的不安全的 Ubuntu 图像。这些图像的限电时间安排在 2022 年 3 月 29 日、4 月 26 日和 5 月 19 日。

你可以在这篇博客文章的中获得更多关于这种形象贬低的信息,并找到全面的限电时间表。本文包含这些迁移指南的链接,帮助您迁移到使用更新的映像:

您可以使用这些资源来发现映像降级对您的团队的影响范围,了解影响,并管理对更安全的新映像的更改。

福雷斯特波| CircleCI

原文:https://circleci.com/blog/why-circleci-is-a-leader-in-the-forrester-wave-cloud-native-continuous-integration-tools-q3-2019-speed-scale-security-and-compliance/

今天,Forrester Research 发布了2019 年第三季度 Forrester Wave:云原生持续集成工具,我们很自豪地宣布 CircleCI 再次被评为领导者。这是 Forrester 关于该领域的第二份报告,也是我们第二次被认定为该行业中唯一的独立持续集成提供商。

该报告强调,各垂直行业的组织都面临着比以前更快交付软件的巨大压力。CI/CD 是确保团队能够快速交付高质量软件的关键组件。这就是为什么我们将所有的时间和资源都集中在为客户提供最佳体验上。我们专注于将 CI/CD 作为同类最佳的独立工具,这使我们能够为在交付流程中需要竞争优势的组织提供最大的灵活性、功能和控制力。

在 CircleCI,持续集成不仅仅是一项副业,而是我们产品的核心。

“要做到真正的好、快、有信心,你必须专注。这就是我们围绕 CI/CD 采取这种深度方法的原因。不仅仅是加快计算速度;它包含了太多的智慧。”——circle ci 首席执行官吉姆·罗斯

我们唯一的关注点是让我们能够构建市场上最智能和性能最好的 CI/CD 工具。这就是我们所做的。因为我们相信开发人员选择同类最佳工具的能力非常重要,所以我们投资于可扩展性,使得与其他关键工具的集成成本变得容易且低。

弗雷斯特同意这一观点。正如该报告所述,“大型企业通常需要使用包括 Linux、Windows 和 macOS 在内的多种技术进行开发。CircleCI 对这些执行环境的支持使其产品成为领先产品。”

以下是 CircleCI 在今年的报告中获得最高分的地方:

  • 支持各种构建系统和预配置环境:报告称,“CircleCI 提供了大量预配置环境,企业告诉 Forrester,他们正在寻找这些环境来帮助他们快速进入云计算。”我们也支持为 macOS、Linux、Android 和最近的 Windows 开发的开发者。

  • CircleCI orbs :去年秋天,我们推出了一个名为 orbs 的机制,允许第三方为开发者提供预先制作的集成,以便在管道中使用。Forrester 指出,“CircleCI 的 orb 技术允许客户轻松集成和维护第三方应用程序,创建更具弹性的构建管道。”自 2018 年 11 月推出以来,orb 已被 CircleCI 用户广泛采用,超过 11,000 家组织在超过 28,000 个项目和 5,770,000 个 CI/CD 管道中使用 orb。这些 orb 提供的功能、灵活性和控制使 CircleCI 成为获得成功的最快和最可靠的途径。

  • 支持企业遵从多种认证,如 FedRAMP :在报告中,Forrester 指出,“另外一个重要因素是[circle ci]支持企业遵从多种认证,如 FedRAMP。”我们很自豪成为第一个在 2018 年获得 FedRAMP 认证的 CI/CD 解决方案。有了这一授权,整个联邦开发者社区都可以访问 CI/CD 工具,该工具每月为世界上一些最具创新性的公司运行超过 3000 万次构建。

虽然 CircleCI 在报告中被评为领导者,但我们在一些领域的得分却不太理想:

  • 缺少迁移工具:在 CircleCI,我们不相信“搬来搬去”的模式,也不相信导入复制粘贴的配置是服务客户的最佳模式。相反,我们创建了一个直观的配置格式和一些常见模式的食谱,使有其他 CI 系统经验的团队能够快速迁移他们的管道。我们的 900 多个 orb 使得配置和设置中常见的构建、测试和部署模式的重复变得微不足道。

  • 混合 CI :目前,您无法使用我们的云来协调您自己的构建车队,但这是我们的长期路线图。

  • 全面的 UX : Forrester 希望看到对桌面 IDE 的支持,这是一种在一个显示器上混合信息和命令(GUI)的 WebUI,以及通过第三方工具或在 IDE 或 WebUI 中支持开发人员协作的能力。因为 config 是代码,使用 CircleCI 开发人员可以在推进管道成熟度方面进行合作,就像他们在代码和测试方面所做的一样,包括 PRs 和代码审查。

CI/CD 就是我们所做的一切。这是故意的:我们认为它非常重要,值得我们全力关注。我们利用九年来帮助公司从初创公司成长为财富 500 强的经验,结合我们新兴行业中最大的构建数据,创建了最佳的持续集成和持续开发工具,也是唯一一个专门为开发人员设计的工具。有了 CircleCI,开发人员可以把时间花在最重要的事情上——无论是建造自动驾驶汽车、比特币交易所,还是下一个大型音乐平台,都可以更快、更有效、更大规模地完成。

我们一次又一次地从我们的客户那里听到,CircleCI 的快速反馈导致了更好的决策、更好的安全性和更高的吞吐量。我们感到自豪的是,今年的 Forrester Wave 证实了我们多年来所知道的事情:开发团队关心性能,他们对他们的工具很挑剔,他们将寻找最佳的 CI/CD。

为什么 CI 是提高部署频率的关键| CircleCI

原文:https://circleci.com/blog/why-continuous-integration-is-key-to-stepping-up-deployment-frequency/

2009 年 O'Reilly Velocity 大会上,当时担任 Flickr 工程经理的 John Allspaw 和 Paul Hammond 给做了一个演讲,讲述了让 Flickr 的 DevOps 组织每天部署 10 次以上的最佳实践。那个著名的演示引发了很多关于每日部署的最佳构建数量以及如何增加部署频率的讨论。

但是,正如我们在之前关于工作流程交付周期的帖子中解释的那样,数量和速度并不总是等同于质量。部署代码是必不可少的,事实上,高性能的 DevOps 团队比他们低性能的同行更频繁地部署代码。然而,没有神奇的部署数量可以保证你在一个高性能的团队中;部署的数量取决于您正在开发的软件。

在这篇文章中学习如何发现和监控团队成功的工程指标

正如我们在最近的报告中所分享的,CI 的数据驱动案例:实践中 3000 万个工作流揭示了什么关于 devo PS,部署频率,以及其他关键指标,如交付周期和变更失败率,可以通过持续集成和持续交付(CI/CD)来改进。使用来自 CircleCI 的 3000 万个工作流的运行数据,我们发现这些数字支持来自 2019 年 DevOps 报告等来源的关键 DevOPs 指标。在这篇文章中,四部分系列的第二部分,我们将深入探讨部署频率。

部署频率和随意部署能力之间的联系

部署频率表明有多少离散的工作单元正在通过开发管道。在我们的报告中,我们测量了团队开始工作流的频率。当您采用 CI/CD 时,您会得到自动化,它甚至允许自动部署复杂的代码库。这对提高部署频率至关重要。在自动化软件管道中,像补丁这样的东西几乎可以立即提供给用户,并像其他更新一样经过严格的测试。

部署频率的重要之处在于,它可以作为团队随意部署能力的指标。如果您的团队对其 CI 渠道有信心,并且能够自动化部署,他们就不必处理手动验证代码,这是更频繁部署的一大绊脚石。使用自动化,可以在每次提交时执行测试,失败的构建通知允许对失败的即时可见性。通过快速向您提供错误信息,您的 DevOps 团队可以更快地推出变更。

为了了解观察到的开发行为与行业标准相比如何,我们查看了 2019 年 6 月 1 日至 8 月 30 日期间观察到的超过 3000 万个工作流的 CircleCI 数据。工作流代表:

  • 每天运行 160 万个作业
  • 超过 40,000 个组织
  • 超过 150,000 个项目

以下是我们的发现:

  • 50%的开发运维组织每天在所有项目中启动六个工作流
  • 在每个项目级别,50%的项目每天都有三个工作流启动
  • 在第 95 百分位,这些数字上升到每个项目 74 个工作流,所有项目 250 个工作流

鉴于 Allspaw 和 Hammond 10 年前关于部署频率的陈述的高姿态,我们本应期望看到今天的许多团队以非常高的频率部署。相反,我们没有看到很多团队每天部署数十次或更多次。虽然这种行为确实存在,但它是例外,而不是规则。

从我们的数据中有什么收获?

尽管我们没有看到很多组织达到每天 10+部署的神奇数字,但我们可以观察 CI/CD 对部署频率的影响。根据 DevOps 2019 报告的加速状态:

  • 精英小组一天部署几次
  • 高绩效团队部署在任何地方,每天一次到每周一次
  • 低绩效团队每月部署一次,每六个月部署一次

这些发现与我们的运行数据非常吻合,数据显示 CircleCI 用户平均每天运行六个工作流。由于使用 CircleCI 的 DevOps 团队可以在任何需要的时候进行部署,因此部署的数量成为组织选择的反映,而不一定是高性能的反映。测试和集成代码的频率,以及团队在遇到错误时的恢复速度,是高性能 DevOps 团队比部署频率更好的指标。

为什么 CI 帮助您始终如一地构建最好的软件

我们的数据显示:

  • 使用 CircleCI 的团队非常快:80%的工作流程不到 10 分钟就完成了
  • 使用 CircleCI 的团队保持在流程中并保持工作进展:50%的恢复发生在不到一个小时内
  • CircleCI 上 25%的故障在 10 分钟内恢复
  • CircleCI 上 50%的故障可以一次恢复

虽然我们仍然在研究 CircleCI 是否能使团队变得高绩效——或者高绩效团队是否只是养成了选择 CircleCI 的习惯——我们可以充满信心地说,致力于持续集成实践的团队可以持续地、高速地生产出最好的软件。

我们还可以自信地说,我们知道实施 CI 并不容易,但简单地启动这一过程将会带来好处,即使您不会每天部署一千次或在基础架构上投资数百万。

在接下来的两篇关于我们工作流数据的博文中,我们将分享对更多关键 DevOps 指标的见解:平均恢复时间变更失败率。如果你错过了,这是我们关于交付周期的第一篇文章。

为什么我的构建很慢?-切尔莱西

原文:https://circleci.com/blog/why-is-my-build-slow/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


很少有什么比看到一张支持票询问为什么构建缓慢,或者从我们的客户经理那里听到客户对缓慢的构建表示担忧更让我们难过的了。在 CircleCI,我们讨厌缓慢的构建。我们认为增加软件开发过程的总吞吐量是我们的使命,对于许多团队来说,构建速度无疑是总吞吐量中的一个重要因素。

我们如何加速构建

我们目前有几种方法可以让构建更快,并且我们一直在努力寻找新的方法。今天我们有:

  • 源缓存——我们在服务器上保留一份副本,只更新自上次构建以来的更改,而不是获取每次构建的所有源代码。

  • 依赖项缓存——我们不是在每次构建时下载依赖项,而是将它们放在磁盘上,以便在每次构建时快速检索。

  • 并行性——通过在几个独立的实例上运行测试,我们可以显著减少总的构建时间。如果您提供测试元数据,我们可以根据时间自动分割测试。否则,我们可以通过文件自动分割(每个容器一个偶数),或者你可以手动设置你的测试分割或者其他并行任务。

  • 按需构建车队 CircleCI 的魔力之一是我们随时准备好干净的执行环境,所以当您开始构建时,在您的构建开始运行之前不会有超过几秒钟的等待时间。每次构建完成时,我们都会回收运行每个构建的机器(或者在并行构建的情况下,回收构建的每个部分),因此可以保证您永远不会泄漏源代码或数据,也不会交叉污染设置、数据库、环境变量等。

那么,为什么我的构建在我的笔记本电脑上运行得更快呢?

我们有时被比作本地运行的构建速度,但是这种比较是复杂的。请这样想——对于我们的许多客户来说,一个非常常见的开发盒是一个带有 4 个强大 CPU 的 MacBook Pro,通常价格约为 3000 美元,大约每两年更换一次。为了给每一次构建提供相同的马力,我们需要提供更多的计算能力,从而对按需机队容量收取更高的价格。

下一步是什么?

如今,当付费用户不再有内存错误时,他们可以联系支持人员,为他们的构建分配更多的内存。虽然我们的并行特性通常会提高构建速度,但在某些情况下,编译密集型构建的组织,如 iOS 和 Android 开发人员,可能不太适合这种设置。目前,当客户在他们的构建中需要更多的 CPU 能力时,我们建议他们与我们讨论 CircleCI Enterprise ,在那里用户可以运行他们自己的 CircleCI 实例,并根据他们团队的需要调整他们自己的构建舰队。

请放心,我们一直致力于在您需要时为您提供更多计算能力。从按需执行环境到依赖性和源代码的缓存,再到业界最简单、最具可扩展性的并行设置,再到帮助识别瓶颈的图表,以及我们多年来构建的许多其他功能,CircleCI 每天都在努力提高您将代码转化为软件的吞吐量。在今年剩下的时间里,我们将继续推出新的方法来调整您的构建,敬请关注。

我们总是希望听到您关于如何充分利用 CI/CD 系统的意见,所以请务必在我们的社区论坛上发表您的想法。

为什么我们打破我们的哲学誓言给你带来 CircleCI 2.0 - CircleCI

原文:https://circleci.com/blog/why-we-broke-our-philosophical-vows-to-bring-you-circleci-2-0/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


几周前,我们发布了 CircleCI 2.0。这是一项巨大的努力,当它全面上市时,CircleCI 的每个人都参与其中。这正是我们作为 CI/CD 公司试图避免的努力。

我们从根本上改变了我们产品的内部结构,这不可能不令人害怕。在第一个客户面前展示这个花了六个月,又花了九个月才上市。很难告诉你达到这个里程碑我们有多轻松,因为这意味着我们实际上又可以开始以小块的形式交付代码了。

那么,作为一家名副其实地以“CI”命名的公司,我们为什么要花这么多时间来精心设计一个真正的版本呢?这难道不违背我们的信仰吗?

是啊!但是我们不得不这么做,因为我们绝对相信持续交付。

绿色的田野很可怕

对于我们许多目睹这一切发生的人来说,Joel 的话仍然是正确的:重写代码是一件你永远不应该做的事情。抛开多年的工作,停止开发来建造一个更好的版本是一个可怕的,可怕的决定:那片极具吸引力的绿色田野和它田园般的纯净是在埋藏的放射性废料上生长的。这不是我们做事的方式。

我们习惯了频繁的节奏,以稳定、快速的剪辑完成小功能。主要版本比一般的软件公司更让我们害怕。因此,更换我们整个平台的核心是双重危险的:试图再造我们的产品存在固有的风险,除了更好,我们必须在黑暗中工作几个月,没有持续验证的指导光。

这就像要求一个电视制作团队停止制作剧集并拍摄整部电影:你没有得到一个验证的试播集,你没有衡量每一集,你肯定也没有在飞行中重写故事弧。你只有一次机会和一大笔预算,你最好别搞砸了。

但是很酷。一鸣惊人就好。

转折点

我们的基础设施的效率正朝着本地最大值前进。我们可以看到自己和客户需求的未来,但我们看不到通向未来的渐进之路。全球最大值在另一座山上,我们需要一个飞跃。

我们绞尽脑汁,寻找一种方法来逐步更新我们的平台,但我们功亏一篑。最终,我们得出了一个严峻的结论:我们将不得不踏上一段充满危险的旅程——对我们的基础设施进行一次完整的、非持续的改造。

我们意识到,基础架构的改变不会逐渐发生。狗屎。

这条道路固有的危险迫使我们认真审视我们的原则:在 CI/CD 的所有好处中,我们能保留什么,我们必须抛弃什么?在重写核心产品的同时,我们如何继续在产品的其他领域取得进展?

我们可能很固执。但是相反,我们问自己这些问题,打破我们的价值观,重建它们就像我们重建 CircleCI 一样。我们投入这个项目是因为我们知道另一面会是什么:更好的持续交付。

我们完全相信这种哲学,因此暂时接受一种相反的世界观是有意义的。通过这样做,我们能够兑现我们对持续交付的承诺。最终,我们不得不迈出一大步,这样我们就可以再次迈出小步,但朝着更高的目标前进。

CircleCI:我们持续的使命

也就是说,我们充分利用了我们能保持的原则:我们突破,我们限时,当我们失败时,我们很快就失败了。我们构建了工具,因此我们可以在 1.0 和 2.0 上并行运行我们自己的构建,在不中断交付的情况下尽早获得反馈。2.0 的新代码完全建立在 2.0 之上,所以问题不会恶化。我们尽可能快地把一个封闭的阿尔法送到了几个客户手里。打着学习的旗号。

我们学到了。有设计缺陷和架构缺陷,甚至没有任何意义的决策。但这正是我们需要知道的,我们非常感谢有客户帮助我们解决这个问题。

一旦我们解决了早期的问题,我们就进入了封闭测试阶段。beta 版和 alpha 版之间的两个主要区别是:( 1)人们可以请求访问新平台,( 2)我们将测试大规模运营。扩展是它自己的一套学习,但每一步都有助于我们更快地获得更好的产品。最终,我们用公开测试版打开了闸门,这在几周前我们发布 CircleCI 2.0 时结束了。

在整个过程中,我们也尽力约束了范围。我们找到了合适的接缝来插入修改后的构建引擎,同时最小化其他地方所需的更改。我们还通过分支级别的配置公开了新的流程,因此我们可以在不中断客户日常软件交付的情况下征求真实的反馈。

所以,我们并没有完全抛弃我们的哲学;不,我们借了所有我们能借的来帮助我们度过一个不舒服和危险的分娩期。但是我们迫不及待地想回到我们对客户需求做出反应和快速发布代码的日常工作中。

不可否认的是,永远继续修补产品是很诱人的,陷入了无休止的完美主义的漩涡。但是我们知道我们需要回到我们的核心理念:不断为我们的客户提供价值。因此,我们承诺将 CircleCI 2.0 作为我们的默认平台,我们已经在它的基础上构建新的东西。

事实上,如果没有 CircleCI 2.0,我们就无法为您带来第一个使用新架构的主要功能 Workflows

探索陌生的新工作流程

以前的 CircleCI 固执己见:我们告诉你应该如何工作:构建、测试、部署——按照这个顺序。这些阶段被敲进了我们的配置中,无论您的组织喜欢与否,您都必须接受它。如果一次测试失败了,你就不得不从头开始,从头再来一遍。

但是 CircleCI 2.0 允许我们将这些步伐一致的阶段分解成松散耦合的工作,这让我们可以构建工作流。现在你不必每次测试失败都重新编译你的代码;只需从上一次失败的作业重新运行您的构建即可。更好的是,每个作业可以有一个相关的分支和并行级别。在较慢的测试上投入资源,在一些分支上运行测试,并且只部署 master…这个世界真的是你的可配置牡蛎。

我们不再告诉你如何做你的工作——你在告诉我们如何做你在 T2 的工作。

TL;速度三角形定位法(dead reckoning)

我们为我们所取得的成就感到自豪:基于 CircleCI 2.0 的构建速度非常快,强大的 Docker 支持意味着它可以更好地与我们的客户的工作方式保持一致,并且我们已经将更多的控制权交给了开发人员。但是我们不认为这个“释放”是这个故事的结束;这只是序幕。

现在我们有了这个坚实的基础,我们正跃跃欲试地在它的基础上进行建设。工作流只是我们将用 CircleCI 2.0 构建的一系列功能中的第一个。无论您已经与我们共事多年,还是刚刚加入我们,我们都很高兴能与您同行。

所以,不要再等待伟大的事情发生——去创造伟大的事情。

工程师安全培训- DefCon -捕捉旗帜| CircleCI

原文:https://circleci.com/blog/why-we-hired-two-defcon-hackers-to-teach-our-team-to-think-like-deviants/

[INT。-酒店宴会厅】2019 年夏天,拉斯维加斯卢克索酒店的一个寒冷、没有窗户的会议室。几十个人,大多数穿着黑色连帽衫,正趴在笔记本电脑前窃窃私语默认系统密码,并在 Docker 容器中寻找已删除的(据说是)password.txt 文件。由 Aphex Twin 制作的 Hedphelym 轻柔而充满威胁。

不,这不是传说中的 DefCon 夺旗比赛。但它被设计成模拟那个事件。这也是我们决定向 CircleCI 的整个工程团队教授安全编码的原因。

“谁知道安全培训不仅仅是 90 年代的剪贴画,坏人戴着滑雪面罩打字?“-CircleCI 平台副总裁 Michael Stahnke

Team_hacking.jpg

如何教授安全性

几年前,当我开始担任 CircleCI 的第一位安全工程师时,首席技术官 Rob Zuber 要求我处理的第一件事就是安全代码培训。他想让它变得有趣。他想让它在人们的胸中制造紧张感。几年前,他参加了谷歌的一次安全培训活动。在活动的一次演习中,他发现了自己服务中的一个漏洞。当他跑去修补时,他的心开始怦怦直跳。那次脉搏加速的经历改变了他对软件的看法。他想让我们所有的开发人员都体验一下。

我们考虑了所有能想到的培训,从在线课程到让整个部门飞往异地参加安全会议。他们都没有承诺同样的内心体验。

作为湾区 OWASP 会议的组织者和主持人,我得到了许多演示想法。一个来自匈牙利的一家小公司,名为 Avatao ,提供安全代码培训。我试了一下他们的一些培训模块,很快就知道,他们不仅可以展示,我们还找到了 CircleCI 培训的工具。这是动手打破东西,有一些指导。这感觉不同于手工静态代码分析游戏和我所知道的类似的东西。它从上至下捕捉概念,从 OWASP 十大应用程序到后端技术,如 Docker,并专注于打破这些东西。

该训练平台由三名安全研究人员设计,并非巧合的是,他们曾多次入围 CTF DefCon 大会。它包括几百个模块,从二进制代码开发到 SQL 注入,再到特定语言的问题。而且它是可扩展的。如果我们想要他们没有的东西,他们会构建一个模块或帮助我们编写一个。

完美的机会

当我的安全培训研究开始取得成果时,我们的工程和产品团队正忙着计划在拉斯维加斯进行为期一周的异地培训。第二天午饭后,我有四个小时的黄金时间来运行一个游戏,我知道这将是一个邀请 Avatao 与我们团队面对面工作的好机会。与我们安全团队的其他几个人一起,我们选择了 12 个模块,重点关注与我们工程师日常生活最相关的主题,如 Docker secrets、OWASP 十大漏洞,如跨站点请求伪造(我们已经在内部处理过)、保险库教程和默认密码。此外,我们还添加了一些真实世界黑客的复制品,比如脸书 Imagekick。

期待着这一事件的奖品购物是一个爆炸:一本开锁书,一套开锁工具,透明的璐彩特手铐和揭示其锁定机制的挂锁。

表演时间到了

在拉斯维加斯的第二天午餐后,阿瓦陶的人们在房间的顶端安装了两个大记分牌,开始了比赛,所有的参与者分成两人一组。结对编程是 CircleCI 文化的一个重要组成部分,所以我们也在活动中利用了这一点。作为一个转变,我们有意将工程师与他们通常不一起工作的人和团队配对,并根据经验水平组织他们。两人一组,每个人都有 E3/4 的平均技能水平,以防止一个高年级组践踏每个人。

活动开始几分钟后,有人指出这里太安静了。我有音乐吗?

碰巧的是,我做到了。当我还是私家侦探的时候,我整理了一份邪恶音乐的播放列表(想想迈克尔·曼的所有电影,美国黑帮,黑鹰坠落等等)。)的一个大项目,寻找与伯尼·马多夫庞氏骗局相关的资产。我把手机接到音响系统上,不得不说,一屋子咔哒咔哒的键盘让音乐听起来比我耳机里的更酷。

半小时后,我知道这是成功的。每个工程师都专注于他们的屏幕,没有一个人坐在后面聊天,房间里有一种竞争的感觉。幸运的是,投影屏幕上的两个记分牌显示每个队都在进步。我们的安全小组是随叫随到的助教,但他们都挤在一个角落里互相交谈。没人有问题。

Team_hacking2.jpg

两个小时后,我们颁发了奖品,与 Avatao 人员进行了问答,然后分成六个工程师小组,让他们讨论每个人学到的知识以及最适用于我们内部流程的知识。最后,每个人都搬到隔壁房间喝鸡尾酒,喝咖啡,做开锁练习。

Lockpicking.jpg lockpicking2.jpg

安全地吸取经验教训

那么,人们从他们的六人小组讨论中报告了什么?

该事件揭开了缓存中毒等具体问题是如何发生的一些神秘面纱。像 Imagetrick 这样对现实世界的黑客进行逆向工程是非常有趣的。这些模块很有挑战性,但并不令人沮丧。安全审查真的很重要。而且,几乎每个小组都要求在 Kubernetes 和 API 验证上添加特定的模块。

与一位首席软件工程师搭档的软件工程师布瑞恩·奈特说:“夺旗太酷了。”。"从一个首席工程师的角度来看,这很有趣."

软件工程师 Jacqueline Garcia 说,直接看到安全漏洞并花两个小时与队友讨论它们是很有趣的。这使得他们对在编码实践中实现安全性有了不同的想法。

“我最喜欢的是其中的合作,”她说。

关键要点:

  1. 专注于你的听众每天使用的技术模块。仅仅因为安全团队对深奥的漏洞感兴趣并不意味着其他人也会感兴趣。
  2. 保持练习简短,这样人们就不会陷入困境。重要的是让人们感觉他们可以做好安全工作,而不是让他们明白安全工作很难,应该留给专家去做。
  3. 召集一组来自整个部门的工程师,他们将通过提前试用来帮助制定课程。这不仅使模块与需求保持一致,还在活动期间创造了可以对问题进行分类的助教。
  4. 安全性不仅仅是代码。在编程的两边添加一些东西,比如开锁,以保持较高的乐趣系数。

如果您正在寻找保护 CI/CD 管道的方法,请查看我们关于持续集成的安全最佳实践的帖子。

工程管理- CircleCI 的工程能力矩阵| CircleCI

原文:https://circleci.com/blog/why-we-re-designed-our-engineering-career-paths-at-circleci/

我们希望 CircleCI 成为一个所有工程师都可以学习和成长的地方,无论他们加入我们的级别如何,也无论他们希望如何塑造自己的职业生涯,都可以得到支持。为了支持这一点,我们使用工程能力矩阵,这是一个概述工程师期望和成长道路的框架。这个矩阵对我们来说是一个非常有用的工具:它为我们的工作描述和面试过程提供了信息。它帮助我们与工程师一起设定期望,是设定目标以及关于学习和发展的对话的基础。这有助于我们进行更客观的绩效对话,更少受到工程师经理的偏见和技能的影响。总的来说,它阐明了我们组织的愿景,并帮助我们在招聘和职业发展的所有阶段保持一致性。三年前,我们开发了第一个能力矩阵。在 2018 年初,我们意识到是时候更新和重建我们的矩阵了,以更好地反映我们的工程组织是如何成熟的,以及我们在这一过程中学到了什么。

今天,我们发布了新的能力矩阵——首先,因为它是我们工程文化的重要组成部分,体现了我们的工程和公司价值观。我们希望它能帮助对加入我们感兴趣的人了解我们重视什么,以及他们如何在 CircleCI 发展自己的工程师职业生涯。第二,因为我们希望其他组织能够学习我们的经验,并且有一个创建反映他们自己团队价值观的矩阵的起点。这个系统是这样形成的。

背景

三年前,当我们开发我们的第一个工程能力矩阵时,我们是一个低开销的小型创业公司,专注于尽快交付代码。我们的开发人员还不到现在的三分之一,而且经验水平更低。

从第一个矩阵开始,我们已经成长为一个工程组织和公司。我们的需求已经改变,我们对优秀工程师的理解已经发展到包括更广泛的人际交往技能。协作现在是我们技术技能组合的一个关键原则,许多团队每天都在使用结对。我们还努力建立反馈文化,将其视为个人成长的宝贵工具。随着我们增加团队和部门,强大的沟通技能的重要性只会继续增加。

在找到任何答案之前,重建矩阵的过程产生了许多问题:我们发现自己正在评估,在某些情况下,重新定义我们行业对职业发展的期望,以及我们在工程文化中重视并希望在未来封装和培养的内容。

【组建团队?获取我们的远程入职员工必备清单

奠定基础

在我们进入这个项目之前,我们思考了创建它的过程。创造出反映我们团队价值观的东西,并且对团队成员的职业发展有意义和有用的最好方法是什么?

为了解决这个问题,我们的工程管理团队在整个项目过程中与我们的工程师保持密切沟通。在每一轮开发中,我们都依靠工程师的反馈,通过在我们的工程全体人员中举办反馈研讨会、试用性能评审以及由工程师、工程经理和人力资源团队成员组成的焦点小组等方式。这种方法对我们非常有用,可以确保我们始终如一地整合反馈,并开发出一个稳定、全面、包容的系统。

我们还决定了矩阵应该实现的一些关键结果。如果我们要取得成功,我们的矩阵将遵循以下属性:

  • 一致性。能力以及对如何展示能力的期望应该在不同级别之间保持一致。
  • 平易近人。即使不知道一个级别的所有细节,也应该很容易了解工程师将关注的工作领域和影响。
  • 简单。该矩阵应足够复杂,以体现成功职业生涯的诸多因素,但也应足够简单,以便用于有意义的职业发展对话。
  • 清晰的语言。词义要准确,有意义。描述符应该是明确的,清楚地表明所展示技能的频率或范围的变化。能力应该使用积极的语言(“做”而不是“能做”),因为它有助于关注活动而不是能力。
  • 直截了当的结构。我们希望开发一种结构,让人们能够看到全局,并根据需要深入了解细节。

价值观很重要

我们从一开始就非常清楚,我们希望我们的矩阵是固执己见的,符合我们对成功组织和实现工程职业的信念。我们的信念之一是职业道路和与我们一起工作的人一样多样化,我们希望以最好的方式支持每个人的成长。在我们努力为设定期望和支持工程师成长设定标准的过程中,我们建立了一些基本假设,这些假设构成了该矩阵的基础:

我们并不认为所有的工程师都应该(或想要)渴望成为经理。我们的工程能力矩阵从助理工程师的 E1 等级到首席工程师的 E6 等级不等。我们认为工程职业道路的“最终目标”不应该默认为管理——许多工程师乐于专注于工程师的成长,而管理工作需要一套完全不同的技能。我们目前正在为工程团队领导、经理和总监开发一个独特的能力矩阵。该矩阵将是一个独立的系统,具有与其他矩阵相互转换的路径。

即使是初级工程师也可以成为领导者。我们坚信领导力可以来自每个人,并希望鼓励我们所有的工程师培养领导技能。这就是为什么我们在我们的能力矩阵中特别强调领导技能,并且跨越每个级别(初级工程师的领导能力部分占整体技能的 17%,不包括其他部分的领导能力,如反馈、沟通和协作)。例如,我们的矩阵看到了测试技能的增长,从编写 E2 级别的测试到驱动策略和帮助团队改进 E6 级别的测试。在所有领域,随着时间的推移,工程师的工作重点从执行转移到促进、指导和辅导他人,无论他们是否处于管理角色。

增长不是线性的,也不是流线型的,因此,我们希望工程师在不同的水平上展现出不同的能力。我们的能力矩阵不是一个清单,而是一个指南,较小的偏差是可以预期的。我们将偏差作为对话的基础,讨论增长机会以及我们作为一个组织如何在这种增长中支持工程师,并讨论工程师如何利用特定优势进一步发展和支持他人,例如通过指导。

工程师随着时间的推移而成长通过扩大他们的影响领域,从任务到史诗、团队级别,通过与他们的团队和团队的业务利益相关者合作,到跨几个团队,最终跨整个组织。成长的第二个重要方面是更频繁地展示技能的能力——从频繁到通常到一般。

优秀工程师的素质远远超出了他们的编码技能。我们的经验以及我们与不同经验水平的工程师的交谈证实了这一点,通常越有经验的工程师花在编码上的时间越少。这就是为什么技术技能目前占我们要求工程师培养的所有能力的 20%,同样比例的技能用于反馈、沟通和协作。我们认为这些技能是成为一名优秀工程师的基础,并且与我们努力追求的工程文化相一致。我们重视优秀的工程设计,我们认为团队合作、共同学习和提供反馈是我们成为更优秀工程师的原因。

《黑客帝国》(电影名)

你可以在这里找到 CircleCI 的工程能力矩阵。矩阵附带了一套指南,定义了如何使用它,你可以在这份文件中找到。如果你有兴趣看看如何在 CircleCI 发展你的职业生涯,我们正在招聘

下一步是什么

四个多月来,我们一直在使用这一能力矩阵来制定与我们的 okr 一致的目标,并进行绩效评估。随着我们逐渐习惯这一新系统,我们也在收集关于能力矩阵、指导方针和流程的反馈,这有助于我们跟踪潜在的变革需求。我们的计划是在 2019 年初使用六个月后,作为一个团队再次审查该矩阵,并根据需要进行修订。

我们在招人!在此查看所有开放的工程职位。你对矩阵有反馈吗?很想听听大家的意见。请给我发电子邮件到 lena@circleci.com。

制定我们自己的能力矩阵?阅读我们希望一开始就知道的内容:构建工程能力矩阵的 7 个步骤

(在 Brad Henrickson 的《自信的承诺》播客中,聆听我们对有目的软件工程的最新见解。)

为什么我们使用 Om,为什么我们对 Om Next - CircleCI 感到兴奋

原文:https://circleci.com/blog/why-we-use-om-and-why-were-excited-for-om-next/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


作为前端开发人员,这是一个激动人心的时刻。脸书的反应颠覆了我们关于渲染用户界面的想法。特别是 Om ,开启了一种全新的思考用户界面工作方式的方式。过去几年是新思想和发展的高峰期。

但是现在,事情变得越来越好。今年,脸书的 GraphQL 和网飞的 Falcor 相继发布,紧随其后的是一个借鉴了他们的最佳创意的项目:Om Next。Om Next 是当前版本 Om 的继任者(现在被称为“Om Now”)。它保留了 Om 的优点,抛弃了不好的地方,并用一种更好的方法取而代之。

Om Next 目前在 alpha 中,但是一旦它发布,我们计划在 CircleCI 使用它。我想解释一下为什么这对我们来说如此激动人心。但是首先,我们需要看看我们为什么要走这条路。

为什么要反应?

自古以来(好吧,自从 JavaScript 出现以来),web 开发人员一直在为一个特殊的问题而奋斗:当新数据到达时,如何更新 UI?也就是说,如果你在显示一个列表,

  • 洋蓟

  • 卷心菜

  • 茄子

并且添加了两个新项——第一个和第二个之间的“花椰菜”,第二个和第三个之间的“莳萝”——如何更新 DOM 以反映这种变化呢?我们有两个选择,每个都很麻烦:

  1. 我们可以将新的列表项插入到现有的 DOM 中,但是找到合适的位置插入它们很容易出错,并且每次插入都会导致浏览器重新绘制页面,这很慢。

  2. 我们可以丢弃整个列表并一次性重建它,但是重新呈现大量的 DOM 也很慢。

为了使客户端 web 开发成为可能,我们必须解决——或者至少减轻——其中的一个问题。直到最近,大多数 JS 框架都专注于第一个问题,要么使 DOM 更容易导航,要么将元素绑定到负责保持它们最新的控制器对象。这些方法当然缓解了第一号问题,但是它们还没有解决 T2 的问题。通常,就像在主干中一样,他们将问题分散到许多小组件上。每个都只有一小块 DOM 要处理,但它仍然要处理。我们仍然有很多小的 DOM 更新来重新绘制页面。

React 解决了第二个问题。使用 React,我们假装重新呈现整个页面既快又容易。就 React 而言,它让我们假装我们正在重新呈现页面,而事实上,我们正在“呈现”一棵 JavaScript 对象树,称为“虚拟 DOM”,这是我们希望页面看起来像什么的完整规范。然后,React 将新的虚拟 DOM 与以前的 DOM 进行比较,以准确地找出在真实 DOM 中需要更改的内容,然后它只更改那些位,一次全部更改。结果是快速容易。

(如果你仔细看,这里面有猫腻。React only 看起来像是在解决第二个问题,但最终它真的解决了第一个问题:它要求我们假装重新呈现整个列表,以便它可以找到正确的位置来自动插入新项目。)

像这样简单的系统是很难超越的。在许多情况下,视图代码只是一个接收数据并返回 DOM(或者至少是虚拟 DOM)的函数。它是无状态和声明性的。直到你需要一点状态,也就是哪个 React 还能处理。今天,我无法想象用别的东西。

但在 CircleCI,我们不只是运行 React,我们还将代码库切换到 ClojureScript,并用 Om 编写我们的前端。ClojureScript 很容易销售:我们的后端已经用 Clojure 编写,并且已经有几个 ClojureScript 的 React 包装器。但是为什么特别是 Om 呢?

为什么是 Om?

Om 即使不是第一个 ClojureScript React 库,也是第一个。但当我们在 2013 年年中开始行动做出反应时,已经有好几个了,包括试剂静止。Om 给了我们什么其他人没有的东西?

React 关心的是数据如何流入页面上的每个组件,而 Om 也关心数据如何存储。Om 对应用程序状态的看法与 React 不同。试剂和静态的主要卖点是它们比 Om 更灵活。但是我们喜欢 Om 的观点,我们喜欢同意它所交换的东西。

  1. 应用程序状态作为一个单一的、不可变的数据结构。这既是入行的成本,也是(取决于你的看法)收益本身。整个应用程序状态作为单个数据结构存储在单个原子中。改变应用程序的状态总是涉及到原子。这意味着应用程序状态总是(原子地)从一个一致状态变为另一个一致状态。

Om 的创建者 David Nolen 喜欢炫耀这使得实现撤销变得多么容易:只要记住一个旧状态列表,你就可以在任何时候将状态原子添加到其中的任何一个。不过,我们喜欢它的另一个原因是:我们希望序列化应用程序状态,以便用户可以向我们的支持团队发送他们的 CircleCI 界面的快照。我们会弹出他们发送的数据,页面会将它反序列化并reset!到状态 atom 中,然后——很快——我们就会看到客户所看到的。

我们还做了另一个技巧:我们将应用程序状态树的一部分保存在浏览器的本地存储中。这是一种在你关闭应用程序后跟踪“粘性”事物的简单方法,比如你在侧边栏中折叠或展开了哪些 repos,或者你喜欢如何排序你的分支。我们观察路径[:settings :browser-settings],并将这些值同步到本地存储中的一个键。然后,在页面加载时,我们将数据从本地存储中取出,并将其放回应用程序状态。如果我们想要一个值持久化,我们只需要把它存储在树的那个部分,它就会自动变成“粘性的”。

sticky-app-state 应用状态树中存储的粘性设置。

sticky-local-storage

粘性设置自动同步到本地存储器。

  1. 光标。 Om 需要一种方法来导航这个巨大的数据结构,这就需要游标。假设您已经得到了上面的购物清单,反映在您的应用程序状态中。
(def app-state
  (atom {:grocery-list [{:name "Artichokes"}
                        {:name "Cabbage"}
                        {:name "Eggplant"}]})) 

假设用户点击一个编辑按钮,将“朝鲜蓟”改为“鳄梨”。不知何故,您需要swap!保存整个页面状态的单个原子,将正确的元素更新为新值。类似于:

(swap! app-state assoc-in [:grocery-list 0 :name] "Avocadoes") 

除此之外,编辑按钮被绘制为列表项目组件的一部分。该列表项不应该知道它表示其包含列表的第 0 项,或者该列表存储在:grocery-list。它也不应该知道应用程序状态存储在一个名为app-state的变量中。

解决方法如下:Om 让您传递一个游标,而不是传递您的组件常规数据({:name "Artichokes"})。光标包含您需要的三条信息:数据本身({:name "Artichokes"})、数据的路径([:grocery-list 0])以及数据所在的原子(app-state)。它神奇地表现得好像它只是数据,所以(:name item-cursor)"Artichokes",但是 Om 的om/update!函数知道如何获得另外两部分。现在,你要做的就是:

(om/update! item-cursor :name "Avocadoes") 

Om 隐藏了组件不需要关心的部分。

拉伸奥姆的接缝

Om(也就是 Om“Now”)是一个很好的开始,但它还不能完全支撑像我们这样的大规模 ClojureScript 应用程序。(怎么可能:这是在他们存在之前写的。)在 CircleCI,我们一直在发现 Om 的模型出问题的地方。

游标的难题:大多数数据不是树。

Om 的面向光标的架构要求你将应用程序的状态组织成一棵树,特别是一棵与你的 UI 结构相匹配的树。包含整个应用程序的根 UI 组件被赋予一个指向整个状态的根光标。然后,它将状态的子树(游标)传递给其子组件。上面,根组件会将(:grocery-list root-cursor)传递给某个grocery-list组件,该组件会将列表中的每个元素传递给某个grocery-item组件。状态的结构就是 UI 的结构。对于简单的应用程序来说,这非常有效。

但是考虑一下我们之前的技巧。UI 的哪个部分呈现了所有的“粘性”设置?none:UI 的任何部分都可能需要在那个分支中存储一两个设置以使其“有粘性”。记住,分支选择器需要存储每个回购的分支列表是折叠的还是展开的。如果分支选择器只被传递了[:dashboard :branch-list]中的列表,它将如何读写[:settings :browser-settings]?我们从很多角度反复探讨这个问题。事实证明,UI 元素有时有横切关注点。他们的数据并不总是映射到树上。

我们是如何解决这个问题的?我们的大多数组件都将整个应用程序状态作为它们的数据。父组件不仅仅将它们关心的部分传递给它们的子组件,而是将整个 enchilada 传递给它们。然后我们有在变量中定义的一系列路径,我们用它们来提取我们想要的数据。不太理想。但这是我们必须做的。

突变的管理:组件不仅仅改变它们自己的数据。

当需要更改数据时,Om 再次很好地解决了简单版本的问题,但很快就崩溃了。正如我们在上面看到的,如果杂货项目“朝鲜蓟”想要改变它的名字,这很容易:它可以在它的光标上使用om/update!。但是如果它想删除自己呢?它不能。那不会改变项目,那会改变列表。杂货列表组件可以通过将的光标更新为删除了“朝鲜蓟”的版本来删除该商品:

(om/update! list-cursor 
  (into [] (remove #(= "Artichokes" (:name %))) list-cursor)) 

但是在 UI 中,“删除”按钮应该是食品杂货组件的一部分。正如我们的数据不能完美地映射到我们的 UI,我们的变异操作也是如此。

Om 中这个问题的通常解决方案是 core.async 通道。杂货列表组件将建立一个通道,并将其传递给每个列表项。为了删除自身,item 组件会在该通道上放置一条消息。同时,list 组件将运行一个go块来监听消息,当它得到消息时,它将om/update!它的光标。

如果这听起来像是一个普通问题的复杂解决方案,你是对的:的确如此。但人就是这样。甚至 Om 的 TodoMVC 例子也诉诸于此。

在 CircleCI 应用程序中,我们做同样的事情,但我们做的规模要大得多。我们有一对多方法, frontend.controllers.controls/control-eventfrontend.controllers.controls/post-control-event! ,为整个应用程序处理这样的消息。(第一个根据需要转换状态树,第二个执行任何副作用。)这里有很多我们自己建造的建筑。我们尽了很大努力来避免 Om 自己的方法(游标),因为它们不符合我们在这个规模上的需求。

数据的交付:数据是如何进入 app 状态的?

当所有数据都存储在客户机上时,Om 工作得非常好。一旦有了服务器支持,事情就变得更棘手了。对于小型应用程序,比如杂货清单,你可以在页面加载时加载所有你需要的数据。但是在 CircleCI 这样的应用程序中,我们无法预先加载你可能想要看到的所有内容。我们按需装载物品。当您进入仪表板时,我们会加载最新的版本。当您转到一个构建页面时,我们会获取该构建详细信息。随着屏幕上组件的变化,我们发现我们需要不同的信息。

今天,我们的钩子是导航动作。我们的导航系统使用来自部长的路线,在此之上是一个多方法调度系统,非常类似于我们上面看到的control-event系统,称为navigation-event。例如,当用户导航到构建页面时,我们点击post-navigated-to! :build,这将触发我们获取构建细节所需的任何 API 调用。当这些 API 调用返回时,它们会将新数据swap!到状态树中。

与此同时,我们的组件试图呈现状态树中仍然为空的部分。这很好:当它是空的时,他们显示一个装载旋转器。当 API 系统swap!在数据中时,Om 在页面上重新呈现组件,显示构建。

这是一个很好的系统,但是它有一个主要的缺陷:它是构建页面组件,知道它需要什么数据来显示,但是它是导航系统,知道 T2 获取什么数据。这些代码在代码库中相距甚远。如果我们希望构建页面显示一些新的东西,我们必须将它添加到构建页面的 UI 上添加一个对导航系统的 API 调用。如果我们从构建页面中移除某些东西,我们很容易忘记从导航系统中移除它,并在每次用户查看构建时进行不必要的 API 调用。

幸运的是,未来是光明的。

为什么接下来是 Om?

Om 接下来是即将到来的 Om 改版。其实更多的是重启。David Nolen 和 Om 团队采用了 Om 背后的原则,应用了过去几年的经验,并建立了一些新的东西。它目前还处于测试阶段,还没有准备好投入生产,但应该会在几个月后准备好。当它是,我们计划迁移到它。何必呢?很高兴你问了。

树实际上是一个图形。

在 Om Next 中,每个组件都确切地声明它需要什么数据。它使用类似于 Datomic 的拉查询语法的语法声明了一个查询。不像树中的简单路径,像这样的查询可以在中导航。(事实上,Om Next 的查询类似于脸书的 GraphQL ,并且部分受到其启发。)

例如,也许某个组件需要显示当前用户最近启动的构建之前的构建的开始时间。你永远不会将这些信息存储在路径[:current-user :initiated-builds 0 :previous :start-at]下的树中。您不会将您的实际构建记录嵌套在其他构建的:previous引用中,就像这样:

{:current-user
 {:initiated-builds 
  [{:id 146
    :repo-name "circleci/frontend"
    :start-at #inst "2015-12-17T17:13:59.167-00:00"
    :previous {:id 144
               :repo-name "circleci/frontend"
               :start-at #inst "2015-12-17T17:05:58.144-00:00"
               :previous {:id 141
                          :repo-name "circleci/frontend"
                          :start-at #inst "2015-12-17T17:05:13.512-00:00"
                          :previous ; and so on...
                          }}}
   {:id 145
    :repo-name "circleci/docker"
    :start-at #inst "2015-12-17T17:09:25.961-00:00"
    :previous {:id 143
               :repo-name "circleci/docker"
               :start-at #inst "2015-12-17T17:05:36.797-00:00"
               :previous {:id 138
                          :repo-name "circleci/docker"
                          :start-at #inst "2015-12-17T17:04:51.124-00:00"
                          :previous ; and so on...
                          }}}]}} 

那就太傻了。你可以把它保存在构建列表中。说,[:builds 145 :start-at]。您的数据将如下所示:

{:current-user {:initiated-builds [[:build/by-id 146]
                                   [:build/by-id 145]]}
 :build/by-id {146 {:id 146
                    :repo-name "circleci/frontend"
                    :start-at #inst "2015-12-17T17:13:59.167-00:00"
                    :previous [:build/by-id 144]}
               145 {:id 145
                    :repo-name "circleci/docker"
                    :start-at #inst "2015-12-17T17:09:25.961-00:00"
                    :previous [:build/by-id 143]}
               144 {:id 144
                    :repo-name "circleci/frontend"
                    :start-at #inst "2015-12-17T17:05:58.144-00:00"
                    :previous [:build/by-id 141]}
               143 {:id 143
                    :repo-name "circleci/docker"
                    :start-at #inst "2015-12-17T17:05:36.797-00:00"
                    :previous [:build/by-id 138]}
               ;; and so on...
               }} 

你不能用光标来导航。一旦你缩小到:current-user和它的:initiated-builds,你就不能构建#146:它在树的不同分支。一旦你缩小到 146 号版本,你就不能退回去找它之前的 145 号版本了。原来你的数据其实不是一棵树,而是一张图表。

查询可以让你从各个方向浏览你的约会图表。Om 接下来的做法非常聪明:它采用类似上面的结构,将您需要的数据反规格化到您期望的位置。你的原始数据是一个图,但是你的 UI 看到的是一棵树,一棵和你 UI 的结构完美匹配的树。在 Om Next 中,您的查询可能类似于:

[{:current-user [{:initiated-builds [{:previous [:start-at]}]}]}] 

此外,根据您的设置方式(Om Next 非常灵活),您可能会得到一个如下所示的树:

{:current-user
 {:initiated-builds
  [{:previous {:start-at #inst "2015-12-17T17:05:58.144-00:00"}}
   {:previous {:start-at #inst "2015-12-17T17:05:36.797-00:00"}}]}} 

注意,在原始数据中,每个构建都引用了前一个构建,但是在这个响应中,我们没有无限深的递归。为什么?因为我们只得到我们在查询中请求的内容。我们要求深入一层,这就是我们得到的。

在 Om Now 中,你将你的应用程序状态存储在一个必须与你的 UI 形状相匹配的树中。在 Om Next 中,你以任何有意义的形式存储你的应用程序状态,并让 Om(在你的帮助下)将这些数据转换成一个与你的 UI 匹配的树。当用户界面的形状改变时,查询的形状也会随之改变,因此它接收的数据的形状也会自动改变。你的用户界面并不驱动数据的形状。这是一个巨大的胜利。

变异是一流的操作。

因为您不再以数据存储的形式接收数据,所以您不再像以前那样直接操作数据:使用游标。相反,Om 接下来要求您定义一组可以改变应用程序状态的变异操作。这些变化被命名,它们做什么在组件本身之外被定义,作为 Om 接下来称之为“解析器”的一部分。

听起来熟悉吗?这正是我们已经在 circle cifrontend.controllers.controls中构建的,只是这个版本不涉及 core.async 诡计,也不需要维护一个其他人从未见过的大型定制架构。显然,我们的方向是正确的,但是将这一点嵌入到 Om 本身会更好。

您的组件知道它们需要什么。

还记得我们从服务器获取数据的问题吗?我们必须挂钩导航事件,然后猜测我们的组件将要显示什么数据。不会了。在 Om Next 中,我们的组件有查询!我们可以问他们需要什么。我们没有将 API 调用绑定到导航事件,而是将 API 调用绑定到应用程序状态的一部分(比如[:current-user :initiated-builds])。如果我们试图显示一个需要知道当前用户发起的构建的组件,这将触发一个 API 调用,向服务器请求数据。如果我们停止使用那个组件,我们就停止了这个请求。自动地。

因为数据和 API 之间的映射集中在解析器中,所以我们可以将请求分成更少的 API 调用,从而提高性能。我们甚至可以改变哪些 API 提供哪些数据,而根本不用接触 UI 组件!

这就是所有大惊小怪的原因

Om Next 让我们兴奋不已,原因有很多。它证明了我们早期做出的一些重大决定,它承担了我们必须定制的许多架构的责任,并且它将极大地简化我们编写大多数前端应用程序的方式。当然,现在最难的是等待它准备好。

与此同时,我们将制定出最好的方法,逐步将我们的应用从现在的 om 迁移到下一个 Om,消除错误,并希望改善每个人的迁移路径。就像我说的:作为一名前端开发人员,这是一个激动人心的时刻!


如果你对这一切感到兴奋,并想自己尝试一下,我强烈推荐托尼·凯的 om 教程。在 Om wiki 上还有几个更有针对性的教程。(以上都是正在进行的工作,因为 Om Next 仍在不断变化,但它们仍然值得一试。)

为什么我们不再使用 Core.typed - CircleCI

原文:https://circleci.com/blog/why-were-no-longer-using-core-typed/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


2013 年 9 月,我们在博客中讨论了为什么我们支持类型化 Clojure,您也应该这样做!现在,两年后,我们的工程团队已经集体决定停止使用类型化 Clojure(特别是 core.typed 库)。作为这个决定的一部分,我们想写一篇关于我们使用 core.typed 的经验的博文。

我们决定停止使用 core.typed 的原因是因为我们发现使用它的成本大于我们获得的收益。当然,这是一个主观的观点,所以我们将在下面详述我们的推理。

core.typed 库是类型的 Clojure 项目的一部分。它是一个为 Clojure 代码添加可选类型的库。Core.typed 允许开发人员向 Clojure 表单添加类型注释,然后可以运行类型检查过程来验证程序的类型信息。

首先,介绍一下我们项目的背景。我们的主应用程序的 repo 包含 93,983 行 Clojure 代码。我们的主应用程序的src目录包含 369 个名称空间,其中 84 个用 core.typed 进行了注释,285 个没有。

因此,我们的代码库大致分为三组:带类型注释的代码,带类型注释并带有 ^:no-check修饰符的代码,以及不带类型注释的代码。

我们在使用类型化 Clojure 时遇到的问题是编程时迭代时间较慢,core.typed 不支持整个 Clojure 编程语言,以及使用没有用 core.typed 注释进行注释的第三方库。

更慢的迭代时间

当在 Clojure 中开发时,我们的团队在 REPL 交互地编写代码。我们希望能够编写代码,然后立即运行测试。在我们的代码库上使用 core.typed 时,运行类型检查器需要很长时间。在我的机器上,(一台 3GHz i7 MacBook Pro)对单个名称空间进行类型检查需要 2 分钟。这导致了代码领先于类型注释的情况。我通常同时编写代码和相应的测试,快速迭代。当使用 core.typed 时,我会在我写的代码中添加类型注释,然后运行类型检查器,因为两分钟的运行时间太慢,不能交互运行

类型检查需要 2 分钟的原因是因为它重新扫描我们项目中的所有文件来收集类型注释。我们确实在一段时间内更改了 core.typed 设置,以便在运行类型检查器时只重新扫描当前文件。这改进了类型检查器的运行时间,使其接近瞬时,但是这阻止了我们处理两个相互依赖的文件。我们发现修改一个文件中的类型注释并在另一个文件上运行类型检查器在这个设置中是不可能的——对第一个文件的更改被忽略了。

Core.typed 没有实现整个 Clojure 语言

我个人在使用 core.typed 时的委屈是,它没有实现整个 Clojure 语言。有些函数不能进行类型检查,例如get-incompor的一些用法。这意味着在编写了一些代码之后,我们经常不得不回去重新编写一些表单来满足类型检查器的要求。

一个特别的例子是,我需要取出一个表单,该表单接受一个函数f并返回:

(comp keyword f name) 

(在调用namekeyword之间编写我的函数f)。这段代码不能被 core.typed 检查,所以我不得不将表单提取到一个顶级函数中,并添加一个类型注释(并添加一个注释来解释我为什么这样做):

(t/ann keyword-transformer
  [(t/IFn [String -> String]) -> (t/IFn [t/Keyword -> t/Keyword])])

(defn- keyword-transformer [f]
  (fn [x] (-> x name f keyword))) 

我的沮丧不是因为有 core.typed 无法检查的代码,这是可以理解的。问题是我们不能轻易地区分代码中的类型错误和 core.typed 不能检查代码的情况。这两种情况下产生的错误消息是相同的。

以下是上面的comp示例产生的错误示例:

Polymorphic function comp could not be applied to arguments:
Polymorphic Variables: x y b
Domains: [x -> y] [b ... b -> x]
Arguments:
  (t/IFn [(t/U t/Symbol java.lang.String t/Keyword) -> t/Keyword]
         [java.lang.String java.lang.String -> t/Keyword])
         [java.lang.String -> java.lang.String]
         [(t/U clojure.lang.Named java.lang.String) -> java.lang.String]
Ranges: [b ... b -> y]
with expected type: [t/Keyword -> t/Keyword] 

当面临这样的问题时,用户不清楚被检查的代码是否包含导致类型检查失败的错误,或者代码是否暴露了 core.typed 可以检查的内容的限制。我解决这类问题的第一个尝试是以几种不同的形式重写代码,看看是否能通过类型检查。这就是 2 分钟迭代时间变得令人沮丧的地方——我会做一点小小的改变,提取出一个表单并添加类型注释,然后运行类型检查器。如果类型检查器再次失败,我将不得不重复提取表单和添加类型注释的过程。每一次迭代将强加另外 2 分钟的等待时间。

在几次失败的迭代之后,我会假设问题是 core.typed 本身的限制,并在我的代码中添加一个^:no-check注释,然后继续。我发现我经常选择添加一个^:no-check注释,而不是花时间去诊断潜在的问题。每一个带有^:no-check注释的表单都会削弱类型系统的保证,并允许 bug 绕过类型检查器。

我们在 CircleCI 遇到的另一个问题是,函数的类型签名不能用 core.typed 进行类型检查,所以我们必须将返回类型注释为 Any 。我们的apply-map函数就是一个很好的例子——该函数类似于 apply ,但是它可以用于需要一组关键字参数作为 rest 参数的函数:

(t/ann ^:no-check apply-map
  [(t/IFn [t/Any -> t/Any]) t/Any * -> t/Any])

(defn apply-map
  "Takes a fn and any number of arguments. Applies the arguments like
  apply, except that the last argument is converted into keyword
  pairs, for functions that keyword arguments.

  (apply foo :a :b {:c 1 :d 2}) => (foo :a :b :c 1 :d 2)"
  [f & args*]
  (let [normal-args (butlast args*)
        m (last args*)]
    (when m
      (assert (map? m) "last argument must be a map"))
      (apply f (concat normal-args (apply concat (seq m)))))) 

我们无法让这个函数使用 core.typed 进行正确的类型检查,所以我们不得不将它注释为返回Any(apply的类型签名相当复杂)。像这样使用Any类型具有传染性——当调用apply-map时,所有类型信息都会丢失——所以任何调用apply-map的函数都不能正确地进行类型检查。我们总是必须用^:no-check标志来注释这些函数,这意味着任何调用apply-map的函数都不会进行类型检查。在 Circle 中,我们大量使用这种风格的关键字参数:在我们的代码库中有 115 个对apply-map的调用,这将是 115 个我们不能进行类型检查的函数,这再次削弱了整个类型系统。

为某些函数编写类型注释可能非常困难——在搜索我们的代码时,我发现了一个特别的注释,我觉得它代表了我们在必须添加^:no-check注释时的感受:

(t/ann ^:no-check all-granted-by
  [t/Hierarchy t/Keyword -> (t/Coll t/Any)])
;; sigh. i still can't figure out how to write moderately 
;; complicated core.typed checks for stuff like this. 

第三方代码

我们使用的第三方库都没有使用 core.typed 进行注释。这意味着我们必须自己为这些第三方库创建和维护一个类型注释列表。我们维护一个文件,其中包含我们所依赖的第三方代码的类型注释。每当我们添加对以前没有调用过的库函数的调用时,我们都需要为该函数编写一个注释。我们编写的类型注释不会被 core.typed 检查(它们被标记为^:no-check)。所以我们需要非常确定这些注释是正确的。如果这些注释被添加到库本身,那么它们将被检查类型。我们已经提出将我们的注释添加回一些项目,但是对于那些自己不使用 core.typed 的项目维护者来说,接受一个添加类型注释的补丁并没有什么好处。额外的维护开销是不值得的,core.typed 会给库增加额外的依赖。

我们的未来- schema.core

现在,我们已经禁用了我们的单元测试,该单元测试将对我们用 core.typed 注释的名称空间运行类型检查器。

我们发现类型检查中最有价值的代码是映射中的字段访问,因为映射是“字符串类型的”(或者对 Clojure 更准确地说,“关键字类型的”)。在这些领域,我们已经采用了棱镜模式,其方式由杰西卡·克尔在最近一集的认知广播中描述。我们将schema.core定义添加到我们想要进行类型检查的函数中。这些大多是将地图作为参数之一的函数。当运行我们的测试时,我们使用schema.test/validate-schemas测试夹具来启用运行时模式检查。对于一些函数,我们还启用了^:always-validate标志,以确保在生产中也检查模式。

使用 schema.core 允许我们逐渐地向我们的代码添加模式检查,而没有我们在使用 core.typed 时遇到的一些问题。为了这种灵活性,我们必须放弃编译时类型检查,而是依赖于运行时类型检查,这延迟了我们在代码中发现类型错误的时间点。模式定义确实给了我们 core.typed 给我们的相同的结构化文档,这是我们真正喜欢的东西。

工作流的广阔世界:控制圈

原文:https://circleci.com/blog/wide-world-of-workflows-control/

在我们之前的文章中,我们探讨了作业编排的好处,或者你是一次运行一个作业还是并行运行;以及多执行者工作流,这是一个让您在一个工作流中跨不同平台、语言和资源类运行作业的功能。现在,我们将探索各种方法来进一步控制您的工作流,使用分支和标签过滤,以及不同的批准选项。

什么是过滤?

有时候,在每次推送时构建整个工作流并不理想。例如,希望仅从特定分支(即主服务器)进行部署,或者如果您有一个大型 monorepo 并希望节省时间。解决这个问题的一个方法是过滤。

工作流中存在两种类型的过滤器:按分支过滤按标签过滤

分支和标记筛选是控制作业何时运行的方法。通过过滤,您可以为您定义的分支或标记部署或运行测试。这意味着您可以设置一个过滤器,说明“仅在满足此条件时运行此作业”。使用筛选器,您可以创建规则并仅基于这些标准运行作业。这些标准可以在作业配置本身下定义。例如:如果您想从您的默认分支部署到生产,您可以使用过滤来过滤所有其他分支并阻止它们运行。您可以创建条件和规则,仅从您选择的分支或标记运行部署作业。

注意:分支和标签过滤器可以在特定于作业和特定于工作流的级别上运行。关于工作流级过滤的例子,请看我们的 CircleCI 图片

workflows_datadog.png 参见 DataDog 的 config.yml 此处

把标签想象成书签。您的 git 提交历史一直都在发生,您可以将标记放在您可能想要返回的关键位置。DataDog 正在使用标签来标记他们的版本,因此他们的团队和他们的客户都可以跟踪版本和发行说明。标签有助于保存重要事物的历史记录。

在这个例子中,他们使用标记来运行所有的作业,除了他们的publish_master作业,它只为他们的主分支运行。当它们推送时,它们会推送一个 git 标签,这会触发一个工作流及其后续作业。如果标记与配置中该标记的正则表达式中定义的模式匹配,则执行工作流及其后续作业。

workflows_googlecloud.png_ 见谷歌云的 config.yml 这里 _

在这个例子中,在 Google Cloud 上工作的团队正在对他们所有工作的标签进行正则表达式匹配。运行作业时,团队不仅可以定义分支级别的过滤器,还可以定义标记级别的过滤器。在 Google Cloud 的例子中,他们使用正则表达式来匹配他们的标签:匹配正则表达式的标签就是将要构建的标签。与这些表达式不匹配的工作将不会建立。

workflows_aeternity.png 此处

在这里,Aeternity 团队使用调度功能在不同的时间为不同的分支机构运行工作流。在这个例子中,他们在晚上运行他们的integration_deploy工作,同时在午夜运行系统测试。对于您希望在特定时间运行的作业,以及长时间运行的作业,将它们安排在工作日结束时运行可能是一个明智的选择,因为您知道您将有足够的带宽。例如,如果你有两个容器,你要在中午运行这些作业,你的团队可能会发现他们在容器上等待,而你永远不希望你的团队在机器上等待

例 4: 天蓝色

workflows_azure.png 见 Azure 的 config.yml 此处

该团队正在运行两种不同的工作流:其 PRs 的工作流和其主分支的工作流。在名为build_and_test_PR的工作流中,他们使用手动批准特性来控制在不同 Kubernetes 环境中运行的作业。这样,他们就不会在每次提交时都运行作业,而是在手动批准后才运行作业。一旦这些工作被批准,他们将会遇到所有不同的 Kubernetes 环境。使用手动批准对长时间运行的作业或非常耗费资源的作业很有好处。此外,在呼叫第三方服务或试图保护计算时,批准也很方便。在这些情况下,您可以设置您的工作,以便来自 QA 或产品团队的批准者需要给予许可。

我们已经向您展示了如何建立工作流的各种方式的示例,以利用各种作业编排设置,在多个平台和跨执行者运行工作流,以及控制运行作业的流程。在我们系列的下一篇文章中,我们将看看我们自己的工作流程,向您展示我们如何构建 Docker 便利图像

工作流程的广阔世界:我们如何构建 Docker 便利图像- CircleCI

原文:https://circleci.com/blog/wide-world-of-workflows-how-we-build-our-docker-convenience-images/

我们的工作流博客系列一直在探索工作流的一些主要功能集,包括作业编排多执行器支持控制流,重点介绍了 CircleCI 客户的开源示例,如 ArtsyDataDog 、脸书( React Native 、谷歌(谷歌云Mapbox

最后,我们想把焦点放在我们自己身上,谈谈我们如何在内部使用工作流。我们的大部分代码库都不是开源的,但是我们的 circleci-images 库是开源的——这是一个很好的例子,展示了您可以使用整个工作流工具集来完成什么。

什么是 circleci-images?

我们使用 circleci-images 来构建和部署我们的 Docker 便利图像,这些图像每天被使用数千次。

由于如此多的客户依赖于这些映像,我们使用扇入/扇出、具有不同执行环境的作业、cron调度、基于分支的过滤器和多个工作流等功能,构建了一个高效可靠的映像构建管道。

我们的 config.yml 文件也大量使用了 YAML 锚,并且,作为我们部署过程的一部分,除了 circleci-images repo 本身之外,我们还整合了多个 GitHub 存储库,并最终将映像推送到 Docker Hub 上的 stagingproduction 组织。

让我们深入探讨一下所有这些不同的部分是如何组合在一起的:circleci-images repo 中的源代码是如何作为 Docker 映像部署到 17 个不同的 Docker Hub 存储库中的,每个存储库中都有自己特定的映像变体集?更重要的是,我们为什么要这样建造它?

circleci 影像之旅

在持续集成和交付管道中跟踪代码通常从一次提交开始。然而,对于计划的工作流,事情并不总是那么简单。在这种情况下,我们的 circleci-images config.yml 从解决两个不同的用例开始:
1)我们希望构建和部署夜间映像。

为什么?因为构建 Docker 映像通常需要引入许多上游依赖项,我们的映像当然也不例外。我们根据映像和变体在 Docker 映像中安装各种语言、框架、实用程序和应用程序,从常见的 CLI 工具如curlgitjq;浏览器测试包,如 Chromedriver 或 PhantomJSDocker、Firefox 和 Chrome 等应用程序。如果该软件有新的补丁或稳定版本,我们希望尽快更新我们的便利映像,以便为我们的用户提供最安全、最稳定的平台。预定工作流程让这一切成为可能。2)我们希望在每次提交时构建、测试和部署映像。

这是更典型的场景:每夜构建很重要,但获得代码变更的即时反馈也很重要。

为了适应这两种用例,我们在我们的 config.yml 中使用了两种不同的工作流:一种夜间build_test_deploy工作流,它在世界协调时每天午夜运行,但只在 circleci-images 的master分支上运行

workflows:
  version: 2
  build_test_deploy:
    triggers:
      - schedule:
          cron: "0 0 * * *"
          filters:
            branches:
              only:
                - master 

—以及一个常规的commit工作流,为每个新的提交处理我们的控制流。然而,除了几个例外,这些工作流运行相同的一组作业,这是多工作流配置的一个重要特性:定义一个作业一次,您可以在任意多个工作流中重用它,这有助于保持您的 config.yml 简洁明了。

既然我们已经解决了夜间工作流场景,让我们继续关注从提交到部署的代码变更。对于给定的提交,我们运行几个初始作业,以编程方式为我们将要构建的所有映像变体生成 docker 文件,因此如果 shell 脚本出现问题,并让作业负责这项工作,工作流就在那里停止。

如果一切顺利,我们就展开下一组工作,做实际的形象塑造工作。这些作业利用了我们基于分支的过滤特性,因为我们只想在提交到stagingmaster分支时构建并推送 Docker 映像。同样,我们在这里使用 YAML 锚点,以避免在一个又一个作业中手动重复相同的工作流过滤器:

workflow_filters: &workflow_filters
  requires:
    - refresh_tools_cache
  filters:
    branches:
      only:
        - master
        - production
        - parallel 

每个形象塑造工作看起来或多或少都像这样:

 publish_image: &publish_image
    machine: true
    working_directory: ~/circleci-bundles
    steps:
      - checkout
      - run:
          name: Docker Login
          command: docker login -u $DOCKER_USER -p $DOCKER_PASS
      - run:
          name: Build and Publish Images
          command: |
            export COMPUTED_ORG=ccistaging
            if [[ "$CIRCLE_BRANCH" == "production" ]]
            then
              export COMPUTED_ORG=circleci
            fi
            export NEW_ORG=${NEW_ORG:-$COMPUTED_ORG}

            make -j $PLATFORM/publish_images
      - store_artifacts:
          path: "."
          destination: circleci-bundles 

这个 YAML 锚点允许我们重用我们的映像构建逻辑,只需传入一个不同的环境变量来指定我们在特定作业中构建哪个映像:

 publish_node:
    <<: *publish_image
    environment:
      - PLATFORM: node 

与传统的扇出/扇入工作流不同,在传统的扇出/扇入工作流中,可以扇出来运行测试,然后扇入来运行一个离散的部署作业或一组作业,这里我们的部署逻辑是在每个单独的映像构建作业中执行的(通过 Make targets 处理的)。虽然我们在 Docker Hub 上托管我们的便利图像,但我们在 CircleCI 上构建它们——我们发现这比使用 Docker Hub 构建每个图像的每个变体要快得多。

正如你所看到的,我们最近更新了一些 Docker Hub 存储库——以[circleci/golang](https://hub.docker.com/r/circleci/golang)为例——我们使用 Docker Hub 为每个存储库只构建了一个示例映像,这为我们提供了 Docker Hub 的自动构建功能所提供的集成 Dockerfile 和 README 支持。

要构建所有这些图像,可以使用远程 Docker 环境执行程序。如果我们的映像构建作业有一些专门的逻辑,需要安装特定的依赖项或框架或语言,或者在构建之前运行一组特定的命令,那么 Remote Docker 可能是最佳选择,因为它允许我们使用 Docker 映像作为主要的构建容器,并将与 Docker 相关的命令发布到远程 Docker 环境。

然而,由于这些作业完全由 Docker 相关的命令组成,所以使用machine executor 更容易。这很好地说明了多平台工作流的威力——为每项工作选择不同的执行环境为您提供了高水平的定制,从而实现了更加简化、优化的构建/测试/部署流程。

想深入挖掘吗?因为作为我们方便的图像构建过程的一部分使用的所有存储库都是开源的,所以您可以确切地看到它是如何工作的:

工作流程-作业编排| CircleCI

原文:https://circleci.com/blog/wide-world-of-workflows-job-orchestration/

我们构建了工作流,让团队可以自由地以他们选择的任何方式运行他们的构建。工作流支持大量的定制,但是也需要做出许多决定:您应该选择顺序作业还是并行作业?你应该如何处理跨多种语言的测试?如果测试失败,您应该设置任何自动操作吗?

我们希望展示工作流可以为您做的许多事情中的一些。但是,我们并不只是告诉您如何设置配置,而是想向您展示真正的开发团队现在是如何使用工作流来发布应用程序的。因此,我们梳理了许多使用 CircleCI 2.0 的开源项目,以便让您访问这些团队正在使用的真实配置文件。如果你看到对你的团队有用的东西,你会知道它是如何建立的。

我们将这些例子分成三种类型的任务,其中有一个额外的帖子:

  1. 工作协调(此职位)
  2. 多执行者
  3. 控制
  4. 我们如何在 CircleCI 使用工作流程

在接下来的四篇博文中,我们将带您了解工作流的各种配置以及团队使用它们的原因。

工作流基础

对于不熟悉工作流的人,我们来定义一些关键术语:

  • 工作流定义工作(如构建、测试、部署)如何运行的一组规则,给予团队对软件开发过程的细粒度控制
  • 作业是步骤的集合和运行它们的执行环境
  • 单步执行可执行命令

好了,让我们来看看一些工作流程!

开源示例

我们要探索的第一个概念是作业编排,或者说,设置您的作业是顺序运行还是并行运行。这两者都有很好的理由,所以让我们探讨一些例子。

例 1: Artsy

workflow_artsy.png 见 Artsy 的 config.yml 这里

这是顺序工作流的一个例子(尽管不完全是顺序的——它们并行运行它们的deploy_herokubuild任务)。他们的工作流包含一个简单的构建-测试-部署序列,在此序列中,他们在部署到生产环境之前先部署到试运行环境。如果您有兴趣在构建和部署之前运行您的测试,或者从部署到试运行和生产,这是一个很好的例子。顺序运行的一个好处是,如果您在部署到登台时发现了问题,您可以在进一步操作之前解决该问题。使用顺序作业,您可以获得增量反馈,并可以在任何阶段进行更改。

workflows_mapbox.png 见 Mapbox 的 config.yml 这里

在这个例子中,Mapbox 跨多个作业运行测试,以获得更快的反馈。他们同时运行集成测试、单元测试和代码 lint 测试,以便在合并到主测试和部署之前获得不同测试套件的反馈。这将推荐给那些希望通过一次推送运行所有测试来优化测试套件总运行时间的团队。对于大多数团队来说,这是一个很好的选择。

一些附加说明:

如果您听说过术语扇入和扇出,我们已经讨论过它们的作用了!在 Mapbox 的例子中,他们从prepare作业开始工作流,然后分散到并行运行的测试套件中,然后再回到deploy作业中。

如果我的测试失败了怎么办?

无论您选择并行运行还是顺序运行,如果您遇到一个或多个作业失败的问题,您会怎么做?

有几个选项:

  • 从头重新运行:您可以使用相同的提交来触发新的工作流,从而从头重新运行您的工作流。
  • 从失败重新运行:如果您认为您有一个不可靠的测试,或者您有一个没有正确部署的作业,您不必重新运行整个工作流。相反,只要从失败的工作开始。

使用作业协调选项可以更快地获得关于哪些工作正常、哪些工作失败的反馈,以便您可以快速做出更改并继续发货。在我们博客系列的下一篇文章中,我们将会看到使用多执行者工作流的开源项目。

工作流-多执行者| CircleCI

原文:https://circleci.com/blog/wide-world-of-workflows-multi-executor-support/

在我们的上一篇文章中,我们讨论了并行和顺序作业编排,以及为什么团队可能会选择其中之一。在本帖中,我们将深入探讨多执行器工作流。

我们所说的多遗嘱执行人是什么意思?

我们有时称之为多平台支持,这意味着您可以灵活地在 Linux 或 macOS 等不同平台上运行您的作业。工作流为团队提供了相同的模块性,以便在相同的工作流中运行不同的语言。此外,它们允许您从多个资源类中进行选择,以适合您的工作。

什么是资源类?

资源类使您能够为每个作业配置 CPU 和 RAM。这意味着您可以选择所需的电量。我们经常听到客户说,内存限制会导致他们的测试失败,或者导致他们的构建过程变得非常慢,或者耗尽他们的 RAM,从而导致浏览器崩溃。有了资源类,你可以在需要的地方选择一个大容器,在不需要的地方选择一个小容器。

workflows_facebookreact.png 见脸书的 config.yml 此处

脸书 React 的仪表板是跨多个平台运行作业的工作流的例子。在检查完代码后,他们正在 Android、iOS、tvOS 上运行测试,并运行端到端测试。这有助于团队同时从不同平台获得反馈。对于上下文,在 CircleCI 1.0 中,您必须将其中的每一个作为不同的构建来运行,但现在通过工作流的组合和在作业中启用不同的平台,您可以一次推送并在多个平台上测试。如果您的团队正在构建跨多个平台的应用程序,这将是一个不错的选择。

给 Android 开发者的特别提示:脸书 React 在更大的机器上运行他们的工作,以考虑 Android 开发经常带来更多内存需求的事实。这项工作运行在一个比其他工作更大的机器上。

workflows_envoyproxy.png 见特使代理的 config.yml 此处

Envoy Proxy 是团队如何利用多平台工作流从不同平台的不同语言获得反馈的另一个例子。只需轻轻一推,他们就可以跨多个平台(macOS 和 Linux)进行测试。他们使用或大或小的资源类,并在不同的环境中进行测试,以查看跨平台的工作是否如预期的那样。使用 CircleCI 资源类,您可以同时在一个大机器和一个小机器上运行——因为对所有作业使用一个大机器并不总是最明智的选择。在这个例子中,Envoy Proxy 的所有 Linux 作业都使用 XL 资源,而其中一个计算不太密集的作业使用一个小的资源类。现在使用这种多平台工作流,他们可以一次推送,同时在两个平台上构建,同时还可以跨所有工作进行测试。

我们已经看了几个例子,展示了团队如何跨多种语言、操作系统运行,以及如何利用各种资源类来适应特定的工作。

在我们的下一篇文章中,我们将探索流程控制:在你的工作流程中使用过滤和批准的不同方式。

Windows CI - Windows 支持| CircleCI

原文:https://circleci.com/blog/windows-general-availability-announcement/

在 CircleCI,我们帮助公司加速创新。虽然 2019 年 StackOverflow 开发者调查参与者中有一半以上是为 Windows 构建的,但我们根本没有为 Windows 社区提供服务。

在过去的几年里,我们对构建 Windows 项目越来越感兴趣。从。NET Core 1.0 和 2016 年 Visual Studio 代码的发布,到即将发布的 WSL2 和 unification。网芯和。2020 年,一些最令人兴奋的开发人员工具创新正在微软生态系统中发生。今天,我们很高兴地告诉大家,Windows 支持现已在 CircleCI 上普遍提供。

支持 Windows 作业

我们的核心功能现在可用于 Windows 环境。缓存、工作空间、审批作业和上下文等功能,以及同样强大的支持和漂亮的用户界面,现在都可以用于 Windows 作业。Windows 作业基于虚拟机,并提供完全的构建隔离。每个新作业都使用一个干净的环境,这个环境是及时创建的,一旦作业运行完毕就会被销毁。这确保了在 CI 环境中构建的可再现性以及代码、数据和秘密的安全性。CircleCI 上的 Windows 环境还支持基于 Docker 的 Windows 工作流的 Docker Engine - Enterprise。

现在,您可以在一个工作流程中使用 Windows、Linux 和 macOS 作业。在这个版本中,CircleCI 是构建跨平台应用程序的标准 CI 解决方案。现在,所有绩效计划客户都可以使用 Windows 作业。点击了解有关我们绩效计划的更多信息。

你能做什么

想知道执行环境到底是什么样的?前往 CircleCI for Windows 文档页面

您是现有客户吗?您需要帮助转向性能定价吗?点击联系我们的销售团队

您是新客户吗,您想在 CircleCI 上试用 Windows 吗?点击这里观看演示项目。

我们期待着根据您的反馈改进我们对 Windows 的支持。如果你有想法,请在以下地方与我们分享:

  • 要报告我们的 Windows 支持中的错误,请前往我们的讨论网站这里
  • 要建议 Windows 支持的新功能,请在我们的创意门户中提交新创意。
  • 我们也想知道你对 CircleCI 上的 Windows 支持的总体想法。请填写这张表格,并附上您想要分享的任何反馈。我们的一位产品经理将很快回复您。

在我们改进 Windows 解决方案的同时,我们也期待微软生态系统的进一步创新。我们的 Windows 路线图包括在 Windows 上使用 Linux 容器,以及为移动开发预装依赖项的容器。如果您的团队迫切需要获得额外的功能,请在这里告诉我们

游牧-库柏人|循环

原文:https://circleci.com/blog/write-less-code-use-more-tools/

我们做出的让游牧部落和库伯内特人合作的无耻决定

“少写代码。”

CircleCI 的首席技术官 Rob Zuber 退后一步,仔细观察这条用褪色的红字写在白板上的指令。像所有重要的指令一样,这一条简洁明了,违反直觉;我们怎么可能希望通过做得更少来构建 CircleCI 2.0 呢?

如果你已经阅读了这篇关于我们发布理念的博客文章,你会记得平台移植对我们来说是一个相当可怕的前景。令人如此恐惧的原因之一是我们正从零开始;当你这样做的时候,过度设计一个产品是非常危险的。但是也有机会评估所有的新选项——为改进工具集所做的伟大工作。这个故事并不是真的关于创造circle ci 2.0 的原型:而是关于扩展这个原型。这是关于我们 SRE 团队如何在很短的时间内完成一个大项目。我们的使命:通过使新 CircleCI 高度可用(HA)来发展我们的平台。我们必须快速行动,因为每天不连续地交付特性会给我们带来巨大的痛苦。

这是 CircleCI 2.0 发布的前传。我们将向您展示我们如何将概念证明转化为概念的真实性——而无需编写超出我们需要的代码和工具。我们在两个玩家的帮助下完成了这个简洁的壮举:诺曼和库贝内特斯。

为什么我们使用 Nomad

在多伦多一个明媚的春日,平台团队提出了一个新的构建系统,更广为人知的名称是 CircleCI 2.0。他们知道他们需要一个合适的调度方案来处理所有这些工作,所以他们像鸭子一样把选项分散在桌子上。Mesos,Nomad,Kubernetes…一群佣兵编制系统,每个都在争夺注意力。

不过,我们有一些要求,这让决定变得稍微容易了一些。

很难找到一个好的计划者

我们运行人们的代码。我们把别人的代码和别人的代码放在同一个服务器上。所以,我们必须从一开始就把适当的隔离烤成圆形。它不可能是在最后一分钟被钉上去的。只有几种方法可以合理地进行隔离,我们选择了 LXC。

多年来,我们已经非常擅长管理 LXC 及其工具的细微差别——我们知道它们会产生什么样的输出,我们也了解边缘情况。平台团队希望通过选择一种共享这些品质的工具来利用这种专业知识,让他们拥有 Mesos 和 Nomad。

为什么不是 Mesos?

起初,Mesos 似乎是明显的赢家:它经过了战斗考验,并在 Twitter 和 Airbnb 等公司大规模运营。但是它支持这些大型组织的部分能力依赖于一些巨大的开销;具体来说,需要像马拉松或 Chronos 这样的框架。根据框架的不同,你会得到不同风格的 Mesos: Marathon 专注于服务,而 Chronos 则专注于 cron 作业。平台团队需要一个为批处理作业设计的框架,而 Mesos 没有很好的解决方案。

此外,经营一个 Mesos 舰队是一个整体的事情,需要额外的设置和工程师,我们不能多余的。开箱即用的配置对于不受信任的代码来说是不安全的,并且它没有提供我们想要的隔离。不要误解我们的意思:Mesos 是一个出色的工具,但它也是一个非常强大的工具。平台团队没有时间隔离这头野兽,而是选择了更温和的方式。

少写代码。

游牧,没有问题(几乎)

Nomad 更加精简,也更加用户友好。它调度作业的速度惊人地快,有很大范围的容器化选项,还拥有一个相当合作的 API。当尝试向我们的客户提供这种灵活性时,所有这些都是关键。此外,Nomad 仍在发展,所以我们对它的发展方向有一些发言权;我们在制作新的构建系统时发布了补丁,并将该工具推向了极限。

经营游牧民族确实有一些特殊的考虑。向 Nomad 服务器推出更新需要保持 quorum 不变,这使得大多数配置工具都存在风险。我们最初的方法是进入每台服务器进行更新和重启。也有 Nomad 服务器无法响应的情况;必须有人检测到这一点,访问服务器并手动重启 Nomad 代理。这种级别的手动干预显然不可扩展,并且会严重影响…

为什么我们使用 Kubernetes

在其雏形阶段,CircleCI 2.0 并不具备大规模应对客户的能力:所有新的核心服务都在一台孤立的服务器上运行。这是一个单点故障,会导致停机和工程师的失误——对于一个成熟的产品来说,这是不可接受的。

因此,我们的任务是将所有已经构建到产品中的东西(包括 Nomad)自动化、扩展,并使所有东西都具有高可用性。这是退出原型阶段并进入客户生活的一个硬性要求。由于这已经是一个非常长的项目,我们的决策过程可以归结为几个因素:

成熟度、社区和经验

CircleCI 2.0 的编排系统有几个候选系统(与调度作业明显不同):Docker Swarm、Mesos、ECS、Nomad 和 Kubernetes。他们中的许多人还在襁褓中,还没有准备好迎接黄金时代。Kubernetes 是一个不同的故事:它是稳定的,在几个生产环境中使用,并且有一个非常活跃的社区。见鬼,甚至我们在以前的工作中也使用过它,所以我们没有陷入未知的领域。

我们不能强调这有多重要。一个工具的寿命和采用给了我们信心,其他人遇到过类似的问题,并且(可能)知道如何解决它们。我们希望能够进入 IRC 频道,提出一个问题,并听到有人提出解决方案——或者至少了解这个问题。也许他们还没有完全解决这个问题,但是当你有一群思考者的时候,获得整个系统的可见性就不那么可怕了。

这也是我们没有使用 Mesos 的主要原因之一:如此多的生产经验隐藏在企业防火墙之后。我们团队中没有人知道如何使用它,如果没有社区讨论具体细节,我们就无法填补这些知识空白。

一个闪亮的新事物的诱惑非常诱人,但是我们抵制了这种诱惑,选择了一个已经在其他生产环境中使用的工具。社区越大,我们就越有可能在社区中找到答案;使用一种工具的一千家公司会比十家公司更快地发现边缘案例。

一种尺寸不应该适合所有人

一些聪明的读者可能会想,“嘿,你已经知道如何使用 Nomad 了;为什么不节省一些时间,利用你的知识再次编排整个产品呢?这难道不会降低复杂性吗?”

公平的问题。虽然 Nomad 在构建容器的“单一目的”调度方面很棒,但 Kubernetes 有一系列功能,包括健壮的健康检查、可配置的部署策略、分布式网络、秘密和操作工具,这些都是其他编排系统所缺乏的。

我们还制定了一项政策,严格隔离我们自己的基础架构和客户的基础架构。虽然这确实降低了杠杆作用,但这意味着我们可以自由地为工作选择合适的工具。我们有一些严格的限制,导致我们选择了 Nomad,但我们没有让这个决定引发任何狭隘的观点——我们从未觉得有义务在任何事情上都使用 Nomad。

相反,我们深吸一口气,认真审视我们的目标。我们希望我们选择的系统是有目的的和可组合的,这意味着要花时间探索其他选择。这是我们唯一能做的事情,因为我们已经到了一定的规模。在一个小商店里,让或者一个游牧专家或者一个 Kubernetes 专家是有意义的——但是不能两个都是。

但是我们足够大,我们可以让团队为工作选择合适的工具。我们也足够大,以至于我们需要为这项工作选择正确的工具:当你运行一个大规模的分布式系统时,每一点都很重要。

处理 Nomad 的怪癖

Kubernetes 帮助我们解决了 Nomad 对人工干预的需求。如果我们想更新我们的 Nomad 版本,我们可以将它推送到 Kubernetes,它只会尝试更新第一个服务器。如果失败,Kubernetes 将停止部署并保持其余四个运行,这样一切都可以安全地回滚。

至于楔入问题,Kubernetes 的健康检查证明是非常宝贵的。就绪探测器可以检查给定的 HTTP 端点,并确保它在更新之前是可访问的,而活性探测器可以检查服务器是否…是活动的,如果不是,就重新启动它。所以 Kubernetes 给了我们自主的,自我实现的容器,如果它们变得不健康,可以自动愈合。

编写更少的代码,使用更多的工具

因此,使用 Kubernetes 来编排 Nomad 真的没有那么离谱。这是一个视角的问题:一旦我们把 Nomad 作为我们的调度问题的解决方案,我们就认为它是产品的部分;Kubernetes 是扩展产品的解决方案。

这种区别很重要,因为它影响我们处理每个问题的方式。一旦 Nomad 完成了它作为作业调度器的使命,我们就不再把它当作一个编排系统;相反,它只是 CircleCI 2.0 中的几个组件之一。这意味着我们可以将使 CircleCI 2.0 高度可用的问题与调度作业的问题分开处理。

所有这些都是本着少写代码的精神。通过将这些问题分解成易于理解的部分,我们能够为每个场景选择正确的工具,而不是选择一个编排系统并在任何地方使用它。这是一种解放的感觉,我们强烈推荐这种感觉。这个故事的辉煌结局是彻底删除最初的 CircleCI 所依赖的所有定制编排代码——我们非常期待这一天的到来。

Xcode 7.3.1,以及我们为什么从 xctool 换成 xcodebuild - CircleCI

原文:https://circleci.com/blog/xcode-7-3-1-and-why-we-changed-from-xctool-to-xcodebuild/

来自出版商的说明:您已经找到了我们的一些旧内容,这些内容可能已经过时和/或不正确。尝试在我们的文档博客中搜索最新信息。


在过去的几天里,我们用最新的 Xcode 7.3.1 更新了 OS X 构建映像。此外,默认情况下,我们在容器中包含了更高版本的 Carthage 和 fastlane。这将有助于您了解最新的开发工具。

xcode image.png

您可能还注意到,默认情况下,OS X 项目现在是用xcodebuild而不是xctool构建的。随着xctool的开发放缓,以及对它的构建支持即将移除,正如 xctool自述文件中所解释的:

注意:不推荐使用 xctool 支持构建项目,并且不会更新以支持 Xcode 的未来版本。对于简单的需求,我们建议迁移到 xcodebuild(带有 xcpretty ),对于更复杂的需求,我们建议迁移到 xcbuild。xctool 将继续支持测试(见上文)。

我们决定使用苹果提供的xcodebuild来执行所有必要的构建动作。

这种变化通常不会影响构建过程或构建的性能。xcodebuild在你的代码上执行与xctool完全相同的动作。

我们的选择是由大量客户在使用xctool时获得的构建行为的不一致性所激发的——我们平台的一些细节可能会阻止我们的用户利用xctool提供的一切。

如果您仍然希望在您的项目中使用xctool,我们非常欢迎在您的circle.yml 中指定。

虽然我们不再使用xctool作为默认的构建命令,但我们很高兴看到脸书团队将他们的努力集中在更快、更灵活和跨平台的 xcbuild 上——我们期待看到xcbuildxcodebuild在功能上旗鼓相当。我们非常感谢开发和支持xctool的团队,它是一个节省了很多开发者时间的工具,我们相信新的xcbuild将继续为世界带来更好的 Xcode 体验。

如果您有任何对 CircleCI 用户社区有益的见解,或者想分享您在测试中使用“xcodebuild”与“xctool”的经验,请在讨论 CircleCI 上发表意见。

xcodebuild -退出代码 65 | CircleCI

原文:https://circleci.com/blog/xcodebuild-exit-code-65-what-it-is-and-how-to-solve-for-ios-and-macos-builds/

xcodebuild退出代码 65:

这四个字吓到了任何 CI 系统经验的 iOS 和 macOS 开发者。你们中的一些人可能在使用 Xcode 的时候已经看到了,但是没有意识到,因为 Xcode 对你们隐藏了那些错误代码。

为了防止xcodebuild返回没有任何错误信息的退出代码,我们必须理解退出代码 65 实际上指的是什么。

如果你在你选择的终端模拟器中输入man xcodebuild并滚动到(几乎)底部,你会在用法示例前看到这一小段:

xcodebuild exit code 65

这个小提示将我们带到了sysexits手册页。像 HTTP 状态码 200 OK,404 Not Found 或者 418 我是茶壶,常见的可执行返回码都定义在sysexits.h头文件中。常见的退出代码是退出代码 65 或错误数据输入。

man xcodebuild exit code 65

知道了这一点,我们就可以研究为什么xcodebuild似乎喜欢用代码 65 退出。

xcodebuild无疑是一个复杂的软件,本质上是类固醇上的make (1)。它决定了你的项目或工作空间的结构,并将所有的东西连同正确的标志一起交给编译器。日志中的单个命令的长度可以从 50 个字符到填满在 27 英寸 iMac 上以全屏模式运行的整个 Safari 窗口。

如果事情没有按照预期的方式进行(这种情况经常发生),它将返回退出代码 65。最令人沮丧的是xcodebuild很少为它无法处理的问题留下任何上下文。我们在与其中一种情况斗争了将近一年

每当xcodebuild执行完它的一个动作(清理、构建、测试、分析等等),下一个动作就会立即执行,没有任何准备。此时,启动固定超时计数器。如果您的硬件不够快,在 iOS 模拟器有机会完全启动之前,这些超时将达到 0。

这个问题在一些设置中变得更加严重,因为 iOS 模拟器会在后台线程上启动,如果另一个线程中有更重要的事情发生,它会被降低优先级。一旦超时值达到 0,xcodebuild就会尝试几次连接 iOS 模拟器,但都失败了。这是因为xcodebuild坐在编译好的二进制和模拟器之间,所以不知道底层硬件的速度,也不一定能恢复。

在您的本地机器上,您可以通过翻白眼、叹气并再次按下 CMD+U 来解决这个问题。但是在 CircleCI 上,我们的系统关闭了所有的东西并回收了你上一次构建运行的虚拟机。

在正常的开发工作流程中,没有 Xcode 通用的派生数据文件夹或其他文件。工作目录将不存在,也不会运行任何 iOS 模拟器。在 CircleCi 上开始一个新的构建时,我们会面临同样的问题,xcodebuild可能无法从未知状态中恢复并返回退出代码 65。

缓解这一问题的最佳方法是借助工具的命令行工具,尽早启动 iOS 模拟器。这将引导 iOS 模拟器并立即返回,因此xcodebuild可以开始编译您的代码。我们通常建议与依赖步骤分开进行,以提醒您未来的自己/同事这是一个至关重要的构建步骤——就像通过 Carthage 获取依赖或更新 fastlane 一样重要。

dependencies:
  pre:
    - xcrun instruments -w 'iPhone 7 (10.3)' || sleep 15 

这个sleep 15很重要,有两个原因:它给 iOS 模拟器一些完全启动的喘息空间,并返回一个 0 退出代码。在不使用set -o pipefail的情况下将命令链接在一起时,将返回最后一个命令的退出代码,并忽略之前命令的代码。CircleCI 查找构建步骤的退出代码以确定成功/失败。排除睡眠命令将意味着xcrun instruments将返回一个非零退出代码,因为-t(模板)标志没有通过。

希望这个对臭名昭著的xcodebuild退出代码 65 的解释能让你对为什么xcodebuild会做这些事情有所了解。每当你看到这个代码,xcodebuild就遇到了一个不可恢复的问题。我们将继续与苹果的开发者工具团队紧密合作,使开发者工具链更快、更稳定。

通过并行和测试分割加速 XCUITest 执行| CircleCI

原文:https://circleci.com/blog/xcuitest-parallel-execution/

本教程涵盖:

  1. 使用 XCUITest 和 fastlane 设置 iOS UI 测试
  2. 使用 CircleCI 自动运行 XCUITest
  3. 利用并行性和测试拆分加速 XCUITest 时间

在本文中,我将向您展示如何通过拆分和并行运行 XCUITest(iOS 模拟器上的 UI 测试)来减少它们的执行时间。

CircleCI 这样的自动化测试和 CI/CD 平台是 iOS 应用开发所必需的。重要的是不仅要一次介绍它们,而且要不断改进它们。

当应用程序代码增长和自动化测试增加时,CI/CD 中构建和测试的执行时间会变长。构建和测试时间越长,开发速度越慢。

因为像 XCUITest (Xcode UITest)这样的 UI 测试需要在实际的 iOS 设备或者 iOS 模拟器上运行,所以执行时间往往会比较长。

CircleCI 有多种方法可以减少 iOS 应用程序开发中的执行时间:

在本文中,我将向您展示如何将 CircleCI 的测试拆分和并行性与 fastlane 结合起来,以减少执行时间。

先决条件

对于本教程,您需要:

  • CircleCI 帐户,最好是用于最佳 macOS 虚拟机并发的性能计划
  • Xcode 已安装。本教程使用 Xcode 13.4.1。
  • 快车道已安装。
  • 熟悉编写 XCUITest (Xcode UITest)。

如何使用 CircleCI 和 fastlane 将测试分割和并行性添加到 XCUITest 运行中(概述)

本教程中介绍的 iOS 应用程序的示例代码可以在 GitHub 上获得。

这包括多个 UI 测试。通过插入sleep()改变执行时间,如下所示:

import XCTest

class CircleCIDemoUITests5: XCTestCase {
    func testTapButton5() throws {
        // UI tests must launch the application that they test.
        let app = XCUIApplication()
        app.launch()

        // Elements
        let text = app.staticTexts["text"]
        let button = app.buttons["button"]

        XCTAssertEqual(text.label, "Hello, world!")
        sleep(50)
        button.tap()
        XCTAssertEqual(text.label, "Button Tapped!")
    }
} 

要使用 fastlane 连续运行所有测试,使用 run_tests 动作并编写以下代码。

desc "Run all UITests"
lane :ui_test_all do
  run_tests(
    scheme: "CircleCIDemoUITests",
    devices: ["iPhone 13 (15.4)"]
  )
end 

要并行运行测试,请遵循以下步骤:

  1. 运行 UI 测试的预构建(build_for_testing)
  2. 启动多个 macOS 虚拟机/iOS 模拟器
  3. 基于执行时间的分割测试
  4. 并行运行分割测试
  5. 上传测试结果,包括执行时间

预构建build_for_testing来运行 UI 测试

所有的 iOS 应用测试,包括 UI 测试,都必须在运行前构建。对于本教程,我提前完成了构建部分,因此您可以稍后在多个 macOS 虚拟机上并行运行测试。

从 Xcode8 开始,build-for-testingtest-without-building允许你分离构建和测试执行。

在快速通道中执行build-for-testing时,启用run_tests动作中的build_for_testing参数。

desc "Run all UITests"
lane :build_for_ui_test do
  run_tests(
    scheme: "CircleCIDemoUITests",
    devices: ["iPhone 13 (15.4)"],
    derived_data_path: "dist",
    build_for_testing: true
  )
end 

derived_data_path参数允许您指定工件的路径。

这是 CircleCI build_for_ui_test的工作:

build_for_ui_test:
  macos:
    xcode: 13.3.1
  resource_class: macos.x86.medium.gen2
  steps:
    - checkout
    - ruby/install-deps
    - run: bundle exec fastlane build_for_ui_test
    - persist_to_workspace:
        root: .
        paths:
          - dist 

这包括以下步骤:

UI 测试的测试拆分和并行性

在预构建build_for_ui_test任务完成后,运行ui_test_parallel任务来并行拆分和运行 UI 测试。

ui_test_parallel:
  parallelism: 2
  macos:
    xcode: 13.3.1
  resource_class: macos.x86.medium.gen2
  steps:
    - checkout
    - macos/preboot-simulator:
        device: iPhone 13
        version: "15.4"
    - attach_workspace:
        at: .
    - ruby/install-deps 

启动多个 macOS 虚拟机来并行运行测试。您可以通过设置parallelism键的值来增加或减少并行运行的 macOS 虚拟机的数量。

这项工作的步骤包括:

然后,拆分测试并并行运行它们。

这是一条快车道:

desc "Run specific UITests"
lane :ui_test_without_building do |options|
  run_tests(
    scheme: "CircleCIDemoUITests",
    devices: ["iPhone 13 (15.4)"],
    only_testing: options[:tests],
    derived_data_path: "dist",
    test_without_building: true
  )
end 

这段代码使用了run_tests动作,但是有两处不同。首先,它启用了test_without_building参数。这次我们已经预构建了应用程序build-for-testing,因此我们可以通过启用test-without-building参数来运行测试,而无需构建。

第二,启用only_testing参数。此参数只允许运行某些测试,而不是所有测试。only_testing(特定测试)的目标可以从外部通过。

下面是 CircleCI 配置文件的一部分,它拆分并并行运行测试:

- run:
    name: Split tests and run UITests
    command: |
      CLASSNAMES=$(circleci tests glob "CircleCIDemoUITests/*.swift" \
        | sed 's@/@.@g' \
        | sed 's/.swift//' \
        | circleci tests split --split-by=timings --timings-type=classname)
      FASTLANE_ARGS=$(echo $CLASSNAMES | sed -e 's/\./\//g' -e 's/ /,/g')
      bundle exec fastlane ui_test_without_building tests:$FASTLANE_ARGS
- store_test_results:
    path: fastlane/test_output/report.junit 

目标测试文件由circleci tests glob检索并由circleci tests split分割。

你可以看到--split-by=timings标志被加到了circleci tests split上。这将利用来自前一次测试运行的计时数据,在指定数量的并行运行的测试环境中尽可能均匀地分割测试套件。--split-by=timings标志给出了可用计算能力的最短测试时间。您可以在下面的视频中了解更多关于测试分割和并行性的信息。

https://www.youtube.com/embed/zeukH6V_gEk

视频

在这种情况下,具有不同执行时间的五个测试分两次并行运行。测试执行时间几乎没有变化。

Parallel test runs

结论

在本文中,我介绍了如何通过拆分和并行运行 XCUITest(iOS 模拟器上的 UI 测试)来减少它们的执行时间。

该解决方案的关键要点:

  • 基于时间的测试分割为可用的计算能力提供了最短的测试时间。
  • 通过设置 CircleCI 配置文件中的parallelism键的值,可以轻松调整并行度。
  • 即使在并行运行多个 macOS 虚拟机时,如在这种情况下,性价比也很高。

注: CircleCI 按每分钟 macOS VM 使用量(积分)收费,而不是按并行数收费。要了解更多信息,请查看我们的 CircleCI 定价和计划信息

如果你想提高 iOS 应用程序开发的性能,我希望你会发现这很有用。

通过浏览器测试防止 XSS 攻击

原文:https://circleci.com/blog/xss-attacks/

本教程涵盖:

  1. 手动测试 XSS 攻击
  2. 修复 XSS 漏洞
  3. 自动化测试过程

网络安全是一场永无止境的战斗。您可以在短短几分钟内启动一台服务器,下一分钟,就有人已经在试图入侵它了。这些攻击可以使用恶意机器人自动进行,也可以手动发起。恶意用户可能会将网站作为攻击目标,试图破坏您的网站或数据。跨站点脚本(XSS) 这只是你的站点容易受到的一种攻击。

在 XSS 攻击中,用户利用应用程序中数据入口点的漏洞。攻击将脚本代码注入表单字段或地址栏,并强制其运行恶意脚本。这些攻击可能导致敏感的 cookie 信息泄露,或者它们可以在您的网页上运行脚本,将外来元素注入您的页面。

在本教程中,您将学习并演示如何使用浏览器安全测试来防止此类 XSS 攻击。

先决条件

要完成本教程,需要做几件事:

  1. JavaScript 的基础知识
  2. 安装在您系统上的 Node.js (版本> = 11)
  3. 一个的账户
  4. GitHub 的一个账户

我们的教程是平台无关的,但是使用 CircleCI 作为例子。如果你没有 CircleCI 账号,请在 注册一个免费的

克隆和运行示例应用程序

首先,您需要克隆将被测试 XSS 攻击的演示应用程序。运行以下命令在您的系统上获取代码:

git clone --single-branch --branch base-project https://github.com/coderonfleek/xss-attacks.git prevent-xss-attacks 

一旦应用程序被克隆,进入项目的根目录(cd prevent-xss-attacks)并通过运行以下命令安装依赖项:

npm install 

完全安装完依赖项后,运行应用程序:

node server 

这将在http://localhost:5000启动应用服务器。在浏览器上导航到此 URL。

Homepage - demo app

该页面由一个表单和一个用于在右侧显示信息的栏组成。当您填写表格并按 Enter 键时,您的电子邮件会出现在Details框中。

Fill form - demo app

手动测试 XSS 攻击

当提交表单时,信息被提交到后台服务器上的一个端点(/sendinfo)。这个端点在一个json响应主体中发回电子邮件,然后被页面获取并显示在细节部分。显示电子邮件表明,在表单中输入的数据到达了后端,并被返回到页面。恶意用户可以通过在电子邮件字段中输入损坏的数据来轻松利用这一过程。例如,在电子邮件字段中输入文件的 HTML 标记,而不是输入有效的电子邮件:

<input type="file" /> 

填写密码字段并点击Submit。细节部分看起来很不一样。

Attacked page - demo app

输入的数据导致页面上显示一个新的 HTML 元素,即文件输入字段。这肯定是不想要的,如果它被很好地定位,可能是危险的。攻击者可以使用这种策略在表单中嵌入恶意数据(或脚本)。在表单中放置一个隐藏的输入字段可能会导致表单向您的服务器提交泄露的数据及其有效负载。这可能会导致严重的损坏并危及数据完整性。您会希望在有人利用之前发现这样的漏洞。一种有效的方法是通过浏览器测试。

安装 Jest 和木偶师

浏览器测试允许您像普通用户一样,通过与网页交互来运行测试。这允许您测试不同的数据输入场景,以找到并修复黑客可能试图利用的任何漏洞。

要设置自动化浏览器测试,您需要两个包。

  • Jest 将被用作测试套件的测试运行程序。
  • 木偶师将用于编写浏览器测试。

通过运行以下命令安装这些软件包:

npm install --save-dev jest puppeteer 

安装了这些包之后,您现在可以开始为您的浏览器编写测试了。

为浏览器添加 XSS 测试

在本节中,您将编写一个测试套件来测试您的浏览器,以检测电子邮件输入漏洞。如果发现漏洞,测试将失败。失败的 XXS 测试表明您的电子邮件字段中存在漏洞,需要解决以避免攻击。

您将要编写的测试将执行与您在上一节中手动执行的攻击相同的攻击。创建测试文件login.test.js并输入以下代码:

const puppeteer = require("puppeteer");

test("Check for XSS attack on email field", async () => {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();

    await page.goto("http://localhost:5000");

    await page.type("#userEmail", '<input type="file" />');
    await page.type("#userPassword", "password");
    await page.click("#submitButton");

    let emailContainer = await page.$("#infoDisplay");
    let value = await emailContainer.evaluate((el) => el.textContent);

    expect(value.length).toBeGreaterThan(0);
  } finally {
    await browser.close();
  }
}, 120000); 

在上面的测试文件中,Check for XSS attack on email field测试用例使用 Puppeteer 启动一个浏览器实例,然后该实例在 URL http://localhost:5000加载应用程序。应用程序运行后,电子邮件字段将使用输入文件标记填充。密码字段也已填写。单击Submit按钮,一旦提交了表单,就会在显示部分检查长度大于零的字符串(非文本 HTML 元素将返回长度为零的字符串)。一旦测试运行完毕,关闭浏览器。

现在,您已经有了检查攻击的 XSS 测试。为了完成测试设置,向package.json文件添加一个test脚本:

...
"scripts" : {
    "test" : "jest"
} 

使用node server.js确保您的应用程序当前正在运行,并运行测试文件:

npm run test 

这个测试将会失败,因为我们已经知道,漏洞确实存在。以下是 CLI 输出显示的内容。

 FAIL  ./login.test.js (5.475 s)
  ✕ Check for XSS attack on email field (2096 ms)

  ● Check for XSS attack on email field

    expect(received).toBeGreaterThan(expected)

    Expected: > 0
    Received:   0

      23 |   let value = await emailContainer.evaluate(el => el.textContent);
      24 |
    > 25 |   expect(value.length).toBeGreaterThan(0);
         |                        ^
      26 |
      27 | }, 120000);
      28 |

      at Object.<anonymous> (login.test.js:25:24)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        14.582 s
Ran all test suites. 

修复 XSS 漏洞

解决 XSS 漏洞的一个有效方法是,确保在对数据进行任何形式的处理之前,对输入到应用程序中的数据进行验证。数据验证可以在应用程序的客户端和服务器端完成。对于这个应用程序,您将验证在服务器端收到的电子邮件,以确保只有安全的文本返回给客户端。

找到server.js/sendinfo端点显示电子邮件未经验证就被返回给客户端:

app.post("/sendinfo", (req, res) => {
  const email = req.body.email;

  res.send({ email });
}); 

现在,用下面的代码替换这个端点:

app.post("/sendinfo", (req, res) => {
  let email = req.body.email;

  if (!validEmail(email)) {
    email = "Enter a Valid Email e.g test@company.com";
  }

  res.send({ email });
});

function validEmail(mail) {
  return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(
    mail
  );
} 

在新代码中,validEmail函数接收一个字符串,并根据该字符串是否是有效的电子邮件返回一个布尔值。然后在/sendinfo中使用这个函数来验证客户端发送的电子邮件。如果电子邮件有效,它将被返回给客户端。如果无效,则会发送一条消息,提示用户输入有效的电子邮件。

随着server.js代码的改变,通过杀死它(Ctrl + C)并使用node server.js重新启动来重新启动应用程序。您可以通过刷新浏览器并重试攻击来首先进行手动测试。这将显示一条验证消息,而不是输入字段。

Successful manual test - demo app

现在用命令npm run test运行测试套件。测试将通过,如控制台输出所示。

 PASS  ./login.test.js (6.953 s)
  ✓ Check for XSS attack on email field (3887 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        16.258 s
Ran all test suites. 

自动化安全测试过程

本练习的主要目的是自动化浏览器测试过程,以便可以在 XSS 漏洞进入您的生产代码之前发现它们。

要开始这个过程,导航到项目的根目录,创建一个名为.circleci的新文件夹。接下来,在其中创建一个新的config.yml文件。打开新创建的文件,并使用以下内容:

version: 2.1
orbs:
  node: circleci/node@5.0.2
jobs:
  build:
    working_directory: ~/repo
    docker:
      - image: cimg/node:18.2.0-browsers
    steps:
      - checkout
      - node/install-packages:
          cache-path: ~/project/node_modules
          override-ci-command: npm install
      - run:
          name: Run the application
          command: node server.js
          background: true
      - run:
          name: Run tests
          command: npm run test 

该脚本拉入 Node.js Orb 来安装 Node 及其包管理器。然后,它获取所需的映像,安装并缓存依赖项。为了确保浏览器测试运行,应用程序在后台进程中启动。一旦应用程序启动并运行,就会运行test脚本来测试它。

提交对项目的所有更改并推送到您的远程 GitHub 存储库

接下来,转到 CircleCI 仪表板上的Projects页面添加项目。

Add Project - CircleCI

点击设置项目开始设置项目。CircleCI 将检测项目中的配置文件。请随意检查并确保选择了配置文件所在的分支。

现在,点击设置项目继续。这将自动触发构建管道并成功构建。

Build successful - CircleCI

点击 build 查看测试细节。

Build Details - CircleCI

结论

将安全检查内置到您的构建过程中会为您的代码增加很多价值。代码能够工作并且没有错误是不够的;您还必须确保它不会受到损害。像这样的安全驱动的开发过程可以扩展到代码中涉及用户交互的其他部分,以防止恶意用户利用代码中的漏洞。鼓励您的团队成员在他们的代码中也使用这些步骤来防止 XSS 攻击。

编码快乐!


Fikayo Adepoju 是 LinkedIn Learning(Lynda.com)的作者、全栈开发人员、技术作者和技术内容创建者,精通 Web 和移动技术以及 DevOps,拥有 10 多年开发可扩展分布式应用程序的经验。他为 CircleCI、Twilio、Auth0 和 New Stack 博客撰写了 40 多篇文章,并且在他的个人媒体页面上,他喜欢与尽可能多的从中受益的开发人员分享他的知识。你也可以在 Udemy 上查看他的视频课程。

阅读 Fikayo Adepoju 的更多帖子

您的 VCS 分支模式如何影响您的交付节奏| CircleCI

原文:https://circleci.com/blog/you-are-what-you-git-how-your-vcs-branching-model-affects-your-delivery-cadence/

软件工程师的道路是不断学习的道路。我们学习从概念和过程到语言和工具的东西。一旦我们看到它们起作用,我们就把它们加入我们的武器库,成为我们的实践。

在加入 CircleCI 之前,我多年的经验让我相信,我是一名工程师,对工艺的技术方面以及被认为是良好的实践有着深刻的理解。CircleCI 告诉我,我还有很多东西要学。

在 CircleCI,我的团队使用一种短暂的分支模型进行软件开发。虽然这种策略与我曾经认为的开发软件的“正确”方式背道而驰,但在这篇文章中,我将描述在实践中看到它是如何教会我接受它的。

我们的模型

最流行的分支模型是 GitFlow 。出于本文的目的,我将其简化为:

  • 使用一个分支进行开发工作。
  • 使用一个或多个分支或标记来表示不同级别的生产就绪性、修补程序等。
  • 逐渐地将代码从特性分支转移到开发,然后随着特性的测试和准备就绪,转移到生产。

作者:文森特·德里森

原始博客帖子

here

这是我熟悉的分支模型。这是我在每个项目、每个公司都经历过的,它对我很有帮助。或许有一些细微的变化,我认为每个人都是这样做的。

那么,当到达 CircleCI 时,发现没有登台环境,没有 hotfixes 分支,master 上的内容直接用于生产,这是多么令人惊讶啊。是的,在我们处理票据时,我们创建了一个分支。但这种分支往往持续不到一天。它可能只是部分地实现了一个特性(受特性标志的保护,或者只是用户不可访问),被审查,并被合并。然后投入生产。还是那句话,没有暂存环境!没有 QA 团队来检查工作是否正常!没有与你的冲刺同步的发布窗口(因为,剧透,没有冲刺)!

CircleCI 的分支模式:一个主分支,许多非常短暂的“子功能”分支。

为什么是这种模式?

我们这样做的原因与 CircleCI 的使命有很大关系,即授权团队快速交付价值。您使用的 VCS 分支模型对您的交付节奏有很大的影响。我们的模型与 CI/CD 最佳实践配合得非常好。我们的流程的特征,比如短生命周期的分支,快速的部署周期,已经被证明与高绩效团队相关联。

你如何能做它

如果你像我一样习惯于更传统的流程,这可能听起来很可怕。我喜欢认为我写了好代码,但是软件有 bugs 那是不可避免的。CircleCI 是成千上万开发人员通往生产之路的关键部分。这意味着很大的责任。如果我将一个 bug 推向生产,却没有 QA 团队来抓它怎么办?如果我破坏了一个特征怎么办?如果我把整个事情搞糟了呢?

我担心我的同事似乎并不关心。他们是否比我更有经验,以至于相信他们的代码是完美的?他们只是天真吗?

CircleCI 有令人难以置信的天才。然而,对软件的信心并不是雇佣神话般的 10 倍工程师的结果。是每天工作的结果,让我们的持续集成管道更好更可靠。以下是我们实现这一目标的方法:

  • 测试,以其多种形式,从单元测试和安全测试扩展到负载和压力测试。
  • 任何开发者都可以使用 canaries 进行部分部署。这包含了错误代码对很小一部分流量的爆炸半径。
  • 我们实施平均恢复时间减少措施,包括监视器和有意义的警报、诊断工具以及快速回滚(或向前)的方法。
  • 进行无过失事故事后分析是为了建立故障模式并最大限度地减少未来的再次发生,为该策略提供反馈。
  • 我们时刻关注着平均故障时间和恢复时间之间、可靠性和敏捷性之间的平衡。

总有改进的空间,但是这个策略提供了难以置信的价值。如果使用像 GitFlow 这样的“更安全”的流程,或者如果我们有一个专门的 QA 团队,我们将无法快速交付价值。

结论

我加入 CircleCI 已经两年多了。虽然我承认没有适合所有团队的 VCS 分支模型,但我很高兴得知 CircleCI 选择的选项存在,并体验了它如何影响我们的交付节奏和我们围绕产品建立的工程文化。

当每个开发人员都对生产中的东西负责,并且把每一个提交都当作一个发布候选,这就培养了一种投资于可持续软件质量的文化,对他们自己和他们的队友都有好处。我们一起建造这个。

posted @ 2024-10-31 16:48  绝不原创的飞龙  阅读(9)  评论(0编辑  收藏  举报