如何在Ubuntu 16.04上使用Concourse CI设置持续集成管道

时间:2022-06-06
本文章向大家介绍如何在Ubuntu 16.04上使用Concourse CI设置持续集成管道,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

介绍

Concourse CI是一个现代的,可扩展的集成系统,旨在通过可组合的声明性语法自动测试管道。

在本教程中,我们将演示如何在将新更改提交到存储库时使用Concourse自动运行项目的测试套件。我们将为使用Node.js Web框架Hapi.js编写的“hello world”应用程序配置持续集成管道。

为确保构建和测试过程始终与它们关联的代码保持同步,我们将CI定义添加到应用程序存储库。之后,我们将使用Concourse的fly命令行工具将管道加载到Concourse中。最后,我们将更改返回到存储库,以便永久地保存它们,并在新的CI工作流程中启动新测试。

准备

在开始之前,您将需要一台至少具有1G RAM的Ubuntu 16.04服务器。设置非root用户,安装和配置Concourse,安装Nginx,获取TLS / SSL证书,保护你网站的最简单方法是使用腾讯云SSL证书服务,它提供免费的可信证书。腾讯云SSL证书安装操作指南进行设置。以及为Concourse Web UI设置安全的反向代理。您需要一个指向Concourse服务器的域名才能正确保护它。

在本教程中,大部分工作将在本地计算机而不是Concourse服务器上完成。因此,您还需要确保本地计算机上有一些工具可用。您将需要一个文本编辑器在存储库中创建和修改文件。您还需要在本地系统上安装和设置Git,可以按照我们的贡献开源:Git入门教程来完成。当然你也可以使用腾讯云Tgit,它可以为开发者提供基于 Git 的在线代码托管工具,包含代码提交/存储/下载/复刻/分支/历史/比对/合并等功能。可一站式完成对代码及代码质量管理,项目及项目人员管理,大大提升研发效率。

当您在本地计算机上设置Concourse服务器并安装Git和文本编辑器后,请继续下面的操作。

在本地安装Fly命令行工具

安装Concourse时,我们将fly命令行工具安装到服务器上,以便我们可以从命令行管理Concourse实例。但是,对于日常使用,在您可以使用常用开发工具和源代码的本地系统上安装fly二进制文件的副本会更方便。

要获取与您的服务器版本匹配的fly本地副本,请在Web浏览器中访问您的Concourse实例:

https://your_concourse_url

如果您已注销,或者您当前没有配置管道,fly则会在窗口中央显示各种平台的下载链接:

如果您已登录并配置了管道,则屏幕右下角将显示下载链接:

单击代表本地计算机操作系统的图标以下载 fly 二进制文件。

接下来,按照平台特定说明 fly 在本地系统上进行设置。

Linux或macOS

如果您的本地计算机运行Linux或macOS,请在下载相应的二进制文件后按照这些说明操作。

首先,将下载的二进制文件标记为可执行,我们假设您已将文件下载到~/Downloads目录中,因此必要时调整下载位置:

chmod +x ~/Downloads/fly 

接下来,通过输入以下命令将二进制文件安装到PATH中的某个位置:

sudo install ~/Downloads/fly /usr/local/bin

您可以输入以下内容来验证可执行文件是否可用:

fly --version
3.3.1

如果能够显示版本, fly 则表示已成功安装。

Window

如果您的本地计算机运行Windows,请按键盘上的Windows键,输入powershell,然后按Enter

在出现的窗口中,输入以下命令创建一个bin文件夹:

mkdir bin

接下来,输入以下命令将fly.exe 文件从Downloads 文件夹移动到新bin文件夹:

mv Downloads/fly.exe bin

检查您是否已有PowerShell配置文件:

Test-Path $profile

如果响应为True,则表示您已有个人资料。

如果响应是False,您需要输入以下内容创建一个:

New-Item -path $profile -type file -force
     Directory: C:UserSammyDocumentsWindowsPowerShell
 Mode              LastWriteTime       Length Name
 ----              -------------       ------ ----
 -a----       7/9/2017   5:46 PM            0 Microsoft.PowerShell_profile.ps1

获得个人资料后,使用编辑器进行编辑:

notepad.exe $profile

