ユーザ数などを cron などで定期的に Slack に POST していたが、変化が無い時に通知されてもあまり意味がないので、変化したときだけ Slack に通知する、というのを Serverless で作ってみた。
出来ること
監視する任意の値に変化があった時に Slack に通知してくれる。
もう少し詳細に
監視する値を API に POST し続け、前回の値から変化があったら Slack に通知するというシンプルなもの。例えば、
- ファイルの
md5
を1時間おきに取って API に投げれば、ファイル内容に変更があったら分かる。 - サービスの登録人数の count を1時間おきに取れば、変化があったときに分かる。
- クローリングしているデータの最終更新日時の文字列を15分おきに投げれば、変化があった時に分かる。
など、 trivial なデータを気軽に監視できるようにする。というのがこのシステムの主な目的。
ちゃんと作ったシステムはデータ解析のための仕組みがちゃんとあるだろうけど、数時間で作った自分用アプリとかだとそういうのまで作らないので、こういうのがあると便利だなと思って作った。
Getting started
Web インターフェイスを作ったので、これを使えば監視が始められる。値を入力して作成したら、 curl
のサンプルコードが出てくるので、値のとり方だけ自分が取りたいものに合わせて、 crontab とかに登録しておけばOKだと思います。
API Gateway + Lambda + DynamoDB で作ったので、サービスが落ちる心配もないと思います。
GitHub
API
監視するリソースはなんでも良いので、 Resource というあまりに抽象的な名前しか思いつかなかったがとりあえずこれで。
- POST /resources
- PUT /resources/:uuid
POST で監視対象のリソースを生成し、以後はそこで発行された uuid に対して PUT をしていくようにする。 POST するときに Slack の Webhook の情報 (URL, Channel, Template) を付けておいて、もし変更があったらまた POST して作ればいいやという方針にした。
これは serverless.yml
に書くだけで済む。
POST の方を、createResource
という function名にして、PUT の方を updateResource
という function名にして、 URL から uuid という名前でパラーメータとして受け取るようにした。
Serverless で、 integration
を指定しない時のデフォルトが、以前は lambda
だったのだが、最近デフォルトが lambda-proxy
になってた。 API Gateway のマッピングが変わるので、 Lambda 側で値の取り方が変わる。 lambda-proxy
にすると、 path が、 pathParameters という名前になってた。
functions:
create:
handler: handler.createResource
memorySize: 128
integration: lambda
events:
- http:
path: resources
method: post
update:
handler: handler.updateResource
memorySize: 128
integration: lambda
events:
- http:
path: resources/{uuid}
method: put
request:
parameters:
paths:
uuid: true
DB
値は DynamoDB に貯めておく。 POST 時に初期値を設定出来て、以後は value
のみを PUT していく。変更があった時は Slack に通知して、 DynamoDB の値を書き換える、というシンプルなもの。これなら書き込み&読み込みのキャパシティー両方1で良いので、1秒間に1回!以内のペースであれば月額$0.67で済む(大事)
Key-Value Store としてしか使わないので、AWS Console からテーブルを作った。 uuid を文字列でキーとしてみた。
IAMの設定は serverless.yml
に書くだけでOK。今回は、 PutItem, GetItem, UpdateItem のみを許可したいので、下記のような感じにした。
iamRoleStatements:
- Effect: "Allow"
Action:
- "dynamodb:PutItem"
- "dynamodb:GetItem"
- "dynamodb:UpdateItem"
Resource:
- "arn:aws:dynamodb:ap-northeast-1:1234567890:table/your-awesome-table-name"
Domain
API Gateway のカスタムドメインが未だによく分かっておらず、内部的には CloudFront の裏にいるのに、 CloudFront のようにシームレスに証明書をつけられなくてめんどくさい。Let’s Encypt とかでTLS証明書を自分で作って上げてみてもよいけれど、楽をするために前に CloudFront を立てることにする。なので、おそらく AWS 全体を見ると、 自分で立てたCloudFront
-> APIGatewayの内部で使われているCloudFront
-> APIGatewayの某かの経路
-> AWSLambda
となっており、電気が無駄になっていてエコではなさそう。
API Gateway の前に CloudFront を置く時の注意点としては、動的なデータを扱いたい場合、
- キャッシュの TTL を 0 にする
- 必要なヘッダーがあったら転送する(
Content-Type
を転送しようと思ったが、JSONしかないのでapplication/json
でオーバーライドした) - 必要に応じて、GET, OPTION 以外のメソッドも許可(詳細に選べないので、今回は全部になった)
- API Gateway 側に stage名がドメイン直下につくので、 CloudFront の Origin Path をそこに設定する。例えば
/production
とか。
CloudFront が出来たら、Route53 で Alias として設定すれば完了する。
Serverless
設定をしたら後は特に変わったことはしていない。
GitHub に設定諸々のコードを上げた。 value-changing-monitor
最近ローカルでの開発は、 node-lambda を使ってやっていたが、シンプルなものなら Serverless で作ってデプロイして確認という方法も楽で良いかもしれない。
Web interface
curl で POST するのは面倒だったので、 mithril を使って Web interface を作ってみた。これも特に変わったことはしておらず特に面白くはない。