InSpecではじめるテスト駆動インフラ
最近、新しくChefのCookbookを書く機会があったので、前から気になっていたInSpecを少し触ってみました。
InSpecとは何か?
InSpecは、Chef社が開発しているオープンソースのサーバーテストフレームワークです。サーバーのテストフレームワークといえば、Serverspecが有名ですが、InSpecはインフラ管理向けというよりコンプライアンスの担保だったりセキュリティ要件を満たしているかどうかのテスト用作られたツールになっています。InSpecのGithubには、
InSpec is inspired by the wonderful Serverspec project. Kudos to mizzy and all contributors!
と記載があるように、Serverspecにインスパイアされているようです。なので、基本的にServerspecで提供されている代表的なリソースはInSpecでも大体用意されていて、今の所(バージョン1.3.0)以下のようなリソースをサポートしています。
apache_conf apt audit_policy auditd_conf auditd_rules bash bond bridge bsd_service command csv directory etc_group etc_passwd etc_shadow file gem group grub_conf host iis_site inetd_conf ini interface iptables json kernel_module kernel_parameter launchd_service limits_conf login_def mount mysql_conf mysql_session npm ntp_conf oneget os os_env package parse_config parse_config_file pip port postgres_conf postgres_session powershell process registry_key runit_service security_policy service ssh_config sshd_config ssl sys_info systemd_service sysv_service upstart_service user users vbscript windows_feature wmi xinetd_conf yaml yum
例えば、指定したパッケージが適切にインストールされているかチェックしたい場合、packageリソースを使って以下のように書きます。InSpecでもServerspecでも大体似たような感じで書くことができ、バージョンチェックのところ以外は全く同じです。
## Serverspec describe package('nginx') do it { should be_installed.with_version('1.9.5') } end ## InSpec describe package('nginx') do it { should be_installed } its('version') { should eq 1.9.5 } end
ディレクトリとファイルのテストもほとんど一緒で、modeの箇所以外はそのままInSpecでも動かせます。
## Serverspec describe file('/usr/local/foo') do it { should be_directory } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode '755' } end describe file('/usr/local/foo/bar') do it { should be_file } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } it { should be_mode '644' } its('content') { should match('Hello') } end ## InSpec describe file('/usr/local/foo') do it { should be_directory } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } its('mode') { should cmp '0755' } end describe file('/usr/local/foo/bar') do it { should be_file } it { should be_owned_by 'root' } it { should be_grouped_into 'root' } its('mode') { should cmp '0644' } its('content') { should match('Hello') } end
ちなみに、よりInSpec風に書くのであれば、以下のようにテストを書くこともできます。
describe directory('/usr/local/foo') do its('owner') { should cmp 'root' } its('group') { should cmp 'root' } its('mode') { should cmp '755' } end describe file('/usr/local/foo/bar') do its('owner') { should cmp 'root' } its('group') { should cmp 'root' } its('mode') { should cmp '644' } its('content') { should match('Hello') } end
機能的にServerspecもInSpecもほとんど大差ない感じなんですが、強いて違う点を挙げるとすれば、InSpecは、Serverspec に比べ、よりRubyやRSpec、Rakeを意識する必要がない点かなと思います。というのも、InSpecは、spec_helper.rbやRakefileを準備する必要がありません。以下のようなCLIが用意されていて、とりあえずテストケースだけ書いてコマンドを実行すれば、すぐ使うことができます。
# run test locally inspec exec test.rb # run test on remote host on SSH inspec exec test.rb -t ssh://user@hostname -i /path/to/key # run test on remote windows host on WinRM inspec exec test.rb -t winrm://Administrator@windowshost --password 'your-password' # run test on docker container inspec exec test.rb -t docker://container_id
test-kitchenでInSpecを使ってみる
では、実際にtest-kitchenと一緒に使ってみます。
準備
現時点での最新版のChefDK(バージョン0.19.6)には、test-kichenもInSpec (kitchen-inspec)もすでに含まれてるので、ChefDKをインストールするだけで使えるようになります。Macであれば以下のコマンドだけで環境の準備ができます。(virtualboxとVagrantは別途必要)
$ brew cask install chefdk
chef generate cookbook
コマンドで適当にcookbookを作って、recipeとテストを用意します。今回触ってみた環境をgithubにあげてますので、よかったら参考にしてください。
https://github.com/tkak/hello-cookbook
git cloneしてきて、kitchen list
を実行すると以下のテスト対象を確認できます。
$ chef exec kitchen list Instance Driver Provisioner Verifier Transport Last Action with-serverspec-ubuntu-1604 Vagrant ChefZero Busser Ssh <Not Created> with-serverspec-centos-72 Vagrant ChefZero Busser Ssh <Not Created> with-inspec-ubuntu-1604 Vagrant ChefZero Inspec Ssh <Not Created> with-inspec-centos-72 Vagrant ChefZero Inspec Ssh <Not Created> with-shell-ubuntu-1604 Vagrant ChefZero Shell Ssh <Not Created> with-shell-centos-72 Vagrant ChefZero Shell Ssh <Not Created>
Serverspecでテスト
まずは、通常の使い方であるServerspec (busser-serverspec)でテストを実行してみます。
$ chef exec kitchen test serverspec ... ... ... -----> Running serverspec test suite -----> Installing Serverspec.. Fetching: diff-lcs-1.2.5.gem (100%) Fetching: rspec-expectations-3.5.0.gem (100%) Fetching: rspec-mocks-3.5.0.gem (100%) Fetching: rspec-3.5.0.gem (100%) Fetching: rspec-its-1.2.0.gem (100%) Fetching: multi_json-1.12.1.gem (100%) Fetching: net-ssh-3.2.0.gem (100%) Fetching: net-scp-1.2.1.gem (100%) Fetching: net-telnet-0.1.1.gem (100%) Fetching: sfl-2.3.gem (100%) Fetching: specinfra-2.64.0.gem (100%) Fetching: serverspec-2.37.2.gem (100%) -----> serverspec installed (version 2.37.2) /opt/chef/embedded/bin/ruby -I/tmp/verifier/suites/serverspec -I/tmp/verifier/gems/gems/rspec-support-3.5.0/lib:/tmp/verifier/gems/gems/rspec-core-3.5.4/lib /opt/chef/embedded/bin/rspec --pattern /tmp/verifier/suites/serverspec/\*\*/\*_spec.rb --color --format documentation --default-path /tmp/verifier/suites/serverspec Package "zsh" should be installed File "/usr/local/foo" should be directory should be owned by "root" should be grouped into "root" should be mode "755" File "/usr/local/foo/bar" should be file should be owned by "root" should be grouped into "root" should be mode "644" content should match "Hello" Finished in 0.17573 seconds (files took 0.3651 seconds to load) 10 examples, 0 failures Finished verifying <with-serverspec-centos-72> (0m5.60s). ... ... ...
kitchen test
コマンドで、以下の処理が一気に動きます。
- VirtualBox上にインスタンスを作る
- Chefを実行する
- Serverspecでテストする
- インスタンスを削除する
ちなみに、上記のログにあるように、busser-serverspec 経由でtest-kitchenを使うと、テスト初回時にインスタンス内でgemのインストールが走り、少し待たされます。ちょっとした待ち時間ですが、何度もインスタンスを作って壊してをやってると、この待ち時間がバカにできないので、そんな時はshell-verifierを使うのがオススメです。shell-verifierを使えば、事前にインストールされたホスト側のServerspecを使ってテストを実行するので、gemインストールの待ち時間なくテストができます。shell-verifierでServerspecを使う方法は以下のとおりです。
まず、ホストにServerspec をインストールしておきます。
$ chef gem install serverspec
そして、.kitchen.ymlファイルに以下のような設定を追記し、
verifier: name: shell command: chef exec rspec -c -f documentation -I test/integration/${KITCHEN_SUITE}/serverspec --pattern test/integration/${KITCHEN_SUITE}/serverspec/**/*_spec.rb --pattern test/integration/helpers/serverspec/spec_helper.rb
spec_helper.rbを用意します。
require 'serverspec' set :backend, :ssh options = Net::SSH::Config.for(host) options[:host_name] = ENV['KITCHEN_HOSTNAME'] options[:port] = ENV['KITCHEN_PORT'] options[:user] = ENV['KITCHEN_USERNAME'] options[:keys] = ENV['KITCHEN_SSH_KEY'] set :host, options[:host_name] set :ssh_options, options set :env, :LANG => 'C', :LC_ALL => 'C'
これでbusser-serverspecを使うより短時間でテストが実行できるようになります。ただし、.kitchen.ymlにコマンドの記述とspec_helper.rbの用意をする必要があり、若干面倒です。例えば、複数のCookbookを管理する時とか。
InSpecでテスト
次にInSpecでテストを動かしてみます。使い方は簡単で、InSpecを使うには.kitchen.ymlに以下のようなveriferを追記するだけで使えるようになります。
verifier: name: inspec
デフォルトだとtest/integration/<test suites>/
以下のディレクトリにテストを配置します。また、以下のようにテストの場所を変更したり、他のリポジトリにあるテストを使うことも可能です。
## local verifier: name: inspec inspec_tests: - test/recipes ## github verifier: name: inspec inspec_tests: - https://github.com/dev-sec/tests-ssh-hardening
kitchen test
のログは以下のようになります。
$ chef exec kitchen test inspec ... ... ... -----> Verifying <with-inspec-centos-72>... Detected alternative framework tests for `inspec` Use `/Users/takaaki.furukawa/src/github.com/tkak/hello-cookbook/test/integration/with-inspec/inspec` for testing Target: ssh://[email protected]:2201 System Package ✔ zsh should be installed File /usr/local/foo ✔ should be directory ✔ should be owned by "root" ✔ should be grouped into "root" ✔ mode should cmp == "0755" File /usr/local/foo/bar ✔ should be file ✔ should be owned by "root" ✔ should be grouped into "root" ✔ mode should cmp == "0644" ✔ content should match "Hello" Test Summary: 10 successful, 0 failures, 0 skipped Finished verifying <with-inspec-centos-72> (0m1.13s). ... ... ...
特に問題なく動きます。しかも、busser-serverspec のようにgemインストールが走って待たされるということもなく、shell-verifierを使うときのようにspec_helper.rbを用意する必要もないので、なかなかいい感じです。
まとめ
test-kitchenでInSpecを試してみました。後継のツールだけあって思ってたよりシンプルで使いやすかったです。すでにServerspec で書いてあるテストコードは、Serverspec で十分テストをまわせているのですぐにInSpecに置き換えようとは思いませんが、新規で書くならInSpecも選択肢としてありだなと感じました。また、InSpec本来の使い方であるコンプライアンスチェックとしてChef Automateと組み合わせるといったことができるらしいのですが、今回はインフラのテストとしてしか試していないので、機会があればその辺も触ってみたいです。
参考
- InSpec - Audit and Test Framework
- [和訳] InSpecへの道 #getchef - クリエーションライン株式会社
- GitHub - chef/inspec: InSpec: Auditing and Testing Framework
- Test-KitchenでServerspecやInfratasterをShell-Verifierから実行 - Qiita
DevOps導入指南 Infrastructure as Codeでチーム開発・サービス運用を効率化する (DEV Engineer’s Books)
- 作者: 河村聖悟,北野太郎,中山貴尋,日下部貴章,株式会社リクルートテクノロジーズ
- 出版社/メーカー: 翔泳社
- 発売日: 2016/10/14
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
Infrastructure As Code: Managing Servers in the Cloud
- 作者: Kief Morris
- 出版社/メーカー: Oreilly & Associates Inc
- 発売日: 2016/06/27
- メディア: ペーパーバック
- この商品を含むブログを見る
- 作者: 宮下剛輔
- 出版社/メーカー: オライリージャパン
- 発売日: 2015/01/17
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (6件) を見る