DevOps - 基礎設施配置測試工具Serverspec


1 - Serverspec

Serverspec是可以測試基礎設施配置的工具,能夠驗證配置管理工具(Ansible、Puppet、Chef等)的配置結果,可以實現基礎設施測試代碼化自動化。

  • 測試代碼即測試設計文檔
  • 測試代碼可以復用
  • 可以通過代碼對測試用例進行評審
  • 使用類似YAML的固定格式編寫測試用例
  • 可以通過安裝gem軟件包coderay將測試結果輸出為HTML格式
  • 可以在安裝了Serverspec的本地主機上進行測試,也可以通過ssh遠程進行測試
  • 提供了配置文件和交互式兩種使用方式

官網信息

2 - 安裝

注意:必須事先安裝有ruby,並且版本不能過低,負責安裝會失敗。

[root@localhost ~]# gem install serverspec
Fetching rspec-support-3.9.0.gem
Fetching diff-lcs-1.3.gem
Fetching sfl-2.3.gem
Fetching multi_json-1.14.1.gem
Fetching rspec-expectations-3.9.0.gem
Fetching specinfra-2.82.4.gem
Fetching net-ssh-5.2.0.gem
Fetching net-scp-2.0.0.gem
Fetching rspec-core-3.9.0.gem
Fetching rspec-its-1.3.0.gem
Fetching rspec-mocks-3.9.0.gem
Fetching rspec-3.9.0.gem
Fetching serverspec-2.41.5.gem
Successfully installed sfl-2.3
Successfully installed net-ssh-5.2.0
Successfully installed net-scp-2.0.0
Successfully installed specinfra-2.82.4
Successfully installed multi_json-1.14.1
Successfully installed diff-lcs-1.3
Successfully installed rspec-support-3.9.0
Successfully installed rspec-expectations-3.9.0
Successfully installed rspec-core-3.9.0
Successfully installed rspec-its-1.3.0
Successfully installed rspec-mocks-3.9.0
Successfully installed rspec-3.9.0
Successfully installed serverspec-2.41.5
Parsing documentation for sfl-2.3
Installing ri documentation for sfl-2.3
Parsing documentation for net-ssh-5.2.0
Installing ri documentation for net-ssh-5.2.0
Parsing documentation for net-scp-2.0.0
Installing ri documentation for net-scp-2.0.0
Parsing documentation for specinfra-2.82.4
Installing ri documentation for specinfra-2.82.4
Parsing documentation for multi_json-1.14.1
Installing ri documentation for multi_json-1.14.1
Parsing documentation for diff-lcs-1.3
Couldn't find file to include 'Contributing.rdoc' from README.rdoc
Couldn't find file to include 'License.rdoc' from README.rdoc
Installing ri documentation for diff-lcs-1.3
Parsing documentation for rspec-support-3.9.0
Installing ri documentation for rspec-support-3.9.0
Parsing documentation for rspec-expectations-3.9.0
Installing ri documentation for rspec-expectations-3.9.0
Parsing documentation for rspec-core-3.9.0
Installing ri documentation for rspec-core-3.9.0
Parsing documentation for rspec-its-1.3.0
Installing ri documentation for rspec-its-1.3.0
Parsing documentation for rspec-mocks-3.9.0
Installing ri documentation for rspec-mocks-3.9.0
Parsing documentation for rspec-3.9.0
Installing ri documentation for rspec-3.9.0
Parsing documentation for serverspec-2.41.5
Installing ri documentation for serverspec-2.41.5
Done installing documentation for sfl, net-ssh, net-scp, specinfra, multi_json, diff-lcs, rspec-support, rspec-expectations, rspec-core, rspec-its, rspec-mocks, rspec, serverspec after 15 seconds
13 gems installed
[root@localhost ~]#

3 - Serverspec設置

