ようへいの日々精進XP

よかろうもん

fluentd 復習(3)〜 HTTP API + Fluentd で始める RabbitMQ のリソースモニタリング 〜

ども、かっぱです。

sensu では

RabbitMQ が重要な役割を担っているはずなんだけど内部情報とかよく解ってなくて本当に申し訳ございません。せめて内部リソースモニタリングしたいなって思っていたら RabbitMQ には HTTP API が提供されているのでコレと fluentd 復習の第三弾として flunet-logger-ruby を絡めてリソースモニタリングを試してみたい。


参考


実装

HTTP API を有効にする

RabbitMQ は既に導入済みの環境にて以下のように HTTP API を有効にする。

rabbitmq-plugins enable rabbitmq_management

上記を実行した後でブラウザにて RabbitMQ ホストのポート 15672 にアクセスしてログインすると以下のように表示される。

f:id:inokara:20150313094431p:plain

この状態で既に HTTP API が利用出来る状態となっているので試しにエンドポイントにアクセスしてみる。

curl -s http://user:pass@rabbitmq_host:15672/api/overview | python -m json.tool

以下のように出力される。

{
   "contexts": [
        {
            "description": "RabbitMQ Management",
            "node": "rabbit@f050e18656fa",
            "path": "/",
            "port": 15672
        },
        {
            "description": "Redirect to port 15672",
            "ignore_in_use": true,
            "node": "rabbit@f050e18656fa",
            "path": "/",
            "port": 55672
        }
    ],
    "erlang_full_version": "Erlang R14B04 (erts-5.8.5) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:30] [kernel-poll:true]",
    "erlang_version": "R14B04",
    "exchange_types": [
        {
            "description": "AMQP topic exchange, as per the AMQP specification",
            "enabled": true,
            "name": "topic"
        },
        {
            "description": "AMQP fanout exchange, as per the AMQP specification",
            "enabled": true,
            "name": "fanout"
        },
        {
            "description": "AMQP direct exchange, as per the AMQP specification",
            "enabled": true,
            "name": "direct"
        },
        {
            "description": "AMQP headers exchange, as per the AMQP specification",
            "enabled": true,
            "name": "headers"
        }
    ],
    "listeners": [
        {
            "ip_address": "::",
            "node": "rabbit@f050e18656fa",
            "port": 5672,
            "protocol": "amqp"
        }
    ],
    "management_version": "3.1.5",
    "message_stats": [],
    "node": "rabbit@f050e18656fa",
    "object_totals": {
        "channels": 0,
        "connections": 0,
        "consumers": 0,
        "exchanges": 8,
        "queues": 0
    },
    "queue_totals": [],
    "rabbitmq_version": "3.1.5",
    "statistics_db_node": "rabbit@f050e18656fa",
    "statistics_level": "fine"
}

エンドポイントの一覧は以下が参考になる。

実装という程では無いけど

rabbitmq_http_api_clientfluent-logger-ruby を使うことで自分のようなド素人でも簡単にサンプルを実装出来た。

#!/usr/bin/env ruby

require "rabbitmq/http/client"
require 'fluent-logger'

Fluent::Logger::FluentLogger.open(nil, :host=>'localhost', :port=>24224)
c = RabbitMQ::HTTP::Client.new("http://user:pass@rabbitmq_host:15672")
nodes = c.list_nodes
nodes.each do |n|
 Fluent::Logger.post("rabbitmq.metrics.#{n.name}", {"sockets_used"=> n.sockets_used, "mem_used" => n.mem_used, "run_queue" => n.run_queue, "disk_free" => n.disk_free, "proc_used" => n.proc_used, "fd_total" => n.fd_total})
end

上記を cron 等(今回は watch コマンドを利用)で定期的に実行させて HTTP API 経由でメトリクスを取得して fluentd で処理させる。

メトリクスを受け取る fluentd の設定

今回は td-agent ではなく gem からインストールした fluentd を利用した。

<source>
  type forward
  bind 0.0.0.0
  port 24224
</source>

<match rabbitmq.**>
  type copy
  <store>
    type map
    map '["stats." + tag, time, {"metric" => tag + ".sockets_used","value" => record["sockets_used"].to_i}]'
  </store>
  <store>
    type map
    map '["stats." + tag, time, {"metric" => tag + ".mem_used","value" => record["mem_used"].to_i}]'
  </store>
  <store>
    type map
    map '["stats." + tag, time, {"metric" => tag + ".run_queue","value" => record["run_queue"].to_i}]'
  </store>
  <store>
    type map
    map '["stats." + tag, time, {"metric" => tag + ".disk_free","value" => record["disk_free"].to_i}]'
  </store>
  <store>
    type map
    map '["stats." + tag, time, {"metric" => tag + ".proc_used","value" => record["proc_used"].to_i}]'
  </store>
  <store>
    type map
    map '["stats." + tag, time, {"metric" => tag + ".fd_total","value" => record["fd_total"].to_i}]'
  </store>
  #<store>
  #  type stdout
  #</store>
</match>

<match stats.**>
  type copy
  <store>
     type dd
     dd_api_key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  </store>
  <store>
    type stdout
  </store>
</match>

以下のように起動する。

fluentd -c fluentd.conf -l test.log &

簡単に起動出来るのが嬉しい。

尚、しばらくすると以下のようなログが出力される。

2015-03-13 15:39:53 +0000 stats.rabbitmq.metrics.rabbit@f050e18656fa: {"metric":"rabbitmq.metrics.rabbit@f050e18656fa.run_queue","value":0}
2015-03-13 15:39:53 +0000 stats.rabbitmq.metrics.rabbit@f050e18656fa: {"metric":"rabbitmq.metrics.rabbit@f050e18656fa.disk_free","value":508637184}
2015-03-13 15:39:53 +0000 stats.rabbitmq.metrics.rabbit@f050e18656fa: {"metric":"rabbitmq.metrics.rabbit@f050e18656fa.proc_used","value":193}
2015-03-13 15:39:53 +0000 stats.rabbitmq.metrics.rabbit@f050e18656fa: {"metric":"rabbitmq.metrics.rabbit@f050e18656fa.fd_total","value":1048576}
2015-03-13 15:40:04 +0000 stats.rabbitmq.metrics.rabbit@f050e18656fa: {"metric":"rabbitmq.metrics.rabbit@f050e18656fa.sockets_used","value":1}
2015-03-13 15:40:04 +0000 stats.rabbitmq.metrics.rabbit@f050e18656fa: {"metric":"rabbitmq.metrics.rabbit@f050e18656fa.mem_used","value":586710400}

実際の可視化は...

お世話になっております... datadog 先生。

f:id:inokara:20150314002333p:plain

上図は約一日放置しておいた RabbitMQ の一部のメトリクスを可視化した状態。 特ににも動かしていないので目立ったメトリクスの変動等は無いが、数行のスクリプトと fluentd だけで RabbitMQ の内部情報の可視化を行うことが出来た。

尚、datadog に関しては...

  • データの保存が一日分
  • 登録可能なホスト数に制限がある

という制限を除けば無償のアカウントで使い続けられるのが嬉しい!


ということで

所感

RabbitMQ の HTTP APIRuby 製のクライアント、fluentd を利用した内部情報の可視化をやってみた。fluent-logger-ruby は初めて利用してみたがビックリするくらい簡単に動いたので先人の方々に足を向けて寝られない。尚、fluentd を利用することなく直接 Datadog の API を叩くことでメトリクスを送ることが出来るが、fluentd に流すことで Datadog 以外の環境での可視化や保存も検討出来るという点でメリットがあると考えている。

rabbitmq_http_api_client について

RabbitMQ の HTTP API からメトリクス情報を取得するにあたって利用した rabbitmq_http_api_client だが、ドキュメントを見ると情報の収集だけではなく Queue や Exchange に対するオペレーションをはじめ vhost や permission の設定等の Management Console で出来るオペレーションがそのまま行えるようなので機会があれば深堀りしてみたいと思う。