CloudWatch API + ZabbixでAWS課金情報をグラフ化

先日、AWSのCloudWatchで 課金情報を監視・通知できるようになったとのアナウンス がありました。

AWSを利用されている方からすると、
今月いくら利用しているのか気になるところだと思うので、
この機能は非常に有り難いのではないでしょうか。

CloudWatchの詳細については こちら にまとめられています。
今回利用する機能は、CloudWatchの「AWS 請求書の予想請求額(Billing)」メトリクスです。

この課金情報の監視については、無料で利用できるようです。

CloudWatchはAWSの状況を監視するには非常に便利ですが、
監視統計データが2週間しか保存できないという制限があります。

そのため、2週間以上前にどういう状況だったのかを確認するには別途監視データを管理する必要があります。

そこで、OSSの統合監視ツールである「Zabbix」を活用し、
CloudWatchで監視している課金情報のデータを管理してみたいと思います。

CloudWatchで課金情報監視を開始

まずは、CloudWatchで課金情報の監視を開始するよう有効化する設定が必要になります。

Amazon Web Services のサイトから「アカウントアクティビティ」のページにアクセスします。

account_activity0.PNG

すると以下の図のようにCloudWatchを使った課金情報の監視を有効化するかどうかの確認メッセージが表示されます。
「Enable now」をクリックして有効化処理を開始します。

account_activity1.PNG

次に、「Security Challenge Questions」の設定がされていない場合には以下の図のように確認メッセージが表示され、
ユーザ情報設定画面に促されます。

account_activity2.PNG

3つの質問と回答をセットしてSaveすれば完了です。

あとは、15分ほど待てば課金情報のデータがCloudWatchから取得できるようになります。

CloudWatch APIを利用してZabbixに課金情報を登録

それではここからCloudWatchAPIを利用して課金データを取得してみたいと思います。

方法については Serverworksさんの技術ブログ が非常に参考になりました。
こちらで作成されているRubyスクリプトを活用し、Zabbixにデータ登録できるようにしてみました。

Rubyスクリプト動作環境の設定

まず、上記のRubyスクリプトを利用するための環境を構築します。
以降、CentOS6.2での環境構築手順を示します。

必要なものは以下になります。

  • ruby,ruby-devel
  • rubygems
  • zbxapi(gemパッケージ)※Zabbix API操作用ライブラリ
  • right_aws(gemパッケージ)※AWS API操作用ライブラリ

Ruby、Rubygemsのインストール

1
2
3
4
5
6
7
$ sudo yum install ruby ruby-devel
$ wget http://production.cf.rubygems.org/rubygems/rubygems-1.8.24.tgz
$ tar xvzf rubygems-1.8.24.tgz
$ cd rubygems-1.8.24
$ ruby setup.rb config
$ ruby setup.rb setup
$ sudo ruby setup.rb install

gemパッケージのインストール

1
2
3
$ sudo gem install zbxapi
$ sudo gem install right_aws
その他必要なパッケージも同時にインストールされます

以上で環境の構築は終了です。

課金情報取得&Zabbixへの登録用スクリプトの配置

次に、以下のRubyスクリプトをZabbixの外部スクリプトとして配置します。
externalscriptsのディレクトリ配下に以下のcloudwatch_billing.rbファイルを配置して下さい。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/bin/env ruby

require 'rubygems'
require 'right_aws'
require 'zbxapi'
require 'logger'

STARTTIME = (Time.now.utc - 1*24*60*60)
ENDTIME = Time.now.utc
PERIOD = 300
ZABBIX_SERVER = 'example.com'
ZABBIX_API_URL = "http://#{ZABBIX_SERVER}/zabbix/api_jsonrpc.php"
ZABBIX_LOGINID = 'admin'
ZABBIX_PASSWORD = 'zabbix'
ZABBIX_SENDER_PATH = '/usr/bin/zabbix_sender'
LOGDIR = '/tmp/script_logs'
LOGFILE = "#{LOGDIR}/zabbix_external_script.log"
HOST = ARGV[0]
AWS_ACCESS_KEY = ARGV[1]
AWS_SECRET_KEY = ARGV[2]

SUCCESS = 0
FAILURE = 1

TOTAL_ITEM_KEY = 'aws.billing.total'    # Zabbix Item key(for Total Billing Data)
ONEDAY_ITEM_KEY = 'aws.billing.oneday'  # Zabbix Item key(for 1 day Billing Data)

def get_total_billing(datapoints)
        data_array = []
        datapoints.each do |datapoint|
                data_array.push({:unixtime => translate_local_unixtime(datapoint[:timestamp]),:value => datapoint[:average]})
        end
        return data_array
