ようへいの日々精進XP

よかろうもん

Sensu 復習(2)〜 OpenWeatherMap と Sensu + Grafana で熱中症対策 〜

はじめに

ムシムシとした日が福岡では続いていますがいかがお過ごしでしょうか。

既に奥さんが熱中症っぽい症状で寝込んでしまっているのを見て、旦那として看病以外で役に立てないものか思いを巡らせて辿り着いたのが、気温、湿度を収集して可視化、不快指数を計算して事前に警告する等が手元の環境で出来そうなので試してみたメモ。


OpenWeatherMap で温度と湿度を取得する

OpenWeatherMap とは

openweathermap.org

ざっくり、無料で天気の情報を API 提供してくれる有り難いサイトで、例えば、福岡の天気は以下のようなリクエストを投げる。

$ curl -s 'http://api.openweathermap.org/data/2.5/weather?q=fukuoka-shi,jp&units=metric' | python -m json.tool

以下のようなレスポンスが返ってくる。

{
    "base": "stations",
    "clouds": {
        "all": 75
    },
    "cod": 200,
    "coord": {
        "lat": 33.609999999999999,
        "lon": 130.41999999999999
    },
    "dt": 1436654395,
    "id": 1863967,
    "main": {
        "humidity": 83,
        "pressure": 1004,
        "temp": 26.760000000000002,
        "temp_max": 28.329999999999998,
        "temp_min": 24.440000000000001
    },
    "name": "Fukuoka-shi",
    "sys": {
        "country": "JP",
        "id": 7546,
        "message": 0.0149,
        "sunrise": 1436559409,
        "sunset": 1436610639,
        "type": 1
    },
    "visibility": 10000,
    "weather": [
        {
            "description": "broken clouds",
            "icon": "04n",
            "id": 803,
            "main": "Clouds"
        }
    ],
    "wind": {
        "deg": 110,
        "speed": 1.5
    }
}

レスポンスの詳細については以下のドキュメントを。

openweathermap.org

ざくっと、今回利用したいキーは以下のとおり。

JSON キー 内容 備考
['main']['humidity'] 湿度
['main']['temp'] 温度 リクエストパラメータで units=metric を付けると摂氏となる

不快指数とは

keisan.casio.jp

気温と湿度から算出する夏の蒸し暑さを数量的に表した指数で計算式は下記の通り。

0.81 × 気温 + 0.01 × 湿度 × (0.99 × 気温 - 14.3) + 46.3

例えば、先ほどの API レスポンスから算出すると以下のとおりとなる。

0.81 × 26.760000000000002 + 0.01 × 83 × (0.99 × 26.760000000000002 - 14.3) + 46.3 = 76.8552

算出された不快指数から体感を以下のように分類されている。(Wikipedia より引用)

不快指数 体感
~55 寒い
55~60 肌寒い
60~65 何も感じない
65~70 快い
70~75 暑くない
75~80 やや暑い
80~85 暑くて汗が出る
85~ 暑くてたまらない

ということで、温度、湿度の値を収集しつつ、不快指数を算出してアラートを上げる仕組みを作ってみることにしよう。


Sensu と Grafana の用意

構成

f:id:inokara:20150711150616p:plain

しきい値

しきい値は以下のように定義。

不快指数 体感 Sensu のしきい値
~55 寒い Normal(exit 0)
55~60 肌寒い Normal(exit 0)
60~65 何も感じない Normal(exit 0)
65~70 快い Normal(exit 0)
70~75 暑くない Normal(exit 0)
75~80 やや暑い Warning(exit 1)
80~85 暑くて汗が出る Warning(exit 1)
85~ 暑くてたまらない Critical(exit 2)

プラグイン

以下のような OpenWeatherMap から取得出来るレスポンスをパースして標準出力に出力するだけのスクリプトを用意。

gist.github.com

スクリプトコマンドラインから以下のように実行することで動作確認をすることが出来る。

# チェック
$ ./open_weather_map.py fukuoka-shi check
fukuoka-shi temperature-humidity_index.current 78.1478 warning

# メトリクス
$ ./open_weather_map.py fukuoka-shi,tokyo metrics
stats.fukuoka-shi.temperature.current 29.88 1436683931

stats.fukuoka-shi.humidity.current 58 1436683931

stats.fukuoka-shi.temperature-humidity_index.current 78.1478 1436683931

stats.tokyo.temperature.current 31.76 1436683931

stats.tokyo.humidity.current 70 1436683931

