ServerSpecの使い方をインストールから解説!EC2のWebサーバを自動テスト

今回は、Serverspecを使って自動テストを行います!
早速!
Serverspecとは
サーバのテストを自動で実行するRubyで作られたプログラムです。
具体的には、「接続できるか」、「必要なポートは開いているか」、「サービスは動いているか」など、サーバを作った後の確認を自動で行ってくれます。
なんで自動でやるのか
人間が手動でやるとどこかでミスが起こる可能性があるからです。
それは、コマンドの打ち間違いであったり、ちゃんとやってあるだろうという思い込み、実行するコマンドの抜け漏れなどなど。
正しいテストが行われていないと、サーバにアクセスできなかったり、正しい動きをしてくれなかったりと言った問題を見つけることができません。
そうすると、実際にサーバを公開したあと、ユーザーからのクレームでようやく気づくと言ったことが起こってしまします。
このようなミスはテストする項目が増えれば増えるほど発生する可能性が上がります。
では、早速やっていきましょう!
事前準備
自動テストをやるので、テストする対象が必要です。
今回実施する環境はこちら

シンプルな構成なので手動でもOKです。
しかし、本ブログではコードから作成する方法もご紹介しているのでぜひIaCを体験してみてください。
事前準備は以下の通り
| No | 事前準備項目 | 参考URL | 
| 1 | テスト環境の構築 | Terraform実践! インストールからEC2の構築までをまとめてみた! | 
| 2 | テストサーバの構築 | EC2にAnsibleをインストールしてWebサーバを構築する手順をインストールから解説 | 
| 3 | rubyのインストール | EC2にrubyをインストールする手順 ~rbenvからbundlerの解説~ | 
Serverspecのインストール
Serverspecはgemです。
なので、rubyインストールの記事で解説しているように、bundlerを使ってテスト実行サーバにインストールします。
$ mkdir serverspec_test
$ cd serverspec_test
$ bundle init
$ echo gem '"serverspec"' >> Gemfile
$ echo gem '"rake"' >> Gemfile
$ bundle install --path vendor/bundleこれで、Serverspecのインストールは完了です。
Serverspecの他に"rake"というgemをインストールしています。
これは、rubyのビルドツールでこの後生成されるRakefileというファイルに書かれた内容を実行できるように準備(ビルド)してくれるコマンドです。
テストのセットアップ
次にseverspecのセットアップをします。
$ pwd
/home/ec2-user/serverspec_test
$ bundle exec serverspec-init
$ bundle exec serverspec-init
Select OS type:
  1) UN*X
  2) Windows
Select number: 1
Select a backend type:
  1) SSH
  2) Exec (local)
Select number: 1
Vagrant instance y/n: n
Input target host name: target
 + spec/
 + spec/target/
 + spec/target/sample_spec.rb
 + spec/spec_helper.rb
$ bundle exec serverspec-initを実行することでテストするために必要なファイルなどが自動生成されます。
| 項目 | 内容 | 設定値 | 備考 | 
| Select OS type | OSのタイプを選択 | 1 | Windowsの場合のみ2を選択 | 
| Select a backend type | 接続方法を選択 | 1 | localhostに接続する場合のみ2を選択 | 
| Vagrant instance | vagrantで管理しているサーバに対しての接続か否か | n | vagrantはvirtualboxなどを利用して仮想サーバを作成できるツールです | 
| Input target host name | ターゲットのホスト名 | target | このホスト名に対して接続します。なんでもOKです。 | 
実行後のファイル
$ ls -l
合計 12
-rw-r--r-- 1 ec2-user ec2-user 175 10月 13 23:07 Gemfile
-rw-rw-r-- 1 ec2-user ec2-user 976 10月 13 23:07 Gemfile.lock
-rw-rw-r-- 1 ec2-user ec2-user 685 10月 13 23:08 Rakefile
drwxrwxr-x 3 ec2-user ec2-user  42 10月 13 23:08 spec
drwxrwxr-x 3 ec2-user ec2-user  20 10月 13 23:07 vendorテストの準備
ここで、どのようなテストをするのかをファイルに書いていきます。
$ bundle exec serverspec-initを実行した時に以下のファイルが作成されていることでしょう。
| 種類 | 名称 | 用途 | 
| ファイル | Rakefile | rakeコマンドで実行する内容が記載されています。 | 
| ディレクトリ | spec | どのようなテストをするのかが記載されたファイルが中に入っています。 | 
specの中身はこのような構成になっております。
$ tree ~/serverspec_test/spec/
/home/ec2-user/serverspec_test/spec/
├── spec_helper.rb
└── target
    └── sample_spec.rb先に、使うコマンドや流れをイメージしやすいように実行までやってしまいましょう。
sample_spec.rbの編集
specディレクトリの中に$ bundle exec serverspec-init の「Input target host name」で指定した名前のディレクトリが生成されていると思います。
Serverspecはテストを実行する時specディレクトリ配下のディレクトリをテスト対象サーバとします。
今回、specディレクトリの下には、targetディレクトリがあり、その中にsample_spec.rbというどんなテストをするかを定義するファイルがあります。
なので、「targetというサーバに対してsample_spec.rbで定義されたテストを実行する。」という動作になります。
別のサーバに対してもテストを行いたい場合はspecの下にホスト名のディレクトリを作成します。
sample_spec.rbは以下のようになるよう編集してください。
require 'spec_helper'
describe package('httpd') do
 # httpdがインストールされていることを確認
  it { should be_installed }
