Vagrant + Jenkins の CI を AWS でも回す

昨晩 Jenkins と Vagrant で CI だ、と書いたら

という反応があった。確かに、可能なら物理サーバに依存しない形でテストできるとより嬉しい場面もありそうですね。

しかしそこは VagrantVagrant はバージョン 1.1 から、バックエンドを VirtualBox だけでなく AWS (EC2) などの IaaS を指定して仮想サーバーを作ったり壊したりできるようになっています。詳しくは http://d.hatena.ne.jp/naoya/20130315/1363340698 この辺を。この機能を利用すれば昨日の Jenkins + Vagrant のフローをほとんど変えずに、EC2 のインスタンスでのインテグレーションテストができそうですね。

速見もこみち「では、早速やっていきましょう。」

MOCO'Sキッチン (日テレBOOKS)

Multi VM でローカル/リモート両対応に

せっかくなので VirtualBox を使ったローカルでのテストと、EC2 を使ったリモートでのテスト、両方を流せるようにしたい。Vagrant の Multi VM を使えば一つの Vagrantfile で複数の仮想サーバーを管理するのも簡単です。

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.define :local do |local|
    local.vm.box = "centos"
  end

  config.vm.define :remote do |remote|
    remote.vm.box = "dummy"
    remote.vm.provider :aws do |aws, override|
      aws.access_key_id     = ENV['AWS_ACCESS_KEY_ID']
      aws.secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
      aws.keypair_name = "naoya's keys"
      aws.instance_type = "t1.micro"
      aws.region = "ap-northeast-1"
      aws.ami = "ami-a3af2ba2"
      aws.security_groups = [ 'webserver' ]
      aws.tags = {
        'Name' => 'jenkins-test'
      }

      override.ssh.username = "ec2-user"
      override.ssh.private_key_path = "~/.ssh/aws-naoyaskeys.pem"
    end
  end
end

こんな感じ。これで

% vagrant up local

とすればいつも通り VirtualBox のサーバーが立ち上がり

% vagrant up remote --provider=aws

で EC2 のインスタンスが立ち上がります。vagrant up で両方同時に立ち上げることも可能です。

Jenkins のパラメータ付きビルドで振り分ける

Jenkins には「ビルドのパラメータ化」なる設定項目があって、これを使うとユーザーからの入力値に応じてビルドの内容を動的に変更したりすることができる。例えば、Git から取ってくるブランチ名をビルドの毎に指定したりとかにも使われる。これを使って先の local / remote の VM 名を選択できるようにします。

ここで設定した値はビルドのシェルスクリプトから環境変数として参照できるのでそれをうまく使って

if [ $VM_NAME = 'remote' ]; then
  vagrant up remote --provider=aws
else
  vagrant up local
fi

vagrant ssh-config $VM_NAME --host=jenkingrant > vagrant-ssh.conf
bundle
bundle exec knife solo bootstrap jenkingrant -F vagrant-ssh.conf
bundle exec rake ci:setup:rspec spec
rm -f vagrant-ssh.conf
vagrant destroy $VM_NAME -f

という具合に、変数を見ていずれの VM を起動するかを選択するようにする。

これでビルドの際にパラメータの入力が要求されるようになる。


テストしてみる

ちゃんとうまくいっているでしょうか。実際に Jenkins のコンソールを見てみます。一部抜粋。

[workspace] $ /bin/sh -xe /var/folders/nl/vdz7nrv12zj44kcjg_vmpr1r0000gn/T/hudson5435348702666934788.sh
+ '[' remote = remote ']'
+ vagrant up remote --provider=aws
Bringing machine 'remote' up with 'aws' provider...
[remote] Warning! The AWS provider doesn't support any of the Vagrant
high-level network configurations (`config.vm.network`). They
will be silently ignored.
[remote] Launching an instance with the following settings...
[remote]  -- Type: t1.micro
[remote]  -- AMI: ami-a3af2ba2
[remote]  -- Region: ap-northeast-1
[remote]  -- SSH Port: 22
[remote]  -- Keypair: naoya's keys
[remote]  -- Security Groups: ["webserver"]
[remote] Waiting for instance to become "ready"...
[remote] Waiting for SSH to become available...
[remote] Machine is booted and ready for use!
[remote] Rsyncing folder: /Users/naoya/.jenkins/jobs/jenkingrant/workspace/ => /vagrant
+ vagrant ssh-config remote --host=jenkingrant
+ bundle
Resolving dependencies...
Using rake (10.0.4) 
Using builder (3.2.0) 

EC2 インスタンスが立ち上がってその VM が使われているのが分かります。上手に焼けました。あ、最後にオリーブオイルをかけるのをお忘れなく。追いオリーブ。

なお、パラメータ付きビルドは、当然のことながら毎回手動でパラメータを入力するのではなくビルド実行のURLを kick する際にパラメータを指定したりといったことが可能です。そのあたりは Jenkinsで外部パラメータで与えたブランチを対象にビルドできるようにしておくと凄惨性あがって墓ドル - ( ꒪⌓꒪) ゆるよろ日記 などを参照。

蛇足ですが、このテストは落ちた際になぜテストが落ちたかを対象サーバを見て検証できるように、サーバーの破棄はしないようにしています。VirtualBox なら問題ないですが、EC2 だとテストが失敗したときそのままインスタンスが立ち上がりっぱなしだと課金が発生してしまうので、ビルド後に必ず vagrant destroy が走るようにする方がいいかもしれません。

この辺、自分は今のところ Jenkins Notifier for Mac OS X を入れて通知を出して放置しないように気をつけることで回避しております。

もこみち「今日もおいしくできましたね!」