stats.tokyo.temperature-humidity_index.current 82.883 1436683931

メトリクスは第一引数に都市名をカンマ区切りで並べることで複数の都市の温度と湿度を取得出来るようにした。

ということで、メトリクス収集用の Sensu Check の設定。

{
  "checks": {
    "metrics-weather": {
      "type": "metric",
      "handlers": ["graphite", "file"],
      "command": "open_weather_map.py fukuoka-shi,tokyo metrics",
      "interval": 300,
      "standalone": true
    }
  }
}

更に不快指数を判断してアラートを上げる為の Sensu Check 設定。

{
  "checks": {
    "check-temperature-humidity-index-fukuoka": {
      "handlers": ["file"],
      "command": "open_weather_map.py fukuoka-shi check",
      "interval": 300,
      "standalone": true
    }
  }
}

例えば、本日の状況

メトリクス

f:id:inokara:20150712154825p:plain

Grafana の Singlestat パネルにもしきい値を設定している。湿度は 60% 前後であるが気温が 30 度を超えており、不快指数は注意レベル。

チェック

f:id:inokara:20150712154838p:plain

Grafana 同様に不快指数は Warning となっている。


ということで...

参考

openweathermap.org

今回利用した API 以外にも有償の API もあるようだ。

qiita.com

API レスポンスの内容について参考にさせて頂きました。

最後に

OpenWeatherMap に感謝しつつ、気温、湿度、不快指数には注意してコマメな水分補給と休養で熱中症を予防しましょう。

Sensu 復習(1)〜 Grafana 2.0 導入メモ 〜

Sensu について復習してみたいと思う。


ということで...

復習環境に Grafana 2.0 を導入した際のメモ。


Grafana とは

サイト

  • 本家

grafana.org

github.com

スクショ

説明下手なのでスクショ一枚で。

f:id:inokara:20150708073331p:plain

三行で

  • Graphite 等の時系列データベースに蓄積されたメトリクスを綺麗に見せるダッシュボード
  • Sensu でメトリクス見るならほぼ標準
  • 導入はとても簡単(でした)

サポートする時系列データベース

上記以外にも KariosDBSQL も実験的にサポートされるようだ。 尚、 Elasticsearch もサポートされているが、実際に試してみたところ手元の環境で Elasticsearch のメトリクスを Grafana に表示することは出来なかった(多分、自分のやり方が悪い)ので引き続き試す。(※もしかしたら Elasticsearch をサポートしているのはダッシュボードを管理している部分だけなのかもしれない...)

2.0 にて追加された機能

docs.grafana.org

上記に纏まっているものをザクっと要約。

  • 独自のバックエンドデータベースが追加
  • バックエンドデータベースへの接続をプロキシするようになり CROS も気にする必要が無い
  • 三種類(閲覧、編集、管理者)の権限をユーザー又は組織に対して付与出来るようになった
  • ダッシュボード共有機能の強化(スナップショットの共有、共有の期限等)
  • パネル毎に時系列を設定出来るようになった
  • raintank との連携

上記以外でも Grafana 自体がパッケージ化されたことにより各ディストリビューション毎のパッケージ管理ツールで導入することが出来るようにもなっている。


ということで...導入

参考

docs.grafana.org

今回は VirtualBox 上の CentOS 6.4 にインストールする。

yum リポジトリの追加

/etc/yum.repos.d/grafana.repo を以下のように作成。

[grafana]
name=grafana
baseurl=https://packagecloud.io/grafana/stable/el/6/$basearch
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://packagecloud.io/gpg.key https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt

yum install

sudo yum install grafana

以上。

起動

sudo service grafana

以上。

尚、デフォルトではポート 3000 番で Listen するので、残念ながら Uchiwa と同居する場合にはどちらかのポートを変えなければいけないということに気づくはず...。Grafana のポート変更を行う場合には /etc/grafana/grafana.ini の設定にて変更を行う。(設定については別途)

早速、ブラウザでアクセスすると以下のようなログイン画面となる。

f:id:inokara:20150708082824p:plain

デフォルトの認証情報はユーザー名、パスワード共に admin となるので、ログインを確認したら変更しておく。

設定

Grafana 自体の設定は /etc/grafana/grafana.ini にて行う。

##################### Grafana Configuration Example #####################
#
# Everything has defaults so you only need to uncomment things you want to
# change

; app_mode = production

#################################### Paths ####################################
[paths]
# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is useD)
#
;data = /var/lib/grafana
#
# Directory where grafana can store logs
#
;logs = /var/log/grafana

