Serverspecの概要からインストールまで

2014年8月1日(金)
池田 大輔

変化の激しいビジネスに対応するためにシステムやサービスには、これまで以上に迅速な導入や安定した運用が求められるようになってきています。このような要求を満たすために、仮想環境やクラウド環境を基盤として利用し、サーバの構築プロセスや構成管理プロセスの効率化のためにChefPuppetAnsibleといった自動化ツールが活用されるようになってきています。

このように自動処理で手軽にシステムやサービスをセットアップできるようになると、構築時の人為的ミスを防げるようになるなど便利になる反面、内部で実施されている処理が人の手から離れて、把握しづらくなる傾向にあります。また自動化の導入に際しては、システムが意図した状態に作り上げられているのかを確認する作業も重要になってきます。特に大規模で複雑な環境において、この確認作業を手作業で実施することは、高コストの要因となってしまいます。

参考:
ThinkIT 連載「システムを自動構築するためのOSSフレームワーク、Chefを使ってみよう!」
ThinkIT 「第3回 クラウドでの設定管理ツールPuppet」

Serverspecの登場

このような背景から、自動構築した環境が本当に意図した通りにできあがっているかの確認作業を自動的に行えるようにするツールが登場しました。それがこの連載で紹介する「Serverspec」です。

Serverspecの登場以前にも、自動構築プロセスに対して処理に間違いがないかをテストするための方法は存在していました。たとえばChefの場合、以下のような様々なツールを活用してテストが実施できます。

ツール テスト可能なこと
knife cookbook test 記法が間違えてないかの記法チェックツール
foodcritic より適切な記述を支援するための構文チェックツール
chefspec 記述したコードによって意図した処理が実施されるかの事前チェックツール
表1:Chefのテスト関連ツール

これらのテストツールを組み合わせることで、自動構築用に作成したコードが正しく実行されるかどうかを事前に確認してから実行に移せるようになります。しかし、自動化処理の記述内容として正しかったとしても、実行時に何らかの要因により、記述したコードの通りに処理が実行されないといったケースも発生し得ます。

そこで効果を発揮するのが、できあがった環境に対して、その環境が意図した通りの状態で稼働しているかどうかをテストする「Serverspec」です。

参考:
serverspec の論文公開します

Serverspecの概要

Serverspecは宮下剛輔氏によって開発されたサーバの状態をテストするためのフレームワークです。

Serverspec公式サイト

Ruby実装で、RubyのテストフレームワークであるRSpecの書き方に準拠しています。2014/7/14時点での最新バージョンは、1.10.0です。本記事では、Serverspecのバージョン1.10.0 および Serverspecのコアのテスト部分を切り出したSpecinfraについてはバージョン1.20.0を対象に解説します。

Serverspecでできること

Serverspecの利用方法を紹介する前に、Serverspecを利用することでどういったことができるようになるのかをまとめます。

Serverspecでできることは、大きく分けて以下の2つです。

  • 意図した処理が環境に適切に反映されていることを確認
  • 意図していない影響が環境に対して発生していないことを確認

意図した処理が環境に適切に反映されていることを確認

サーバにあるパッケージがインストールされているか、TCPの何番ポートがLISTEN状態になっているか、ファイアウォール設定で、あるIPアドレスからのアクセスが遮断される設定が入っているかなど、意図した設定通りにサーバが稼働しているかどうかを確認することができます。これにより、ChefやAnsible等の自動構築処理に対して、正常に稼働していることを保証できます。

意図していない影響が環境に対して発生していないことを確認

サーバになんらかの変更作業を施した結果、意図しない影響がその他の箇所で発生していないかどうかを確認することもできます。たとえばiptablesの設定を変更した結果、意図せずあるプロセスに接続ができない状態になってしまったなどの状況を確認できます。

Serverspecの内部的な仕組み

次に、Serverspecの内部的な仕組みを簡単に紹介します。Serverspecは4つのテスト実施方法に対応しています。

  1. LinuxマシンへのSSH接続を経由したリモート実行
  2. Linuxマシンでのローカル実行
  3. WindowsマシンへのWinRM接続を経由したリモート実行
  4. Windowsマシンでのローカル実行

