#はじめに
個人アカウントは基本的に無料枠で運用しているので、少しでも請求がある場合はいち早く気づきたいです。
先日、とあるハンズオンイベントで使ったリソースを消し忘れて、最終的に$30ぐらい請求が来てしまいました。。。
CloudWatchで請求アラートは設定していますが、閾値超えが想定の場合、当然見逃すことになり、最終的な請求額に驚くハメになります。
これを防ぐためにLambdaで毎日SlackにAWS料金を通知することにします。
先日LambdaがPython3に対応したので、せっかくだし勉強がてらPython3で実装したい。
ネット上にはNode.jsでの実装例が多いようで、今回はこちらを参考にPython3で実装してみます。
#必要なもの
- Slack
- incoming-webhooks URL
- 取得についてはこちらを参考に
- 適当なchannel
- incoming-webhooks URL
- lambda-uploader
- requestsモジュールをLambda上でimportするために利用
- カレントディレクトリにモジュールをインストールして、モジュールごとZipに固めてアップロードでもいけるはずですが、私の環境だとうまくいかなかったので
- requestsモジュールをLambda上でimportするために利用
- aws cli
- lambda-uploaderで必要
- AWS
- Lambda関数用IAM Role
-
CloudWatchReadOnlyAccess
ポリシーをアタッチ
-
- 請求アラートの有効化
- Lambda関数用IAM Role
#事前準備
ローカル
lambda-uploaderをインストール
$ pip install lambda-uploader
こちらを参考にさせていただきました。
###aws cliをインストール
$ pip install awscli
###credential、リージョン設定
$ aws configure
確認
$ aws configure list
AWS(Lambda)
コードをlambda-uploader
でデプロイするためには、あらかじめ空の関数を用意する必要があります。
###IAMロール修正
関数定義の際にポリシーテンプレートから自動作成されたIAMロールにCloudWatchReadOnlyAccess
ポリシーをアタッチします。コスト情報をLambdaから読み込むのに必要です。
ロールARNはあとで必要になるのでメモしておきます。
#コード
##ディレクトリ構成
ディレクトリ名は任意です。関数名とは無関係です。
awscost_to_slack/
|--lambda_function.py
|--requirements.txt
|--lambda.json
##lambda_function.py
超過金額に応じて色をつけるようにしています。
$0.0なら緑、超過したら黄色、$10超えで赤になります。
#!/usr/bin/env python
# encoding: utf-8
import json
import datetime
import requests
import boto3
import os
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
# Slack の設定
SLACK_POST_URL = os.environ['slackPostURL']
SLACK_CHANNEL = os.environ['slackChannel']
response = boto3.client('cloudwatch', region_name='us-east-1')
get_metric_statistics = response.get_metric_statistics(
Namespace='AWS/Billing',
MetricName='EstimatedCharges',
Dimensions=[
{
'Name': 'Currency',
'Value': 'USD'
}
],
StartTime=datetime.datetime.today() - datetime.timedelta(days=1),
EndTime=datetime.datetime.today(),
Period=86400,
Statistics=['Maximum'])
cost = get_metric_statistics['Datapoints'][0]['Maximum']
date = get_metric_statistics['Datapoints'][0]['Timestamp'].strftime('%Y年%m月%d日')
def build_message(cost):
if float(cost) >= 10.0:
color = "#ff0000" #red
elif float(cost) > 0.0:
color = "warning" #yellow
else:
color = "good" #green
text = "%sまでのAWSの料金は、$%sです。" % (date, cost)
atachements = {"text":text,"color":color}
return atachements
def lambda_handler(event, context):
content = build_message(cost)
# SlackにPOSTする内容をセット
slack_message = {
'channel': SLACK_CHANNEL,
"attachments": [content],
}
# SlackにPOST
try:
req = requests.post(SLACK_POST_URL, data=json.dumps(slack_message))
logger.info("Message posted to %s", slack_message['channel'])
except requests.exceptions.RequestException as e:
logger.error("Request failed: %s", e)
##requirements.txt
pip installしたいモジュール名を書きます。
requests
##lambda.json
Lambda関数名、IAMロールのARNなどは環境に合わせてください。
用意したSlackのincoming-webhooks URL
と通知するchannelを関数に渡す環境変数としてvariables
に定義します。
また、スクリプト本体のファイル名とハンドラの前半(サンプルコードではデフォルトlambda_function
)を一致させないと動きません。地味にハマるので注意!
{
"name": "LAMBDA_FUNCTION_NAME",
"description": "DESCRIPTION",
"region": "ap-northeast-1",
"handler": "lambda_function.lambda_handler",
"role": "arn:aws:iam::XXXXXXX:role/ROLE_NAME_FOR_LUMBDA",
"timeout": 300,
"memory": 128,
"variables":
{
"slackPostURL":"https://hooks.slack.com/services/XXX",
"slackChannel":"#XXX"
}
}
##デプロイ
上記ファイルを配置したディレクトリに移動して、lambda-uploaderを実行します。
$ cd awscost_to_slack
$ lambda-uploader
λ Building Package
λ Uploading Package
λ Fin
##Lambda定期実行設定
CloudWatchのスケジュールイベントを定義して、lambda関数をターゲットに指定します。
時刻はUTCなので注意しましょう。
毎日UTC0:00に実行されるよう設定しました。
##実行イメージ
こんな感じで毎朝9:00に通知がきます。
今日も無料!
#まとめ
Lambdaはほぼ無料でプログラムが動かせるので楽しいですね!
Python初心者なのでコードが見苦しい点はご容赦ください。