#################################### Server ####################################
[server]
# Protocol (http or https)
;protocol = http

# The ip address to bind to, empty will bind to all interfaces
;http_addr =

# The http port  to use
http_port = 3001

# The public facing domain name used to access grafana from a browser
;domain = localhost

# The full public facing url
;root_url = %(protocol)s://%(domain)s:%(http_port)s/

# Log web requests
;router_logging = false

# the path relative working path
;static_root_path = public

# enable gzip
;enable_gzip = false

# https certs & key file
;cert_file =
;cert_key =

#################################### Database ####################################
[database]
# Either "mysql", "postgres" or "sqlite3", it's your choice
;type = sqlite3
;host = 127.0.0.1:3306
;name = grafana
;user = root
;password =

# For "postgres" only, either "disable", "require" or "verify-full"
;ssl_mode = disable

# For "sqlite3" only, path relative to data_path setting
;path = grafana.db

#################################### Session ####################################
[session]
# Either "memory", "file", "redis", "mysql", default is "memory"
;provider = file

# Provider config options
# memory: not have any config yet
# file: session dir path, is relative to grafana data_path
# redis: config like redis server addr, poolSize, password, e.g. `127.0.0.1:6379,100,grafana`
# mysql: go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1)/database_name`
;provider_config = sessions

# Session cookie name
;cookie_name = grafana_sess

# If you use session in https only, default is false
;cookie_secure = false

# Session life time, default is 86400
;session_life_time = 86400

#################################### Analytics ####################################
[analytics]
# Server reporting, sends usage counters to stats.grafana.org every 24 hours.
# No ip addresses are being tracked, only simple counters to track
# running instances, dashboard and error counts. It is very helpful to us.
# Change this option to false to disable reporting.
;reporting_enabled = true

# Google Analytics universal tracking code, only enabled if you specify an id here
;google_analytics_ua_id =

#################################### Security ####################################
[security]
# default admin user, created on startup
;admin_user = admin

# default admin password, can be changed before first start of grafana,  or in profile settings
;admin_password = admin

# used for signing
;secret_key = SW2YcwTIb9zpOOhoPsMm

# Auto-login remember days
;login_remember_days = 7
;cookie_username = grafana_user
;cookie_remember_name = grafana_remember

#################################### Users ####################################
[users]
# disable user signup / registration
;allow_sign_up = true

# Allow non admin users to create organizations
;allow_org_create = true

# Set to true to automatically assign new users to the default organization (id 1)
;auto_assign_org = true

# Default role new users will be automatically assigned (if disabled above is set to true)
;auto_assign_org_role = Viewer

#################################### Anonymous Auth ##########################
[auth.anonymous]
# enable anonymous access
;enabled = false

# specify organization name that should be used for unauthenticated users
;org_name = Main Org.

# specify role for unauthenticated users
;org_role = Viewer

#################################### Github Auth ##########################
[auth.github]
;enabled = false
;client_id = some_id
;client_secret = some_secret
;scopes = user:email
;auth_url = https://github.com/login/oauth/authorize
;token_url = https://github.com/login/oauth/access_token
;api_url = https://api.github.com/user
# Uncomment bellow to only allow specific email domains
; allowed_domains = mycompany.com othercompany.com

#################################### Google Auth ##########################
[auth.google]
;enabled = false
;client_id = some_client_id
;client_secret = some_client_secret
;scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
;auth_url = https://accounts.google.com/o/oauth2/auth
;token_url = https://accounts.google.com/o/oauth2/token
;api_url = https://www.googleapis.com/oauth2/v1/userinfo
# Uncomment bellow to only allow specific email domains
; allowed_domains = mycompany.com othercompany.com

#################################### Logging ##########################
[log]
# Either "console", "file", default is "console"
# Use comma to separate multiple modes, e.g. "console, file"
;mode = console, file

# Buffer length of channel, keep it as it is if you don't know what it is.
;buffer_len = 10000

# Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
;level = Info

# For "console" mode only
[log.console]
;level =

# For "file" mode only
[log.file]
;level =
# This enables automated log rotate(switch of following options), default is true
;log_rotate = true

# Max line number of single file, default is 1000000
;max_lines = 1000000

# Max size shift of single file, default is 28 means 1 << 28, 256MB
;max_lines_shift = 28

# Segment log daily, default is true
;daily_rotate = true

# Expired days of log file(delete after max days), default is 7
;max_days = 7

