2015年3月27日金曜日

JenkinsとServerspecでサーバーの設定ファイルを定期監視する

このエントリーをはてなブックマークに追加 はてなブックマーク - JenkinsとServerspecでサーバーの設定ファイルを定期監視する

既に動いているサーバーの運用をしていて

  • 検証環境と本番環境で設定ファイルが意図せず違うことがある(DB接続先が違うというものではなく)
  • 運用でよく使う設定(cron,logrotate)の設定方針がサーバーで違う

など辛かったので、状況を改善するためにまずはJenkinsとServerspecを使って設定ファイルの定期監視をするようにしました。(本来はそのような状況になっていないべきですが、既にその状況になっている場合にはまず現状把握が大事かと個人的には思いました)

具体的には

  • Serverspecで指定された場所にファイルがあるか確認する(cronであればanacronなのか/etc/cron.dなのか/var/spool/なのか)
  • 設定ファイルをGitlabに登録し、実サーバーのファイルと同一であるか確認する(md5sum)

ということをJenkinsのデイリーで実行するジョブを設定するようにしてみました。

実際に行うと

  • SSHしなくても設定ファイルがどのようになっているかをGitlab(ブラウザ)から見れるように可視化
  • cronなどの設定がサーバーごとにどこにされているか分かるようにする
  • Gitlabに登録されたファイルと違う場合にはエラーとしてすぐに検出し、誰が何のために変えたかを確認し、Gitlabに登録することで履歴を追えるようにする

というメリットが得られました。

本サンプルではJenkinsを動かすサーバーとServerspecで検証するサーバーを同じにしていますが、実際はServerspecで検証したいサーバーは別ホストになると思うので適宜置き換えてください。

サンプル

toshihirock/MonitorFiles

とりあえず構成みたい、最終的にどんな感じになるか確認する場合にはどうぞ。

準備

各サーバーから設定ファイルを取得してGitlabに登録しておきます。検証、本番でファイルが違う場合にはフォルダを分けるなどして管理とします。

例えばnginxの設定ファイルが各環境で違う場合

nginx/
├── development
├── production
└── verification

という風にします。トップディレクトリをverificationなどの環境にしてその下にサービスごとにディレクトリを切ってもいいでしょうし、状況に応じて構成を変更すれば良いと思います。

差分が少ないファイル(DB接続先が違うだけなど)であれば管理するファイルは一つにしてServerspecのテスト内でファイル内の文字列を置き換える方式でも良いかもしれません。

Jenkinsのインストール

Jenkinsをインストールします。

  • インストール方法はJenkinsの公式サイト参照
  • JenkinsのGit Pluginをインストールしておくこと

また、Jenkinsユーザーになり、以下のコマンドでbundleは入れておきます。

$gem install bundle

Serverspecのテストを書く

設定ファイルが登録してあるリポジトリでServerspecのテストを書きます。書くテストコードですが、ServerspecはRubyなので以下のようにすることで設定ファイルが同一か確認出来きます。

require 'spec_helper'
require 'digest/md5'

describe file('/etc/nginx/nginx.conf') do
  its(:md5sum) { should eq Digest::MD5.file('nginx/nginx.conf').to_s }
end

パスの設定部分(nginx/nginx.conf)は適宜環境に合わせて変更してください。

Jenkinsで実行する

ジョブを作って以下のようなスクリプトを実行することでServerspecを実行できます。

bundle install
bundle exec rake

ただ、この状態だとJenkinsの出力結果で色がつかず見ずらいので、設定変更およびJenkinsプラグインを使って見やすくします。

まず、以下2つのJenkinsプラグインをインストールします。

次にJenkinsの設定でURL of theme CSSと記載されている場所にhttp://jazzzz.github.io/jenkins-black-console/black-console.cssというコンソールの背景を黒くするCSSを指定します。

また、対象のジョブの設定のColor ANSI Console Outputで任意のAnsiColorMapを指定します。

Jenkins側の設定はこれで終わりなのですが、Serverspec側でも.rspecファイルに–ttyを追加してRspec実行時にttyオプションを有効化します。

--color
--format documentation
--tty

これでジョブを実行すればいい感じに色付きで見えるようになります。

XMLでのテスト結果の出力を行う

通常出力されるメッセージでもわかりやすいのですが、せっかくJenkinsを使っているのでより見やすいようにXML出力して、グラフ形式などで見れるようにしようと思います。

RSpec(Servespec)ではXML出力は出来ないのでrspec_junit_formatterを利用します。

$echo "gem 'rspec_junit_formatter'" >> Gemfile
$bundle install

