ノウハウ
[エンジニア向け]

serverspecでリストを使った複数サーバテスト

ノウハウ
テクニカルサポート

社内勉強会で紹介されていたserverspecを、セットアップ後チェックに導入したのでまとめます。

http://serverspec.org/

インストールとテスト

■環境
CentOS 6.5
ruby 2.1.2p95

■インストール

# gem install serverspec
# gem install rspec-its

rspec-itsも一緒にインストールしておきます。

■構成ファイル作成
serverspec-initを使って構成ファイルを作成します。
例として/home/SERVERSPECディレクトリを作成し、作業ディレクトリにします。

以下は、serverspecを実行するサーバから、spec.ex-cloud.jpに対して、ssh接続を使ってテストする場合です。

# mkdir /home/SERVERSPEC
# cd /home/SERVERSPEC
# 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: n
Input target host name: spec.ex-cloud.jp
 + spec/
 + spec/test.excloud-jp/
 + spec/test.excloud-jp/httpd_spec.rb
 + spec/spec_helper.rb
 + Rakefile

以下のようなファイル・ディレクトリが作成されます。

SEVERSPEC/
├── Rakefile
└── spec
    ├── spec.ex-cloud.jp
    │   └── httpd_spec.rb
    └── spec_helper.rb

spec.ex-cloud.jpというディレクトリ名がそのままssh接続先になるので、テスト対象サーバを変えたい場合は、このディレクトリ名を変更します。

サーバ名ディレクトリの中に入っているhttpd_spec.rbに、サンプルテストが記述されています。
Rakefileで*_spec.rbファイルを読むように指定されているので、テストファイル名は_spec.rbで統一します。

■rspec-itsライブラリ読み込み

require 'serverspec'
require 'pathname'
require 'net/ssh'
require 'rspec/its' #追加

以下略

テストでits(:content)などを使う場合に必要です。

■テスト作成
サンプルテストファイルhttpd_spec.rbを削除して、簡単なテストを作成してみます。
テスト内容の詳細はhttp://serverspec.org/resource_types.htmlにあります。

require 'spec_helper'

# hostnameコマンドの出力結果が'spec.ex-cloud.jp'と同じか
describe command('hostname') do
  it { should return_stdout 'spec.ex-cloud.jp' }
end

# /etc/sysconfig/i18nにja_JP.UTF-8が記述されているか
describe file('/etc/sysconfig/i18n') do
  its(:content) { should match 'ja_JP.UTF-8' }
end

# /root/.ssh/authorized_keysのパーミションは600か
describe file('/root/.ssh/authorized_keys') do
  it { should be_mode 600 }
end

# sshdの初期起動は有効か
# sshdは起動しているか
describe service('sshd') do
  it { should be_enabled }
  it { should be_running }
end

# ex-cloud.jpの名前解決はできるか
describe host('ex-cloud.jp') do
  it { should be_resolvable }
end
 

■テスト実行
テスト対象サーバに、キー認証でSSHノーパスワードログインできるよう設定してあります。

# cd /home/SEVERSPEC
# rake spec

エラーが出た場合は、結果が細かく表示されるので、サーバ側の設定を修正していくことができます。

■特殊なテスト
セットアップ後チェックに必要だったテスト作成で、少し調べて作成したのは以下2つぐらいでした。
ほとんどのテストはResource Typesに掲載されている情報で作成できます。

describe command("TIME=`svstat /service/ntpd | awk '{print $5}'` ; test $TIME -gt 10") do
  it { should return_exit_status 0 }
end
 
describe service('ntpd') do
  it { should_not be_enabled }
  it { should_not be_running }
end
 

複数サーバテスト

このままでは、テスト対象サーバが増えた場合に対応できないので、リストを使って複数サーバにテストできるようにします。

■構成
テスト対象サーバは、リストで持つので、ディレクトリ名がサーバ名である必要がありません。
ディレクトリをテスト内容別に分け、このサーバにはweb用のテスト、このサーバにはdb用のテストといったことができるようにします。
今回は、baseテスト・webテスト・dbテストに分けてみました。

SEVERSPEC/
├── Rakefile
└── spec
    ├── base
    │   └── base_spec.rb
    ├── web
    │   └── web_spec.rb 
    ├── db
    │   └── db_spec.rb  
    └── spec_helper.rb 

■リスト

spec.ex-cloud.jp:
  :roles:
    - base
    - web
  :ip: 203.104.xxx.xx1
  :gw: 203.104.xxx.xx1
  :host_name: spec.ex-cloud.jp

spec2.ex-cloud.jp:
  :roles:
    - base
    - db
  :ip: 203.104.xxx.xx2
  :gw: 203.104.xxx.xx2
  :host_name: spec2.ex-cloud.jp 
 

▽roles
さきほどテスト内容別に分けたディレクトリ名をここに記述すると、そのディレクトリ内のテストが、各サーバに対して実行されます。

▽:xxx:
各サーバごとに違った値を持たせて、テストに利用することができます。
テスト内に#{property[:xxx]}と記述すると、値として利用することができます。

■テスト作成例

require 'spec_helper'

describe interface('eth0') do
  it { should have_ipv4_address("#{property[:ip]}") }
end

describe default_gateway do
  its(:ipaddress) { should eq "#{property[:gw]}" }
end

describe command('hostname') do
  it { should return_stdout "#{property[:host_name]}" }
end
 
require 'spec_helper'

describe service('httpd') do
  it { should be_enabled }
  it { should be_running }
end
 
require 'spec_helper'

describe service('mysqld') do
  it { should be_enabled }
  it { should be_running }
end
 

baseテスト・webテスト・dbテストそれぞれ作成しました。

■設定ファイル変更
リストを使ったテストに必要な変更です。
rubyの知識が無いので、ありがたいwebの情報を組み合わせてあります。

require 'rake'
require 'rspec/core/rake_task'
require 'yaml'

properties = YAML.load_file('properties.yml')

desc "Run spec to all hosts"
task :spec => 'spec:all'

namespace :spec do
  task :all => properties.keys.map {|key| 'spec:' + key.split('.')[0] }
  properties.keys.each do |key|
    desc "Run spec to #{key}"
    RSpec::Core::RakeTask.new(key.split('.')[0].to_sym) do |t|
      ENV['TARGET_HOST'] = key
      puts "\n========================================"
      puts "HOSTNAME: #{key}"
      t.fail_on_error = false
      t.pattern = 'spec/{' + properties[key][:roles].join(',') + '}/*_spec.rb'
    end
  end
end
 

t.fail_on_error = false これをつけておくと、エラーが出てもテストが中断されません。

require 'serverspec'
require 'pathname'
require 'net/ssh'
require 'rspec/its'
require 'yaml'

include Serverspec::Helper::Ssh
include Serverspec::Helper::DetectOS
include Serverspec::Helper::Properties

properties = YAML.load_file('properties.yml')

RSpec.configure do |c|
  c.host  = ENV['TARGET_HOST']
  set_property properties
  options = Net::SSH::Config.for(c.host)
  user    = options[:user] || Etc.getlogin
  c.ssh   = Net::SSH.start(c.host, user, options)
  c.os    = backend(Serverspec::Commands::Base).check_os
end

■テスト実行

# cd /home/SEVERSPEC
# rake spec

これで、リスト内にある全サーバにテストが行われます。

この記事を書いた人

テクニカルサポート

テクニカルサポートを得意とするエクスクラウドのサポートスタッフ。

この記事のタグ

オススメの記事

ページトップへ