Python の Elasticsearch クライアントを使えばいいことに気付いた

CloudWatch のメトリクスを Elasticsearch に突っ込むスクリプトPython の標準ライブラリだけ使って書いたけど、 Elasticsearch クライアントを使えばよかったことに今さら気付いた。

pip install elasticsearch

でインストールしたら、スクリプトはこんな風に書き直すことができた。

# -*- coding: utf-8 -*-
import os
import sys
import json
from elasticsearch import Elasticsearch

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",
]


es = Elasticsearch(ELASTICSEARCH_URL)


def post_datapoint(index_name, type_name, datapoint):
    # Elasticsearch クライアントを使ってインデックスにデータを登録
    es.index(index=index_name, doc_type=type_name, id=datapoint["Timestamp"], body=datapoint)


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)

スッキリ。 awscli は pip でインストールしたのに、Elasticsearch は標準ライブラリだけ使うというのも、 今考えればおかしな話だ。

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 のダッシュボードを手動で作るのは、 これまたメンドイので、そこを自動化するのが今後の課題だな。

BAKE

天神地下街にあるチーズタルト専門店『BAKE』は、 2015 年 9 月にオープンしてから 10 ヶ月くらい経つのに、 まだ行列が出来ている。 半年も経てば落ち着くと思っていたんだけどな。 行列が出来なくなったら行こうと思っていたんだけど、 さすがにもう待てなくなって、15 分くらい並んで買ってみた。

売っているのはチーズタルト1つのみという潔さは好き。 その肝心のチーズタルトは、生地がカリッと焼いてあってザクザクと香ばしい。 チーズはふわっとしていて口溶け良く、濃厚さと甘さ、柑橘系のほのかな酸味が丁度良いバランス。 非常に軽い口当たりで、1人1個じゃ食べたり無かった。 2~3個はペロリと食べれそう。

ずっと行列が耐えなかったけど、その理由も分かった。 この味ならリピートするね。 自分もチーズタルトが食べたくなったら、新しい店でもオープンしない限りは、 次もここに買いに行くと思う。

関連ランキング:洋菓子(その他) | 天神駅西鉄福岡駅(天神)天神南駅

一周忌の準備を自分がやることになった話

はじめに

親から、祖父の一周忌の準備を手伝って欲しいと依頼された。 お寺の予約だけは入れたけど、それ以外の準備がまったく進んでいないらしい。 で、何日を予約したのか聞いたら、なんと1週間後。 マジですか。 1ヶ月前に予約入れたらしいけど、それから今まで何してたんだ…。

親や弟に任せたらこのままズルズル何も進まないのは目に見えているので、 自分が残り(というかほぼ全て)の準備をやることになった。

要望の聞き取り

最終的にお金を出すのは親なので、 後で文句をつけられないように、 まずは要望を聞き取っておいた。

で挙がってきた要望は

  • お寺でお経をあげた後は、出席者みんなで食事をしたい
    • できればお店がいい
    • お寺の部屋を借りるのは高いから嫌
  • 出欠確認は往復はがきがいい
    • 祖父側の親族とはあまり電話したくない
  • お返しの品はオシャレなのが良い
    • お茶は年寄りくさいから嫌

といったところ。なんとも注文が多い。 それにお茶は年寄りくさいって、出欠確認する方々は、親世代かそれより上なので、もうお年(自主規制)。

かといって過去の経験上、気に入らない部分があったらずっとグチグチ言われ続けるので、 要望通りにやっておかないと後々面倒。 しぶしぶ了承。

名簿の作成

親が電話での出欠確認をどうしてもやりたくないと言うので、 仕方ないから出席して欲しい人の名簿を、 弟経由でメールで受け取る。 名簿は CSVExcel ではなく本文に書いてあったので、 せっせと Excel にまとめた。

名簿の列順は氏名・郵便番号・住所にしておく。 これが後々役に立った。

お寺の予約の変更

まず 1週間では到底無理なので、お寺にお願いして日にちを変えてもらった。 幸い、ゴールデンウィーク明けが空いていたので、その日に変更。 命日よりも前なので問題なし。 1週間の猶予が2週間になっただけだがな!

お斎のお店を予約

一周忌後の食事で使うような、法要プランのある旅館を地元で探し、電話で予約した。 名簿から予想される出席者は 13 ~ 17 人の範囲という、アバウトな人数だったけど、 なんとか予約を入れることができた。

大半がご年配の方々なので、食事を豪華にしても、食べきれずに残すことは想像に難くない。 一番安いコースを注文しておいた。 お返しの品で頑張る方針。

大きく人数が変わらないなら前日に最終的な人数を伝えればいい、ということだったので、 出欠確認が終わったらまた電話することに。

ここだけの話、地元は田舎でお店のあてが全く無かった。 Google 先生に聞いても教えてくれなかった。 そこで、祖父の葬儀で利用した葬儀会社のホームページに載っていた提携している旅館に、 直接予約を入れてみた次第。

お返しの品の調査

ご仏前のお返しの品をどこで買えばいいか皆目見当が付かなかったので、ネットで調査。 どうやらデパートで買えば良さそう。 職場は天神なので、岩田屋三越・大丸が使えて助かった。

会社の昼休みに岩田屋に行き、総合案内でお返しの品を扱ってそうな店を聞いたら、 食品ギフト売り場を紹介してくれた。

海苔やお茶やコーヒーといった、ド定番のものしかなかったが、 他に良いものが見つからなかったときの最終手段として候補に入れておく。 この時点では人数が確定していないから、まだ注文しない。

念のため、いつまでに注文すれば間に合うかを店員に聞いておいた。 5/1 に注文すれば間に合うとのことだったので、人数確認の期限は 4/30 に決定。

往復はがきの作成と郵送

人数分の往復はがきを郵便局で購入し、家のプリンターで印刷することにした。 往復はがきはインクジェット版が無かったけど、写真を印刷するわけじゃないので問題なかった。

往復はがきの作成には Word を使った。 Word で往復はがきまで作成できるとはね。 往復はがきの文面はネットで良さそうなテンプレートを探して使った。 宛名も Excel で作成した名簿を読み込んで印刷できた。 Word スゴイ。 はがき作成専用のアプリはもう不要だな。

往復はがきの返信先は実家で、返信の期限は 4/29。 はがきが届いてから1週間くらいしか猶予が無いのは申し訳なかった。 本当なら1ヶ月前には送って、2週間くらいは猶予を設けるべきなのに。

往復はがきの作成は夜中までかかったが、なんとか失敗も無しに全て印刷できたので、 出勤時にポストに投函。

ここまでで一段落

あとはお返しの品の手配と、旅館に最終的な人数を伝えるのみ。 この2つはある程度はがきが返ってこないと数が分からないので。

ちなみに準備の依頼を受けてからここまで2日。

ここから、はがきの返信期限である1週間後までは、 実家にはがきが届いたかをこまめに確認しつつ、 デパートを回ってお返しの品を探した。

お返しの品の手配

返信はがきが結構早い段階で集まってきて、一周忌に出席する人数がだいぶ固まったので、 お返しの品の手配を行った。

今回お返しの品に選んだのは、OSUYA 銀座のデザートビネガーセット。 ゴールデンウィーク期間中&母の日が近いということで、 7500 円のセットが期間限定で安くなっていたので即決定。

店員に話を聞いたら、一周忌のお返しの品にも問題ないし、 のしと紙袋も付けてくれるとのことだった。

食品ギフトのコーナーではなくて、デパ地下に出店している洋菓子や和菓子のショップでも、 お願いすれば一周忌のお返しの品として手配してくれるかもしれない。

旅館に最終的な人数を連絡

はがきの返信がほとんど届いて、人数が確定したので、旅館に最終的な人数を連絡。 最初予約するときに伝えた 13 ~ 17 人の間に収まったので特に問題も無く、 当日お願いします、で終わった。

当日

食事はニコニコ現金払いなので、当日必要な金額をメールで連絡。 メールなら証拠残るからね。 お返しの品は実家にまとめて届いたようなので、 車に積んで持ってくるように念を押しておいた。

お返しの品を渡す人数、旅館で食事する人数、どちらもピッタリで一安心。 食事中の飲み物のことはすっかり頭から抜け落ちていたので、その分が誤算だったけど、 御仏前でもらった金額と比べて丁度良い具合に収まったはず。 御仏前が総額いくらになったのかは聞いてないけど。

終わりに

幹事みたいなことは苦手ではないので、準備の残りを引き受けたけど、 終わるまで結構ストレスだった。 決して安くは無いお金が絡んでいるからねぇ。

そもそも祖父の葬儀をお願いした葬儀会社に、一周忌も依頼するのがベストだった。 親が葬儀会社ともめて頼めない状況に陥ったらしいが。 まったく、そんな役に立たない面子は捨ててしまえ。

このぶんだと三回忌も自分が準備することになりそうだ。

CloudWatch からダウンロードしておいたメトリクスを S3 にアップロード

過去 2 週間分しか取得できない CloudWatch のメトリクスを、 毎日ダウンロードするようにスクリプトを書いた。

tnakamura.hatenablog.com

このメトリクスはいずれツールを使って分析に使いたいので、 ローカルにだけ保存しておくのは心もとない。 念のためバックアップしておきたい。

というわけで、S3 にアップロードするスクリプトも書いたのでメモしておく。

# ログの出力先
# メトリクスもこの下に保存されている
logdir=/var/log/your_app_name/perform
today=`date -u +%Y/%m/%d`
logfile=${logdir}/cloudwatch.log

# アップロード先のバケット
bucket=your_bucket_name

# アップロードするメトリクス
metrics="CPUUtilization DatabaseConnections FreeStorageSpace FreeableMemory ReadIOPS WriteIOPS ReadLatency WriteLatency DiskQueueDepth NetworkTransmitThroughput NetworkReceiveThroughput"

# ログ出力
function write_log() {
  echo -e "`date -u +%Y-%m-%dT%TZ` ${1}\r\n" >> ${logfile}
}

# メトリクスをアップロード
function upload_metrics() {
  instance_id=${1}

  for metric in $metrics; do
    metric_file=${logdir}/${instance_id}/${today}/${metric}.json
    upload_url=s3://${bucket}/performlogs/${instance_id}/${metric}/${today}/${metric}.json

    if [ -f ${metric_file} ]; then
      write_log "${metric_file}  S3 にアップロードします。"

      # S3 にアップロード
      aws s3 cp ${metric_file} ${upload_url}

      write_log "${upload_url} にアップロードしました。"
    fi
  done
}

# RDS インスタンス取得
ids=`aws rds describe-db-instances | jq -r '.DBInstances[].DBInstanceIdentifier' | perl -pe 's/\r\n/ /g'`
for id in ${ids}; do
  upload_metrics ${id}
done

CloudWatch のメトリクスをダウンロードするスクリプト

AWS CLI で CloudWatch のメトリクスを取得できるけど、 過去 2 週間までしかさかのぼれないみたいだ。 2 週間よりも前のメトリクスを見たくなったときに備えて、 あらかじめダウンロードしておく必要があるな。

そういうわけで、1 日分のメトリクスをダウンロードする bash スクリプトを書いてみた。 このスクリプトを cron で毎日実行してローカルに保存しておく。

# ログの出力先
# メトリクスもこの下にダウンロードする
logdir=/var/log/your_app_name/perform/
today=`date -u +%Y/%m/%d`
logfile=${logdir}cloudwatch.log

# aws コマンドに渡すパラメータ
namespace=AWS/RDS
statistics=Average
starttime=`date -u -d '1 days ago' +%Y-%m-%dT%TZ`
endtime=`date -u +%Y-%m-%dT%TZ`
metrics="CPUUtilization DatabaseConnections FreeStorageSpace FreeableMemory ReadIOPS WriteIOPS ReadLatency WriteLatency DiskQueueDepth NetworkTransmitThroughput NetworkReceiveThroughput"

# ログ出力
function write_log() {
  echo -e "`date -u +%Y-%m-%dT%TZ` ${1}\r\n" >> ${logfile}
}

# CloudWatch のメトリクスをダウンロード
function download_metrics() {
  instance_id=${1}

  # ログフォルダが無ければ作る
  instance_logdir=${logdir}${instance_id}/${today}
  [ ! -d ${instance_logdir} ] && mkdir -p ${instance_logdir}

  for metric in $metrics; do
    write_log "${instance_id}/${metric} を取得"
    aws cloudwatch get-metric-statistics \
      --namespace ${namespace} \
      --dimensions Name=DBInstanceIdentifier,Value=${instance_id} \
      --metric-name ${metric} \
      --statistics ${statistics} \
      --start-time ${starttime} \
      --end-time ${endtime} \
      --period 300 > ${instance_logdir}/${metric}.json
    done
}

# RDS インスタンス取得
ids=`aws rds describe-db-instances | jq -r '.DBInstances[].DBInstanceIdentifier' | perl -pe 's/\r\n/ /g'`
for id in ${ids}; do
  download_metrics ${id}
done

橋本わっぱ定食堂

行きつけだった天神の友楽は天神地区再開発のため閉店してしまったので、 カツ丼を食べに行く店を新たに発掘しなければいけなくなった。

そういえば、わっぱ定食堂は『ドン田中』と銘打って、丼ものを扱っていたな。 まだ食べたことが無かったので『橋本わっぱ定食堂』に行ってみた。

注文したのは当然、カツ丼。 味噌汁だけでなく冷奴も付いているのは嬉しい。

トンカツがからっと揚がっていて、まだサクサクの状態で美味かった。 衣がふやけていないのは好評価。

友楽のコストパフォーマンスには及ばないけど、 わっぱ定食堂はカツ丼以外にもメニューが豊富で味も良いし、 普段使いにもってこいの店だな。

関連ランキング:定食・食堂 | 橋本駅次郎丸駅