VPN接続の帯域をCloudWatchでモニタしてみた

VPNコネクションの帯域は各種SNMP監視ツールでモニタするのがよいと思いますが、せっかくなのでCloudWatchの画面でも確認できるようにしてみました。

下準備

使用しているルータのトンネルインターフェースのindexを調べます。うちはSEILを使用していて、トンネルインターフェース名はそれぞれipsec0/1なので、

$ snmpwalk -v 2c -c public gw2 ifDescr | grep ipsec | head -2
IF-MIB::ifDescr.549 = STRING: ipsec0
IF-MIB::ifDescr.550 = STRING: ipsec1

549と550がそれぞれtunnel1と2のindexである事が分かりました。
あとは、ifInOctets(CustomerGatewayから見て受信なので、AWSから見てNetworkOut)やifOutOctets(同じくNetworkIn)のOIDが分かればOK。.1.3.6.1.2.1.2.2.1.10と.1.3.6.1.2.1.2.2.1.16がそれぞれ該当します*1
次にそれらの値をCloudWatchのCustomMetricのどのNameSpace/Metric/Dimensionで書き出したいかをまとめます。

[
  { "NameSpace":"VPC","MetricName":"NetworkOut","Dimension":{"TunnelName":"vpn-18943e19-1"}, "host":"192.168.1.254", "community":"public", "mib":".1.3.6.1.2.1.2.2.1.10.549" } ,
  { "NameSpace":"VPC","MetricName":"NetworkIn","Dimension":{"TunnelName":"vpn-18943e19-1"}, "host":"192.168.1.254", "community":"public", "mib":".1.3.6.1.2.1.2.2.1.16.549" } ,
  { "NameSpace":"VPC","MetricName":"NetworkOut","Dimension":{"TunnelName":"vpn-18943e19-2"}, "host":"192.168.1.254", "community":"public", "mib":".1.3.6.1.2.1.2.2.1.10.550" } ,
  { "NameSpace":"VPC","MetricName":"NetworkIn","Dimension":{"TunnelName":"vpn-18943e19-2"}, "host":"192.168.1.254", "community":"public", "mib":".1.3.6.1.2.1.2.2.1.16.550" }
]

スクリプトを動かす

pythonスクリプトを動かします。botoとpysnmpをpip等でインストールしておいてください。
かなり適当に書きなぐった(リージョンやコンフィグファイル名が決め打ちです)物ですので、気に入らないところは適宜修正してください。
環境変数やIAM Roleなどで認証情報が設定されている事が前提となります。
cron等で1分間毎に動かす事で、平均使用帯域(Mbps)をCloudWatchのCustomMetricとして記録します。/var/tmp/{OID} に前回の値を書き込むので注意です。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import boto.ec2.cloudwatch
c = boto.ec2.cloudwatch.connect_to_region('ap-northeast-1')

import json
config=json.load(open("config.json"))

from pysnmp.entity.rfc3413.oneliner import cmdgen
cmdGen = cmdgen.CommandGenerator()

for metric in config:
    try:
        f=open('/var/tmp/'+metric['mib'].encode('latin-1'))
        previous=int(f.read())
        f.close()
    except:
        print "new mib"
        previous=0

    errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd(
            cmdgen.CommunityData(metric['community']),
            cmdgen.UdpTransportTarget((metric['host'], 161)),
            cmdgen.MibVariable(metric['mib'].encode('latin-1')),
            lookupNames=True,lookupValues=True
    )
    if errorIndication:
        print(errorIndication)
    elif errorStatus:
        print(errorStatus)
    elif len(varBinds) != 1 :
        print("only 1 var is allowed")
    else:
        for name, val in varBinds:
            if previous == 0 :
                print('%s : %s (new)'  % (metric['mib'], val.prettyPrint() ))
            else :
                diff=int(val)-previous
		if diff < 0:
			diff=diff + 4294967296

                mbps=8.00*diff/(1024*1024)/60
                print('%s : %s - %d = %d / 60 sec = %f Mbps' % (metric['mib'], val.prettyPrint(), previous, diff, mbps))
                c.put_metric_data(metric['NameSpace'],metric['MetricName'],mbps,None,'Megabits/Second',metric['Dimension'])

            f=open('/var/tmp/'+metric['mib'].encode('latin-1'),'w')
            f.write(str(val))
            f.close()

結果の確認

しばらく動かしてみて、グラフを確認します。

こんな感じに描画されます。
CloudWatchのデータは2週間しか保存されませんので、データ蓄積用というよりは状況の確認やアラーム用途といった所でしょうか。

*1:これらは32bitカウンタなのですが、64bitカウンタが使えるルータがあればそれを利用した方がよいと思われます