end

def get_oneday_billing(datapoints)
        first_data = datapoints.last[:average]
        data_array = []
        datapoints.each do |datapoint|
                data_array.push({:unixtime => translate_local_unixtime(datapoint[:timestamp]),:value => datapoint[:average] - first_data})
        end
        return data_array
end

def get_local_time(time)
        Time.parse(time).getlocal
end

def translate_local_unixtime(time)
        get_local_time(time).to_i              
end

def get_echo_command(host,key,data)
        echo_cmd = "echo -n -e '"
        data.each do |d|
                echo_cmd += "#{host} #{key} #{d[:unixtime]} #{d[:value]}n"
        end
        echo_cmd += "'"
end

if ARGV.size != 3
        print 'Argument Error:cloudwatch_billing.rb HOST AWS_ACCESS_KEY AWS_SECRET_KEY'
        exit(false)
end

begin
        logger = Logger.new(LOGFILE)
        logger.level = Logger::WARN
rescue
        begin
                Dir::mkdir(LOGDIR)
                retry
        rescue
                print "Can't open #{LOGFILE}"
                exit(false)
        end
end

### Get AWS Billing info (use AWS CloudWatch API)###

begin
        acw = RightAws::AcwInterface.new(AWS_ACCESS_KEY, AWS_SECRET_KEY, {:logger => logger})
        data = acw.get_metric_statistics(options={:dimentions => {"Currency"=>"USD"},
                                                :measure_name => "EstimatedCharges",
                                                :namespace => "AWS/Billing",
                                                :start_time => STARTTIME,
                                                :end_time => ENDTIME,
                                                :period => PERIOD})
        data[:datapoints].sort! {|a,b| b[:timestamp] <=> a[:timestamp]}

rescue
        logger.error("Can't Access AWS API")
        print FAILURE
        exit(false)
end

### Zabbix API Login ###

zbxapi = ZabbixAPI.new(ZABBIX_API_URL)
begin
        zbxapi.login(ZABBIX_LOGINID,ZABBIX_PASSWORD)
rescue
        logger.error("Login Failure")
        print FAILURE
        exit(false)
end

### Send Zabbix (use Zabbix Sender)###

host = zbxapi.raw_api("host.get", {:filter => {:host => HOST},:output => "extend"}).first
if !host.nil?
        cmd_total = "#{get_echo_command(HOST,TOTAL_ITEM_KEY,get_total_billing(data[:datapoints]))} | #{ZABBIX_SENDER_PATH} -z #{ZABBIX_SERVER} -T -i - 2>> #{LOGFILE} >/dev/null"
        cmd_oneday = "#{get_echo_command(HOST,ONEDAY_ITEM_KEY,get_oneday_billing(data[:datapoints]))} | #{ZABBIX_SENDER_PATH} -z #{ZABBIX_SERVER} -T -i - 2>> #{LOGFILE} >/dev/null"
        if system(cmd_total) && system(cmd_oneday)
                print SUCCESS
        else
                print FAILURE
        end
else
        logger.error("Can't Found Host #{HOST}")
        print FAILURE
        exit(false)
end

配置後、利用される環境に合わせて設定部分を変更して下さい。
変更が必要な部分は以下になります。

設定項目 用途 デフォルト
STARTTIME,ENDTIME CloudWatchAPIで取得するデータの期間を選択 最新の24時間
PERIOD CloudWatchの監視データの間隔指定(秒単位) 300
ZABBIX_SERVER Zabbixサーバのホスト名orIPアドレス ‘example.com’
ZABBIX_API_URL ZabbixAPI用URL http://#{ZABBIX_SERVER}/zabbix/api_jsonrpc.php”
ZABBIX_LOGINID Zabbix APIアクセス用ユーザ名 ‘admin’
ZABBIX_PASSWORD Zabbix APIアクセスパスワード ‘zabbix’
ZABBIX_SENDER_PATH zabbix_senderパス ‘/usr/bin/zabbix_sender’
LOGDIR このスクリプトのエラーログ出力先ディレクトリ指定 ‘/tmp/script_logs’
LOGFILE エラーログファイル名指定 “#{LOGDIR}/zabbix_external_script.log”

最後にスクリプトにZabbix起動ユーザが実行できるよう権限を付与して下さい。

1
2
$ sudo chown zabbix:zabbix cloudwatch_billing.rb
$ chmod u+x cloudwatch_billing.rb

このスクリプトの利用方法は以下です。