在编辑器窗口中(如果必须创建配置文件,它将为空白),请添加以下行:

$env:path += ";C:UsersSammybin"

完成后保存并关闭文件。

接下来,为当前用户将执行策略设置为“RemoteSigned”,以允许PowerShell读取配置文件:

Set-ExecutionPolicy -scope CurrentUser RemoteSigned

最后,输入以下命令来源PowerShell配置文件:

. $profile

您现在应该可以fly.exe从任何位置调用可执行文件。通过二进制打印其版本来测试:

fly.exe --version
3.3.1

在本教程中,您将需要替换fly命令的每个实例fly.exe以匹配Windows命令。

使用Concourse Server进行身份验证

安装fly后,登录到远程Concourse服务器,以便您可以在本地管理CI环境。单个fly二进制文件可用于联系和管理多个Concourse服务器,因此该命令使用称为“目标”的概念作为标签来标识要向其发送命令的服务器。

我们在本教程中使用main作为Concourse服务器的目标名称,但您可以替换所需的任何目标名称。在-c选项后输入您的Concourse服务器的域名以及https://协议规范,以指明您的服务器位置:

fly -t main login -c https://example.com

系统将提示您输入/etc/concourse/web_environment在Concourse服务器上的文件中配置的用户名和密码:

logging in to team 'main'
​
username: sammy
password: 
​
target saved

在您进行身份验证后,该fly工具将创建一个配置文件,~/.flyrc用于存储您的凭据以供将来的命令使用。

注意:如果稍后升级Concourse版本,则可以通过输入以下命令来安装匹配版本的fly命令:

fly -t main sync

这将更新fly系统上的二进制文件,同时保持配置不变。

分叉和克隆示例存储库

现在您已经fly在系统上进行了设置,我们可以继续设置我们将用于演示Concourse管道的存储库。

在您的Web浏览器中,访问GitHub上“hello hapi”应用程序。这个应用程序是一个简单的“hello world”程序,带有一些单元和集成测试,用Node.js Web框架Hapi.js编写。

由于此示例用于演示各种持续集成系统,您可能会注意到一些文件用于为其他系统定义管道。对于Concourse,我们将在我们自己的存储库fork中创建持续集成管道。

要创建存储库的分支,请登录GitHub并导航到项目存储库。单击右上角的Fork按钮,在您的帐户中制作存储库的副本:

如果您是GitHub组织的成员,可能会询问您在哪里分叉存储库。选择帐户或组织后,存储库的副本将添加到您的帐户中。

接下来,在本地计算机的终端中,转到您的主目录:

cd $HOME

使用以下命令将存储库克隆到本地计算机,替换您自己的GitHub用户名:

git clone git@github.com:your_github_user/hello_hapi

这在您的主目录中创建一个名为hello_hapi的新目录。输入新目录以开始:

cd hello_hapi

我们将为此存储库中的示例项目定义持续集成管道。在进行任何更改之前,最好在Git中创建并切换到新分支以隔离我们的更改:

git checkout -b pipeline
Switched to a new branch 'pipeline'

现在我们有了一个新的分支,我们可以开始定义我们的持续集成管道。

为应用程序设置持续集成过程

我们将在项目存储库本身中定义我们的管道及其所有相关文件。这有助于确保持续集成过程始终与其测试的代码保持同步。

测试套件已在名为test的目录中定义。它包括一个单元测试和两个基本集成测试。运行测试的命令在scripts对象内test名称下的package.json文件中定义。在安装了npm,Node.js 的环境中,您可以通过输入npm test(在安装项目依赖项npm install之后)来运行测试。这些是我们需要在管道中复制的过程。

首先,创建一个在存储库中调用的ci目录,以容纳项目的持续集成资产。我们还将创建两个子目录ci/tasksci/scripts用于保存管道引用的各个任务定义以及任务调用的脚本。

输入以下内容创建必要的目录结构:

mkdir -p ci/{tasks,scripts}

接下来,我们可以开始创建Concourse将使用的单个文件。

定义管道

创建并用文本编辑器打开一个ci目录名为pipeline.yml的文件。如扩展名所示,Concourse文件使用YAML数据序列化格式定义:

