notebook

都内でWEB系エンジニアやってます。

Cloudwatch + SNS + SQS でSlackへ通知

ChatOpsの一環としてサービスのアラートをSlackに投げれるようにします

さっと調べた感じ今だとCloudwatch -> SNS -> Lambda -> Slackというのがlambda to slackのblueprintもあるので楽そう + 使ってみたかったのですが担当サービスがそもそもlambdaが使えないリージョンだったため断念

なので代替案としてCloudwatch -> SNS -> SQS -> script -> Slack で実装してみます

既にSNSで特定のメールアドレスに対して通知を送るようにしてあるので、メール+Slackで通知がいくようにします

f:id:swfz:20160216040108p:plain

準備

SQSの作成

slack通知用のQueueを作成します

特別な設定はしません

今回は「sns2slack」という名前のQueueを作成

ここで作成したQueueのARNをメモしておきます

SNSトピックの作成

queueに通知するためのトピックを作成します

既にメール通知用のトピックがあればそれに追加

メール、HTTPなど複数選択できるので今回はメール+SQSへの通知をするように設定

CreateSubscriptionで通知先を追加します

f:id:swfz:20160216040119p:plain

Protocolに「Amazon SQS」に設定

EndpointにSQSの作成でメモしたQueueのARNを入力

Emailに関しても同様の手順で設定します

権限の付与

SNSがSQSへメッセージを送るための権限が必要なので下記手順を参考に権限を付与する(ステップ2)

Amazon SQS キューへの Amazon SNS メッセージの送信

対象Queueを選択してPerissionsのタブをクリック

f:id:swfz:20160216040128p:plain

「Add Permission」をクリックして権限の追加を行います

参考に書いてあるとおりに入力を行います

valueにはSNSのTopicのARNを入力します

f:id:swfz:20160216040139p:plain

スクリプトの準備

SQSへメッセージを取得しにいくスクリプトを用意します

#!/bin/bash

# AWS configuration
SQS_URL="<sqs url>"
REGION="<region>"

# slack configuration
SLACK_CHANNEL="<channel name>"
SLACK_USERNAME="CloudWatchAlarmBot"
SLACK_ICON="<icon_name>"
SLACK_WEBHOOK_URL="<slack webhook url>"

# project configuration
PROJECT="<project name>"

warn(){
  echo $1 >&2
  sleep 10
  continue
}

delete_queue(){
  handle=$1
  aws sqs delete-message --queue-url ${SQS_URL} --region ${REGION} --receipt-handle ${handle}
}

push2slack(){
  messages=($@)

  set -- ${messages[*]}
  name=$1
  description=$2
  state=$3
  metric=$4
  operator=$5
  threshold=$6
  period=$7
  evaluation_periods=$8

data=`cat << EOF
    payload={
    "channel": "${SLACK_CHANNEL}",
    "username": "${SLACK_USERNAME}",
    "icon_emoji": "${SLACK_ICON}",
    "link_names": 1 ,
    "attachments": [{
        "fallback": "Alarm",
        "color": "#993300",
        "pretext": "CloudWatch Alarm" ,
        "title": "${description} ",
        "text": "確認お願いします @channel",
        "fields": [
          {
            "title": "Project",
            "value": "$PROJECT",
            "short": true
          },
          {
            "title": "Detail",
            "value": "${metric} ${operator} ${threshold} . check in every ${period} seconds. failed ${evaluation_periods} times.",
            "short": true
          }
        ]
      }]
  }
EOF`
  curl -X POST --data-urlencode "${data}" ${SLACK_WEBHOOK_URL}
}
main(){
  while true
  do
    res=`aws sqs receive-message --queue-url ${SQS_URL} --region ${REGION}`
    handle=`echo ${res} | jq -r '.Messages[].ReceiptHandle'`
    body=`echo ${res} | jq -r '.Messages[].Body'`
    [[ ${body} == "" || ${handle} == "" ]] && warn 'body or handle not found'
    IFS=$'\n'
    messages=`echo ${body} | jq -r '.Message' | jq -r -c ' \
      .AlarmName, \
      .AlarmDescription, \
      .NewStateReason, \
      .Trigger.MetricName, \
      .Trigger.ComparisonOperator, \
      .Trigger.Threshold, \
      .Trigger.Period, \
      .Trigger.EvaluationPeriods'`
    push2slack ${messages[*]}
    IFS=$' \t\n'
    [[ $? == 0 ]] && delete_queue $handle
    sleep 10
  done
}
main $@

固定値をそれぞれ設定したものにして実行すればOKです

デーモン化

先述のスクリプトをデーモン化もさせておきたいのでsupervisorでデーモン化させまます

ansibleでスクリプト含め対象サーバにデーモン化させるまでのplaybookを用意しました

ansible-playbook-sqs2slack

READMEに従い実行すると環境が整います

確認

アラート設定を変えて通知がいくか確認します

f:id:swfz:20160216040150p:plain

無事Slackに通知が行きました

感想

  • シェルスクリプトに結構時間がかかってしまったのでもっと早く書けるようになりたい
  • SQS,SNS共に便利に使えそう
  • Lambda使いたかった...