SD-WANのAPIを使って
トラフィック監視システムを作ってみた

【技業LOG】技術者が紹介するNTTPCのテクノロジー

2020.02.20
SD-WAN
村田 健

サーバーエンジニア
村田 健

技業LOG

APIで広がるSD-WANの機能

クラウドサービスの普及により、様々なサービスやシステムがAPIでつながるということが当たり前になっています。
これはネットワークサービスにも言えることであり、その代表と言えるのはSD-WANではないでしょうか。
そこで今回はクラウドサービスとSD-WANサービスをAPI連係させ、通信トラフィックを監視するシステムを構築してみたいと思います。

SD-WANの特長

本題に入る前に簡単にSD-WANの特長を説明します。
SD-WANとは Software-Defined Wide Area Network の略であり、簡単に言えばソフトウェア制御された高機能なネットワークです。

その特長の一つに、APIが整備されていることが挙げられます。
そしてAPIが使えることのメリットとして最も大きいのは 他システムとの連係が容易 という点かと思います。

これまでであれば各ネットワークサービスの製品のインターフェイスやコマンドに合わせて開発が必要だったのに対し、 APIを使うことで一般的なインターフェイス(Restful API)で連係でき、開発が容易になります。

Master'sONE CloudWAN®のAPIについて

NTTPCが提供するSD-WANサービス、Master'sONE CloudWAN® (以下CloudWAN) のAPIについて少しご説明します。
このサービスにはRESTful APIが用意されており、様々なシステムとの連係を容易に行うことができます。
このAPIを使ってできることは大きく分けて

  • 拠点の追加/廃止等のオーダー
  • VPNの設定
  • エッジ装置の状態取得

などがありますが、今回は活用例の一つとして エッジ装置の状態取得APIを使ったトラフィック監視システムを構築してみたいと思います。

やりたいこと

簡単にまとめると次のようになります。

  • 特定アプリケーションの通信量を定期的に監視し、設定した閾値を超えている場合、Slackに通知させたい

これをCloudWAN + AWS を用いて実現します。

利用するシステム

今回は次のような構成を考えました。

利用するシステム 概要図

AWS Lambda(以下Lambda)から定期的にCloudWANのAPIを実行して、指定したアプリケーションの通信量データを取得し閾値と比較します。
閾値を超えていた場合は異常と判断し、slackに通知します。

前提条件

本システムを構築するためには次の前提条件があります。

  • CloudWANのご契約があること
  • AWSのご契約があること
  • slackのアカウントがあること(有償 / 無償問いません)

手順概要

大まかに以下のような作業を行います。

  1. Slackにwebhookの設定を行う
  2. LambdaにCloudWANの通信量を取得する関数を作成する
  3. Lambdaを定期実行する設定を行う

手順詳細

それでは具体的な手順を説明していきます。

1. Slackにwebhookの設定を行う。

今回はLambdaから通知を行いたいので、slackのwebhookを使います。
webhookの設定はこちらのslack社公式ドキュメントを参考に設定してください。
設定が完了したら、払い出されたURLをメモしておきます。

2. LambdaにCloudWANの通信量を取得する関数を作成する。

ではいよいよ本記事の中心となるCloudWANの通信量を取得する仕組みを作っていきたいと思います。

まずAWSのコンソールでLambdaに新しい関数を作成します。(名前等はお好きなものでOKです)
言語はpython3系を選択します。

次に関数に次のコードを貼り付けます。

# -*- coding: utf-8 -*-
import sys
import os
import re
import json
import requests
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
portal_login = 'https://portal.onewan.cloud/auth/login'
api = 'https://api.onewan.cloud/v1.0'
def lambda_handler(event, context):
    # 通知するかを判定するためのフラグ    
    alert_flag = 0
    # 環境変数thresholdはbps単位なのでAPIのレスポンス形式(1分間の合計byte)に変換
    threshold = float(os.environ['threshold']) * 60 / 8
    # APIコールし、metricsを取得
    response = run_api("/cloudwan/sites/" + os.environ['site_config_id'] + "/ese_devices/ " + os.environ['ese_device_id'] + "/ese_physical_ports/" + os.environ['port_id'] + "/metrics?start=1h-ago&tenant_id=" + os.environ['tenant_id'])
    # APIコールに失敗した場合
    if response == 1:
        return("api error")
    # 成功した場合、レスポンスから通信量を確認
    for d in response:
        if(d['metric'] == "ese_physical_port.network.bytes.in" and d['tags']['application_name'] == os.environ['app']):
            for u_time, byte in d['dps'].items():
                if byte != None:
                    if byte > float(threshold):
                        # 超えていた場合ログに出力し通知フラグを立てる
                        logger.info(u_time + " bytes = " + str(byte))
                        alert_flag = 1
    # 閾値を超えたデータがあったらアラート通知
    if alert_flag == 1:
        # slack通知
        res = send_notification()
        # slack通知の成功/失敗を判定
        if res == 0:
            logger.info("通知に成功しました")
        else:
            logger.warn("通知に失敗しました")
    return(0)
