- 2013-12-24 (火) 23:53
- Advent Calendar 2013 | Vagrant
Shin x blog Advent Calendar 2013 の 24 日目です。
先日リリースされた vagrant-serverspec を使って、テストドリブンなサーバ構築を行ってみました。
vagrant-serverspec
vagrant-serverspec は、サーバ、インフラの状態をテストするツール serverspec を Vagrant のプロビジョナとして実行できるプラグインです。これを使うことで、vagrant コマンドから、serverspec のテストを実行することができます。
詳しくは、@ryuzee さんの下記エントリを参照して下さい。
vagrant-serverspecを使ってプロビジョニング結果をテストする | Ryuzee.com
仕様
今回構築するサーバの仕様は下記です。PHP 5.5.x をインストールして、ビルトインサーバを起動するというものです。(※ちなみにビルトインサーバは開発用途でのみ利用して下さい。)
* PHP 5.5.x を yum でインストール(remi-php55)
* date.timezone を Asia/Tokyo に設定
* ビルトインサーバを Port 8000 で起動
また、環境は下記で動作確認しています。必要であれば、それぞれについてインストールを行なって下さい。
* Vagrant 1.4.1
* VirtualBox 4.3.0
* serverspec 0.13.7
* ansible 1.2.2
vagrant-serverspec インストール
vagrant plugin
コマンドで、vagrant-serverspec をインストールします。
$ vagrant plugin install vagrant-serverspec
Vagrantfile 作成
Vagrant で仮想サーバを構築して、このサーバを仕様を満たす形に進めていきます。まず、vagrant init
コマンドで Vagrantfile を作成します。
$ vagrant init centos64_ja
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
serverspec-init でデフォルトのテストケースを作成
テストドリブンということで、先に serverspec のテストを作っていきます。
serverspec-init
コマンドで、デフォルトのテストケースを生成します。いくつかオプションを選択する必要があるので、下記のように回答しています。
$ serverspec-init Select OS type: 1) UN*X 2) Windows Select number: 1 Select a backend type: 1) SSH 2) Exec (local) Select number: 1 Vagrant instance y/n: y Auto-configure Vagrant from Vagrantfile? y/n: y + spec/ + spec/default/ + spec/default/httpd_spec.rb + spec/spec_helper.rb + Rakefile
作成されたスペックファイルを元にテストケースを作ります。今回は、PHP に関するテストとなるので、spec/default/httpd_spec.rb を spec/default/php_spec.rb というファイルにリネームします。そして、このファイルにテストケースを書いていきます。
まずは、下記のように、PHP パッケージがインストールされているかだけを確認するテストを書きました。
$ mv spec/default/httpd_spec.rb spec/default/php_spec.rb $ vim spec/default/php_spec.rb require 'spec_helper' describe package('php') do it { should be_installed } end
作成したテストケースが実行されるように Vagrantfile に serverspec のプロビジョニング設定を記述します。spce/default/ 以下の全てのスペックファイルを対象としています。
config.vm.provision :serverspec do |spec| spec.pattern = "spec/default/*_spec.rb" end
これで、vagrant up
を実行すると、serverspec のテストが実行されます。では、実際にvagrant up
を実行してみましょう。下記のように、serverspec プロビジョナが実行され、テストが失敗しました。まだ、PHP はインストールしていないので、これは当然の結果です。
$ vagrant up Bringing machine 'default' up with 'virtualbox' provider... (snip) [default] Running provisioner: serverspec... F Failures: 1) Package "php" should be installed Failure/Error: it { should be_installed } sudo rpm -q php パッケージ php はインストールされていません。 expected Package "php" to be installed # ./spec/default/php_spec.rb:4:in `block (2 levels) in' Finished in 5.07 seconds 1 example, 1 failure Failed examples: rspec ./spec/default/php_spec.rb:4 # Package "php" should be installed
では、PHP をインストールするプロビジョニングをシェルで記述してみます。このプロビジョニングは、serverspec よりも前に実行する必要があるので、serverspec プロビジョニングより上に記述します。
config.vm.provision :shell, :inline => <<-EOT # # yum repository # rpm -ivh http://ftp.riken.jp/Linux/fedora/epel/6/i386/epel-release-6-8.noarch.rpm rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm yum -y install php --enablerepo=remi-php55 EOT config.vm.provision :serverspec do |spec| (snip)
これで、PHP をインストールが行われ、その後に serverspec が実行されるので、テストが通るはずです。vagrant provision
コマンドを実行して、プロビジョニングを行うと、今後はちゃんとテストが通りました。
$ vagrant provision (snip) [default] Running provisioner: serverspec... . Finished in 4.31 seconds 1 example, 0 failures
次に PHP のバージョンが、5.5.x であることを確認するためにテストを追加します。spec/default/php_spec.rb に下記を追加します。
describe command('php -v') do it { should return_stdout /^PHP 5\.5\./ } end
テストを再実行します。次は、--provision オプションで、serverspec のみを実行するようにします。ちゃんとテストが通りました。これで、サーバには PHP パッケージがインストールされており、バージョンが 5.5.x であることが確認できました。
$ vagrant provision --provision=serverspec [default] Running provisioner: serverspec... .. Finished in 4.23 seconds 2 examples, 0 failures
date.timezone を Asia/Tokyo に変更する
次に PHP ではお馴染みの date.timezone 設定が Asia/Tokyo であることを確認します。
serverspec では、php_config という、そのままずばり PHP の設定を確認するためのリソースタイプが用意されているので、これを spec/default/php_spec.rb に追加します。
describe 'PHP config parameters' do context php_config('date.timezone') do its(:value) { should eq 'Asia/Tokyo' } end end
プロビジョニングを再実行して、今回追加したテストが失敗することを確認します。
$ vagrant provision (snip) [default] Running provisioner: serverspec... ..F Failures: 1) PHP config parameters Php config "date.timezone" value should eq "Asia/Tokyo" Failure/Error: its(:value) { should eq 'Asia/Tokyo' } sudo php -r 'echo get_cfg_var( "date.timezone" );' expected: "Asia/Tokyo" got: "" (compared using ==) # ./spec/default/php_spec.rb:13:in `block (3 levels) in' Finished in 5.23 seconds 3 examples, 1 failure Failed examples: rspec ./spec/default/php_spec.rb:13 # PHP config parameters Php config "date.timezone" value should eq "Asia/Tokyo"
では、このテストが通るように date.timezone
を設定するプロビジョニングを書きましょう。
今回は、php.ini の値を上書きするために zzmyphp.ini というファイルを作成し、ここに date.timezone
の設定を記述することにします。
$ mkdir provision $ vi provision/zzmyphp.ini date.timezone="Asia/Tokyo"
このファイルをシェルプロビジョニングで、仮想サーバの /etc/php.d/ にコピーします。
config.vm.provision :shell, :inline => <<-EOT # # yum repository # rpm -ivh http://ftp.riken.jp/Linux/fedora/epel/6/i386/epel-release-6-8.noarch.rpm rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm yum -y install php --enablerepo=remi-php55 cp -a /vagrant/provision/zzmyphp.ini /etc/php.d/ # <--- 追加 EOT
これでテストが通るはずです。vagrant provision を再実行します。下記のとおり、テストが通ったので、date.timezone
の値が Asia/Tokyo
となっていることが確認できました。
$ vagrant provision (snip) [default] Running provisioner: serverspec... ... Finished in 5.12 seconds 3 examples, 0 failures
ビルトインサーバを起動
最後に PHP のビルトインサーバを 8000 ポートで起動します。DocumentRoot は、/vagrant にします。
まずテストから書きます。ここでは簡易的にポート 8000 で Listen しているかを確認します。
describe port(8000) do it { should be_listening } end
プロビジョニングを行なって、追加したテストが失敗することを確認しておきます。
$ vagrant provision (snip) [default] Running provisioner: serverspec... ...F Failures: 1) Port "8000" should be listening Failure/Error: it { should be_listening } sudo netstat -tunl | grep -- :8000\ expected Port "8000" to be listening # ./spec/default/php_spec.rb:18:in `block (2 levels) in' Finished in 6.07 seconds 4 examples, 1 failure Failed examples: rspec ./spec/default/php_spec.rb:18 # Port "8000" should be listening
では、シェルプロビジョニングでビルトインサーバを起動するように設定します。ここでは supervisor を使って、ビルトインサーバを起動するようにします。
config.vm.provision :shell, :inline => <<-EOT rpm -ivh http://ftp.riken.jp/Linux/fedora/epel/6/i386/epel-release-6-8.noarch.rpm rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm yum -y install php --enablerepo=remi-php55 cp -a /vagrant/provision/zzmyphp.ini /etc/php.d/ # 追加 yum -y install supervisor cp -a /vagrant/provision/supervisord.conf /etc/ chkconfig supervisord on service supervisord start EOT
vagrant provision を実行すると、ビルトインサーバが起動され、テストが通りました。これで、ビルトインサーバがポート 8000 で起動していることが確認できました。
$ vagrant provision (snip) [default] Running provisioner: serverspec... .... Finished in 6.07 seconds 4 examples, 0 failures
プロビジョナを ansible に変更
これで仕様どおりに稼働するサーバを構築することができました。すでに仕様を検証する serverspec のテストがあるので、構築するためのプロビジョナを変更しても、要求された仕様を満たしているかを検証することができます。ではプロビジョナを ansible に変えて再度サーバを構築し直してみましょう。
Vagrantfile のシェルプロビジョニングの設定を削除して、代わりに ansible の設定を下記のように書きます。
config.vm.provision :ansible do |ansible| ansible.playbook = "provision/playbook/main.yml" end
provision/playbook 以下に main.yml というプレイブックを配置しています。やっている内容は、シェルプロビジョニングとほぼ同じです。
--- - hosts: all sudo: yes tasks: - name: add yum repositories copy: src=files/{{ item }} dest=/etc/yum.repos.d/ with_items: - epel.repo - epel-testing.repo - remi.repo - name: add rpm-gpg-keys copy: src=files/{{ item }} dest=/etc/pki/rpm-gpg/ with_items: - RPM-GPG-KEY-EPEL-6 - RPM-GPG-KEY-remi - name: install PHP 5.5.x yum: name=php enablerepo=remi-php55 - name: put zzmyphp.ini copy: src=../zzmyphp.ini dest=/etc/php.d/ backup=yes - name: install supervisor yum: name=supervisor - name: put supervisord.conf copy: src=../supervisord.conf dest=/etc/supervisord.conf backup=yes - name: enable supervisor service: name=supervisord enabled=yes state=started
では、この ansible プロビジョニングでもテストが通るかを確かめてみます。前回のシェルプロビジョニングで構築した内容が残っているので、仮想マシンを破棄してから、再構築します。
下記のとおり、ansible によるプロビジョニングが実行され、テストが無事に通りました。これにより、要求された仕様が満たされていることが確認できました。
$ vagrant destory -f $ vagrant up (snip) [default] Running provisioner: ansible... PLAY [all] ******************************************************************** GATHERING FACTS *************************************************************** [98/3036] ok: [default] TASK: [add yum repositories] ************************************************** changed: [default] => (item=epel.repo) changed: [default] => (item=epel-testing.repo) changed: [default] => (item=remi.repo) TASK: [add rpm-gpg-keys] ****************************************************** changed: [default] => (item=RPM-GPG-KEY-EPEL-6) changed: [default] => (item=RPM-GPG-KEY-remi) TASK: [install PHP 5.5.x] ***************************************************** changed: [default] TASK: [put zzmyphp.ini] ******************************************************* changed: [default] TASK: [install supervisor] **************************************************** changed: [default] TASK: [put supervisord.conf] ************************************************** changed: [default] TASK: [enable supervisor] ***************************************************** changed: [default] PLAY RECAP ******************************************************************** default : ok=8 changed=7 unreachable=0 failed=0 [default] Running provisioner: serverspec... .... Finished in 10.62 seconds 4 examples, 0 failures
さいごに
Vagrant と serverspec の相性が良いのは分かっていたのですが、vagrant up の後にいちいち rake spec を実行するのが面倒だなと感じていました。
vagrant-serverspec を使えば、プロビジョニングの後に自動で serverspec が実行されるので、構築とテストを一息で行うことができます。
仕様を決める、テスト書く、vagrant provision 、プロビジョニング書く、vagrant provision の流れはなかなか気持ち良いです。こうして検証済のプロビジョニングは、本番環境など Vagrant 以外の環境でも利用できますし、当然ながらそのテストも serverspec で行うことができます。
サーバ設定はわりと仕様が決まりやすく、テストが書きやすいと思うので、vagrant-serverspec を使って、テストドリブンなサーバ構築を行ってみて下さい。