CircleCI 博客中文翻译(二十七)

原文:CirecleCI Blog

协议:CC BY-NC-SA 4.0

优化 OSS 版本| CircleCI

原文:https://circleci.com/blog/optimizing-open-source-projects-on-circleci/

在之前的一篇博客文章中,我写了我如何在 CircleCI 上优化 React Native Camera 的构建。我们从这项工作中了解到,维护人员甚至没有意识到这些优化。我所做的一切都可以在我们的文档中参考,但正如那句老话所说,“你不知道你不知道的。”现在,我们想借此机会推广 CircleCI 上所有项目可用的大量优化。

构建时间非常宝贵,尤其是当你是一个预算有限的 OSS 项目时。我们的文档中有一页是专门针对优化的,但是我们决定写一篇博客文章来强调和介绍使我们的平台与众不同的各种功能。

以下几乎所有的特性都是免费的,任何用户都可以使用,无论是私人项目还是公共项目。高级功能将被如此标记。尽管我很想为更多的项目开放 PRs,但这并不实际。我能做的下一件最好的事情是给每个人配备知识和资源,让他们自己去做。

CircleCI 的优化特性

CircleCI 有许多特性,开发人员可以使用它们来加速构建和部署。

依赖缓存

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们系统中的每项工作都在一个全新的环境中运转。这是为了防止以前的工作和意外行为造成的污染。但这意味着同一作业的后续运行将再次下载相同的依赖项。

为了解决这个问题,我们提供了依赖缓存。您可以缓存项目的依赖项,从而大大加快构建过程。

依赖关系缓存的示例

例如,如果构建一个 Node.js 应用程序,您应该缓存node_modules。你将缓存存储为一个键,我们有个模板可以用于这个键。

在下面的例子中,我将我的node_modules文件夹缓存到一个包含package.json的 SHA256 的键中。这样,如果我的依赖项发生变化,缓存键也会发生变化,随后会为新的依赖项创建一个新的缓存。有些人可能会选择使用他们的package.lockyarn.lock文件的校验和。

version: 2.1
jobs:
  my-job:
    executor: my-executor
    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-dependencies-{{ checksum "package.json" }}
            - v1-dependencies- # Fallback cache
      - run: yarn
      - save_cache:
          key: v1-dependencies-{{ checksum "package.json" }}
          paths:
            - node_modules 

有关更多信息和示例,请访问我们的 awesome 文档:

与工作区共享文件和工件

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如上一节提到的,我们系统中的每个作业都在一个全新的环境中旋转。为了在工作流中的作业之间共享文件和工件,您可以使用工作区。工作区允许您轻松地将工作从一项工作转移到下一项工作,以避免重复劳动。

跨工作区共享文件的示例

在下面的例子中,我将我的 Go 二进制构建和 Docker 映像构建分离到不同的作业中。为了避免再次克隆存储库和/或重做构建,我已经通过工作区转移了所需的文件。

version: 2.1
jobs:
  my-build-job:
    executor: my-executor
    steps:
      - checkout
      - run: go build -o APP_NAME cmd/**/*.go
      - persist_to_workspace:
          root: .
          paths:
            - Dockerfile
            - cmd
            - internal
            - go.mod
            - go.sum
  my-docker-job:
    executor: my-other-executor
    steps:
      - attach_workspace:
          at: .
      - run: docker build -t TAG_NAME . 

有关更多信息和示例,请访问我们的 awesome 文档:

重用以前构建的 Docker 映像

特别是,工作区对于在作业之间传输构建的 Docker 图像非常有用。我们的许多客户都在我们的平台上进行 Docker 构建,在某些情况下,他们将构建 Docker 映像和测试分开进行。

您可以通过使用docker savedocker load命令传输保存的图像。

重复使用先前构建的 Docker 映像的示例

在本例中,我在第一个作业中构建了 Docker 映像,并在将其保存到工作空间之前将其写入docker-cache/image.tar。下一个作业连接到那个工作区,然后在运行它和测试套件之前加载映像。

version: 2.1
jobs:
  my-build:
    executor: my-executor
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Build and Save Docker Image
          command: |
            docker build -t MY_APP:MY_TAG .
            mkdir docker-cache
            docker save -o docker-cache/image.tar MY_APP:MY_TAG
      - persist_to_workspace:
          root: .
          paths:
            - docker-cache
  my-test:
    machine: true
    steps:
      - attach_workspace:
          at: .
      - run:
          name: Load, Run Docker Image and Tests
          command: |
            docker load < docker-cache/image.tar
            docker run -d -p 8080:80 MY_APP:MY_TAG
            mvn test -Dhost=http://localhost:8080 

创建自定义 Docker 图像执行器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的平台提供了各种执行环境,其中之一就是 Docker。我们有针对各种框架和语言的便利图片,对于大多数用例,我们建议使用这些图片作为起点。然而,对于非常高级或复杂的用例,您可以轻松地制作自己的定制 Docker 图像来用作执行者。

通过使用自定义映像,您可以使用所有必需的依赖项和所需的任何其他设置来预烤环境。默认情况下,我们从 Docker Hub 获取图像,但是如果您需要从其他来源获取图像(使用 auth ),您可以在配置中轻松更改。请记住指定完整的 URL:

version: 2.1
jobs:
  build:
    docker:
      - image: quay.io/project/IMAGE:TAG
        auth:
          username: $QUAY_USER
          password: $QUAY_PASS 

有关更多信息和示例,请访问我们的 awesome 文档:

测试拆分和并行性

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

您还可以在我们的平台中使用测试分割和并行。通过拆分测试并并行运行不同的工作负载,您可以极大地减少测试套件的挂钟时间。我们的免费计划中的开源组织在 Linux、Docker、Arm 和 Windows 版本上享有高达 30 倍的并发性,而我们的性能计划客户在所有执行者上享有高达 80 倍的并发性。如果您对您团队的更多资源感兴趣,今天就联系我们

有关更多信息和示例,请访问我们的 awesome 文档:

可配置资源

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

默认情况下,所有执行环境都在中等大小上运行,这取决于执行器的类型。然而,我们的平台允许用户灵活地为他们的构建配置不同的资源(vcpu 和 RAM)。

有些工作不需要很多资源,所以你可以选择一个small Docker 容器来节省计算和信用。但是还有其他工作会受益于一个2xlarge+集装箱。无论如何,CircleCI 可以配置为满足您的工作负载需求。

使用可配置资源的示例

在这个例子中,我已经指定我的构建使用xlarge资源类,这意味着我将拥有 8 个 vCPUs 和 16GB RAM。这只是对配置的一行修改。

version: 2.1
jobs:
  build:
    docker:
      - image: circleci/node:10.15.1-browsers
    resource_class: xlarge
    steps:
      # ... steps here 

Docker 层缓存

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于构建 Docker 图像的客户,他们可以利用一个叫做 Docker 层缓存的功能。正如您可以缓存应用程序的依赖关系一样,您也可以缓存 Docker 映像层,以加快 Docker 映像的后续构建。

为了最好地利用这个特性,你会希望把不断变化的项目(例如,复制或添加源代码等。)在 Dockerfile 文件的底部附近,最不频繁改变的项目在顶部。

Docker 层缓存示例

使用它就像给 YAML 添加一个额外的键一样简单。如果将 Docker 执行器与远程 Docker 一起使用:

jobs:
  build:
    executor: my-executor
    steps:
      - checkout
      - setup_remote_docker:
          docker_layer_caching: true
      # ... other steps here 

或者,如果使用我们的machine执行者:

jobs:
  build:
    machine: true
      docker_layer_caching: true
    steps:
      # ... steps here 

有关更多信息和示例,请访问我们的 awesome 文档:

便利功能

除了优化构建时间的特性之外,我们还有许多便利的特性,有助于提高生产率和改善整体开发人员体验。

可重复使用的配置和 orb(带参数)

</blog/media/2021-01-31-orbs-browser-example.mp4>

一年前,我们推出了 orbs 和可重用配置。使用新的 2.1 配置,您可以在项目内以及跨项目和组织定义可重用的作业、执行者和命令,并进行参数化。这对您来说意味着几件事:

  • 您可以定义一次配置并重用它:这对于想要定义或建立共享的执行器、作业和命令,并在 orb 中维护它们作为单一事实来源的团队来说非常好。这些参数允许您将不同的输入插入到相同的作业或命令逻辑中,以获得最大的可重用性。
  • 你不必重新发明轮子:举个例子,AWS 是一个广泛使用的云服务提供商。如果你正在部署,比如说, AWS ECS ,你可能会意识到其他人也在做同样的事情。每个人都重写相同的配置是没有意义的,事实证明你不需要
  • 你可以像维护代码一样维护它们:orb 只是打包的 YAML,所以它们可以像其他代码一样被版本化、测试和发布。这意味着整个过程可以自动化和跟踪,作为开发人员,您不必学习任何新的语言或框架来使用 orb。
orb 用法示例

在下面的例子中,我将我的文件部署到 AWS S3。不必编写许多行配置来更新 AWS CLI,只需这样配置,那样做,等等。,我必须只写五行:两行“导入”orb,然后三行使用它(带参数)。

orbs: # "Import" the orb
  aws-s3: circleci/aws-s3@2.0.0

jobs:
  deploy:
    docker:
      - image: circleci/python:2.7
    steps:
      - attach_workspace:
          at: .
      - aws-s3/sync: # Using said orb
          from: .
          to: my_awesome_bucket 

有关更多信息和示例,请访问我们的 awesome 文档:

数据库和服务

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

你知道你可以为你的测试旋转数据库吗?还是其他服务形象?在第一个docker之后指定的任何图像都被视为服务图像。它将在与主执行容器相同的网络中运行。

启动多个服务的示例

在下面的例子中,我正在构建一个 Postgres 数据库,用于在主容器中测试我的应用程序。默认情况下,如果不指定名称,可以通过localhost访问容器,但是如果指定了名称,也可以使用它。

jobs:
  build:
    docker:
      # Primary Executor
      - image: cimg/openjdk:11.0.9

      # Dependency Service(s)
      - image: postgres:10.8
        name: postgres # Can access via postgres:5432
        environment:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: postgres 

如果您正在使用一个完整的machine执行器,您可以像对待任何其他虚拟机一样简单地对待它,并构建一个 Docker 容器网络。

jobs:
  build:
    machine: true
    steps:
      - run: docker-compose up
      # OR
      - run: docker run -d -p 5432:5432 -e POSTGRES_USER=postgres postgres
      - run: docker run -d -p 8080:80 MY_APP:MY_TAG
      # ...etc. 

有关更多信息和示例,请访问我们的 awesome 文档:

测试总结

</blog/media/2021-01-31-failed-test.mp4>

你在运行测试套件吗?我们有一个测试总结特性,它解析 JUnit XML 格式的文件并存储数据。这使您能够看到您运行了多少测试的摘要,以及它们是否都通过了。如果它们中的任何一个失败了,您将会看到是哪一个,并且它们的输出将会在那里被访问。方便快速地访问重要信息。

测试总结的使用示例

要实现这一点,您需要配置您的测试套件,将结果以 JUnit XML 格式输出到一个文件中。然后你将文件存储在一个文件夹中,并使用store_test_results YAML 键:

jobs:
  build:
    executor: my-executor
    steps:
      # Other build steps
      - run:
          name: Run TestCafe tests
          command: |
            yarn runTests -- ${TESTFILES} # Outputs > /tmp/test-results/results.xml
      - store_test_results:
          path: /tmp/test-results
      # ...etc. 

查看构建查看我的配置

有关更多信息和示例,请访问我们的 awesome 文档:

洞察力

当我们收集您所有项目、管道、工作流和作业的所有构建信息和测试摘要时,我们开始跟踪它们每一个运行的时间、频率和成功率。这样,您可以很容易地看到项目管道的哪些部分最有问题,应该首先改进。您可以在 CircleCI 仪表盘的 Insights 选项卡中找到这些信息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有关 Insights 的更多信息,您可以阅读初始版本和文档:

如果洞察力让你兴奋,你可能也会对我们的 2020 年软件交付状态报告感兴趣,其中我们探索了来自超过 160,000 个软件项目、构建时间以及团队如何合作的信息。

人工制造

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

CircleCI 的作业每次都在新的、干净的、隔离的执行环境中运行。随后,如果您想让任何文件或构建工件可供外部访问,您可以使用我们的加工特性。工件文件使它们在您的作业详情页面的工件选项卡中可用——顺便提一下,因为我们为此使用 AWS S3,只要引用路径正确,您就可以预览文件,就好像它们是静态托管的一样。

示例工件创建

这个例子和上面的一样:我已经运行了一个测试套件,并将结果输出到/tmp/test-results/results.xml。然后,我使用store_artifacts键构建这些文件,它们现在可以在构件选项卡上查看。

查看构建查看我的配置

jobs:
  build:
    executor: my-executor
    steps:
      # Other build steps
      - run:
          name: Run TestCafe tests
          command: |
            yarn runTests -- ${TESTFILES}
      - store_artifacts:
          path: /tmp/test-results
          destination: .
      # ...etc. 

有关更多信息和示例,请访问我们的 awesome 文档:

加入构建

</blog/media/2021-01-31-ssh-into-build.mp4>

测试输出不明显?构建失败的原因你无法确定?你可以在我们的平台上用 SSH 重新运行。这将重新运行相同的构建,但是这次 SSH 端口是打开的。使用与 GitHub 或 Bitbucket 帐户相同的 SSH 密钥,您可以在运行时登录并验证执行环境,以实时调试构建。通过直接访问环境,您可以实际检查环境、tail日志,或者查看其他项目来研究构建。

相对于在环境之外的反复试验,这对于高效调试来说是非常有用的特性。

有关更多信息和示例,请访问我们的 awesome 文档:

构建分叉的拉请求和传递秘密

开源项目通常需要来自社区开发人员的跨项目分支的协作。默认情况下,CircleCI 只会在 GitHub 上你自己的项目上构建分支,而不会在外部分叉上构建。如果你希望允许跨分支构建拉请求,你可以在你的项目设置中切换——在高级设置标签下。

您的 CircleCI 管道可能还需要秘密来执行某些操作,比如签署二进制包,或者与外部服务交互,例如在部署到云环境时。您也可以在同一个选项卡下切换。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有关更多信息,请参见相关文档页面:

结论

在上面提到的所有功能中,只有三个是高级或半高级的。对于使用 CircleCI 的开发人员来说,有很多功能可以提高构建速度和生产力。我们的用户一次又一次地告诉我们他们有多爱我们的产品,喜欢我们提供的功能;从现在开始,我们只会对体验进行改进。

优化 React 原生相机的构建| CircleCI

原文:https://circleci.com/blog/optimizing-react-native-camera/

上个月,我的一个拉取请求被合并到流行的社区项目 React Native Camera 中,以回应一个维护者的推文。React Native Camera 的构建被阻止,因为他们试图使用自由/OSS 计划中不可用的资源类。除了解除阻塞,我还做了一些额外的优化。

我在这篇 PR 中使用的策略可以被推广并用于优化许多其他项目。在这篇文章中,我将分解我对 React 原生相机项目所做的一些更改,解释它们如何提高性能,并建议您可以使用类似的技术来改进自己的项目。

缓存步骤放置

一般来说,人们习惯于将他们的save_cache语句放在构建的末尾。React Native Camera 也是这样做的,就像这样:

jobs:
  build-app:
    # ...environment setup here
    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-npm-{{ .Branch }}-{{ checksum "yarn.lock" }}
            - v1-npm
      - run:
          name: Install Dependencies
          command: yarn install --ignore-engines
      # ...other steps, etc.
      - save_cache:
          key: v1-npm
          paths:
            - node_modules/
      - save_cache:
          key: v1-npm-{{ .Branch }}-{{ checksum "yarn.lock" }}
          paths:
            - node_modules/
      # ...rest of config 

这不是最佳选择,因为如果作业中的任何其他步骤在save_cache步骤有机会执行之前失败,将不会保存缓存。最好将save_cache步骤放在依赖步骤之后——这保证了只要依赖步骤工作,缓存就会被保存:

jobs:
  build-app:
    # ...environment setup
    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-npm-{{ .Branch }}-{{ checksum "yarn.lock" }}
            - v1-npm
      - run:
          name: Install Dependencies
          command: yarn install --ignore-engines
      #### MOVED
      - save_cache:
          key: v1-npm
          paths:
            - node_modules/
      - save_cache:
          key: v1-npm-{{ .Branch }}-{{ checksum "yarn.lock" }}
          paths:
            - node_modules/
      #### /MOVED
      # ...rest of config 

您可以找到更多关于缓存的信息,并在我们的文档中查看示例

为 Gradle 添加缓存

我做的另一个改变是为 Gradle 依赖项添加缓存。他们在 CircleCI 中的Run ChecksBuild Sample App步骤是 Gradle 包装器执行,它们占用了大部分构建时间。在下面的配置中,我存储了 Gradle 缓存和包装器。

jobs:
  build-app:
    # ...environment setup
    steps:
      # ...previous build steps
      #### ADDED
      - restore_cache:
          keys: 
            - v1-gradle-{{ checksum "android/gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "examples/basic/android/gradle/wrapper/gradle-wrapper.properties" }}
            - v1-gradle-wrapper
      - restore_cache:
          keys:
            - v1-gradle-cache-{{ checksum "android/build.gradle" }}-{{ checksum "examples/basic/android/build.gradle" }}
            - v1-gradle-cache
      #### /ADDED
      - run:
          name: Run Checks
          command: |
            cd android
            chmod +x ./gradlew && ./gradlew check
      # ...other step(s)
      - run:
          name: Run Yarn to Generate react.gradle
          command: cd examples/basic/android && yarn
      - run:
          name: Build Sample App
          command: |
            cd examples/basic/android && chmod +x ./gradlew && ./gradlew build
      #### ADDED
      - save_cache:
          key: v1-gradle-wrapper-{{ checksum "examples/basic/android/gradle/wrapper/gradle-wrapper.properties" }}
          paths:
            - ~/.gradle/wrapper
      - save_cache:
          key: v1-gradle-cache-{{ checksum "examples/basic/android/build.gradle" }}
          paths:
            - ~/.gradle/caches
      #### /ADDED
      # ...rest of config 

从报表制作工件

您知道我们的工件就像一个静态文件服务器吗?这对于存储测试套件的 HTML 报告和像访问网页一样访问它们是很有用的。我添加了一个工件步骤来存储林挺命令生成的输出。这允许你在工件标签上浏览工件,并像访问网页一样访问那些文件。

jobs:
  build-app:
    # ...environment setup
    steps:
      # ...previous build steps
      - run:
          name: Run Checks
          command: |
            cd android
            chmod +x ./gradlew && ./gradlew check
      #### ADDED
      - store_artifacts:
          path: android/build/reports
      #### /ADDED
      # ...other step(s)
      - run:
          name: Build Sample App
          command: |
            cd examples/basic/android && chmod +x ./gradlew && ./gradlew build
      #### ADDED
      - store_artifacts:
          path: examples/basic/android/app/build/reports
          destination: app
      #### /ADDED
      # ...rest of config 

你可以在我们的文档中找到更多关于创建工件的信息。

结论

在这个 pull 请求中所做的小改动只是您可以用来优化 CircleCI 构建的一些特性的例子。我们平台的文档内容丰富,包含各种示例和教程。T2 的 CircleCI 博客也是一个很好的资源。如果你被困住了,试着发微博给我们 @CircleCI

要了解更多优化特性和技术,请阅读我们在 CircleCI 上发布的优化开源项目。

感谢阅读,并快乐建设!

使用 CircleCI AWS ECR orb 构建 CI/CD 管道

原文:https://circleci.com/blog/orbs-aws-ecr/

CircleCI 最近发布了一款名为 orbs 的新产品,旨在让你在 CircleCI 上快速启动并运行。现在,您可以轻松地将您的 DevOps 工具与我们的技术合作伙伴提供的可信 orb 相集成。

在这篇文章中,我将演示和解释 AWS ECR Orb 及其在 CircleCI 配置文件中的用法,利用 CircleCI 工作流,并将图像推送到 Docker Hub 和指定的 AWS ECR。

什么是 CircleCI 球体?

orb 是 CircleCI 配置的包,可以跨项目共享。orb 允许您创建一个作业、命令和执行器的捆绑包,它们可以相互引用,可以导入到 CircleCI 构建配置中,并在它们自己的名称空间中调用。orb 在 CircleCI 注册,使用 semver 模式表示修订。CircleCI orbs 由 CircleCI Orbs Registry 托管和维护,该注册中心是 CircleCI Orbs 的集中存储库。

先决条件

在你开始之前,你需要有这些东西:

完成所有先决条件后,就可以继续下一部分了。

配置 CircleCI 项目环境变量

我们项目的 CI/CD 管道将 Docker 映像部署到多个容器存储库,因此您需要在 CircleCI 项目设置中设置一些环境变量:

  • 点击左侧菜单中 CircleCI 仪表板上的添加项目
  • 在项目列表中找到并点击项目名称,点击右侧的设置项目
  • 点击右上方 CircleCI 仪表盘中的项目重心按钮
  • 构建设置部分,点击环境变量
  • 点击添加变量

Add an Environment Variable对话框中,你将定义这个构建所需的几个环境变量。下面是必须定义的环境变量列表:

  • 名称:AWS_ECR_ACCOUNT_URL值:您的 AWS ECR 注册表 URL
  • 名称:AWS_ACCESS_KEY_ID值:Your AWS IAM Account's Access Key ID
  • 名称:AWS_SECRET_ACCESS_KEY值:Your AWS IAM Account's Secret Access Key
  • 名称:DOCKER_LOGIN值:Your Docker Hub User Name
  • 名称:Docker_PWD值:Your Docker Hub Password

正确设置这些环境变量对于构建成功完成至关重要。在继续下一节之前,请确保正确设置了这些变量及其值。

使用 CircleCI AWS ECR Orb

下面是定义这个项目管道的config.yml文件。在接下来的部分中,我将解释这个config.yml文件的各种元素。

version: 2.1

orbs:
  aws-ecr: circleci/aws-ecr@0.0.4

workflows:
  build_test_deploy:
    jobs:
      - build_test
      - docker_hub_build_push_image:
          requires:
            - build_test
      - aws-ecr/build_and_push_image:
          region: us-east-1
          account-url: ${AWS_ECR_ACCOUNT_URL}
          repo: ${CIRCLE_PROJECT_REPONAME}
          tag: ${CIRCLE_BUILD_NUM}
          requires:
            - build_test
jobs:
  build_test:
    docker:
      - image: circleci/python:2.7.14
    steps:
      - checkout
      - run:
          name: Setup VirtualEnv
          command: |
            virtualenv helloworld
            . helloworld/bin/activate
            pip install --no-cache-dir -r requirements.txt
      - run:
          name: Run Tests
          command: |
            . helloworld/bin/activate
            python test_hello_world.py
  docker_hub_build_push_image:
    docker:
      - image: circleci/python:2.7.14
    steps:
      - checkout
      - setup_remote_docker:
          docker_layer_caching: false
      - run:
          name: Build and push Docker image to Docker Hub
          command: |
            echo 'export TAG=0.1.${CIRCLE_BUILD_NUM}' >> ${BASH_ENV}
            echo 'export IMAGE_NAME=${CIRCLE_PROJECT_REPONAME}' >> ${BASH_ENV}
            source ${BASH_ENV}
            docker build -t ${DOCKER_LOGIN}/${IMAGE_NAME} -t ${DOCKER_LOGIN}/${IMAGE_NAME}:${TAG} .
            echo ${DOCKER_PWD} | docker login -u ${DOCKER_LOGIN} --password-stdin
            docker push ${DOCKER_LOGIN}/${IMAGE_NAME} 

指定工作流和 orb

orbs:
  aws-ecr: circleci/aws-ecr@0.0.4 

orbs:键指定一个 orb 将在这个管道中使用。aws-ecr:键定义了配置中使用的内部名称。circleci/aws-ecr@0.0.4值指定并关联由aws-ecr:键使用和引用的实际 orb。这些 orb 语句可以被认为是其他语言和框架中的 import 语句。

workflows:
  build_test_deploy:
    jobs:
      - build_test
      - docker_hub_build_push_image:
          requires:
            - build_test
      - aws-ecr/build_and_push_image:
          region: us-east-1
          account-url: ${AWS_ECR_ACCOUNT_URL}
          repo: ${CIRCLE_PROJECT_REPONAME}
          tag: ${CIRCLE_BUILD_NUM}
          requires:
            - build_test 

工作流定义

workflows:键指定了由构建作业和 orb 组成的工作流列表。该段指定了一个称为build_test_deploy:的工作流

jobs:
  - build_test
  - docker_hub_build_push_image:
      requires:
        - build_test 

在这个片段中,指定了一个jobs:键,列出了在这个管道中执行的所有作业和 orb。该列表中的第一个作业是build_test,它没有作业依赖性。

Orb 定义

工作流列表中的下一个任务docker_hub_build_push_image:指的是稍后将讨论的任务,但是请注意requires:键,它指定docker_hub_build_push_image:任务依赖于build_test任务的通过。否则,整个构建将会失败,并且工作流中的剩余作业将不会执行。

- aws-ecr/build_and_push_image:
    region: us-east-1
    account-url: ${AWS_ECR_ACCOUNT_URL}
    repo: ${CIRCLE_PROJECT_REPONAME}
    tag: ${CIRCLE_BUILD_NUM}
    requires:
      - build_test 

上面的部分显示了指定 AWS ECR Orb 执行的aws-ecr/build_and_push_image:键。在本例中,AWS ECR Orb 的参数需要分配给内置环境变量的值。有关此 Orb 的更多详细信息,请参见 AWS ECR Orb 文档。这个特定的 AWS ERC Orb 依赖于在执行这个 Orb 的build_and_push_image方法之前成功完成的build_test:作业。

作业定义

我已经讨论了配置中的workflows:orbs:元素,它们是对该配置的主jobs:元素中定义的作业的引用。以下代码段显示了在此配置语法中定义的所有作业。

jobs:
  build_test:
    docker:
      - image: circleci/python:2.7.14
    steps:
      - checkout
      - run:
          name: Setup VirtualEnv
          command: |
            virtualenv helloworld
            . helloworld/bin/activate
            pip install --no-cache-dir -r requirements.txt
      - run:
          name: Run Tests
          command: |
            . helloworld/bin/activate
            python test_hello_world.py
  docker_hub_build_push_image:
    docker:
      - image: circleci/python:2.7.14
    steps:
      - checkout
      - setup_remote_docker:
          docker_layer_caching: false
      - run:
          name: Build and push Docker image to Docker Hub
          command: |
            echo 'export TAG=0.1.${CIRCLE_BUILD_NUM}' >> ${BASH_ENV}
            echo 'export IMAGE_NAME=${CIRCLE_PROJECT_REPONAME}' >> ${BASH_ENV}
            source ${BASH_ENV}
            docker build -t ${DOCKER_LOGIN}/${IMAGE_NAME} -t ${DOCKER_LOGIN}/${IMAGE_NAME}:${TAG} .
            echo ${DOCKER_PWD} | docker login -u ${DOCKER_LOGIN} --password-stdin
            docker push ${DOCKER_LOGIN}/${IMAGE_NAME} 

在上面的片段中,定义了两个作业build_test:docker_hub_build_push-image:,演示了原始配置语法。build_test:作业实例化一个 Python 容器,安装应用程序依赖项,并运行应用程序的单元测试。

docker_hub_build_push_image:作业设置用于命名和标记图像的环境变量,基于 Docker 文件构建 Docker 图像,并将构建的图像推送到 Docker Hub。

这整个配置演示了如何定义和实现工作流、orb 和作业,它们在配置语法上提供了健壮和强大的功能。成功执行这个管道后,您应该在 Docker Hub 和 AWS ECR 存储库中得到一个经过测试的 Docker 映像。

摘要

如您所见,与 AWS ECR Orb 定义相比,jobs:段更加冗长。orb 旨在封装功能,提供一致性并减少管道配置中的重复代码量,从而减少冗长的配置语法。

使用 orbs,您可以在团队和项目之间共享您首选的 CI/CD 设置,并且只需几行代码就可以轻松集成工具和第三方解决方案。开发社区的成员可以编写 orb 来解决常见问题并帮助管理配置。在常见用例中共享和重用 orb 使团队能够解决有趣的、独特的问题,使他们的业务与众不同,同时为社区中的其他人提供加速 CI/CD 工作流的解决方案。

我期待 orb 的未来发展,以及它们给 CI/CD 管道带来的令人敬畏的流线型功能。如果您浏览 orb 注册表,没有找到符合您需要的功能的 orb,那么我鼓励您创作您自己的定制 orb 。请注意,目前 CircleCI Orb 注册表上托管的所有 Orb 都是开放的,可供公众使用。注册中心目前还不支持私有 orb,但是这个特性已经在 orb 的路线图上了,将来会提供。

参考

要了解有关 orb、工作流和 CircleCI 的更多信息,请使用下列参考资料:

我们的云平台,您的计算:CircleCI runner 简介

原文:https://circleci.com/blog/our-cloud-platform-your-compute-introducing-the-circleci-runner/

从今天开始,circle ci runner将让我们新规模计划的客户能够选择哪些作业在云中运行,哪些作业在他们自己的基础设施上运行。

我们为什么要造转轮?

我们的目标一直是建立一个平台,让开发者做他们最擅长的事情:创造伟大的东西。首先是减轻为持续集成和部署而维护基础设施的负担。

在 CircleCI 之前,工程团队必须担心保持构建机器运行、清理依赖关系、更新插件、维护映像等等。在过去的九年里,我们一直在不断改进我们的云托管计算选项,消除了所有这些开销。

尽管在云中使用我们的机队速度很快,也很容易,但在一些边缘情况下,它不能满足我们客户的所有需求。例如,我们在金融和医疗保健等监管更严格的行业中的一些大客户必须满足合规性要求,以防止他们在云中运行某些工作负载。其他从事嵌入式系统或物联网工作的人需要在云中根本不存在的硬件上构建。我们构建了 runner 来满足这些要求,因此即使是具有最严格的安全性和合规性要求的客户也可以使用 CircleCI 来满足他们 100%的软件交付需求。

跑步者如何工作

我们对 runner 的目标是为我们的用户提供最大的灵活性,因此我们将其构建为可以在任何云或内部的任何环境中运行,无论您的网络是如何设计的。runner 软件本身是一个简单的应用程序,您可以将其安装在自托管的机器上。一旦设置好了,你给跑步者分配一个名字,称为resource_class,它就在 CircleCI 服务中轮询工作。当一个作业正在进行时,runner 报告状态并记录回 CircleCI,这些显示在 UI 中,就像其他任何作业一样。

通过我们的云托管计算无缝使用 runner

runner 提供了额外的灵活性和控制,但它也带来了管理基础架构的额外责任,这是我们努力为客户消除的。尽管在使用 runner 时会有一些维护开销,但是我们希望确保您的团队只在绝对必要的时候管理基础设施。我们确保使用 runner 的作业与使用我们的云托管计算的作业无缝协作。您可以像现在一样继续使用我们的托管环境,但现在您可以使用 runner 将之前因安全限制或计算要求而受阻的工作负载添加到您现有的管道和工作流中。您甚至可以使用工作区在自托管作业和云托管作业之间共享数据。

规模计划

自从我们推出性能计划(我们的第一个基于使用的定价计划)以来,已经过去了一年多。在这段时间里,我们看到了平台使用的巨大增长,我们认识到使用 CircleCI 的最大和最复杂的组织需要一个比性能更先进和更灵活的计划。

对于这些组织,我们很高兴推出规模计划。Scale 提供对我们的云托管平台的无限制访问,但具有额外的控制,有助于应对大规模运营 CI/CD 的独特挑战。该计划为客户提供自托管运行程序、无限并发机器、包括 GPU 在内的我们最大的资源类别,并包括金牌支持,以确保客户在 CircleCI 的投资获得最大回报。

下一步是什么?

CircleCI runner 和我们的新规模计划是我们领先的云托管 CI/CD 平台的最新成员,也是我们满足企业软件组织独特需求的第一步。在接下来的几个月中,我们将推出更多功能,使使用 CircleCI 大规模操作 CI/CD 管道变得更加容易。我们最近还收到了 SOC 2 Type II 报告,让我们的企业客户对 CircleCI 平台更有信心。

如果使用 CircleCI 跑步者听起来像是你的团队的正确解决方案,你可以在这里找到更多信息

Clojure Web 框架使用 Docker - Clojure web 开发| CircleCI

原文:https://circleci.com/blog/package-a-clojure-web-application-using-docker/

这是关于构建、测试和部署 Clojure web 应用程序的系列文章的第二篇。你可以在这里找到第一帖,在这里找到第三帖

在这篇文章中,我们将关注如何向应用程序添加生产数据库(在本例中是 PostgreSQL ),如何将应用程序打包为 Docker 实例,以及如何在 Docker 中运行应用程序和数据库。为了跟进,我建议浏览第一篇文章,并按照步骤创建应用程序。否则,您可以通过分叉这个存储库并检查主分支来获得源代码。如果你选择这种方法,你还需要按照第一篇文章中的描述设置你的 CircleCI 账户。

尽管我们正在构建一个 Clojure 应用程序,但是本系列的这一部分并不需要太多 Clojure 知识。

先决条件

为了构建这个 web 应用程序,您需要安装以下软件:

  1. Java JDK 8 或更高版本——clo jure 运行在 Java 虚拟机上,实际上只是一个 Java 库(JAR)。我用版本 8 构建了这个,但是一个更好的版本应该也可以。
  2. Leiningen - Leiningen,通常被称为 lein(读作‘line’)是最常用的 Clojure 构建工具。
  3. Git -无处不在的分布式版本控制工具。
  4. Docker——一个工具,旨在通过使用容器来简化应用程序的创建、部署和运行。
  5. Docker Compose -一个定义和运行多容器 Docker 应用程序的工具。

您还需要注册:

  1. CircleCI 账号 - CircleCI 是一个持续集成和交付平台。
  2. GitHub 账户 - GitHub 是一个基于网络的托管服务,使用 Git 进行版本控制。
  3. Docker Hub 帐户 - Docker Hub 是一个基于云的存储库,Docker 用户和合作伙伴可以在其中创建、测试、存储和分发容器映像。

运行 PostgreSQL 数据库

在本节中,我们将介绍如何运行 PostgreSQL 数据库,我们将从本博客系列的第一部分中构建的 web 应用程序连接到该数据库。

我们将使用 Docker 来“打包”我们的应用程序以进行部署。已经有很多关于为什么以及如何使用 Docker 的文章,比我计划在这里讨论的还要详细。我之所以决定使用它,是为了提供与我们要部署到的物理机器的一定程度的隔离,我认为,更重要的是,为了确保应用程序在本地或远程环境之间运行时的运行时行为的一致性。

出于这个原因,最终的游戏将是运行我们在上一篇博客中构建的 web 应用程序和 Docker 中的 PostgreSQL 数据库,并让两者进行通信。

该应用程序当前在开发模式下运行时使用 SQLite。在博客系列的第一部分中,我们只在开发模式下运行,要么使用lein repl从 REPL 运行服务器,要么使用lein test运行单元测试。如果我们通过从我们的项目目录中发出lein run命令,尝试在生产模式下运行应用程序,我们将得到一个错误,因为没有指定生产数据库连接。

$ lein run
Exception in thread "main" clojure.lang.ExceptionInfo: Error on key :duct.database.sql/hikaricp when building system {:reason :integrant.core/build-threw-exception ... 

我们将使用官方的 postgres Docker 映像 (alpine 版本)在 Docker 容器中运行数据库。为此,我们可以发出以下命令:

$ docker run -p 5432:5432 -e POSTGRES_USER=filmuser -e POSTGRES_DB=filmdb -e POSTGRES_PASSWORD=password postgres:alpine
...
2019-01-20 10:08:32.064 UTC [1] LOG:  database system is ready to accept connections 

这个命令运行 postgres Docker 镜像(如果需要,从 Docker Hub 中下载),数据库监听 TCP 端口 5432,设置一个名为filmuser的默认用户,将该用户的密码设置为password,并创建一个名为filmdb的空数据库。如果您已经将 PostgreSQL 作为一项服务安装在您的计算机上,您可能会收到一条关于端口 5432 正在使用的消息。如果发生这种情况,要么停止本地 PostgreSQL 服务,要么更改-p 5432:5432条目以公开不同的端口,例如端口 5500 -p 5500:5432

为了检查您是否可以连接到数据库,请在不同的终端窗口中发出以下命令:

psql -h localhost -p 5432 -U filmuser filmdb
Password for user filmuser:
psql (11.1 (Ubuntu 11.1-1.pgdg16.04+1))
Type "help" for help.

filmdb=# 

虽然您现在已经连接到了数据库,但是此时您还不能做很多事情,因为我们还没有创建任何表、视图等(关系)。

filmdb=# \d
Did not find any relations. 

所以让我们关闭 psql 实用程序。

filmdb=# exit 

接下来,让 postgres 的 Docker 容器保持运行,并更改我们的应用程序,使其具有可以连接到数据库的生产配置。

打开电影分级项目目录中的resources/film_ratings/config.edn文件。然后找到:duct.module/sql条目,并在它下面添加以下内容:

:duct.database.sql/hikaricp {:adapter "postgresql"
                              :port-number #duct/env [ "DB_PORT" :or "5432" ]
                              :server-name #duct/env [ "DB_HOST" ]
                              :database-name "filmdb"
                              :username "filmuser"
                              :password #duct/env [ "DB_PASSWORD" ]} 

此条目使用 PostgreSQL 定义了光连接池的配置。注意,我们从环境变量 DB_HOSTDB_PASSWORD中获取服务器名称和密码。我们还考虑了一个可选的环境变量DB_PORT,但是如果需要的话,可以用来设置应用程序连接到不同的端口而不是5432

您还需要在project.clj文件中添加 PostgreSQL 数据库驱动程序和 hikaricp 库的依赖项,因此依赖项部分如下所示:

:dependencies [[org.clojure/clojure "1.9.0"]
                 [duct/core "0.6.2"]
                 [duct/module.logging "0.3.1"]
                 [duct/module.web "0.6.4"]
                 [duct/module.ataraxy "0.2.0"]
                 [duct/module.sql "0.4.2"]
                 [org.xerial/sqlite-jdbc "3.21.0.1"]
                 [org.postgresql/postgresql "42.1.4"]
                 [duct/database.sql.hikaricp "0.3.3"]
                 [hiccup "1.0.5"]] 

此外,我们希望在插入一部新电影时,id列被自动分配一个唯一的编号,因此我们需要稍微改变一下迁移,以便id列的类型不再是整数(适用于 SQLite ),而是 PostgreSQL 中的类型serial。这意味着您需要将resources/film_ratings/config.edn中的 migrator ragtime 条目更改为:

[:duct.migrator.ragtime/sql :film-ratings.migrations/create-film]
 {:up ["CREATE TABLE film (id SERIAL PRIMARY KEY, name TEXT UNIQUE, description TEXT, rating INTEGER)"]
  :down ["DROP TABLE film"]} 

为了测试这个配置,首先需要设置环境变量。因此,从运行 Docker postgres 实例的终端窗口中打开一个单独的终端窗口,并像这样设置两个环境变量:

$ export DB_HOST=localhost
$ export DB_PASSWORD=password 

注意 : 如果您必须更改 postgres Docker 实例正在使用的端口号,您还需要将DB_PORT环境变量设置为相同的端口号。

一旦您设置了这些环境变量,您就可以在生产配置文件中运行应用程序,如下所示(首先将目录更改为您的项目根目录):

$ lein run
lein run
19-01-21 07:19:51 chris-XPS-13-9370 REPORT [duct.server.http.jetty:13] - :duct.server.http.jetty/starting-server {:port 3000} 

正如您从输出中看到的,我们的迁移(在博客的第一部分中定义)并不是为了插入film表而运行的。默认情况下,迁移不是通过生产配置文件中的管道运行的,但是我们将在以后解决这个问题。为了创建film表,我们可以通过打开另一个终端会话并执行以下命令来手动运行我们的迁移(在设置环境变量并将目录更改为项目根目录之后):

$ lein run :duct/migrator
19-01-21 07:48:59 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["CREATE TABLE ragtime_migrations (id varchar(255), created_at varchar(32))"], :elapsed 4}
19-01-21 07:48:59 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["SELECT id FROM ragtime_migrations ORDER BY created_at"], :elapsed 6}
19-01-21 07:48:59 chris-XPS-13-9370 REPORT [duct.migrator.ragtime:14] - :duct.migrator.ragtime/applying :film-ratings.migrations/create-film#11693a5d
19-01-21 07:48:59 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["CREATE TABLE film (id SERIAL PRIMARY KEY, name TEXT UNIQUE, description TEXT, rating INTEGER)"], :elapsed 10}
19-01-21 07:48:59 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["INSERT INTO ragtime_migrations ( id, created_at ) VALUES ( ?, ? )" ":film-ratings.migrations/create-film#11693a5d" "2019-01-21T07:48:59.960"], :elapsed 4}
19-01-21 07:48:59 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:31] - :duct.database.sql/batch-query {:queries [], :elapsed 0} 

