Concourse学习:介绍和实践

Concourse is what

Concourse是起到CI(Continuous Integration,持续集成)作用的集成工具。而CI强调的是开发人员提交新代码后,立刻进行构建、单元测试。根据测试结果,我们可以确定新代码和原代码能否正确地集成在一起。更多的介绍可以看知乎回答

ci diagram

如上图,Concourse在开发者提交新版本代码后会按部就班的执行应用的构建、测试(单元测试),并保留执行的状态。

Concourse有如下几个特性:

  • Pipeline是一等公民:Concourse以pipeline机制运行集成任务。pipeline将Task、Resource、Job三者有机地结合起来。
  • 容器和微服务:everthing运行在可重现的容器上。
  • 代码化:任务每件事情都自动化、脚本化、无需手动配置的冲洗创建。
  • 基础架构不可知:以Bosh进行部署,Bosh兼容多种基础架构。
  • Golang:原生支持语言。
  • 为CloudFoundry构建:嗯,这点也很重要。

第一条提到的三个概念:Task、Resource、Job的官方解释如下:

A task is the execution of a script in an isolated environment with dependent resources available to it.

Task是脚本的执行。Task可以被Job执行或者通过fly(concourse的cli)手动执行。

A resource is any entity that can be checked for new versions, pulled down at a specific version, and/or pushed up to idempotently create new versions.

Resource是带版本的实体,比如git仓库。

a job describes some actions to perform when dependent resources change (or when manually triggered).

Job是将相依赖的Resource变化时执行的行为。

Concourse的整体架构如下图。
concourse arch

其中几个重要的组件:

  • Garden:go实现的Warden,即CF的容器管理。
  • ATC:全称Air Traffic Control,提供REST API和Web GUI
  • TSA:全称Transport Security Agency,提供工具的安全机制。
  • baggage-claim:remote layered file system。

How to connect Concourse

本节翻录自concourse-tutorial

1. fly cli

fly是Concourse的命令行工具(cli)。第一次使用时需要制定target API以登录Concourse工具。使用 fly —-target tutorial login —-concourse-url http://192.168.100.4:8080 sync
命令。--target可以缩写成-t。每次执行fly命令时都会接yaml格式的配置信息。

比如在tutorial中的第一个例子里

---
platform: linux

image_resource:  
  type: docker-image
  source: {repository: busybox}

run:  
  path: echo
  args: [hello world]

我们定义平台为linux,镜像使用busybox;并执行echo.sh脚本,脚本里就是bash echo命令。Concourse执行时会先获取busybox的镜像并执行run里的job。

这里需要注意的点是使用了run来封装命令的shell脚本,并且为了方便管理将task files(.yml)和scripts(.sh)以同样的名字命名。原因当通过fly执行task时,fly会讲当前目录上传,作为当前task的输入。这意味着脚本能够被执行(找到)。

其在命令行中执行pipeline的结果如下图,成功时会将'hello world'打印出来。

result 1

2. task's inputs

这一小节介绍怎么向task传递输入。和执行封装的脚本一样,当要获得数据文件时,需要指明想要获取的文件。

inputs:  
- name: 01_task_hello_world
  path: .

path: .的意思是将名叫01taskhello_world的目录替代现有task的根目录。path默认设置为根目录下inputs的name。

3. Basic pipeline

前面说到可以使用命令行查看pipeline的执行结果,我也可以使用Web UI管理和查看pipeline的执行。url默认为http://192.168.100.4:8080/teams/main/pipelines/your-pipeline-name。

首先我们在fly设置pipeline。

set_pipeline

并且设置pipeline的状态为unpause:fly -t tutorial unpause-pipeline -p helloworld

然后在页面上点击相应的pipeline,并且点击右上角的'+'符号手动开启。下图就是一个成功运行晚的结果。

ui_status

4. Tasks extracted into resources

前面说过,Resource是带版本的实体。那么我们可以将执行的源码也放在resource里。比如我们可以将yaml添加为resource。

resources:  
- name: resource-tutorial
  type: git
  source:
    uri: https://github.com/starkandwayne/concourse-tutorial.git

通过上面的步骤,我们就可以将resources添加到job中。

jobs:  
- name: job-hello-world
  public: true
  plan:
  - get: resource-tutorial
  - task: hello-world
    file: resource-tutorial/01_task_hello_world/task_hello_world.yml

看到jobs plan,job被分为两步。第一步是获得 git repos资源。第二步是引用另一个yaml执行用户定义的任务。这样做的好处是引用的task需要修改时能够进行修改。而缺点就是对于任务的描述会不够清晰。我们可以通过文件名来提高pipeline的可读性(readable)。

这一步pipeline的结果如下。

resource_result

5. Trigger

这一节介绍触发器——trigger。一般在一般当resource更新完毕时都要触发job的执行。触发jobs的方式有三种:

  • 手动点击web上的’+’button
  • yaml文件中输入trigger to resources
  • 命令fly --target target trigger-job --job pipeline/jobname command

第一种和第三种我们都尝试过,这里通过配置文件添加trigger的方式来触发任务。

triggering jobs和non-triggering jobs的定义是一样的。不同的是job的构建plan里会有需要的trigger对象: trigger: true的意思就是当resource-tutorial更新时完就触发。

jobs:  
- name: job-demo
  plan:
  - get: resource-tutorial
    trigger: true

还可以通过time resource周期性的触发job。其写法如下:

resources:  
- name: my-timer
  type: time
  source:
    interval: 2m

当up这个pipeling之后,可以在web上看到pipeline的组成。有trigger的是实线,无trigger的是连字符线。

pipeline

开始执行时,在resource步骤会将version缓存起来。

6. Job‘s inputs

前面我们提到了task的input,对于job的inputs也很类似。我们需要在这一节实习一个进行单元测试的simple go web app。

对于测试,需要满足以下条件:

  • a task: 包含所需依赖的image
  • an input: 包含任务脚本的resource
  • an input: 包含应用代码的resource

其pipelin.yml内容如下:

---
platform: linux

image_resource:  
  type: docker-image
  source: {repository: golang, tag: 1.6-alpine}

inputs:  
- name: resource-tutorial
- name: resource-app
  path: gopath/src/github.com/cloudfoundry-community/simple-go-web-app

run:  
  path: resource-tutorial/10_job_inputs/task_run_tests.sh

在这个用例里,需要有Go语言的环境,所以image选择带golang的。resource-app会放置来路路径(使用path替换file)保持源码,默认在相同目录下存储内容。这么做的原因是指定构建和测试go应用(后续会介绍)。

test_result

最后的ok表示测试通过。

7. Pass tasks outputs to another task

这节将使用outputs section将一个任务的输出作为另一个任务的输入。其写法如下,这个例子输出到some-files文件中。

outputs:  
- name: some-files

在这节pipeline中的job首先获取resource-tutorial源码,并且依次执行执行另外两个task。createsomefiles创建4个文件,showsomefiles使用ls打印文件。结果如下图。

pass<em>files</em>result

8. Publish outputs

在这节我们将resource-gist的dump文件写入date变量值并上传到gist上。这个pipeline通过push new git commits来完成修改的上传。使用 put section 完成数据的更新。

- put: resource-gist
  params: {repository: updated-gist}

puts_pipeline

其pipeline如上图。


通过几个较简单的例子可以看到concourse还是很有意思的。通过yaml文件配置集成的步骤,并且trigger也是频繁使用额工具。总之,concourse是Bosh在部署应用环境的集成利器。那么下次就来学习一下Bosh。