こんにちは、おじさん技術者の iga です。
とあるサーバ更改のプロジェクトで、「Serverspecを使った設定チェック」を実施しましたので、その時のノウハウを共有したいと思います。
設定チェックの対象となるサーバが全部で約600台あり、あるサーバ種別で最大282台と大規模なため、人力でのチェックはあきらめていました。
そこで利用したのが、Serverspecです。
Serverspecとは
Serverspecは、サーバの状態をコードにより自動的にテストするためのツールです。
公式サイトはこちらです。
http://serverspec.org/
Ruby製のテストフレームワークRSpecがベースになっていて、抽象化したコードによってサーバの状態を確認するようになっています。
システム構成の概要
今回のテスト対象のシステム構成は、次の図のようになっています。
メンテナンス端末にServerspecをインストールして、メンテナンス端末からテスト対象のサーバにsshで接続できるようになっています。
今回のカスタマイズ方針
サーバの台数は多いのですが、同じ役割のサーバが数十台で構成されるとなっているため、Serverspec標準のテスト方法だと、同じチェック内容を大量に複製する必要があります。
標準のフォルダ構成だと、以下の図のようになります。
「websvr001」と「websvr002」に同じテストを適用していますが、台数が少ない間はファイルのコピーやシンボリックリンクで対応できますが、台数が増えると管理しきれなくなります。
そこで、複数サーバでテストを共有するため、公式サイトにも記述されている「How to share Serverspec tests among hosts」という手法を取りました。
サーバ種別ごとにテスト対象を配置することで、同じサーバ種別で台数が増えても、管理できるようになりました。
ただ、サンプルで記述されている内容だと、以下のような問題があってそのまま適用できませんでした。
1) 対象サーバが固定で記述されている。
→台数が多いので、テスト時間短縮のためあるサーバ種別だけテストしたい、ということができない。
2) サーバ種別(Role)をサーバ名から取得するようになっている。
→サーバ名が命名規則に沿ってつけられているが、サーバ種別が分かりやすい名前になっていない。
→記述ミスがあると実行の無駄になってしまうので、分かりやすい記述にしたかった。
今回対応するカスタマイズ内容
今回の対応(以降、Role対応 と記載します)では、以下のことを行いました。
1) 対象サーバとサーバ種別は、JSON形式の外部ファイルに用意しておく。
2) サーバ種別は、分かりやすい名前にしておく。
具体的なカスタマイズ内容
Role対応するため、Rakefileを次のように修正しました。
Rakefileは、Serverspecが自動生成した、テストを実行する処理が記述されたファイルになります。
まずは実行対象のサーバ情報を取り出すところです。
標準のRakefile
targets = [] Dir.glob('./spec/*').each do |dir| next unless File.directory?(dir) target = File.basename(dir) target = "_#{target}" if target == "default" targets << target end
標準ではspecフォルダ配下のフォルダが実行対象サーバ名(またはIPアドレス)になっているので、それを読み込んで実行対象(target)に保存しています。
Role対応版Rakefile
# 環境変数から使用するIPアドレスリストを取得 rolePath = ENV['SERVERSPEC_ROLE'] # specフォルダ配下のrole名を取得する roles = [] Dir.glob('./spec/*').each do |dir| next unless File.directory?(dir) target = File.basename(dir) roles << target end # role別のIPアドレスリストをjsonファイルから読み込む all_addrs = [] targets = [] roles.each do |role| ip_addrs = get_ipaddr(rolePath, role) if ip_addrs all_addrs.concat(ip_addrs) ip_addrs.each do |ip_addr| targets << {:role => role, :addr => ip_addr} end end end
Role対応では、specフォルダ配下のフォルダがサーバ種別になっているので、それを読み込みます。
そして、JSONファイルからサーバ種別と実行対象サーバ名を読み込んで、specフォルダ配下にサーバ種別が定義されている場合に、サーバ種別と実行対象サーバ名をtargetに保存しています。
読み込むJSONファイル名は、環境変数「SERVERSPEC_ROLE」から取得しています。
JSONファイルのフォーマットは、次のようにしています。
サーバ種別をキーとして、実行対象サーバ名を配列として定義しています。
{ "websvr" : [ "websvr001", "websvr002" ], "appsvr" : [ "appsvr001" ], "dbsvr" : [ "dbsvr001" ] }
テストの実行部分は次のようになります。
標準のRakefile
targets.each do |target| original_target = target == "_default" ? target[1..-1] : target desc "Run serverspec tests to #{original_target}" RSpec::Core::RakeTask.new(target.to_sym) do |t| ENV['TARGET_HOST'] = original_target t.pattern = "spec/#{original_target}/*_spec.rb" end end
標準では、targetに入っているサーバ分、テストを実行しています。
Role対応版Rakefile
targets.each do |target| original_role = target[:role] original_target = target[:addr] begin desc "Run serverspec tests to #{original_target}" RSpec::Core::RakeTask.new(original_target.to_sym) do |t| ENV['EXEC_TIME'] = Time.now.strftime("%Y%m%d_%H%M%S").to_s ENV['SERVER_KIND'] = original_role ENV['TARGET_HOST'] = original_target t.pattern = "spec/#{original_role}/*_spec.rb" t.fail_on_error = false end rescue => ex puts ex.message end end
Role対応では、標準と同じくtargetに入っているサーバ分、テストを実行しています。その際、specフォルダ配下のサーバ種別をtargetに格納したroleから決定するようにしています。
Role対応の結果、サーバ種別「websvr」に対してテストする場合、次のように実行します。
$ export SERVERSPEC_ROLE=addr/websvr.json $ rake spec
まとめ
今回、初めてServerspecを利用しましたが、その威力に感動しました。
これまでは、サーバの設定確認といえば目視やdiffを使った差分確認など、人の注意力に頼った作業をやっていました。
それでは設定ミスを見落としたことが、後になって見つかって大変になる、ということをやってきました。
Serverspecを利用すると、設定のOK/NGが分かりやすく出てくるため、NG理由を確認するのも設定ミスなのかテスト内容のミスなのか、切り分けが容易になりました。
それでは!
Acroquest Technologyでは、キャリア採用を行っています。
- ビッグデータ(Hadoop/Spark、NoSQL)、データ分析(Elasticsearch、Python関連)、Web開発(SpringCloud/SpringBoot、AngularJS)といった最新のOSSを利用する開発プロジェクトに関わりたい。
- マイクロサービス、DevOpsなどの技術を使ったり、データ分析、機械学習などのスキルを活かしたい。
- 社会貢献性の高いプロジェクトや、顧客の価値を創造するようなプロジェクトで、提案からリリースまで携わりたい。
- 書籍・雑誌等の執筆や、対外的な勉強会の開催・参加を通した技術の発信、社内勉強会での技術情報共有により、エンジニアとして成長したい。
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
急増するDevOps案件の中でスキルを活かしたいエンジニア募集! - Acroquest Technology株式会社のインフラエンジニア中途・インターンシップ・契約・委託の求人 - Wantedlywww.wantedly.com