インフラCIの要
“Serverspec”の活用Tips紹介
TIS株式会社
OSS推進室
池田 大輔
2016.3.10
Agenda
Serverspecが必要とされる背景
Serverspecとは?
Serverspecのテスト実行の仕組み
Serverspecのテストコードの書き方
Serverspecの応用例
インフラCIの中でのServerspec
2
Hello!
I am Daisuke IKEDA
You can find me at @ike_dai
3
OSS利用時のサポートします
TISエンタープライズOSSサポートサービス
https://www.tis.jp/service_solution/oss/
保守サポート 技術コンサル マイグレーション
4
0.
Background
Serverspecが必要とされる背景
5
Change
6
Cloud
HW
OS
MW
APP
HW
OS
MW
APP
HV
VM
OS
MW
APP
VM
OS
MW
APP
VM
OS
MW
APP
VM
HW
OS
HW
HV
VM
Cloud
VM
Container
Container
Container
Container
Container
Container
物理 仮想化 クラウド化 マルチ環境化・コンテナ型仮想化
仮想化レイヤの追加
HW・HVレイヤの
管理がクラウドに隠蔽
複数環境の併用
プロセス空間を分離した
アプリケーション稼働基盤の実現
HW
HV
HW
HV
Infrastructure as Code
すべてのことはCodeで記述ができる
Codeで自動化・迅速対応が必須要件
7
Infrastructure as Code
すべてのことはCodeで記述ができる
Codeで自動化・迅速対応が必須要件
8
正確にこなすための Test
1.
What about?
Serverspecとは?
9
Concept
サーバ内の設定・稼働状況をRSpecライクにテスト
http://serverspec.org/
10
Serverspec? 1/2
宮下氏によって開発
Ruby製のサーバの状態のテストフレームワーク
Ansible,Chef,Itamae等で自動構築
本当に正しい?
構築自動化したのにテスト工程は手作業?
11
Bad...
12
$ ps
Excel..
$ rpm -qa
Excel..
$ ss
Excel..
$ ip addr
・・・
No. テスト項目 結果 担当者 日付
001 プロセスが起動していること OK 池田 2016.3.10
002 ポートがリッスンしていること OK 池田 2016.3.10
003
xxパッケージがインストールされている
こと
NG 池田 2016.3.10
Serverspec? 2/2
サーバの稼働状態をコードで記述
その通りに実際に稼働しているかサーバの中から確認
httpdが
 インストールされているべき
httpdが
 80/TCPでListenしているべき
httpdが
 稼働しているべき
package httpd
service httpd running
port 80 listening
13
2.
Architecture?
Serverspecのテスト実行の仕組み
14
Serverspec backend
Serverspec実行元にはRuby実行環境が必要
対象へのコマンド実行方法は様々サポート
Exec
localへのLinuxコマンド実行
SSH
SSH接続してコマンド実行
WinRM
WinRM接続してコマンド実行
Cmd
localへのWindowsコマンド実行
Docker
dockerコンテナに対してコマンド実行
Dockerfile
Dockerfileからbuildしてコマンド実行
15
Serverspec実行元 テスト対象
Ruby
gem
Serverspec
gem
Specinfra
テストコード
SSH backend module
SSH Server
SSH接続
OS上コマンド実行
戻り値を評価
16
Specinfra
net-ssh
winrm
docker-api
Serverspec component -gem package-
・メインパッケージ
・Resource TypeやMatcher
(後述)の処理が記述
Serverspec
・実際の実行コマンド
・OS毎に各コマンド処理内容を記述
・テスト対象への接続処理
・必要に応じて追加インストール必要
17
Serverspec component -file-
Rakefile
・テスト実行対象の定義と
 テストコードの紐付けを管理
spec_helper.rb
・バックエンドへの接続処理定義
*_spec.rb
・ テストコードの実体
$ tree serverspec-test/
serverspec-test/
├── Rakefile
└── spec
├── server-01
│ └── test_spec.rb
└── spec_helper.rb
18
Rakefile
require 'rake'
require 'rspec/core/rake_task'
task :spec => 'spec:all'
task :default => :spec
namespace :spec do
targets = []
Dir.glob('./spec/*').each do |dir|
next unless File.directory?(dir)
target = File.basename(dir)
target = "_#{target}" if target == "default"
targets << target
end
task :all => targets
task :default => :all
targets.each do |target|
original_target = target == "_default" ? target[1..-1] : target
desc "Run serverspec tests to #{original_target}"
RSpec::Core::RakeTask.new(target.to_sym) do |t|
ENV['TARGET_HOST'] = original_target
t.pattern = "spec/#{original_target}/*_spec.rb"
end
end
end
「spec/配下」の
ディレクトリ名を取得して
テスト対象ホストリストを作成
各テスト対象ホストに対して
spec/ホスト名/*_spec.rbのテストを実行
【テスト実行対象の定義】
【テストコードとの紐付け】
19
spec_helper.rb
require 'serverspec'
require 'net/ssh'
set :backend, :ssh
if ENV['ASK_SUDO_PASSWORD']
begin
require 'highline/import'
rescue LoadError
fail "highline is not available. Try installing it."
end
set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false }
else
set :sudo_password, ENV['SUDO_PASSWORD']
end
host = ENV['TARGET_HOST']
options = Net::SSH::Config.for(host)
options[:user] ||= Etc.getlogin
set :host, options[:host_name] || host
set :ssh_options, options
SSH接続処理に関する記述
backendとしてsshモジュールを利用
20
3.
How to use?
Serverspecのテストコードの書き方
21
Zabbix Server
MariaDB
Apache HTTP server
DBUser=zabbix
・・・
zabbix_server.conf
Example: Zabbix Server
zabbix-server
80/TCP
10051/TCP
3306/TCP
(localからのみ)
22
Test Code for Zabbix Server
require 'spec_helper'
packages = ['zabbix-release',
'zabbix-server-mysql',
'zabbix-web',
'zabbix-web-mysql',
'zabbix-web-japanese',
'httpd',
'mariadb-server']
packages.each do |package|
describe package(package) do
it { should be_installed }
end
end
services = ['zabbix-server',
'httpd',
'mariadb']
services.each do |service|
describe service(service) do
it { should be_running }
end
end
23
describe file('/etc/zabbix/zabbix_server.conf') do
its(:content) { should match /DBUser=zabbix/ }
end
ports = [80,10051]
ports.each do |port|
describe port(port) do
it { should be_listening }
end
end
describe port(3306) do
it { should be_listening.on('127.0.0.1').with('tcp')
}
end
Test Code for Zabbix Server
require 'spec_helper'
packages = ['zabbix-release',
'zabbix-server-mysql',
'zabbix-web',
'zabbix-web-mysql',
'zabbix-web-japanese',
'httpd',
'mariadb-server']
packages.each do |package|
describe package(package) do
it { should be_installed }
end
end
services = ['zabbix-server',
'httpd',
'mariadb']
services.each do |service|
describe service(service) do
it { should be_running }
end
end
24
describe file('/etc/zabbix/zabbix_server.conf') do
its(:content) { should match /DBUser=zabbix/ }
end
ports = [80,10051]
ports.each do |port|
describe port(port) do
it { should be_listening }
end
end
describe port(3306) do
it { should be_listening.on('127.0.0.1').with('tcp')
}
end
packageが
インストールされているか
Test Code for Zabbix Server
require 'spec_helper'
packages = ['zabbix-release',
'zabbix-server-mysql',
'zabbix-web',
'zabbix-web-mysql',
'zabbix-web-japanese',
'httpd',
'mariadb-server']
packages.each do |package|
describe package(package) do
it { should be_installed }
end
end
services = ['zabbix-server',
'httpd',
'mariadb']
services.each do |service|
describe service(service) do
it { should be_running }
end
end
25
describe file('/etc/zabbix/zabbix_server.conf') do
its(:content) { should match /DBUser=zabbix/ }
end
ports = [80,10051]
ports.each do |port|
describe port(port) do
it { should be_listening }
end
end
describe port(3306) do
it { should be_listening.on('127.0.0.1').with('tcp')
}
end
serviceが起動しているか
Test Code for Zabbix Server
require 'spec_helper'
packages = ['zabbix-release',
'zabbix-server-mysql',
'zabbix-web',
'zabbix-web-mysql',
'zabbix-web-japanese',
'httpd',
'mariadb-server']
packages.each do |package|
describe package(package) do
it { should be_installed }
end
end
services = ['zabbix-server',
'httpd',
'mariadb']
services.each do |service|
describe service(service) do
it { should be_running }
end
end
26
describe file('/etc/zabbix/zabbix_server.conf') do
its(:content) { should match /DBUser=zabbix/ }
end
ports = [80,10051]
ports.each do |port|
describe port(port) do
it { should be_listening }
end
end
describe port(3306) do
it { should be_listening.on('127.0.0.1').with('tcp')
}
end
fileに設定が
正しくされているか
Test Code for Zabbix Server
require 'spec_helper'
packages = ['zabbix-release',
'zabbix-server-mysql',
'zabbix-web',
'zabbix-web-mysql',
'zabbix-web-japanese',
'httpd',
'mariadb-server']
packages.each do |package|
describe package(package) do
it { should be_installed }
end
end
services = ['zabbix-server',
'httpd',
'mariadb']
services.each do |service|
describe service(service) do
it { should be_running }
end
end
27
describe file('/etc/zabbix/zabbix_server.conf') do
its(:content) { should match /DBUser=zabbix/ }
end
ports = [80,10051]
ports.each do |port|
describe port(port) do
it { should be_listening }
end
end
describe port(3306) do
it { should be_listening.on('127.0.0.1').with('tcp')
}
end
portが正しくリッスンしているか
Resource Type
テスト対象のリソース指定
file
port
package
command
service
など
その他、Resource Typeはこちら
 http://serverspec.org/resource_types.html
28
describe package(package) do
it { should be_installed }
end
Matcher
どうあるべきかの定義
file
be_file: 指定したパスがファイルであるべき
be_directory:指定したパスがディレクトリであるべき
content match: ファイルの中身に指定した文字列が含まれているべき
port
be_listening: 指定したポートがリッスン状態であるべき
service
be_enabled : 起動設定が有効になっているべき
be_running: 起動しているべき
package
be_installed: インストールされているべき
など
29
describe package(package) do
it { should be_installed }
end
Execution
$ rake spec
30
All Green
Package "zabbix-release"
should be installed
Package "zabbix-server-mysql"
should be installed
・・・略
File "/etc/zabbix/zabbix_server.conf"
content
should match /DBUser=zabbix/
Package "httpd"
should be installed
・・・略
Port "10051"
should be listening
Port "3306"
should be listening on 127.0.0.1 with tcp
Finished in 2.78 seconds (files took 0.76283 seconds to load)
14 examples, 0 failures
Red
Package "zabbix-release"
should be installed
Package "zabbix-server-mysql"
should be installed
・・・略
Port "3306"
should be listening on 127.0.0.1 with tcp (FAILED - 1)
Failures:
1) Port "3306" should be listening on 127.0.0.1 with tcp
On host `ikeda-3.0-test'
Failure/Error: it { should be_listening.on('127.0.0.1').with('tcp') }
expected Port "3306" to be listening on 127.0.0.1 with tcp
# ./spec/ikeda-3.0-test/sample_spec.rb:81:in `block (2 levels)
in <top (required)>'
Finished in 2.72 seconds (files took 0.67181 seconds to load)
14 examples, 1 failure
Scope
Serverspecはサーバの稼働状態を評価
 外から見た振る舞いは対象外      → Infratastor等
 サーバのリソース状況等の監視は対象外 → Zabbix等
31
【OSレイヤ(Linux)のテスト例】
  ・セキュリティ設定が正しく機能していること
  ・インタフェース設定が正しく行われていること
  ・DNSサーバとの名前解決が正常に機能すること
  ・NTP時刻同期設定が正常であること
  ・logrotateやcronの設定が正しく行われていること
                       等々
4.
Advanced
Serverspecの応用例
32
Test for Docker containers
Backend moduleはDocker対応
・Docker imageのテスト
・Docker containerのテスト
33
Docker Host
Docker
image
Serverspec実行元
Docker API経由
run
Docker
container
exec
Customize Rakefile
Rakefileをカスタマイズすることで、
どのホストに対してどのテストを実行するかを自由に制御
34
例) Zabbixに登録されているホスト情報を元にテスト実行対象を自動制御
  http://qiita.com/ike_dai/items/857d56311ca5c4fe10bb
RSpec formatter
ServerspecはRSpecに準拠
     =rspec formatterをそのまま活用可能
例:rspec_html_formatter
$ gem install rspec_html_formatter
$ vim ~/.rspec
--format html --out result.html
$ rake spec
35
5.
Apply to Infra CI
インフラCIの中のServerspec
36
CI with Travis CI
(Docker + Ansible + Serverspec)
Serverspec
test
Ansible
deploy
Docker build
& run
※テスト対象: Ansibleのコード
Travis CI
37
Sample: Docker + Ansible + Serverspec
https://github.com/ike-dai/infraci-test
38
Ansible
Travis CI
Docker container
docker
run
docker
exec
(デプロイ処理)
Serverspec
docker
exec
(テスト処理)
Travis CI result
39https://travis-ci.org/ike-dai/infraci-test
迅速かつ正確に回すためにはテストが重要
Serverspecはそんなシーンに柔軟に対応できる
テスティングフレームワーク
Serverspecはあくまで手段
何を実現したいのかを忘れずに
Conclusion
40
Thanks!
Any questions?
You can find me at @ike_dai
TIS OSS Promotion Office.
oss-sales@ml.tis.co.jp
41

Serverspecの活用tips紹介