nano ci/pipeline.yml

我们现在可以开始建立我们的管道了。

定义NPM缓存资源类型

在文件中,我们将首先定义一个新的资源类型:

---
resource_types:
  - name: npm-cache
    type: docker-image
    source:
      repository: ymedlop/npm-cache-resource
      tag: latest

为了将持续集成中的进程与通过系统的数据分开,Concourse将所有状态信息卸载到称为资源的抽象中。资源是Concourse可用于从中提取信息或将信息推送到外部的数据源。这就是所有数据进入持续集成系统以及如何在作业之间共享所有数据的方式。Concourse不提供任何在作业之间内部存储或传递状态的机制。

resource_types标题允许你定义新类型,你可以在你的管道使用诸如电子邮件通知,Twitter整合,或RSS的资源。我们定义的新资源类型告诉Concourse如何使用npm-cache-resource,这是一种作为Docker镜像提供的资源,允许Concourse安装Node.js项目的依赖项并在作业之间共享它们。

定义存储库和缓存资源

接下来,我们需要为管道定义实际资源:

. . .
​
resources:
  - name: hello_hapi
    type: git
    source: &repo-source
      uri: https://github.com/your_github_user/hello_hapi
      branch: master
  - name: dependency-cache
    type: npm-cache
    source:
      <<: *repo-source
      paths:
        - package.json

本节定义了Concourse CI作业完成任务所需的两个资源。Concourse使用资源定义来监视上游系统的变化,并了解在作业需要时如何下拉资源。默认情况下,Concourse每分钟检查一次每个新资源。设置了“触发器”选项的资源作业将在新版本可用时自动启动。

第一个资源代表GitHub上hello_hapi存储库的fork。 “source”行包含一个名为“repo-source”的YAML锚,它标记该元素以供将来参考。 这使我们可以在文档后面的不同位置包含元素的内容(“uri”和“branch”定义)。

第二个资源称为“dependency-cache”,它使用我们定义的“npm-cache”资源类型来下载项目的依赖项。在此资源的“source”规范中,我们使用该<<: *repo-source行来引用扩展&repo-source锚点指向的元素。这会将我们的应用程序存储库资源中的uri和branch设置插入到第二个资源中。名为“paths”的附加元素指向package.json定义项目依赖项的文件。

定义依赖关系收集并测试任务

最后,我们使用Concourse job定义实际的持续集成过程:

. . .
​
jobs:
  - name: Install dependencies
    plan:
      - get: hello_hapi
        trigger: true
      - get: dependency-cache
  - name: Run tests
    plan:
      - get: hello_hapi
        trigger: true
        passed: [Install dependencies]
      - get: dependency-cache
        passed: [Install dependencies]
      - task: run the test suite
        file: hello_hapi/ci/tasks/run_tests.yml

在本节中,我们定义了两个作业,每个作业都包含一个名称和一个计划。反过来,我们的每个计划都包含“获取”和“任务”元素。该任务的项目指定如何执行动作当获取的项目是任务的资源依赖关系的时候。

第一份job没有任何任务陈述。第一个get语句需要hello_hapi资源并指定trigger: true选项。这告诉Concourse每次在hello_hapi存储库中检测到新提交时自动获取存储库并开始新作业。

第一个job(get: dependency-cache)中的第二个get语句需要我们定义的资源来下载和缓存项目的Node.js依赖项。此语句评估package.json文件中的要求并下载它们。如果没有为此作业定义任务,则不会执行任何其他操作,但下载的依赖项将可用于后续作业。

注意:在此特定示例中,只有一个额外的作业,因此将Node.js依赖关系作为独立步骤缓存的好处尚未完全实现(将get语句添加到下面的测试作业就足以下载依赖项)。但是,几乎所有使用Node.js的工作都需要项目依赖项,因此如果您有可能并行完成的单独作业,单独的依赖项缓存的好处将会体现出来。

第二个job(name: Run tests)开始时声明相同的依赖关系,但有一个明显的区别。“传递”约束导致get语句仅匹配已成功遍历管道中先前步骤的资源。这就是如何形成作业之间的依赖关系以将管道流程链接在一起。

