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
get
に trigger: 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: