Upload
daisuke-ikeda
View
15.364
Download
12
Embed Size (px)
DESCRIPTION
社内勉強会で発表した資料です。 serverspecの基本から属性値の管理、カスタマイズ方法まで紹介しています。 ※2013/8/14 version 0.7.8時点の情報なので注意
Citation preview
serverspecで
サーバ環境のテストを書いてみよう
TIS 池田 大輔( @ike_dai )
Agenda● 1. 基本の紹介
○ serverspecとは何か?
○ 導入方法の紹介
○ 設定方法の紹介
○ テストコードの書き方
● 2. 簡単なデモ
○ テスト実行
○ 結果の表示
● 3. 属性情報の扱い方
○ 属性情報を外出しして実行
● 4. カスタマイズしてみる
○ NTP時刻同期状況テストを作ってみる
serverspecの基本
● serverspecとは?
○ サーバの状態をテストするためのフレームワーク
○ Ruby実装 (8/14時点最新version 0.7.8 ※日々バージョンアップするので注意)
○ Rspecに準拠した形式で記述が可能→Rspecに慣れた方ならすぐに書けるかも
○ ChefやPuppet等環境の自動構成管理ツールと組み合わせて使うのに最適
○ serverspecは構成管理ツールに依存せずにテストコードが書ける
○ テスト実行方法は2種類
■ ローカルに対してテスト実行
■ SSH接続してテスト実行
○ テストコードを1箇所で集約管理
○ ただ単にSSH接続して実行するので特別なAgentの導入の必要がない
serverspecの基本
● どんな用途で使う?
○ 自動構築した結果、正しく稼働しているか確認
○ 内部から見てどう動いているのかを確認するため、外から見てどうかは
Zabbix等の監視ツールを利用
■ ポートがリッスンしているか?iptablesでそのポートを開放する設定が
されているか?はserverspecで確認
■ 本当にそのポートにアクセスできるか?アクセスが拒否されるか?は
監視ツールで確認
serverspecの導入方法
● gemコマンドで簡単にインストール可
● インストール後、テストコード初期設定実施
$ gem install serverspec
$ serverspec-initSelect a backend type:1) SSH2) Exec (local)Select number: 1
Vagrant instance y/n: nInput target host name: 192.168.xxx.xxx + spec/ + spec/192.168.xxx.xxx/ + spec/192.168.xxx.xxx/httpd_spec.rb + spec/spec_helper.rb + Rakefile
● 事前設定○ 1.SSH接続設定
○ 2.sudo設定
SSH接続設定●● 1. 公開鍵のキーペア作成 (NoPasswordで)● 2. テスト対象機器側の鍵認証設定● 3. serverspec実行機器側での .ssh/config設定
serverspecの設定方法
$ vim .ssh/configHost 192.168.xxx.xxx
HostName 192.168.xxx.xxxPort 22IdentityFile ~/.ssh/serverspec_testUser maintain
serverspec-init実行時に指定したホスト名と一致するように設定
sudo設定● SSHログイン後に実行されるテスト処理はsudoをつけて実行される● 接続ユーザに対してsudo権限を付与(実行されるサーバ側で設定)
● serverspec実行サーバ上の環境変数指定
● spec/spec_helper.rbを書き換えればパスワード認証のSSHログインも可能
serverspecの設定方法
# visudomaintain ALL=(ALL) NOPASSWD:ALL
$ export SUDO_PASSWORD=”xxxxxxxx”or$ export ASK_SUDO_PASSWORD=1
sudo実行時のパスワードを環境変数にセット
sudo実行時のパスワードを対話式入力有効化
● 重要なのはリソースタイプとマッチャー
● この組み合わせで様々なテストが記述できる
● リソースタイプ
○ command,cron,default_gateway,file,group,host,interface,ipfilter,ipnat,iptables,kernel_module,linux_kernel_parameter,package,php_config,port,routing_table,selinux,service,user,yumrepo,zfs
● マッチャー
○ 例:commandリソースタイプの場合
■ return_stdout:あるコマンド実行時の標準出力の文字列確認テスト
■ return_stderr:あるコマンド実行時の標準エラー出力の文字列確認テスト
テストコードの書き方
詳しくはこちら: http://serverspec.org/
● 例: 指定したパッケージがインストールされているかどうかのテスト
テストコードの基本
$ vim spec/httpd_spec.rb
require 'spec_helper'describe package('httpd') do it { should be_installed }end
リソースタイプを指定
マッチャーを指定してテスト実行
● ソースコードが非常に見やすい
1. どのリソースタイプか?
- インストールディレクトリ/serverspec-バージョン/lib/serverspec/type以下を確認
- 先述の例のテストの場合、package.rbを確認
2. どのマッチャーか?
- be_installedの場合、def installed?を確認
- この中に処理が記述
- def installed?の内部ではcheck_installedメソッドが呼ばれている
3. 実際の処理メソッドを確認
- check_installedメソッドを確認(commands/redhat.rb,debian.rb...)- OS毎にテスト実行コマンドが違う場合はOS毎に用意されているファイルに
- OS共通のテスト実行コマンドの場合はcommands/base.rbに
ソースコードの追い方
● テスト実行時に実際にはどんなコマンドが実行されているの?○ RedHat系の場合(lib/serverspec/commands/redhat.rb)
○ Debian系の場合(lib/serverspec/commands/debian.rb)
テスト実行時のコマンド
def check_installed(package,version=nil) cmd = "rpm -q #{escape(package)}" if ! version.nil? cmd = "#{cmd} | grep -w -- #{escape(version)}" end cmd end
def check_installed(package,version=nil) escaped_package = escape(package) "dpkg -s #{escaped_package} && ! dpkg -s #{escaped_package} | grep -E '^Status: .+ not-installed$'"end
● テスト実行時に実際にはどんなコマンドが実行されているの?○ RedHat系の場合(lib/serverspec/commands/redhat.rb)
○ Debian系の場合(lib/serverspec/commands/debian.rb)
テスト実行時のコマンド
def check_installed(package,version=nil) cmd = "rpm -q #{escape(package)}" if ! version.nil? cmd = "#{cmd} | grep -w -- #{escape(version)}" end cmd end
def check_installed(package,version=nil) escaped_package = escape(package) "dpkg -s #{escaped_package} && ! dpkg -s #{escaped_package} | grep -E '^Status: .+ not-installed$'"end
● 実行されるコマンドが何であるかを理解した上で書きましょう○ httpdパッケージのインストールバージョンをテストする場合の例
■ 極端な例ですが、以下2つはどちらもテストOKになる
テストコード作成時の注意点
実際にインストールされているパッケージ
$ rpm -q httpdhttpd-2.2.15-15.el6.centos.x86_64バージョン指定した場合に実行されるコマンド$ rpm -q httpd | grep -w -- 指定した文字列
describe package('httpd') do it { should be_installed.with_version('2.2') }end
describe package('httpd') do it { should be_installed.with_version('2.15') }end
● 実行方法
● ~/.rspecファイルを編集して表示を見やすく
● テスト失敗時、どういったコマンドが実行されのかが表示される
デモ
$ rake spec
--color--format documentation(またはs)
色付けをして表示
実行結果を文字列表記
Failures: 1) Port "80" Failure/Error: it { should be_listening } sudo netstat -tunl | grep -- :80\ expected Port "80" to be listening # ./spec/192.168.xxx.xxx/httpd_spec.rb:13:in `block (2 levels) in <top (required)>'
実際に実行されたコマンド
● テスト対象サーバ毎に変更する属性値を外だし管理可能1. 属性値情報をまとめたYAMLファイル作成
2. RakefileをYAMLファイルのキーの項目毎にテスト実行できるよう編集
3. spec/spec_helper.rbを編集し、属性値を変数に格納
4. テストコード内部で3.で格納した変数を利用するよう変更
設定情報を外だし
詳しくはこちら: http://mizzy.org/blog/2013/05/12/2/
● サーバ毎に異なるApacheのバージョンのテストを実施する場合の例
1. 属性値情報をまとめたYAMLファイル作成(attributes.yml)
設定情報を外だし
192.168.xxx.xxx: :apache_version: 2.2.15192.168.yyy.yyy: :apache_version: 2.4.4
キー
属性値
● サーバ毎に異なるApacheのバージョンのテストを実施する場合の例
2. RakefileをYAMLファイルのキーの項目毎にテスト実行できるよう編集
設定情報を外だし
require 'rake'require 'rspec/core/rake_task'require 'yaml'
attributes = YAML.load_file('attributes.yml')
desc "Run serverspec to all services"task :spec => 'spec:all'
namespace :spec do task :all => attributes.keys.map {|key| 'spec:' + key } attributes.keys.each do |key| desc "Run serverspec to #{key}" RSpec::Core::RakeTask.new(key.to_sym) do |t| ENV['TARGET_HOST'] = key t.pattern = "spec/#{key}/*_spec.rb" end end end
attrubutes.yml読込み
キー毎にspecを実行するよう変更
spec_helperで利用するため環境変数にキー情報を登録
● サーバ毎に異なるApacheのバージョンのテストを実施する場合の例
3. spec/spec_helper.rbを編集し、属性値を変数に格納
設定情報を外だし
require 'serverspec'require 'pathname'require 'net/ssh'require 'yaml'
include Serverspec::Helper::Sshinclude Serverspec::Helper::DetectOSinclude Serverspec::Helper::Attributes
attributes = YAML.load_file('attributes.yml')
RSpec.configure do |c| ・・・略 attr_set attributes[ENV['TARGET_HOST']]end
attr_setで指定したキーの属性値を変数に格納
● サーバ毎に異なるApacheのバージョンのテストを実施する場合の例
4. テストコード内部で3.で格納した変数を利用するよう変更
設定情報を外だし
describe package('httpd') do it { should be_installed.with_version(attr[:apache_version]) }end
attr[:属性名]という変数に値が格納
● 簡単にカスタマイズすることも可能○ commandリソースを使えば任意のコマンド実行テストが可能だが
○ よく使うものは新たにリソースタイプ、マッチャーを作成すれば使い回しできる
● 参考例:ntpの時刻同期状況テストを新たに追加してみる
○ 目指す形■ テスト対象サーバ内の指定したntpサーバのステータスをテスト
カスタマイズしてみる
describe ntp('xxx.xxx.xxx.xxx') do its(:status) { should eq '*' }end
● リソースタイプを追加○ lib/serverspec/helper/type.rbにリソースタイプを追加
○ 今回はntpというリソースタイプを新たに追加
カスタマイズしてみる
4: types = %w( 5: base yumrepo service package port file cron command linux_kernel_parameter iptables host6: routing_table default_gateway selinux user group zfs ipnat ipfilter kernel_module interface php_config ntp7: )
ここにタイプを追加することでserverspec/type/ntp.rbが読み込まれてリソースタイプが有効になる。
● ntpリソースタイプの定義○ lib/serverspec/type/ntp.rbに定義内容を記述
カスタマイズしてみる
module Serverspec module Type class Ntp< Base def status ret = backend.run_command(commands.get_ntp_status_of(@name)) val = ret[:stdout].strip val end end end end
status情報を取得するためのメソッドを定義
● 実行コマンド処理を記述
○ lib/serverspec/commands/linux.rbに実際の処理内容を記述
○ 各OSに依存する処理を書きたい場合はredhat.rbとかdebian.rbとかOS毎の
ファイルに処理を記載
カスタマイズしてみる
def get_ntp_status_of(name) "ntpq -p -n | grep #{name} | head -c1"end
実際に実行されるコマンド
$ sudo ntpq -p -n remote refid st t when poll reach delay offset jitter==================================================================*172.xx.xx.xx 172.xx.xxx.xx 6 u 248 1024 377 0.799 25.298 20.663
ここのステータス情報を取得
● serverspecはサーバ内部の状況が正しいかどうかをテストす
るフレームワーク● ChefやPuppet等自動構築ツールに依存せず手軽に扱える
● SSHログインしてテスト実行することが可能なので複数サーバ
のテストを統合管理可能● カスタマイズも容易にできるのでいろんな場面に適用も可
まとめ