CloudWatch からダウンロードした RDS インスタンスのメトリクスを、 Elasticsearch に突っ込んで、 Kibana のダッシュボードで可視化するところまではできた。
RDS インスタンスは今後増えていく予定で、 Kibana のダッシュボードは RDS インスタンスごとに分けたい。 RDS インスタンスが増えるたびに、Kibana をぽちぽち操作してダッシュボードを作るのは手間だな。
なんとか自動化できないものか調べていたら、
Kibana の設定は Elasticsearch の .kibana インデックスに保存されているみたいだった。
ダッシュボードは type = dashboard
に、
Visualization は type = visualization
に保存されていた。
ということは、ダッシュボードの JSON を .kibana インデックスにスクリプトで登録すれば、 動的にダッシュボードを追加できそうだ。
一から JSON を書くのは面倒なので、すでに作成したダッシュボードや Visualization もろもろの JSON を Kibana からエクスポートし、加工してテンプレートにした。 かなり長いので、一部抜粋。
[ { "_id": "${instance}", "_type": "dashboard", "_source": { "title": "${instance}", "hits": 0, "description": "", "panelsJSON": "[{\"col\":1,\"id\":\"${instance}-CPUUtilization\",\"panelIndex\":1,\"row\":1,\"size_x\":3,\"size_y\":2,\"type\":\"visualization\"},{\"col\":4,\"id\":\"${instance}-DatabaseConnections\",\"panelIndex\":2,\"row\":1,\"size_x\":3,\"size_y\":2,\"type\":\"visualization\"},{\"col\":7,\"id\":\"${instance}-FreeableMemory\",\"panelIndex\":3,\"row\":1,\"size_x\":3,\"size_y\":2,\"type\":\"visualization\"},{\"col\":10,\"id\":\"${instance}-DiskQueueDepth\",\"panelIndex\":4,\"row\":1,\"size_x\":3,\"size_y\":2,\"type\":\"visualization\"},{\"col\":1,\"id\":\"${instance}-FreeStorageSpace\",\"panelIndex\":5,\"row\":3,\"size_x\":3,\"size_y\":2,\"type\":\"visualization\"},{\"col\":4,\"id\":\"${instance}-ReadIOPS\",\"panelIndex\":6,\"row\":3,\"size_x\":3,\"size_y\":2,\"type\":\"visualization\"},{\"col\":7,\"id\":\"${instance}-WriteIOPS\",\"panelIndex\":7,\"row\":3,\"size_x\":3,\"size_y\":2,\"type\":\"visualization\"},{\"id\":\"${instance}-NetworkReceiveThroughput\",\"type\":\"visualization\",\"panelIndex\":8,\"size_x\":3,\"size_y\":2,\"col\":10,\"row\":3},{\"id\":\"${instance}-NetworkTransmitThroughput\",\"type\":\"visualization\",\"panelIndex\":9,\"size_x\":3,\"size_y\":2,\"col\":1,\"row\":5},{\"id\":\"${instance}-ReadLatency\",\"type\":\"visualization\",\"panelIndex\":10,\"size_x\":3,\"size_y\":2,\"col\":4,\"row\":5},{\"id\":\"${instance}-WriteLatency\",\"type\":\"visualization\",\"panelIndex\":11,\"size_x\":3,\"size_y\":2,\"col\":7,\"row\":5}]", "optionsJSON": "{\"darkTheme\":true}", "uiStateJSON": "{\"P-1\":{\"vis\":{\"legendOpen\":false}},\"P-2\":{\"vis\":{\"legendOpen\":false}},\"P-3\":{\"vis\":{\"legendOpen\":false}},\"P-4\":{\"vis\":{\"legendOpen\":false}},\"P-5\":{\"vis\":{\"legendOpen\":false}},\"P-7\":{\"vis\":{\"legendOpen\":false},\"spy\":{\"mode\":{\"name\":null,\"fill\":false}}},\"P-8\":{\"vis\":{\"legendOpen\":false}},\"P-11\":{\"vis\":{\"legendOpen\":false}},\"P-10\":{\"vis\":{\"legendOpen\":false}},\"P-9\":{\"vis\":{\"legendOpen\":false}},\"P-6\":{\"vis\":{\"legendOpen\":false}}}", "version": 1, "timeRestore": false, "kibanaSavedObjectMeta": { "searchSourceJSON": "{\"filter\":[{\"query\":{\"query_string\":{\"analyze_wildcard\":true,\"query\":\"*\"}}}]}" } } }, // search と visualization は省略 ]
このテンプレートからダッシュボードの JSON を出力する Python スクリプトを書いた。
# -*- coding: utf-8 -*- import os import sys import codecs from string import Template TEMPLATE_FILE_NAME = "dashboard_template.json" JSON_FILE_SUFFIX = "_dashboard.json" class DashboardGenerator(object): def _get_work_dir(self): dir_path = os.path.abspath(os.path.dirname(__file__)) return dir_path def _get_template_path(self): dir_path = self._get_work_dir() template_path = os.path.join(dir_path, TEMPLATE_FILE_NAME) return template_path def _get_template(self): template_path = self._get_template_path() with open(template_path, "r") as f: template_text = f.read() template = Template(template_text) return template def _generate_dashboard_json(self, rds_instance_name): template = self._get_template() json = template.substitute(instance=rds_instance_name) return json def _save_dashboard_json(self, rds_instance_name, json): file_name = rds_instance_name + JSON_FILE_SUFFIX dir_path = self._get_work_dir() file_path = os.path.join(dir_path, file_name) with codecs.open(file_path, "w", "utf-8") as f: f.write(json) def generate(self, rds_instance_name): json_data = self._generate_dashboard_json(rds_instance_name) self._save_dashboard_json(rds_instance_name, json_data) if __name__ == "__main__": generator = DashboardGenerator() generator.generate("rds-0001")
出力した JSON を elasticsearch クライアントを使って .kibana インデックスに登録すれば、 ダッシュボードを追加できる。これも Python スクリプトを書いた。
# -*- coding: utf-8 -*- import os import sys import json from elasticsearch import Elasticsearch class KibanaSetup(object): def __init__(self): self.es = Elasticsearch("localhost:9200") def _get_work_dir(self): dir_path = os.path.abspath(os.path.dirname(__file__)) return dir_path def _get_dashboard_json_path(self, rds_instance_name): work_dir = self._get_work_dir() file_path = os.path.join(work_dir, rds_instance_name + "_dashboard.json") return file_path def _load_dashboard_json(self, rds_instance_name): file_path = self._get_dashboard_json_path(rds_instance_name) with open(file_path) as f: json_data = json.load(f) return json_data def _post_object(self, obj): self.es.index(index=".kibana", doc_type=obj["_type"], id=obj["_id"], body=obj["_source"]) def _create_dashboard(self, rds_instance_name): json_data = self._load_dashboard_json(rds_instance_name) for obj in json_data: self._post_object(obj) def setup(self, rds_instance_name): self._create_dashboard(rds_instance_name) if __name__ == "__main__": app = KibanaSetup() app.setup("rds-0001")
これでやりたいことはだいたい出来た。 ダッシュボードに Visualization を追加するときはテンプレートをいじる必要があって、 そこはまだ手間だけど当面は増やす予定ないからいいことにしよう。