Vagrant+Puppet+Serverspec を使って Windows Server をテスト駆動開発する
インフラ構築・運用をコーディングで行うことは珍しい事ではなくなってきていますが、Windows Server の開発記事はあまり見受けられません。しかし、現場では Windows Server を使っている人も多いと思います。私の職場でも Windows Server の案件が多いです。
そこで3連休を利用して、 Vagrant , Puppet , Serverspec を使った Windows Server のテスト駆動開発環境を作ってみましたのでシェアします。
開発マシンは Mac OSX です。開発マシンの内容は以下の通りです。事前に VirtualBox と Vagrant ,Serverspec が使えるようにしておいて下さい。
ソフト | バージョン |
---|---|
開発OS | OS X 10.9.4 |
ゲストOS | Windows Server 2012 |
Vagrant | 1.6.5 |
Vagrant Plugin | puppet(3.7.0) / vagrant-windows (1.6.0) |
Serverspec | 1.16.0 |
VirtualBox | 4.3.12 |
Microsoft Remote Desktop Connection for Mac | 2.3.6 |
winrm (gem) | 1.2.0 |
作業の流れ
本稿の流れを以下にまとめます。
順番 | 内容 |
---|---|
1 | Vagrant で操作できる Windows Server の Box を作成する |
2 | Vagrant の設定をする |
3 | Serverspec でテストを先に実行する |
4 | Puppet を実行する |
5 | 再びテストを実行する |
1.Vagrant で操作できる Windows Server の Box を作成する
開発で使用する Vagrant Box を作成します。manifest 開発中はこの Box を使って何度も Windows Server を壊したりしますので作っておくと楽ですね。
Windows Server の設定をする
設定はこちらのサイトを参考に行いました。
[Vagrant] WindowsServer2012R2のBoxを自作する #Vagrant - NAVER まとめ
まずは Vagrant から Windows Server を操作できるように Windows Server を設定します。VirtualBox に Windows Server 2012 インスタンスを普通にインストールします。
リモートディスプレイのサーバを有効化する
開発マシンからリモートデスクトップ(RDP)できるようにリモートディスプレイのサーバを有効にします。インストールした仮想インスタンスを右クリックし設定を開きます。
[ディスプレイ] を選択し中央ペインの [リモートディスプレイ(R)] を選択します。[サーバを有効化する] にチェックを入れます。
パスワードの複雑性を無効化
インストールした Windows Server にログインし PowerShell を管理者権限で実行します。参考のサイトに習って以降の設定はすべて PowerShell で行います。
パスワードの設定ポリシーをファイルにエクスポートします。エクスポートしたファイルの値をノートパッドで開き以下のように修正し再びポリシーを適用します。(Import LocalSecurityPolicy.cfg after changing)
MaximumPasswordAge = -1 PasswordComplexity = 0
vagrant ユーザを作る
Vagrant は 仮想インスタンスへアクセスする際、vagrant ユーザでログインします。なので vagrant Box には予め vagrant ユーザを作成しておきます。
vagrant ユーザを Administrator グループに追加する
リモート管理(WinRM)を有効化する
テストツールの Serverspec は WinRM を使って Windows Server に PowerShell を実行しテストを行います。 WinRM が使えるように設定しておきます。その他、開発マシンから Windows Server にアクセスできるよう設定しておきます。
Firewall のルールを設定する
全ての端末から WinRM ポートにアクセスできるよう設定します。
Puppet のインストール
Puppet Lab から Windows 用の Puppet をダウンロードしインストールします。Windows 用 Puppet はこちらからダウンロードできます。
https://downloads.puppetlabs.com/windows/
今回は puppet-3.7.0-x64.msi をダウンロードしました。
インストールは簡単です。全てデフォルトのままで [ Next ] 連打でOKです。ちなみにコマンドを使えばサイレントインストールも出来ます。サイレントインストールのやり方は公式ページに記載されています。
自動ログインを有効にします
インスタンスを起動した際、自動で vagrant ユーザがログインできるようにしておきます。
VirtualBox Guest Addtionsのインストール
こちらも忘れずにインストールしておきましょう。
設定した Windows Server の Box を作成する
ここまでの設定で Vagrant から操作できる Windows Server が出来上がっています。設定した Windows Server を用途に合わせて柔軟に使えるように VirtualBox からエクスポートし Vagrant Box にしたいと思います。
Box 用のディレクトリを作成する
Windows Server をシャットダウンしておきます。VirtualBox から Windows Server をエクスポートしておくためのフォルダを用意します。フォルダは任意です。
$ mkdir -p vagrant-boxfiles/windows2012
VirtualBox からエクスポートする
作成したフォルダに VirtualBox からエクスポートしたファイルを保存します。
$ vagrant package --base "windows-for-vagrantbox_default_1410696984773_58536" --output /Users/[User]/Documents/vagrant-boxfiles/windows-for-vagrant.box
--base windows-for-vagrantbox_default_1410696984773_58536 は先ほど設定した Windows Server のインスタンス名を入れて下さい。 --output にエクスポート用に作成したフォルダパスと box 名を指定します。box ファイルの名前は任意です。分かりやすい名前をつけましょう。
Vagrant に登録する
エクスポートした box を vagrant に登録します。
$vagrant box add "windows2012-puppet" /Users/[User]/Documents/vagrant-boxfiles/windows-for-vagrant.box
add のあとは Box の論理名です。エクスポートしたファイル名と同じじゃなくても OK です。こちらも分かりやすい名前をつけるといいでしょう。ちなみに、私は " puppet が使える Windows Server 2012 " なので " windows2012-puppet "という Box 名前にしました。
2.Vagrant の設定をする
vagrant から Windows Server にアクセスできるように Vagrantfile を作成します。まずは、作業用のフォルダを用意しましょう。任意の場所にフォルダを作成します。
$ mkdir -p /Users/[User]/Documents/vagrant/windows-puppet
Vagrantfile を作成する
先ほど作成したフォルダに移動します。
$ cd /Users/[User]/Documents/vagrant/windows-puppet
移動したフォルダで以下のコマンドで Vagrantfile のひな形を作成します。
$ vagrant init
すると Vagrantfile が生成されます。 Windows Server インスタンスにアクセスできるように Vagrantfile を以下の内容に変更します。
RDP で接続する
ここまで設定が完了すると Vagrant から Windows Server を操作する事が可能になってます。vagrant up コマンドで Windows Server を起動し、RDP でアクセスしてみましょう。
$ vagrant up WARNING: Could not load IOV methods. Check your GSSAPI C library for an update WARNING: Could not load AEAD methods. Check your GSSAPI C library for an update Bringing machine 'default' up with 'virtualbox' provider... ==> default: Clearing any previously set network interfaces... ==> default: Preparing network interfaces based on configuration... default: Adapter 1: nat ==> default: Forwarding ports... default: 3389 => 3389 (adapter 1) default: 5985 => 5985 (adapter 1) default: 22 => 2222 (adapter 1) ==> default: Booting VM... ==> default: Waiting for machine to boot. This may take a few minutes... ==> default: Machine booted and ready! Sorry, don't know how to check guest version of Virtualbox Guest Additions on this platform. Stopping installation. ==> default: Checking for guest additions in VM... ==> default: Mounting shared folders... default: /vagrant => /Users/[User]/Documents/Vagrant/windows-for-vagrantbox ==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision` ==> default: to force provisioning. Provisioners marked to run always will still run.
共有フォルダも作成されています。Vagrant から Windows Server をうまく起動出来ました!早速アクセスしてみましょう。
ちなみに Vagrant 1.6 から Windows Guests を正式にサポートしたそうです。vagrant rdp コマンドで直接 rdp クライアントを起動できるようになりました。
$ vagrant rdp
と実行してみてください。RDP が起動します。
ここまで出来たら、puppet も Serverspec も動きます。
3.Serverspec でテストを先に実行する
テスト駆動 Windows をやってみたいと思います。今回は簡単に「 C ドライブの直下にファイルを作成する」お題でテスト駆動してみたいと思います。
要件 |
---|
Puppet を使って Cドライブ直下に test.txt を配置する |
マニフェストを書く前に先にテストを書きます。 C ドライブ直下に test.txt が存在している事をテストに書いて実行し、わざと失敗します。
Serverspec の環境を整える
Vagrantfile を作成したフォルダで serverspec-init を実行しテストのひな形を生成します。
$ serverspec-init
設定内容を対話形式で聞かれるので答えていきます。
Select OS type: 1) UN*X 2) Windows Select number: 2 Select a backend type: 1) WinRM 2) Cmd (local) Select number: 1 + spec/ + spec/localhost/ + spec/localhost/httpd_spec.rb + spec/spec_helper.rb + Rakefile
最初の質問には、対象は Windows OS なので 2 を。次の質問は WinRM で接続するので 1 を回答します。 すると spec フォルダが生成されます。spec フォルダ直下の spec_helper.rb の編集を行います。spec_helper.rb は接続先の情報が記載されています。
以下の内容で編集して下さい。
require 'serverspec' require 'winrm' include SpecInfra::Helper::WinRM include SpecInfra::Helper::Windows RSpec.configure do |c| user = "vagrant" pass = "vagrant" endpoint = "http://127.0.0.1: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
Serverspec は Windows Server へ WinRM で接続します。 gem ライブラリの winrm をインストールしておいて下さい。
gem install winrm
テストを書く
serverspec-init を実行した時点でテストファイルがすでに生成されています。
spec/localhost/httpd_spec.rb
httpd 用のテストファイルです。必要ありませんので削除しておきましょう。
rm -rf spec/localhost/httpd_spec.rb
代わりに httpd_spec.rb のあった場所に filecheck_spec.rb というテストファイルを作成します。要件に沿って「C:¥test.txt が存在している事」と記述します。
ちなみに、テストファイルの命名規則は spec.rb です。ファイル名の語尾に spec.rb とあると Serverspec がテストファイルと認識できます。
require 'spec_helper' describe file('C:/test.txt') do it { should be_file } end
早速実行してみましょう。
rake spec
当然、結果はエラーになりました。
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby -S rspec spec/localhost/filecheck_spec.rb WARNING: Could not load IOV methods. Check your GSSAPI C library for an update WARNING: Could not load AEAD methods. Check your GSSAPI C library for an update F Failures: 1) File "c:/test.txt" should be file On host `` Failure/Error: it { should be_file } $exitCode = 1 $ProgressPreference = "SilentlyContinue" try { $success = (((Get-Item -Path "c:/test.txt" -Force).attributes.ToString() -Split ', ') -contains 'Archive') if ($success -is [Boolean] -and $success) { $exitCode = 0 } } catch { Write-Output $_.Exception.Message } Write-Output "Exiting with code: $exitCode" exit $exitCode You cannot call a method on a null-valued expression. Exiting with code: 1 #< CLIXML <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><S S="Error">Get-Item : Cannot find path 'C:\test.txt' because it does not exist._x000D__x000A_</S><S S="Error">At line:5 char:17_x000D__x000A_</S><S S="Error">+ $success = (((Get-Item -Path "c:/test.txt" -Force).attributes.ToString() -S_x000D__x000A_</S><S S="Error">pli ..._x000D__x000A_</S><S S="Error">+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~_x000D__x000A_</S><S S="Error"> + CategoryInfo : ObjectNotFound: (C:\test.txt:String) [Get-Item], _x000D__x000A_</S><S S="Error"> ItemNotFoundException_x000D__x000A_</S><S S="Error"> + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetIt _x000D__x000A_</S><S S="Error"> emCommand_x000D__x000A_</S><S S="Error"> _x000D__x000A_</S></Objs> expected file? to return true, got false # ./spec/localhost/filecheck_spec.rb:4:in `block (2 levels) in <top (required)>' Finished in 0.54846 seconds 1 example, 1 failure Failed examples:
Cドライブ直下に test.txt を配置するマニフェストを書きたいと思います。
4.Puppet を実行する
テストを通すために Puppet を実行できる環境にします。Puppet は Vagrant と同じタイミングで実行されるようにします。例えば、 vagrant up 、vagrant provision した時、仮想インスタンスが起動したあと自動で Puppet が実行されるようにします。
Vagrantfile を修正する
vagrant provision が使えるように Vagrantfile に以下の内容を追記します。「 Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 」セクションの中に書きます。
config.vm.define :windows do |c| c.vm.provision "puppet" do |puppet| puppet.manifest_file = "default.pp" end end
※ Vagrantfile 全体
# -*- mode: ruby -*- # vi: set ft=ruby : # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = "Windows2012-puppet" config.vm.guest = :windows config.vm.communicator = "winrm" config.vm.network :forwarded_port, guest: 3389, host: 3389 config.vm.network :forwarded_port, guest: 5985, host: 5985, id: "winrm", auto_correct: true config.vm.define :windows do |c| c.vm.provision "puppet" do |puppet| puppet.manifest_file = "default.pp" end end end
マニフェストを書く
マニフェストを格納するフォルダを作成しマニフェスト( default.pp )を作成します。マニフェストを格納するフォルダは必ず「 manifests 」にします。
mkdir manifests
マニフェストファイルを作成します。マニフェストファイル名は任意ですが Vagrantfile の記述と同じにします。
vi manifests/default.pp
Cドライブ直下に test.txt を生成するマニフェストを書きます。
file { "C:\\text.txt": content => "aaaa" }
マニフェストを実行します。
vagrant provision
WARNING: Could not load IOV methods. Check your GSSAPI C library for an update WARNING: Could not load AEAD methods. Check your GSSAPI C library for an update ==> windows: Running provisioner: puppet... Running Puppet with /tmp/vagrant-puppet-2/manifests/default.pp... Notice: Compiled catalog for win-r2elstmlcu3 in environment production in 0.25 seconds Notice: Finished catalog run in 0.02 seconds
無事、マニフェストが実行されました。 vagrant rdp で Windws Server にログインし、 test.txt ファイルが生成されている事を確認します。
生成されていますね!!やった!
5.再びテストを実行する
ファイルが生成されたのでもう1度テストを実行します。
rake spec
Finished in 0.62373 seconds 1 example, 1 failure Failed examples:
失敗しました。なんでだろう。もう1度マニフェストを確認してみます。
file { "C:\\text.txt": content => "aaaa" }
なるほど。text.txt と誤植してしまってました。test.txt に修正してもう1度 vagrant provision します。その後、rake spec します。
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby -S rspec spec/localhost/filecheck_spec.rb WARNING: Could not load IOV methods. Check your GSSAPI C library for an update WARNING: Could not load AEAD methods. Check your GSSAPI C library for an update . Finished in 0.41336 seconds 1 example, 0 failures
テスト通りました!やった。
※これ演出ではなく、本当に誤植してました。早速、テスト駆動開発の大切さを実感しました。
最後に
このように Windows Server でもテスト駆動開発ができる事を確認できました。あとは現場でどう運用していくかだと思います。本当に Puppet , Chef を使った Infrastructure as Code が現場のスタイルに合っているのか見極めていきたいと思います。
感謝
この記事を沢山の方がリツートしていただきました。ありがとうございます。
Vagrant+Puppet+Serverspec を使って Windows Server をテスト駆動開発する に関するツイート