SHOYAN BLOG

I am a pragmatic programmer.

Exception Notificationでundefined Method `current' for Time:Classエラーがでた

例外発生時にException Notificationで通知をしようと思い、導入してみたところ以下のエラーがでました。

1
2
ERROR: Failed to generate exception summary:
ActionView::Template::Error: undefined method `current' for Time:Class

日付の取得にTime.currentを使っており、Time.currentはActive supportにより拡張されたメソッドなのでActive Supportを使っている環境でしか動作しません(要するにRailsじゃないと動かない。Sinatraは…)。

PRもでているので対応してほしいところです。

対応方法

require 'active_support/core_ext/time’ をすることでTime.currentを使えるようにしました。

サンプルコード

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
require 'rubygems'
require 'bundler/setup'
require "sinatra/base"
require 'exception_notification'
# Time.currentを使えるようにする
require 'active_support/core_ext/time'

class App < Sinatra::Application
  use ExceptionNotification::Rack,
    :email => {
      :email_prefix => "[Exception] ",
      :sender_address => %{"notifier" <notifier@example.com>},
      :exception_recipients => %w{shoyan@example.com}
    }

  get '/' do
    begin
      // 何かの処理
    rescue Exception => e
      status 500
      ExceptionNotifier.notify_exception(e, env: env)
      e.message
    end
  end
end

Sinatraのエラーハンドリング

Sinatraには not_foundハンドラとerrorハンドラの2つのハンドラが用意されています。
not_foundハンドラは404エラーを補足するためのエラーハンドラです。
errorハンドラは様々なエラーを補足するためのエラーハンドラです。

Not Foundハンドラ

not_foundハンドラは404エラーを補足するためのエラーハンドラです。
Sinatra::NotFoundが発生したとき、またはステータスコードが404のときは not_foundハンドラが実行されます。

1
2
3
not_found do
  'This is nowhere to be found.'
end

Errorハンドラ

errorハンドラは様々なエラーを補足するためのエラーハンドラです。
例外オブジェクトにはRack変数の sinatra.error でアクセスできます。

1
2
3
error do
  'Sorry there was a nasty error - ' + env['sinatra.error'].message
end

以下の設定をすると、environmentがDevelopmentのときにブラウザにスタックトレースを表示することができます。

1
set :show_exceptions, :after_handler

エラー固有の制御もできます。
MyCustomeErrorのエラーハンドリングをしたいときは以下のように定義します。

1
2
3
error MyCustomError do
  'So what happened was...' + env['sinatra.error'].message
end

raiseでエラーを発生させるようにしてみます。

1
2
3
4
5
6
7
get '/' do
  raise MyCustomError, 'something bad'
end

# 以下のようにレスポンスが返ります。

So what happened was... something bad

ステータスコードを指定してエラーハンドリングを行う方法もあります。

1
2
3
error 403 do
  'Access forbidden'
end

レンジの指定も可能です。

1
2
3
error 400..510 do
  'Boom'
end

errorハンドラにerrorコードを指定しなかった場合は何を補足するのか

errorハンドラにerrorコードを指定しなかった場合は、Exceptionを補足します。

1
2
3
4
5
6
7
  def error(*codes, &block)
    args  = compile! "ERROR", /.*/, block
    codes = codes.map { |c| Array(c) }.flatten
    codes << Exception if codes.empty? #errorコードの指定がない場合
    codes << Sinatra::NotFound if codes.include?(404)
    codes.each { |c| (@errors[c] ||= []) << args }
  end

https://github.com/sinatra/sinatra/blob/939ce04c1b77d24dd78285ba0836768ad57aff6c/lib/sinatra/base.rb#L1287

その他の例外は補足しません。
例えばExceptionのサブクラスであるStandardErrorは拾ってくれません。
ですので、明示的にerrorコードを指定しておいたほうがよいです。

1
2
3
error 500 do
  'Sorry there was a nasty error - ' + env['sinatra.error'].message
end

参考文献

Knife Zeroを使ってレシピを適用する

Chefでよく使われるknifeコマンドですが、そのプラグインであるknife zeroを使ってレシピを適用する方法を紹介します。
knife zeroはknifeプラグインで、リモートnode上でchef-clientを実行するツールです。

リモートnodeとはchefを適用するサーバー(管理対象となるサーバー)のことです。

インストール

今回はGemfileに定義してインストールします。

1
2
# Gemfile
gem 'knife-zero'

以下でインストールされます。

1
$ bundle

chefをリモートnodeにインストール

まずはchefをリモートnodeにインストールします。
以下のコマンドでインストールします。

1
$ bundle exec knife zero bootstrap shoyan@server01.example.com --sudo

実行すると node/ 配下にファイルが作成さます。

chef_environmentrun_listを追加します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
  "name": "server01.example.com",
  "chef_environment": "production",
  "run_list": [
    “role[awesome_cookbook]"
  ],
  "normal": {
    "knife_zero": {
      "host": "server01.example.com"
    },
    "tags": [

    ]
  },
  "automatic": {
    "fqdn": "server01.example.com",
    "os": "linux",
    "os_version": "2.6.32-504.3.3.el6.x86_64",
    "platform": "centos",
    "platform_version": "6.4",
    "hostname": "server01.example.com",
    "ipaddress": “192.168.1.1",
    "roles": [

    ]
  }
}

リモートnodeにログインして、chefのコマンドが実行されていることを確認してみます。

