次世代監視ツール Sensu リファレンス


バージョン 0.9 くらいのときの公式ドキュメントのざっくり訳＋個人のメモ
情報が古い＋理解が間違ってるとこあるかもなので注意して欲しいけど、需要がありそうなので出してみる

![](http://sensuapp.org/img/sensu_logo_large-c92d73db.png)

## Overview
Sensu は監視ツールの一つ。Sensu はよく "monitoring router" と記述される。もっと平たく言うと、Sensu は多くのノードに対して "check" スクリプトを実行し、1 つまたは複数の Sensu サーバーにて "handler" スクリプトを実行する。

例えば、Apache の死活チェックをするとしよう。チェックスクリプトにより死活だけでなくメトリクスも収集する。そしてそのアウトプットは 1 つまたは複数の Handlers にルーティングされる。Handlers はチェック結果によって何をするのか定義するものだ。Handlers は今のところ E メール、IRC、Twitter にアラートを送ったり、Graphite, Librato などにメトリクスを渡すことができる。またチェックスクリプトやハンドラースクリプトはどんな言語で書いてもよい。

```イメージ
# router のイメージ
# 色々な監視結果を sensu が適切なハンドラーにルーティング
checks -> sensu -> handlers
```

### 作者 Sean Porter によるイントロダクション

* [DevOpsDays Tokyo 2013 Introduction to Sensu - Vimeo ](http://vimeo.com/76414799)
* [DevOpsDays Vancouver 2013 のときのスライド](http://slides.sensuapp.org/)



### Key details

*   Ruby (EventMachine, Sinatra, AMQP), RabbitMQ, Redis
*   travis-ci を通した CI と、高いテストカバレッジ率
*   メッセージ志向アーキテクチャ。メッセージは JSON オブジェクト
*   既存の Nagios プラグインを再利用できる
*   プラグイン、ハンドラー、チェックスクリプトはどんな言語で書いてもよい
*   メトリクスを送るバックエンドは様々なものをサポート (Graphite, Librato, etc)
*   Chef や Puppet のようなモダンな CM ツールを考慮した設計
*   クラウド環境のための設計
*   1200 行程度で軽い
*   "オムニバス"（多くの項目を扱う）スタイルパッケージ

### Event Data とは
Event Data は

*   Client info
*   Check info
*   いくつかの metadata

から構成される JSON データ。言語の縛りがなく人間にも読みやすい

**Example**

```json
{
  "client":{
    "name": "host01",
    "address": "10.2.1.11",
    "subscriptions": [
      "all",
      "frontend",
      "proxy"
    ],
    "timestamp": 1326390159
  },
  "check":{
    "name": "frontend_http_check",
    "issued": 1326390169,
    "output": "HTTP CRITICAL: HTTP/1.1 503 Service Temporarily Unavaliable",
    "status": 2,
    "command": "check_http -I 127.0.0.1 -u http://web.example.com/healthcheck.html -R 'pageok'",
    "subscribers":[
      "frontend"
    ],
    "interval": 60,
    "handler": "campfire",
    "history": [
      "0",
      "2"
    ],
    "flapping": false
  },
  "occurrences": 1,
  "action": "create"
}
```

### コンポーネント
#### Sensu Server
Sensu server は sensu client のチェック開始のトリガーとなります。そしてチェック結果の出力を受け取ると handlers にフィードします。（バージョン 0.9.2 では client は server が知らないチェックも行います）

Sensu server は persistent データの保持に Redis インスタンスを使います。また自分自身と sensu client のデータのやりとりにおいて RabbitMQ もヘビーに利用します。

#### Sensu client
Sensu client は監視したいサーバー上で実行する。Sensu client はチェックスクリプト (`check_http`, `check_load` etc) を実行し RabbitMQ を通して Sensu server に結果を返します。

#### Sensu API
Sensu server (Redis) 上の色々なデータへアクセスするための REST API 。普通は Sensu Server または Redis インスタンスと同じサーバーで実行します。

#### Sensu dashboard
現在の Sensu インフラとアラートなどのアクションの状態を表示する Web ダッシュボード

![](http://misheska.com/images/sensudashboard.png)

## インストール
一般的には Sensu 環境には 2 種類のノードが必要になる。Sensu-Server にも Sensu-Client をインストールすることに注意

*   Sensu Server
    *   RabbitMQ
    *   Redis
    *   Sensu-Server / Sensu-Client / Sensu-API / Sensu-Dashboard
*   Sensu Client
    *   Sensu-Client

Chef （または Puppet ） を使っているなら既に recipe が用意されている。

### Sensu Server の構築
[Install Guide](https://github.com/sensu/sensu/wiki/Install%20Guide)

* [Sensu 監視システムを Chef で制御](http://jedipunkz.github.io/blog/2013/06/20/sensu-chef-controll/) 

Chef で構築する場合は、[sensu / sensu-chef - Github](https://github.com/sensu/sensu-chef) を利用する。

sensu-chef をそのまま使うのではなくて、sensu-chef の recipe を include した wrapper cookbook を別に用意する。
Sensu Server の recipe。default handler がないと server が起動しないので適当に定義しておく

```ruby
include_recipe "sensu"
include_recipe "sensu::rabbitmq"
include_recipe "sensu::redis"

sensu_handler "default" do
  type "pipe"
  command "ls"
end

include_recipe "sensu::api_service"
include_recipe "sensu::client_service"
include_recipe "sensu::dashboard_service"
include_recipe "sensu::server_service"
```

自分は [chef-monitor](https://github.com/portertech/chef-monitor) という作者が作ってる wrapper cookbook のさらに wrapper cookbook を作って使っている


#### Chef なしで構築
[Amazon Linux 上に sensu-server を構築する（1）](http://inokara.hateblo.jp/entry/2014/03/03/013147) 参考


## 起動方法
### Sensu Server の起動
Sensu サービスを enable にして、

CentOS/REHL

```
sudo chkconfig sensu-server on
sudo chkconfig sensu-api on
sudo chkconfig sensu-client on
sudo chkconfig sensu-dashboard on
```

Debian/Ubuntu

```
update-rc.d sensu-server defaults
update-rc.d sensu-api defaults
update-rc.d sensu-clent defaults
update-rc.d sensu-dashboard defaults
```

起動

```
sudo /etc/init.d/sensu-server start
sudo /etc/init.d/sensu-api start
sudo /etc/init.d/sensu-client start
sudo /etc/init.d/sensu-dashboard start
```


Sensu Server WebUI は 8080 番ポート

## 設定
[Configuration](https://github.com/sensu/sensu/wiki/Configuration)

### 設定ファイル
主な設定ファイルは

```
/etc/sensu/config.json
```

また、

```
/etc/sensu/conf.d/
```

以下に置いたファイルも設定ファイルとして読み込まれる。

設定項目は

*   Client 情報
*   Handler 定義
*   Mutator 定義
*   Checks 定義

#### Sensu Server に必須の設定
Sensu Server は default handler が必ず必要になる。`/etc/sensu/config.json` や `/etc/sensu/conf.d/default_handler.json` に

```json
"handlers": {
  "default": {
    "type": "pipe",
    "command": "PATH/default"
  }
}
```

を追加する。（default handler の中身は適宜変える）

### Client Info
#### Client Info とは

*   各 Client はある定義、 Client Info を必要とする。
*   Client info は監視サービスが起動された際に publish される
    *   これがどのように Sensu に client が登録されるかの仕組みとなる

#### 必須 Client Info
Client info にはいくつかの指定必須 key-value がある。

*   name - クライアント名
*   address -  クライアントの IP アドレス
*   subscriptions - Roles のリストまたは Role 同等のもの
    *   監視内容はこれに基づく

**Example**

```json
{
  "client": {
    "name": "web00",
    "address": "127.0.0.1",
    "subscriptions": [
      "production",
      "webserver",
      "mysql"
    ]
  }
}
```

#### カスタム Client Info
Client info にはカスタム key-value を追加できる

```json
{
  "client": {
    "name": "web00",
    "address": "127.0.0.1",
    "subscriptions": [
      "production",
      "webserver",
      "mysql"
    ],
    "mysql": {
      "host": "localhost",
      "port": 3306,
      "user": "foo",
      "password": "bar"
    }
  }
}
```

## Handlers
### Handlers とは
Event Data に基づくハンドリング。

*   アラートメールの送信
*   [PagerDuty](http://www.pagerduty.com/)(クラウド通知・警報サービス) のインシデントの作成
*   [Graphite](http://graphite.wikidot.com/) へのメトリクスの保存

といったことを行う。

`/etc/sensu/config.json` や `/etc/sensu/conf.d/default_handler.json` といったファイルに記述する

### Handlers のタイプ
#### Pipe
Pipe は STDIN を通して Event Data を渡し、あるコマンドやスクリプトを実行する

**Example**

**Handler スクリプト**(`/etc/sensu/handlers/file.rb`)

```rb
#!/usr/bin/env ruby

require "rubygems"
require "json"

# Read event data
event = JSON.parse(STDIN.read, :symbolize_names => true)
# Write the event data to a file
file_name = "/tmp/sensu_#{event[:client][:name]}_#{event[:check][:name]}"
File.open(file_name, "w") do |file|
  file.write(JSON.pretty_generate(event))
end
```

**Handler 定義**

```json
{
  "handlers": {
    "file": {
      "type": "pipe",
      "command" : "/etc/sensu/handlers/file.rb"
    }
  }
}
```

コマンドを実行するのではなくメールを送信するように修正してみる

```json
{
  "handlers": {
    "mail": {
      "type": "pipe",
      "command" : "mail -s 'sensu event' email@address.com"
    }
  }
}
```

#### TCP
TCP handlers は TCP socket に Event data を書き込む

（この具体的な利用例として Sean Porter が言ってたのが、例えば application のあるポートに監視のステータスを送ることで、メンテナンスモードにサーバーが自律的に変わるとかができるようになるとか。色々広がりそう。）

**Example**

localhost:4242 の TCP socket へ event data を書き込む

```json
{
  "handlers": {
    "tcp_socket": {
      "type": "tcp",
      "socket": {
        "host": "127.0.0.1",
        "port": 4242
      }
    }
  }
}
```

#### UDP
UDP Handlers は UDP socket に Event data を書き込む

**Example**

localhost:2424 の UDP socket へ event data を書き込む

```json
{
  "handlers": {
    "udp_socket": {
      "type": "udp",
      "socket": {
        "host": "127.0.0.1",
        "port": 2424
      }
    }
  }
}
```

#### AMQP
AMQP handlers は AMQP exchange へ event data を publishing する

**Example**

```json
{
  "handlers": {
    "amqp_exchange": {
      "type": "amqp",
      "exchange": {
        "type": "topic",
        "name": "events"
      }
    }
  }
}
```

Ruby AMQP ライブラリについては[ここを](http://rubyamqp.info/articles/working_with_exchanges/)

#### Set
Handlers Sets は handler のグルーピングを行う。

*   1 つの event data を複数の handlers に渡す
*   または単純にある handler の alias を作る

**Example**

```json
{
  "handlers": {
    "default": {
      "type": "set",
      "handlers": [
        "file",
        "tcp_socket"
      ]
    }
  }
}
```

### Handler 設定
**Example**

`my_handler1` では、`settings["handlers"]["my_handler1"]["custom_setting"]` という設定値を利用している。おそらく `/etc/sensu/handlers/file.rb` にてその値を使う。

`my_handler2` では、トップレベルのネームスペースにある `settings["my_handler2"]["another_custome_setting"]` という設定値を利用している。おそらく `/etc/sensu/handlers/file.rb` にてその値を使う。

handlers 定義（設定）

```json
{
  "handlers": {
    "my_handler1": {
      "type": "pipe",
      "command": "/etc/sensu/handlers/file.rb",
      "custom_setting": "custom_val"
    },
    "my_handler2": {
      "type": "pipe",
      "command": "/etc/sensu/handlers/file.rb",
      "custom_setting": "custom_val"
    }
  },
  "my_handler2": {
    "conventional_configuration": "true",
    "another_custome_setting": "some_custom_value"
  }
}
```

### 深刻度のフィルタリング
Handlers はある深刻度のときだけ実行する、といった事が可能だ。

**Example**

PagerDuty handler は Event が check の結果が 2 である CRITICAL または OK の時だけ実行する定義（設定）

```json
{
  "handlers": {
    "pagerduty": {
      "type": "pipe",
      "command": "/etc/sensu/handlers/pagerduty.rb",
      "severities": [
        "critical",
        "ok"
      ]
    }
  }
}
```

## Mutators
### Mutators とは
特定の handler に渡す前に event data を mutate(= 変異させる) もの。

```
cat event.json | mutator.rb | handler.rb
```

### Mutators の例
event data (JSON データ) に、`:mutated => true, :its_a_tumor => true` という key-value をマージさせる。（mutate 済みというタグを付ける例）

**Event mutator** (`/etc/sensu/mutators/tag.rb`)

```rb
#!/usr/bin/env ruby

require "rubygems"
require "json"

# Read event data
event = JSON.parse(STDIN.read, :symbolize_names => true)
# Add a 'tag' to prove it has been mutated
event.merge!(:mutated => true, :its_a_tumor => true)
# Output mutated event data to STDOUT
puts event.to_json
```

**Mutator 定義** (設定)

```json
{
  "mutators": {
    "tag": {
      "command": "/etc/sensu/mutators/tag.rb"
    }
  }
}
```

**特定の Handler に mutator を使う**

```json
{
  "handlers": {
    "file": {
      "type": "pipe",
      "mutator": "tag",
      "command": "/etc/sensu/handlers/file.rb"
    }
  }
}
```

### ビルトイン mutators
mutator 定義をしなくても利用できるビルトイン mutators がいくつかある。

*   Only check output

## Checks
### Checks とは

*   Checks とはサービス状態をモニタリング・メトリクスの収集する Check スクリプトのこと。
*   Checks は Sensu-Client によって実行される。
    *   実行タイミングは Sensu-Server が握っている（設定ファイルに記述）
    *   Sensu-Server が定期的に Check 定義に紐づく Subscriptions を持つ client へ publish する（Check スクリプト実行の指示）
*   Checks はコマンドまたはスクリプト
    *   STDOUT / ERR に出力する
    *   終了コードを持つ
        *   `0` : OK
        *   `1` : WARNING
        *   `2` : CRITICAL
        *   `3` : UNKNOWN or CUSTOM

この仕様は Nagios と互換性があるので、Nagios plugins をそのまま利用できる。

### Checks Example

**Check plugin** (`/etc/sensu/plugins/check-chef-client.rb`)

```rb
procs = `ps aux`
running = false
procs.each_line do |proc|
  running = true if proc.include?('chef-client')
end
if running
  puts 'OK - Chef client daemon is runnig'
  exit 0
end
else
  puts 'WARNING - Chef client daemon is NOT running'
  exit 1
end
```

**Check 定義** (設定)

```json
{
  "checks": {
    "chef_client": {
      "command": "check-chef-client.rb",
      "subscribers": [
        "production"
      ],
      "interval": 60
    }
  }
}
```

定義内容

*   `chef_client`: 一意な Check 名
*   `command`: 実行する Check スクリプト
*   `subscribers`: どの subscriptions で Check を実行するか
*   `interval`: Check スクリプトを実行する間隔（秒）

### どのように Check 結果が Event を生成するか

*   デフォルトでは Check スクリプトの終了コードが 0 以外のとき、event が生成される

### Handler ルーティング

*   handler の指定がなければデフォルトで `default` handler へルーティングされる
*   handler は `"handler": "foo"` のように指定する
    *   複数ある場合は、`"handlers": ["foo", "bar"]` とする

**Example**

**Check 定義**（設定）

```json
{
  "checks": {
    "chef_client": {
      "command": "check-chef-client.rb",
      "subscribers": [
        "production"
      ],
      "interval": 60,
      "handlers": [
        "pagerduty",
        "irc"
      ]
    }
  }
}
```

### メトリクス Checks
Sensu の Checks における決まりとして構造データの出力先を STDOUT にする、というのがある。なのでメトリクス収集エージェントも利用できる。

*   終了コード `0` にて event を発生させるため、`"type": "metric"` を checks 定義に追加する

**Example**

**Check 定義** (`/etc/sensu/plugins/cpu-usage-metrics.sh`)

```sh
#!/bin/bash

SCHEME=`hostname`

usage()
{
  cat <<EOF
usage: $0 options

This plugin produces CPU usage (%) using /proc/stat

OPTIONS:
   -h      Show this message
   -s      Metric naming scheme, text to prepend to cpu.usage
EOF
}

while getopts "hs:" OPTION
  do
    case $OPTION in
      h)
        usage
        exit 1
        ;;
      s)
        SCHEME="$OPTARG"
        ;;
      ?)
        usage
        exit 1
        ;;
    esac
done

get_idle_total()
{
  CPU=(`cat /proc/stat | grep '^cpu '`)
  unset CPU[0]
  IDLE=${CPU[4]}
  TOTAL=0
  for VALUE in "${CPU[@]}"; do
    let "TOTAL=$TOTAL+$VALUE"
  done
}

get_idle_total
PREV_TOTAL="$TOTAL"
PREV_IDLE="$IDLE"

sleep 1

get_idle_total

let "DIFF_IDLE=$IDLE-$PREV_IDLE"
let "DIFF_TOTAL=$TOTAL-$PREV_TOTAL"
let "DIFF_USAGE=(1000*($DIFF_TOTAL-$DIFF_IDLE)/$DIFF_TOTAL+5)/10"

echo "$SCHEME.cpu.usage $DIFF_USAGE `date +%s`"
```

check スクリプトはこんな出力をする

```
foo.cpu.usage 4 1350486269
```

**Check 定義**（設定）

```json
{
  "checks": {
    "cpu_usage_metrics": {
      "type": "metric",
      "command": "cpu-usage-metrics.sh",
      "subscribers": [
        "production"
      ],
      "interval": 10
    }
  }
}
```

### Standalone Checks
Standalone Checks は Sensu-Client 自身によってスケジューリングされる Check 。

*   定義された client ではどの client でもスケジューリングされ実行される
    *   つまり subscriptions と無関係に実行されるということ
*   `"standalone": true` を Check 定義に追加し、`"subscribers"` を取り除く

**Examples**

**Check 定義**（設定）

```json
{
  "checks": {
    "cpu_usage_metrics": {
      "type": "metric",
      "command": "cpu-usage-metrics.sh",
      "standalone": true,
      "interval": 10
    }
  }
}
```

### Check コマンドへ引数を渡す
client 情報のカスタム key-value を利用する

**Example**
カスタム key-value `mysql.user` と `mysql.password` を利用する

**Check 定義**（設定）

```json
{
  "checks": {
    "chef_client": {
      "command": "check-mysql-replication.rb --user :::mysql.user::: --password :::mysql.password:::",
      "subscribers": [
        "mysql"
      ],
      "interval": 60
    }
  }
}
```

### カスタム Check 定義 key-value
Check 定義にもカスタム key-value を追加することができる。これにより、handler をよりクリエイティブにできる。

よくやるのは、`"occurrences"` という、「何度発生したら handler が動くようにするかの最低回数」を追加する

**Example**

**Check 定義**（設定）

```json
{
  "checks": {
    "chef_client": {
      "command": "check-chef-client.rb",
      "subscribers": [
        "production"
      ],
      "interval": 60,
      "occurrences": 2
    }
  }
}
```

### Flap 検知
Check の結果が "頻繁に" 変化しすぎるとき（監視設定が悪かったりネットワークの問題だったり）、`"flapping": true` を Event Data に追加することができる。

flapping マークが着いた Event は flapping が解消するまでマークは消えない。

**Example**

**Check 定義**（設定）

```json
{
  "checks": {
    "chef_client": {
      "command": "check-chef-client.rb",
      "subscribers": [
        "production"
      ],
      "interval": 60,
      "low_flap_threshold": "5",
      "high_flap_threshold": "25"
    }
  }
}
```

## Extensions
大量のトラフィックに対して handling, mutating を行うときに有効

## コミュニティプラグイン
[sensu-community-plugins](https://github.com/sensu/sensu-community-plugins)

コミュニティにて開発されている handlers / plugins (check script) が置いてある。

* [Sensu の Community Plugin の一覧 日本語](http://www.ryuzee.com/contents/blog/6877)


## Tips
### Sensu Admin
![](http://www.ragedsyscoder.com/monitoring-slides/file/img/sensu-admin.png)

モバイル対応済みの WebUI

### Uchiwa
[uchiwa](https://github.com/palourde/uchiwa)
Node.js ベースのリアルタイム WebUI 。データベースいらずで気軽。

![](http://beingasysadmin.com/images/uchiwa1.png)
![](http://beingasysadmin.com/images/uchiwa2.png)

### デバッグ
ログ一覧

```
/var/log/sensu/
  sensu-api.log
  sensu-client.log
  sensu-dashboard.log
  sensu-server.log
```

### 〜が立ち上がらない

1.  Redis, RabbitMQ
2.  Sensu-Server
3.  Sensu-API
4.  Sensh-Dashboard

の順で起動するので、先に起動しておくべきものが起動してなかったり、
起動してるけど接続出来てなかったり。

/etc/sensu/config.json の rabbitmq や redis への接続先や iptables など

AWS だと、Redis, RabbitMQ, API のポートを空けておかないといけない

### monitor cookbook メモ

* checks
 * plugin は cookbook file
 * config は data bag (sensu_checks)
* hanlers
 * script は cookbook file
 * config は recipe (Ex.) _graphite_handler.rb
 * set config は attributes (Ex.) metrics set を作りたい時は sensu.handlers.metrics = ["a", "b"]
* chef-monitor の _graphite_handler.rb の bug


## 関連リンク / 参照

* [sensu / sensu - GitHub](https://github.com/sensu/sensu)
* [公式ドキュメント](http://sensuapp.org/)
* [sensu-admin - GitHub](https://github.com/sensu/sensu-admin)
* Graphite
* [Giraffe](http://www.moongift.jp/2012/12/20121231-2/)
* statsD
* [flpjck / flapjack](https://github.com/flpjck/flapjack)
 * 通知をルーティングしてくれる OSS
* [Sensu の Community Plugin の一覧](http://www.ryuzee.com/contents/blog/6877)
* [Sensuを使って自由度の高い監視システムの構築を行う方法](http://www.ryuzee.com/contents/blog/6843)
* [ようへいの日々精進 XP sensu カテゴリ](http://inokara.hateblo.jp/category/sensu)
 * [FAQ 日本語](https://github.com/inokappa/sensu-docs/blob/japanese/source/docs/0.12/faq.md)
* [監視ソフトをNagiosからSensuに切り替えて2ヶ月経ったのでまとめた](http://blog.glidenote.com/blog/2013/11/26/sensu/)
* [Sensuを使ってクラウド環境の監視を効率よく行おう](http://wadap.hatenablog.com/entry/2014/03/30/140913)
 * iOS アプリ期待！