これでbundle exec rake –format RspecJunitFormatter –out reports/result.xmlとするとXML出力は可能です。ただし、CLIから実行するとホストが複数ある時に後で実行した結果をresultx.xmlで上書きするので、一つのホストの結果しか見えません。なので、Rakefileでホスト名ごとに結果のxmlを出力するようにします。また、ローカルで確認する場合にはXML出力でなく、documentフォーマットで見たいのでRakefileでは環境変数JENKINSが設定されている場合のみ、XML出力するようにします。

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

task :spec    => 'spec:all'
task :default => :spec

namespace :spec do
  targets = []
  Dir.glob('./spec/*').each do |dir|
    next unless File.directory?(dir)
    target = File.basename(dir)
    target = "_#{target}" if target == "default"
    targets << target
  end

  task :all     => targets
  task :default => :all

  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"
      t.rspec_opts = "--format RspecJunitFormatter --out reports/#{target}.xml" if ENV['JENKINS']
    end
  end
end

上記変更後、Jenkinsのジョブで以下のように指定することでreports配下にホストごとの結果が出力され、以下のような形で確認できるようになります。

export JENKINS=True
bundle install
bundle exec rake

あとはJenkinsのジョブ設定のPublish JUnit test result reportのTest reports XMLsで「reports/*.xml」と指定すればOKです

終わりに

とりあえずこの対応を行っていくことで現状のサーバーの状況を確認、監視できるようになります。また、現状はChefAnsibleなどのコードのインフラ化は進んでいないのですが、これをもっと細かく作成できれば安心してコードによるインフラ構成管理に進めるような気がします。

2015年3月17日火曜日

cron(anacron)について確認した時のメモ

このエントリーをはてなブックマークに追加 はてなブックマーク - cron(anacron)について確認した時のメモ

ドキュメントの全くないサーバーで定期処理(cronやanacron)を調べるときに知らないことが多く、苦労したのでメモ

確認シェル作ってみた

cron(anacron含む)はとにかく書き方が色々ありすぎて、どこに何が書いてあるか分からないので、一覧である程度出すものを作りました。

知らないサーバーに入ってcronで何をやっているか把握するときはこれを使うとだいたいの内容が分かるはず!

  • /etc/crontabの内容を出力
  • /var/spool/crontab/[user-name]の内容を出力
  • /etc/cron.d/“ ”/etc/cron.hourly/“ ”/etc/cron.daily/“ ”/etc/cron.weekly/“ ”/etc/cron.monthly/

実行するとこんな感じです。

**************/etc/crontab****************
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed

***********************************

**************/var/spool/cron/ec2-user****************
0 * * * * echo "hello" >> /tmp/hello
***********************************

**************/var/spool/cron/root****************
0 * * * * echo "hello" >> /tmp/hello
***********************************

**************/etc/cron.d/****************
0hourly  raid-check  update-motd
***********************************

**************/etc/cron.hourly/****************
0anacron  test
***********************************

**************/etc/cron.daily/****************
logrotate  man-db.cron  tmpwatch
***********************************

**************/etc/cron.weekly/****************
***********************************

**************/etc/cron.monthly/****************
***********************************

結局どうするのが良さそうか

  • 正確な時間でやりたい場合にはcron
  • daily以上の処理でかつ処理の依存関係がないものや実行時間にバラツキをもたせたい場合にはanacron。例えばバッチ実行した処理結果をDBに書くなどの場合にバッチ実行サーバーが多くあるとある時間帯に一斉にDB書き込みが走るのでそれを分散させる場合など。/etc/rc.d/hourlyはanacronではない点は注意
  • 内容にもよるがcronの設定をする場合、/etc/cron.d/配下にファイルを置くやり方の方がファイルを分けることができ、crontab -rを押して内容を消してしまう恐れもないのでベターだと思う
  • ユーザー毎に設定するものはcrontab -eで設定する(事前にバックアップはとる事)
  • cronの標準出力や標準エラー出力は捨てずにログに出力すること

記述の仕方

  • /etc/crontabに記載
  • crontab -eコマンドを使う
  • /etc/cron.d/配下にファイルを配置
  • anacronを使う

/etc/crontabに記載する

  • /etc/crontabファイルに記載した内容を定期実行する

crontab -eコマンドを使う

/etc/cron.d/配下にファイルを配置

  • /etc/cron.d/配下にファイルを置いておくと指定された時間に処理を実行する
  • crontabと違ってサービス(アプリ)ごとにファイルを分けることができるので見やすくできる
  • 後述するanacronのhourly設定のような/etc/cron.hourlyのジョブは/etc/cron.d/0hourlyによって実行されており、anacronの特徴である遅延処理などがない。デフォルトの場合、毎時0分に実行されるようになっている

anacronを使う

  • RHEL 6のジョブスケジューラ「anacron」とは Part1
  • /etc/cron.daily, /etc/cron.weekly, /etc/cron.monthly配下にファイルを置いておけば定期的に処理を実行する
  • cronと違い、正確に実行時間を指定できない(3時から5時のいつかのタイミングで実行などのレンジ指定は可能)
  • 設定内容は/etc/anacrontabに記載

2015年3月1日日曜日

EC2でSeleniumとPhantomJSを使ってブラウザのテストをする環境を作る

このエントリーをはてなブックマークに追加 はてなブックマーク - EC2でSeleniumとPhantomJSを使ってブラウザのテストをする環境を作る

自分用メモ。それぞれの概要はこちらでは説明しません。

環境

  • EC2-AmazonLinux(t2.micro)
  • Ruby(2.1.5)
  • Selenium-webdriver(2.45.0)
  • PhantomJS(2.0)

EC2でSwap領域を確保

PhantomJSはLinuxで動かす時にビルドが必要だったのですが、t2.microのデフォルト状態でやったら、メモリが足りずに途中でエラーになってしまいます。

上記より、Amazon EC2(Linux)のswap領域ベストプラクティスを参考に、Swap領域を確保してからビルドしました。

まず、/etc/rc.localに以下を追加します。 (上記リンクに書いてあるスクリプトは動かなかったので少しだけ変更)

その後、再起動。

$sudo shutdown -r now

freeコマンドを打つと、Swap領域が確保されているのが確認できます。

$ free
             total       used       free     shared    buffers     cached
Mem:       1020188     562280     457908          0      49092     346636
-/+ buffers/cache:     166552     853636
Swap:      2040372       9568    2030804

PhantomJSのビルド

PhantomJS-Buildの通りにやります。 まずは必要なパッケージ群をインストールします。

$sudo yum -y install gcc gcc-c++ make flex bison gperf ruby openssl-devel freetype-devel fontconfig-devel libicu-devel sqlite-devel libpng-devel libjpeg-devel

その後、ソースコードを落としてきてビルドします。

$git clone git://github.com/ariya/phantomjs.git
$cd phantomjs
$git checkout 2.0
$./build.sh

30分ぐらい待つと終わりました。デフォルトで平行ビルドするらしいので、スペックの低いマシンの場合、./build.sh –jobs 1として平行ビルドしないようになるみたいです。

正常に終わっていればphantomjs/bin/phantomjsという感じでバイナリが出来ているのが確認できます。

Selenium-webdriverのインストール

自分の環境だとデフォルトでインストールされたRubyからSelenium-webdriverのインストールを試みたところ、パスの問題?でうまくいかなかったので、rbenvを使って、別途Rubyをインストールしました。

rbenv を使って ruby をインストールする(CentOS編)の通りにやって2.1.5をインストール。このやり方は割とどこにでも書いてあるので省略。

終わったら以下のコマンドでインストール

$gem install selenium-webdriver

テストする

Selenium 使いのための PhantomJS 解説を参考に実施。

まず、PhantomJSを起動させます。

$ phantomjs/bin/phantomjs -w 8001 &
[1] 18769
[ec2-user@ip-172-31-15-31 ~]$ [INFO  - 2015-02-28T14:53:40.058Z] GhostDriver - Main - running on port 8910

-wオプションを使ってポートを指定しているのですが、なぜか8910が開いてしまいましたが、原因を探す時間はありませんでした。。。(80001を使っているわけでもないのに)

気持ち悪いですが、起動はしたので、irbから操作してみます。

$irb

以下irbでのコマンドを順番に打っていきます。詳細はselenium-webdriverのAPIドキュメント参照のこと

まず、selenium/webdriverを使うことを記載

irb(main):001:0> require "selenium/webdriver"
=> true

次に、利用するDriverを指定。(GhostDriverを使おうとするとエラーになってしまったので、この方法で実施)

irb(main):002:0> driver = ::Selenium::WebDriver.for(:remote, url: "http://localhost:8910")

Googleのページを開く

irb(main):003:0>driver.get "http://www.google.co.jp"
=> {}

タイトルを表示

irb(main):004:0> puts driver.title
Google
=> nil

画面キャプチャ取得

irb(main):006:0> driver.save_screenshot('/home/ec2-user/test.png')
=> #<File:/home/ec2-user/test.png (closed)>

実際に取得できたキャプチャがこちら。うーん、便利。