#################################### AMPQ Event Publisher ##########################
[event_publisher]
;enabled = false
;rabbitmq_url = amqp://localhost/
;exchange = grafana_events

上記の通り、全文掲載。

英語がダメダメな自分でもドキュメントから読み取れない機能や設定は設定ファイルを見ると判るのがせめてもの救いだが、先述のポート番号変更は http_port を修正する。また、GithubGoogle の認証への対応や AMQP を使ったイベントの配信等もサポートしているようだ(何に使うんだ...)。


ハンズオン

やること

せっかくなのでハンズオンしてみたい。

  • データソース(データベース)の登録
  • メトリクスの登録
  • ダッシュボードの共有機能を試す

尚、ハンズオンの環境は下記の通り、Sensu から Graphite に送られてくるメトリクスを利用してダッシュボードを作成して共有してみる。

f:id:inokara:20150708100143p:plain

データソース(データベース)の登録

詳細はドキュメントを。

f:id:inokara:20150708100945p:plain

  • Data Source メニューから Add new をクリックして登録する
  • Type から Graphite を選択
  • Url には http://localhost:80 を記載し AccessProxy を選択する

尚、Access には ProxyDirect があり、違いについては以下の通り。

  • Proxy は Grafana Backend を介したアクセス
  • Direct はブラウザから直接アクセス(2.0 以前の接続方法だったはず...)

メトリクスの登録(1)

Graph パネルにメトリクスを登録してみる。 詳細はドキュメントを。

f:id:inokara:20150708143632p:plain

ADD ROW をクリックして Row を追加、引き続き、Add Panel から Graph をクリックする。

f:id:inokara:20150708145028p:plain

Metrics タブをクリックして必要なメトリクスをプルダウンで選択していく。

メトリクスの登録(2)

Singlestat パネルにメトリクスを登録してみる。 詳細はドキュメントを。

f:id:inokara:20150708150636p:plain

上図のように時系列データではなく、ある時点の値や合計値、平均値等を表示することが出来る。

f:id:inokara:20150708150413p:plain

メトリクスの登録については Graph パネルと同様にデータソースを指定してメトリクスを選択し、Options タブにて平均値や合計値等を指定する。また、しきい値を設けることで値によって色を付ける事も出来る。

ダッシュボードの共有機能を試す

実際の運用でダッシュボードを共有することがあるか(ニーズがあるか)判らないが...Share Dashboard をクリックすると以下のようにダッシュボードのリンクが表示される。

f:id:inokara:20150708153443p:plain

これをこのまま共有するとメトリクスのクエリ情報等の外部に共有する必要の無い情報までも共有されてしまう。

f:id:inokara:20150708154842p:plain

これを解決する為に上記のようなスナップショット機能が提供されており、以下のような 2 つの機能が提供されている。尚、実際に試してみたところ、スナップショット機能はスナップショットを作成した時点よりも以前のメトリクスのみが確認出来るのでまさにスナップショット。

  • Publish snapshots(snapshot.raintank.io にパブリッシュする)
  • Local snapshot(ローカルの URL が与えられるが Link とは異なる)

今回は Publish snapshots を利用して snapshot.raintank.io に時間限定(1 時間)で公開してみた。

f:id:inokara:20150708155306p:plain

表示されている URL にアクセスしてみると...

f:id:inokara:20150708155329p:plain

おお。 しかも、日本語のタイトルでも問題無さそう。

f:id:inokara:20150708155401p:plain

そして、気になるクエリ等が共有されていないかを念のために確認してみるとクエリ等は共有されていない。


ということで...

Grafana 2.0 のインストールからホンのちょっとだけ触ってみたが、データソースの指定や共有機能、今回は触れなかったユーザー管理機能等多くの機能が追加されており、また、以前のバージョンだと閲覧している PC の CPU 負荷が高くなってしまう状況も見られなかったので、従来バージョンを利用している場合にはバージョンアップを検討しても良いかもしれない。

尚、1.x 系から 2.0 へのバージョンアップについては以下のページにて言及されている。

docs.grafana.org

ポイントとしては...

  • config.js は廃止
  • データソースは Grafana のダッシュボード、HTTP API で再設定する必要がある
  • ダッシュボードは Elasticsearch のインデックスを直接参照する方法で移行する

となっている。


次回は...

github.com

から...

github.com

に変更になったことによる、導入の方法等について復習してみたい。

Elasticsearch の JVM のヒープ使用量を Graphite / Grafana で可視化するちょっとしたスクリプト

Elasticsearch の JVM ヒープ使用量を Graphite と Grafana を利用して可視化したいと思ったので Cluster APIPython を利用して実現してみた。

Cluster API

Cluster APIsNodes Stats を利用するので curl を利用して取得してみる。

f:id:inokara:20150703081941p:plain

実際に叩いてみる。

$ curl -XGET 'http://localhost:9200/_nodes/stats/jvm?pretty=true'
{
  "cluster_name" : "kappa-elasticsearch",
  "nodes" : {
    "RiQNuftXTIO2fiX4GJjO4w" : {
      "timestamp" : 1435845867712,
      "name" : "node2",
      "transport_address" : "inet[/xxx.xxx.xx.11:9300]",
      "host" : "localhost.localdomain",
      "ip" : [ "inet[/xxx.xxx.xx.11:9300]", "NONE" ],
      "jvm" : {
        "timestamp" : 1435845867712,
        "uptime_in_millis" : 44858656,
        "mem" : {
          "heap_used_in_bytes" : 105512584,
          "heap_used_percent" : 9,
          "heap_committed_in_bytes" : 259719168,
          "heap_max_in_bytes" : 1065025536,
          "non_heap_used_in_bytes" : 49661032,
          "non_heap_committed_in_bytes" : 49938432,
          "pools" : {
            "young" : {
              "used_in_bytes" : 64778368,
              "max_in_bytes" : 69795840,
              "peak_used_in_bytes" : 69795840,
              "peak_max_in_bytes" : 69795840
            },
            "survivor" : {
              "used_in_bytes" : 2255592,
              "max_in_bytes" : 8716288,
              "peak_used_in_bytes" : 8716288,
              "peak_max_in_bytes" : 8716288
            },
            "old" : {
              "used_in_bytes" : 38478624,
              "max_in_bytes" : 986513408,
              "peak_used_in_bytes" : 38478624,
              "peak_max_in_bytes" : 986513408
            }
          }
        },
        "threads" : {
          "count" : 37,
          "peak_count" : 42
        },
        "gc" : {
          "collectors" : {
            "young" : {
              "collection_count" : 41,
              "collection_time_in_millis" : 369
            },
            "old" : {
              "collection_count" : 0,
              "collection_time_in_millis" : 0
            }
          }
        },
        "buffer_pools" : {
          "direct" : {
            "count" : 198,
            "used_in_bytes" : 6273408,
            "total_capacity_in_bytes" : 6273408
          },
          "mapped" : {
            "count" : 10,
            "used_in_bytes" : 205985,
            "total_capacity_in_bytes" : 205985
          }
        }
      }
    },
    "BPARRUhjSMuQlJBBxrp4Mg" : {
      "timestamp" : 1435845867715,
      "name" : "node1",
      "transport_address" : "inet[/xxx.xxx.xx.10:9300]",
      "host" : "localhost.localdomain",
      "ip" : [ "inet[/xxx.xxx.xx.10:9300]", "NONE" ],
      "jvm" : {
        "timestamp" : 1435845867715,
        "uptime_in_millis" : 44867169,
        "mem" : {
          "heap_used_in_bytes" : 79838632,
          "heap_used_percent" : 7,
          "heap_committed_in_bytes" : 259719168,
          "heap_max_in_bytes" : 1065025536,
          "non_heap_used_in_bytes" : 48005728,
          "non_heap_committed_in_bytes" : 48300032,
          "pools" : {
            "young" : {
              "used_in_bytes" : 40145808,
              "max_in_bytes" : 69795840,
              "peak_used_in_bytes" : 69795840,
              "peak_max_in_bytes" : 69795840
            },
            "survivor" : {
              "used_in_bytes" : 2416792,
              "max_in_bytes" : 8716288,
              "peak_used_in_bytes" : 8716288,
              "peak_max_in_bytes" : 8716288
            },
            "old" : {
              "used_in_bytes" : 37276032,
              "max_in_bytes" : 986513408,
              "peak_used_in_bytes" : 37276032,
              "peak_max_in_bytes" : 986513408
            }
          }
        },
        "threads" : {
          "count" : 39,
          "peak_count" : 44
        },
        "gc" : {
          "collectors" : {
            "young" : {
              "collection_count" : 40,
              "collection_time_in_millis" : 355
            },
            "old" : {
              "collection_count" : 0,
              "collection_time_in_millis" : 0
            }
          }
        },
        "buffer_pools" : {
          "direct" : {
            "count" : 68,
            "used_in_bytes" : 4085453,
            "total_capacity_in_bytes" : 4085453
          },
          "mapped" : {
            "count" : 10,
            "used_in_bytes" : 206859,
            "total_capacity_in_bytes" : 206859
          }
        }
      }
    }
  }
}

上記のように Elasticsaerch のクラスタを構成している全てのノード JVM に関する各種値を取得することが出来るので、これらの値から jvm キーに含まれる mem キーの heap_used_in_bytes を取得するスクリプトを作成してみたい。スクリプトの言語は基本的に問わないが、CentOS ですぐに利用出来る Python を利用して作成する。


スクリプトと Sensu からの利用

スクリプト

以下のようなスクリプトを作ってみた。

#!/usr/bin/env python

import json, urllib2, sys, time, random

argvs     = sys.argv
hosts = argvs[1].split(",")

url = 'http://' + random.choice(hosts) + ':9200/_nodes/stats/jvm'
r = urllib2.urlopen(url)
j = json.loads(r.read())
nodes = j['nodes']
for node in nodes:
        timestamp = int(time.time())
        label = argvs[2] + '.' + j['nodes'][node]['name'] + '.' + 'elasticsearch_jvm.heap_used'
        print '%s %s %d\n' % (label, j['nodes'][node]['jvm']['mem']['heap_used_in_bytes'], timestamp)
r.close()

PythonHello World レベルの為、謎や無駄が多くなってしまっているかもしれない... で、実際に利用する場合には、以下のように利用する。

$ ./script.py node1,node2 foo

第一引数にはクラスタに含まれるノードをカンマ区切りで渡す。また、第二引数には Graphite で利用するトップレベルネームを指定する。 実行結果は以下のようになる。

foo.node2.elasticsearch_jvm.heap_used 84739064 1435879508

foo.node1.elasticsearch_jvm.heap_used 51600232 1435879508

Sensu からの利用

既に Sensu プラグインでは sensu-plugins-elasticsearch というプラグインが配布されており、実際に metrics-es-node-graphite.rb を利用してみるとプラグインを動かしているホストの CPU 使用率が高くなってしまう現象が見られた(自分の環境だけかもしれないが)ので、今回はシンプルにヒープサイズのみを取得するようにしてみた。

以下のような利用シーンをイメージ。

f:id:inokara:20150703084012p:plain

スクリプト/etc/sensu/plugins 以下に実行権限を付けて設置する。

sudo mv script.py /etc/sensu/plugins/

Sensu Check の設定は以下のように。今回は Standalone Check を有効にしている。

{
  "checks": {
    "metrics-elasticsearch-jvm-heap": {
      "type": "metric",
      "handlers": ["graphite", "file"],
      "command": "script.py node1,node2 foo",
      "interval": 30,
      "standalone": true
    }
  }
}

設定すると以下のように Grafana で確認することが出来るようになる。

f:id:inokara:20150703085508p:plain

おお、Grafana キレイ。

おまけ

Cluster API のエンドポイントをランダムに選択する実装

上記例の node1 と node2 のどちらのノードでも Cluster API が同様に結果を返してくれるのであれば、スクリプトでもどちらかのノードにアクセスさせることで、負荷の偏りを防ぐ、片方のノードが落ちてもメトリクスの収集が出来る(その前に復旧すれば良いが...)というメリットがありそうなので、アクセスするエンドポイントをランダムに選択するようにしてみたのが以下。

(略)
hosts = argvs[1].split(",")

url = 'http://' + random.choice(hosts) + ':9200/_nodes/stats/jvm'

スクリプトの第一引数で対象のノードをカンマ区切りで指定するところと random.choice を利用しているところがミソ。カンマ区切りの第一引数は配列になって変数 hosts に代入されて、random.choice により配列からランダムに一つのノードが取り出されてエンドポイントにアクセスする。

$ ./script.py node1,node2 foo
http://node1:9200/_nodes/stats/jvm
foo.node2.elasticsearch_jvm.heap_used 65309008 1435882093

foo.node1.elasticsearch_jvm.heap_used 106770624 1435882093

$ ./script.py node1,node2 foo
http://node2:9200/_nodes/stats/jvm
foo.node2.elasticsearch_jvm.heap_used 65323128 1435882095

foo.node1.elasticsearch_jvm.heap_used 106774232 1435882095

$ ./script.py node1,node2 foo
http://node2:9200/_nodes/stats/jvm
foo.node2.elasticsearch_jvm.heap_used 65328928 1435882099

foo.node1.elasticsearch_jvm.heap_used 106774232 1435882099

$ ./script.py node1,node2 foo
http://node1:9200/_nodes/stats/jvm
foo.node2.elasticsearch_jvm.heap_used 66125096 1435882100

foo.node1.elasticsearch_jvm.heap_used 107118056 1435882100

実際にアクセスさせてみると、上記のように node1 と node2 がランダムに利用されていることが判る。

EC2 の meta-data を利用してインスタンス ID を取得する場合

以下のように urllib2 を利用して簡単に取得することが出来る。

meta_url = 'http://169.254.169.254/latest/meta-data/instance-id'
i = urllib2.urlopen(meta_url)
instance_id = i.read()
i.close()

ということで

も少しちゃんとスクリプトを作れるようになりたいのと、ヒープ使用量以外の値も同時に取得出来るように少し修正しなければ。

Jolokia を利用して JMX のメトリクスを監視する sensu プラグインを作った

ども、ご無沙汰してます。かっぱです。

はじめに

しばらく Qiita に記事を書いてましたが、はてなブログにも適宜書いていきたいと思います。どんな基準でかき分けるかはあまり考えてません。すいません。

ということで、久しぶりのネタは Jolokia という JMX の HTTP プロキシツールを利用して sensu プラグインを作ったのでメモです。尚、Jolokia の記事に関しては以下のブログ記事を御覧ください。


参考にした記事と過去に書いた Jolokia ネタ

参考にした記事。


プラグインについて

github

(汚いです...orz)

使い方

HeapMemoryUsage の使用量を監視したい場合には以下のように実行します。

./check-jmx-jolokia.rb -u http://127.0.0.1:8778 -m "java.lang:type=Memory" -a "HeapMemoryUsage" -i used -k value -w 10 -c -100

オプションは以下の通り。

オプション ロングオプション パラメータ例 説明
-m --mbean java.lang:type=Memory Mbean を指定
-a --attribute HeapMemoryUsage Attribute を指定
-i --innner-path used inner path を指定
-k --key value 出力される JSON のキーを指定
-w --warning 10 Warning の値を指定
-c --critical 100 Critical の値を指定

その他にもオプションがありますが設定しなくても利用することが可能です。

プラグインの todo

  • コードの最適化
  • Mbean や Attribute を複数指定出来るようにする

プラグインを作る上で Jolokia についてちょっと調べた

プラグインを作るにあたって以下のように Jolokia のエンドポイントや利用の方法について調べてみました。

取得したい情報(MBean や Attribute 等)を指定して取得出来るのは便利だと思います。また、FullGC 等を実行出来るらしいので Java 環境を運用する際には利用を検討したい Jolokia です。


ということで

Jolokia についてはちゃんと把握しきれていないので、引続き触っていきたいと思います。

Sensu 小ネタ(可用性とか)

小ネタ

RabbitMQ に依存しなくなりそう??

可用性に関する議論

以下の記事や issue が興味深い。

上記の記事や issue をざっくりまとめると...

  • Sensu のデータをミラーリングする必要はあまりない
  • むしろ、データ(イベント)が発生した時間にセンシティブになる必要がある
  • HA Proxy または Route 53L4 レベルの単純な HA 構成で問題無い
  • キューのミラーリングはオススメしない...(マジか...)

あれ...

  • RabbitMQ の可用性はあまり深く考慮する必要がない??

もう怖くない(はず) RabbitMQ クラスタリングの云々かんぬん

Sensu とかに RabbitMQ が動いてて「なんだこれ?」と思ったのは自分だけでしょうか?、おはようございます。かっぱです。

はじめに

  • ちゃんと理解しきれてない RabbitMQクラスタ周りを改めてメモった

メモ


引き続き

  • 上記のメモは更新する

ちょっとだけ解った気になる Graphite と Grafana

東京からおはようございます。かっぱです。

はじめに

  • Graphite は苦手だったのでちょっと克服したくて書いた

ほげふが

に書いた。


Graphite と Grafana を 1 時間位使ってみたメモ

竹富島からこんばんわ。かっぱです。

はじめに

  • GraphiteGrafana を 1 時間位使ってみた
  • どちらともセットアップは簡単(但し、Dockerfile 作るのは苦労した)

参考


環境

  • GraphiteこちらDockerfile を使った
  • Grafana は上記の Dockerfile で構築したコンテナに手動で構築した

Graphite だけなら

以下のような手順で利用可能。

git clone https://github.com/inokappa/dockerfiles.git
cd dockerfiles/graphite
docker build -t your_name/repo_name
docker run -t -d your_name/repo_name

Graphite + Grafana は...

Dockerfile で表現しようとすると色々と面倒なので上記の Graphite コンテナ上に手動で構築した。以下、サクッと手順。(上記の DockerfileGraphite を利用出来る状態からの手順...)

まずは上記の docker コンテナを以下のようにして起動。

docker run -t -i -p 1919:1919 your_name/repo_name /bin/bash

次に tarball をダウンロードして展開。

wget http://grafanarel.s3.amazonaws.com/grafana-1.5.4.tar.gz
tar zxvf grafana-1.5.4.tar.gz

展開したファイルを DocumentRoot 以下に置く。

cp -rfp grafana-1.5.4 /var/www/html/grafana

config.js を下記のように設定、修正する。

--- grafana-1.5.4/config.sample.js      2014-05-13 13:09:27.000000000 -0400
+++ grafana/config.js   2014-05-28 09:07:15.965941347 -0400
@@ -13,7 +13,7 @@
      * elasticsearch url:
      * For Basic authentication use: http://username:password@domain.com:9200
      */
-    elasticsearch: "http://"+window.location.hostname+":9200",
+    //elasticsearch: "http://"+window.location.hostname+":9200",

     /**
      * graphite-web url:
@@ -22,7 +22,7 @@
      * in nginx or apache for cross origin domain sharing to work (CORS).
      * Check install documentation on github
      */
-    graphiteUrl: "http://"+window.location.hostname+":8080",
+    graphiteUrl: "http://172.17.0.2",

     /**
      * Multiple graphite servers? Comment out graphiteUrl and replace with something like this:

上記のように graphiteUrl: のみを設定する。今回 Graphite172.17.0.2 という IP でアクセスしているので graphiteUrl: にも "http://172.17.0.2" を設定する。

また、今回は GraphiteGrafana を同じ Apache で別のポートで動作させたいので以下のようにそれぞれ設定、修正する。

ポイントとしては graphite-web.conf の冒頭に記載されている CORS 対策の以下の記述。

Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, OPTIONS"
Header set Access-Control-Allow-Headers "origin, authorization, accept"

CORS の詳細についてはこちらあたりが詳細に書かれている。

monit で全てのサービスを起動する

これで準備完了なので以下のように monit を起動することで関連するサービスが全て起動する。

/etc/init.d/monit start

以下のように起動しているプロセス等が確認出来る。

f:id:inokara:20140528223728p:plain


Graphite と Grafana

関連するプロセスが起動して暫くすると...

Graphite

f:id:inokara:20140528224432p:plain

左ペインから cpuUsage を選択した画面。尚、Graphite > carbon > aggregater 以下のツリーは別の Docker コンテナの情報。carbonaggregater を設定することで他のホストの情報もグラフに表示することが出来る(ようだ)。


Grafana

f:id:inokara:20140528224938p:plain

Graphite に蓄積されているメトリクスから cpuUsagememUsage を表示してみた。設定も簡単で以下のように Graphite の設定が適切であれば自動的に Graphite に蓄積されているメトリクスがプルダウンに表示されて選択するだけで簡単にグラフに落としこむことが出来る。

f:id:inokara:20140528225609p:plain

簡単なのは素晴らしい。


ということで

  • GraphiteGrafana1 時間位使ってみた
  • Sensu では Graphite が標準っぽい状況なのでも少し掘り下げてみたい
  • GrafanaKibana っぽいカッコよさを残しつつ Graphite と連携して簡単にメトリクスを可視化出来るツールでお気に入り!

Chef Server を使って Sensu をセットアップするメモ

やっぱり

  • Chef Server が必要そうです
  • Chef Server の復習を兼ねて Chef 活用ガイドを片手(片手で抱えるにはチト重いけど...)に作業 done

メモ


とりあえず 作業 done


画面とか

とりあえず以下までは出来るはず

f:id:inokara:20140511230102p:plain

cron で定期的に chef-client 実行結果

f:id:inokara:20140512065228p:plain

尚、上記の Graphite グラフは Sensu と連携した図ではなく、あくまでも Chef Handerschef-client の実行結果を可視化したものなので注意。SensuGraphite についてはまた別途で。

パッと見た感じ Sensu Server を収束させる方が走るレシピの数も多くて resource も多いので処理時間は掛かっているのが解る。


ほげふが

センス、センスと言いつつ、諸般の事情により CloudWatch で当面は凌ぐことになりそうなので CloudWatch についても調査。

諦めきれないけど。