LoginSignup
1
1

More than 5 years have passed since last update.

CloudWatch AlarmをSlackへ通知する(グラフ 画像 付き)

Last updated at Posted at 2019-03-02

はじめに

CloudWatchAlarm を Slack へ通知しているが、もっとわかりやすく出来ないかと思って探していると、
CloudWatchAlarmをグラフ付きでSlackに通知する という素晴らしい記事を見つけたので、試させてもらいました。

記事の内容と異なる点は、 S3 を使わず Slack file.upload API を利用して、画像をアップロードするようにした点です。
Python の例は何個かあったのですが、node.js で Slack file.upload API を利用してグラフ画像をアップする例はなかったので、参考になればと思います。

ソースは以下です。

SlackBot

Slack file.upload API を使う方法は何通りかあります。利用するトークンにも何種類かあります。
ここでは、 SlackApp を作り Bot とトークンを用意します。

SlackApp の作成とトークン

上記URLにアクセスし「Create New App」ボタンをクリックして、SlackApp を作ります。

次に、左メニュー「Bot Users」をクリックして、Bot を作ります。
最後に、左メニュー「OAuth & Permissions」をクリックし、「Install App to Workspace」ボタンをクリックし Slack Workspace へ SlackApp をインストールします。

すると「OAuth & Permissions」に「Bot User OAuth Access Token」が表示されます。このトークンが後ほど作成する Lambda に記述するトークンです。

Source

Lambda は SAM を使って、デプロイします。

各ファイルを説明します。

index.js

Lambdaで実行するファイルです。

getMetricWidgetImage メソッドで取得したグラフの画像は Buffer として返却されますが、 Buffer のままではアップロードすることができませんでした。
そのため、画像ファイルとして出力後(fs.writeFileSync)、読み込むこと(fs.createReadStream)で StreamReader へ変換しています。
これでアップロードすることができました。

'use strict'

const AWS = require('aws-sdk')
const axios = require('axios')
const FormData = require('form-data')
const fs = require('fs')
const uuidv4 = require('uuid/v4')

const SLACK_BOT_TOKEN = process.env.SLACK_BOT_TOKEN
const SLACK_CHANNEL = process.env.SLACK_CHANNEL

async function getMetricsGraphFromCloudWatch(message) {
  const props = {
    width: 480,
    height: 240,
    start: '-PT3H',
    end: 'PT0H',
    timezone: '+0900',
    view: 'timeSeries',
    stacked: false,
    metrics: [
      [
        message.Trigger.Namespace,
        message.Trigger.MetricName,
        message.Trigger.Dimensions[0].name,
        message.Trigger.Dimensions[0].value
      ]
    ],
    stat:
      message.Trigger.Statistic.charAt(0).toUpperCase() +
      message.Trigger.Statistic.slice(1).toLowerCase(),
    period: message.Trigger.Period,
    annotations: {
      horizontal: [
        {
          color: '#ff6961',
          label: 'Trouble threshold start',
          value: 0
        }
      ]
    }
  }
  const widgetDefinition = {
    MetricWidget: JSON.stringify(props)
  }

  const cloudwatch = new AWS.CloudWatch()
  try {
    const response = await cloudwatch
      .getMetricWidgetImage(widgetDefinition)
      .promise()
    return response.MetricWidgetImage
  } catch (err) {
    console.error(err)
  }
}

async function sendSlackBot(records) {
  for (const record of records) {
    const sns = record.Sns
    const message = JSON.parse(sns.Message)

    let emoji = ':kissing:'
    if (message.NewStateValue == 'ALARM') {
      emoji = ':scream:'
    } else if (message.NewStateValue == 'OK') {
      emoji = ':grinning:'
    }

    const image = await getMetricsGraphFromCloudWatch(message)

    // to stream from buffer
    const file = '/tmp/' + uuidv4() + '-alarm.png'
    fs.writeFileSync(file, image)
    const streamImage = fs.createReadStream(file)

    const formData = new FormData()
    formData.append('token', SLACK_BOT_TOKEN)
    formData.append('filename', message.AlarmName + '.png')
    formData.append('file', streamImage)
    formData.append('filetype', 'png')
    formData.append('initial_comment', emoji + ' ' + sns.Subject + ', ' + message.NewStateReason)
    formData.append('channels', SLACK_CHANNEL)
    formData.append('title', sns.Subject)

    const response = await axios.create({
      headers: form.getHeaders()
    }).post('https://slack.com/api/files.upload', formData)
    console.log(response.data)
  }
}

