Help us understand the problem. What is going on with this article?

Concourse CI

More than 1 year has passed since last update.

Concourse Tutorial by Stark & Wayne に従って Concourse CI という Jenkins のような物を試す。

Concourse とは、ほぼコマンドラインだけで動作する CI ツールだ。Concourse では、レポジトリやファイルサーバなどの外部の資源を Resource として統一的に扱う。Resource を読み書きする一連の Task をまとめた Job の依存関係を Pipeline と呼び、Pipeline を yaml ファイルを定義する事によって動作を決める。

インストール

Docker Compose のある環境なら 以下の手順でローカルに Concourse を構築出来る。リモートに構築したい場合は適当に docker-compose.yml を編集すれば OK。

curl -O https://concourse-ci.org/docker-compose.yml
docker-compose up &
  • ブラウザで http://127.0.0.1:8080 を開いて user: test, password: test でログイン出来る。
  • ブラウザから fly をダウンロードして適当にパスを通し、fly login でログインする。-t (--target)で名前を指定すると、以降このサーバには -t で指定した名前でアクセス出来る。
    chmod a+x fly
    PATH=$PATH:$PWD
    fly -t tutorial login -c http://127.0.0.1:8080 -u test -p test
  • Concourse CI を試すには Docker Compose を使う
  • コマンドラインでログインするには Web 画面からダウンロードした fly コマンドを使う。

Task は Concourse CI で行いたい仕事の単位

Hello World を試す。チュートリアルを clone して fly execute で最初の例を試す。fly execute は指定されたタスクをすぐ実行する。

    git clone https://github.com/starkandwayne/concourse-tutorial
    cd concourse-tutorial/tutorials/basic/task-hello-world
    fly -t tutorial execute -c task_hello_world.yml

task_hello_world.yml の内容。image_resource: でビルド用コンテナ指定し、run: で指定したコマンドを実行する。この場合、dockerhub にある busybox からビルド用コンテナが作られる。

platform: linux

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

run:
  path: echo
  args: [hello world]

ターミナルに hello world が表示されれば OK

  • Task の動作は concourse ファイルの image_resource で指定したコンテナ上で run で指定したコマンドを実行する事。

Task Inputs でタスクのコンテナにファイルを配置する

fly コマンドでローカルにあるディレクトリをビルド用コンテナにコピーしたい時は inputs: を使う

inputs_required.yaml の内容。コンテナ内に inputs の name に指定した名前のディレクトリが作られる(この場合は some-important-input)。

platform: linux

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

inputs:
- name: some-important-input

run:
  path: ls
  args: ['-alR']

コピーするディレクトリは -i で指定する。この場合カレントディレクトリがコンテナの some-important-input にコピーされる。 execute の代わりに e 一文字で良い。

cd ../task-inputs
fly -t tutorial e -c inputs_required.yml -i some-important-input=.

ターミナルにカレントディレクトリの内容が表示されれば OK

カレントディレクトリの名前を inputs の name に指定すると、-i でカレントディレクトリを指定した事になる。参照: input_parent_dir.yml

Task Scripts でタスクで行う内容を細かく記載

普通は concourse ファイルの run で複雑なコマンドを実行せずに、Task Inputs を使ってスクリプトを渡す。

task_show_uname.sh の内容

platform: linux

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

inputs:
- name: task-scripts

run:
  path: ./task-scripts/task_show_uname.sh

実行

cd ../task-scripts
fly -t tutorial e -c task_show_uname.yml

Linux のバージョンが表示されたら OK

  • この場合、inputs の name で task-scripts というディレクトリ名が指定されている。
  • 現在のディレクトリも task-scripts である。
  • ということで、カレントディレクトリがコンテナの task-scripts にコピーされる。
  • ということで、カレントディレクトリの task_show_uname.sh が run: で実行される。

Pipeline と Job で一連の仕事を定義してブラウザから実行したり閲覧したりする。

jobs:
- name: job-hello-world # Job の名前
  public: true # true にするとビルドログが認証なしで見れる
  plan: # 実行したい [Steps](https://concourse-ci.org/steps.html) の列を書く
  - task: hello-world # [Task](https://concourse-ci.org/tasks.html) の名前
    config: # Task の内容
      platform: linux
      image_resource: # task_hello_world.yml と同じ。
        type: docker-image
        source: {repository: busybox}
      run:
        path: echo
        args: [hello world]

