Chefで書いたレシピをテストする(serverspec)
こんにちは。小宮です。
今回で最終回です。
前回までにご紹介したレシピのテストするところをご紹介します。Serverspecを用います。
なぜServerspecがいいかというのは以下リンクにも書いてますが、以下の点がよいと思います。
・Chefのテストツールでなく外部のツールなので依存関係がない(puppetでも使える)
・設計思想がシンプル簡単に使えるものということで、手間があんまりなくて簡単につかえた
参考:
Serverspec at hbstudy #45
入門Chef Solo落ち穂拾い
kayac/newbie-training
「入門Puppet」
resource_typeのマニュアル
advanced_tips
ncstudy#05 ハンズオン資料
parallel_tests
・セットアップ
バージョン0.3と0.6だとテストの書き方が若干違う感じだったので、マニュアルに沿ってる新しいほうを推奨します。
[shell]# yum install rubygems
gem install serverspec rake
serverspec-init
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Select a backend type: 1) SSH 2) Exec (local) Select number: 1 Vagrant instance y/n: y Input vagrant instance name: 10.0.0.241 + spec/10.0.0.241/ + spec/10.0.0.241/httpd_spec.rb[/shell] <br> コマンドの実行が終わると、上記のようにいくつかのファイルが作成されます。<br> |
[shell]# serverspec-init
~略~
Input target host name: 10.0.0.240
+ spec/10.0.0.240/
+ spec/10.0.0.240/httpd_spec.rb[/shell]
SSHを指定し、ターゲットホストを入力すると、ホスト毎のディレクトリが作成されました。
テストを書いていきます。
[shell]# vi spec/10.0.0.240/httpd_spec.rb
require ‘spec_helper’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
describe 'httpd' do it { should be_installed } it { should be_enabled } it { should be_running } end describe 'port 80' do it { should be_listening } end describe '/etc/httpd/conf/httpd.conf' do it { should be_file } it { should contain "ServerName localhost:80" } end[/shell] <br> DetectOSとなってるのをRedHatに変更(CentOSなので)<br> |
[shell]# vi spec/spec_helper.rb
include Serverspec::Helper::RedHat[/shell]
sshのconfigを修正して接続情報を渡せるようにします
[shell]# vi .ssh/config
Host 10.0.0.240
HostName 10.0.0.240
User root
Port 22
UserKnownHostsFile /dev/null
StrictHostKeyChecking no
PasswordAuthentication no
IdentityFile "/root/.ssh/komi-test.pem"
IdentitiesOnly yes
LogLevel FATAL
Host 10.0.0.241
HostName 10.0.0.241
User root
Port 22
UserKnownHostsFile /dev/null
StrictHostKeyChecking no
PasswordAuthentication no
IdentityFile "/root/.ssh/komi-test.pem"
IdentitiesOnly yes[/shell]
db側のテストファイル名を修正して内容も実体に合わせて修正します
[shell]# mv spec/10.0.0.241/httpd_spec.rb spec/10.0.0.241/mysqld_spec.rb
vi spec/10.0.0.241/mysqld_spec.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
require 'spec_helper' describe 'mysql-server' do it { should be_installed } end describe 'mysqld' do it { should be_enabled } it { should be_running } end describe 'port 3306' do it { should be_listening } end describe '/etc/my.cnf' do it { should be_file } it { should contain "server-id = 103" } end[/shell] <br> ひとまず書いたとおりのテストがとおるか確認します<br> |
[shell]# rake spec
(in /root)
/root/.rbenv/versions/1.9.2-p290/bin/ruby -S rspec spec/10.0.0.240/httpd_spec.rb spec/10.0.0.241/mysqld_spec.rb
…………
1 2 3 4 5 6 |
Finished in 0.74866 seconds 12 examples, 0 failures[/shell] <br> 書いた分は上手くいった模様。<br> エラーのときは以下のようにダメだったコマンドとその戻りが出力されるので大変わかりやすいです。<br> |
[shell]Failures:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
1) mysql-server Failure/Error: it { should be_enabled } chkconfig --list mysql-server | grep 3:on error reading information on service mysql-server: No such file or directory 2) mysql-server Failure/Error: it { should be_running } ps aux | grep -w -- mysql-server | grep -qv grep[/shell] <br> →これらはchkconfigとpsコマンドでmysql-serverとかないと言われてるので、should be_installedとenabledとrunnningのブロックを分けて修正します。<br> <br> <br> つづいて必要なテストを全て追加しテストしていきます<br> レシピとテストをセットで使いまわすために、テストファイルもレシピと同じように分割して記述したほうがいいように考えられるので、<br> |
とりあえずクックブック単位にテストファイルを分けることにします。
たぶんホストディレクトリ毎に適当にレシピに対応した名前のテストファイルを作ってけば大丈夫です。
base_settingのレシピのテストは重複するのでロール毎に管理したいとかありますが今後の課題となります。
[shell]# vi spec/10.0.0.240/base_spec.rb
require ‘spec_helper’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# バックアップディレクトリ作成 describe file('/etc/.backup') do it { should be_directory } end describe file('/etc/hosts') do it { should contain '10.0.0.240 xxx-web03' } end # デフォルトゲートウェイの設定 describe default_gateway do its(:ipaddress) { should eq '10.0.0.93' } its(:interface) { should eq 'eth0' } end # selinuxがdisableであること describe selinux do it { should be_disabled } end # yum.confでkernelをupdate除外 describe file('/etc/yum.conf') do it { should contain 'exclude=kernel*' } end # modprobe.confでipv6を無効化 describe file('/etc/modprobe.conf') do it { should contain 'options ipv6 disable=1' } end # 必要なパッケージが入っていること %w{ sendmail ntp }.each do |pkg| describe package("#{pkg}") do it { should be_installed } end end # 不要サービス停止 %w{ ip6tables iptables messagebus kudzu }.each do |services| describe service("services") do it { should_not be_enabled } it { should_not be_running } end end # logの切りまわし設定 describe file('/etc/logrotate.d/syslog') do it { should contain 'compress' } it { should contain 'rotate 53' } end # 不要cronのパーミッションが0であること %w{ makewhatis.cron mlocate.cron prelink }.each do |files| describe file("/etc/cron.daily/#{files}") do it { should be_mode 0 } end end describe file('/etc/cron.weekly/makewhatis.cron') do it { should be_mode 0 } end # cron for ntpdate describe cron do it { should have_entry '0 * * * * /usr/sbin/ntpdate -bs 10.0.0.93' } end # timezone #describe file('/etc/localtime') do # it { should be_linked_to '/usr/share/zoneinfo/Japan' } #end # kernelparams for webserver describe 'Linux kernel parameters' do context linux_kernel_parameter('net.ipv4.tcp_syncookies') do its(:value) { should eq 1 } end context linux_kernel_parameter('vm.swappiness') do its(:value) { should eq 30 } end context linux_kernel_parameter('net.ipv4.tcp_tw_reuse') do its(:value) { should eq 0 } end context linux_kernel_parameter('net.ipv4.tcp_tw_recycle') do its(:value) { should eq 0 } end context linux_kernel_parameter('net.ipv4.tcp_fin_timeout') do its(:value) { should eq 60 } end context linux_kernel_parameter('net.ipv4.tcp_max_syn_backlog') do its(:value) { should eq 4096 } end context linux_kernel_parameter('net.core.somaxconn') do its(:value) { should eq 4096 } end end # login users のテスト %w{ xxx-op yyy-op dev }.each do |u| describe user("#{u}") do it { should exist } it { should belong_to_group 'wheel' } end end |
rake spec
1 2 3 4 5 6 7 8 |
~略~ Finished in 3.13 seconds 40 examples, 0 failures[/shell] <br> 一応、ここまで動きました。<br> Serverspec自体を0.6にupgradeしないと一部のlinux_kernel_paramaterだとかcronなどはそんなメソッドはないとかで動きませんでした。<br> <br> |
[shell]# cp -p spec/10.0.0.240/base_spec.rb spec/10.0.0.241/
vi spec/10.0.0.241/base_spec.rb
diff spec/10.0.0.240/base_spec.rb spec/10.0.0.241/base_spec.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
9c9 < it { should contain '10.0.0.240 xxx-web03' } --- > it { should contain '10.0.0.241 xxx-db03' } 75c75 < # kernelparams for webserver --- > # kernelparams for dbserver 82c82 < its(:value) { should eq 30 } --- > its(:value) { should eq 0 } 86c86 < its(:value) { should eq 0 } --- > its(:value) { should eq 1 } 90c90 < its(:value) { should eq 0 } --- > its(:value) { should eq 1 } 94c94 < its(:value) { should eq 60 } --- > its(:value) { should eq 10 } 98c98 < its(:value) { should eq 4096 } --- > its(:value) { should eq 8192 } 102c102 < its(:value) { should eq 4096 } --- > its(:value) { should eq 8192 }[/shell] <br> |
・httpdのクックブックのテストを少し書きくわえた
[shell]require ‘spec_helper’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
%w{ httpd php php-pecl-ssh2 php-mysql php-common php-devel php-pear php-pdo php-mbstring php-pecl-apc php-mcrypt php-cli mysql-libs }.each do |pkg| describe package("#{pkg}") do it { should be_installed } end end describe service('httpd') do it { should be_enabled } it { should be_running } end describe port(80) do it { should be_listening } end describe file('/etc/httpd/conf/httpd.conf') do it { should be_file } it { should contain "ServerName localhost:80" } end # basic auth test describe file('/etc/httpd/conf.d/basic_auth.conf') do it { should be_file } it { should contain "Require valid-user" } end describe file('/etc/httpd/conf/.htpasswd') do it { should be_file } it { should contain "dev" } end # wp contents exists test describe file('/var/www/html/wp-config.php') do it { should be_file } end ## mount fuse test #describe file('/var/www/html/assets') do # it { should be_mounted.with(:type => 'fuse') } # it { should be_mounted.with(:options => { :rw => true } ) } #end[/shell] <br> |
・mysqldのテストを少し追加
[shell]require ‘spec_helper’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
describe package('mysql-server') do it { should be_installed } end describe service('mysqld') do it { should be_enabled } it { should be_running } end describe port(3306) do it { should be_listening } end describe file('/etc/my.cnf') do it { should be_file } it { should contain "server-id = 103" } end describe file('/etc/logrotate.d/mysqld') do it { should be_file } it { should contain "/usr/bin/mysqladmin flush-logs" } end describe file('/opt/bin/mysql-back.sh') do it { should be_file } it { should be_executable } end[/shell] <br> ・muninのテストを書く<br> |
[shell]# vi spec/10.0.0.240/munin_spec.rb
require ‘spec_helper’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
%w{ munin-node perl-DBI }.each do |pkg| describe package("#{pkg}") do it { should be_installed } end end describe service('munin-node') do it { should be_enabled } it { should be_running } end describe port(4949) do it { should be_listening } end # munin plugins %w{ cpu memory df load tcp iostat if_eth0 if_err_eth0 }.each do |plg| describe file("/etc/munin/plugins/#{plg}") do it { should be_file } end end # munin plugins for httpd %w{ apache_accesses apache_processes }.each do |plg| describe file("/etc/munin/plugins/#{plg}") do it { should be_file } end end ## munin plugins for mysql #%w{ mysql_slowqueries mysql_queries mysql_threads }.each do |plg| # describe file("/etc/munin/plugins/#{plg}") do # it { should be_file } # end #end |
cp -p spec/10.0.0.240/munin_spec.rb spec/10.0.0.241/munin_spec.rb
vi spec/10.0.0.241/munin_spec.rb
diff spec/10.0.0.240/munin_spec.rb spec/10.0.0.241/munin_spec.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
26,33c26 < %w{ apache_accesses apache_processes }.each do |plg| < describe file("/etc/munin/plugins/#{plg}") do < it { should be_file } < end < end < < ## munin plugins for mysql < #%w{ mysql_slowqueries mysql_queries mysql_threads }.each do |plg| --- > #%w{ apache_accesses apache_processes }.each do |plg| 38a32,38 > # munin plugins for mysql > %w{ mysql_slowqueries mysql_queries mysql_threads }.each do |plg| > describe file("/etc/munin/plugins/#{plg}") do > it { should be_file } > end > end >[/shell] <br> ・zabbixのテストを書く<br> |
[shell]# vi spec/10.0.0.240/zabbix_spec.rb
require ‘spec_helper’
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
%w{ zabbix-agent zabbix zabbix-jp-release }.each do |pkg| describe package("#{pkg}") do it { should be_installed } end end describe service('zabbix-agent') do it { should be_enabled } it { should be_running } end describe port(10050) do it { should be_listening } end describe file('/opt/bin/mem_monitor.sh') do it { should be_executable } end |
cp -p spec/10.0.0.240/zabbix_spec.rb spec/10.0.0.241/zabbix_spec.rb
rake spec
1 2 3 4 5 6 7 |
~略~ Finished in 5.09 seconds 152 examples, 0 failures[/shell] <br> 問題なくテストとおりました。<br> テストそれだけでいいのかというのは疑問ののこるところではあります。<br> |
ユーザとかreplicationとかのテストはかいてないがcommandリソースタイプで標準出力をチェックするしかなさそう。
コマンドがこれ↓で、
mysql -u root -pcat /path_to_file
-s -e “show grants for repl@’10.0.0.%’;”
戻り値がこれ↓というテストを書くくらいしか思いつかない
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON . TO ‘repl’@’10.0.0.%’ IDENTIFIED BY PASSWORD ‘*43E209EED080057E35C2630AC06D3296*****’
複雑なやつは戻り値がシンプルなチェックコマンド作っておく感じになるんでしょうか。。
こんな情報も
・rake spec SPEC_OPTS=”–format html”でHTML出力ができる。エラーでないとOKしか帰ってこないのでレポートがほしい場合等に視認性がいい感じです。
・gem install ci_reporterでRakefileにrequire ‘ci/reporter/rake/rspec’追記で
JUnit 形式の XML (Jenkins で利用可能) へ変換できる
・parallel_testsつかうと並列実行できて早い。台数多いときよさそうです。
・ロールやattribute的なものを使いたい場合は以下のサイトが参考になりそうです。
advanced_tips
serverspec でホスト固有の属性値を扱う方法
Serverspecでchefのjsonを読み込む
serverspecでサーバ環境のテストを書いてみよう
長編Chefシリーズはこれでおしまいです。
では長々見ていただいてありがとうございました。
バックナンバーはこちら↓
Chef-SoloとVagrantの導入(VPC環境)
Chef-Soloでレシピを書く前の環境について(Vagrantfile,role,node,data_bags)
Chefで既存手順のレシピを書く1(初期設定)
Chefで既存手順のレシピを書く2(ユーザ作成)
Chefで既存手順のレシピを書く3(WEBサーバ)
Chefで既存手順のレシピを書く4(DBサーバ)
Chefで既存手順のレシピを書く5(munin、zabbix)
Chefで書いたレシピをテストする(serverspec)