在CentOS系統下以本地主機模式運行,測試內容保存在spec/localhost/*_spec.rb文件中。
參考示例文件sample_spec.rb可以編寫指定的測試內容。

[root@localhost ~]# pwd
/root
[root@localhost ~]# mkdir serverspec
[root@localhost ~]# cd serverspec
[root@localhost serverspec]# pwd
/root/serverspec
[root@localhost serverspec]# 
[root@localhost serverspec]# serverspec-init
Select OS type:
​
  1) UN*X
  2) Windows
​
Select number: 1
​
Select a backend type:
​
  1) SSH
  2) Exec (local)
​
Select number: 2
​
 + spec/
 + spec/localhost/
 + spec/localhost/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec
[root@localhost serverspec]# ll
total 4
-rw-rw-r-- 1 root root 685 Nov 20 09:48 Rakefile
drwxrwxr-x 3 root root  45 Nov 20 09:48 spec
[root@localhost serverspec]# tree
.
├── Rakefile
└── spec
    ├── localhost
    │   └── sample_spec.rb
    └── spec_helper.rb
​
2 directories, 3 files
[root@localhost serverspec]# 
[root@localhost serverspec]# cat spec/localhost/sample_spec.rb 
require 'spec_helper'
​
describe package('httpd'), :if => os[:family] == 'redhat' do
  it { should be_installed }
end
​
describe package('apache2'), :if => os[:family] == 'ubuntu' do
  it { should be_installed }
end
​
describe service('httpd'), :if => os[:family] == 'redhat' do
  it { should be_enabled }
  it { should be_running }
end
​
describe service('apache2'), :if => os[:family] == 'ubuntu' do
  it { should be_enabled }
  it { should be_running }
end
​
describe service('org.apache.httpd'), :if => os[:family] == 'darwin' do
  it { should be_enabled }
  it { should be_running }
end
​
describe port(80) do
  it { should be_listening }
end
[root@localhost serverspec]#

4 - 使用示例

4.1 示例1 - 使用rake命令執行測試

注意:必須在Rakefile文件所在目錄執行rake命令

[root@localhost serverspec]# pwd
/root/serverspec
[root@localhost serverspec]# ll
total 4
-rw-rw-r-- 1 root root 685 Nov 20 09:48 Rakefile
drwxrwxr-x 3 root root  45 Nov 20 09:48 spec
[root@localhost serverspec]# 
[root@localhost serverspec]# rake spec
/usr/local/rvm/rubies/ruby-2.5.5/bin/ruby -I/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-support-3.9.0/lib:/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/lib /usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/exe/rspec --pattern spec/localhost/\*_spec.rb
​
Package "httpd"
  is expected to be installed
​
Service "httpd"
  is expected to be enabled (FAILED - 1)
  is expected to be running (FAILED - 2)
​
Port "80"
  is expected to be listening
​
Failures:
​
  1) Service "httpd" is expected to be enabled
     On host `localhost'
     Failure/Error: it { should be_enabled }
       expected Service "httpd" to be enabled
       /bin/sh -c systemctl\ --quiet\ is-enabled\ httpd
       
     # ./spec/localhost/sample_spec.rb:12:in `block (2 levels) in <top (required)>'
​
  2) Service "httpd" is expected to be running
     On host `localhost'
     Failure/Error: it { should be_running }
       expected Service "httpd" to be running
       /bin/sh -c systemctl\ is-active\ httpd
       unknown
​
     # ./spec/localhost/sample_spec.rb:13:in `block (2 levels) in <top (required)>'
​
Finished in 0.14409 seconds (files took 0.65376 seconds to load)
4 examples, 2 failures
​
Failed examples:
​
rspec ./spec/localhost/sample_spec.rb:12 # Service "httpd" is expected to be enabled
rspec ./spec/localhost/sample_spec.rb:13 # Service "httpd" is expected to be running
​
/usr/local/rvm/rubies/ruby-2.5.5/bin/ruby -I/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-support-3.9.0/lib:/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/lib /usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/exe/rspec --pattern spec/localhost/\*_spec.rb failed
[root@localhost serverspec]#

4.2 示例2 - 為基礎設施編寫測試代碼

以Nginx為例。

[root@localhost ansible-playbook-sample]# pwd
/root/zzz/ansible-playbook-sample
[root@localhost ansible-playbook-sample]# 
[root@localhost ansible-playbook-sample]# cat site.yml 
---
- hosts: webservers
  become: yes
  connection: local
  roles:
    - common
    - nginx
    - serverspec
    - serverspec_sample
#    - jenkins
[root@localhost ansible-playbook-sample]# 
[root@localhost ansible-playbook-sample]# tree roles/serverspec_sample/
roles/serverspec_sample/
├── files
│   └── serverspec_sample
│       ├── Rakefile
│       └── spec
│           ├── localhost
│           └── spec_helper.rb
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
├── templates
│   ├── nginx_spec.rb.j2
│   └── web_spec.rb.j2
└── vars
    └── main.yml
​
8 directories, 7 files
[root@localhost ansible-playbook-sample]# 
[root@localhost ansible-playbook-sample]# ansible-playbook -i development site.yml 
[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names by default, this will change, but still be user configurable on deprecation. This feature will be removed in version 2.10. 
Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
​
​
PLAY [webservers] **************************************************************************************************************************************************************************************************************************
​
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************************
ok: [localhost]
​
TASK [common : install epel] ***************************************************************************************************************************************************************************************************************
ok: [localhost]
​
TASK [nginx : install nginx] ***************************************************************************************************************************************************************************************************************
ok: [localhost]
​
TASK [nginx : replace index.html] **********************************************************************************************************************************************************************************************************
ok: [localhost]
​
TASK [nginx : nginx start] *****************************************************************************************************************************************************************************************************************
ok: [localhost]
​
TASK [serverspec : install ruby] ***********************************************************************************************************************************************************************************************************
ok: [localhost]
​
TASK [serverspec : install serverspec] *****************************************************************************************************************************************************************************************************
ok: [localhost] => (item=rake)
ok: [localhost] => (item=serverspec)
​
TASK [serverspec_sample : distribute serverspec suite] *************************************************************************************************************************************************************************************
changed: [localhost]
​
TASK [serverspec_sample : distribute spec file] ********************************************************************************************************************************************************************************************
changed: [localhost]
​
PLAY RECAP *********************************************************************************************************************************************************************************************************************************
localhost                  : ok=9    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
​
[root@localhost ansible-playbook-sample]# 
[root@localhost ansible-playbook-sample]# ll /tmp/serverspec_sample/
total 4
-rw-rw-r-- 1 root root 685 Nov 20 13:54 Rakefile
drwxrwxr-x 3 root root  45 Nov 20 13:54 spec
[root@localhost ansible-playbook-sample]# 
[root@localhost ansible-playbook-sample]# tree /tmp/serverspec_sample/
/tmp/serverspec_sample/
├── Rakefile
└── spec
    ├── localhost
    │   └── web_spec.rb
    └── spec_helper.rb
​
2 directories, 3 files
[root@localhost ansible-playbook-sample]# cd /tmp/serverspec_sample/
[root@localhost serverspec_sample]# cat spec/localhost/web_spec.rb 
require 'spec_helper'
​
describe package('nginx') do
  it { should be_installed }
end
​
describe service('nginx') do
  it { should be_enabled }
  it { should be_running }
end
​
describe port(80) do
  it { should be_listening }
end
​
describe file('/usr/share/nginx/html/index.html') do
  it { should be_file }
  it { should exist }
  its(:content) { should match /^Hello, development ansible!!$/ }
end
[root@localhost serverspec_sample]# 
[root@localhost serverspec_sample]# rake spec
/usr/local/rvm/rubies/ruby-2.5.5/bin/ruby -I/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-support-3.9.0/lib:/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/lib /usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/exe/rspec --pattern spec/localhost/\*_spec.rb
​
Package "nginx"
  is expected to be installed
​
Service "nginx"
  is expected to be enabled
  is expected to be running
​
Port "80"
  is expected to be listening
​
File "/usr/share/nginx/html/index.html"
  is expected to be file
  is expected to exist
  content
    is expected to match /^Hello, development ansible!!$/ (FAILED - 1)
​
Failures:
​
  1) File "/usr/share/nginx/html/index.html" content is expected to match /^Hello, development ansible!!$/
     On host `localhost'
     Failure/Error: its(:content) { should match /^Hello, development ansible!!$/ }
       expected "hello, development ansible\n" to match /^Hello, development ansible!!$/
       Diff:
       @@ -1,2 +1,2 @@
       -/^Hello, development ansible!!$/
       +hello, development ansible
       
       /bin/sh -c cat\ /usr/share/nginx/html/index.html\ 2\>\ /dev/null\ \|\|\ echo\ -n
       hello, development ansible
​
     # ./spec/localhost/web_spec.rb:19:in `block (2 levels) in <top (required)>'
​
Finished in 0.23396 seconds (files took 0.64631 seconds to load)
7 examples, 1 failure
​
Failed examples:
​
rspec ./spec/localhost/web_spec.rb:19 # File "/usr/share/nginx/html/index.html" content is expected to match /^Hello, development ansible!!$/
​
/usr/local/rvm/rubies/ruby-2.5.5/bin/ruby -I/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-support-3.9.0/lib:/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/lib /usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/exe/rspec --pattern spec/localhost/\*_spec.rb failed
[root@localhost serverspec_sample]#

發現有一個失敗的測試用例,根據報錯信息分析並更改,然后重新執行。

[root@localhost templates]# pwd
/root/zzz/ansible-playbook-sample/roles/nginx/templates
[root@localhost templates]# 
[root@localhost templates]# ll
total 4
-rw-r--r-- 1 root root 25 Nov 19 17:25 index.html.j2
[root@localhost templates]# cat index.html.j2 
hello, {{ env }} ansible
[root@localhost templates]# 
[root@localhost templates]# vim index.html.j2 
[root@localhost templates]# cat index.html.j2 
Hello, {{ env }} ansible!!
[root@localhost templates]# 
[root@localhost serverspec_sample]# cd  ~/zzz/ansible-playbook-sample/
[root@localhost ansible-playbook-sample]# ansible-playbook -i development site.yml
[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names by default, this will change, but still be user configurable on deprecation. This feature will be removed in version 2.10. 
Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
​
​
PLAY [webservers] **************************************************************************************************************************************************************************************************************************
​
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************************
ok: [localhost]
​
TASK [common : install epel] ***************************************************************************************************************************************************************************************************************
ok: [localhost]
​
TASK [nginx : install nginx] ***************************************************************************************************************************************************************************************************************
ok: [localhost]
​
TASK [nginx : replace index.html] **********************************************************************************************************************************************************************************************************
changed: [localhost]
​
TASK [nginx : nginx start] *****************************************************************************************************************************************************************************************************************
ok: [localhost]
​
TASK [serverspec : install ruby] ***********************************************************************************************************************************************************************************************************
ok: [localhost]
​
TASK [serverspec : install serverspec] *****************************************************************************************************************************************************************************************************
ok: [localhost] => (item=rake)
ok: [localhost] => (item=serverspec)
​
TASK [serverspec_sample : distribute serverspec suite] *************************************************************************************************************************************************************************************
ok: [localhost]
​
TASK [serverspec_sample : distribute spec file] ********************************************************************************************************************************************************************************************
ok: [localhost]
​
PLAY RECAP *********************************************************************************************************************************************************************************************************************************
localhost                  : ok=9    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
​
[root@localhost ansible-playbook-sample]# 
[root@localhost ansible-playbook-sample]# cd /tmp/serverspec_sample/
[root@localhost serverspec_sample]# rake spec
/usr/local/rvm/rubies/ruby-2.5.5/bin/ruby -I/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-support-3.9.0/lib:/usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/lib /usr/local/rvm/gems/ruby-2.5.5/gems/rspec-core-3.9.0/exe/rspec --pattern spec/localhost/\*_spec.rb
​
Package "nginx"
  is expected to be installed
​
Service "nginx"
  is expected to be enabled
  is expected to be running
​
Port "80"
  is expected to be listening
​
File "/usr/share/nginx/html/index.html"
  is expected to be file
  is expected to exist
  content
    is expected to match /^Hello, development ansible!!$/
​
Finished in 0.19861 seconds (files took 0.61345 seconds to load)
7 examples, 0 failures
​
[root@localhost serverspec_sample]#

5 - 問題處理

通過gem安裝serverspec時,ruby版本過低導致安裝失敗,版本必須大於等於2.2.6。

# gem install serverspec
Fetching: rspec-support-3.9.0.gem (100%)
Successfully installed rspec-support-3.9.0
......
......
......
ERROR:  Error installing serverspec:
    net-ssh requires Ruby version >= 2.2.6.
#    
# ruby -v
ruby 2.0.0p648 (2015-12-16) [x86_64-linux]
#

處理方法

通過yum方式在線安裝ruby,其版本仍然為2.0.0。
只能通過更換ruby倉庫和使用RAM的方式來完成版本升級。
參考信息:https://www.jianshu.com/p/7a625eb8cde0

[root@localhost ~]# gem sources -a http://mirrors.aliyun.com/rubygems/
http://mirrors.aliyun.com/rubygems/ added to sources
[root@localhost ~]# 
[root@localhost ~]# gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3  7D2BAF1CF37B13E2069D6956105BD0E739499BDB
gpg: requesting key D39DC0E3 from hkp server keys.gnupg.net
gpg: requesting key 39499BDB from hkp server keys.gnupg.net
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key D39DC0E3: public key "Michal Papis (RVM signing) <mpapis@gmail.com>" imported
gpg: key 39499BDB: public key "Piotr Kuczynski <piotr.kuczynski@gmail.com>" imported
gpg: no ultimately trusted keys found
gpg: Total number processed: 2
gpg:               imported: 2  (RSA: 2)
[root@localhost ~]# 
[root@localhost ~]# curl -sSL https://get.rvm.io | bash -s stable
Downloading https://github.com/rvm/rvm/archive/1.29.9.tar.gz
Downloading https://github.com/rvm/rvm/releases/download/1.29.9/1.29.9.tar.gz.asc
gpg: Signature made Wed 10 Jul 2019 04:31:02 PM CST using RSA key ID 39499BDB
gpg: Good signature from "Piotr Kuczynski <piotr.kuczynski@gmail.com>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 7D2B AF1C F37B 13E2 069D  6956 105B D0E7 3949 9BDB
GPG verified '/usr/local/rvm/archives/rvm-1.29.9.tgz'
Creating group 'rvm'
Installing RVM to /usr/local/rvm/
Installation of RVM in /usr/local/rvm/ is almost complete:
​
  * First you need to add all users that will be using rvm to 'rvm' group,
    and logout - login again, anyone using rvm will be operating with `umask u=rwx,g=rwx,o=rx`.
​
  * To start using RVM you need to run `source /etc/profile.d/rvm.sh`
    in all your open shell windows, in rare cases you need to reopen all shell windows.
  * Please do NOT forget to add your users to the rvm group.
     The installer no longer auto-adds root or users to the rvm group. Admins must do this.
     Also, please note that group memberships are ONLY evaluated at login time.
     This means that users must log out then back in before group membership takes effect!
Thanks for installing RVM 🙏
Please consider donating to our open collective to help us maintain RVM.
​
👉  Donate: https://opencollective.com/rvm/donate
​
​
[root@localhost ~]# 
[root@localhost ~]# source /etc/profile.d/rvm.sh
[root@localhost ~]# 
[root@localhost ~]# rvm -v
rvm 1.29.9 (latest) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [https://rvm.io]
[root@localhost ~]# 
[root@localhost ~]# rvm install 2.5
Searching for binary rubies, this might take some time.
No binary rubies available for: centos/7/x86_64/ruby-2.5.5.
Continuing with compilation. Please read 'rvm help mount' to get more information on binary rubies.
Checking requirements for centos.
Installing requirements for centos.
Installing required packages: patch, autoconf, automake, bison, gcc-c++, libffi-devel, libtool, patch, readline-devel, sqlite-devel, zlib-devel, openssl-devel...............................
Requirements installation successful.
Installing Ruby from source to: /usr/local/rvm/rubies/ruby-2.5.5, this may take a while depending on your cpu(s)...
ruby-2.5.5 - #downloading ruby-2.5.5, this may take a while depending on your connection...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 13.5M  100 13.5M    0     0   383k      0  0:00:36  0:00:36 --:--:--  356k
ruby-2.5.5 - #extracting ruby-2.5.5 to /usr/local/rvm/src/ruby-2.5.5.....
ruby-2.5.5 - #configuring...................................................................
ruby-2.5.5 - #post-configuration..
ruby-2.5.5 - #compiling....................................................................................
ruby-2.5.5 - #installing..............................
ruby-2.5.5 - #making binaries executable..
ruby-2.5.5 - #downloading rubygems-3.0.6
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  866k  100  866k    0     0  21612      0  0:00:41  0:00:41 --:--:-- 57928
No checksum for downloaded archive, recording checksum in user configuration.
ruby-2.5.5 - #extracting rubygems-3.0.6.....
ruby-2.5.5 - #removing old rubygems........
ruby-2.5.5 - #installing rubygems-3.0.6...............................................
ruby-2.5.5 - #gemset created /usr/local/rvm/gems/ruby-2.5.5@global
ruby-2.5.5 - #importing gemset /usr/local/rvm/gemsets/global.gems................................................................
ruby-2.5.5 - #generating global wrappers.......
ruby-2.5.5 - #gemset created /usr/local/rvm/gems/ruby-2.5.5
ruby-2.5.5 - #importing gemsetfile /usr/local/rvm/gemsets/default.gems evaluated to empty gem list
ruby-2.5.5 - #generating default wrappers.......
ruby-2.5.5 - #adjusting #shebangs for (gem irb erb ri rdoc testrb rake).
Install of ruby-2.5.5 - #complete 
Ruby was built without documentation, to build it run: rvm docs generate-ri
[root@localhost ~]# 
[root@localhost ~]# ruby -v
ruby 2.5.5p157 (2019-03-15 revision 67260) [x86_64-linux]
[root@localhost ~]# 
[root@localhost ~]# gem sources --remove https://rubygems.org/
https://rubygems.org/ removed from sources
[root@localhost ~]#

6 - HTML格式

可以通過安裝gem軟件包coderay將測試結果輸出為HTML格式


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM