Chefのハンドラを使ってSensuやSlackと組み合わせる方法

 2014/10/06

Chef Serverを使って大規模なインフラを自動運用している場合、当然のことながら各ノードできちんとChef Clientが動作して、指定したロールやクックブックが実行されていることを確認しなければいけません。 残念なことに、継続的インテグレーションでクックブックの品質を常時担保していたとしても、実際にそのクックブックをノードに適用する歳に正常終了するとは限りません。たとえばパッケージの配布元が高負荷で落ちていたといったこともあります。 つまり、Chef Clientが正常に実行が終わったかどうかは監視の対象になるということです。

そこで今回は、Chefのハンドラを使って、SensuやSlackに通知する方法を紹介しましょう。

Chefのハンドラってなんぞ?

詳細は公式サイトを見ていただくのが一番ですが、ハンドラとは処理の前後や例外の発生時に別の処理を呼び出す機能です。例えばこれらの場合に外部への通知をおこなったりデータを取得して他の場所に蓄積したりといった用途に使えます。

ハンドラには3種類あります。

  • Exception Handler:Chef Clientの実行に失敗したときに呼ばれます
  • Report Handler:Chef Clientの実行に成功した場合に呼ばれます。例えば変更内容を通知したりするのに使えます
  • Start Handler:Chef Clientの実行が開始された場合に呼ばれます

また、ハンドラを設定するための方法は2つあります。

  • レシピの中でchef_handlerのリソースを指定する
  • ノード側のclient.rbで設定をおこなう

Chef Clientの実行が失敗した場合にSlackに通知する

では、まずは、Chef Clientの実行が失敗した場合にメジャーなチャットツールの1つであるSlackに通知する方法を見て行きましょう。 ここでは、GitHubで公開されているchef-handler-slackを使い、レシピの中でchef_handlerリソースを指定する方法を使って設定してみます。

SlackのTokenを取得する

まずはSlackに対してメッセージを送信するために、Tokenを取得します。SlackのIntegrations設定の画面に移動してください。 ページ下部に以下のような箇所がありますので、Incoming WebHooksを選択します。

選択すると、以下のような画面が表示されますので、既存のチャンネルを設定するか新たなチャンネルを作成します。

Add Incoming WebHookをクリックすると、以下のような画面が表示され、Tokenの生成が完了します。図の中でグレーになっている箇所がTokenになります。

Cookbookの作成

いつも通りの方法でクックブックを作成してください。knifeとかBerkshelfとか使えばOKです。

Berksfileに依存するchef_handlerを追加します。

source "https://supermarket.getchef.com"

metadata

cookbook 'chef_handler'

またmetadata.rbにも依存関係を定義するために以下を追加します。

depends          'chef_handler'

recipes/default.rbは以下のようになります。冒頭でslack用のハンドラのgemをインストールし、その後で、通知先を設定します。

chef_gem "chef-handler-slack" do
  action :upgrade
end

require 'chef/handler/slack'

chef_handler "Chef::Handler::SlackReporting" do
  source "chef/handler/slack"
  arguments [
    # The name of your team registered with Slack
    :team => node["slack-handler"]["team"],

    # Your incoming webhook token
    :token => node["slack-handler"]["token"],

    # An existing channel
    :channel => node["slack-handler"]["channel"],

    # Watever.
    :icon_emoj => node["slack-handler"]["icon_emoj"],
  ]
  action :nothing
end.run_action(:enable)

ここでは、チーム名などの可変情報をアトリビュートから引くようにしていますので、attributes/default.rbを以下のように作成してください。 なお、プライベートレポジトリであればベタで書いてしまっても大丈夫かもしれませんが、普通はChef Server側でアトリビュートの設定をすることをオススメします。

default['slack-handler']['channel'] = '#chef'
default['slack-handler']['token'] = 'SUSHIKUITAIUNIIKURATORO' 
default['slack-handler']['team'] = 'your-team-name-here'
default['slack-handler']['username'] = 'chef'
default['slack-handler']['icon_emoj'] = ':chef:'

