読者です 読者をやめる 読者になる 読者になる

nigoblog

スタートアップのCMOブログ

AWS ELB配下にあるインスタンスに対しserverspecでテスト・管理する方法

serverspecはよく使っているのですが、ある状態をチェックしたいとき、その自動化に役立ちます。

例としてcrondやnginxのステータス確認などがそれにあたるかと思います。
これまでは

ssh '対象ホスト'
/etc/init.d/crond status

のようにやっていました。
これもサーバーが1台であったり、確認することが1つであればそこまでめんどくさくはないのですが、複数台で、複数のことがらをチェックするのは結構大変です。

なので自動化したい!っていうことなのですが、単純な使い方ならこちらの記事を参考に
一番参考になるのはなんだかんだで公式ドキュメント!DevOpsのためにチェックすべきドキュメント3選! - nigoblog
serverspec - Home

serverspecは基本的に

~/.ssh/config

の内容を参考に動きます。
その際、ELB配下で固定IPをつけていない場合、いちいちIPを見に行かなければなりません。

今回そのめんどくさい部分を解決したserverspecの使い方をしているのでその紹介をします。

流れとしては

  1. aws-sdkで動的にインスタンスのIPを取得
  2. ssh/configで各インスタンス共通のログイン情報を作成
  3. インスタンスのspecを作成
  4. serverspecでELBの場合どうするか記述する

このような流れとなります。

aws-sdkで動的にインスタンスのIPを取得

これはよくcapistranoでELB配下のインスタンスにデプロイをする際によく使われます。
これの使い方は簡単で
Ruby - CapistranoでELB配下のEC2インスタンスを取得してデプロイ - Qiita [キータ]
こちらの記事を参考にしました。
serverspecで使う場合、

cd serverspec
bundle init

でserverspecディレクトリにGemfileを作成
Gemfileには

gem 'aws-sdk'

と追加し

bundle install

を行う。
次にspec/spec_helper.rbを開いて

require 'serverspec'
require 'pathname'
require 'net/ssh'
require 'aws-sdk'

これでaws-sdkを読み込み、

RSpec.configure do |c| 
  AWS.config({
    :access_key_id => '<アクセスキー>',
    :secret_access_key => '<シークレットキー>',
    :ec2_endpoint => 'ec2.ap-northeast-1.amazonaws.com',
    :elb_endpoint =>'elasticloadbalancing.ap-northeast-1.amazonaws.com'
  })  
  elb       = AWS::ELB.new.load_balancers['<ELBの名前>']
  instances = elb.instances.select {|i| i.exists? && i.status == :running}.map(&:dns_name)

するとinstancesにELB配下インスタンスのIPが配列として代入されます。
まず第一段階のELB配下インスタンスのIPを動的に取得しました。

ssh/configで各インスタンス共通のログイン情報を作成

次のステップは一旦serverspecとは離れ、ssh/configの設定を行います。

vim ~/.ssh/config

を次のように編集します。

Host ELBの名前
    User <sshで入るユーザー名>
    IdentityFile <鍵のパス>

一応ELBの名前としておりますが、実際はなんでも構いません。
第二段階もこれでオッケー

インスタンスのspecを作成

ここはまずひとつはベタで作成し、それをコピーしていく形が理想です。
最終的にはspecディレクトリが

spec/
    spec_helper.rb
    ELBの名前_0/
    ELBの名前_1/
    ...
    ELBの名前_N-1/

のようになっていると良いでしょう。(Nはインスタンスの数)
まずELBの名前_0というディレクトリに基本のspecを書きます。
次にどんどんコピーしていきます。(ここだけ手動でイケてないので改善していこうかと)

cp -r ELBの名前_0 ELBの名前_1
...
cp -r ELBの名前_0 ELBの名前_N-1

これで第三段階も終了

serverspecでELBの場合どうするか記述する

というわけで最後にもう一回spec_helper.rbを編集します。

    host  = File.basename(Pathname.new(file).dirname)
    if c.host != host
      c.ssh.close if c.ssh
      c.host  = host
      options = Net::SSH::Config.for(c.host)
      user    = options[:user] || Etc.getlogin
      c.ssh   = Net::SSH.start(c.host, user, options)
    end 

まずデフォルトのserverspecはこのようになっております。
ここに先ほど取得したインスタンス情報を反映させます。

    host  = File.basename(Pathname.new(file).dirname)
    if c.host != host
      c.ssh.close if c.ssh
      # ELB対応
      if host =~ /<ELBの名前>*/
        host_num = File.basename(Pathname.new(file).dirname)[-1].to_i
        c.host = "<ELBの名前>"
        options = Net::SSH::Config.for(c.host)
        options[:host_name] = instances[host_num]
      else
        c.host  = host
        options = Net::SSH::Config.for(c.host)
      end 
      c.ssh   = Net::SSH.start(c.host, user, options)
  end

すると自動でELB配下のインスタンスIPが取得され、serverspecのテストを実行することができます。

というわけで以上ELB配下のインスタンスにserverspecを適用する方法でした!
いろいろ改善点はありますが、ひとまず動くところまで!