Werckerの仕組み,独自のboxとstepのつくりかた

WerckerはTravisCIやDrone.ioのようなCI-as-a-Serviceのひとつ.GitHubへのコードのPushをフックしてアプリケーションのテスト,ビルド,デプロイを行うことができる.

Werckerは,TravisCIのように,レポジトリのルートにwercker.ymlを準備し,そこに記述された実行環境と実行コマンドをもとにテスト/ビルドを走らせる.

Werckerには,その実行環境をbox,実行コマンド(の集合)をstepとして自作し,あらかじめWercker Directoryに登録しておくことで,様々なテストからそれらを呼び出して使うという仕組みがある.実際,Werkcerで標準とされているboxやstepも同様の仕組みで作成されている(wercker · GitHub).

今回,WerkcerでのGolangのCross-compileとリリースのために,いくつかboxとstepを自作した.

これらの作り方を簡単にまとめておく.まず,大まかなWerckerの仕組み説明し,次に具体的なboxとstepの作り方をそれぞれ説明する.

Werckerの仕組み

Werckerにはpipelineという概念がある.pipelineは,BuildフェーズとDeployフェーズに分けられ,フェーズは複数のstepで構成される.すべてのフェーズは1つのboxと呼ばれる環境上で実行される.

How wercker works

1つのpipelineは1つのwercker.ymlに記述する.例えば,以下のようにBuildフェーズとDeployフェーズ,それらの具体的なstepを記述する.

box: box
build:
  steps:
    - stepA
    - stepB
deploy:
  steps:
    - step1
    - step2

Buildフェーズ

Buildフェーズは,GitHubやBitbucketへのコードのpushを契機に始まり,アプリケーションのビルド,テスト,コンパイルを行う.生成物がある場合は,PackegeとしてDeployフェーズに渡す.

Deployフェーズ

Deployフェーズは,Packegeを受け取り,それを外部サービスへデプロイする.例えば,Webアプリケーションであれば,Herokuへデプロイし,バイナリであれば,Github Releasebintray.comへリリースする.

box

各フェーズはboxと呼ばれる同一の環境上で実行される.boxはOSと一連のパッケージがインストールされたVMである.例えば,rubyがインストールされたbox,Golangがインストールされたboxなどがある.

boxはWerckerが提供するもの,もしくは自分でプロビジョニングを定義してWercker Directoryに登録したものを利用することができる.

例えば,Werckerが提供するGolangの実行環境が整ったboxを使いたい場合は,以下のようにwerker.ymlを記述する.

box: wercker/golang

Step

フェーズを構成するのが複数のstepであり,stepは名前がつけられた一連のコマンドの集合である.

stepは,Werckerが提供するもの,自分で定義してWercker Directoryに登録したもの,もしくはscriptとしてwercker.ymlに直接定義したものを使うことができる.

例えば,Jekyllで静的サイトを生成するBuildフェーズは以下のようにwercker.ymlを記述する.

build:
  - bundle-install
  - script:
    name: generate static site
    code: |-
      bundle exec jekyll build --trace --destination "$WERCKER_OUTPUT_DIR"

この場合bundle-installはWerckerが提供する標準のstepであり,bundlerのインストールや,Gemfileを元に依存gemのインストールを行う(wercker/step-bundle-install).scriptgenerate static siteと名付けられたstepであり,codeに実行したいコマンドを直接記述している.

Services

アプリケーションによっては,テストの際にデータベースやメッセージキューを一緒に使いたい場合がある.このようなアプリケーションとは別のソフトウェアプロセスを使うために,Werckerではboxの他にservicesを準備することができる.

servicesも,標準のもの,もしくは独自で準備してWercker Directoryに登録したものを使うことができる.

例えば,データベースにMongoDB,メッセージキューにRabbitMQを使いたい場合は,wercker.ymlに以下を記述する.

box: wercker/ruby
services:
  - wercker/mongodb
  - wercker/rabbitmq