您现在可以通过打开浏览器并指向http://localhost:3000来测试正在运行的应用程序。

如果您尝试添加电影,您将看到一条错误消息,指出列rating是整数类型,但提供的值是字符类型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是因为 add film 表单中的所有值都是字符串,但是film表中的ratings列需要一个INTEGER。在我们的开发模式下不会发生这种情况,因为 SQLite 数据库驱动程序将评级的字符串值强制转换为整数,但 PostgreSQL 驱动程序不会。这说明使用不同的数据库进行生产和开发可能会有问题!

现在,让我们修复处理程序中的这个错误。编辑src/film_ratings/handler/film.clj文件,将处理电影形式的代码重构为一个单独的函数,该函数还处理将ratings强制为整数的操作:

(defn- film-form->film
  [film-form]
  (as-> film-form film
    (dissoc film "__anti-forgery-token")
    (reduce-kv (fn [m k v] (assoc m (keyword k) v))
               {}
               film)
    (update film :rating #(Integer/parseInt %))))

(defmethod ig/init-key :film-ratings.handler.film/create [_ {:keys [db]}]
  (fn [{[_ film-form] :ataraxy/result :as request}]
    (let [film (film-form->film film-form)
          result (boundary.film/create-film db film)
          alerts (if (:id result)
                   {:messages ["Film added"]}
                   result)]
      [::response/ok (views.film/film-view film alerts)]))) 

为了测试这一点,停止运行应用程序的服务器并重新启动它(CTRL-C 停止,lein run重新启动)。然后将您的浏览器指向http://localhost:3000并试用该应用程序。

一旦您对一切正常感到满意,现在您可以使用 CTRL-C 关闭服务器,并通过在运行 Docker 实例的终端会话中发出 CTRL-C 来关闭 Docker postgres 实例。

为了确保我们没有破坏任何东西,我们可以尝试在开发模式下运行服务器,这应该使用 SQLite 数据库实例。

$ lein repl
 lein repl
nREPL server started on port 38553 on host 127.0.0.1 - nrepl://127.0.0.1:38553
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.9.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_191-b12
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> (dev)
:loaded
dev=> (go)
Jan 20, 2019 12:16:20 PM org.postgresql.Driver connect
SEVERE: Connection error:
org.postgresql.util.PSQLException: Connection to localhost:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections. 

我们的配置应用方式有问题。这里不使用dev/resources/dev.edn中的配置:

:duct.module/sql
{:database-url "jdbc:sqlite:db/dev.sqlite"} 

我们似乎在resources/config.edn中至少获得了一些 postgres 配置。实际发生的是开发配置和生产配置映射被合并。因此,postgres 适配器配置和其余的键-值对被添加到:database-url中。我们需要解决这个问题,所以让我们在dev/src/dev.clj文件中添加一些代码来删除这些生产数据库属性。让我们添加一个函数来完成这项工作,并从对set-prep!的调用中调用这个新函数:

(defn remove-prod-database-attributes
  "The prepared config is a merge of dev and prod config and the prod attributes for 
  everything except :jdbc-url need to be dropped or the sqlite db is 
  configured with postgres attributes"
  [config]
  (update config :duct.database.sql/hikaricp 
    (fn [db-config] (->> (find db-config :jdbc-url) (apply hash-map)))))

(integrant.repl/set-prep! 
  (comp remove-prod-database-attributes duct/prep read-config)) 

现在,如果我们像这样在开发模式下运行服务器(使用quit退出之前运行的 repl):

$ lein repl
nREPL server started on port 44721 on host 127.0.0.1 - nrepl://127.0.0.1:44721
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.9.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_191-b12
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> (dev)
:loaded
dev=> (go)
:duct.server.http.jetty/starting-server {:port 3000}
:initiated
dev=> 

注意: 如果你克隆了我的回购协议,而不是按照之前的博客,你可能会看到这样的错误:SQLException path to 'db/dev.sqlite': '.../blog-film-ratings/db' does not exist org.sqlite.core.CoreConnection.open (CoreConnection.java:192)。在这种情况下,使用mkdir db在项目根目录下手动创建一个空的‘db’目录,然后重试 repl 命令。

我们可以看到服务器现在成功启动,并连接到 SQLite 数据库。现在,让我们提交我们的更改。

$ git add --all .
$ git commit -m "Add production database config, dependencies & fix dev db config"
$ git push 

在 Docker 中运行我们的应用程序

学习了如何在 Docker 中运行 PostgreSQL 数据库,以及如何从我们的应用程序连接到它,下一步是在 Docker 中运行我们的应用程序。

我们将添加一个 Docker 文件以在 Docker 中构建我们的应用程序,并且我们将使用 Docker Compose 在它们自己的网络中同时运行我们的应用程序 Docker 实例和 postgres Docker 实例。

在我们创建 docker 文件之前,让我们检查一下我们的 web 应用程序将如何在 docker 文件中实际运行。到目前为止,我们一直使用 repl 在开发模式下运行我们的应用程序,或者使用 lein 在生产模式下运行我们的应用程序。

通常,Clojure 应用程序在生产中的运行方式与 Java 应用程序相同。最常见的是将应用程序编译成。类文件,然后将它们打包在一个 Uberjar 中,这是一个包含所有。我们的应用程序所需的类文件和所有依赖库(jar 文件)以及任何资源文件(例如,我们的配置文件)。然后,这个 Uberjar 将使用 JVM (Java 虚拟机)运行。

在我们必须在 Docker 内部运行应用程序之前,让我们尝试在本地以这种方式运行我们的应用程序。首先,我们可以使用以下命令在 Uberjar 中编译和打包我们的应用程序:

$ lein uberjar
Compiling film-ratings.main
Compiling film-ratings.views.template
Compiling film-ratings.views.film
Compiling film-ratings.views.index
Compiling film-ratings.boundary.film
Compiling film-ratings.handler.film
Compiling film-ratings.handler.index
Created /home/chris/circleciblogs/repos/film-ratings/target/film-ratings-0.1.0-SNAPSHOT.jar
Created /home/chris/circleciblogs/repos/film-ratings/target/film-ratings.jar 

这将创建两个 JAR 文件。标记为快照的那个只包含没有库依赖关系的应用程序,而名为film-ratings.jar的那个是我们的 Uberjar,包含所有的依赖关系。我们现在可以从这个 Uberjar 运行我们的应用程序,但是首先要确保 postgres 的 Docker 实例正在运行,并且在发出这个命令之前,已经在终端会话中设置了DB_HOSTDB_PASSWORD环境变量:

$ java -jar target/film-ratings.jar
19-01-22 07:26:20 chris-XPS-13-9370 REPORT [duct.server.http.jetty:13] - :duct.server.http.jetty/starting-server {:port 3000} 

我们现在需要做的是编写一个 docker 文件来执行这个命令。这意味着我们需要 Docker 实例拥有一个 Java 运行时环境(在这种情况下,我们实际上使用的是 Java 开发工具包环境)。在项目基本目录中创建 Dockerfile 文件,并添加以下内容:

FROM openjdk:8u181-alpine3.8

WORKDIR /

COPY target/film-ratings.jar film-ratings.jar
EXPOSE 3000

CMD java -jar film-ratings.jar 

您现在可以像这样构建 Docker 实例:

$ docker build . -t film-ratings-app
Sending build context to Docker daemon  23.71MB
Step 1/5 : FROM openjdk:8u181-alpine3.8
 ---> 04060a9dfc39
Step 2/5 : WORKDIR /
 ---> Using cache
 ---> 2752489e606e
Step 3/5 : COPY target/film-ratings.jar film-ratings.jar
 ---> Using cache
 ---> b282e93eff39
Step 4/5 : EXPOSE 3000
 ---> Using cache
 ---> 15d2e1b9197e
Step 5/5 : CMD java -jar film-ratings.jar
 ---> Using cache
 ---> 2fe0b1e058e5
Successfully built 2fe0b1e058e5
Successfully tagged film-ratings-app:latest 

这创建了一个新的 Docker 图像,并将其标记为film-ratings-app:latest。我们现在可以像这样运行我们的 dockerized 应用程序:

$ docker run --network host -e DB_HOST=localhost -e DB_PASSWORD=password film-ratings-app
19-01-22 09:12:20 chris-XPS-13-9370 REPORT [duct.server.http.jetty:13] - :duct.server.http.jetty/starting-server {:port 3000} 

但是,我们仍然存在之前遇到的问题,即迁移没有运行。如果您打开浏览器进入http://localhost:3000/list-films,您可以演示这一点。您会看到内部服务器错误,并且会在 Docker 中运行应用程序的终端会话中看到一个巨大的堆栈跟踪,该跟踪以以下内容结束:

serverErrorMessage: #object[org.postgresql.util.ServerErrorMessage 0x5661cc86 "ERROR: relation \"film\" does not exist\n  Position: 15"] 

为了解决这个问题,我们将做一些实际上不建议可伸缩生产服务器做的事情,让迁移在应用程序启动时运行。更好的方法是在生产环境中有一个单独的 Docker 实例,它可以按需启动,可能是通过 CI 管道,在发生变化时运行迁移。为了这篇博客的目的,让我们采用更简单的方法,将我们的主函数改为调用迁移器。打开并编辑src/film_ratings/main.clj文件,如下所示:

(defn -main [& args]
  (let [keys (or (duct/parse-keys args) [:duct/migrator :duct/daemon])]
    (-> (duct/read-config (io/resource "film_ratings/config.edn"))
        (duct/prep keys)
        (duct/exec keys)))) 

这确保了在守护程序启动服务器之前,调用:duct\migrator Integrant 键来运行迁移。为了将这一变化加入到我们的 Docker 映像中,我们需要重新运行lein uberjardocker build . -t film-ratings-app。然后我们可以在 Docker 中运行我们的应用程序:

$ docker run --network=host -e DB_HOST=localhost -e DB_PASSWORD=password film-ratings-app
19-01-22 18:08:23 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["CREATE TABLE ragtime_migrations (id varchar(255), created_at varchar(32))"], :elapsed 2}
19-01-22 18:08:23 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["SELECT id FROM ragtime_migrations ORDER BY created_at"], :elapsed 11}
19-01-22 18:08:23 chris-XPS-13-9370 REPORT [duct.migrator.ragtime:14] - :duct.migrator.ragtime/applying :film-ratings.migrations/create-film#11693a5d
19-01-22 18:08:23 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["CREATE TABLE film (id SERIAL PRIMARY KEY, name TEXT UNIQUE, description TEXT, rating INTEGER)"], :elapsed 13}
19-01-22 18:08:23 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["INSERT INTO ragtime_migrations ( id, created_at ) VALUES ( ?, ? )" ":film-ratings.migrations/create-film#11693a5d" "2019-01-22T18:08:23.146"], :elapsed 3}
19-01-22 18:08:23 chris-XPS-13-9370 INFO [duct.database.sql.hikaricp:31] - :duct.database.sql/batch-query {:queries [], :elapsed 0}
19-01-22 18:08:23 chris-XPS-13-9370 REPORT [duct.server.http.jetty:13] - :duct.server.http.jetty/starting-server {:port 3000} 

这一次,我们可以看到在服务器开始添加film表之前,迁移正在运行。现在,如果我们打开指向http://localhost:3000/list-films的浏览器,我们会看到“找不到电影”消息。您可以通过添加一些电影来试用该应用程序。

让我们在继续之前提交这些更改。

$ git add --all .
$ git commit -m "Add Dockerfile & call migrations on startup."
$ git push 

持久数据

我们还有一些问题。目前,如果我们停止 postgres Docker 容器进程并重新启动它,我们将丢失所有的电影。此外,我们通过主机网络在两个 Docker 容器之间进行通信。这意味着如果您在本地运行 PostgreSQL 服务器,您将会遇到端口冲突。

让我们通过创建 Docker 合成文件来解决这两个问题。我们的 Docker Compose 文件将构建我们的应用程序 Docker 文件,设置适当的环境变量并启动 postgres 实例。Docker Compose 将通过桥接网络促进应用程序和数据库之间的通信,这样我们就不会在本地主机上发生端口冲突,除了应用程序暴露的端口 3000。

在根项目目录中创建一个docker-compose.yml文件,并添加以下内容:

version: '3.0'
services:
  postgres:
    restart: 'always'
    environment:
      - "POSTGRES_USER=filmuser"
      - "POSTGRES_DB=filmdb"
      - "POSTGRES_PASSWORD=${DB_PASSWORD}"
    volumes:
      - /tmp/postgresdata:/var/lib/postgresql/data
    image: 'postgres:alpine'
  filmapp:
    restart: 'always'
    ports:
      - '3000:3000'
    environment:
      - "DB_PASSWORD=${DB_PASSWORD}"
      - "DB_HOST=postgres"
    build:
      context: .
      dockerfile: Dockerfile 

该文件在 docker-compose 中注册了两个服务:一个名为 postgres,它使用postgres:alpine映像并设置适当的 postgres 环境变量;另一个名为filmapp,它构建我们的 Dockerfile,运行它并公开端口 3000 并设置它的环境变量。

您还可以看到,我们已经定义了一个卷,该卷将本地机器上的目录/tmp/postgresdata映射到容器上的/var/lib/postgresql/data,该容器是 postgres 的数据目录。

这意味着,当我们运行 docker-compose 进程时,存储在数据库中的任何数据都将被写入本地的/tmp/postgresdata,甚至在我们重新启动 docker-compose 进程后,这些数据也将持续存在。

让我们试试这个。首先,我们构建 docker-compose 图像(确保您已经首先设置了DB_PASSWORD环境变量)。

$ docker-compose build
postgres uses an image, skipping
Building filmapp
Step 1/5 : FROM openjdk:8u181-alpine3.8
 ---> 04060a9dfc39
Step 2/5 : WORKDIR /
 ---> Using cache
 ---> 2752489e606e
Step 3/5 : COPY target/film-ratings.jar film-ratings.jar
 ---> b855626e4a45
Step 4/5 : EXPOSE 3000
 ---> Running in 7721d74eee62
Removing intermediate container 7721d74eee62
 ---> f7caccf63c3b
Step 5/5 : CMD java -jar film-ratings.jar
 ---> Running in 89b75d045897
Removing intermediate container 89b75d045897
 ---> 48303637af01
Successfully built 48303637af01
Successfully tagged film-ratings_filmapp:latest 

现在,让我们启动 docker-compose(首先退出任何正在运行的 Docker postgres 或 filmapp 实例)。

$ docker-compose up
Starting film-ratings_filmapp_1  ... done
Starting film-ratings_postgres_1 ... done
Attaching to film-ratings_filmapp_1, film-ratings_postgres_1
...
postgres_1  | 2019-01-22 18:35:58.117 UTC [1] LOG:  database system is ready to accept connections
filmapp_1   | 19-01-22 18:36:06 5d9729ccfdd0 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["CREATE TABLE ragtime_migrations (id varchar(255), created_at varchar(32))"], :elapsed 4}
filmapp_1   | 19-01-22 18:36:06 5d9729ccfdd0 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["SELECT id FROM ragtime_migrations ORDER BY created_at"], :elapsed 11}
filmapp_1   | 19-01-22 18:36:06 5d9729ccfdd0 REPORT [duct.migrator.ragtime:14] - :duct.migrator.ragtime/applying :film-ratings.migrations/create-film#11693a5d
filmapp_1   | 19-01-22 18:36:06 5d9729ccfdd0 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["CREATE TABLE film (id SERIAL PRIMARY KEY, name TEXT UNIQUE, description TEXT, rating INTEGER)"], :elapsed 12}
filmapp_1   | 19-01-22 18:36:06 5d9729ccfdd0 INFO [duct.database.sql.hikaricp:30] - :duct.database.sql/query {:query ["INSERT INTO ragtime_migrations ( id, created_at ) VALUES ( ?, ? )" ":film-ratings.migrations/create-film#11693a5d" "2019-01-22T18:36:06.120"], :elapsed 3}
filmapp_1   | 19-01-22 18:36:06 5d9729ccfdd0 INFO [duct.database.sql.hikaricp:31] - :duct.database.sql/batch-query {:queries [], :elapsed 0}
filmapp_1   | 19-01-22 18:36:06 5d9729ccfdd0 REPORT [duct.server.http.jetty:13] - :duct.server.http.jetty/starting-server {:port 3000} 

从消息中可以看出,我们已经启动了 postgres 服务,然后是 filmapp 服务。filmapp 服务已经连接到 postgres 服务(通过在config.edn中拾取的DB_HOST=postgres环境变量)。

filmapp 服务已经运行了迁移以添加film表,并且现在正在侦听端口 3000。

您现在可以尝试添加一些电影。然后,通过在另一个终端会话中运行项目根目录中的docker-compose down,或者在运行的会话中按 CTRL-C 键,停止 docker-compose 进程。

如果您随后使用docker-compose up -d重新启动 docker-compose 服务,您应该会发现您添加的任何电影数据都被保存了下来。

注意:-d标志运行 docker-compose detached,因此您必须运行docker-compose logs来查看日志输出,运行docker-compose down来关闭服务。

如果您想再次从一个空数据库开始,只需删除/tmp/postgresdata目录并再次调用 docker-compose 服务。

同样,让我们在继续之前提交 docker-compose 文件。

$ git add --all .
$ git commit -m "Added docker-compose"
$ git push 

将 Docker 映像发布到 Docker Hub

我们几乎已经完成了我们在这个博客中设定的目标。我们想做的最后一件事是将我们的 Docker 映像推送到 Docker Hub,最好作为我们持续集成系统的一部分。

首先,让我们手动操作。

手动发布到 Docker Hub

如果你还没有这样做,在 Docker Hub 上创建一个账户。然后,在您的帐户中创建一个名为film-ratings-app的新存储库。

我们只想发布应用程序的 Docker 图像,因为我们不会将 docker-compose 文件用于生产。首先,让我们重新构建 Docker 映像,并用 Docker Hub 存储库 id(在我的例子中为chrishowejones/film-ratings-app)标记它:

$ docker build . -t chrishowejones/film-ratings-app
Sending build context to Docker daemon  23.72MB
Step 1/5 : FROM openjdk:8u181-alpine3.8
 ---> 04060a9dfc39
Step 2/5 : WORKDIR /
 ---> Using cache
 ---> 2752489e606e
Step 3/5 : COPY target/film-ratings.jar film-ratings.jar
 ---> Using cache
 ---> 60fe31dc32e4
Step 4/5 : EXPOSE 3000
 ---> Using cache
 ---> 672aa852b89a
Step 5/5 : CMD java -jar film-ratings.jar
 ---> Using cache
 ---> 1fdfcd0dc843
Successfully built 1fdfcd0dc843 

然后,我们需要像这样将该映像推送到 Docker Hub(记住使用您的 Docker Hub 存储库,而不是chrishowejones!):

$ docker push chrishowejones/film-ratings-app:latest
The push refers to repository [docker.io/chrishowejones/film-ratings-app]
25a31ca3ed23: Pushed
...
latest: digest: sha256:bcd2d24f7cdb927b4f1bc79c403a33beb43ab5b2395cbb389fb04ea4fa701db2 size: 1159 

添加 CircleCI 作业以构建 Docker 映像

好了,我们已经证明了可以手动推送。现在,让我们看看如何设置 CircleCI,每当我们标记一个版本的存储库时,circle ci 就会为我们做这件事。

首先,我们想使用 CircleCI 的一个名为 executors 的特性来减少重复。由于这个特性只在 2.1 版本中引入,我们需要打开我们的.circleci/config.yml文件,并将版本参考从2更改为2.1

version: 2.1
jobs:
... 

我们需要添加一个作业来为应用程序构建 Docker 图像,并添加另一个作业来发布 Docker 图像。我们将添加两个工作流来控制各个构建步骤的运行时间。

让我们首先在文件的顶部添加一个 executor,它将声明一些我们希望在两个新任务中重用的有用内容。

version: 2.1
executors:
    Docker-publisher:
      working_directory: ~/cci-film-ratings # directory where steps will run
      environment:
        IMAGE_NAME: chrishowejones/film-ratings-app
      Docker:
        - image: circleci/buildpack-deps:stretch
jobs:
... 

这个执行器声明了工作目录、本地环境变量IMAGE_NAME,以及一个 Docker 映像,该映像具有我们支持执行 Docker 命令所需的 buildpack 依赖关系。和以前一样,您需要更改图像名称值,以您的 Docker Hub 用户而不是我的用户作为前缀。

在我们开始添加作业来构建 Docker 映像之前,我们需要确保在build作业中构建的应用程序的 Uberjar 持久保存到我们将要编写的新作业中。

所以在build作业的底部,我们需要添加 CircleCI 命令来持久化工作空间。

 - run: lein do test, uberjar
      - persist_to_workspace:
          root: ~/cci-film-ratings
          paths:
            - target 

这将持久保存target目录,以便我们可以在新作业中重新附加该目录。现在,让我们将该作业添加到我们的build作业之后:

 build-docker:
    executor: docker-publisher
    steps:
      - checkout
      - attach_workspace:
          at: .
      - setup_remote_docker
      - run:
          name: Build latest Docker image
          command: docker build . -t $IMAGE_NAME:latest
      - run:
          name: Build tagged Docker image
          command: docker build . -t $IMAGE_NAME:${CIRCLE_TAG}
      - run:
          name: Archive Docker image
          command: docker save -o image.tar $IMAGE_NAME
      - persist_to_workspace:
          root: ~/cci-film-ratings
          paths:
            - ./image.tar 

好了,让我们来看看这个build-docker任务在做什么。首先,我们引用我们的docker-publisher执行器,这样我们就有了工作目录、IMAGE_NAME变量和正确的 Docker 映像。

接下来,我们检验我们的项目,以便我们有可用的应用程序的Dockerfile。之后,我们将保存的工作空间附加到我们当前的工作目录中,这样我们就可以在 Dockerfile 的正确路径上获得包含 jar 的target目录。

setup_remote_docker命令创建了一个远离主容器的环境,这是一个 Docker 引擎,我们可以用它来执行 Docker 构建/发布事件(尽管其他 Docker 事件也发生在我们的主容器中)。

接下来的两个 run 命令在 Docker 文件上执行 Docker 构建,并将结果图像分别标记为chrishowejones/film-ratings-app:latestchrishowejones/film-ratings-app:${CIRCLE_TAG}。在您的情况下,您应该将IMAGE_NAME更改为适合您的 Docker Hub 帐户的前缀,而不是chrishowejones

${CIRCLE_TAG}变量将被插入到与这个构建相关的 GitHub 标签中。这里的想法是当我们标记一个 commit 并将其推送到 GitHub 时触发这个作业。出于论证的目的,如果我们将提交标记为0.1.0并将其推送到 GitHub,当我们的build-docker作业运行时,它将构建一个标记为latest的 Docker 映像,但也会构建相同的映像并将其标记为0.1.0

docker save命令将IMAGE_NAME的所有 Docker 图像保存到一个 tar 存档文件中,然后我们在persist-to-workspace命令中保存该文件,这样我们可以在下一个任务中使用它。

添加 CircleCI 作业以发布到 Docker Hub

我们现在有一个构建两个 Docker 映像并持久化它们的作业,这样我们就可以在下一个作业中使用它们,下一个作业会将这两个映像推送到 Docker Hub。

让我们将该作业添加到config.yml中的build-docker作业之后:

 publish-docker:
        executor: docker-publisher
        steps:
          - attach_workspace:
              at: .
          - setup_remote_docker
          - run:
              name: Load archived Docker image
              command: docker load -i image.tar
          - run:
              name: Publish Docker Image to Docker Hub
              command: |
                echo "${DOCKERHUB_PASS}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin
                docker push $IMAGE_NAME:latest
                docker push $IMAGE_NAME:${CIRCLE_TAG} 

让我们来看看这份工作是做什么的。首先,它重用了带有工作区、环境变量和图像的执行器。接下来,它将持久化的工作空间附加到工作目录。然后,我们使用setup_remote_docker命令获取远程 Docker 引擎,这样我们就可以推送图像。

之后,我们运行 Docker 命令从持久化的 tar 文件中加载之前存储的 Docker 图像。下一个 run 命令使用两个尚未设置的环境变量DOCKERHUB_USERDOCKERHUB_PASS登录到 Docker Hub,然后将前一个作业中构建的两个 Docker 映像推送到 Docker Hub。

在我们继续之前,让我们在 CircleCI 中设置这两个环境变量,这样我们就不会忘记。登录 https://circleci.com/,进入你的仪表板 https://circleci.com/dashboard.为你的项目选择项目设置(点击工作侧边栏中所列项目的图标。)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

接下来,从BUILD SETTINGS部分选择Environment Variables,并添加两个新变量DOCKERHUB_USERDOCKERHUB_PASS,分别为您的 Docker Hub 用户名和密码设置适当的值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

添加工作流,以便在发布新版本时运行作业

我们现在有了构建和发布 Docker 映像所需的作业,但是我们还没有指定如何执行这些作业。默认情况下,CircleCI 将在每次推送 GitHub 时运行一个名为build的作业,但是额外的作业将被忽略,除非我们设置工作流来运行它们。让我们将工作流添加到.circleci/config.yml文件的底部。

workflows:
  version: 2.1
  main:
    jobs:
      - build
  build_and_deploy:
    jobs:
      - build:
          filters:
            branches:
              ignore: /.*/
            tags:
              only: /^\d+\.\d+\.\d+$/
      - build-docker:
          requires:
            - build
          filters:
            branches:
              ignore: /.*/
            tags:
              only: /^\d+\.\d+\.\d+$/
      - publish-docker:
          requires:
            - build-docker
          filters:
            branches:
              ignore: /.*/
            tags:
              only: /^\d+\.\d+\.\d+$/ 

main工作流指定build作业应该执行,并且当这发生时没有给出任何特殊的过滤器,因此build作业在每次推送到 GitHub 时执行。

build_and_deploy工作流程有点复杂。在这个工作流中,我们指定当我们将任何符合指定正则表达式/^\d+\.\d+\.\d+$/的标签推送到 GitHub 时,将运行build作业。这个正则表达式是符合语义版本的标签的简单匹配,例如1.0.1。因此,如果我们向 GitHub 推送一个像1.0.1这样的标签,构建工作就会运行。

该工作流接下来指定build-docker作业将在相同的条件下运行,但是它要求build作业首先成功完成(这就是requires命令所做的)。

工作流的最后一步是在将标签推送到 GitHub 的相同条件下运行publish-docker作业,但前提是前一个build-docker作业成功完成。

我们完成的.circleci/config.yml文件应该是这样的(记住将对chrishowejones的引用改为您的 Docker Hub 帐户)。

 version: 2.1
    executors:
        docker-publisher:
          working_directory: ~/cci-film-ratings # directory where steps will run
          environment:
            IMAGE_NAME: chrishowejones/film-ratings-app
          docker:
            - image: circleci/buildpack-deps:stretch
    jobs:
      build:
        working_directory: ~/cci-film-ratings # directory where steps will run
        docker:
          - image: circleci/clojure:lein-2.8.1
        environment:
          LEIN_ROOT: nbd
          JVM_OPTS: -Xmx3200m # limit the maximum heap size to prevent out of memory errors
        steps:
          - checkout
          - restore_cache:
              key: film-ratings-{{ checksum "project.clj" }}
          - run: lein deps
          - save_cache:
              paths:
                - ~/.m2
              key: film-ratings-{{ checksum "project.clj" }}
          - run: lein do test, uberjar
          - persist_to_workspace:
              root: ~/cci-film-ratings
              paths:
                - target
      build-docker:
        executor: docker-publisher
        steps:
          - checkout
          - attach_workspace:
              at: .
          - setup_remote_docker
          - run:
              name: Build latest Docker image
              command: docker build . -t $IMAGE_NAME:latest
          - run:
              name: Build tagged Docker image
              command: docker build . -t $IMAGE_NAME:${CIRCLE_TAG}
          - run:
              name: Archive Docker images
              command: docker save -o image.tar $IMAGE_NAME
          - persist_to_workspace:
              root: ~/cci-film-ratings
              paths:
                - ./image.tar
      publish-docker:
        executor: docker-publisher
        steps:
          - attach_workspace:
              at: .
          - setup_remote_docker
          - run:
              name: Load archived Docker image
              command: docker load -i image.tar
          - run:
              name: Publish Docker Image to Docker Hub
              command: |
                echo "${DOCKERHUB_PASS}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin
                docker push $IMAGE_NAME:latest
                docker push $IMAGE_NAME:${CIRCLE_TAG}
    workflows:
      version: 2.1
      main:
        jobs:
          - build
      build_and_deploy:
        jobs:
          - build:
              filters:
                branches:
                  ignore: /.*/
                tags:
                  only: /^\d+\.\d+\.\d+$/
          - build-docker:
              requires:
                - build
              filters:
                branches:
                  ignore: /.*/
                tags:
                  only: /^\d+\.\d+\.\d+$/
          - publish-docker:
              requires:
                - build-docker
              filters:
                branches:
                  ignore: /.*/
                tags:
                  only: /^\d+\.\d+\.\d+$/ 

现在可以将这个配置提交给 GitHub 了。

$ git add --all .
$ git commit -m "Added workflow & jobs to publish to Docker Hub."
$ git push 

现在,您可以标记当前构建并检查新工作流是否运行以构建 Docker 映像并将其推送到 Docker Hub:

$ git tag -a 0.1.0 -m "v0.1.0"
$ git push --tags 

这将创建一个带注释的标签,并将其推送到您的 GitHub 存储库中。这将触发build_and_deploy工作流来构建应用程序 Uberjar,构建 Docker 映像(latest0.1.0),并将这两个映像推送到您的 Docker Hub 存储库用于film-ratings-app。你可以在 CircleCI 仪表盘中查看,也可以在 Docker Hub 中浏览你的film-ratings-app库。

摘要

恭喜你!如果您已经阅读了本系列,那么您已经创建了一个简单的 Clojure web 应用程序,并使用 Docker 和 Docker Compose 对其进行了打包,这样您就可以在类似生产的环境中本地运行它。您还了解了如何让 CircleCI 构建、测试、打包和发布您的应用程序作为 Docker 映像。

在本系列的下一篇博文中,我将逐步介绍如何设置一个相当复杂的 AWS 环境,以便使用 Terraform 在云中运行我们的应用程序。


Chris Howe-Jones 是顾问 CTO、软件架构师、精益/敏捷蔻驰、开发人员和 DevCycle 的技术导航员。他主要从事 Clojure/ClojureScript、Java 和 Scala 方面的工作,客户从跨国组织到小型创业公司。

阅读更多克里斯·豪-琼斯的文章

使用对象参数| CircleCI 管理可重用管道配置

原文:https://circleci.com/blog/parameters-in-pipeline-config/

CircleCI 管道是使用 YAML 语法定义的,它已经被许多软件工具和解决方案广泛采用。YAML 是一种人类可读的声明性数据结构,用于配置文件(类似于 CircleCI 管道的配置文件)以及存储或传输数据的应用程序中。管道配置文件中的数据指定并控制工作流和作业在平台上触发时的执行方式。配置文件中的这些管道指令往往会变得重复,这可能会导致配置语法量增加。随着时间的推移,这种数量的增加使得配置更难维护。因为 YAML 是一种数据结构,所以只有最低限度的语法可重用性能力(锚和别名)可用于处理增加的数量。锚和别名太有限,不能用于定义 CI/CD 管道。幸运的是,CircleCI 配置参数特性为封装和重用冗余的功能数据提供了强大的功能。

在这篇文章中,我将介绍管道配置参数,并解释在您的管道配置中采用它们的一些好处。

什么是配置参数?

执行者作业命令被认为是流水线配置文件中的对象。像面向对象编程(OOP) 范例中的对象一样,管道对象可以被扩展以提供定制的功能。CircleCI 配置参数通过提供创建、封装和重用管道配置语法的方法,让开发人员扩展执行器、作业和命令的功能。

执行器、作业和命令是具有各自属性的对象。参数也有自己独特的属性,可以与对象交互。参数的组成和属性包括:

parameters:
  |_ parameter name: (Specify a name the parameter)
    |_ description: (Optional. Describes the parameter)
    |_ type: (Required. datatype string, boolean, integer, enum)
    |_ default: (The default value for the parameter) 

何时应该在管道配置中使用参数?

当数据和功能在管道中重复时,使用参数。换句话说,如果您的管道中有任何执行器、作业或命令在您的管道中被多次定义或执行,我建议识别这些模式或元素,并在您的配置文件语法中将它们定义为参数。使用参数使您能够集中管理和维护功能,并极大地减少配置文件中的冗余数据和语法行数。提供可变参数的能力也是一个好处,管道语法的整体可读性也得到了提高。

如何创建参数?

如前所述,执行者作业命令是可以用参数扩展的配置元素。决定扩展这些元素中的哪一个将取决于您的特定用例。

注意: 参数功能仅在 CircleCI 及以上版本中可用。必须在配置文件的顶部定义版本,如下所示: version: 2.1 ,以便平台识别参数。

让我给你举一个在jobs:对象中为并行度量定义参数的例子:

version: 2.1
jobs:
  build_artifact:
    parameters:
      parallelism_qty:
        type: integer
        default: 1
    parallelism: << parameters.parallelism_qty >>
    machine: true
    steps:
      - checkout
workflows:
  build_workflow:
    jobs:
      - build_artifact:
          parallelism_qty: 2 

在这个例子中,build-artifact:作业有一个用名称parallelism_qty:定义的parameters:键。此参数的数据类型为整数,默认值为 1。 parallelism: 键是jobs:对象的一个属性,它定义了在steps:列表中产生和执行命令的执行器数量。在这种情况下,特殊的checkout命令将在所有产生的执行器上执行。作业的parallelism:键已被赋予值 parameters . parallelism _ qty, which references the` parallelism _ qty:上面定义的参数定义。此示例显示了参数如何为管道构造增加灵活性,并提供了一种方便的方式来集中管理管道语法中重复出现的功能。

在作业对象中使用参数

使用前面的例子,工作流块中的parallelism_qty:参数演示了如何在配置语法中使用参数。因为parallelism_qty:是在作业对象中定义的,所以它可以作为工作流中指定的作业来执行。

workflows:块有一个指定了build_artifact:jobs:列表。它还为parallelism_qty:赋值 2 个执行器,这将产生 2 个执行器并执行steps:列表中的命令。如果该值为 3,那么 build_artifact 作业将产生 3 个执行器,并运行命令 3 次。

执行器、作业和命令是具有属性的对象,可以通过管道配置语法进行定义、自定义和重用。

在管道配置中重用执行器对象

上一节演示了如何在 jobs 对象中定义和使用参数。在这一节中,我将描述如何在执行器中使用参数。执行器定义用于执行管道作业和命令的运行时或环境。执行器对象有一组它们自己的独特属性,参数可以与之交互。这是一个定义和实现可重用执行器的例子:

version: 2.1
executors:
  docker-executor:
    docker:
      - image: cimg/ruby:3.0.2-browsers
   ubuntu_20-04-executor:
    machine:
      image: 'ubuntu-2004:202010-01'
jobs:
  run-tests-on-docker:
    executor: docker-executor
    steps:
      - checkout
      - run: ruby unit_test.rb 
  run-tests-on-ubuntu-2004:
    executor: ubuntu_20-04
    steps:
      - checkout
      - run: ruby unit_test.rb 
workflows:
  test-app-on-diff-os:
    jobs:
      - run-tests-on-docker
      - run-tests-on-ubuntu-2004 

这个例子展示了如何在你的管道配置中定义和实现可重用的执行器。我在文件开头使用了executors:键来定义 2 个执行者,一个名为docker-executor:,一个名为ubuntu_20-04-executor:。第一个指定使用 Docker 执行器,第二个指定使用 Ubuntu 20.04 操作系统映像的机器执行器。通过这种方式预定义执行器,开发人员可以创建一个要在管道中使用的执行器资源列表,并集中管理与执行器类型相关的各种属性。例如,码头执行器具有不属于机器执行器且不可用的属性,因为机器执行器不属于docker类型。

重用对象将语法量保持在最低限度,同时提供简洁的对象实现,优化代码可读性和功能的集中管理。

jobs:块定义了run-tests-on-docker:run-tests-on-ubuntu-2004:;两者都有一个指定了值的executor:键,作为该作业的适当执行者。run-tests-on-docker:作业使用docker-executor定义执行其步骤,而run-tests-on-ubuntu-2004:作业根据ubuntu_20-04定义执行。正如您所看到的,在它们自己的节中预定义这些执行器使得 config 语法更容易阅读,这将使它更容易使用和维护。对执行器的任何更改都可以在各自的定义中进行,并将传播到实现它们的任何作业。这种对定义的执行器的集中管理也可以应用于以类似方式定义的作业和命令对象。

workflows:块中,test-app-on-diff-os:工作流并行触发两个作业,这两个作业在各自的执行器环境中执行单元测试。当您想了解应用程序在不同操作系统中的行为时,使用不同的执行器运行这些测试是很有帮助的。这种测试是常见的做法。这里的要点是,我只定义了一次执行者,并在多个作业中轻松地实现了它们。

可重复使用的命令对象

命令也可以在配置语法中定义和实现,就像执行器和作业一样。尽管命令对象属性不同于执行器和作业,但是它们的定义和实现是相似的。以下是显示可重复使用的命令的示例:

version: 2.1

commands:
  install-wget:
    description: "Install the wget client"
    parameters:
      version:
        type: string
        default: "1.20.3-1ubuntu1"
    steps:
      - run: sudo apt install -y wget=<< parameters.version >>
jobs:
  test-web-site:
    docker:
      - image: "cimg/base:stable"
        auth:
          username: $DOCKERHUB_USER
          password: $DOCKERHUB_PASSWORD
    steps:
      - checkout
      - install-wget:
          version: "1.17.0-1ubuntu1"
      - run: wget --spider https://www.circleci.com
workflows:
  run-tests:
    jobs:
      - test-web-site 

在这个例子中,一个可重用的命令已经由一个作业定义和实现。配置顶部的command:键定义了一个名为install-wget:的命令,该命令安装特定版本的 wget 客户端。在这种情况下,定义一个参数来指定要安装哪个 wget 版本号。如果没有指定值,default:键安装1.20.3-1ubuntu1的默认值。steps:键列出了 1 个run:命令,用于安装在versions:参数中指定的 wget 版本。versions:参数被<< parameters.version >>变量引用。

如示例所示,作业对象可以实现和使用定义的命令。test-web-site:作业中的步骤节实现了- install-wget:命令。它的version:参数被设置为 wget 的早期版本,而不是默认的版本值。作业中的最后一个run:命令使用 wget 测试来自给定 URL 的响应。这个例子运行一个简单的测试来检查一个网站是否响应请求。

像往常一样,workflows:块触发- test-web-site作业,该作业执行可重用的install-wget命令。就像执行器和作业一样,命令带来了重用代码、集中管理更改以及提高管道配置文件中语法可读性的能力。

结论

在这篇文章中,我描述了使用管道参数和可重用管道对象的基础:执行器、作业和命令。关键要点:

  • 执行器、作业和命令被视为具有属性的对象,可以在整个管道配置语法中进行定义、自定义和重用
  • 重用这些对象有助于保持最少的语法量,同时提供简洁的对象实现,优化代码可读性和功能的集中管理

虽然我写这篇文章是为了向您介绍参数和可重用对象的概念,但我还是想鼓励您阅读一下可重用配置参考指南。它将帮助您更深入地了解这些功能,以便您能够充分利用这些出色的功能。

我很想知道你的想法和观点,所以请发推特给我 @punkdata 加入讨论。

感谢阅读!

如何以及在哪里隔离测试环境

原文:https://circleci.com/blog/path-to-production-how-and-where-to-segregate-test-environments/

学习如何以及在哪里隔离测试环境

将一个新工具引入一个组织不是一件简单的事情。采用 CI/CD 工具或任何其他工具应该在您的组织内进行一段时间的研究、分析和调整。

在我的上一篇文章中,我解释了任何成功的工具采用的先驱是如何与人相关的:有目的地一致,获得一些“之前”的度量来支持你的评估,以及适当地设置期望。我建议您重新阅读这篇文章,以便在进行任何工具更改之前为您的团队做好充分准备。

下一步是关于分析:准确推断出你最需要解决的管道问题。通过检查你的开发过程(我将称之为你的“生产之路”),你可以找出你最大的问题来自哪里。只有当您知道您试图集中解决什么问题时,您才能做出合理的工具决策。

检查生产路径的目标是创建清晰的阶段,作为检查点。为了从一个阶段进入下一个阶段,构建必须通过这些质量关。将使用各种测试来分离这些门。一旦测试通过,质量就有了保证,构建就可以继续了。

更喜欢看它的实际操作?观看视频,了解如何以及在哪里隔离测试环境

这些管道阶段或执行环境被开发人员承担的越来越大的责任范围所分隔,从他们的本地笔记本电脑到团队空间,再到应用程序的整个代码库。随着责任范围的扩大,犯错的成本也就越高。这就是为什么我们在将一个构建传递到每一个连续的级别之前进行增量测试。

更重要的是,每个阶段需要不同类型的测试。当您从试运行阶段转移到生产阶段时,测试会从轻量级转移到重载级。资源成本也随着每个阶段而增加。重型测试只能发生在更像生产的环境中,包括完整的技术堆栈或外部依赖。为了正确地进行这些测试,还有更多的东西需要旋转,它们需要更昂贵的机器。因此,如果您能够在早期环境中进行尽可能多的测试,这是非常有益的,因为早期环境的成本要低得多。

在我们继续之前总结一下,职责分离的生产方式的好处是:
1。通过将开发和测试分成几个阶段,调试更快更容易。您可以更早地发现问题,反馈循环也更快。
2。通过保持尽可能少的类似生产的环境(一到两个就好)来节省资源

我想提到的是,如果你的过程看起来不完全像我下面描述的那样,那也没关系。也许现在不会,或者永远不会。你可能有不同的目的,需要不同的流程。这里的目标是让您了解您的管道可能包含哪些阶段、测试和质量关。看到可能性应该有助于发现您的开发和测试工作流程中缺少了什么。此外,您的组织可能会使用与我在这里使用的不同的术语,这也没关系。这里重要的是考虑责任的级别,以及如何用测试来区分这些级别。

不同类型的测试环境

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**-单元/组件测试:**这些测试涵盖了最小可能的组件、单元或功能。它们是运行起来最便宜和最快的测试,因为它们不需要大量的依赖或模仿。这些应该早做,让他们不碍事。
**-集成测试:**这些测试检查前一阶段的每个单元与其他组件、单元和功能的工作情况。从更广泛的意义上来说,它可以测试服务(比如 API)如何相互集成。
**-UI 层测试:**这是基于浏览器的自动化测试,测试基本的用户流程。它建立起来很昂贵,运行起来也很慢,所以应该在管道的后期进行。

现在让我们来谈谈这些测试如何适应软件开发管道。每个测试都有特定的角色和位置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意:这个例子使用了一个微服务应用程序,它允许我们分别测试和部署每个服务。

局部环境

这个环境是私有的,仅限于单个开发人员和他们的笔记本电脑。这种环境是最容易进行更改和测试您自己的实现的环境。

我们这里说的“本地”,其实只是一个“个人环境”。它可以在您的笔记本电脑上运行,但是如果应用程序太大而无法在您的本地笔记本电脑上运行,您也可以轻松地使用云环境。这里的关键是,它是一个较小规模的实例,是您自己的,您可以在开发过程中测试和调试您的实现,而不会干扰团队中的其他开发人员。因为您的本地环境中没有任何东西对其他人可见,所以您的团队可以同意将 Git 预推送挂钩集成到您的 repo 中,以确保使用本地环境,并在代码被推送到远程或共享的存储库之前运行自动化测试。

**测试:**在从本地环境迁移之前,我推荐的测试是单元测试、与模拟组件的集成测试,以及尽可能的 UI 测试。您在这个环境中可以做的测试越多,您在集成环境中取得成功的空间就越大。

**职责范围:**这里的范围只包括您正在构建的东西的实现或功能。你的组件能自己工作吗?表面积相对较小。但是,这是合并到共享环境之前的最后一个环境。因此,您需要负责任地进行测试,这样您就不会破坏共享的环境并阻碍其他人的积极开发。

CI 环境

这是最短命的环境;它与建筑同在。它是在构建被触发时创建的,并在构建完成后被拆除。也是最不稳定的环境。作为一名开发人员,我将代码签入 CI 环境。由于其他开发人员可能会同时进行部署,CI 环境中有许多并发的部署活动。因此,配置项可能并且经常会中断。没关系——它注定会坏掉的。关键是当它发生时要修复它。

如果您不能在本地环境中启动整个应用程序(这是非常可能的),CI 环境将是您能够进行浏览器驱动测试的第一个环境。您还可以进行在本地环境中无法进行的任何 UI 测试。

在这种环境中,您应该使用模拟外部服务和数据库来保持快速运行。

我建议像这样自动化 CI 环境的生命周期:当您合并代码时,CI 环境会自动加速,运行该代码,告诉您它是否安全,然后自行关闭。使用 Docker 自动启动环境将节省时间,自动化构建和环境创建的整个过程使团队成员更容易更频繁地提交。

从 CI 环境中获得的最大收益取决于您可以提高它的速度,因为这将决定您的反馈循环的长度。如果您的整个应用程序需要大约 30 分钟来启动和部署,那么您可能需要考虑进行部分部署。由于 CI 环境变化很快,您应该嘲笑大多数外部服务。请记住,您只是在测试核心组件的集成,而不是任何外部服务。

测试:单元测试,模拟组件的集成测试,模拟数据的 UI 测试

**职责范围:**在这里,您关心的是您要将代码合并到的共享代码库。您的组件与您的服务的其余部分一起工作吗?

发展环境

开发环境是与其他开发人员共享的环境。在这种环境下,应用程序中的每项服务每次都会得到部署。这些环境非常不稳定,因为每时每刻都有来自不同团队的不断变化。这里需要注意的是,您的集成和基于浏览器的测试现在可能会失败,即使它们在 CI 环境中通过了。这是因为它们现在已经与外部服务完全集成,其他服务也正在开发中。

尽管 CI 环境只是为您的团队(或者您产品的一个服务)准备的,并且可以在构建之间被拆除,但是这个开发环境是为您的整个产品代码库准备的。如果您选择合并到一个只属于您团队的开发环境中,那么与合并到所有团队共享的完整开发环境中相比,影响范围会更小。在开发环境中,现在需要系统健康检查监控,因为这是一个快速移动的环境,有许多不同的组件。由于它位于生产路径的中间,当这个环境中断时,它会对随后的环境产生不利影响——如果开发环境中断,这里的失败会阻止所有的更改,并且没有代码可以继续前进。

在开发环境中,有些外部服务会被模仿,有些不会,这取决于每个服务对您测试的内容有多重要,以及连接到那个外部服务的成本。如果您在这里使用模拟数据,请确保有足够的测试数据可用。

组织中的每个人都可以看到这个环境;任何开发人员都可以登录并将其作为应用程序运行。所有开发人员都可以使用这个环境进行测试和调试。

**测试:**单元测试,模拟组件集成测试,模拟数据 UI 测试,系统健康检查。

**职责范围:**范围扩大到与其他服务的集成。您的服务与它需要连接的其他服务一起工作吗?

质量保证环境

这是该场景中第一个手动部署的环境。它是手工部署的,因为 QA 团队需要根据变更的结构决定哪些特性值得他们自己测试。他们可能会选择一个堆叠的变更(变更 A、B 和 C ),并分别测试它们。在这种情况下,他们测试变更 A,一旦他们确定变更 A 按预期工作,他们就可以继续测试变更 B,当它抛出错误时,他们知道问题是由变更 B 引起的。否则,在只测试变更 C 的情况下,如果它抛出错误,就会阻碍 C、B 和 A 的进度。

QA 环境是一个受控的集成环境。在这里,QA 团队控制着即将到来的变化;相比之下,在开发环境中,任何变化都可能随时发生。构建现在与它将在应用程序中与之交互的服务集成在一起。

在此环境中,我们现在拥有与生产环境中相同的基础架构和应用程序。我们使用生产数据的一个代表性子集,足够接近生产数据来进行测试。QA 工程师或测试人员知道他们应该把测试的重点放在什么地方。我建议在这个阶段进行手动部署,这样可以在一个隔离的环境中测试小的变化,以帮助识别 bug。

如果在测试中不使用生产或类似生产的数据,QA 测试可能会错过很多。如果可能的话,使用真实的外部服务,这样 QA 可以捕捉到可能发生的真实问题。QA 环境越接近生产,您对测试结果的信心就越高。和往常一样,你必须权衡这种保证和成本。根据应用程序的功能,您仍然可以模仿一些外部服务,而不会产生不良影响。

**测试:**这个阶段是开放式探索性测试和安全性测试的适当时机。在这个阶段,你不仅希望发现技术问题,还希望发现潜在的用户流问题。

现在,您已经完成了所有种类的充分测试。是否存在任何潜在的用户流量或安全问题?在迁移到试运行环境之前,您还有什么需要解决的吗?

暂存环境

这是生产前的最后一个环境。试运行的目的是拥有一个与生产环境几乎完全相同的环境。当您将某些东西部署到产品化阶段,并且它工作正常时,您可以合理地确信该版本不会在生产中失败并导致停机。所有环境都有助于您发现潜在问题;筹备是信心的最后检查。在这种情况下,拥有与生产中几乎相同的数据量非常重要。这使您能够进行负载测试,并在生产中测试应用程序的可伸缩性。

应该将生产就绪代码部署到此环境中;同样,几乎与生产中相同。基础设施、数据库和外部服务集成应该与生产中的完全相同。维护和建设的成本将会很高——唯一更高的成本是不去做,以及中断生产。规模可以更小,但您的设置和配置必须相同。

这是生产前测试实施、迁移、配置和业务需求的最后一道关口。我想强烈建议您在进入试运行阶段之前获得业务签署。否则,做出改变将会非常昂贵。当你进行开发时,请确保拥有产品或所请求特性的人完全理解你在构建什么。从头到尾都保持一致。

测试:这个环境可以用来完成迄今为止我们所有测试中最繁重的工作:系统测试、负载测试、性能测试和安全性测试。

考虑到环境的重要性,如果在以前的环境中已经完成了其他核心应用程序功能和内部集成测试,那将是最有利的。

此时,要问的问题是:您的实现满足所有业务目标吗?

如何充分利用你的生产之路

  • 使路径对每个人可见。
    • 使用生成监控
    • 允许所有开发人员访问预生产环境
  • 构建和环境可能会中断。快速修复它,并尽可能保持绿色。
    • 如果你知道如何修复构建,就修复它。
    • 如果由于调试和实现的原因,某个构建修复需要一段时间,请先恢复该构建以取消阻止其他开发。
  • 当一个构建在早期阶段被破坏时,不要将其提升到后期环境。
  • 编写可靠的,自动化测试。
    • 如果你的测试毫无理由地失败了,你不能信任他们,这条管道也不会为你工作。当这个管道可见时,并且当测试结果被准确地表示时,它是值得信任的。这就是为整个团队提供价值的方式。

创建您自己的集成测试环境

我刚刚与你分享了一个理想的生产途径。但是这里的理想可能对您或您的组织来说并不理想:您可能能够利用一个更轻量级的过程,这取决于您的组织和应用程序。但是我分享这个的意图是给你一个新的视角,通过它来看你自己的生产和测试实践之路。通过将您的质量关口与我概述的进行比较,您可以看到您可能遗漏了什么,因此您可以添加必要的控制,交付高质量的产品,并节省成本。

最后一点:你的整个生产路径,不管看起来像什么,都应该对组织中从事开发的每个人可见。每个在本地环境中实现代码的开发人员都应该能够了解代码将如何部署到生产环境中。在我之前的文章中,我谈到了 DevOps 意味着关心你的代码如何通过管道到达客户。其中一部分意味着要知道它将要通过的质量关。只有意识到他们的代码是如何被部署的,开发人员才能在进入下一阶段之前对减少错误产生最大的影响。

阅读更多信息:

CircleCI 的渗透测试- CircleCI

原文:https://circleci.com/blog/penetration-testing-at-circleci/

在过去的五年里,CircleCI 聘请了第三方渗透测试公司,至少每 90 天对我们的产品发布或基础设施进行一次测试。到目前为止,我们已经做了超过 25 个。有时他们涵盖新产品功能,有时他们涵盖基础设施。

CircleCI Server 3.0 的渗透测试

我们最近对我们的 server 3.0 主要版本进行了第三方渗透测试。这一版本使 Kubernetes 成为我们企业产品的一等公民。随着向 Kubernetes 的重大转变,所有后续版本都将建立在 Kubernetes 的基础设施之上,我们找到了最佳的合作伙伴来测试我们新基础的弹性。回车:位尾。

与比特轨迹合作

Trail of Bits 是云原生计算基金会(Cloud Native Computing Foundation)这两家第三方渗透测试公司之一,该基金会负责监督 Kubernetes 的开发,受雇于 2019 年对 Kubernetes 本身进行为期四个月的测试。将我们的工作尽可能紧密地联系起来似乎是最好的前进方式。

CircleCI 向 Trail Bits 提供了我们服务器产品的测试版,并要求他们按照我们向新客户或现有客户提供的说明随意敲打。三周后,他们发现了一些惊人的漏洞,这些漏洞推迟了产品的发布。为此,我们非常感激。我们非常乐意推迟发布(并对等待发布的客户无限感激),因为我们最不想做的事情就是将不安全的软件发送给任何人。

主要服务器版本的 Bits 测试不是唯一的。每隔 90 天,我们的一个开发团队就会进行一次类似的演练。其他测试包括我们的 Mac 机队、新的用户认证工作流程以及我们最近的技术收购

CircleCI 的渗透测试政策

因为我们将客户的安全放在第一位,所以 CircleCI 有一个长期的渗透测试政策,可以立即修复所有关键和重要的发现,然后在测试结束之前验证这些修复。安全工程师与开发团队领导一起评估风险,并在适当的时候添加到团队的待办事项列表中。一个非常好的中等可能是一个中等,也需要立即修复,但是有了更多的上下文,我们可能有信心将其降低到一个较低的水平,并将其添加到团队的一般待办事项中。我们所有产品(云、运行器和服务器)的渗透测试遵循与基础设施测试相同的流程。

客户经常要求我们提供渗透测试结果的副本。出于安全原因,我们无法提供原始调查结果,但我们会提供一份面向客户的摘要,在不暴露任何潜在秘密的情况下提供尽可能多的细节。要请求渗透测试摘要,请联系您的客户成功经理。

权限 101:了解 CircleCI - CircleCI 上的权限

原文:https://circleci.com/blog/permissions-101-understanding-permissions-on-circleci/

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


我们最近为所有 CircleCI 用户推出了许多新功能。请求最多的功能之一集中在权限设置上。这篇博客将深入探讨这个新特性,它是如何工作的,以及它对使用 CircleCI 的你和你的团队意味着什么。

什么是权限?

权限是 CircleCI 上的一个新特性,它可以让您更好地控制项目的设置。使用权限,组织可以限制谁可以在 CircleCI 上更改“项目设置”。此设置在组织级别或组织内的项目级别可用。为此,CircleCI 利用 GitHub 的权限来确定用户的访问权限。

用户或组织为什么要启用权限??

为您的组织启用权限后,只有 GitHub repo 管理员或 GitHub 所有者才能在 CircleCI 上更改“项目设置”。这对于大型团队来说特别有用,可以确保您的项目设置只能由具有管理员权限的团队成员更改。这对于大型团队来说尤其有用,可以确保您的项目设置不会被团队中的非 GitHub 管理员/所有者意外更新。

这将如何影响我的团队?

为您的组织启用权限后,GitHub 成员和贡献者将无法对 CircleCI 的“项目设置”页面进行任何更改。这包括为您的组织设置容器、项目级并行化设置、构建设置等。GitHub 成员或合作者仍然可以使用 CircleCI 的功能,如重建、使用缓存重建或通过 SSH 重建。

如果我改变主意了怎么办?我可以关闭这个功能吗?

如果您发现权限不适合您,您可以随时创建支持请求(在应用程序中,点按“帮助?”按钮并选择“支持”),我们将为您的组织禁用权限。

入门:如何在 GitHub 上设置用户权限?

要设置您的团队在 GitHub 上的访问权限,请导航到您所在组织的以下链接:https://github.com/orgs/org-name/teams.

您可以使用以下链接设置 repo 级别的用户访问:https://github . com/org-name/repo-name/settings/collaboration。

阅读更多关于在 GitHub 上设置团队权限的信息。

如何在 CircleCI 上启用权限?

如果您希望为您的组织或组织内的特定项目启用此功能,请创建支持请求(在应用程序中,单击“?”当你滚动鼠标时,按钮上写着“救命?”并选择“支持”)

你希望 CircleCI 增加什么功能?您可以在我们社区网站的功能请求类别上提交一个新主题。你也可以在我们的开发者选择奖中投票,在这里我们收集了九个最常被请求的功能,我们很想构建这些功能,但由于各种原因,它们从未出现在我们的待办事项列表中。这是您直接对这些功能进行投票的机会,我们将构建获得最多投票的功能。投票将于太平洋时间 4 月 22 日星期一下午 5:00 结束。请访问开发者的选择,为您希望我们下一步开发的功能投票。

在工作流中持久化数据:何时使用缓存、工件和工作区

原文:https://circleci.com/blog/persisting-data-in-workflows-when-to-use-caching-artifacts-and-workspaces/

CircleCI 提供了许多不同的方法来将数据移入和移出作业,持久化数据,以及使用工作区在作业之间移动数据。为正确的任务使用正确的特性将有助于加速您的构建,提高可重复性,并提高效率。

对于任何曾经等待 CI 测试套件变绿的人来说,更快的 CI 运行的好处是显而易见的。

重复性也很重要。可重复的 CI 流程意味着,如果您对您的回购中的相同 SHA 再次运行相同的流程,您将获得相同的结果。当 CI 流程不可重复时,您会发现自己在浪费时间重新运行作业来实现环保。

数据如何在 CircleCI 作业之间流动

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据可以以不同的方式在 CircleCI 作业之间流动。工作空间在单个工作流中的作业之间保存数据。缓存在不同工作流版本中的相同作业之间保存数据。工件在工作流完成后保存数据。它们之间的用例、实现和数据停留的时间各不相同。

CircleCI 工作区

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 工作区在工作流程中的连续作业之间移动数据。

当在作业中声明工作空间时,可以添加一个或多个文件或目录。每次添加都会在工作区文件系统中创建一个新层。然后,下游作业可以根据自己的需要使用该工作空间,或者在其上添加更多层。

一种常见的方法是使用工作区将生成的版本号从构建作业传递到部署作业。它们也可以用来传递编译好的二进制文件——但是由于它们需要在每个作业中被上传和下载,这比仅仅传递元数据要慢。

与缓存不同,工作空间不会在运行之间共享,因为一旦工作流完成,它们就不再存在。有一个例外,即重新运行工作流。关于这一点的更多信息以及对工作空间的全面深入了解,可以在明天的博客文章中找到:深入 CircleCI 工作空间

CircleCI 中的缓存

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 缓存在多个工作流运行中的同一作业之间持久保存数据。

缓存使您可以重用以前作业中昂贵的提取操作的数据。初始作业运行后,通过不重做工作,作业的未来实例将运行得更快。一个主要的例子是包依赖管理器,如 Yarn、Bundler 或 Pip。通过从缓存中恢复依赖关系,像yarn install这样的命令将只需要下载新的依赖关系,如果有的话,而不需要在每次构建时重新下载所有内容。

缓存在一个项目中是全局的,保存在一个分支上的缓存将被其他分支使用,因此它们应该只用于可以跨分支共享的数据。要获得更多类似的技巧和对 CircleCI 缓存的更深入了解,您可以阅读 CircleCI 2.0 缓存文档

CircleCI 文物

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 工件在一个工作流完成并消失后持久保存数据。

工件用于构建过程输出的长期存储。例如,如果你有一个 Java 项目,你的构建很可能会产生一个代码的.jar文件。这些代码将通过您的测试来验证。如果整个构建/测试过程通过了,那么过程的输出(.jar)可以被存储为一个工件。.jar文件在创建它的工作流程结束后很久还可以从我们的工件系统下载。

如果您的项目需要以某种形式或方式打包,比如说一个 Android 应用程序。apk 文件被上传到 Google Play,这是一个很好的例子。许多用户将他们的工件上传到公司范围内的存储位置,如亚马逊 S3 或 Artifactory。更多提示和最新信息可以在 CircleCI 工件文档中找到。


想深入了解工作区以及如何最好地使用它们?阅读我们的后续博文深入 CircleCI 工作区

有关工作流可以做的所有事情的概述,包括 OSS 配置,请参见工作流系列的广阔天地:

如何使用工件获得构建和测试结果

原文:https://circleci.com/blog/piecing-together-build-and-test-results-with-artifacts/

想象一下:你在一个偏远的沙漠地区,那里发现了一个历史性的考古定居点。通过最初的调查,这一群人似乎没有幸存下来的好运气。这就是你在那里的原因。你在寻找线索,看看你是否能拼凑出一幅准确的图像,来解释为什么他们的运气用完了。

通过你的搜索和搜索,你没有发现多少有用的东西,突然-砰!你发现了一个似乎是解释这里发生的事情的关键的物体。你找到的这件艺术品正是你所需要的,它能告诉你并帮助你理解发生了什么。

想象一下,对于您的测试运行、构建等等,有这些类型的关键线索!CircleCI 已经涵盖了你,虽然因为我们不能给系统像陶器的沙片,剑,或银猴之类的人工制品,让我们来看看我们可以创造什么,以及我们可以使用这些物品的一些方法。

让我们回顾一下什么是人工制品

神器到底是什么?摘自文档中的,“工件在一项工作完成后保存数据,并可用于长期存储您的构建过程的输出。”如果这对你来说仍然模糊不清,让我们来分解它。

在整个工作流程中,每个作业都有自己的数据和文件,每次运行后都会被擦除。为了跨作业传送数据,可以将数据保存到工作区,工作区是特定于每个工作流的临时数据存储区。与作业一样,工作流完成后,工作空间数据也将消失。怎么回事?我们如何在工作流之后保留可能对我们有用的项目?

我们将这些物品作为艺术品保存。如下图所示,您有了一个额外的步骤来将文件移动到存储,即使在您的工作流完成后,这些文件也不会丢失。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们如何使用工件,它们在哪里配置?

为了让工件有用,它们需要是可访问的。简单地把它们埋在土里,然后希望一千年后有人会碰巧发现它们,这并没有多大帮助。幸运的是,存储工件文件是一个非常简单的过程,它发生在 config.yml 文件中,通过恰当命名的内置步骤:store_artifacts。假设你有一个在测试你的网站的视觉回归时拍摄的截图目录,假设这些截图存储在/tmp/screenshots

将它们保存为配置中的工件的最简单方法如下所示:

- store_artifacts:
    path: /tmp/screenshots 

这是它在完整配置中的样子:

version: 2
jobs:        
    build_and_save: 
        docker:
        - image: circleci/golang:latest
        steps:
            - checkout
            - run: make build
            - store_artifacts:
                path: /bin/final_build
workflows:
    version: 2
    main:
        jobs:
            - build_and_save 

就是这样!真的就这么简单。🎉

如果您想指定一个在通过工件 API 访问工件时使用的前缀,您可以通过添加如下代码行来指定:

- store_artifacts:
    path: /tmp/screenshots
    destination: screenshot 

关于通过 API 下载工件的更多信息可以在文档中找到。

有哪些真实世界的人工制品的例子?

我们现在知道如何保存和访问我们的工件,我们甚至想保存什么?以下是一些可以保存下来长期使用的物品的简单列表:

  • 测试步骤或失败的截图
  • 试验结果
  • APKs 二进制文件
  • 压力测试的性能输出
  • 日志文件
  • 核心转储
  • 覆盖率文件

基于什么对你的需求有意义,你可以保存这些项目中的一些或者一个都不保存,这完全没问题。CircleCI 不在乎,重要的是什么对你的需求最有用、最有帮助。(如果你保存了上面没有列出的神器,请留下评论,让我们知道是什么!)

保存文件固然很好,但如果不使用它们又有什么用呢?说得好!

  • 如果你有正在保存的截图,在一次失败的运行后,你可以在一个作业的工件选项卡中调出截图,并查看在可视化回归中到底有什么不同,或者当 Selenium 找不到一个按钮时你的站点在哪里。如果您想更进一步,您甚至可以设置一个与您最喜欢的消息平台的集成,并使用完全相同的屏幕截图发送失败消息,从而节省您的时间!
  • 如果你正在创建应用程序,你可以保存一个版本的 Android 代码,然后再分发。
  • 如果您的日志文件输出非常大,并且您希望某些软件可以访问它们进行解析,您也可以这样做。

最后

天空是你选择使用神器的极限。它们是一个易于学习和实施的简单概念,但是它们的威力在于使您和您的团队能够以更深层次的方式进一步微调和补充您的 CI/CD 流程,而不仅仅是通过/失败。

感谢阅读!


Gemini Smith 是一名不断学习的软件工程师,对测试、交流和改进软件开发生命周期充满热情。主要用 Go 写作,她还通过公开演讲、宣传、咨询和指导对软件和测试社区做出贡献。

阅读更多 Gemini Smith 的文章

采用 circleback 模式| CircleCI 的高级管道编排

原文:https://circleci.com/blog/pipeline-orchestration-circleback/

本教程涵盖:

  1. 为依赖管道编排触发器
  2. circleback 模式介绍
  3. 为 circleback 管道创建配置

随着多个团队在许多项目上工作,为您的软件拥有一个单一的管道是不够的。在测试和发布之前,需要构建和集成这些项目。那么开发团队如何处理这种情况呢?许多团队通过将软件分解成更小的部分来解决问题,这些部分做得更少,更容易维护和构建。这种方法导致微服务架构在我们的行业中越来越普遍。

不利的一面是将软件分成更小的部分会增加复杂性。更多部件和更高复杂性的结合使得在生产中测试和运行应用程序更加困难。

CI/CD 管道在这里也不例外。在本系列的前一篇文章中,我们研究了从其他管道触发管道。那篇文章描述了如何在 CircleCI 中将多个项目链接在一起,以便一个部署启动另一个。

在这篇文章中,我们将更进一步。我将提供一个管道编排的例子,其中第一个管道不仅触发另一个管道,而且在继续之前等待另一个管道完成。我称之为 circleback 模式,因为它“循环返回”到第二个管道,在它完成工作时发出信号。使用这种模式允许更好的集成测试和更复杂的部署和发布场景。

先决条件

  • 本教程涵盖了高级管道编排技术,因此必须具备 CircleCI 和 DevOps 的经验。
  • 本教程也建立在之前的教程之上:从其他管道触发管道。

资源和示例代码

本例的源代码位于两个存储库中:

  1. 管道 A -是否触发

  2. 管道 B -被触发并返回

介绍 circleback 模式

在这些多管道的 circleback 编排中有三个步骤:

  1. 第一个管道等待
  2. 第二管道终止
  3. 从第一个管道中检索结果。另一种描述方式是,第二个管道调用或“循环回”第一个管道,而第一个管道暂停并等待信号继续。

另一种方法是保持第一个管道运行,定期检查第二个管道是否已经完成。这种方法的主要缺点是正在运行的作业的连续轮询成本增加。

根据第二个管道的状态,第一个管道将继续执行,直到成功终止或失败。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为什么管道要等待其他项目完成?

正如在简介中提到的,这是一种高级技术,旨在降低处理多个项目所带来的复杂性。它可以被在多个相互依赖的项目上工作的团队使用,这些项目不希望像 monorepo 一样放在一个存储库中。另一个用途是拥有一个集中的测试库,例如在一个硬件公司。这项技术对于微服务应用程序的集成测试或者编排复杂的部署场景也很有用。有很多种可能。

实现管道触发器

我们有 2 个管道,我们想编排

  1. 管道 A,它执行触发
  2. 管道 B 被触发,并循环回到管道 A

管道 B 依赖于 A,可用于验证 A。

两个管道都需要设置 API 密钥并使其可用。您可以在管道 A 的作业中使用 API 键集作为环境变量(CIRCLECI_API_KEY),在管道 B 回调时也可以在管道 B 中使用。您可以在两个项目中设置它,也可以在组织级别作为上下文设置它。对于本教程,我在组织级别将其设置为circleci_api上下文,这样两个项目可以使用相同的 API 键。

触发管道

触发过程在本教程的第一部分从其他管道触发管道中进行了深入解释。在接下来的教程中,我将只讲述重要的区别。

  • 若要从管道返回,请将原始管道的 ID 传递给它。然后可以通过 API 检索和到达它。
  • 您还需要存储触发的管道的 ID。你需要稍后得到它的结果。

在示例代码中,该参数称为triggering-pipeline-id:

curl --request POST \
                --url https://circleci.com/api/v2/project/gh/zmarkan-demos/circleback-cicd-project-b/pipeline \
                --header "Circle-Token: $CIRCLECI_API_KEY" \
                --header "content-type: application/json" \
                --data '{"branch":"main","parameters":{"triggering-pipeline-id":"<< pipeline.id >>"}}' 

为了存储管道 ID,将您的curl调用包装在$()中,并将其分配给变量CREATED_PIPELINE。要从响应体中提取 ID,使用jq工具,并将其写入文件pipeline.txt:

CREATED_PIPELINE=$(curl --request POST \
                --url https://circleci.com/api/v2/project/gh/zmarkan-demos/semaphore_demo_project_b/pipeline \
                --header "Circle-Token: $CIRCLECI_API_KEY" \
                --header "content-type: application/json" \
                --data '{"branch":"main","parameters":{"triggering-pipeline-id":"<< pipeline.id >>"}}' \
              | jq -r '.id'
              )
              echo "my created pipeline"
              echo $CREATED_PIPELINE
              mkdir ~/workspace
              echo $CREATED_PIPELINE > pipeline.txt 

现在您已经创建了文件pipeline.txt,使用persist_to_workspace来存储它并在后续的作业中使用它:

- persist_to_workspace:
            root: .
            paths: 
              - pipeline.txt 

整个作业配置如下:

...
jobs:
  trigger-project-b-pipeline:
      docker: 
        - image: cimg/base:2021.11
      resource_class: small
      steps:
        - run:
            name: Ping another pipeline
            command: |
              CREATED_PIPELINE=$(curl --request POST \
                --url https://circleci.com/api/v2/project/gh/zmarkan-demos/semaphore_demo_project_b/pipeline \
                --header "Circle-Token: $CIRCLECI_API_KEY" \
                --header "content-type: application/json" \
                --data '{"branch":"main","parameters":{"triggering-pipeline-id":"<< pipeline.id >>"}}' \
              | jq -r '.id'
              )
              echo "my created pipeline"
              echo $CREATED_PIPELINE
              mkdir ~/workspace
              echo $CREATED_PIPELINE > pipeline.txt
        - persist_to_workspace:
            root: .
            paths: 
              - pipeline.txt  
... 

精心安排等待

前一个作业将触发管道 B,它需要在循环回到管道 a 之前完成。您可以像这样使用 CircleCI 中的approval作业:

...
workflows:
  node-test-and-deploy:
    jobs:
      ...
      - trigger-project-b-pipeline:
          context: 
            - circleci-api
          requires:
            - build-and-test
          filters:
            branches:
              only: main
      - wait-for-triggered-pipeline:
          type: approval
          requires: 
            - trigger-project-b-pipeline
      - check-status-of-triggered-pipeline:
          requires:
            - wait-for-triggered-pipeline
          context:
            - circleci-api
      ... 

作业trigger-project-b-pipeline结束后,进入wait-for-triggered-pipeline。因为作业类型是approval,所以它将一直等到有人(在本例中是 API)手动批准它。(更多细节在下一节。)在它被批准后,添加一个requires节,以便它继续后续的作业。

使用 CircleCI API 的两个作业都指定了context,因此 API 令牌作为环境变量对两者都可用。

绕回管道 A

现在我们已经完成了管道 A,是管道 B 大放异彩的时候了。CircleCI 的approval工作是一种特殊的工作,一直等到被接受。它通常用于将管道保持在挂起状态,直到它被人工交付主管或 infosec 工程师批准。

此时,管道 B 知道管道 A 的 ID,因此您可以使用批准 API 来获取它。您只有正在运行的管道的 ID,而没有需要批准的实际作业,因此您将需要不止一个 API 调用:

  1. 获取管道中的所有工作
  2. 按名称查找审批作业
  3. 发送批准作业的请求

批准该作业将允许管道 A 继续运行。

如果管道 B 中的测试失败,那么该作业将自动失败。在这种情况下,包含所需作业的工作流将不会继续。您可以通过在管道中使用post-steps来解决问题,它总是会执行。整个工作流显示在下一个示例代码块中。

参数:

parameters:
  triggering-pipeline-id:
    type: string
    default: ""

...

workflows:
  node-test-and-deploy:
    jobs:
      - build-and-test:
          post-steps:
            - approve-job-in-triggering-pipeline
          context: 
            - circleci-api 

执行批准 API 调用的脚本可以这样实现。对于本教程,我使用了一个command

...
commands:
  approve-job-in-triggering-pipeline:
    steps:
      - run:
          name: Ping CircleCI API and approve the pending job
          command: |
            echo << pipeline.parameters.triggering-pipeline-id >>
            if ! [ -z "<< pipeline.parameters.triggering-pipeline-id >>" ] 
            then
              workflow_id=$(curl --request GET \
                --url https://circleci.com/api/v2/pipeline/<< pipeline.parameters.triggering-pipeline-id >>/workflow \
                --header "Circle-Token: $CIRCLECI_API_KEY" \
                --header "content-type: application/json" \
              | jq -r '.items[0].id')

              echo $workflow_id

              waiting_job_id=$(curl --request GET \
                --url https://circleci.com/api/v2/workflow/$workflow_id/job \
                --header "Circle-Token: $CIRCLECI_API_KEY" \
                --header "content-type: application/json" \
              | jq -r '.items[] | select(.name == "wait-for-triggered-pipeline").id')

              echo $waiting_job_id

              curl --request POST \
                --url https://circleci.com/api/v2/workflow/$workflow_id/approve/$waiting_job_id \
                --header "Circle-Token: $CIRCLECI_API_KEY" \
                --header "content-type: application/json"

            fi
          when: always       
... 

该脚本首先检查是否存在triggering-pipeline-id管道参数。仅当该参数存在时,它才会继续。命令中的when: always行确保不管终止状态如何都会执行。

然后,它进行 3 次 API 调用:

  1. 获取管道中的工作流 ID。对于这个示例项目,只有一个工作流正在进行中。
  2. 获取该工作流中的作业,使用jq选择与批准作业名称(wait-for-triggered-pipeline)匹配的作业,并提取批准作业的 ID。
  3. 向具有等待作业 ID 的审批端点发出请求。

对于本教程,我们将像工作流 ID 和作业 ID 这样的结果存储在本地 bash 变量中,并在对 API 的后续调用中使用它们。

注意 : 如果您的任务比单个响应所能发送的要多,那么您可能还需要处理分页。

现在您已经发出了批准请求,管道 B 已经完成,管道 A 应该可以再次运行了。

用 B 的结果更新管道 A

管道 A 获得批准后,工作流中的下一个作业将开始。如果工作流图需要,管道 A 可以触发多个作业。

我们仍然不知道先前工作流程的结果。要获得这些信息,您可以再次使用 API 从管道 a 获得 B 的状态。

首先,您需要检索被触发的管道(管道 b)的 ID,这与在前面的步骤中保存在工作区中的 ID 相同。使用cat检索它:

 - attach_workspace:
          at: workspace
      - run:
          name: Check triggered workflow status
          command: |
            triggered_pipeline_id=$(cat workspace/pipeline.txt) 

然后使用 API 来检索工作流。使用jq获取返回的工作流数组中第一个项目的状态:

created_workflow_status=$(curl --request GET \
                --url "https://circleci.com/api/v2/pipeline/${triggered_pipeline_id}/workflow" \
                --header "Circle-Token: $CIRCLECI_API_KEY" \
                --header "content-type: application/json" \
              | jq -r '.items[0].status'
            ) 

检查状态是否不是success。如果不是,使用exit终止作业,退出代码-1。如果工作流成功,它将终止:

if [[ "$created_workflow_status" != "success" ]]; then
              echo "Workflow not successful - ${created_workflow_status}"
              (exit -1) 
            fi

            echo "Created workflow successful" 

以下是作业check-status-of-triggered-pipeline的完整配置:

 check-status-of-triggered-pipeline:
    docker: 
      - image: cimg/base:2021.11
    resource_class: small 
    steps:
      - attach_workspace:
          at: workspace
      - run:
          name: Check triggered workflow status
          command: |
            triggered_pipeline_id=$(cat workspace/pipeline.txt)
            created_workflow_status=$(curl --request GET \
                --url "https://circleci.com/api/v2/pipeline/${triggered_pipeline_id}/workflow" \
                --header "Circle-Token: $CIRCLECI_API_KEY" \
                --header "content-type: application/json" \
              | jq -r '.items[0].status'
            )
            echo $created_workflow_status
            if [[ "$created_workflow_status" != "success" ]]; then
              echo "Workflow not successful - ${created_workflow_status}"
              (exit -1) 
            fi

            echo "Created workflow successful" 

结论

在本文中,我们回顾了一个复杂管道编排模式的例子,我将其命名为“circleback”。circleback 模式创建一个依赖管道,并允许您在完成之前等待它终止。它涉及到从两个项目中生成几个 API 键,使用一个批准作业,以及 CircleCI 的 workspace 特性来存储和传递值,例如工作流中作业间的管道 ID。样本项目位于不同的存储库中:项目 A ,以及项目 B

如果这篇文章在某种程度上对你有所帮助,我很想知道,如果你对它有任何问题或建议,或者对未来的文章和指南有任何想法,请通过 Twitter - @zmarkan电子邮件给我联系我。

关于 CircleCI 的新用户界面,你需要知道什么

原文:https://circleci.com/blog/pipelines-speed-and-a-new-test-summary-what-you-need-to-know-about-circleci-s-new-ui/

经过几个月的努力,我们今天正式推出了我们的新 UI。

我们要感谢所有 CircleCI 的用户,他们在我们开发界面时发布了帖子,发了推文,并完成了 UX 会议。您的反馈使我们能够确保团队构建、测试和部署高质量代码的最佳体验。

管道

新 UI 最大的升级是围绕管道而不是作业的定位。基于作业的视图是为 CircleCI 1.0 构建的,非常适合配置简单的用户。然而,从事复杂项目的团队需要更多的可见性,而新的 UI 提供了这一点。新的基于管道的视图将所有作业组合到一个工作流中,并将所有工作流组合到一个管道中,这使得参与项目的每个人都可以尽快轻松找到与其运行相关的信息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最近,我们使用户能够从管道页面取消和重新运行工作流,这是我们特别优先考虑的改进,因为我们的用户要求这样做!

速度

领导这个项目的高级产品经理 Kate Catlin 对用户在新的用户界面上体验更快的页面加载时间感到非常兴奋。“开发人员很忙,他们在我们的平台上完成特定的任务,然后回去写代码,”卡特林说。“通过减少每个屏幕加载信息所需的时间,并重新安排信息以优先考虑最重要的内容,我们可以让开发人员更快地提供新功能。”

快速导航的其他改进

您将在新用户界面上看到的其他一些激动人心的改进包括:

  • 跨项目管道视图,提高跨团队的可见性
  • 在分支和项目选择器上键入搜索,轻松过滤管道视图
  • 在仪表板上自动打开失败的和最新的管线,以便于导航
  • 失败时测试摘要选项卡的默认视图,因此最重要的信息总是在您的指尖

接下来会发生什么

在不久的将来,您可以看到更多特定于管道的信息,比如查看每个管道中使用了哪些orb的能力。CircleCI orbs 是 YAML 配置的可重用片段,它将重复的配置压缩成一行代码。它们有助于自动化流程,并且易于与开发人员已经使用的第三方工具集成。

我们将进行的最令人兴奋的更新之一是专门为新的基于管道的 UI 构建的 Insights UI。请继续关注 Insights 的更多信息。

即将推出的其他一些激动人心的 UI 改进包括:

  • 通过每页顶部改进的面包屑更快地导航
  • 失败测试的更好总结
  • 能够通过单击按钮关闭“管道”页面上所有打开的管道

在不断变化的世界中支持开发人员

对于 Catlin 来说,改进 circle ci UI 的动力一直是帮助开发人员更好地完成他们的工作——现在比以往任何时候都要多。

“现在世界上有很多不确定性,但我个人认为,软件开发商在推动我们走向可以克服这些挑战的未来方面发挥着关键的、不可替代的作用,”卡特林说。“帮助软件开发人员更好、更快、更少地完成工作有助于我们战胜世界面临的新挑战。”

要了解更多关于我们新用户界面的信息,请查看我们的文档或阅读更多关于为什么新用户界面是为提高用户工作效率而构建的

最佳性能计划:为什么 CircleCI 改变我们的定价模式

原文:https://circleci.com/blog/plans-for-optimal-performance-why-circleci-is-changing-our-pricing-model/

当 CircleCI 在 2011 年成立时,持续集成和持续部署(CI/CD)对一些人来说是白日梦,对几乎所有人来说都不是惯例。当时我们所有的客户都在构建我们的 1.0 系统,我们引入了一个基于容器的定价计划,客户将为他们想要访问的每个容器支付月费。在过去的七年里,我们一直在提供同样的计划。今天,我们介绍一个更好的选择。

逐步淘汰集装箱

当我们开始的时候,容器模型很容易计划和考虑。七年前,团队使用 CircleCI 的简单性,容器模型反映了这一点。小团队?拿四个容器。大型团队?拿 40。这是最简单不过的了。

容器模型限制了优化

在过去的几年里,我们一直在 CircleCI 上发布一些功能来帮助客户优化他们的管道:工作流来编排作业上下文来管理跨项目的秘密,以及 orbs 来使配置可重用和集成更容易。有一点保持不变:容器。团队总是受到他们购买的容器数量的限制,许多团队要么资源闲置,要么开发人员排队等待开放的机器。

一个更好的选择:基于使用的循环定价

今天,我们将推出一种新的基于使用的定价模式。我们的新付费计划的客户将能够同时运行他们想要的任意多的资源,而不是被限制在一定数量的容器中。

它是如何工作的

CircleCI 的绩效计划有每个座位的基本费用(前 3 个用户 15 美元,然后每个额外用户 15 美元),然后根据信用包扩展使用。然后,客户在 CircleCI compute 上花费信用来运行他们的工作。CircleCI 提供多种类型的计算(Linux、Windows、macOS、Docker 等。),并且每种类型的计算每分钟花费给定数量的信用。例如,中型 Docker 计算资源(Docker 的默认值)每分钟花费 10 个信用点(0.006 美元),因此在中型 Docker 资源上运行 3 分钟的作业花费 30 个信用点(0.018 美元)。

根据您使用的计算机类型和您使用计算机的分钟数,您消耗的点数只有;同时运行多少个作业并不重要。这意味着,如果您的管道中有 10 个可以独立运行的作业,它们将同时运行。

主要优势

与基于容器的计划相比,基于使用的计划有许多优势:

  1. 消除排队
    使用任何付费使用计划(性能或定制)的团队几乎不会经历作业排队,因为他们不受一定数量容器的限制。CircleCI 会随着需求的增加而增加团队可用资源的容量,这样开发人员就不会在高峰时段排队。

  2. 同一计划中的 Linux、Windows 和 macOS
    我们付费使用计划中的客户可以在同一计划中使用 Linux、Windows 和 MAC OS 计算。(免费层的客户可以访问 Linux 和 Windows)

  3. 优化计算能力
    基于使用的定价使 CircleCI 能够提供大量不同规格的不同计算类型。性能计划中的客户可以在其配置文件中指定资源类,以更改单个作业的计算资源能力。在小型 Docker 资源(1 个 CPU、2GB RAM)、大型 macOS 资源(8 个 CPU、16GB RAM)、中型 Linux GPU 资源(32 个 CPU、244GiB RAM、2 个 GPU)或两者之间的任何资源上运行作业。

  4. 按需付费
    除了为满足高峰需求而支付 100 个集装箱的费用,性能计划中的客户还可以在高峰时段扩展到同时运行 100 个作业,在无人推送代码时则减少到零。团队只为他们使用的东西付费。

这对我意味着什么?

基于容器的计划将不再可供购买。如果您想要升级您的 CircleCI 计划,您可以从您组织的帐户设置页面将您的免费帐户升级到绩效计划,或者您可以与我们的销售团队讨论定制计划。

如果你已经在 CircleCI 支付了集装箱的费用…

你目前的计划将保持不变。您仍将有权访问所有付费的容器,并且管理员仍将能够在组织的计划设置页面中添加和移除容器。但是,如果您降级为免费帐户并希望在以后再次升级,您将只能注册绩效计划或定制计划。此外,容器计划中的客户将无法访问为使用计划保留的功能,如高级资源类、Windows 构建和 Docker 层缓存。

如果你目前免费使用 CircleCI,或者你是一个新用户…

CircleCI 的免费层现在每周将为每个组织提供 2500 个免费信用点,用于私有存储库(见下文关于开源存储库的信息)。自由计划的组织将继续使用我们的中型 Docker 计算,但现在也可以使用我们的小型 Linux中型 Windows中型 macOS 计算(即将推出)

如果你在 OSS 项目中使用 CircleCI

CircleCI 将向我们的免费计划中的组织每月提供 400,000 信用点,用于开源存储库的中型 Docker 计算,但它们只能用于 Linux 计算。构建 OSS Windows 项目的组织仍然可以使用每周 2500 的免费配额,所有项目都可以在这些项目上使用,构建 OSS macOS 项目的组织可以通过联系 billing@circleci.com 请求免费的 macOS 访问。

如果您正在构建一个大型开源项目,并且您的项目需要更多的学分或更多的并发性,请与我们讨论为您的组织定制计划。

升级到性能

如今,CI/CD 不仅仅是关于自动化构建,它还关于实现软件开发生命周期的最大优化,以便您的开发人员可以花更多的时间做他们最擅长的事情:编写代码。CircleCI 引入性能计划是因为开发人员的生产力不应该被并发限制所限制。

目前使用容器计划的 CircleCI 客户可以通过向我们的支持团队提交一张票来升级性能。我们免费计划的客户可以通过访问其组织的帐户设置页面进行升级。如果您想了解更多关于绩效计划的信息,请查看我们的定价

有问题吗?

阅读我们的账单常见问题解答文档或联系我们的销售团队。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值