Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

serverspecでサーバ環境のテストを書いてみよう

19,884 views

Published on

社内勉強会で発表した資料です。
serverspecの基本から属性値の管理、カスタマイズ方法まで紹介しています。
※2013/8/14 version 0.7.8時点の情報なので注意

Published in: Technology

serverspecでサーバ環境のテストを書いてみよう

  1. 1. serverspecで サーバ環境のテストを書いてみよう TIS 池田 大輔( @ike_dai )
  2. 2. Agenda ● 1. 基本の紹介 ○ serverspecとは何か? ○ 導入方法の紹介 ○ 設定方法の紹介 ○ テストコードの書き方 ● 2. 簡単なデモ ○ テスト実行 ○ 結果の表示 ● 3. 属性情報の扱い方 ○ 属性情報を外出しして実行 ● 4. カスタマイズしてみる ○ NTP時刻同期状況テストを作ってみる
  3. 3. serverspecの基本 ● serverspecとは? ○ サーバの状態をテストするためのフレームワーク ○ Ruby実装 (8/14時点最新version 0.7.8 ※日々バージョンアップするので注意) ○ Rspecに準拠した形式で記述が可能→Rspecに慣れた方ならすぐに書けるかも ○ ChefやPuppet等環境の自動構成管理ツールと組み合わせて使うのに最適 ○ serverspecは構成管理ツールに依存せずにテストコードが書ける ○ テスト実行方法は2種類 ■ ローカルに対してテスト実行 ■ SSH接続してテスト実行 ○ テストコードを1箇所で集約管理 ○ ただ単にSSH接続して実行するので特別なAgentの導入の必要がない
  4. 4. serverspecの基本 ● どんな用途で使う? ○ 自動構築した結果、正しく稼働しているか確認 ○ 内部から見てどう動いているのかを確認するため、外から見てどうかは Zabbix等の監視ツールを利用 ■ ポートがリッスンしているか?iptablesでそのポートを開放する設定が されているか?はserverspecで確認 ■ 本当にそのポートにアクセスできるか?アクセスが拒否されるか?は 監視ツールで確認
  5. 5. serverspecの導入方法 ● gemコマンドで簡単にインストール可 ● インストール後、テストコード初期設定実施 $ gem install serverspec $ serverspec-init Select a backend type: 1) SSH 2) Exec (local) Select number: 1 Vagrant instance y/n: n Input 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
  6. 6. ● 事前設定 ○ 1.SSH接続設定 ○ 2.sudo設定 SSH接続設定 ● ● 1. 公開鍵のキーペア作成 (NoPasswordで) ● 2. テスト対象機器側の鍵認証設定 ● 3. serverspec実行機器側での.ssh/config設定 serverspecの設定方法 $ vim .ssh/config Host 192.168.xxx.xxx HostName 192.168.xxx.xxx Port 22 IdentityFile ~/.ssh/serverspec_test User maintain serverspec-init実行時に指定したホスト名と 一致するように設定
  7. 7. sudo設定 ● SSHログイン後に実行されるテスト処理はsudoをつけて実行される ● 接続ユーザに対してsudo権限を付与(実行されるサーバ側で設定) ● serverspec実行サーバ上の環境変数指定 ● spec/spec_helper.rbを書き換えればパスワード認証のSSHログインも可能 serverspecの設定方法 # visudo maintain ALL=(ALL) NOPASSWD:ALL $ export SUDO_PASSWORD=”xxxxxxxx” or $ export ASK_SUDO_PASSWORD=1 sudo実行時のパスワードを環境変数にセット sudo実行時のパスワードを対話式入力有効化
  8. 8. ● 重要なのはリソースタイプとマッチャー ● この組み合わせで様々なテストが記述できる ● リソースタイプ ○ 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/
  9. 9. ● 例: 指定したパッケージがインストールされているかどうかのテスト テストコードの基本 $ vim spec/httpd_spec.rb require 'spec_helper' describe package('httpd') do it { should be_installed } end リソースタイプを指定 マッチャーを指定してテスト実行
  10. 10. ● ソースコードが非常に見やすい 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に ソースコードの追い方
  11. 11. ● テスト実行時に実際にはどんなコマンドが実行されているの? ○ 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
  12. 12. ● テスト実行時に実際にはどんなコマンドが実行されているの? ○ 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
  13. 13. ● 実行されるコマンドが何であるかを理解した上で書きましょう ○ httpdパッケージのインストールバージョンをテストする場合の例 ■ 極端な例ですが、以下2つはどちらもテストOKになる テストコード作成時の注意点 実際にインストールされているパッケージ $ rpm -q httpd httpd-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
  14. 14. ● 実行方法 ● ~/.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)>' 実際に実行されたコマンド
  15. 15. ● テスト対象サーバ毎に変更する属性値を外だし管理可能 1. 属性値情報をまとめたYAMLファイル作成 2. RakefileをYAMLファイルのキーの項目毎にテスト実行できるよう編集 3. spec/spec_helper.rbを編集し、属性値を変数に格納 4. テストコード内部で3.で格納した変数を利用するよう変更 設定情報を外だし 詳しくはこちら: http://mizzy.org/blog/2013/05/12/2/
  16. 16. ● サーバ毎に異なるApacheのバージョンのテストを実施する場合の例 1. 属性値情報をまとめたYAMLファイル作成(attributes.yml) 設定情報を外だし 192.168.xxx.xxx: :apache_version: 2.2.15 192.168.yyy.yyy: :apache_version: 2.4.4 キー 属性値
  17. 17. ● サーバ毎に異なる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で利用するため 環境変数にキー情報を登録
  18. 18. ● サーバ毎に異なるApacheのバージョンのテストを実施する場合の例 3. spec/spec_helper.rbを編集し、属性値を変数に格納 設定情報を外だし require 'serverspec' require 'pathname' require 'net/ssh' require 'yaml' include Serverspec::Helper::Ssh include Serverspec::Helper::DetectOS include Serverspec::Helper::Attributes attributes = YAML.load_file('attributes.yml') RSpec.configure do |c| ・・・略 attr_set attributes[ENV['TARGET_HOST']] end attr_setで指定した キーの属性値を変数に格納
  19. 19. ● サーバ毎に異なるApacheのバージョンのテストを実施する場合の例 4. テストコード内部で3.で格納した変数を利用するよう変更 設定情報を外だし describe package('httpd') do it { should be_installed.with_version(attr[:apache_version]) } end attr[:属性名]という変数に値が格納
  20. 20. ● 簡単にカスタマイズすることも可能 ○ commandリソースを使えば任意のコマンド実行テストが可能だが ○ よく使うものは新たにリソースタイプ、マッチャーを作成すれば使い回しできる ● 参考例:ntpの時刻同期状況テストを新たに追加してみる ○ 目指す形 ■ テスト対象サーバ内の指定したntpサーバのステータスをテスト カスタマイズしてみる describe ntp('xxx.xxx.xxx.xxx') do its(:status) { should eq '*' } end
  21. 21. ● リソースタイプを追加 ○ lib/serverspec/helper/type.rbにリソースタイプを追加 ○ 今回はntpというリソースタイプを新たに追加 カスタマイズしてみる 4: types = %w( 5: base yumrepo service package port file cron command linux_kernel_parameter iptables host 6: routing_table default_gateway selinux user group zfs ipnat ipfilter kernel_module interface php_config ntp 7: ) ここにタイプを追加することで serverspec/type/ntp.rbが読み込まれてリソースタ イプが有効になる。
  22. 22. ● 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情報を取得するためのメソッドを定義
  23. 23. ● 実行コマンド処理を記述 ○ 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 ここのステータス情報を取得
  24. 24. ● serverspecはサーバ内部の状況が正しいかどうかをテストす るフレームワーク ● ChefやPuppet等自動構築ツールに依存せず手軽に扱える ● SSHログインしてテスト実行することが可能なので複数サーバ のテストを統合管理可能 ● カスタマイズも容易にできるのでいろんな場面に適用も可 まとめ

×