1 - Serverspec
Serverspec是可以測試基礎設施配置的工具,能夠驗證配置管理工具(Ansible、Puppet、Chef等)的配置結果,可以實現基礎設施測試代碼化自動化。
- 測試代碼即測試設計文檔
- 測試代碼可以復用
- 可以通過代碼對測試用例進行評審
- 使用類似YAML的固定格式編寫測試用例
- 可以通過安裝gem軟件包coderay將測試結果輸出為HTML格式
- 可以在安裝了Serverspec的本地主機上進行測試,也可以通過ssh遠程進行測試
- 提供了配置文件和交互式兩種使用方式
官網信息
- HomePage:https://serverspec.org/
- Tutorial:https://serverspec.org/tutorial.html
- GitHub:https://github.com/mizzy/serverspec
- Version:https://rubygems.org/gems/serverspec/versions/
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格式