CloudWatch Syntheticsを時刻指定で実行してみた
CloudWatch Synthetics
を、 AWS Lambda
から時刻指定して実行するようにしました。
今回は、その背景と実行方法や注意事項についてご紹介いたします。
背景
弊社では、コーポレートサイトのお問い合わせページが正常に機能しているかという確認を、 Amazon CloudWatch Synthetics
を使って行っています。
Amazon CloudWatch Synthetics
はREST API、URL、ウェブサイトコンテンツを監視するためのサービスです。
2021年2月20日現在、 CloudWatch Synthetics
のスケジュール設定画面には、下記のようになっており、「1回実行」「継続的に実行」の2つしか選べません。
「継続的に実行」の設定値としては最大で 60
までしか入力できないため、例えば「1日に1度だけ実行したい」場合や「XX月XX日の10時に実行したい」などの細かい設定は行なえません。
そのため、これまで弊社では実行タイミングを1時間に1度に(設定値を 60 に設定)した上で、実行された時間が9時台だったら処理を行うが、それ以外だったら何も行わない(処理を抜けてしまう)という実装にして、1日に1回の動作確認を行っていました。
【9時台にだけ実行する場合の実装例】
// only 09:00 JST
const now = new Date()
if (now.getHours() !== 0) return;
---その後の処理略---
この実装で、数カ月間運用してきたものの、なぜか徐々に実行時間がずれてきました。
【運用開始直後(2020/10/26)】
【最近(2021/02/13)】
画像をご覧いただけるとおわかりかと思いますが、運用開始直後は 09:01
に終わっている処理が、最近では 09:36
になっています。
こんな状況だったので、「あれ?今日は動作確認の通知が来てないな」と思った数分後に来ることがあったり、実際のお客様からのお問い合わせが来た!と思ったら動作確認の通知だった、ということがありました。
この状況を解消すべく、 CloudWatch Synthetics
を実行するだけの AWS Lambda
を作り、それを時刻指定で実行するように変更しました。
AWS Lambda
はこういった「決まった時間」や、「S3にファイルが置かれたというようなイベント」をトリガーに、ちょっとした関数を安価に実行したいという場合に最適です。
実行方法
Lambda 関数作成
Lambda 関数を作成します。
Lambda の関数としては主に synthetics の StartCanary を実行するだけの処理になっています。
package main
import (
"log"
"os"
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/synthetics"
)
const region = "ap-northeast-1"
func main() {
lambda.Start(startSynthetics)
}
func startSynthetics() error {
name := os.Getenv("CANARY_NAME")
sess, err := session.NewSession(&aws.Config{Region: aws.String(region)})
if err != nil {
log.Fatal(err)
}
svc := synthetics.New(sess)
input := &synthetics.StartCanaryInput{
Name: aws.String(name),
}
_, err = svc.StartCanary(input)
if err != nil {
log.Fatal(err)
}
return nil
}
汎用的に使えるように、Canary名を CANARY_NAME
の環境変数から取得するようにしました。
serverless framework の設定
先程作成した関数を、 serverless framework を使ってデプロイします。
serverless framework
の設定ファイル serverless.yml
を下記のように設定します。
service: synthetics-start
frameworkVersion: '2.23.0'
provider:
name: aws
runtime: go1.x
stage: prd
region: ap-northeast-1
timeout: 300
memorySize: 128
iamManagedPolicies:
- "arn:aws:iam::aws:policy/CloudWatchSyntheticsFullAccess"
iamRoleStatements:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "arn:aws:logs:*:*:*"
environment:
CANARY_NAME: ${env:CANARY_NAME}
package:
exclude:
- ./**
include:
- ./bin/**
functions:
syntheticsstart:
handler: bin/app
events:
- schedule: cron(0 0 * * ? *)
ポイントとしては、 iamManagedPolicies
に CloudWatchSyntheticsFullAccess
を付与することです。
これがないと、Lambda から CloudWatch Synthetics
が実行できません。
iamManagedPolicies:
- "arn:aws:iam::aws:policy/CloudWatchSyntheticsFullAccess"
CloudWatchSyntheticsFullAccess
を付与することに抵抗がある場合は、下記のページを参考により厳密な権限を設定してみて下さい。
毎日、日本時間の9時に実行したい場合は下記のようにcron設定します。
events:
- schedule: cron(0 0 * * ? *)
あとは、デプロイすれば終わり、なのですが、最後に1つだけ注意事項があります。
CloudWatch Synthetics
の Canary
の設定が「継続的に実行」になっている状態だと、Lambda から Canary
を呼び出した時に、下記のようなエラーが出てしまいます。
ConflictException: Canary is not in a startable state.
これを回避するためには、スケジュールを「1回実行」に変更しておかなければならないので、忘れずに変更しておきましょう。
まとめ
以上、 CloudWatch Synthetics
を時刻指定で実行してみた背景と実行方法や注意事項でした。
今回使用したファイルや、実行環境については、 GitHub にあげてあるので、もしご興味があればご覧ください。