たとえば、LinuxマシンへのSSH接続を経由したリモート実行のパターンの場合、Serverspecのテストコードを実行する元となるサーバからテスト実行対象のサーバに対してSSH接続し、接続先に対して稼働状況確認用のコマンドを実行します。そしてその結果を、Serverspec実行元のサーバにて正常かどうかを評価するといった仕組みです。

図1:Serverspecの仕組み概要図(クリックで拡大)

このような仕組みとなっているため、テスト実行対象のサーバにはエージェントのような特別なソフトウェアをインストールする必要はありません。Linuxマシンの場合はSSH接続、Windowsマシンの場合はWinRM接続が可能であるだけで利用できます。

より具体的に、テスト実行対象サーバ内での挙動を見てみます。Pythonのパッケージがインストールされているかをテストするコードを実行した時の挙動です。

テスト実行対象サーバがCentOSの場合、テスト実行対象サーバ側のSSH接続のログ(/var/log/secure)には次のような結果が記録されます。

図2: SSHログ(クリックで拡大)

このログをみると、pythonパッケージの存在確認コマンド(/bin/rpm -q python)の実行前に、OSのアーキテクチャの確認やOSの種類・バージョンの確認を実施しているのがわかります。このようにServerspecは、実行対象サーバのOSの種別を判断して自動的にテスト用の実行コマンドを切り替えて処理を行います。OSの種別毎の違いを吸収するコードは、Serverspecと併用するSpecinfraにて実装されています。たとえば、図2でCentOSに対して実行したpythonパッケージのインストール状態を確認するコードをUbuntuに対して行うと、「dpkg-query -f '${Status}' -W python」が実行されます。

Serverspec利用までの流れ

それでは具体的にServerspecの利用方法を紹介します。Serverspecを利用するには以下の作業が必要となります。筆者はServerspec実行元サーバのOSとしてUbuntu 14.04を導入して作業を実施しました。

  作業 実施対象
1 Serverspecのインストール Serverspec実行元サーバ
2 Serverspecの初期化処理 Serverspec実行元サーバ
3 接続設定 Serverspec実行元サーバとテスト実行対象サーバ
4 sudo実行設定 テスト実行対象サーバ(Linuxの場合)
5 テストコードの作成 Serverspec実行元サーバ
6 テスト実行 Serverspec実行元サーバ
表2:Serverspec利用までの作業

Serverspecのインストール

ServerspecはRubyのgemパッケージとして配布されています。そのため、RubyおよびRubygemsがインストールされている環境であれば、以下のコマンドだけでインストール完了です。

    $ sudo gem install serverspec

このコマンドを実行すると、Specinfraなどの依存するパッケージもすべてインストールされます。

Serverspecの初期化処理

Serverspecの実行元サーバ上で初期化処理(serverspec-init)を行います。

    $ serverspec-init
    Select OS type:

      1) UN*X
      2) Windows

    Select number: 1  ←テスト対象サーバのOS種別を選択

    Select a backend type:

      1) SSH
      2) Exec (local)

    Select number: 1  ←テスト実行のタイプを選択

    Vagrant instance y/n: n ←Vagrantの管理下のインスタンスかどうかを選択
    Input target host name: server-01 ←テスト対象サーバのホスト名を入力
     + spec/
     + spec/server-01/
     + spec/server-01/httpd_spec.rb
     + spec/spec_helper.rb
     + Rakefile

上記はLinuxマシン(server-01)に対してSSH接続でテストする場合の実行例です。Windowsマシンに対してWinRM接続でテストする場合には、OS typeを「2) Windows」、backend typeを「1) WinRM」に設定して初期化します。

この初期化処理により、テスト実行に最低限必要なファイル群が生成されます。そのうちの1つであるspec_helper.rbの中には、対象サーバに接続するための設定が含まれます。そのため、初期化実行時の選択内容に応じてspec_helper.rbの中身が変わります。実際のテストコードは生成された「spec/<実行対象サーバ名>/」ディレクトリの中のxxx_spec.rbです。serverspec-initを実行するとデフォルトでApache HTTP Serverのテストコードであるhttpd_spec.rbがサンプルとして作成されます。

接続設定

先述した通り、Serverspecをリモートにあるサーバに対して実行する場合には接続設定が必要となります。

SSH接続の場合

SSH接続の場合には、セキュリティ面を考慮して、公開鍵認証での接続を利用することをお勧めします。

テスト対象のサーバに公開鍵認証での接続設定をした上で、Serverspec実行元サーバに対して以下の設定を実施します。まずServerspecを実行するユーザが、対象サーバにSSH接続する際に秘密鍵を読み込んで接続できるようにするため、SSH接続を設定します。Serverspecでは、初期化処理時に設定したホスト名をもとに対象サーバに接続します。そのため、そのホスト名でのSSH接続時に作成した秘密鍵を読み込んで接続を実行できるようSSHクライアントを設定します。たとえば、ホームディレクトリ以下の.ssh/configに以下のように記述します。

    Host server-01
      HostName 10.0.0.10
      Port 22
      User user1
      IdentityFile ~/.ssh/serverspec-key

こうすることで、Serverspecがspec/server-01ディレクトリ以下のテストコードを実行する際、server-01にuser1というユーザ名で~/.ssh/serverspec-keyという鍵ファイルを使って接続を試みることになります。また接続時にインタラクティブにパスワード入力を求められないよう、事前にパスフレーズなしで公開鍵認証できるように設定しておきます。

WinRM接続の場合

WinRMの場合、テスト実行対象のWindowsマシンにてwinrm qcコマンド等を利用してWindows Remote Managementサービスを起動し、WinRMの接続設定を実施します。

Serverspec実行元サーバには、WinRM接続用に追加でwinrmというgemパッケージをインストールします。

    $ sudo gem install winrm

serverspec-init実行時にWinRMで実行するように進めると以下のようなspec_helper.rbが作成されます。

    require 'serverspec'
    require 'winrm'

    include SpecInfra::Helper::WinRM
    include SpecInfra::Helper::Windows

    RSpec.configure do |c|
      user = <user name>
      pass = <password>
      endpoint = "http://<hostname>:5985/wsman"

      c.winrm = ::WinRM::WinRMWebService.new(endpoint, :ssl, :user => user, :pass => pass, :basic_auth_only => true)
      c.winrm.set_timeout 300 # 5 minutes max timeout for any operation
    end

ここにWinRM接続情報を記述することで、Windowsマシンに対して接続してテストを実行できます。

sudo実行設定

テスト実行対象サーバがLinuxマシンの場合、稼働状況の確認用のコマンドを実行する際に「sudo」をつけて実行する仕様となっています。そのため、先述の接続設定の時に指定したユーザに対してsudoの実行権限を付与する必要があります。テスト実行対象サーバ内で、以下の設定を実施します。

    # visudo

    user1   ALL=(ALL)       ALL

上記のように設定すると、sudo実行時にユーザのパスワードを聞かれます。パスワードについてはSUDO_PASSWORD環境変数に設定するか、環境変数にパスワードを設定したくない場合には、ASK_SUDO_PASSWORD環境変数を1に設定することでServerspec実行時にインタラクティブにパスワード入力を求められるようになります。

テストコードの作成

実行したいテストコードはserverspec-init実行時に作成されたフォルダの中に作成します。フォルダ構成としては以下のような形となります。

    Rakefile
    spec/
        - spec_helper.rb
        - server-01/
            - httpd_spec.rb
            - xxx_spec.rb
            ・・・・
        - server-02/
        ・・・・

Serverspecのテスト実行時の読み込みファイルの設定等は、Rakefileにて定義されています。標準のRakefileの記述では、specディレクトリ以下の各ディレクトリ内のxxx_spec.rbという名前のファイルが読み込まれて、順次テストが実行されるようになっています。

標準のRakefileの内容

    require 'rake'
    require 'rspec/core/rake_task'

    RSpec::Core::RakeTask.new(:spec) do |t|
      t.pattern = 'spec/*/*_spec.rb'
    end

    task :default => :spec