1
2
3
4
$ ssh shoyan@server01.example.com

[shoyan@server01 ~]$ chef-[Tabを押す]
chef-apply   chef-client  chef-shell   chef-solo

レシピをリモートnodeに適用する

Chefを実行する準備ができました。
以下のコマンドでレシピをリモートnodeに適用します。

1
$ bundle exec knife zero converge 'fqdn:server01.example.com' -x shoyan

freeコマンドで確認するOSのメモリ情報

サーバーのメモリ情報はシステムの空きメモリと使用メモリの量を表示するfreeコマンドを使って確認することができます。

1
2
3
4
5
[shoyan@server01 ~]$ free
             total       used       free     shared    buffers     cached
Mem:       1922324    1716832     205492          0     197248    1055352
-/+ buffers/cache:     464232    1458092
Swap:      4194300          0    4194300

-mオプションを使うとMB単位で表示させることができます。

1
2
3
4
5
[shoyan@server01 ~]$ free -m
             total       used       free     shared    buffers     cached
Mem:          1877       1676        200          0        192       1030
-/+ buffers/cache:        453       1423
Swap:         4095          0       4095

まずどこを見るべきか

見るべきところは、3行目の -/+ buffers/cache: 453 1423 の部分です。
453MBが使用中で、1423MBは自由に使えるメモリがあります。

項目の説明

表示項目を確認していきます。

total: OSが認識している物理的なメモリサイズです。
1877MBなので、およそ1.8GBです。
RAIDカードやNICなどを装着しているときは、それらのためにメモリが使われるので実際の搭載メモリサイズよりも少なくなります。
ですので、物理メモリとしては2GBなのですが、RAIDカードやNICでメモリが使用されてOSが認識しているメモリは1.8GBとなります。

used: 使用しているメモリサイズです。
バッファキャッシュとページキャッシュが含まれています。
およそ1.6GBを使用しています。

free: 空きメモリサイズです。
この値にはバッファキャッシュとページキャッシュが含まれていません。
OSは使い続けるほどメモリにキャッシュを割り当てます。
そのため、使い続けるほどfreeの値は0に近づきます。
ですので、この値が少ないからといって空きメモリがないわけではありません。
200MBのメモリがfreeとして表示されています。

shared: 共有メモリに割り当てられたメモリです。
0なので共有メモリに割り当てられているメモリはありません。

buffers: バッファキャッシュに割り当てられたメモリです。
バッファキャッシュとはブロックデバイス用のキャッシュです。
192MBが割り当てられています。

cached: ページキャッシュに割り当てられたメモリです。
ページキャッシュはファイルに対するキャッシュです。
1030MBがページキャッシュとして割り当てられています。

バッファキャッシュとページキャッシュという2つのキャッシュがでてきました。
ここでは厳密にわけて考える必要はありません。
バッファキャッシュとページキャッシュを合計したものがキャッシュとして使用されているメモリと考えます。

-/+ buffers/cache: バッファキャッシュとページキャッシュを考慮したメモリです。
1つ目の値がused、2つめの値がfreeです。

used: 2行目のusedからページキャッシュとバッファキャッシュを引いた値です。
OSとアプリケーションが純粋に使用しているメモリサイズを表します。

実際に計算してみましょう。
1676 - 192 - 1030 = 454
ほぼあっていますね。

free: 2行目のfreeにページキャッシュとバッファキャッシュを足した値です。
キャッシュに割り当てられているメモリを自由に割り当て可能なメモリと考えれば、この値が空きメモリサイズになります。

実際に計算してみましょう。
200 + 192 + 1030 = 1422
こちらもほぼあっています。

Swap: スワップに割り当てたサイズです。
1つめの値がtotal、2つめの値がused、3つめの値がfreeです。

total: スワップに割り当てたディスクサイズです。
used: 割り当てた中で使用中のサイズです。
free: 割り当てた中で使用していないサイズです。

Swapのtotal値は4095MB、usedは0、freeは4095MBです。
usedが0なのでスワップ領域は使われていないことになります。

キャッシュはフリーメモリと考える

注意点として、freeの値がないからといってすぐにメモリ不足と判断することはできません。
なぜなら、OSは処理の高速化のためにキャッシュを使うようになっているからです。
時間の経過とともにキャッシュに割り当てられるメモリは増えていきます。
キャッシュはフリーメモリと考えて問題ないようです。

メモリの仕組みやキャッシュについて詳しく知りたい場合は以下の本をおすすめします。

参考文献

AllowGroupsの問題でsshログインできない

sshログインできないので /var/log/secure のログを見てみました。
以下のメッセージがでていました。

1
2
3
Apr 28 14:11:14 server01 sshd[31264]: User shoyan from example.jp not allowed because none of user's groups are listed in AllowGroups
Apr 28 14:11:14 server01 sshd[31265]: input_userauth_request: invalid user shoyan
Apr 28 14:11:14 server01 sshd[31265]: Connection closed by 192.168.1.1

none of user's groups are listed in AllowGroups と書いてあるので、groupの問題らしい。

/etc/ssh/sshd_configをみると、AllowGroups にいなかったのでユーザーのグループを追加しました。

sshd_configを変更したら、設定を反映させるためにreloadします。

1
2
# service sshd reload
sshd を再読み込み中:                                       [  OK  ]

AllowGroupsにユーザーのグループを追加することによってログインできるようになりました。

参考文献