このサンプルでは、job-hello-world という一つの Job を持つ Pipeline を定義している。

cd ../basic-pipeline
fly -t tutorial set-pipeline -c pipeline.yml -p hello-world

ここで y を入力すると Pause された pipeline と Pause された Job が出来る。pause の解除はブラウザでも出来るが非常に難しいので CLI の方が良いかも知れない。

fly -t tutorial unpause-pipeline -p hello-world
fly -t tutorial unpause-job --job hello-world/job-hello-world

表示された URL http://127.0.0.1:8080/teams/main/pipelines/hello-world から Job を選択し + でトリガすると Job が走る。

fly -t tutorial trigger-job --job hello-world/job-hello-world

pipeline を削除する。

fly -t tutorial destroy-pipeline -p hello-world
  • Task は仕事の単位
  • Job は Task をまとめたもの
  • Pipeline は Job をまとめたもの

Pipeline Resources で Git などからファイルを取得する。

Git レポジトリからソースを取ってビルドするような時 resources を使う。

resources: # 下の Job の get で参照される。
- name: resource-tutorial # resource の名前、このディテクトリに clone するので後で file で指定出来る。
  type: git # git clone で取ってくるリソース
  source:
    uri: https://github.com/starkandwayne/concourse-tutorial.git
    branch: develop

jobs:
- name: job-hello-world
  public: true
  plan:
  - get: resource-tutorial # 一番目の Step (Get): この名前のディレクトリに resource-tutorial を取得する。
  - task: hello-world # 二番目の Step (Task)
    file: resource-tutorial/tutorials/basic/task-hello-world/task_hello_world.yml # config の代わりに task を指定すると、Concourse 設定ファイルに書かれた Task を実行する。

この場合、jobs.plan が2つある。最初の Step でソースを取得して次の Step でソースを実行する。

最初の例では config を使ってコンテナとスクリプトを指定したが、代わりに file: を使って外部のファイルに記述出来る。file の内容は config 内に書くものと同じ。このように分けると task の内容を resource の中でバージョン管理出来る。

platform: linux

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

run:
  path: echo
  args: [hello world]

実行

cd ../pipeline-resources
fly sp -t tutorial -c pipeline.yml -p hello-world # sp は set-pipeline の略
fly -t tutorial up -p hello-world # up は unpause-pipeline の略

unpause したらブラウザで起動する。下の色々な方法で Job を実行するも参照。

Job を自動起動する。

トリガで実行する例。

cd ../triggers
fly sp -t tutorial -c pipeline.yml -p hello-world

gettrigger: true を指定すると自動的に Pipeline を実行する。ここでは type: time で起動しているが、type: git を使うとコミットを検出して起動するようになる。

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

- name: my-timer # 二分間のタイマを定義する。
  type: time
  source:
    interval: 2m

jobs:
- name: job-hello-world
  public: true
  serial: true # [serial](https://concourse-ci.org/jobs.html#job-serial) に true を指定すると一つづつ実行する。
  plan:
  - get: resource-tutorial # ここに trigger: true を追加すると git commit を検知して実行するらしい。
  - get: my-timer # また、タイマによる実行も出来る。
    trigger: true
  - task: hello-world
    file: resource-tutorial/tutorials/basic/task-hello-world/task_hello_world.yml

ターミナルで Job を操作する。

進行中の Job の動作結果を見る。

fly -t tutorial watch -j hello-world/job-hello-world

Job のリストを見る。

fly -t tutorial builds

色々な方法で Job を実行する。

ブラウザ UI で + を押す。

Resource を使う (後述)。

コマンド。-w を付けるとその場で結果を表示する。

fly -t tutorial trigger-job -j hello-world/job-hello-world -w

Resource で Job を自動実行する。

上の Pipeline Resources を自動実行するように変更するには Resouce を Get する際に trigger を使う。

resources: # この部分は変更なし
- name: resource-tutorial
  type: git
  source:
    uri: https://github.com/starkandwayne/concourse-tutorial.git
    branch: develop

- name: my-timer # 指定した秒数ごとに実行するリソース
  type: time
  source:
    interval: 2m