Cookbookの適用

最後にこのクックブックの適用の設定をおこないます。

Chef Serverを使っている場合はberks uploadコマンドを使ってクックブックをアップロードし、各ノードのrun_listに追加してください。また併せて前述の通り、アトリビュートの設定をChef Server側でおこないます。 Chef Serverを使っていない場合は、run_listにslack-handlerを追加しつつ、アトリビュートをコマンドの引数などで指定してください。

これでChef Clientの実行に失敗した場合は以下のような感じでSlackに通知されるようになります。

もうこれでいつChef Clientの実行でこけてもその場で検知して、「まーた◯◯の依存関係が壊れた」とか愚痴りながらすぐに修正することができますね!!

Chef Clientの実行が失敗した場合にSensuに通知する

次に同じようにSensuに通知してみるようにしましょう。 ここで使うのは、同じくGitHubで公開されているchef-handler-sensu-eventです。

今回はclient.rbに設定を行う方法でやってみましょう。client.rbを見れば分かるように/etc/chef/client.d以下にある拡張子.rbのファイルを読むようになっていますので、直接client.rbをいじらないで、このディレクトリに設定ファイルを置くようにします。

Cookbookの作成

まずは、/etc/chef/client.dに配置するためのテンプレートをtemplates/default/sensu.rb.erbという名前で以下の内容で作成します。 これはChefのテンプレートなので、ERBの書式部分は別で設定されたアトリビュートによって実行時に設定されファイルが作成されます。

require 'chef-handler-sensu-event'
option = Hash.new
option[:server] = "<%= node['sensu-handler']['server'] %>"
option[:port] = "<%= node['sensu-handler']['port'] %>"
option[:severity] = <%= node['sensu-handler']['severity'] %>
option[:handlers] = <%= node['sensu-handler']['handlers'] %>
sensu_handler = Chef::Handler::SensuEvent.new(option)
report_handlers << sensu_handler
exception_handlers << sensu_handler

次にレシピです。recipes/default.rbを以下の内容で作成します。 まず冒頭で先ほどのテンプレートから設定ファイルを作成します。その上で、ハンドラ保存用のディレクトリを作成し、ファイルを保存しつつ、このハンドラを有効にします。

template "/etc/chef/client.d/sensu.rb" do
  source "sensu.rb.erb"
  owner "root"
  group "root"
  mode "00644"
end

directory "/var/chef/handlers" do
  owner "root"
  group "root"
  mode 00755
  action :create
end

remote_file "#{node[:chef_handler][:handler_path]}/chef-handler-sensu-event.rb" do
  source "https://raw.githubusercontent.com/SimpleFinance/chef-handler-sensu-event/master/lib/chef-handler-sensu-event.rb"
  mode 0644
end

chef_handler 'Chef::Handler::SensuEvent' do
  source "#{node[:chef_handler][:handler_path]}/chef-handler-sensu-event.rb"
  action :enable
end

最後にアトリビュートです。attributes/default.rbを作成します。 ここで設定できるようにしているのは、Sensuで使うRabbitMQのIPアドレスとポート番号、警告の重大度、そしてSensuのどのハンドラ(Chefのハンドラとは違います)を呼ぶかを設定します このアトリビュートについても先ほどのSlackの場合と同様にChef Server側で設定する方が良いでしょう。

default['sensu-handler']['server'] = '127.0.0.1'
default['sensu-handler']['port'] = '5672'
default['sensu-handler']['severity'] = 1 
default['sensu-handler']['handlers'] = ['default'] 

Cookbookの適用

あとは今までと同様にクックブックを適用します。この際に、Chef Clientが正常に終了しないと、以下のような感じでSensuに通知されます。

もちろんこの警告を受けて、更にメールを投げたり、PagerDutyで電話したりといったこともできます。

ということで楽しいインフラ職人ライフを!!

 2014/10/06

サイト内検索


著作

寄稿

Latest post: