AWS
CloudWatch
lambda
Slack

AWSの前日コストをSlackに通知してみた。概算でなくて正確な値で。

More than 1 year has passed since last update.

概要

弊社ではSmoozというブラウザアプリを作っていて、一部機能をEC2のスポットインスタンスで運用しています。
ですが、ユーザー数の変動などでインスタンス数がかなり上下し、コスト変動が激しいです(うれしい悲鳴?)。
結構お金がかかるので気にはなるが、毎日コストエクスプローラーで確認するのが面倒。
どうにかしてみました。

問題点

ぐぐってみると、

同じニーズはたくさんあるようで、Lambdaを使ってCloudWatchのBillingを取得してSlackに通知というのが王道のようです。

ということで真似てやってみたのですが、、、

あれ、実際の額と結構違わない?

ClougWatchのBillingはあくまでも概算なので、正確な値と異なります。
これだとあんま役に立たないよ、ということでコストエクスプローラーからスクレイピングでコストを取得するようにしました。

ざっくり説明

Seleniumでコストエクスプローラーにアクセスして、
前日のコストを取得してそれをSlackに流すようなスクリプトをEC2上で毎日17時に動かしています。

EC2上でHeadless Chromeを動かす

Xvfbを使っても良かったのですが、せっかくHeadless Chromeが出たということで
試してみました。環境構築に関する記事はネット上にたくさんあるのですが、↓が一番サクッと導入できました。

headless chromeをPythonのseleniumから動かして引数を考えた (Ubuntu 16.04)

ちなみにAmazon Linx上でHeadless Chromeを動かす記事もいくつかあったので試したのですが、
うまくいかずUbuntuで動かしてます。
コスト的にAmazon Linux上で動かしたい、、、誰かサクッとできた人いれば教えてください。

Pythonでのスクレイピング部分

サービスとそこまで関係ないので適当に書いてます笑
スクレイピングに慣れ親しんだ方なら瞬殺かと。

#!/usr/bin/python3

import os
import datetime
import slackweb
from time import sleep
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

# headless chrome
CHROME_BIN = "/usr/bin/chromium-browser"
CHROME_DRIVER = os.path.expanduser('/usr/bin/chromedriver')

options = Options()
options.binary_location = CHROME_BIN
options.add_argument('--headless')
options.add_argument('--window-size=1280,3000')

driver = webdriver.Chrome(CHROME_DRIVER, chrome_options=options)

# ログイン
driver.get("https://{ACCOUNT_ID}.signin.aws.amazon.com/console")
driver.find_element_by_id('username').send_keys({USENAME})
driver.find_element_by_id('password').send_keys({PASSWORD})
driver.find_element_by_id('signin_button').click()
sleep(3)

driver.get("https://console.aws.amazon.com/cost-reports/home?region=ap-northeast-1#/savedReports")
sleep(3)

# デイリーレポートを選択
driver.find_element_by_partial_link_text('Daily').click()
sleep(3)

# slackにpostするときに使う
current_url = driver.current_url

# 期間を昨日から昨日、つまり昨日だけにする
driver.find_element_by_xpath('//*[@class="picker-dropdown"]').click()

# 前日の日付
yesterday = datetime.date.today() -datetime.timedelta(1)
yesterday_str = yesterday.strftime('%m/%d/%Y')

# from
elem = driver.find_element_by_xpath('//label[text()="From"]/following::input')
elem.clear()
elem.send_keys(yesterday_str)
sleep(1)

# to
elem = driver.find_element_by_xpath('//label[text()="To"]/following::input')
elem.clear()
elem.send_keys(yesterday_str)
sleep(1)

# 適用
elem = driver.find_element_by_xpath('//div[text()="Apply"]').click()
sleep(1)

# コスト取得
cost = driver.find_element_by_xpath('//*[@id="tr-total"]/td[2]').text

# slackに投稿
slack = slackweb.Slack(url={Webhook_URL})
yesterday_str2 = yesterday.strftime('%Y/%m/%d')
url = 'https://console.aws.amazon.com/cost-reports/home?region=ap-northeast-1'
text = u"{yesterday_str2}のコストは${cost}です。/ {current_url}".format(**locals())

slack.notify(text=text)
driver.quit()

Slackにはこんな感じでPostされます

image.png

(おまけ)コストエクスプローラーはいつ更新されるのか?

コストエクスプローラーに馴染みががある人はわかると思いますが、
一体いつ更新されるのか謎ですよね。

AWSのサポートに問い合わせたところ、
24時間ごとに少なくとも一度はデータを更新する。
ただし、正確な更新時間は非公開とのこと。

それじゃあ困る、、、
とうことでcronを1時間おきに設定して、更新時間を追ってみました。
朝と夕方、翌日に更新されてるみたい。

コストエクスプローラーの日付はUTCでして、
JST 9:00をすぎて昨日のコストを見に行くと、実際に請求される額の半分程度。
その日の16時ごろにまた更新されて実際に請求される額の9割程度。

そしてまた翌日にも更新されて実際の額になっているようです。