end
describe service('httpd') do
  # httpdのサービスが起動していることを確認
  it { should be_enabled }
  # httpdのサービスが自動起動設定になっていることを確認
  it { should be_running }
end
describe port(80) do
  # テスト対象に80番ポートでアクセスできることを確認
  it { should be_listening }
end
describe file('/var/www/html/index.html') do
  # /var/www/html/index.htmlが存在することを確認
  it { should exist }
end対象への接続設定
serverspceはテスト対象にsshで接続してテストを行うためテスト実行サーバからテスト対象サーバに指定したホスト名でssh接続させる必要があります。
今回、テスト対象を「target」としているためtargetを名前解決してssh接続させる設定が必要です。
秘密鍵の配置
テスト対象には鍵認証で接続します。
そのため、テスト実行サーバに秘密鍵を配置する必要があります。
ローカルに秘密鍵がある場合ローカルから以下のコマンドで秘密鍵を配置します。
$ scp -i <テスト実行サーバに接続するための秘密鍵> <テスト対象サーバに接続するための秘密鍵> ec2-user@<テスト実行サーバのIP>:~/.ssh/~/.ssh/configの編集
このファイルはssh接続する時に参照される設定ファイルです。
すでにある場合は追記、なければ新規作成してください。
今回の場合、targetというサーバに対して接続し、テストを行いたいのでHostをtargetと指定します。
こうるすことでsshの実行は、$ ssh target とするだけで良くなります。
秘密鍵や接続ユーザの指定はssh実行時に自動で探して設定してくれます。
Host target
        HostName <対象のアドレス>
        IdentityFile ~/.ssh/<秘密鍵>
        User ec2-user
        ServerAliveInterval 30| 設定項目 | 設定値 | 設定内容 | 
| Host | target | この設定の名前です。 | 
| HostName | 対象のIPアドレス | 実際に接続するIPアドレス(DNSで名前解決できればホスト名でもOK) | 
| IdentityFile | 秘密鍵の絶対パス | HostNameのサーバに対して接続する時に使う秘密鍵の絶対パス | 
| User | ec2-user | HostNameのサーバに対して接続する時のユーザ | 
| ServerAliveInternal | 30 | 必須ではないです。 無操作時にssh接続がタイムアウトで切断されるのを防止します。 | 
実行
実行はRakefileのあるディレクトリで実行してください。
うまくいっていれば以下のような出力になっているかと思います。
$ bundle exec rake spec
(in /home/ec2-user/serverspec_test)
/home/ec2-user/.rbenv/versions/3.0.2/bin/ruby -I/home/ec2-user/serverspec_test/vendor/bundle/ruby/3.0.0/gems/rspec-core-3.10.1/lib:/home/ec2-user/serverspec_test/vendor/bundle/ruby/3.0.0/gems/rspec-support-3.10.2/lib /home/ec2-user/serverspec_test/vendor/bundle/ruby/3.0.0/gems/rspec-core-3.10.1/exe/rspec --pattern spec/target/\*_spec.rb
Package "httpd"
  is expected to be installed
Service "httpd"
  is expected to be enabled
  is expected to be running
Port "80"
  is expected to be listening
File "/var/www/html/index.html"
  is expected to exist
Finished in 1.11 seconds (files took 0.34133 seconds to load)
5 examples, 0 failures
実行がうまくいかない場合
このようなエラーが出ていたら、テストする以前の問題で失敗していたり正常なテストができていなかったりします。
原因と考えられるものをあげておきますので再度ご確認ください。
getaddrinfo: Name or service not known
テスト対象の名前解決が失敗し接続できずにエラーになっています。
~/.ssh/configの設定をお忘れではないですか?
Net::SSH::AuthenticationFailed:
認証エラーでテスト前に
~/.ssh/configの設定はお間違い無いですか?
特に秘密鍵についてご確認ください。
- 存在しているか
- パスに間違いはないか
- 秘密鍵の権限が600になっているか
- .sshの権限が700になっているか
SyntaxError:
テストを定義すしているsample_spec.rbに構文エラーがあります。
先程のサンプルと見比べて間違いがないか確認してください。
最後に

テストは成功しましたか?
自動ですとができるようになると、正しく定義さえして仕舞えば「誰が」、「何度やっても」正しい結果を得ることができます。
その性質から、先にテストコードを作成してからアプリケーションを作っていく「テスト駆動開発」という開発方法もあります。
これは、最初テストに失敗している状態からサーバを構築していき正しい状態になればテストが成功すると言った考え方で進める開発方法です。
手動によるミスが発生しないからこそできる方法だと思います。
また、今までTerraform、Ansibleを使って構築も自動化してきたので近々Jenkinsも使ってみようと思います。
それでは!最後までご覧いただきありがとうございました。














