AWS Lambdaをつかって、CloudWatchが監視した値を超えたら、slackに通知する仕組みをつくろう on Python


## はじめに

社内で「らむだが〜らむだが」と話していて、元C#の僕からするとプログラミング言語の何かかな？と思っていましたが、違いましたね。（C#にはラムダ式というのがある。）

社内では、AWS Lambda上にnode.jsランタイムを配置して、slack通知していましたが、nodeのversionが古くてそろそろサポートやめるよ、と言われたので、Pythonに切り替えることにしました。（nodeより、Pythonのほうが知っている人が社内にいたので）

AWS Lambdaでは、よくあるような処理はテンプレートとして用意されているので、結構簡単にできます。手順をまとめてみます。

## 設定手順

おおまかに下記の手順でやります。

1. Lambda関数作成
2. SNSの作成とLambdaとの連携
3. EC2とSNSの連携
4. コーディング
5. テスト

## 構成図

最終的にこんな感じになります。

![aws.png](https://qiita-image-store.s3.amazonaws.com/0/37873/96de62d6-6c0b-a771-c12b-4b9125b2db1b.png "aws.png")

※ 構成図は[draw.io](https://www.draw.io/)を使って書いてみました

## 手順
### 1. Lambda関数の作成

今回のメインとなるLambda関数を作成しておきます。指示に従っていけば簡単に作れます。
作り直しや削除も簡単にできるのでご安心を。

サービス一覧から、Lambdaを選択。

![①.png](https://qiita-image-store.s3.amazonaws.com/0/37873/16258ef8-c835-404a-e7a6-9b7e3fae3439.png "①.png")

Lambda関数の作成を開始します。

![②.png](https://qiita-image-store.s3.amazonaws.com/0/37873/6a41c9a7-a514-f929-6f09-fccff50facf2.png "②.png")

フィルターに、slackと入力するとslackの雛形ができますが、今回はブランクで作成します。

![③.png](https://qiita-image-store.s3.amazonaws.com/0/37873/77970c5b-4ddc-717c-6184-d8eb958199c0.png "③.png")

トリガーはあとで設定するので、からのままにしておきます。

![④.png](https://qiita-image-store.s3.amazonaws.com/0/37873/da99ee68-e3b2-b6a5-bdb1-71ad36d0ec67.png "④.png")


### 2. SNSの作成とLambdaとの連携

SNS = Simple Notification Service。何らかの処理や状態変更を受取、メッセージにして通知してくれるサービスです。
メールや今回のようなLamdaへメッセージを通知できます。
サービス自体は、2017/04/06現在翻訳はされていませんが、簡単な英語なので問題ないと思います。

SNSを作成し、subscriptionをくっつけます。

* 参考 : [amazon sns で、iOS,Androidにpush通知する方法](http://qiita.com/papettoTV/items/f45f75ce00157f87e41a)

Subscriptionsを作成する。

![AWS_SNS.png](https://qiita-image-store.s3.amazonaws.com/0/37873/c221abad-f54c-aa95-9e9f-2a70b867ebec.png "AWS_SNS.png")

先程作成したLambda関数を選択します

![AWS_SNS.png](https://qiita-image-store.s3.amazonaws.com/0/37873/1be9dc0e-af73-b28b-c6d4-6e110a2ab702.png "AWS_SNS.png")


### 3. EC2（Cloud Watch）とSNSの連携

EC2の画面から、アラームを設定します。

![EC2_Management_Console.png](https://qiita-image-store.s3.amazonaws.com/0/37873/16deface-478c-ba7d-ece5-06ac1f492b30.png "EC2_Management_Console.png")


### 4. コーディング

各種設定を行います

設定するものは下記です。
* Lambda関数名
* コード
* 環境変数
* 関数ハンドラーとロール
etc


今回は下記のように設定してみました。

![Lambda_Management_Console.png](https://qiita-image-store.s3.amazonaws.com/0/37873/a4b73c86-0cd4-3bff-f0b1-ae752870ec1e.png "Lambda_Management_Console.png")


@コード

```python:lambda_handler.py
from __future__ import print_function

import boto3
import json
import logging
import os

from urllib2 import Request, urlopen, URLError, HTTPError

# slackの設定
SLACK_CHANNEL = os.environ['SLACK_CHANNEL']
HOOK_URL      = os.environ['HOOK_URL']

logger = logging.getLogger()
logger.setLevel(logging.INFO)


def lambda_handler(event, context):
    logger.info("Event: " + str(event))
    message = json.loads(event['Records'][0]['Sns']['Message'])
    logger.info("Message: " + str(message))

    alarm_name  = message['AlarmName']
    description = message['AlarmDescription']
    new_state   = message['NewStateValue']
    reason      = message['NewStateReason']

    if new_state == 'OK':
        emoji = ":+1:"
    elif new_state == 'ALARM':
        emoji = ":exclamation:"
        
    slack_message = {
        'channel': SLACK_CHANNEL,
        'text': "*%s %s: %s*\n %s\n %s" % (emoji, new_state, alarm_name, description, reason)
    }

    req = Request(HOOK_URL, json.dumps(slack_message))
    try:
        response = urlopen(req)
        response.read()
        logger.info("Message posted to %s", slack_message['channel'])
    except HTTPError as e:
        logger.error("Request failed: %d %s", e.code, e.reason)
    except URLError as e:
        logger.error("Server connection failed: %s", e.reason)
```

@環境変数+あるふぁ

![Lambda_Management_Console_1.png](https://qiita-image-store.s3.amazonaws.com/0/37873/b221f013-9e26-9659-f433-7990186dd489.png "Lambda_Management_Console_1.png")


### 5. テスト

Lambdaの画面から、下記を実施します。

1. アクション→テストイベントの設定→テストイベント入力
2. 保存してテスト

手前味噌ですが、テストデータはこちらを参考にしていただければと。
* 参考：[AWS Lambdaをつかった、CloudWatch監視 -> slack通知の際のテストデータ](http://qiita.com/TanakanoAnchan/items/87c893e38b7ed68e1e5d)

## 実行結果

sandboxチャンネルに無事通知が飛びました。絵文字も入っていますね。

![Slack_-_eversense.png](https://qiita-image-store.s3.amazonaws.com/0/37873/21775a01-87db-e2fe-33d3-bcd5e647ef06.png "Slack_-_eversense.png")


## おわりに

簡単なプログラムでslack通知が実現できました。適切にメトリクス設定して、快適なAWSライフを。（通知しても、見をとしてしまったりしたら意味ないけれど…）

なお、Lambdaからは同一リージョンで作成したSNSしか設定できないようなので、リージョンをまたぐ場合は各リージョンのSNSをからLambdaを選択してあげます。

当初、Lambdaの環境変数を利用しようと思いましたが、base64周りでエラーが出てしまったので使えていません。これ使えると、Lambda関数が簡単に使いまわせるので次の機会にはきちんと対応しようと思います。