jobs:
- name: job-hello-world
  public: true
  serial: true # true だと同時実行しないようにするらしいが、なぜこのサンプルにあるのか謎。
  plan:
  - get: resource-tutorial
  - get: my-timer
    trigger: true # Get に trigger を付けると自動実行するようになる。
  - task: hello-world
    file: resource-tutorial/tutorials/basic/task-hello-world/task_hello_world.yml

実行

cd ../triggers
fly sp -t tutorial -c pipeline.yml -p hello-world

二分待つと自動実行されている。自分の git レポジトリでやる時は get: resource-tutorial に trigger: true を追加すればコミット時に自動実行される?

Job Input でテストコードとテスト対象の2つのレポジトリに分ける。

ここでは特に新しい知識は無いが応用としてテストコードを分ける方法。

resources:
- name: resource-tutorial # テストコードを resource-tutorial に clone する。
  type: git
  source:
    uri: https://github.com/starkandwayne/concourse-tutorial.git
    branch: develop

- name: resource-app # テスト対象を resource-app に clone する。
  type: git
  source:
    uri: https://github.com/cloudfoundry-community/simple-go-web-app.git

jobs:
- name: job-test-app
  public: true
  plan:
  - get: resource-tutorial
  - get: resource-app
    trigger: true # テスト対象が変更されると自動実行する。
  - task: web-app-tests
    file: resource-tutorial/tutorials/basic/job-inputs/task_run_tests.yml

実行方法と確認

cd ../job-inputs
fly -t tutorial sp -p simple-app -c pipeline.yml
fly -t tutorial up -p simple-app
fly -t tutorial watch -j simple-app/job-test-app

Output で次の task にファイルを渡す

ある task で作成したディレクトリを outputs: で指定すると、次の task の input として使える。

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

jobs:
- name: job-pass-files
  public: true
  plan:
  - get: resource-tutorial
  - task: create-some-files
    config:
      platform: linux
      image_resource:
        type: docker-image
        source: {repository: busybox}

      inputs:
      - name: resource-tutorial
      outputs: # 次の task にわたすディレクトリ名を指定する
      - name: some-files

      run: # create_some_files.sh で output を作成する。
        path: resource-tutorial/tutorials/basic/task-outputs-to-inputs/create_some_files.sh

  - task: show-some-files
    config:
      platform: linux
      image_resource:
        type: docker-image
        source: {repository: busybox}

      inputs:
      - name: resource-tutorial
      - name: some-files # create_some_files.sh で作成したディレクトリを input にする。

      run:
        path: resource-tutorial/tutorials/basic/task-outputs-to-inputs/show_files.sh

create_some_files.sh の内容:

#!/bin/sh

mkdir some-files
echo "file1" > some-files/file1
echo "file2" > some-files/file2
echo "file3" > some-files/file3
echo "file4" > some-files/file4

ls some-files/*

実行

$ cd ../task-outputs-to-inputs
$ fly -t tutorial sp -p pass-files -c pipeline.yml
$ fly -t tutorial up -p pass-files
$ fly -t tutorial trigger-job -j pass-files/job-pass-files -w

started pass-files/job-pass-files #1

initializing
running resource-tutorial/tutorials/basic/task-outputs-to-inputs/create_some_files.sh
mkdir: can't create directory 'some-files': File exists
some-files/file1  some-files/file2  some-files/file3  some-files/file4
initializing
running resource-tutorial/tutorials/basic/task-outputs-to-inputs/show_files.sh
some-files/file1  some-files/file2  some-files/file3  some-files/file4
succeeded

実行結果で mkdir: can't create directory 'some-files': File exists と出てくるように、output で指定したファイルは自動的に作られるので自分で mkdir しなくても良い。

put でビルド結果をアップロードする

Resource を使って get でダウンロードするだけでなく、put: でアップロードも出来る。この例では予め作っておいた gist にファイプをアップロードする。

まず https://gist.github.com/ に bumpme という名前のファイルを作る。

Embed ドロップダウンから Clone via HTTP を選んで URL をコピーする。例: https://gist.github.com/bc24f321b58326a97461dfce6c30d2fc.git

https://github.com/settings/tokens/new で Personal access token を作る。

Select scopes で gist を選択 例: 5d4c84b8b868391765c43803982e137028d60fee

テスト

$ git clone https://gist.github.com/bc24f321b58326a97461dfce6c30d2fc.git bumpme
$ cd bumpme
$ echo hoge > bumpme
$ git add bumpme
$ git commit -m 'updated'
$ git push origin master
Username for 'https://gist.github.com':
Password for 'https://user@gist.github.com': 

上手く行けばこの情報を使う。

cd ../publishing-outputs
cp pipeline-missing-credentials.yml pipeline.yml

ここで pipeline.yaml を書き換える。

resources:
- name: resource-tutorial
  type: git
  source:
    uri: https://github.com/starkandwayne/concourse-tutorial.git
    branch: develop
- name: resource-gist # アップロードやダウンロードを行うリソースの定義
  type: git
  source:
    branch: master
    uri: https://gist.github.com/bc24f321b58326a97461dfce6c30d2fc.git # ここを gist の clone HTTP URL に変更
    username: your_name # ここを自分のユーザ名に変更
    password: 5d4c84b8b868391765c43803982e137028d60fee # ここを Personal access token に変更

jobs:
- name: job-bump-date
  serial: true
  plan:
  - get: resource-tutorial # タスク内容が置かれたレポジトリを clone
  - get: resource-gist # 書き込むレポジトリを clone
  - task: bump-timestamp-file
    config:
      platform: linux
      image_resource:
        type: docker-image
        source: { repository: starkandwayne/concourse }

      inputs: # コンテナにタスク内容と書き込むディレクトリを入力
        - name: resource-tutorial
        - name: resource-gist
      outputs: # コンテナから更新したディレクトリを出力
        - name: updated-gist
      run:
        path: resource-tutorial/tutorials/basic/publishing-outputs/bump-timestamp-file.sh

  - put: resource-gist # 上のタスクで生成した updated-gist を resource-gist にアップロードする
    params:
      repository: updated-gist

タスクの内容 bump-timestamp-file.sh の中身はこのようになっている。resource-gist を updated-gist にコピーして書き換えて commit する。

#!/bin/sh

set -e # fail fast
set -x # print commands

git clone resource-gist updated-gist

cd updated-gist
echo $(date) > bumpme

git config --global user.email "nobody@concourse-ci.org"
git config --global user.name "Concourse"

git add .
git commit -m "Bumped date"

実行

fly -t tutorial sp -p publishing-outputs -c pipeline.yml
fly -t tutorial up -p publishing-outputs
fly -t tutorial check-resource -r publishing-outputs/resource-gist
fly -t tutorial trigger-job -j publishing-outputs/job-bump-date -w

fly check-resource を使うと、即時にレポジトリを確認するそうです。

パイプライン変数を使って外部から pipeline に値を渡す。

以下の pipeline.yaml では、params: を使って CAT_NAME と DOG_NAME という2つの環境変数を宣言しています。この際に値を パイプライン変数 を ((name)) という形式で指定すると外部から値を指定出来ます。

jobs:
- name: show-animal-names
  plan:
  - task: show-animal-names
    config:
      platform: linux
      image_resource:
        type: docker-image
        source: {repository: busybox}
      run:
        path: env
        args: []
      params:
        CAT_NAME: ((cat-name))
        DOG_NAME: ((dog-name))

具体的な値を指定するには、fly set-pipeline の -v を使うか、

$ cd ../parameters
$ fly -t tutorial sp -p parameters -c pipeline.yml -v cat-name=garfield -v dog-name=odie
$ fly -t tutorial up -p parameters
$ fly -t tutorial trigger-job -j parameters/show-animal-names -w
...
initializing
running env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOME=/root
CAT_NAME=garfield
DOG_NAME=odie
USER=root
succeeded

変数の値を定義する別の yaml ファイルを作って

cat > credentials.yml <<YAML
cat-name: tama
dog-name: pochi
YAML

-l (--load-vars-flom) でファイル名を渡します。実行:

fly -t tutorial sp -p parameters -c pipeline.yml -l credentials.yml
fly -t tutorial trigger-job -j parameters/show-animal-names -w

ただ、fly get-pipeline で丸見えになるので同僚には見える。

todo:
* https://concoursetutorial.com/basics/pipeline-jobs/
* https://concoursetutorial.com/basics/secret-parameters/

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away