Edited at
SensuDay 19

SensuのhandlerとしてAnsibleを使う

More than 3 years have passed since last update.

うどん大学 情報系 院1年の堀内です.

勢いで Sensu Advent Calendar を立てましたが,無事埋まっていてホッとしています.

SensuはAnsibleと同時期に使い始めました.

現在は,研究室のサーバ監視に使っています.


もくじ


  1. Sensuとの出会い

  2. 研究室のサーバ監視で

  3. handlerとしてのAnsible


Sensuとの出会い

研究室でAnsible使ってる話」でも書きましたが,8月末にヤフーのインターンシップに参加しました.

そこでSensuが使われているのを見せて頂き,興味を持ったのがキッカケです.

ホテルに戻ってから,試してみようと作業していたのを覚えています.

(Ansibleを使ってsensu-clientを流し込もうと四苦八苦.)

ヤフーでの取組みはAdvent Calendarに書かれています.


研究室のサーバ監視で

研究室にはVMを含めて20台以上のサーバがあります.

そこにAnsibleを使ってsensu-cluentを流し込み,監視しています.

監視項目はCPUやメモリ,HTTPなど,基本的なものだけです.

community-plugins もしくは,その機能を削って使っています.

metricsはElasticsearchに投げて,Kibanaで可視化しています.

(fluentdなどを介さず,handlerから直接投げています.)

また,研究室で Typetalk を使っているので,Hubotを使ってアラートを投げたりもしています.


handlerとしてのAnsible

ここからが本題です.

例えば,SensuのHTTPチェックでCRITICALが発生したとします.

そこで,handlerからWebサーバを再起動するためにAnsibleを使ってみます.

(ついでに,Typetalkに再起動の通知も出します.)

発生したアラートはこんな感じ.

{

"client":{
"name": "web-server-01",
"address": "192.168.11.10",
"subscriptions": ["webservers"],
"timestamp": 1326390159
},
"check":{
"name": "check-http",
"issued": 1326390169,
"output": "HTTP CRITICAL: HTTP/1.1 503 Service Temporarily Unavailable",
"status": 2,
"command": "etc/sensu/community-plugins/plugins/http/check-http.rb -r -u http://localhost/",
"subscribers":["webservers"],
"interval": 60,
"handler": "ansible",
"history": [],
"flapping": false
},
"occurrences": 1,
"action": "create"
}

まず,このアラートを受ける/etc/sensu/plugins/ansible_handler.rbを作ります.

#!/usr/bin/env ruby

# Sensu Ansible Handler

require 'sensu-handler'

class AnsibleHandler < Sensu::Handler
def playbooks
settings['ansible']['playbooks'] || '/etc/sensu/playbooks'
end

def typetalk_vars
{
client_id: settings['ansible']['typetalk_client_id'],
client_secret: settings['ansible']['typetalk_client_secret'],
topic: settings['ansible']['typetalk_topic']
}
end

def generate_hostfile(event)
client = event['client']['name']
address = event['client']['address']
check = event['check']['name']

hostfile = "/tmp/#{Time.now.strftime '%y%m%d%H%M%S'}.hosts"
typetalk = typetalk_vars

File.open(hostfile, 'w') do |f|
f.puts "[#{check}]"
f.puts "#{client} ansible_ssh_host=#{address}"
f.puts "[#{check}:vars]"
f.puts "typetalk_client_id=#{typetalk[:client_id]}"
f.puts "typetalk_client_secret=#{typetalk[:client_secret]}"
f.puts "typetalk_topic=#{typetalk[:topic]}"
end

hostfile
end

def handle
status = @event['check']['status']
occurrences = @event['occurrences']
return if status < 2 || occurrences > 1

hostfile = generate_hostfile @event
check = @event['check']['name']
result = `ansible-playbook -i #{hostfile} #{playbooks}/#{check}.yml`
unless $?.to_i == 0
puts result
end

File.delete hostfile
end
end

handlerの設定ファイル/etc/sensu/conf.d/handler_ansible.jsonは下記の通り.

{

"ansible": {
"playbooks": "/etc/sensu/playbooks",
"typetalk_client_id": "abcdefghijklmnopqrstuvwxyz123456",
"typetalk_client_secret": "abcdefghijklmnopqrstuvwxyz123456789abcdefghijklmnopqrstuvwxyz123",
"typetalk_topic": 1234
},
"handlers": {
"ansible": {
"type": "pipe",
"command": "/etc/sensu/handlers/ansible_handler.rb"
}
}
}

ansibleで実行する/etc/sensu/playbooks/check-http.yml

Nginxの再起動と,Typetalkの通知のみでシンプルにします.

---

- hosts: check-http
sudo: yes
tasks:
- name: restart nginx
service:
name=nginx
enabled=yes
state=restarted

- name: notify to typetalk
typetalk:
client_id={{ typetalk_client_id }}
client_secret={{ typetalk_client_secret }}
topic={{ typetalk_topic }}
msg='[sensu] restart nginx on {{ ansible_hostname }}'

後は,実際に対象サーバのNginxを止めると,Ansibleが実行されるはずです.

こんな感じで Huboco ちゃんが通知してくれます.


まとめ

今ハマっているSensuとAnsibleを組合せる方法を紹介しました.

今回はNginxでしたが,AnsibleでRailsをデプロイ しているので,こちらも対応させたいなと思います.

また,この記事のhandlerはGitHubで公開しています.

コメント・プルリクお願いします!