CloudWatch から取得しておいたメトリクスを Elasticsearch と Kibana で可視化

はじめに

CloudWatch からメトリクスをダウンロードし、 S3 にバックアップするところまで出来た。

次はいよいよ、ダウンロードしたメトリクスの可視化にとりかかる。 CloudWatch と同じようにチャートで見れるようにしたい。 このために CloudWatch からメトリクスをダウンロードしたからね。

可視化には、前から目をつけていた Elasticsearch と Kibana を試すことにした。

Elasticsearch と Kibana をダウンロード

公式サイトから zip ファイルをダウンロードし、適当な場所に展開。

https://www.elastic.co/downloads/elasticsearch

https://www.elastic.co/downloads/kibana

head プラグインをインストール

Web ブラウザで Elasticsearch に保存されているデータを見るために、 head プラグインをインストールしておく。

plugin install mobz/elasticsearch-head

Elasticsearch と Kibana を起動

Elasticsearch インストールフォルダ内にある bin\elasticsearch と Kibana インストールフォルダ内にある bin\kibana を実行。

Elasticsearch にインデックスを作成

簡単に言えば、RDS のデータベースに対応するものが index、テーブルに対応するものが type。 その index と type のスキーマを定義した mapping.json を作成する。

{
  "mappings": {
    "CPUUtilization": {
      "properties": {
        "Timestamp": {
          "type": "date",
          "format": "dateOptionalTime"
        },
        "Average": {
          "type": "double"
        }
      }
    },
    "DatabaseConnections": {
      "properties": {
        "Timestamp": {
          "type": "date",
          "format": "dateOptionalTime"
        },
        "Average": {
          "type": "double"
        }
      }
    },
    "DiskQueueDepth": {
      "properties": {
        "Timestamp": {
          "type": "date",
          "format": "dateOptionalTime"
        },
        "Average": {
          "type": "double"
        }
      }
    },
    "FreeableMemory": {
      "properties": {
        "Timestamp": {
          "type": "date",
          "format": "dateOptionalTime"
        },
        "Average": {
          "type": "double"
        }
      }
    },
    "FreeStorageSpace": {
      "properties": {
        "Timestamp": {
          "type": "date",
          "format": "dateOptionalTime"
        },
        "Average": {
          "type": "double"
        }
      }
    },
    "NetworkReceiveThroughput": {
      "properties": {
        "Timestamp": {
          "type": "date",
          "format": "dateOptionalTime"
        },
        "Average": {
          "type": "double"
        }
      }
    },
    "NetworkTransmitThroughput": {
      "properties": {
        "Timestamp": {
          "type": "date",
          "format": "dateOptionalTime"
        },
        "Average": {
          "type": "double"
        }
      }
    },
    "ReadIOPS": {
      "properties": {
        "Timestamp": {
          "type": "date",
          "format": "dateOptionalTime"
        },
        "Average": {
          "type": "double"
        }
      }
    },
    "ReadLatency": {
      "properties": {
        "Timestamp": {
          "type": "date",
          "format": "dateOptionalTime"
        },
        "Average": {
          "type": "double"
        }
      }
    },
    "WriteIOPS": {
      "properties": {
        "Timestamp": {
          "type": "date",
          "format": "dateOptionalTime"
        },
        "Average": {
          "type": "double"
        }
      }
    },
    "WriteLatency": {
      "properties": {
        "Timestamp": {
          "type": "date",
          "format": "dateOptionalTime"
        },
        "Average": {
          "type": "double"
        }
      }
    }
  }
}

curl で Elasticsearch の REST API を呼び出して、インデックスを作成する。

curl -XPOST localhost:9200/rds -d @mapping.json

Elasticsearch にメトリクスを保存

curlREST API を呼び出してデータを Elasticsearch に PUT できるけど、 メトリクスは JSON になっているので、加工が超メンドイ。

なので Python スクリプトを書いた。

# -*- coding: utf-8 -*-
import os
import sys
import json
import urllib
import httplib

ELASTICSEARCH_URL = "localhost:9200"
METRICS_ROOT_DIR = "/var/log/perform/my-app-name"
INSTANCES = [
    "rds",
]
METRICS = [
    "CPUUtilization",
    "DatabaseConnections",
    "DiskQueueDepth",
    "FreeableMemory",
    "FreeStorageSpace",
    "ReadIOPS",
    "WriteIOPS",
    "ReadLatency",
    "WriteLatency",
    "NetworkReceiveThroughput",
    "NetworkTransmitThroughput",
]

def post_datapoint(index_name, type_name, datapoint):
    conn = httplib.HTTPConnection(ELASTICSEARCH_URL)
    path = "/" + index_name + "/" + type_name + "/" + datapoint["Timestamp"]
    params = json.dumps(datapoint)
    headers = {
        "Content-type": "application/json",
    }
    conn.request("PUT", path, params, headers)
    response = conn.getresponse()
    print(response.read())
    conn.close()


def post_instance_metrics(dir_path, instance):
    for metric in METRICS:
        file_path = os.path.join(dir_path, metric + ".json")
        with open(file_path) as f:
            data = json.load(f)
            for datapoint in data["Datapoints"]:
                post_datapoint(instance, metric, datapoint)


for instance in INSTANCES:
    metrics_dir = os.path.join(METRICS_ROOT_DIR, instance)
    post_instance_metrics(metrics_dir, instance)

こいつを実行して、メトリクスが Elasticsearch の index に格納。

Kibana でメトリクスを表示

_index:rds AND _type:CPUUtilization

みたいな検索クエリで LineChart の Visualization を作成する。

_type のところを他のメトリクス名に変えて作成しまくったら、 ダッシュボードに配置。

出来上がった Kibana のダッシュボードはこんな感じ。

f:id:griefworker:20160601100803p:plain

なかなか良いかも。

おわりに

CloudWatch から RDS インスタンスのメトリクスをダウンロードして、 それを Elasticsearch + Kibana で可視化できた。

ただ、RDS のインスタンスは今後増えていきそう。 RDS インスタンスごとに Elasticsearch のインデックスを分けたいので、 インデックスも増えていきそう。

インデックスが増えるたびに Kibana のダッシュボードを手動で作るのは、 これまたメンドイので、そこを自動化するのが今後の課題だな。