Serverless Framework + SNSでLambdaのエラーをSlackに通知しよう!

記事タイトルとURLをコピーする

この記事は約4分で読めます。

この記事は1年以上前に書かれたものです。
内容が古い可能性がありますのでご注意ください。

はじめに

こんにちは。孔子の80代目子孫兼技術5課の孔です。やっと梅雨が終わりましたね!日光で干した洗濯物の方が気持ちよく着れるので、とても嬉しいですが、ここ3ヶ月間家からほとんど出てないのでそんなに意味なかったな…とダークモードになっています。コロナ終息のために、頑張りましょう!

今日はServerless Framework + SNSというトピックを持ってきました。例えば、CloudWatch Eventsを使ってLambdaを毎日朝9時に走らせるような処理をするとしいて、毎日朝9時にLambdaが正常に起動したかどうか確認しにAWSコンソールを開くのはとても面倒な作業ですね。そこでLambdaが失敗した時にSNSで自動的に通知して、Slackでそのメッセージを受け取るような構成を作れば、毎日確認する作業がなくなってとても楽になります。

そのような構成とサンプルコードを、Serverless Frameworkでデプロイしてみよう!というのが本日のテーマとなります。Serverless FrameworkはAWS上にLambdaを中心にいろいろなAWSリソースをデプロイできるサービスとなります。それでは、実際やってみましょう。

※ Serverless Frameworkを触ったことのある方を対象としているため、基本的な操作については説明を割愛しています。

まずはPythonコード

2つのコードを用意しました。Slackに投稿するためのLambdaコード(notify_to_slack.py)と、SNSにpublishするコード(publisher.py)となります。

# notify_to_slack.py
import urllib3
import json
http = urllib3.PoolManager()
def lambda_handler(event, context):
url = "https://hooks.slack.com/services/XXXXX...."
msg = {
"channel": "#slack_channel", # Slackのチャンネル名
"username": "Lambdaからのお知らせ", # bot名
"text": event['Records'][0]['Sns']['Message'],
"icon_emoji": "emoji" # botのアイコン
}
encoded_msg = json.dumps(msg).encode('utf-8')
resp = http.request('POST', url, body=encoded_msg)
return {
"message": event['Records'][0]['Sns']['Message'],
"status_code": resp.status,
"response": resp.data
}

urlの中には、SlackのWebhookを入力してください。msgの中身のtext以外のものも適切なものに変えてください。

# publisher.py
import boto3
from os import getenv
def lambda_handler(event, context):
try:
raise Exception('エラー発生!')
except:
client = boto3.client('sns')
TOPIC_ARN = getenv('SNS_TOPIC')
msg = 'テストです'
subject = 'てすてす'
response = client.publish(
TopicArn=TOPIC_ARN,
Message=msg,
Subject=subject
)
return response

こちらのコードはclientにSNSを指定し、publishというAPIを使っています。publish APIは名前通りSNSのTopicにメッセージをpublishするAPIとなります。

これで、コードの準備は完了です。それではServerless FrameworkでSNSおよびLambda関数をデプロイしてみましょう!

serverless.ymlを記入する

serverless.ymlの中身は以下となります

service: sns-test
provider:
name: aws
runtime: python3.8
stage: dev
region: ap-northeast-1
functions:
publisher:
handler: publisher.lambda_handler
environment:
SNS_TOPIC: !Ref FailureTopic
role: publishToSns
notify_to_slack:
handler: notify_to_slack.lambda_handler
events:
- sns:
arn: !Ref FailureTopic
topicName: FailureTopic
resources:
Resources:
FailureTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: FailureTopic
publishToSns:
Type: AWS::IAM::Role
Properties:
RoleName: PublishToSNSRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: PublishToSNSPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- sns:*
Resource:
- !Ref FailureTopic

まずfunctionsで先ほど作成した2つのコードをLambdaにデプロイしています。notify_to_slackの方で、eventsの中にSNSを指定していますが、こちらがLambdaのトリガーイベントとなります。

f:id:swx-kong:20200804174339p:plain

トリガーとして例えばAPIGWやS3イベントがあると思いますが、そのようなトリガーの設定をこちらのeventsから設定します。

また、resourcesの中でSNSトピックを作成しています。Serverless Frameworkを使ってリソースをデプロイする際にはこちらのresources項目を使います。このように作成したSNSトピックをnotify_to_slackのSNSイベント項目に!Ref FailureTopicとして指定することで、Serverless FrameworkがデプロイしたSNSトピックをデプロイされるLambdaが参照できるようになります。

デプロイしてからpublisher.pyをテストなどで起動してみると、無事Slackに指定したチャンネルにメッセージが飛んでくることが確認できるかと思います。

最後に

Serverless Frameworkはとても便利なデプロイツールですのでどんどん使ってみましょう!特に今回触れたeventsは、イベントドリブンで発火することの多いLambdaにとってとても高頻度で使用される機能ですので、覚えておくと役に立つことが多いかと思います。