1
$ ./cloudwatch_billing.rb Zabbixに登録したホスト名 AWSのアクセスキー情報 AWSのシークレットキー情報

上記Rubyスクリプトはアカウントの課金情報の月額トータル値と直近24時間分の課金額をZabbixに登録します。
24時間に1度実施することで、1日分の情報をまとめて登録することができます。

このように「外部スクリプト」と「Zabbix Sender」を組み合わせることで、
一度のスクリプト処理で複数の監視結果を登録することも可能になります。
その他、監視したい内容に応じて上記スクリプトをカスタマイズして下さい。

また、Consolidated Billing(一括決済)の設定を実施されている場合は注意して下さい。
上記スクリプトでは、登録しているアカウント全てのトータル課金情報を取得しています。

アカウント毎の課金情報を取得したい場合は、上記スクリプトのget_metric_statisticsの引数のdimentionsに「LinkedAccount」の設定を追加して下さい。
さらに、サービス毎の課金情報を取得したい場合には、「ServiceName」の項目を追記して下さい。

以下、「123456789012」アカウントの「AmazonEC2」の利用料金のみ取得する場合の設定です。

1
{:dimentions => {"Currency"=>"USD","LinkedAccount"=>"123456789012","ServiceName"=>"AmazonEC2"},

LinkedAccountの情報やServiceNameの情報は、AWS Management Consoleから調べることができます。
以下の図はCloudWatchの画面から課金情報の監視結果を見た図になります。

cloudwatch_manage.PNG

スクリプトを利用する際の注意点として、社内にあるZabbixサーバから利用するなど、
Zabbixサーバがプロキシ経由でしかインターネットアクセスできない場合は、
gemパッケージ「right_aws」内のlib/awsbase/right_awsbase.rbに以下の変更を加えて下さい。

1
2
3
4
・・・略
#connection[:connection] ||= Rightscale::HttpConnection.new(:exception => RightAws::AwsError, :logger => @logger)←コメントアウト
connection[:connection] ||= Rightscale::HttpConnection.new(:exception => RightAws::AwsError, :logger => @logger, :proxy_host => "プロキシホスト名", :proxy_port => プロキシポート番号, :proxy_username => "プロキシユーザ名", :proxy_password => "プロキシパスワード")
・・・略

これでAWS APIへのアクセスがプロキシ経由で実施されるかと思います。

Zabbixに課金情報収集用ホスト設定

最後に、Zabbixに課金情報を収集するためのホスト設定を行います。

前提として、上記スクリプトが稼働するZabbix Serverでは、zabbix_senderが利用でき、
Zabbix APIで以下で設定する課金情報収集用ホストの情報が取得できることが条件となります。
Zabbix APIの設定方法などについては こちら をご参考に設定して下さい。

まず、以下ののテンプレートをインポートします。
(Zabbix2.0用のテンプレートです)

zabbix_aws_billing_template.xml

このテンプレートには以下の3アイテムが登録されています。

アイテム名 タイプ キー 説明
AWS Billing Info Check 外部チェック cloudwatch_billing.rb[{HOST.HOST},{$ACCESS_KEY},{$SECRET_KEY}] cloudwatch_billing.rbを定期的に実行するアイテム
Total Billing Zabbixトラッパー aws.billing.total ZabbixSenderで送信された当月トータル課金情報受信用アイテム
Oneday Billing Zabbixトラッパー aws.billing.oneday ZabbixSenderで送信された監視時点から24時間分の課金情報受信用アイテム

次に、このテンプレートを割当てたAWSアカウント用アイテムを作成します。

インタフェースの設定情報は利用しないので、何を設定しても問題ありません。
マクロに以下の2点を登録して下さい。

  • {$ACCESS_KEY} : AWSアクセスキー情報
  • {$SECRET_KEY} : AWSシークレットキー情報

以上で設定は終了です。
以下のように課金情報がグラフ化されます。
また、必要に応じてトリガー設定を変更することで課金状況に応じた処理を行うことが可能になります。

1day_graph.PNG

total_graph.PNG

まとめ

AWSの新機能を利用して課金情報をZabbixで収集してみました。
AWS CloudWatchの機能だけだと、課金情報を監視してアラートメールを送付するといったことしかできなかったり、
2週間しか監視データが保存できなかったりと制限が多いかと思います。
そういった制限をカバーして、より柔軟に管理するためにも外部ツールとの連携は必要なのではないかと思います。
今回は課金情報のみ取得しましたが、
同様の方法でCloudWatchの他の監視情報についてもZabbixで集約可能です。
APIを活用すればより柔軟な管理ができるのではないでしょうか。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中