そのためテストコードを追加作成する場合は、xxx_spec.rbのような名前でファイルを作成して「spec/接続先サーバ名ディレクトリ/」以下に配置します。テストコードのファイルは、Webサーバやデータベースサーバ、セキュリティ設定関連のテストというように、テストの種類毎に分類して作成するなど、環境にあわせて管理しやすい形で分割することをお勧めします。またRakefileの内容を修正すれば、特定のディレクトリ以下のテストコードのみ実行するなどの応用も可能です。

テスト実行

Serverspecのテストを実行するためには、rakeコマンドを利用します。先述したRakefileにタスクが定義されています。rakeコマンドを利用することで、この記述に従ってテストコードの内容が実行されます。標準のRakefileには「spec」という名前のRakeタスクが定義されていて、これを実行することで先述した通りに各テストコードが実行される仕組みになっています。

    $ rake spec

また特定のテストファイルのみ実行したければ、rspecコマンドを利用して以下のようにテストファイルを指定します。

    $ rspec spec/server-01/httpd_spec.rb

Serverspecの出力は、デフォルトではテスト結果が正常なら「.」のみ、失敗した場合には「F」とその状況が表示される形式となっています。より詳細に実行結果を表示したい場合には、Serverspecの実行元サーバにてテストを実行するユーザのホームディレクトリ直下に以下のようなファイルを作成します。

    $ vim ~/.rspec
    --format documentation

これにより、テスト結果の出力は以下のようになります。

    $ rake spec

    Package "python"
      should be installed

    Finished in 0.24518 seconds
    1 examples, 0 failures

テストコードの記述方法

テストコードは「ResourceType」と「Matcher」の2つの要素から成り立っています。

ResourceType

ResourceTypeはテストの対象となるリソースを定義します。標準でサポートしているResourceTypeはこちらにまとめられています。サーバ内のファイルやプロセス、パッケージなどがResourceTypeに相当します。

Matcher

MatcherとはResourceTypeの状態を定義するもので、ResourceTypeの種類毎にサポートされるMatcherは異なります。ResourceType一覧のページに、各Matcher毎の例がまとめられています。

テストコードの書式

テストコードの書式は以下のようになります。

例1:「◯◯◯が△△△であるべきである」という形のテスト

具体例として、「pythonというパッケージ」が「インストールされているべきである」というテストコードは以下のようになります。

    require 'spec_helper'

    describe package('python') do
      it { should be_installed }
    end

例2:「◯◯◯の□□□が△△△であるべきである」という形のテスト

具体例として、「server-01というホスト」の「IPアドレス」が「10.1.1.2であるべきである」というテストコードは以下のようになります。

    require 'spec_helper'

    describe host('server-01.example.com') do
      its(:ipaddress) { should eq '10.1.1.2' }
    end

このようにServerspecのテストコードはRSpec形式の記法となっており、テストしたい内容を非常に読みやすい形で記述できます。

まとめ

ServerspecはChefやAnsibleによる自動処理とセットで活用することで、非常に効果を発揮するテストフレームワークです。迅速で正確な環境セットアップに欠かせないものではないでしょうか。内部の仕組みや利用方法についても、本記事で紹介したように非常にシンプルになっています。「構築したつもりだったが設定漏れがあり、障害の原因となった」といった苦い経験をされたことがある方は、ぜひお試しいただくことをお勧めします。今回は、仕組みや基本的な利用の流れについて触れました。次回は、より具体的なケースをもとにテストコードの書き方を解説します。

TIS株式会社

Twitter : @ike_dai
TIS株式会社OSS推進室所属。社内向けシステムの保守運用業務を経験後、クラウド時代の効率的な統合運用管理をテーマに活動中。特に、OSSを駆使した運用のエコシステム実現を目指し、Zabbix,fluentd,Serverspec,Ansibleなどの導入や検証に取り組む。技術検証成果などを技術ブログ『Tech-Sketch』にて発信中。著書:『Zabbix統合監視徹底活用 - 複雑化・大規模化するインフラの一元管理』

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています