定期的に Slack へメッセージ通知を行うバッチジョブをサーバーレス化してみた

はじめに

オペレーション部の下田です。最近、業務でちょっとしたツールやサービスを Serverless で実装することが多く、ちょっとした Tips を備忘録として残しておきたいと思います。今回のテーマはバッチジョブをサーバーレス化する方法です。

Serverless Framework を利用する

本記事では、Serverless Framework を利用し CloudWatch Event の cron 式を利用して Lambda 関数(Python)をスケジュール起動するバッチジョブを実装します。

AWS Lambda では、下記にリストされる言語が標準でサポートされています。 好みの言語があれば、既存のバッチジョブを置き換えてサーバーレス化してみるのはいかがでしょうか。

  • Java
  • Go
  • PowerShell
  • Node.js
  • C#
  • Python
  • Ruby

本記事では、環境構築手順の紹介は割愛させていただきます。 もし、環境構築作業がお済みでなければ公式ドキュメント等をご参照ください。

参考情報として筆者の環境構成を紹介しておきます。

$ sw_vers 
ProductName:    Mac OS X
ProductVersion: 10.14.4
BuildVersion:   18E226
$ aws --version 
aws-cli/1.16.75 Python/3.6.0 Darwin/18.5.0 botocore/1.12.65
$ python -V
Python 3.6.0
$ sls -v
1.39.1
$ npm -v
6.8.0
$ node -v
v10.10.0
$ pipenv --version
pipenv, version 2018.10.13

やってみた

毎日夕方の 18:00(JST)に実行されるバッチジョブを作成します。 serverless コマンドを利用して、Yaml や Python スクリプト(のテンプレート)を 生成しても構いませんが、今回はコード量も少ないためサンプルソース全文をご紹介します。

まずは、serverless.yml です。

次に Python スクリプトです。

上記は、serverless framework のテンプレートで生成される handler.py のコピーです。まずは、デプロイを行い Lambda コードが実行可能であることを確認しましょう。

$ sls plugin install -n serverless-python-requirements
$ sls deploy
:
Service Information
service: batch
stage: dev
region: ap-northeast-1
stack: batch-dev
resources: 7
api keys:
  None
endpoints:
  None
functions:
  notify: batch-dev-notify
layers:
  None
$ sls invoke -f notify -d '{"input":"event test"}'
{
    "statusCode": 200,
    "body": "{\"message\": \"Go Serverless v1.0! Your function executed successfully!\", \"input\": {\"input\": \"event test\"}}"
}

問題無さそうです。なお、先程のデプロイ時に Lambda 関数と合わせて CloudWatch イベントのルールが作成されています。

現状では、実用性に乏しいため少し実用性のあるコードに変更していきます。

変更点は、下記のとおりです。

  • Lambda 呼び出し時に JSON フォーマットの入力データを渡す
  • Lambda 関数で Secrets Manager に保存した credential 情報を利用できるように Role を付与
  • Secrets Manager に保存した Slack の Incoming Webhook URL を取得して、メッセージを POST

変更後の各ファイルは、以下のとおりです。

ポイントは、dev ステージとしてデプロイした際に Lambda 環境変数として SECRET_NAME に 'dev/slack-webhook-url' がセットされるところでしょうか。また、シークレット情報(この場合は、Slack の Webhook URL)を AWS Secrets Manager から取得するため、ソースコードに URL を埋め込む必要もありません。なお、input の id として指定する情報はメンバーID です。下記の手順を参考に、取得しておいてください。

次に Python スクリプトです。

Lambda の処理は、以下のとおりです。

  1. Lambda 環境変数としてセットされた SECRET_NAME(dev/slack-webhook-url)の情報を元に AWS Secrets Manager から Slack の Incoming Webhook URL を取得
  2. CloudWatch Events から呼び出される際に渡される JSON の情報(Slack のユーザーID)を取得
  3. create_message メソッドで Direct Message を作成
  4. Slack の Incoming Webhook へメッセージを POST

ソースコードの修正と合わせて、Slack の Webhook URL を取得し AWS Secrets Manager にシークレットを作成しておいてください。まずは、Slack の Webhook URL を取得するために Incoming Webhooks を作成します。予め、Slack ワークスペースにログインした状態で下記のページにアクセスし「設定を追加」ボタンをクリックします。

次にメッセージを投稿するチャンネルを選択し、「Incoming Webhook インテグレーションの追加」ボタンをクリックします。払い出された Webhook URL を、下記のように JSON の Value として指定し slack-creds.json といったファイル名で保存しておきます。

{
    "url": "https://hooks.slack.com/services/hogehoge/123456789"
}

AWS CLI でシークレットを作成します。

$ aws secretsmanager create-secret --name dev/slack-webhook-url --secret-string file://slack-creds.json
{
   "ARN": "arn:aws:secretsmanager:ap-northeast-1:...",
   "Name": "dev/slack-webhook-url",
   "VersionId": "hogehoge..."
}
$ rm -f slack-creds.json

シークレット作成後は、json ファイルを削除しておきます。詳細およびマネジメントコンソールでのシークレット作成手順については、下記の公式ドキュメントを参照ください。

なお、追加で boto3 や requests といった Python のライブラリを利用するように実装を変更したため、予め必要なライブラリをインストールしておきます。

$ pip install boto3 requests # 開発環境に既にライブラリがインストールされていれば不要です。
$ pipenv install boto3 requests

最後にデプロイを行い、CloudWatch Event ルールで指定した時間に Lambda が起動し Slack へ通知が届くか確認を行います。

$ sls deploy

通知されれば、成功です。

例えば、Cron 式 (0 0 1W * ? *) を設定することで月の 1番目の平日 9:00(JST)に通知することができると思いますし、 CloudWatch Event ルールの Cron 式や、Slack の通知メッセージをカスタマイズすることで、活用用途は広がると思います。

さいごに

実際に試していただくと実感できると思いますが、Serverless Framework を利用することで簡単に Lambda 関数を開発したりデプロイすることができます。また、シークレット情報を AWS Secrets Manager に保存して利用するパターンは(個人的には)王道パターンであると感じております。これからも、Serverless Framework を利用する際のちょっとした Tips を備忘録として、ブログで共有していきたいと思います。

ではでは