MackerelでEC2スポットインスタンスの価格変動を監視する
まえがき
Mackerel Advent Calendar 2015 12月13日の記事です。
昨日は la_luna_azul さんでした。Ore no homepageはよく参考にさせてもらってます!
go-check-pluginsを勝手に解説 – Mackerelアドベントカレンダー12日目 | Ore no homepage
今日のカレンダーがたまたま空いていたので、ここまで続いたのが途切れるのもなんかもったいないなと思って参加してみました。
この記事では、AWSのAPIからEC2スポットインスタンスの価格を取得し、Mackerelにサービスメトリックとして送信する方法を紹介します。
使用するライブラリーについて
Python3を使いますので、AWS APIとの連携にはboto3、Mackerelとの連携にはmackerel.clientを使用します
環境構築について、詳しい説明は割愛しますが、boto3, mackerel.clientともにpipでインストールできます。
RubyでrbenvやRVMを使用するように、PythonではVirtualenvを使用するのが良いでしょう。
Virtualenvを使う前提にはなりますが、以下のコマンドでこの記事で使用するコードを動かす環境を作れると思います。
$ virtualenv -p python3 .venv $ source .venv/bin/activate $ pip install boto3 mackerel.client
スポットインスタンスの価格を取得する
スポットインスタンスの価格はEC2 APIのDescribeSpotPriceHistoryを使うと取得できます。
http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSpotPriceHistory.html
boto3を使用してAWS東京リージョンのスポットインスタンス価格を取得するには、以下のようなコードになります
import boto3
access_key_id = 'AWSのアクセスキー'
secret_access_key = 'AWSのシークレットキー'
region_name = 'ap-northeast-1' # AWSのリージョン名
boto3_session = boto3.session.Session(aws_access_key_id=access_key_id,
aws_secret_access_key=secret_access_key,
region_name=region_name)
ec2_client = boto3_session.client('ec2')
spot_price_history = ec2_client.describe_spot_price_history()
for history in spot_price_history.get('SpotPriceHistory'):
print(history)
このコードを実行すると以下のような結果が表示されます。
$ python boto3_example.py
{'SpotPrice': '0.087700', 'Timestamp': datetime.datetime(2015, 12, 13, 8, 21, 25, tzinfo=tzutc()), 'AvailabilityZone': 'ap-northeast-1b', 'InstanceType': 'm2.4xlarge', 'ProductDescription': 'Linux/UNIX'}
{'SpotPrice': '0.126400', 'Timestamp': datetime.datetime(2015, 12, 13, 8, 21, 13, tzinfo=tzutc()), 'AvailabilityZone': 'ap-northeast-1c', 'InstanceType': 'c3.2xlarge', 'ProductDescription': 'Linux/UNIX'}
{'SpotPrice': '0.419200', 'Timestamp': datetime.datetime(2015, 12, 13, 8, 21, 11, tzinfo=tzutc()), 'AvailabilityZone': 'ap-northeast-1c', 'InstanceType': 'c3.2xlarge', 'ProductDescription': 'Windows'}
{'SpotPrice': '0.104200', 'Timestamp': datetime.datetime(2015, 12, 13, 8, 21, 8, tzinfo=tzutc()), 'AvailabilityZone': 'ap-northeast-1c', 'InstanceType': 'd2.xlarge', 'ProductDescription': 'Linux/UNIX'}
{'SpotPrice': '0.091900', 'Timestamp': datetime.datetime(2015, 12, 13, 8, 21, 8, tzinfo=tzutc()), 'AvailabilityZone': 'ap-northeast-1b', 'InstanceType': 'd2.xlarge', 'ProductDescription': 'Linux/UNIX'}
...
このままではAvailability Zoneやインスタンスタイプなどでの絞り込みができていませんが、そのような条件でフィルタリングすることもできるので後述します。
Mackerelと連携する
オーガニゼーションにサービスを追加する
スポットインスタンスの価格をサービスメトリックとして記録するために、オーガニゼーションにサービスを追加します。
サービスの登録方法については11日目の記事、Mackerelを使ってTwitterを監視してみようでも手順が紹介されていますので、この記事では割愛します。
mackerel.clientを使う
mackerel.clientについても、11日目の記事で紹介されているのですが、せっかくなのでmackerel.clientのサンプルを紹介します。
以下のコードは、mackerel.clientを使用して、公式ヘルプのホストのカスタムメトリックを投稿すると同じように6面ダイス、20面ダイスの結果をサービスメトリックとして送信します。
import random
import time
import mackerel.client
api_key = 'MackerelのAPIキー'
service_name = 'Mackerelのオーガニゼーションに追加したサービス名'
timestamp = int(time.time())
metrics = [
{'name': 'test.mackerel_client.dice6', 'value': random.randint(1, 6), 'time': timestamp},
{'name': 'test.mackerel_client.dice20', 'value': random.randint(1, 20), 'time': timestamp},
]
mackerel_client = mackerel.client.Client(mackerel_api_key=api_key)
mackerel_client.post_service_metrics(service_name, metrics)
上記のコードを定期的に実行すると、以下のようにMackerel上でサービスメトリックのグラフを見ることができます。

