JavaScript
AWS
nodejs
lambda
serverless

Serverlessで日々のAWSコストを算出する

最近、Cost Explorer APIというものが新しくリリースされ、請求ダッシュボードから見られるコストが取得できるようになりました。
Introducing the AWS Cost Explorer API

これを使用し、Serverless FrameworkでEC2やCloudFrontなど各サービス毎のコストを算出してみます。

serverless.yml

serverless.yml
service: cost-checker

provider:
  name: aws
  runtime: nodejs6.10
  region: us-east-1

  iamRoleStatements:
    - Effect: Allow
      Action:
        - ce:GetCostAndUsage
      Resource: "*"

functions:
  costCheck:
    handler: cost.check
    events:
      - schedule: cron(0 1 * * ? *)
    timeout: 300

Cost Explorer API はバージニア北部でしか使えないため region: us-east-1 を指定しdeployします。
cronはUTC表記のため、今回は日本時間の朝10時にCloudWatchEventsでLambdaが実行されます。

Cost Explorer API

cost.js
const AWS = require('aws-sdk');

const costexplorer = new AWS.CostExplorer();

module.exports.check = () => {
  const params = {
    Granularity: 'DAILY',
    Metrics: [ 'UnblendedCost' ],
    GroupBy: [{
      Type: 'DIMENSION',
      Key: 'SERVICE',
    }],
    TimePeriod: {
      Start: '2017-12-07',
      End: '2017-12-08',
    },
  };

  costexplorer.getCostAndUsage(params, (err, data) => {
    if (err) {
      console.error(err, err.stack);
      return;
    }

    console.log(data);    
  });
};

これだけで2017-12-07の各コストが取得できます。
細かいAPIの使い方はこちら
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CostExplorer.html

動的に日付を取得する

これだけでは、2017-12-07の分しか取得できないため、TimePeriodを動的に指定できるようにします。

const _ = require('lodash');

const now = new Date();
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate());

const TimePeriod = {
  Start: `${start.getFullYear()}-${_.padStart(start.getMonth() + 1, 2, 0)}-${_.padStart(start.getDate(), 2, 0)}`,
  End: `${end.getFullYear()}-${_.padStart(end.getMonth() + 1, 2, 0)}-${_.padStart(end.getDate(), 2, 0)}`,
};

now.getDate() - 1で実行日の前日を取得、この方法であれば月をまたいだときも日付を自動で生成してくれます。
また、lodashを使い_.padStart()で1桁の場合でも簡単に0で埋めることができます。

Slackに送る

かなり雑ですが、costexplorer.getCostAndUsage()で取得した値を、Slackのmessage apiのattachments.fieldsに詰めて送信しています。

const costs = [];
_.each(data.ResultsByTime, (item) => {
  _.each(item.Groups, (group) => {
    costs.push({
      serviceName: _.head(group.Keys),
      value: _.round(group.Metrics.UnblendedCost.Amount * 120, 2),
    });
  });
});

const message = {
  username: 'コストさん',
  channel: '#cost',
  text: `${TimePeriod.Start}のコストです。:money_with_wings:`,
  attachments: [
    {
      fields: _.map(costs, (item) => {
        return {
          title: item.serviceName,
          value: item.value,
          short: true,
        }
      });,
    }
  ],
};

slack.post(message);

金額はドルで返ってくるので、1ドル120円換算にして、小数点2位まで表示

Slack上では、こんな感じで投稿されて便利です。
sc.png

おわり

実際のところ、デイリーでコストを出しても毎日のアクセス数の上下で変わってくるので、よくわからないw
でも、一気にコストを削ったときにこうして見られるのは楽しい。一時的に使ったサーバーを消し忘れたとか、設定のミスとかもこういったレポートで、すぐに気がつけるんじゃないかなーと思います。
何よりメンバーにコスト感が生まれるし、どのAWSサービス使ってるかわかって良い。

Cost Explorer APIは1コール$0.01と結構お高い額を請求されます。
基本的に乱打するものではないと思いますが注意!