Serverspec 개요
Serverspec은 인프라스트럭처의 실제 상태가 의도한 구성과 일치하는지 검증하는 Ruby 기반 테스팅 프레임워크다. Ansible, Puppet, Chef 등의 구성 관리 도구가 적용된 결과를 코드로 검증하여 인프라 테스트를 자동화한다.
주요 특징
- 테스트 코드 자체가 문서 역할 수행
- 재사용 가능한 검증 스크립트 작성
- 코드 리뷰를 통한 테스트 케이스 검증
- 직관적인 DSL(Domain Specific Language) 제공
- coderay 젬 설치 시 HTML 리포트 생성 가능
- 로컬 실행 및 SSH 원격 실행 모두 지원
- 설정 파일 기반 및 대화형 초기화 방식 제공
공식 자료
- 공식 사이트: https://serverspec.org/
- 학습 자료: https://serverspec.org/tutorial.html
- 소스 저장소: https://github.com/mizzy/serverspec
- 젬 버전: https://rubygems.org/gems/serverspec/versions/
설치 과정
Ruby 2.2.6 이상 버전이 사전에 설치되어 있어야 한다. 버전이 낮으면 의존성 해결에 실패한다.
$ 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
프로젝트 초기화
CentOS 환경에서 로컬 모드로 실행할 경우, 검증 스크립트는 spec/localhost/*_spec.rb 경로에 위치한다. serverspec-init 명령으로 생성되는 샘플 파일을 참고하여 테스트를 작성한다.
$ mkdir ~/infra-test && cd ~/infra-test
$ 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
생성된 디렉터리 구조:
.
├── Rakefile
└── spec
├── localhost
│ └── sample_spec.rb
└── spec_helper.rb
2 directories, 3 files
기본 샘플 테스트 내용:
$ 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
실행 예시
Rake를 통한 테스트 실행
Rakefile이 있는 디렉터리에서 명령을 실행해야 한다.
$ 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
Nginx 환경 검증 예시
Ansible 연동을 통한 인프라 구성 및 검증 자동화 사례다.
$ cat site.yml
---
- hosts: webservers
become: yes
connection: local
roles:
- common
- nginx
- serverspec
- 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
Ansible 실행 및 검증 파일 배포:
$ ansible-playbook -i development site.yml
...
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
배포된 검증 스크립트:
$ tree /tmp/serverspec_sample/
/tmp/serverspec_sample/
├── Rakefile
└── spec
├── localhost
│ └── web_spec.rb
└── spec_helper.rb
2 directories, 3 files
$ cat /tmp/serverspec_sample/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
첫 실행 결과 - 콘텐츠 불일치 오류:
$ cd /tmp/serverspec_sample && rake spec
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
Finished in 0.23396 seconds (files took 0.64631 seconds to load)
7 examples, 1 failure
템플릿 수정 후 재배포:
$ cat roles/nginx/templates/index.html.j2
Hello, {{ env }} ansible!!
$ ansible-playbook -i development site.yml
...
TASK [nginx : replace index.html] ********************************************
changed: [localhost]
PLAY RECAP *******************************************************************
localhost : ok=9 changed=1 unreachable=0 failed=0
최종 검증 성공:
$ cd /tmp/serverspec_sample && rake spec
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
문제 해결
Ruby 버전 오류
gem 설치 시 Ruby 2.2.6 미만 버전에서는 다음 오류가 발생한다:
$ gem install serverspec
...
ERROR: Error installing serverspec:
net-ssh requires Ruby version >= 2.2.6.
$ ruby -v
ruby 2.0.0p648 (2015-12-16) [x86_64-linux]
해결 방안: RVM을 통한 Ruby 업그레이드
yum으로는 최신 Ruby를 설치할 수 없으므로 RVM(Ruby Version Manager)을 사용한다.
$ gem sources -a http://mirrors.aliyun.com/rubygems/
$ gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
$ curl -sSL https://get.rvm.io | bash -s stable
$ source /etc/profile.d/rvm.sh
$ rvm install 2.5
설치 확인:
$ ruby -v
ruby 2.5.5p157 (2019-03-15 revision 67260) [x86_64-linux]
HTML 리포트 생성
coderay 젬을 설치하면 테스트 결과를 HTML 형식으로 출력할 수 있다.
$ gem install coderay
적절한 포맷터 설정을 통해 시각적인 테스트 리포트를 생성할 수 있다.