exports.handler = async (event, context) => {
  // output event
  console.log(JSON.stringify(event))

  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: "OK"
    })
  }

  try {
    await sendSlackBot(event.Records)
  } catch (err) {
    console.log(err)
    return err
  }

  return response
}

package.json

node.js で Lambda を使う場合、aws-sdk はデフォルトで利用できますが、バージョンが古く
CloudWatch.getMetricWidgetImage メソッドが利用できません。
そのため、別途インストールする必要があります。

{
  "name": "aws-cloudwatchalarm-to-slack",
  "version": "0.0.1",
  "description": "Cloudwatch alarm to Slack.",
  "main": "index.js",
  "scripts": {},
  "dependencies": {
    "aws-sdk": "^2.334.0",
    "axios": "^0.18.0",
    "form-data": "^2.3.3",
    "uuid": "^3.3.2"
  },
  "devDependencies": {}
}

template.yaml

SAM のテンプレートファイルです。

Paramters で指定することにより、deploy 時、コマンド引数で指定することができます。

Resources として、Lambda、SNS Topic、IAM Role を指定し作成します。
今回の IAM Role は、あらかじめ用意されている ManagedPolicy を利用しています。
ManagedPolicyArns に指定している内容は、AWS管理コンソール > IAM > ポシリーの一覧から該当ポリシーを選択し詳細を見ると確認することができます。

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: 'Cloudwatch alarm to Slack.'
Parameters:
  SnsTopicName:
    Type: String
    Default: 'xxx-alarm-topic'
  SlackBotToken:
    Type: String
    Default: 'xxx-999-999-xxx'
  SlackChannel:
    Type: String
    Default: '#notify_xxx_alarm'

Resources:
  NotifyFunction:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: index.handler
      Runtime: nodejs8.10
      Role: !GetAtt NotifyRole.Arn
      CodeUri: .
      Description: 'Cloudwatch alarm to Slack.'
      MemorySize: 128
      Timeout: 300
      Environment:
        Variables:
          SLACK_BOT_TOKEN: !Ref SlackBotToken
          SLACK_CHANNEL: !Ref SlackChannel
      Events:
        SNS:
          Type: SNS
          Properties:
            Topic: !Ref NotifyTopic
  NotifyTopic:
    Type: 'AWS::SNS::Topic'
    Properties:
      TopicName: !Ref SnsTopicName
  NotifyRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: 'sts:AssumeRole'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
        - 'arn:aws:iam::aws:policy/AmazonSNSReadOnlyAccess'
        - 'arn:aws:iam::aws:policy/CloudWatchReadOnlyAccess'

Install & Deploy

AWS CLI を使って deploy するので、事前に AWS CLI を インストール し、設定(aws configure) を行ってください。

また、 npm のインストールを Docker を利用して行うので、こちらもインストールしてください(Install Docker Desktop for Mac, Install Docker Desktop for Windows)。

コマンド実行時に変更が必要な引数のみを説明します。

install

docker run -it -v $(pwd):/home/app -w /home/app node:8.12 npm install

package

Argument Example Description
--s3-bucket xxx-bucket-sam ソース等(アーティファクト)をアップロードするバケット
aws cloudformation package \
--template-file template.yml \
--output-template-file serverless-output.yaml \
--s3-bucket xxx-bucket-sam

deploy

Argument Example Description
SnsTopicName xxx-alarm-topic アラームを通知する SNS トピック名
SlackBotToken xxx-999-999-xxx Slack Bot のトークン
SlackChannel #notify_xxx_alarm アラームを通知する Slack のチャンネル
aws cloudformation deploy \
--template-file serverless-output.yaml \
--stack-name cloudwatchalarm-to-slack \
--capabilities CAPABILITY_IAM \
--parameter-overrides "SnsTopicName=xxx-alarm-topic" "SlackBotToken=xxx-999-999-xxx" "SlackChannel=#notify_xxx_alarm"

CloudWatch Alarm

AWS管理コンソールから CloudWatch Alarm を作成し、作成した SNS Topic名を指定すれば、Slack へアラームが画像付きで通知されます。

CloudWatch Alarm の設定は、CloudWatch Alarmでメール通知する際に一緒にグラフ画像を添付してみたの「CloudWatch Alarmの作成」が参考になります。

さいごに

まだ、この設定を行ってアラームは発生していないので、まわりの反応を見ることはできませんが、テストでアラームを発生させた限りだと、画像があることでかなりわかりやすくなったと思います。

忘れたころに来るアラームにも慌てずに対処できるようになると良いな。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1