くりにっき

フルスタックキュアエンジニアです

Jenkinsのジョブごとにdatabase.ymlを動的生成する

database.ymlって書いてますがファイルならなんでもいけます

前提

自分のビルドスクリプトだとこういう風にすることが多いです

# 最初にJenkins用のDB接続情報が書かれたdatabase.ymlを作る
cp config/database.yml.jenkins config/database.yml

# あとは普通にテスト
RAILS_ENV=test bundle exec rake db:create
RAILS_ENV=test bundle exec rake db:migrate:reset
bundle exec rspec

しかしこれだと同じプロジェクトを別々のジョブ*1にした場合にも同じDBを使うためロックがかかってテストが落ちることになり、Throttle Concurrent Builds Plugin - Jenkins - Jenkins Wiki で同一DBは1つずつしかテストしないような排他制御をしていました。

ジョブが増えてくるとDBがボトルネックになってビルド全体が遅くなるため、スクリプト

if [ $JOB_NAME = "app_develop" ]; then
  cp config/database.yml.jenkins_develop config/database.yml
else
  cp config/database.yml.jenkins_master config/database.yml
fi

cp config/database.yml.${JOB_NAME} config/database.yml

のようにすることも考えたのですが、ジョブが増えた時に似たようなファイルが大量にできてしまうので汎用性がありません。

差し込み変数でファイルを作れるようにgemやJenkinsのプラグイン作るしかないと思ってたのですが、今日画期的な方法*2を思いついたので実践してみました。

【本題】ジョブごとに手軽にファイルを動的生成する

こんなファイルを作っておいて

database.yml.jenkins

test:
  adapter: mysql2
  encoding: utf8
  database: app_test_JOB_NAME
  pool: 20
  username: user
  password: pass
  socket: /var/run/mysqld/mysqld.sock

スクリプト内ではsedで置換するw

cat config/database.yml.jenkins | sed -e "s/JOB_NAME/${JOB_NAME}/" > config/database.yml

sedし忘れてもエラーにならないように CREATE DATABASE できる名前にしてます

ビルド実行時には環境変数が代入されるため実際には

cat config/database.yml.jenkins | sed -e "s/JOB_NAME/app_develop/" > config/database.yml

のようなコマンドが実行されて

test:
  adapter: mysql2
  encoding: utf8
  database: app_test_app_develop
  pool: 20
  username: user
  password: pass
  socket: /var/run/mysqld/mysqld.sock

のようなdatabase.ymlが作られるため、ジョブの増減に柔軟に対応できるようになりました。

まとめ

Simple is best.

gemやpluginを作らずともシェルの基本コマンドだけで簡単に実現できるならそれにこしたことはないと思った次第。

参考リンク

*1:例えばmasterとdevelopでジョブを分けるとか

*2:俺調べ

JenkinsとGroovyで低レベルなトラブルシューティングをする

昨日会社でJenkins立てた時に .ssh/config が読めなくてハマってました。

読めなかった原因というのはJenkinsを動かしてるtomcat7ユーザのホームディレクトリ*1とJenkinsが見ているホームディレクトリ*2 が食い違ってたためなのですが、そこに至るまでの原因調査で非常に低レベルなトラブルシューティングをしてたので備忘録として残しておきます。

低レベルなトラブルシューティングとは?

ここでは「Jenkinsがシェルコマンドを叩いた時にどう実行できるか」を調べることを低レベルなトラブルシューティングとしてます。

Jenkinsをインストールしてるサーバにsshで入れれば

sudo -u tomcat7 〜

でやるのが模範解答なのですが、今回は権限の関係でtomcat7ユーザになれなかったためJenkinsにシェルコマンドを実行してもらうことにしました

どうやって?

「Jenkinsの管理 -> スクリプトコンソール」でGroovyが動かせるので、Groovy経由でシェルコマンドを実行できればよさそう

f:id:sue445:20140531024909p:plain

Groovyでシェルコマンドを実行する方法

こんな感じ

println "whoami".execute().text

結果が下に表示される

f:id:sue445:20140531025116p:plain

ただ毎回 execute().text ってtypeするのは大変なのでこういうラッパを作ると多少楽になりそう

void system(String command){
    println command.execute().text
}

f:id:sue445:20140531025441p:plain

2015/2/4 追記

自分用にスクリプトを更新。

f:id:sue445:20150204155514p:plain

サンプルコードに他意はありません(すっとぼけ)

参考URL: Groovy - Process Management

*1:/usr/share/tomcat7

*2:/var/lib/tomcat7 = CATALINA_BASE

Jenkinsで使うシェルスクリプトは-xeつけた方がよかった

忙しい人のためのまとめ

f:id:sue445:20140523142631p:plain

で指定するシェルスクリプトには

#!/bin/bash -xe

#!/bin/sh -xe

つけるべき

経緯

Jenkins周りで調べることがあってたまたま下記のエントリを発見

Jenkinsのシェルの実行について - Qiita

シェルの途中でこけても処理が続行されるため

run()
{
  command=$1
  echo "$command"
 
  eval $command
 
  # if error code returned, exit this script with error code
  RET=$?
  if [ $RET -ne 0 ]; then
    exit $RET
  fi
}

のようなラッパ経由でコマンド実行してたのですがそれが不要だったという俺的衝撃事実!!! *1

検証

test1.sh(-xe つけてないスクリプト

#!/bin/bash

ls unknown_path

echo "finish"
$ ./test1.sh
ls: unknown_path: No such file or directory
finish

$ echo $?
0

途中でエラー起こっても処理が続行して最後の echo が成功してるのでステータスコード0が返却

test2.sh(-xeつけたスクリプト

#!/bin/bash -xe

ls unknown_path

echo "finish"
$ ./test2.sh
+ ls unknown_path
ls: unknown_path: No such file or directory

$ echo $?
1

エラーが起きたらそこで中断されてステータスコードも非0が返却

Jenkinsに入れているプラグインまとめ(Rails中心)

ふと思いついたので会社のJenkinsで使ってるプラグインをまとめてみます。 RailsでJenkins運用してる人の参考になれば幸いです。(入れてるけどあまり使ってないのは端折ってます)

他に便利なプラグインあったらコメントやブコメで教えてくださいw

Ruby/Rails系プラグイン

PMD Plugin - Jenkins - Jenkins Wiki

CPD(Copy PasteDetector=コピペ検出)のためのプラグイン。

入門Jenkinsで紹介されていたので入れたはず。

入門Jenkins―実践「継続的インテグレーション」

入門Jenkins―実践「継続的インテグレーション」

参考:Javaだけじゃない入門jenkins

アプリ作る度にRailsなのにjar一式コミットする悲しみがあるので他の静的解析使いたいw

rbenv plugin - Jenkins - Jenkins Wiki

最初の頃はRVM Plugin - Jenkins - Jenkins Wiki を使ってましたが、昨今の流行でrbenvに移行しました。

Ruby/Rails以外でも使えるプラグイン

AnsiColor Plugin - Jenkins - Jenkins Wiki

コンソールに色がつきます

f:id:sue445:20140108220502p:plain

Categorized Jobs View - Jenkins - Jenkins Wiki

1つのアプリ(ライブラリ)を複数のrubyのバージョンでビルドしてるため

  • my_gem
    • my_gem_1.9.3
    • my_gem_2.0.0
    • my_gem_2.1.0

みたいに階層表示できて便利

Ci Skip Plugin - Jenkins - Jenkins Wiki

出来て1ヶ月経ってない出来たてほやほやのプラグイン

Travis CIのようにコミットコメントに [ci skip] を含めるとビルドをスキップしてくれます。 コメントだけの修正でビルド走らせたくないので使ってます。

Latest Release Date Dec 24, 2013

あっ(察し)

Embeddable Build Status Plugin - Jenkins - Jenkins Wiki

READMEに貼る定番のバッジ

Green Balls - Jenkins - Jenkins Wiki

テストが通ってる時は青よりも緑の方が個人的には嬉しい

f:id:sue445:20140108223105p:plain

f:id:sue445:20140108222008p:plain

JobConfigHistory Plugin - Jenkins - Jenkins Wiki

f:id:sue445:20140108222449p:plain

設定が変更された時に右の方にアイコンがついてdiffが見れます

Throttle Concurrent Builds Plugin - Jenkins - Jenkins Wiki

同じアプリを複数のバージョンでビルドしてる場合、

  1. masterにpushされた時にJenkinsに通知
  2. app_1.9.3 と app_2.0.0 で同時にビルドが走る
  3. 両方のテストでinsert→after(:each)でデータクリアしてるとロックがかかってテストが落ちるw

ということがあるため、DB単位で排他制御かけています。

f:id:sue445:20140108223402p:plain

下の方がモザイクかかってますが、

  • app1_db
  • app2_db
  • app3_db

みたいなチェックボックスがあって1つだけにチェックが入っています

Plot Plugin - Jenkins - Jenkins Wiki

Jenkinsにスローテストのグラフを表示する - くりにっき

これと組み合わせてrspecの実行時間をグラフ化しています

Timestamper - Jenkins - Jenkins Wiki

コンソールのログ単位でタイムスタンプがつけるプラグイン。 一時期ジョブが遅い時があってボトルネックを調べるために使っていました。

Emotional Jenkins Plugin - Jenkins - Jenkins Wiki

ビルドが失敗した時におっさんが怒り狂います

f:id:sue445:20140108224344p:plain

平常時

f:id:sue445:20140109104737p:plain

ビルドが不安定で悲しみにくれているおっさん

f:id:sue445:20140109104808p:plain

ビルドが失敗して怒り狂っているおっさん

f:id:sue445:20140109104852p:plain

Beer Plugin - Jenkins - Jenkins Wiki

f:id:sue445:20140108220649p:plain

疲れた時に眺めます

その他便利ツール

sue445/my_rails_template

rails new する時に使うオレオレテンプレート

Railsアプリをビルドするためのシェルスクリプトやspec_helper.rbを同梱

sue445/jenkins-backup-script

Jenkinsの設定ファイルやプラグインをtar.gzに固めるためのスクリプト。

このスクリプト自体もJenkinsに登録して夜中にジョブを定期実行し、別のスクリプトでバックアップサーバに転送しています

参考:jenkins-backup-scriptを作りました - くりにっき

jenkins-backup-scriptを作りました

会社でそういう需要があったので作りました

https://github.com/sue445/jenkins-backup-script

Jenkinsのバックアップ方法を探していて Backup Plugin というのはあったのですが、これだと手動バックアップしかできなかったので、Jenkinsのジョブ上で定期実行するために自分で作成。

想定としては

  • jenkins-backup-script 自体をJenkinsのジョブとして実行
    • githubからcloneして使うので、普通のジョブの設定と同じ
    • crontab形式で指定できるので1日1回定期実行
  • 設定ファイルとプラグインを階層構想を保持してtar.gzでアーカイブ
    • (弊社の場合)インフラチームのスクリプトでtar.gzファイルを別のサーバに転送
  • プラグインをアップデート前に手動バックアップしておいて、何かあったらバックアップから復旧
    • tar.gzを展開してそのまま上書きするだけ

という感じです

なんとなく二番煎じくさいですが、ググっても自分が作ったようなのは見つかりませんでした。

みんなJenkinsの定期バックアップはしなかったり、社内スクリプトなので外部に公開してないのだろうか。。。

カスタマイズしたければ適当にforkしてください。

あと、bashだけで書いているのでUNIX系ならどんな環境でも動くはずですが、Debian lennyとCentOS 6でしか動作確認してないので若干不安。。。

社内でJenkins勉強会を開催しました

他の会社だと1つのJenkinsをみんなで使うという運用(な気がする)ですが、うちの会社の場合だと個人やアプリで好き勝手にJenkinsを乱立させてます。

他の人はどんな風にJenkinsを使ってるのかなーと思って何人かに声をかけて開催してみました。

登壇者は自分を含めて3人。参加者は20人くらい。勉強会初主催にしてはまぁまぁだったと思います。

唯一の心残りは、新卒がたくさん来ると思って資料を張り切ってたのですが新卒研修とかぶってしまったという(´・ω・`)

 

社内で使ったスライド(無修正版)に社外公開用に修正を加えたスライド(モザイク修正版)をslideshareにアップしたのでよければ御覧ください。

 

Railsアプリに特化してるので他の言語の方はどうもスミマセン

一緒に発表した @ さんのエントリ

関連エントリ

おすすめ書籍

入門Jenkins―実践「継続的インテグレーション」

入門Jenkins―実践「継続的インテグレーション」

僕がJenkinsを立てようと思った時に丁度発売されたのですんごい助かりました!

Twitter API 1.1でも CI したい

以前LTでしゃべった内容をブログ用に再構成してみます

冒頭に結論

Twitter API 1.1対応は廃止されたAPIだけ対応してもダメ

ことの発端

AZusaar!!のテストにて

  • 1テストケースずつ -> GREEN
  • テストスイート一括実行 -> RED

という事象が発生

WHY ?

RateLimitに引っかかってた(;´Д`)

一度に同じAPIを大量に使うと規制される&Jenkinsのビルドも通らないorz

解決策

Groovy基礎勉強会 で @ さんに教えてもらったBetamaxを使った。

Betamaxって何よ?

テストケース中のhttp通信を全部yamlに記録するライブラリ。groovyで作られているので当然Javaからでも使えます

AZusaar!!でやったこと

pom.xml

betamaxとgroovyを追加

<project>
  <dependencies>
    <dependency>
        <groupId>co.freeside</groupId>
        <artifactId>betamax</artifactId>
        <version>1.1.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy</artifactId>
        <version>2.1.2</version>
        <scope>test</scope>
    </dependency>
  </dependencies>
</project>

テストケース

public class SomeTest{
    @Rule
    public Recorder recorder = new Recorder();

    @Betamax(tape="SomeTest#yourTest", match={MatchRule.uri})
    @Test
    public void yourTest() throws Exception {
        // do something
    }
}
  • テストメソッド単位でtapeの名前を一意にした
  • matchはyamlを読み込む時の条件(上記の例ならuriが一致したらそのyamlを読み込む)
  • 初回のテスト実行時にsrc/test/resources/betamax/tapesにyamlが保存されるので2回目以降はそれが参照される(READ_WRITEモード)
    • READ_WRITEモードの他にもWRITE_ONLYモードやREAD_ONLYモードもある
    • yamlをcommitしておけばjenkinsでも同じ結果を返せる

参考リンク

書いていて気になった点

  • テストクラスごとにRecorderやBetamaxをつけるのが冗長
    • テストケース全体の振る舞い (rspecにおけるspec_helper的なもの) を定義できればもっとシンプルになった。。。
    • Groovyでテストを書いていたらRecorderを継承した新しいRecorderを使えばBetamaxアノテーション自体不要になりそう
      • ただしそこまでやろうとするとテストのためのテストが必要になるのでちょっと重い
  • tape名にクラス名とメソッド名を書く方式だとTheoriesのようなパラメタライズテストに対応できない
    • Theoriesを1つだけだったのでそこは割りきってBetamaxを使わないようにした
  • RSpecに慣れすぎてJUnit面倒くさい
  • Javaだけでやるのが面倒くさい部分でGroovyを利用することでさっくり解決できるのは素敵。まさに万能接着剤!

重要なことなのでもう一度

Twitter API 1.1対応は廃止されたAPIだけでなくRateLimitも考慮しよう

参考書籍

API 1.1対応マダ-?

Twitter API ポケットリファレンス (POCKET REFERENCE)

Twitter API ポケットリファレンス (POCKET REFERENCE)

Ruleの使い方とか

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)