読者です 読者をやめる 読者になる 読者になる

日記っぽい何か

インフラ系っぽいエンジニアのメモっぽい何か ドラクエ10の記事はblogs.yahoo.co.jp/j_k54dqxに移転しました

ItamaeとServerspecを使ったサーバ構築と自動テストを試してみた

まえがき

こんにちはじょにーです。夏休みの自由研究です。
前から触ってみたかったItamaeと、Serverspecによるテストを行える環境を作ってみました。

こえむさんによる
Itamae + Serverspec でサーバ構築・テスト自動化 総仕上げ! | こえむの編集後記

こちらを参考に(というかほぼそのままなぞって)CentOS7にてItamaeとServerspec によるインフラ構築を試した記録です。
ほぼそのままなぞっただけですので、本来は記事として書くようなことでもないのですが、
特に予備知識なくそのままなぞった際に、うまくく動かないところがありましたので、トライアンドエラーを繰り返してかつそのまま記載しています。そのあたりの試行の参考になればいいなという感じです。

手順

IDCFクラウドにてCentOS7.1の仮想サーバーを2台準備しました

  • admin01
  • node01

上記にログインして進めていきます。
admin01を作業環境として、
node01にmysqlhttpdphpをインストール&テスト という使い方をしてみます。


まずはadmin01にログインし、
rubyのバージョンが2.0以上であることを確認します

[root@admin01 ~]# ruby -v
ruby 2.0.0p598 (2014-11-13) [x86_64-linux]


admin01のhostsにnode01のローカルIPを記載します

[root@admin01 ~]# vi /etc/hosts
(10.3.0.222  node01 を追記。IPアドレスはIDCFクラウドの管理画面より確認)
[root@admin01 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
10.3.0.222  node01


手順に従って、Itamae, Serverspecをインストールして準備します

[root@admin01 ~]# mkdir work
[root@admin01 ~]# cd work/
[root@admin01 work]# gem install bundler
Fetching: bundler-1.10.6.gem (100%)
Successfully installed bundler-1.10.6
Parsing documentation for bundler-1.10.6
Installing ri documentation for bundler-1.10.6
1 gem installed
[root@admin01 work]# vim Gemfile
[root@admin01 work]# cat Gemfile
# Gemfile
source "https://rubygems.org"

gem "rake"
gem "itamae"
gem "serverspec"
[root@admin01 work]# bundle install


レシピを書いてみます
レシピ配置用のディレクトリを作っておきます

[root@admin01 work]# mkdir recipes
[root@admin01 work]# mkdir recipes/mysqld
[root@admin01 work]# mkdir recipes/httpd


お手本通りにmysqldのレシピを作成します

[root@admin01 work]# vi recipes/mysqld/mysqld.rb
[root@admin01 work]# cat recipes/mysqld/mysqld.rb
# recipes/mysqld/mysqld.rb
MYSQL_PASSWORD=node[ENV['TARGET_HOST']]['mysqld']['password']

execute "yum -t -y install http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm"

%w{
  mysql-community-client
  mysql-community-devel
  mysql-community-server
}.each do |pkg_name|
  package pkg_name do
    action :install
  end
end

service "mysqld" do
  action [ :enable, :start ]
end

execute "mysql -u root -e \"SET PASSWORD=PASSWORD('#{MYSQL_PASSWORD}');\""
execute "mysql -uroot -p#{MYSQL_PASSWORD} -e \"DELETE FROM mysql.user WHERE User='';\""
execute "mysql -uroot -p#{MYSQL_PASSWORD} -e \"DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');\""
execute "mysql -uroot -p#{MYSQL_PASSWORD} -e \"DROP DATABASE test;\"; echo"
execute "mysql -uroot -p#{MYSQL_PASSWORD} -e \"DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'\""
execute "mysql -uroot -p#{MYSQL_PASSWORD} -e \"FLUSH PRIVILEGES;\""


続いて、httpdのレシピです

[root@admin01 work]# vi recipes/httpd/httpd.rb
[root@admin01 work]# cat recipes/httpd/httpd.rb
# recipes/httpd/httpd.rb
%w{
  httpd
  php
  php-gd
  php-mbstring
  php-xml
  php-pdo
  php-mysql
}.each do |pkg_name|
  package pkg_name do
    action :install
  end
end

template "/etc/php.ini" do
  source "php.ini.erb"
end

service "httpd" do
  action [ :enable, :start ]
end

execute "firewall-cmd --permanent --add-service=http"
execute "firewall-cmd --reload"
execute "setsebool -P httpd_can_network_connect 1"


php.iniはとりあえずローカルにあったものを持ってきてみました

[root@admin01 work]# cp /etc/php.ini ./recipes/httpd/php.ini.erb


手順に従ってServerspecのテストを持ってきます

[root@admin01 work]# mkdir -p spec/mysqld
[root@admin01 work]# cd spec/mysqld
[root@admin01 mysqld]# wget https://raw.githubusercontent.com/koemu/webapp_mook_servertest/master/spec/saito-hb-vm007/mysql_spec.rb
中略
2015-08-13 15:56:12 (209 MB/s) - `mysql_spec.rb' へ保存完了 [1093/1093]
[root@admin01 mysqld]# cd ../../
[root@admin01 work]# mkdir -p spec/httpd
[root@admin01 work]# cd spec/httpd
[root@admin01 httpd]# wget https://raw.githubusercontent.com/koemu/webapp_mook_servertest/master/spec/saito-hb-vm007/httpd_spec.rb
中略
2015-08-13 15:56:33 (341 MB/s) - `httpd_spec.rb' へ保存完了 [1777/1777]
[root@admin01 httpd]# cd ../../
[root@admin01 work]# cd spec
[root@admin01 spec]# wget https://raw.githubusercontent.com/koemu/webapp_mook_servertest/master/spec/spec_helper.rb
中略
2015-08-13 15:56:51 (136 MB/s) - `spec_helper.rb' へ保存完了 [679/679]
[root@admin01 spec]# cd ..


Rakefileを整えてみます

[root@admin01 work]# wget https://raw.githubusercontent.com/koemu/webapp_mook_itamae_demo/master/Rakefile
2015-08-13 15:59:16 (192 MB/s) - `Rakefile' へ保存完了 [1004/1004]


properties.jsonを書きます

[root@admin01 work]# vi properties.json
[root@admin01 work]# cat properties.json
{
  "node01": {
    "ssh_user": "root",
    "ssh_port": 22,
    "roles": ["mysqld", "httpd"]
  }
}


sshの設定をします

[root@admin01 work]# vi ~/.ssh/config
[root@admin01 work]# cat ~/.ssh/config
Host node01
  HostName node01
  User root
  IdentityFile ~/.ssh/hoge.key


確認をします

[root@admin01 work]# bundle exec rake -vT
rake aborted!
LoadError: cannot load such file -- json
/root/work/Rakefile:2:in `require'
/root/work/Rakefile:2:in `<top (required)>'
(See full trace by running task with --trace)
[root@admin01 work]#


あれ、怒られましたね
jsonを入れます

[root@admin01 work]# gem install json
Fetching: json-1.8.3.gem (100%)
Building native extensions.  This could take a while...
ERROR:  Error installing json:
        ERROR: Failed to build gem native extension.

    /usr/bin/ruby extconf.rb
mkmf.rb can't find header files for ruby at /usr/share/include/ruby.h


Gem files will remain installed in /usr/local/share/gems/gems/json-1.8.3 for inspection.
Results logged to /usr/local/share/gems/gems/json-1.8.3/ext/json/ext/generator/gem_make.out


さらに怒られました Gemfileでやってみよう

[root@admin01 work]# vi Gemfile
[root@admin01 work]# cat Gemfile
# Gemfile
source "https://rubygems.org"

gem "json"
gem "rake"
gem "itamae"
gem "serverspec"
[root@admin01 work]# bundle install

なんか入ったっぽい


再度確認します

[root@admin01 work]# bundle exec rake -vT
rake aborted!
LoadError: cannot load such file -- json/pure
/usr/share/gems/gems/json-1.7.7/lib/json.rb:60:in `require'
/usr/share/gems/gems/json-1.7.7/lib/json.rb:60:in `rescue in <module:JSON>'
/usr/share/gems/gems/json-1.7.7/lib/json.rb:57:in `<module:JSON>'
/usr/share/gems/gems/json-1.7.7/lib/json.rb:54:in `<top (required)>'
/root/work/Rakefile:2:in `require'
/root/work/Rakefile:2:in `<top (required)>'
(See full trace by running task with --trace)


ありゃ、今度はjson/pureがないそうです
Gemfileに書き足して再度bundle installします

[root@admin01 work]# vi Gemfile
[root@admin01 work]# cat Gemfile
# Gemfile
source "https://rubygems.org"

gem "json"
gem "json_pure"
gem "rake"
gem "itamae"
gem "serverspec"
[root@admin01 work]# bundle install

なんか入ったっぽい


再再度確認します

[root@admin01 work]# bundle exec rake -vT
rake itamae:node01      # Run itamae to node01
rake serverspec:node01  # Run serverspec to node01


OKでしたね
いよいよitamaeの実行とテストです

[root@admin01 work]# bundle exec rake itamae:node01 serverspec:node01
bundle exec itamae ssh -h node01 -u root -i  -p 22 -j properties.json recipes/mysqld/mysqld.rb recipes/httpd/httpd.rb
 INFO : Starting Itamae...
 INFO : Loading node data from /root/work/properties.json...
/usr/local/share/gems/gems/net-ssh-2.9.2/lib/net/ssh.rb:224:in `start': Authentication failed for user root@node01 (Net::SSH::AuthenticationFailed)


おっと 中略しますが鍵認証で接続できてませんでした
Rakefileをよく読むと鍵の指定をしているようなので、properties.jsonに鍵の指定を追記します

[root@admin01 work]# vi properties.json
[root@admin01 work]# cat properties.json
{
  "node01": {
    "ssh_user": "root",
    "ssh_port": 22,
    "private_key": "~/.ssh/hoge.key",
    "roles": ["mysqld", "httpd"]
  }
}


再度実行してみます

[root@admin01 work]# bundle exec rake itamae:node01 serverspec:node01
(中略)
/root/work/recipes/mysqld/mysqld.rb:2:in `load': undefined method `[]' for nil:NilClass (NoMethodError)


ということで、ちょっと謎のエラーが出て解決できないので、切り分けも含めてmysqldの実行とテストを後回しにします
properties.jsonからmysqldを削除

[root@admin01 work]# vi properties.json
[root@admin01 work]# cat properties.json
{
  "node01": {
    "ssh_user": "root",
    "ssh_port": 22,
    "private_key": "~/.ssh/hoge.key",
    "roles": ["httpd"]
  }
}


httpdのみの設定で、実行してみます

[root@admin01 work]# bundle exec rake itamae:node01 serverspec:node01
bundle exec itamae ssh -h node01 -u root -i ~/.ssh/hoge.key -p 22 -j properties.json recipes/httpd/httpd.rb
 INFO : Starting Itamae...
 INFO : Loading node data from /root/work/properties.json...
 INFO : Recipe: /root/work/recipes/httpd/httpd.rb
 INFO :   package[httpd] installed will change from 'false' to 'true'
 INFO :   package[php] installed will change from 'false' to 'true'
 INFO :   package[php-gd] installed will change from 'false' to 'true'
 INFO :   package[php-mbstring] installed will change from 'false' to 'true'
 INFO :   package[php-xml] installed will change from 'false' to 'true'
 INFO :   package[php-pdo] installed will change from 'false' to 'true'
 INFO :   package[php-mysql] installed will change from 'false' to 'true'
 INFO :   diff:
 INFO :   --- /etc/php.ini      2015-06-24 06:20:23.000000000 +0900
 INFO :   +++ /tmp/itamae_tmp/1439455347.5930629        2015-08-13 17:42:27.843020664 +0900
 INFO :   @@ -210,7 +210,7 @@
 INFO :    ; http://php.net/short-open-tag
 INFO :    short_open_tag = Off
 INFO :
 INFO :   -; Allow ASP-style <% %> tags.
 INFO :   +; Allow ASP-style  tags.
 INFO :    ; http://php.net/asp-tags
 INFO :    asp_tags = Off
 INFO :
 INFO :   service[httpd] enabled will change from 'false' to 'true'
 INFO :   service[httpd] running will change from 'false' to 'true'
 INFO :   execute[firewall-cmd --permanent --add-service=http] executed will change from 'false' to 'true'
ERROR :     stdout | [91mFirewallD is not running[00m
ERROR :     Command `firewall-cmd --permanent --add-service=http` failed. (exit status: 252)
ERROR :   execute[firewall-cmd --permanent --add-service=http] Failed.
/usr/bin/ruby -I/usr/local/share/gems/gems/rspec-core-3.3.2/lib:/usr/local/share/gems/gems/rspec-support-3.3.0/lib /usr/local/share/gems/gems/rspec-core-3.3.2/exe/rspec --pattern spec/\{httpd\}/\*_spec.rb
.......FF....

Failures:

  1) Apacheのインストール PHPのインストール php.iniの設定を変更している Php config "date.timezone" value should eq "Asia/Tokyo"
     Failure/Error: its(:value) { should eq "Asia/Tokyo" }

       expected: "Asia/Tokyo"
            got: ""

       (compared using ==)
     # ./spec/httpd/httpd_spec.rb:38:in `block (5 levels) in <top (required)>'

  2) Apacheのインストール PHPのインストール php.iniの設定を変更している Php config "mbstring.internal_encoding" value should eq "UTF-8"
     Failure/Error: its(:value) { should eq "UTF-8" }

       expected: "UTF-8"
            got: ""

       (compared using ==)
     # ./spec/httpd/httpd_spec.rb:42:in `block (5 levels) in <top (required)>'

Finished in 1.32 seconds (files took 0.26656 seconds to load)
13 examples, 2 failures

Failed examples:

rspec ./spec/httpd/httpd_spec.rb:38 # Apacheのインストール PHPのインストール php.iniの設定を変更している Php config "date.timezone" value should eq "Asia/Tokyo"
rspec ./spec/httpd/httpd_spec.rb:42 # Apacheのインストール PHPのインストール php.iniの設定を変更している Php config "mbstring.internal_encoding" value should eq "UTF-8"

/usr/bin/ruby -I/usr/local/share/gems/gems/rspec-core-3.3.2/lib:/usr/local/share/gems/gems/rspec-support-3.3.0/lib /usr/local/share/gems/gems/rspec-core-3.3.2/exe/rspec --pattern spec/\{httpd\}/\*_spec.rb failed


テストはちゃんと動いたようです。ということはmysqldのレシピに問題がありそうです。あとで見てみましょう。

ひとまず、httpdのテスト結果の方はどうかというと、ローカルに適当に置いてあったphp.iniなのでテストと差分があります。
php.iniを修正します

[root@admin01 work]# vi recipes/httpd/php.ini.erb
date.timezone = Asia/Tokyo
mbstring.internal_encoding = UTF-8


再度実行してみます

[root@admin01 work]# bundle exec rake itamae:node01 serverspec:node01
bundle exec itamae ssh -h node01 -u root -i ~/.ssh/hoge.key -p 22 -j properties.json recipes/httpd/httpd.rb
 INFO : Starting Itamae...
 INFO : Loading node data from /root/work/properties.json...
 INFO : Recipe: /root/work/recipes/httpd/httpd.rb
 INFO :   diff:
 INFO :   --- /etc/php.ini      2015-08-13 17:42:27.843020664 +0900
 INFO :   +++ /tmp/itamae_tmp/1439455835.036678 2015-08-13 17:50:35.053458574 +0900
 INFO :   @@ -875,7 +875,7 @@
 INFO :    [Date]
 INFO :    ; Defines the default timezone used by the date functions
 INFO :    ; http://php.net/date.timezone
 INFO :   -;date.timezone =
 INFO :   +date.timezone = Asia/Tokyo
 INFO :
 INFO :    ; http://php.net/date.default-latitude
 INFO :    ;date.default_latitude = 31.7667
 INFO :   @@ -1663,7 +1663,7 @@
 INFO :    ; Some encoding cannot work as internal encoding.
 INFO :    ; (e.g. SJIS, BIG5, ISO-2022-*)
 INFO :    ; http://php.net/mbstring.internal-encoding
 INFO :   -;mbstring.internal_encoding = EUC-JP
 INFO :   +mbstring.internal_encoding = UTF-8
 INFO :
 INFO :    ; http input encoding.
 INFO :    ; http://php.net/mbstring.http-input
 INFO :   execute[firewall-cmd --permanent --add-service=http] executed will change from 'false' to 'true'
ERROR :     stdout | [91mFirewallD is not running[00m
ERROR :     Command `firewall-cmd --permanent --add-service=http` failed. (exit status: 252)
ERROR :   execute[firewall-cmd --permanent --add-service=http] Failed.
/usr/bin/ruby -I/usr/local/share/gems/gems/rspec-core-3.3.2/lib:/usr/local/share/gems/gems/rspec-support-3.3.0/lib /usr/local/share/gems/gems/rspec-core-3.3.2/exe/rspec --pattern spec/\{httpd\}/\*_spec.rb
.............

Finished in 0.70656 seconds (files took 0.26856 seconds to load)
13 examples, 0 failures


php.iniの内容が書き換わり、テストを全部通過することが出来ました。これでhttpdについてはOKですね。

さてmysqldの方に戻ってみましょう レシピの設定で怪しかったところを変えます

[root@admin01 work]# vi /root/work/recipes/mysqld/mysqld.rb
MYSQL_PASSWORD="TESTPASSWORD"


mysqldのテストを行うように、properties.jsonを書き換えます

[root@admin01 work]# vi properties.json
[root@admin01 work]# cat properties.json
{
  "node01": {
    "ssh_user": "root",
    "ssh_port": 22,
    "private_key": "~/.ssh/hoge.key",
    "roles": ["mysqld"]
  }
}


mysqldの実行とテストと行います

[root@admin01 work]# bundle exec rake itamae:node01 serverspec:node01
bundle exec itamae ssh -h node01 -u root -i ~/.ssh/hoge.key -p 22 -j properties.json recipes/mysqld/mysqld.rb
 INFO : Starting Itamae...
 INFO : Loading node data from /root/work/properties.json...
 INFO : Recipe: /root/work/recipes/mysqld/mysqld.rb
 INFO :   execute[yum -t -y install http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm] executed will change from 'false' to 'true'
 INFO :   package[mysql-community-client] installed will change from 'false' to 'true'
 INFO :   package[mysql-community-devel] installed will change from 'false' to 'true'
 INFO :   package[mysql-community-server] installed will change from 'false' to 'true'
 INFO :   service[mysqld] running will change from 'false' to 'true'
 INFO :   execute[mysql -u root -e "SET PASSWORD=PASSWORD('TESTPASSWORD');"] executed will change from 'false' to 'true'
 INFO :   execute[mysql -uroot -pTESTWASSWORD -e "DELETE FROM mysql.user WHERE User='';"] executed will change from 'false' to 'true'
 INFO :   execute[mysql -uroot -pTESTWASSWORD -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');"] executed will change from 'false' to 'true'
 INFO :   execute[mysql -uroot -pTESTWASSWORD -e "DROP DATABASE test;"; echo] executed will change from 'false' to 'true'
 INFO :   execute[mysql -uroot -pTESTWASSWORD -e "DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%'"] executed will change from 'false' to 'true'
 INFO :   execute[mysql -uroot -pTESTWASSWORD -e "FLUSH PRIVILEGES;"] executed will change from 'false' to 'true'
/usr/bin/ruby -I/usr/local/share/gems/gems/rspec-core-3.3.2/lib:/usr/local/share/gems/gems/rspec-support-3.3.0/lib /usr/local/share/gems/gems/rspec-core-3.3.2/exe/rspec --pattern spec/\{mysqld\}/\*_spec.rb
.......F

Failures:

  1) MySQLのインストール MySQLにrootユーザでログインできる Command "mysql -uroot -phbadmin -e "SELECT user FROM mysql.user"" exit_status should eq 0
     Failure/Error: its(:exit_status) { should eq 0 }

       expected: 0
            got: 1

       (compared using ==)
     # ./spec/mysqld/mysql_spec.rb:40:in `block (4 levels) in <top (required)>'

Finished in 0.70786 seconds (files took 0.25649 seconds to load)
8 examples, 1 failure

Failed examples:

rspec ./spec/mysqld/mysql_spec.rb:40 # MySQLのインストール MySQLにrootユーザでログインできる Command "mysql -uroot -phbadmin -e "SELECT user FROM mysql.user"" exit_status should eq 0

/usr/bin/ruby -I/usr/local/share/gems/gems/rspec-core-3.3.2/lib:/usr/local/share/gems/gems/rspec-support-3.3.0/lib /usr/local/share/gems/gems/rspec-core-3.3.2/exe/rspec --pattern spec/\{mysqld\}/\*_spec.rb failed


一応ちゃんとテストまでできましたね。ということはnodeのあたりがうまく動いていなかったようですが、、、ruby全然わかりませんので誰か詳しい方教えてください><
さて今回のテストでFailedになった箇所を見てみますと、MySQLにrootでログインできなかった ということで、一番肝心なところがコケていたわけですが……。

テストの方を再度確認してみると

  describe "MySQLにrootユーザでログインできる" do
    describe command('mysql -uroot -phbadmin -e "SELECT user FROM mysql.user"') do
      its(:exit_status) { should eq 0 }
    end
  end


とのことで、パスワードが違いますね。
-phbadmin ってこれ、いろいろ大丈夫かしらw

パスワードを書き換えます

[root@admin01 work]# vi spec/mysqld/mysql_spec.rb
    describe command('mysql -uroot -pTESTPASSWORD -e "SELECT user FROM mysql.user"') do


再度実行。

[root@admin01 work]# bundle exec rake itamae:node01 serverspec:node01
bundle exec itamae ssh -h node01 -u root -i ~/.ssh/hoge.key -p 22 -j properties.json recipes/mysqld/mysqld.rb
 INFO : Starting Itamae...
 INFO : Loading node data from /root/work/properties.json...
 INFO : Recipe: /root/work/recipes/mysqld/mysqld.rb
 INFO :   execute[yum -t -y install http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm] executed will change from 'false' to 'true'
ERROR :     stdout | Loaded plugins: fastestmirror, remove-with-leaves, show-leaves
ERROR :     stdout | mysql-community-release-el7-5.noarch.rpm                 | 6.0 kB     00:00
ERROR :     stdout | Examining /var/tmp/yum-root-cdVUFD/mysql-community-release-el7-5.noarch.rpm: mysql-community-release-el7-5.noarch
ERROR :     stdout | /var/tmp/yum-root-cdVUFD/mysql-community-release-el7-5.noarch.rpm: does not update installed package.
ERROR :     stdout | Error: Nothing to do
ERROR :     Command `yum -t -y install http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm` failed. (exit status: 1)
ERROR :   execute[yum -t -y install http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm] Failed.
/usr/bin/ruby -I/usr/local/share/gems/gems/rspec-core-3.3.2/lib:/usr/local/share/gems/gems/rspec-support-3.3.0/lib /usr/local/share/gems/gems/rspec-core-3.3.2/exe/rspec --pattern spec/\{mysqld\}/\*_spec.rb
........

Finished in 0.36045 seconds (files took 0.27488 seconds to load)
8 examples, 0 failures


…無事通りました!この書き方ですと毎回executeでinstallしようとしてしまいますが、、、仕方ないですかね。
ともあれ、これでひとまず基礎的なところは追えたかと思います。
最近は仕事のほうでサーバーをちゃんと触る機会がなかなか少なくなって来てまして、久々に楽しかったです。rubyはわからんけど!w

余裕があれば後日、対象サーバーを増やしたりなどのバリエーションを試そうと思います。
僕のお財布にも余裕があるとたくさん増やせるのですが。。。まぁいいでしょうw