在get语句之后,定义了一个名为“运行测试套件”的任务。它不是定义完成内联的步骤,而是告诉Concourse从它获取的存储库中的文件中提取定义。接下来我们将创建此文件。

完成后,完整的管道应如下所示:

---
resource_types:
  - name: npm-cache
    type: docker-image
    source:
      repository: ymedlop/npm-cache-resource
      tag: latest
​
resources:
  - name: hello_hapi
    type: git
    source: &repo-source
      uri: https://github.com/your_github_user/hello_hapi
      branch: master
  - name: dependency-cache
    type: npm-cache
    source:
      <<: *repo-source
      paths:
        - package.json
​
jobs:
  - name: Install dependencies
    plan:
      - get: hello_hapi
        trigger: true
      - get: dependency-cache
  - name: Run tests
    plan:
      - get: hello_hapi
        trigger: true
        passed: [Install dependencies]
      - get: dependency-cache
        passed: [Install dependencies]
      - task: run the test suite
        file: hello_hapi/ci/tasks/run_tests.yml

完成后保存并关闭文件。

定义测试任务

虽然管道定义概述了我们持续集成过程的结构,但它将实际测试任务定义为另一个文件。提取任务有助于使管道定义简洁易读,但需要您读取多个文件以了解整个过程。

ci/tasks名为的目录下打开一个新文件run_tests.yml

nano ci/tasks/run_tests.yml

要定义任务,您需要指定worker需要具有的操作系统类型,定义用于运行任务的映像,命名任务将使用的任何输入或输出,并指定要运行的命令。

粘贴以下内容以设置我们的测试任务:

---
platform: linux

image_resource:
  type: docker-image
  source:
    repository: node
    tag: latest

inputs:
  - name: hello_hapi
  - name: dependency-cache

run:
  path: hello_hapi/ci/scripts/run_tests.sh

在上面的配置中,我们指定此任务需要Linux worker。Concourse服务器本身可以满足此要求,无需额外配置。

接下来,我们指出worker将用于运行任务的图像。虽然您可以创建和使用自己的图像类型,但实际上,这几乎总是Docker图像。由于我们的存储库是Node.js应用程序,因此我们选择最新的“节点”映像来运行我们的测试,因为它已经安装了相应的工具。

Concourse任务可以指定输入和输出,以指示它需要访问的资源以及它将产生的环境。输入对应于之前在“job”级别下拉的资源。这些资源的内容作为可在任务运行期间操作的顶级目录提供给任务环境。这里,应用程序存储库将在hello_hapi目录下可用,并且Node.js依赖项将在名为dependency-cache的目录下可用。您的执行步骤可能需要在任务开始时将文件或目录移动到其预期位置,并在任务结束时将环境放置在输出位置。

最后,run项列出了要运行的命令的路径。每个任务只能是带有参数的单个命令,因此虽然可以通过组合bash字符串来内联构造命令,但将任务指向脚本文件更为常见。在这种情况下,我们指向hello_hapi位于的输入目录中的脚本hello_hapi/ci/scripts/run_tests.sh。接下来我们将创建此脚本。

完成后保存并关闭文件。

定义测试脚本

最后,我们需要创建任务将执行的脚本。打开一个位于ci/scripts/run_tests.sh名为run_tests.sh的新文件:

nano ci/scripts/run_tests.sh

此脚本将操纵测试环境的输入以将项目移动到正确的位置。然后,它将通过npm test运行存储库中定义的测试套件。

将以下内容粘贴到新文件中:

#!/usr/bin/env bash

set -e -u -x

mv dependency-cache/node_modules hello_hapi
cd hello_hapi && npm test

首先,我们指出这个脚本应该由Docker容器的bash解释器执行。这些set选项修改了shell的默认行为,导致任何错误或未设置的变量停止脚本执行,并在执行时打印每个命令。这些有助于使脚本更安全,并为调试目的提供可见性。

我们运行的第一个命令将位于node_modules目录中的缓存依赖项从dependency-cache目录中移动到hello_hapi目录。请记住,这两个目录都可用,因为我们在任务定义中将它们指定为输入。这个新位置将查找npm所需的下载依赖项。

然后,我们进入应用程序库并运行npm test以执行定义的测试套件。