boto3とmackerel.clientを組み合わせる
さて、boto3とmackerel.clientの使い方がわかったので、組み合わせてみましょう。
import time
from datetime import datetime, timedelta
import boto3
import mackerel.client
def metric_exists(instance_type, availability_zone, metrics):
for metric in metrics:
if metric.get('name') == 'ec2_spot_price_history.{0}.{1}'.format(availability_zone, instance_type):
return True
return False
def build_metric_name(availability_zone, instance_type):
return 'ec2_spot_price_history.{0}.{1}'.format(availability_zone, instance_type)
def build_metric(instance_type, availability_zone, spot_price, timestamp):
return {
'name': build_metric_name(availability_zone, instance_type),
'time': timestamp,
'value': spot_price,
}
aws_access_key_id = 'AWSのアクセスキー'
aws_secret_access_key = 'AWSのシークレットキー'
region_name = 'ap-northeast-1' # AWSのリージョン名
aws_availability_zone = 'ap-northeast-1c' # AWSのAZ名
mackerel_api_key = 'MackerelのAPIキー'
mackerel_service_name = 'Mackerelのオーガニゼーションに追加したサービス名'
boto3_session = boto3.session.Session(aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
region_name=aws_region_name)
boto3_client = boto3_session.client('ec2')
ec2_instance_types = [
'c4.large', 'c4.xlarge', 'c4.2xlarge', 'c4.4xlarge',
]
# スポットインスタンスの価格履歴をAZ, インスタンスタイプ, OSタイプで絞り込む
filters = [
{'Name': 'availability-zone', 'Values': [aws_availability_zone]},
{'Name': 'instance-type', 'Values': ec2_instance_types},
{'Name': 'product-description', 'Values': ['Linux/UNIX']},
]
timestamp = int(time.time())
one_min_ago = datetime.now() - timedelta(minutes=1)
# スポットインスタンスの履歴を取得
# 直近の値を取得するため、開始日時を1分前の日時とする
spot_price_history = boto3_client \
.describe_spot_price_history(Filters=filters, StartTime=one_min_ago) \
.get('SpotPriceHistory')
mackerel_client = mackerel.client.Client(mackerel_api_key=mackerel_api_key)
metrics = []
for history in spot_price_history:
# 価格履歴からインスタンスタイプなどの情報を取得
# ハイフンはMackerelのメトリクス名として使用できないので、アンダースコアに置換する
instance_type = history.get('InstanceType')
aws_availability_zone = history.get('AvailabilityZone').replace('-', '_')
spot_price = float(history.get('SpotPrice'))
# すでにメトリックがリストに存在する場合は読み飛ばす
if metric_exists(instance_type, aws_availability_zone, metrics):
continue
# メトリックを構築してリストに追加
metric = build_metric(instance_type, aws_availability_zone, spot_price, timestamp)
metrics.append(metric)
# 指定したサービス名のメトリックとして価格履歴を送信
mackerel_client.post_service_metrics(mackerel_service_name, metrics)
上記のコードを定期的に実行すると、Mackerel上で以下の様なグラフを見ることができます。

サービスメトリックとしてMackerelに送信してしまえば、価格の変更を監視することも簡単にできます。
Slackなどのサービスと連携することが簡単なので、使い慣れたツールで通知を受け取ることもできて便利そうですね。
まとめ
気軽にメトリクスを送りつけるだけで可視化も監視もできるので、Mackerel便利ですね!
明日はpyama86さんです。