この記事はリンク情報システム(Facebook)が主催するイベント「Tech Connect! Autumn」のリレー記事です。
「Tech Connect! Autumn」は engineer.hanzomon のグループメンバによってリレーされます。
本記事は7日目、9/16(日)分です。ちょうど折り返しですね。
きっかけ
Ansible で環境構築しServerspec で自動テスト、みたいなことをやっているのですが、
- Ansible はRole 単位、Serverspec はHost 単位で管理されているので、管理が大変
- Ansible の設定とServerspec の設定で二重化されてしまい、管理が大変
ということで、自前でServerspec のRakefile やspec_helper.rb 修整したりしてなんとかServerspec もRole 単位で管理できないかとごにょごにょやっていたのですが、イマイチしっくりきていませんでした。
そんな時にansible_specなるものの存在を知り、これいいじゃん!となったので、ご紹介したいと思います。
ansible_spec とは?
ansible_spec は、@volanja 氏が作成した、Serverspec をAnsible 用の設定ファイルを使って管理できるように作成されたGem です。
これにより、Ansible とServerspec を同一ディレクトリでいい感じに管理することができるようになります。
使い方
実際に使ってみてどんな塩梅か確認してみましょう。
環境は
- Red Hat Enterprise Linux 7.2
- Python 2.7.5
- Ruby 2.5.1
- Ansible 2.6.3
- Serverspec 2.41.3
- ansible_spec 0.2.25
とします。
超簡単な動作確認なので、ここではWeb サーバにnginx をインストールするという感じでやってみます。
1. Ansible のPlaybook をつくる
ざっくりディレクトリ構造は以下のようになります。
ansible_sample
├─ hosts
├─ site.yml
└─ roles
└─ nginx
├─ handlers
│ └─ main.yml
├─ tasks
│ └─ main.yml
├─ vars
│ └─ main.yml
└─ templates
└─ nginx.conf.j2
対象ホストをhosts で管理し、
nginx の環境構築に関わる情報をroles/nginx 配下のディレクトリで管理します。
タスクの中身としてはこんな感じで。
---
- name: install epel repository
yum:
name: http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/7/x86_64/Packages/e/epel-release-7-11.noarch.rpm
state: present
- name: install nginx
yum:
name: nginx
state: present
- name: enable service nginx
systemd:
name: nginx
state: started
daemon_reload: yes
enabled: yes
- name: configration nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify:
- restart nginx
ほんとざっくりインストールするだけという感じで。
動作確認もしてみましょう。
$ ansible-playbook -i hosts site.yml
PLAY [web] *************************************************************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************************************************************
ok: [webserver]
TASK [nginx : install epel repository] *********************************************************************************************************************************
changed: [webserver]
TASK [nginx : install nginx] *******************************************************************************************************************************************
changed: [webserver]
TASK [nginx : enable service nginx] ************************************************************************************************************************************
changed: [webserver]
TASK [nginx : configration nginx] **************************************************************************************************************************************
changed: [webserver]
RUNNING HANDLER [nginx : restart nginx] ********************************************************************************************************************************
changed: [webserver]
PLAY RECAP *************************************************************************************************************************************************************
webserver : ok=6 changed=5 unreachable=0 failed=0
とりあえずAnsible は動作しました。
2. Serverspec でテストを書く
ほんとは先にこっちやるべきなんですが、まぁ動作確認なんで…
素のServerspec ですと、ディレクトリ構造は以下のようになります。
serverspec_sample
├── Rakefile
└── spec
├── webserver
│ └── nginx_spec.rb
└── spec_helper.rb
こちらは、対象ホストをspec 配下のサブディレクトリ名で管理し、
nginx のテストコードをspec 配下のnginx_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
プロセスが起きてて80番で受け付けてること確認するだけ。
これも動作確認。
$ rake spec:webserver
/root/.rbenv/versions/2.5.1/bin/ruby -I/root/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-support-3.8.0/lib:/root/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.0/lib /root/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.0/exe/rspec --pattern spec/webserver/\*_spec.rb
Package "nginx"
should be installed
Service "nginx"
should be enabled
should be running
Port "80"
should be listening
Finished in 1.34 seconds (files took 0.35733 seconds to load)
4 examples, 0 failures
テストは通りました。
いちおうやりたいことはこれでできているわけですが、これがもっとたくさんのRole であったりHost であったりがあるとそれはもう管理がごちゃごちゃになるわけです。
そこでansible_spec に登場していただきましょう。
3. ansible_spec をインストール
インストールはgem で行います。
$ gem install ansible_spec
インストールできたらAnsible の管理ディレクトリで以下のコマンドを実行します。
$ ansiblespec-init
create spec
create spec/spec_helper.rb
create Rakefile
create .ansiblespec
create .rspec
Serverspec 用のRakefile とspec/spec_helper.rb が作成されるはずです。
ここまでのディレクトリ構造は以下となっています。
ansible_sample
├─ hosts
├─ site.yml
├─ roles
│ └─ nginx
│ ├─ handlers
│ │ └─ main.yml
│ ├─ tasks
│ │ └─ main.yml
│ ├─ vars
│ │ └─ main.yml
│ └─ templates
│ └─ nginx.conf.j2
+├── Rakefile
+└── spec
+ └── spec_helper.rb
ここに先程作成したspec ファイルを格納します。
格納先はroles/rolename/spec/ 配下となります。つまりこうですね。
ansible_sample
├─ hosts
├─ site.yml
├─ roles
│ └─ nginx
│ ├─ handlers
│ │ └─ main.yml
│ ├─ tasks
│ │ └─ main.yml
│ ├─ vars
│ │ └─ main.yml
│ ├─ templates
│ │ └─ nginx.conf.j2
+│ └─ spec
+│ └─ nginx_spec.rb
├── Rakefile
└── spec
└── spec_helper.rb
Serverspec のテストコードをAnsible 管理化のRoles 配下にまとめることができました。
そうそう、これがやりたかったんだよ。
4. ansible_spec 導入状態でServerspecのテストを実行する
テストの実行コマンドを確認してみましょう。
$ rake -T
rake all # Run serverspec to all test
rake serverspec:web # Run serverspec for web
コマンド確認できたら実際叩いてみます。
$ rake all
rake all
Run serverspec for web to {"name"=>"webserver", "port"=>22, "uri"=>"webserver"}
/root/.rbenv/versions/2.5.1/bin/ruby -I/root/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-support-3.8.0/lib:/root/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.0/lib /root/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.0/exe/rspec --pattern \{roles\}/\{nginx\}/spec/\*_spec.rb
Package "nginx"
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
should be installed (FAILED - 1)
Service "nginx"
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
should be enabled (FAILED - 2)
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
should be running (FAILED - 3)
Port "80"
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
No backend type is specified. Fall back to :exec type.
should be listening (FAILED - 4)
Failures:
1) Package "nginx" should be installed
On host `webserver'
Failure/Error: it { should be_installed }
expected Package "nginx" to be installed
/bin/sh -c rpm\ -q\ nginx
package nginx is not installed
# ./roles/nginx/spec/nginx_spec.rb:4:in `block (2 levels) in <top (required)>'
2) Service "nginx" should be enabled
On host `webserver'
Failure/Error: it { should be_enabled }
expected Service "nginx" to be enabled
/bin/sh -c systemctl\ --quiet\ is-enabled\ nginx
# ./roles/nginx/spec/nginx_spec.rb:8:in `block (2 levels) in <top (required)>'
3) Service "nginx" should be running
On host `webserver'
Failure/Error: it { should be_running }
expected Service "nginx" to be running
/bin/sh -c systemctl\ is-active\ nginx
unknown
# ./roles/nginx/spec/nginx_spec.rb:9:in `block (2 levels) in <top (required)>'
4) Port "80" should be listening
On host `webserver'
Failure/Error: it { should be_listening }
expected Port "80" to be listening
/bin/sh -c ss\ -tunl\ \|\ grep\ --\ :80\\\
# ./roles/nginx/spec/nginx_spec.rb:13:in `block (2 levels) in <top (required)>'
Finished in 0.10702 seconds (files took 0.51083 seconds to load)
4 examples, 4 failures
Failed examples:
rspec ./roles/nginx/spec/nginx_spec.rb:4 # Package "nginx" should be installed
rspec ./roles/nginx/spec/nginx_spec.rb:8 # Service "nginx" should be enabled
rspec ./roles/nginx/spec/nginx_spec.rb:9 # Service "nginx" should be running
rspec ./roles/nginx/spec/nginx_spec.rb:13 # Port "80" should be listening
/root/.rbenv/versions/2.5.1/bin/ruby -I/root/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-support-3.8.0/lib:/root/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.0/lib /root/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.0/exe/rspec --pattern \{roles\}/\{nginx\}/spec/\*_spec.rb failed
ありゃ、なんかエラーになりました。
「No backend type is specified. Fall back to :exec type.」言うてるので、どうもssh でターゲットHost に接続しないでローカルに対して動作しているみたいです。
ansible_spec で生成したspec/spec_helper.rb を、Serverspec で生成したspec/spec_helper.rb で置き換えればとりあえずは動作するようです。(なんか設定漏れてる?)
で、改めて実行。
$ rake all
Run serverspec for web to {"name"=>"webserver", "port"=>22, "uri"=>"webserver"}
/root/.rbenv/versions/2.5.1/bin/ruby -I/root/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-support-3.8.0/lib:/root/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.0/lib /root/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/rspec-core-3.8.0/exe/rspec --pattern \{roles\}/\{nginx\}/spec/\*_spec.rb
Package "nginx"
should be installed
Service "nginx"
should be enabled
should be running
Port "80"
should be listening
Finished in 1.05 seconds (files took 0.35596 seconds to load)
4 examples, 0 failures
うまくいきました。とりあえずはこれで動作確認完了になります。
結局何がうれしいの?
Serverspec をAnsible のplaybook 定義に従いRole を割り当て、Role 配下のspec でテストすることができるようになります。
ansible_spec を使用しない場合はServerspec のspec をHost 単位で割り振っていたので、Role の定義変更をした際は各Host 配下のspec もすべて変更する必要がありましたが、spec をRole 配下で管理できればそのようなこともする必要がなくなり、管理が容易になります。
また、Ansible の変数をServerspec で使用することもできます。
Ansible とServerspec で定義の二重管理が不要になるということです。
同名変数の読込順序はAnsible の読込順序に従います。
ただし、2018/09/16現在、一部変数についてはサポートされていないようです。
まとめ
ansible_spec 、オススメです。
Ansible / Serverspec を使用している方なら導入して損はないと思います。
管理を簡便化して効率よく自動化していけるようにしましょう。
お読みいただきありがとうございました。
他のTechConnect!Autumn の記事も楽しみにしています。
明日は@hura さんです。