servicesには,環境変数を使ってアクセスすることができる.例えば,上の例の場合は,以下のような環境変数を使うことができる.

  • WERCKER_MONGODB_HOST
  • WERCKER_MONGODB_PORT
  • WERCKER_RABBITMQ_HOST
  • WERCKER_RABBITMQ_PORT

Environmental Variables

Werckerは実行時に独自の環境変数を設定する(Environmental Variables).例えば,WERCKER_GIT_REPOSITORYにGitのレポジトリ名を,WERCKER_STEP_NAMEにstepの名前を設定する.これらは各stepから参照することができる.

環境変数は,Werckerのダッシュボードから独自の値を設定することもできる.これは,自分以外からは隠蔽できるので,例えば,外部サービスに接続するためのTOKENなどを設定することができる.

Packeageの受け渡し

BuildフェーズからDeployフェーズへのPackageの受け渡しには,WERCKER_OUTPUT_DIRディレクトリを利用する.

Werckerでは,すべてのstepをWERCKER_SOURCE_DIRディレクトリへの移動で始める.Buildフェーズでは,レポジトリのソースが,Deployフェーズでは,BuildフェーズでWERCKER_OUTPUT_DIRに出力されたPackageがWERCKER_SOURCE_DIRに配備される.

boxのつくり方

まず,boxのつくり方を説明する. boxを自作する利点は,ビルド時間の短縮にある.プロビジョニングはBuildフェーズの途中でstepとして定義することもできるが,テストの度にそれを実行するのは時間がかかりすぎる.

boxの作成は,アプリケーションと同様にpipelineを作成して行う.boxを作成する流れは以下のようになる.

  1. box専用のGithub(Bitbucket)レポジトリの作成
  2. wercker-box.ymlの作成
  3. レポジトリをWerckerに登録
  4. DeployフェーズにWercker directoryへの登録を指定

具体的なプロビジョニングはwercker-box.ymlに記述する.boxのプロビジョニングにはBashスクリプト,もしくはChef,Puppetが使える.ここではBashスクリプトで説明する.他は以下を参考.

wercker-box.yml

以下にシンプルなwercker-box.ymlを示す.

name: hello
version: 0.0.1
inherits: wercker/ubuntu12.04-webessentials@0.0.3
type: language
platform: ubuntu@12.04
description: Say hello world
keywords:
  - hello
    script: |
      sudo apt-get update -y
      sudo apt-get install -y hello

各項目を簡単に説明する.

  • name
    • boxの名前.werkcer.ymlでboxを指定する際に利用する.例えば,アカウント名がtcnksmである場合は,tcnksm/helloという名前で指定する.
  • version
    • boxのバージョン.同じバージョンではWercker Directoryにデプロイできないので,boxを新しくする度にバージョンを上げる必要がある.バージョンを使ってboxを指定することもできる.例えば,tcnksm/hello@0.0.1のように指定できる.
  • inherits
    • 継承するbox名.プロビジョニングはこのboxに対して行われる.基本的にはWerckerの標準boxを使うことになると思う.例えば, Golangの実行環境が整ったboxに対してプロビジョニングを行いたい場合は,wercker/golangを指定する.
  • type
    • boxのタイプ.mainserviceを指定する.普通のboxであればmain,servicesとして使いたい場合はserviceとする.
  • platform
    • プラットフォーム.現在(2014年10月)は,Ubuntu12.04のみしかサポートされていない.
  • description,keyword
    • boxの簡単な説明とキーワード.これらはメタデータとしてWecker directoryに登録される.
  • script
    • 実際のプロビジョニングを記述する.

他にもserviceを作成したい場合は,そのサービスにアクセスするための環境変数をenvという項目で登録する必要がある.

Wercker Directoryへの登録

あとは上で作成したwercker-box.ymlをGithub(Bitbucket)のレポジトリに上げ,Werckerと連携する.

Werckerと連携したら,Deployフェーズの登録をする.登録は,settingsタブのDeploy targetsから行う.TargetにはWercker Diretoryを指定する.ターゲットの名前は自分が分かりやすい名前をつければよい.

これで,アプリケーションと同様にレポジトリへのPushを契機に,boxのプロビジョニングとWerckerディレクトリへの登録が継続的に行われるようになる.デプロイが完了したら,そのboxはすぐに別のpipelineから利用できる.

stepのつくり方

次に,stepの作り方を説明する.stepを作成する利点としては,wercker.ymlをシンプルに保つことができること,他で使い回しが出来ることなどが挙げられる.stepもわざわざ作成しなくても,scriptとして,直接コマンドを定義することができる.ただ,複雑なことをやろうとするとscriptは肥大化し,wercker.ymlの可読性はどんどん下がる.

stepの作成も,pipelineを作成して行う.stepを作成する流れは以下のようになる.

  1. step専用のGithub(Bitbucket)レポジトリの作成
  2. wercker-step.ymlの作成
  3. run.shの作成
  4. レポジトリをWerckerに登録
  5. DeployフェーズにWercker directoryへの登録を指定

wercker-step.ymlにstepの定義,run.shに実際に実行したいコマンドを記述する.

wercker-step.yml

以下にシンプルなwercker-step.ymlを示す.

name: hello
version: 0.1.0
description: Say hello
kyewords:
  - hello
properties:
  first-name:
    type: string
    required: true
  last-name:
    type: string

各項目を簡単に説明する.

  • name
    • stepの名前.werkcer.ymlでstepを指定する際に利用する.例えば,アカウント名がtcnksmである場合は,tcnksm/helloという名前で指定する.
  • version
    • stepのバージョン.同じバージョンではWercker Directoryにデプロイできないので,stepを新しくする度にバージョンを上げる必要がある.バージョンを使ってstepを指定することもできる.例えば,tcnksm/hello@0.1.0のように指定できる.
  • description,keyword
    • stepの簡単な説明とキーワード.これらはメタデータとしてWecker directoryに登録される.
  • properties
    • run.sh に渡す引数を定義する.引数はwercker.ymlから指定できる.required: trueにより引数を必須にすることができ,指定されていない場合にstepを失敗させることができる.

propertiesに指定した引数は,環境変数としてrun.shに渡される.例えば,上の例の場合は,以下のような環境変数に値が格納されることになる:

  • WERCKER_HELLO_FIRST_NAME
  • WERCKER_HELLO_LAST_NAME

このstepはwerkcer.ymlでは,以下のように記述して利用する.

steps:
  - tcnksm/hello:
    first-name: taichi
    last-name: nakashima

シェルスクリプト書きたくない

stepは単にrun.shを起点にするだけなので,その中から.js.rbを呼び出せばよい.ただ,boxにその言語環境が必ずしも準備されているわけではないので,stepの汎用性が下がるということに注意する必要がある.

インストールが必要なコマンドを使いたい

run.shからboxに依存することなく特別なコマンドを叩きたい場合がある.その場合は,インストールコマンドを直接書いてしまえばよい.

バイナリをあらかじめレポジトリに含めることもできる.例えば,自分が作成したtcnksm/wercker-step-ghrghrバイナリをbinディレクトリに含めており,run.shからそれを直接呼び出して使っている.

Wercker Directoryへの登録

boxと同じようにWercker上でDeployフェーズを登録すればよい.デプロイが完了したら,すぐにboxはすぐに別のpipelineから利用できる.

まとめ

肥大化したwercker.ymlは扱いづらいので,これからも何かあればboxとstepは自作すると思う.

Wercker Directoryの仕組みは,DockerHubの仕組みに似ていて扱いやすい.というか,Werckerにはwercker-lab/dockerというboxが準備されていてDockerを使うことができる.次は,その辺で遊んでみようと思う.