Uptime RobotでWebサービスの死活監視する


REMPStobo , Casto のそれぞれのHTTP(S)レベルでの疎通監視に以前から使っていたUptime Robotというサービスのことを書いていなかったので思い出した様に書いてみます。*1

http://uptimerobot.com/

Uptime Robotは50箇所のモニタリングで且つ5分間隔であれば無料で使える監視のWebサービスでHTTP(S)の疎通監視したいサイトを登録すると上の様なダッシュボードで監視状況を見ることができて、且つアラートの通知先として、

  • 基本通知
  • アプリケーション連携
    • Pushbullet, Pushover, HipChat, Slack

が使えます。ちなみにTwitterは通知先のアカウントが @ をフォローすれば、DMで通知が届きます。

またダッシュボードで各監視しているサイトの情報は以下の様な感じでレスポンスタイムの遷移と直近起きたUp / Down 状況とともに確認できます。

無償内でも個人で利用する上では十分な機能を抑えていますので、個人サービス等運営されている方は利用してみてはどうでしょうか。

*1:無論、今日アラートがここから飛んだから書いているということは言うまでもない訳で…。

Rendrで作られたnode.jsアプリをminaでデプロイする


Rendrで新しくアプリを作ろうとしていて、rendr-cli
プロジェクトの雛形を作ったので、では実際にデプロイどうしようと考えた時にminaを使ってやってみたので
その際のメモ。

手順としては、

  • rendrアプリをデーモン化して稼働させるためにforeverで動く様にする
  • minaでデプロイするタスクを書く

minaのデプロイはここあたりに書いているので詳細は省略。

foreverでデーモン化する

rendr-cliで作られたアプリのベースには既にgruntを使って開発環境用のサーバを
稼働させたり、stylusをコンパイルするためのgruntタスクが既に用意されていて、開発環境であれば

$ grunt server

とすれば、一通りのコンパイルと手元でサーバ起動、そしてstyles等の更新を監視して再コンパイルをしてくれるのですが、
これをこのままproduction環境で動かす訳にはいかないので、foreverでデーモン起動、停止をする
タスクを書き足します。

まず、foreverが使える様にするためにアプリケーションプロジェクトのルートで、

$ npm install forever --save

としてforeverをインストール。 --save を付けて package.json も更新します。

次にrendr-cliが作った Gruntfile.js にforeverを使ったアプリケーション起動、停止をするタスクを足します。

// Gruntfile.js
grunt.registerTask('startProductionNode', function () {
grunt.util.spawn({
cmd: 'node',
args: ['./node_modules/forever/bin/forever', 'start', 'index.js'],
opts: {
stdio: 'inherit'
}
}, function () {
grunt.fail.fatal(new Error("forever quit"));
});
});
grunt.registerTask('stopProductionNode', function () {
grunt.util.spawn({
cmd: 'node',
args: ['./node_modules/forever/bin/forever', 'stop', 'index.js'],
opts: {
stdio: 'inherit'
}
}, function () {
grunt.fail.fatal(new Error("forever quit"));
});
});

ここまで設定すると、アプリケーションをデーモン化して起動、停止ができます。

起動は、*1

$ grunt startProductionNode
Running "startProductionNode" task
warn:    --minUptime not set. Defaulting to: 1000ms
warn:    --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
info:    Forever processing file: index.js
Done, without errors.

一方停止は、

$ grunt stopProductionNode
Running "stopProductionNode" task
info:    Forever stopped process:
data:        uid  command                                            script   forever pid   logfile                             uptime
[0] sKj9 /Users/hideack/.nodebrew/node/v0.11.11/bin/node index.js 34008   34012 /Users/hideack/.forever/sKj9.log 0:0:1:15.836
Done, without errors.

これで、Rendrアプリをデーモン化して動かせる様になりました。

minaでのデプロイ

続けて実際にデプロイする際には、リポジトリから最新のコードを取ってきてstylus等をコンパイルして上で作ったgruntタスクを利用してアプリケーションを再起動させます。

gitリポジトリから引っ張ってくるところまではRubyのアプリケーションの場合と同様ですが、アプリケーションサーバの起動のタスクは独自で書く必要があるので以下の様なタスクを書き足します。

# rendr
# =============
namespace :rendr do
task :compile do
queue 'echo "-----> Start compile tasks."'
queue! %{
      cd #{deploy_to}/current
      grunt compile
    }
end
task :start do
queue 'echo "-----> Start server."'
queue! %{
      cd #{deploy_to}/current
      grunt startProductionNode
    }
end
task :stop do
queue 'echo "-----> Stop server."'
queue! %{
      cd #{deploy_to}/current
      grunt stopProductionNode
    }
end
end
desc "Deploys the current version to the server."
task :deploy => :environment do
deploy do
# Put things that will set up an empty directory into a fully set-up
# instance of your project.
invoke :'git:clone'
invoke :'deploy:link_shared_paths'
to :launch do
invoke :'rendr:compile'
invoke :'rendr:stop'
invoke :'rendr:start'
queue "touch #{deploy_to}/tmp/restart.txt"
end
end
end

ここまで準備できれば手元から、

$ bundle exec mina deploy

でRendrアプリのデプロイ&アプリの再起動*2が行えます。

*1:warning出ているのはアプリのプロセスの監視条件を設定していないためなので設定するべきですね…

*2:ただこのままではgracefulな再起動になってないので今後直していく予定

wkhtmltoimageをChefで入れてスクリーンショットを撮ってみる


ストーリーボードでスライドページのスクリーンショットを取りたい。
という話があって、wkhtmltoimageを使ってみたらどうだろうと朝起きたら
issueにコメントが付いていた*1のでインストールしてみました。OSはUbuntu 12.10です。

wkhtmltoimageは、webkitのエンジンを使ってページをレンダリングしてそれをJPGやPNG画像として保存することができます。

で、例によってChefを使ってレシピを下の様に書いてみました。

# default.rb
%w{libxrender1 libfontconfig1 otf-ipafont-gothic libxext6}.each do |pkg|
package pkg do
action :install
end
end
bash "wkhtmltoimage install." do
user "root"
cwd "/tmp"
code <<-EOH
    wget 'https://wkhtmltopdf.googlecode.com/files/wkhtmltoimage-0.10.0_rc2-static-amd64.tar.bz2';
    tar xjfv wkhtmltoimage-0.10.0_rc2-static-amd64.tar.bz2
    mv wkhtmltoimage-amd64 /usr/local/bin
  EOH
end

やっていることとしては、wkhtmltoimage自体、バイナリがGoogle codeに圧縮されているものがあるので、それを引っ張ってきて配置するだけで、
それに依存するライブラリやスクリーンショットを撮る際に必要な日本語フォントをインストールしています。

インストールされた環境から、以下の様なコマンドを打つことでスクリーンショットを保管することができます。

$ wkhtmltoimage-amd64 --javascript-delay 5000 \
--width 800 --height 600 \
--format png --quality 70 http://www.remp.jp test.png

上の例では、

  • JavaScript実行完了から5秒待ってからスクリーンショットを撮る
  • 画像サイズは800×600
  • 画像クオリティは最高を100とした場合に70を指定
  • 画像フォーマットはPNG

をオプションで指定しています。

実際にスクリーンショットを撮ってみた画像は以下の様な形。

*1:朝起きるとやること決まってて便利

node.jsを公式リポジトリからChefを使ってインストールする


Ubuntu 12.10にChefを利用してnode.jsをインストールしたかったのですが、単に apt-get install すると0.6.19と古いバージョン入るので、リポジトリを指定して以下の様なレシピを用意することでインストールできます。

# site-cookbook/nodejs/recipes/default.rb
%w{software-properties-common python-software-properties python g++ make}.each do |pkg|
package pkg do
action :install
end
end
execute "add-apt-repository -y ppa:chris-lea/node.js" do
user "root"
end
execute "apt-get update" do
user "root"
end
%w{nodejs}.each do |pkg|
package pkg do
action :install
end
end

もう少しスマートになる様な気もするのですが…。インストールされたバージョンは以下の様な形になります。

remper@host:~$ node -v
v0.10.23
remper@host:~$ npm -v
1.3.17

VPS上のLXCを利用してサーバを再構築した


いままで個人で運用しているアプリケーションはレンタルサーバーだったり、PaaSだったり、データベースサーバーだけはVPSで動かしていたりと散った構成になっていたので、重い腰を上げて1箇所に集約する作業を行いましたのでその際のメモ。

まずVPSをConoHaで契約をしてここに全て集約し、単にVPS上に寄せるだけではなくてLXC*1を利用して仮想環境(マシン)を設けてそれぞれでアプリケーションサーバやデータベースサーバを稼働させました。イメージとしては、以下の様な環境。

LXC自体はubuntuであれば、

$ sudo apt-get install lxc

でインストールされて、これだけでLXCを構築する準備ができます。LXCコマンドのリファレンスはWeb上に十分ありますが、簡単にここへも自分の備忘録を兼ねてメモしておきます。

以下、sample という名前のコンテナを作成する例で記載。

処理例 コマンド
コンテナ作成 MIRROR=http://jp.archive.ubuntu.com/ubuntu lxc-create -t ubuntu -n sample
コンテナ一覧 lxc-list
コンテナ起動 lxc-start -n sample -d -c /var/log/lxc-console.log
コンテナコンソール接続 lxc-console -n sample
コンテナシャットダウン lxc-shutdown -n sample
コンテナ強制終了 lxc-stop -n sample
コンテナ完全削除 lxc-destroy -n sample

各作られたコンテナの設定はubuntuの場合は /var/lib/lxc/(コンテナ名) で記録されています。

LXCの構成管理

各LXCはChefで構成管理をしていてコンテナを作りさせすれば各アプリケーションやデータベースサーバ向けのレシピを適用することですぐにサーバが構築される様にしていて、今回だとPadrinoアプリケーション用サーバのレシピ、MongoDB用のレシピ、MySQL用のレシピを作ったりしています。

やっていることは対象のLXCへSSH接続できる様にするための公開鍵の設定だったり、パッケージマネージャを利用してインストールしたりというとてもシンプルな内容です。

得られたメリット

まずは散っていたサーバを寄せることができたこと。あと、LXCを使うことで気楽に実験的に使いたいサーバーを構築することができる様になったのが一番得られて大きなメリットでした。

参考

*1:今、思うとdockerを試してもよかったなとも思った

Chefでcron設定をする


データベースサーバをChefで構築することを行っていて、そのサーバにバックアップスクリプトを撒いて併せてcronで定期的にバックアップを行わせることを行いたかったのでその際のメモです。

ここに全てが記載されていますがchefでcron設定を行う場合、以下の様な記述をrecipeに記載することで実現できます。

cron "Backup script" do
user "remper"
command "/bin/sh /home/foo/backup_script"
hour "3"
minute "55"
end

上記の様な記述だと、

  • remperユーザのcronに設定
  • 実行するコマンドは /bin/sh /home/foo/backup_script
  • 毎日3:55に実行

を意味していることになり、Chefを適用したサーバには、

$ whoami
remper
$ crontab -l
# Chef Name: Backup script
55 3 * * * /bin/sh /home/foo/backup_script

とcronが設定されます。

MongoDBでユーザ認証を有効にする


そして続けて新しく立ち上げた時のユーザ認証と外部ホストから接続できる様にする設定を忘れていたのでメモ…。

mongodb.conf の設定を以下の様にする

  • デフォルトでは bind_ip = 127.0.0.1 が有効になっていてMongoDBと同じホストからのみ接続できる様になっているので無効化
  • 同様に #auth = true となっていてユーザ認証が無効になっているのでこれを解除してユーザ認証を有効化

unicornでgraceful restartする


unicornを無停止で再起動(新しい設定をリロード) すなわちgraceful restartさせるのにはマスタープロセスに対して kill -s USR2 すると薄ら理解していたのですが、そのためにはきちんと設定がいるのでその際の対応と、実際に行った際に観察したプロセスをメモしておきます。

まず、設定しているunicorn.confには以下の設定を記載しておきます。

よく参照されているunicorn.conf.rb の中でコメントアウトされている辺りを有効にする必要があります。

また、リスタート時にGemfileのパスを強制的に指定させる様にしているのは、graceful restartさせた後に新しいGemfileの更新を適用させるためです。

上の記述がある状態でunicornを起動していれば、USR2シグナルをmasterのプロセスに送るとgraceful restartできます。

試しに手動でプロセスをkillしてみます。まずは現状を確認。masterのプロセスは7864であることが確認できます。

remper    7864 66.2  7.1 320460 72732 ?        Sl   23:11   0:05 unicorn master -c config/unicorn.conf -E production -D
remper    7876  0.0  6.7 320460 69156 ?        Sl   23:11   0:00 unicorn worker[0] -c config/unicorn.conf -E production -D
remper    7880  0.0  6.7 320460 69148 ?        Sl   23:11   0:00 unicorn worker[1] -c config/unicorn.conf -E production -D

この状態から、master processをkillしてみます。

$ kill -s USR2 (unicorn master processのPID, ここでは7864)

とすると、現行のmasterが新しいmasterをforkします。
(下のプロセス一覧だと7908というPIDが該当)

remper    7864 15.2  7.1 320460 72900 ?        Sl   23:11   0:05 unicorn master (old) -c config/unicorn.conf -E production -D
remper    7876  0.0  6.8 321488 69744 ?        Sl   23:11   0:00 unicorn worker[0] -c config/unicorn.conf -E production -D
remper    7880  1.2  7.0 320896 71576 ?        Sl   23:11   0:00 unicorn worker[1] -c config/unicorn.conf -E production -D
remper    7908 57.0  2.2  84032 23164 ?        Rl   23:11   0:00 ruby /〜/releases/11/vendor/bundle/ruby/1.9.1/bin/unicorn -c config/unicorn.conf -E production -D

そして、いままでのmasterから生えていたworkerが無くなって、新しいworkerが新しいmasterから生えてきます。

下の一覧だと unicorn worker[(0|1)] というプロセスのPIDが上のPID一覧のものと換わっているのがわかります。

remper    7908 44.6  7.1 320580 72736 ?        Sl   23:11   0:05 unicorn master -c config/unicorn.conf -E production -D
remper    7922  0.0  6.7 320580 69116 ?        Sl   23:11   0:00 unicorn worker[0] -c config/unicorn.conf -E production -D
remper    7926  0.1  6.7 320580 69112 ?        Sl   23:11   0:00 unicorn worker[1] -c config/unicorn.conf -E production -D

ということで、unicorn.confに適切な設定を記載することでgraceful restartさせることができました。

そして、もろもろこの辺り調べていると大抵私が所属しているペパボのエンジニアの方のブログにぶつかって便利。そして感謝。

参照

Unicornのgraceful restartで少しハマった件 – blog.tnmt.info

追伸

はてなブログから移設した際にgraceful restartのスクリプト例が崩れていたのでgistで貼り直しました。

Chefで ~/.ssh/authorized_keys を配置する


Chefでコンフィグレーションをさせることを先日から取り組んでいて、その中でサーバに接続する予定のユーザの公開鍵を予め配置しておきたいと思っていたところ以外とあっさりとできたのでそのメモ。

状況的にはチームで共有しているユーザがあって、

  • 対象のサーバに存在するそのユーザを利用して複数の端末から公開鍵認証でのSSH接続を行いたい
  • そのため各端末の公開鍵を予め管理してコンフィグレーションする際に自動的に設定したい

が、満たせれば満足。これができれば自分たちの状況であれば、LXCを作るたびにこのレシピを適用すればすぐにチームメンバがSSH接続を特定のユーザでログインできる様になります。

まず、公開鍵を配置するdefinitionを新しく定義します。

# site-cookbooks/base/definition/authorized_keys_for.rb
define :authorized_keys_for, :keys => "public-keys", :group => nil, :home => nil do
user  = params[:name]
group = params[:group] || user
home  = params[:home]  || "/home/#{user}"
keys  = data_bag params[:keys]
if keys.any?
ssh_public_keys = keys.map do |key|
data_bag_item(params[:keys], key)
end
directory "#{home}/.ssh" do
owner user
group group
mode 0700
action :create
only_if "test -d #{home}"
end
file "#{home}/.ssh/authorized_keys" do
owner user
group group
content ssh_public_keys.collect{ |x| "#{x['key']} ##{x['id']}" }.join("\n")
end
end
end

次に配置したい公開鍵をdata bagとして定義します。今後配置したい鍵が増えればその毎に定義を増やしていけばよくなります。

data_bags/public-keys/(ユーザ名).json に足していきます。

# data_bags/public-keys/hideack.json
{
"id": "hideack",
"key": "ssh-rsa AAAAB3…."
}

ここまで準備できたら、上のdefinitionをレシピ中で使えるので、記述します。

# site-cookbooks/base/recipes/default.rb
authorized_keys_for 'remper'   # ← これが上のdefinition呼出
%w{}.each do |pkg|
package pkg do
action :install
end
end

実行してみます。

$ knife solo cook remper@*****
Running Chef on ****...
Checking Chef version...
Enter the password for remper@*****:
Uploading the kitchen...
Generating solo config...
Running Chef...
Starting Chef Client, version 11.8.0
Compiling Cookbooks...
Converging 10 resources
Recipe: base::default
* directory[/home/remper/.ssh] action create
- change mode from '0775' to '0700'
* file[/home/remper/.ssh/authorized_keys] action create
- update content in file /home/remper/.ssh/authorized_keys from d0209a to 5cf54f
(略)

これで、Chefを適用したサーバに対してSSH接続の認証に利用するための公開鍵の配置が行われました。地味に楽になりました…。

参照