完成后,保存并关闭文件。

在继续之前,将新脚本标记为可执行文件,以便可以直接运行:

chmod +x ci/scripts/run_tests.sh

我们的管道和所有相关文件现已定义。

在Concourse中设置管道

在我们将pipeline分支合并回main并将其推送到GitHub之前,我们应该继续将管道加载到Concourse。Concourse将观察我们的新提交存储库,并在检测到更改时运行我们的持续集成过程。

虽然我们需要手动加载管道,但是当Concourse执行管道时,它将从存储库中的目录中读取任务和脚本。对管道本身的任何更改都需要重新加载到Concourse中才能生效,但由于我们没有内联定义所有内容,因此当作为提交的一部分上载时,将自动注意到对任务或脚本的更改。

要设置新管道,请使用set-pipeline操作使用fly命令定位Concourse服务器。我们需要使用-p选项传递新管道的名称,并使用以下-c选项传递管道配置文件:

fly -t main set-pipeline -p hello_hapi -c ci/pipeline.yml

在继续之前,系统将提示您确认配置。输入YENTER键

. . .

apply configuration? [yN]: y
pipeline created!
you can view your pipeline here: https://example.com/teams/main/pipelines/hello_hapi

the pipeline is currently paused. to unpause, either:
  - run the unpause-pipeline command
  - click play next to the pipeline in the web ui

如输出所示,管道已被接受,但当前已暂停。您可以使用其中一个fly或Web UI 取消暂停管道。我们将使用Web UI。

在您的Web浏览器中,访问您的Concourse服务器并登录。您应该看到您的新管道的可视化定义:

挂起的作业用灰色框表示,资源是较小的暗块。由资源变化触发的作业由实线连接,而非触发资源使用虚线。流动资源进行的工作表明,passed约束已在接下来的工作中设置。

蓝色标题表示管道当前已暂停。单击左上角的菜单图标(三条堆叠的水平线)以打开菜单。您应该看到管道的条目(如果管道不可见,您可能需要注销并返回)。单击管道旁边的蓝色播放图标以取消暂停:

现在管道应该取消暂停并开始运行。

在一开始,各种资源和工作可能会变成橙色,表明发生了错误。发生这种情况是因为需要下载各种Docker镜像,并且pipeline仍需要将main分支合并到我们的存储库的分支中以使任务和脚本文件可用。

使Git改动生效

现在定义了持续集成过程,我们可以将它提交到我们的git存储库并将其添加到Concourse。也可以提交到腾讯云Tgit存储库,它安全可靠,可将您从耗时的数据库管理任务中解放出来,让您有更多时间专注于您的应用和业务。

输入以下命令将新目录ci添加到临时区域:

git add ci

通过检查状态验证要提交的文件:

git status
On branch pipeline
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   ci/pipeline.yml
    new file:   ci/scripts/run_tests.sh
    new file:   ci/tasks/run_tests.yml

输入以下命令提交更改:

git commit -m 'Add Concourse pipeline'

这些更改现在已提交给我们的pipeline分支机构。我们可以通过切换分支和合并将分支合并回master分支:

git checkout master
git merge pipeline

现在,将带有新更改的master分支推送回GitHub:

git push origin master

提交后将在六十秒内启动新构建,Concourse将在下拉更改后访问管道任务和脚本。

查看新版本

回到Concourse Web UI,新构建将在下一分钟内开始通过管道进行:

黄色轮廓表示作业当前正在进行中。要监视进度,请单击“ 运行测试”作业以查看当前输出。作业完成后,完整输出将可用,作业应变为绿色:

单击主页图标返回主管道屏幕。每个作业的绿色状态表示最新的提交已通过管道的所有阶段:

管道将继续监视存储库,并在提交更改时自动运行新测试。

结论

在本教程中,我们设置了一个Concourse管道来自动监视存储库的变化。检测到更改后,Concourse会下载最新版本的存储库并使用Docker容器来安装和缓存项目依赖项。然后构建进入测试阶段,在该阶段复制依赖项并运行存储库的测试套件以检查是否引入了中断更改。


参考文献:《How To Set Up Continuous Integration Pipelines with Concourse CI on Ubuntu 16.04 》