### APIを実行する関数
def run_api(url):
    # API実行のアクセストークンを取得
    try:
        access_token = get_token(os.environ['id'],os.environ['pw'])
    except:
        return(1)
    if access_token == 1:
        return(1)
    # パラメータ作成
    URL = api + url
    headers = {"Authorization": "Bearer " + access_token}
    try:
        r = requests.get(URL,headers=headers)
    except:
        return(1)
    # 成功した場合はレスポンスをreturn
    return(r.json())
### トークンを取得する関数
def get_token(id, pw):
    # 同一セッションにするとcookieの処理が不要なので、セッションで実行
    with requests.Session() as s:
        # まずログイン画面を取得
        login = s.get(api)
        login_html = login.text
        # csrfを取得
        tmp_line = re.search(r'name="_csrf" value="[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}"', login_html)
        tmp_csrf = re.search(r'[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}', tmp_line.group())
        csrf = tmp_csrf.group()
        # ID/PWをcsrfと一緒にPOST
        params = {'username': id, 'password': pw, '_csrf': csrf}
        token_response = s.post(portal_login,data=params)
        # ID/PWが正しければaccess_tokenがjson形式で帰ってくる。NGの場合はHTMLが帰ってくる
        try:
            j = token_response.json()
            access_token = j['access_token']
            return(access_token)
        except:
            return(1)
### slack通知関数
def send_notification():
    payload = {
        "text": "テスト:通信量の増加を検知しました",
        'icon_emoji': ':warning:'
    }
    try:
        r = requests.post(os.environ['slack_url'], json.dumps(payload))
    except:
        return(1)
    return(0)
  • なお、本コードを実行するためにはrequests ライブラリを使えるようにする必要があります。
    AWSドキュメントを参考に準備してください。

次にLambdaの環境変数に次のパラメータを設定してください。

・id = "コントロールパネルのログインID"
・pw = "コントロールパネルのログインパスワード"
・ese_device_id = "監視対象のエッジ装置のID"
・site_config_id = "監視対象のエッジ装置が設置されている拠点ID"
・tenant_id = "監視対象のエッジ装置が所属するテナントのID"
・port_id = "監視対象のエッジ装置の通信ポートのID"
・threshold = "監視の閾値。bps単位で指定してください。(例: 1Mbpsの場合は 1000000)"
・app = "監視対象のアプリケーション名。"
・slack_url = "通知先slackのWebhookのURL"

なおこれらのIDを確認する手順は こちらのAPI利用ガイドをご参照ください。

  • 本記事では手順簡略化のため環境変数の暗号化等は行なっておりませんが、実際にご利用になる場合はパスワードを暗号化されることを推奨致します。

3. Lambdaを定期実行する設定を行う。

次に作成した関数を定期実行する設定を行います。こちらはAWSのcloud watch eventを使います。
この手順は AWSのドキュメントを参考に行ってください。

動作確認

これで準備は完了です。後はトラフィックが閾値を超えると以下のような通知が届きます。

Slack上で表示されるアラート画面(イメージ)

※Slack上で表示されるアラート画面(イメージ)

まとめ

ということで、今回はSD-WANの通信トラフィックを監視し、一定量を超えた場合にアラートを通知するシステムを構築しました。
このシステムを更に発展させれば通信量の傾向分析等にも活用できるかと思います。
ぜひご参考にしてくださればと思います。

技業LOG

この記事で紹介しているサービスは
こちら

Master'sONE CloudWAN®

SD‒WAN技術を活用し、即時性・柔軟性・拡張性に優れたネットワーク構築・運用を実現

おすすめ記事

    お気軽にご相談ください