はじめに
AWS Batch記事第2弾。第1弾はこちら。
AWS Batchは標準のメトリクスでエラーをモニタリングすることが難しいので、今回はEventBridgeを使って外部に通知を行うことでモニタリング可能にする。今回はAPI Destinationsを使ってSlackに通知を行う。
前提として、本記事では、第1弾で作成した構成にAWSリソースを付け加えていくので、未読の型は第1弾から読んでいただきたい。
API Destinationsの事前準備
API Destnationsの前準備として、Slack側の設定とIAMロールの設定が必要になる。
Slack側の設定
以前の別の記事でも書いたように、クラスメソッド先生の記事を参考に、Slackのボットを作っておいて、OAuth用のトークンを取得しておこう。
OAuth用トークンはvar.slack_api_keyで指定できるようにしておく。
variable "slack_api_key" {
type = string
}
IAMロール
IAMロールは以下のように設定する。
API Destinationsのロール設計におけるハマりどころ
API DestinationsではAPIキーの管理をする際に自動的にSecrets Managerを使うため、関連した権限を付与しておく必要がある。また、イベント処理の実行のためにevents:InvokeApiDestination
の権限を付与しておく。
resource "aws_iam_role" "eventbridge" {
name = local.iam_eventbridge_role_name
assume_role_policy = data.aws_iam_policy_document.eventbridge_assume.json
}
data "aws_iam_policy_document" "eventbridge_assume" {
statement {
effect = "Allow"
actions = [
"sts:AssumeRole",
]
principals {
type = "Service"
identifiers = [
"events.amazonaws.com",
]
}
}
}
resource "aws_iam_role_policy" "eventbridge" {
role = aws_iam_role.eventbridge.id
name = local.iam_eventbridge_policy_name
policy = data.aws_iam_policy_document.eventbridge_custom.json
}
data "aws_iam_policy_document" "eventbridge_custom" {
statement {
effect = "Allow"
actions = [
"secretsmanager:CreateSecret",
"secretsmanager:UpdateSecret",
"secretsmanager:DescribeSecret",
"secretsmanager:DeleteSecret",
"secretsmanager:GetSecretValue",
"secretsmanager:PutSecretValue"
]
resources = [
"*",
]
}
statement {
effect = "Allow"
actions = [
"events:InvokeApiDestination",
]
resources = [
aws_cloudwatch_event_api_destination.batch_failed.arn,
]
}
}
API Destinationsの設定
イベントルール
AWS Batchの状態変更時におけるイベント通知は、公式のユーザーガイドのチュートリアルに載っているので参考にする。これで、"FAILED"
に遷移した際のイベントが拾える。
resource "aws_cloudwatch_event_rule" "batch_failed" {
name = local.eventbridge_batch_failed_rule_name
description = "AWS Batch failed event capture rule."
is_enabled = true
event_pattern = jsonencode({
detail-type = [
"Batch Job State Change"
]
source = [
"aws.batch"
]
detail = {
status = [
"FAILED"
]
}
})
}
イベントターゲット
イベントターゲットでAPI Destinationsを設定する。
なお、Slackに通知する際に何のジョブがエラーになったか分からないと困るので、ジョブ名を設定するために入力トランスフォーマー(input_transformer
)でイベント通知されたJSONを変換する。input_paths
で置換したい要素をJSONから取り出し変数名として定義し、input_template
で"<>"で変数名を囲んで利用する。詳細は公式のEventBridgeのユーザーガイドを参照してほしい。また、イベント通知のJSONの内容は公式のAWS Batchのユーザーガイドを参照。
ここで、変なreplaceを使っているのは、jsonencode
関数は"<"と">"をエスケープ対象として扱って置換してしまうためである。そのままの文字でSlack通知する場合はエスケープするのが正しいが、今回はあくまでもSlack通知前にAWS側で変数の置換をさせたいため、エスケープをしないのが正しい。
リトライ設計を忘れずに
外部のSaaSではRate Limitにかかる可能性もあるので、retry_policy
を適切に設定しよう。今回のケースでは、イベント保存期間(maximum_event_age_in_seconds
)とリトライ回数(maximum_retry_attempts
)を設定可能な最大値に設定する。
resource "aws_cloudwatch_event_target" "batch_failed" {
rule = aws_cloudwatch_event_rule.batch_failed.name
arn = aws_cloudwatch_event_api_destination.batch_failed.arn
role_arn = aws_iam_role.eventbridge.arn
retry_policy {
maximum_event_age_in_seconds = 86400
maximum_retry_attempts = 185
}
input_transformer {
input_paths = {
jobname = "$.detail.jobName"
}
input_template = replace(replace(jsonencode({
channel = var.slack_channel_id,
username = "AWSモニタリング",
icon_emoji = ":fire:",
text = "バッチ処理が失敗しました。処理名: <jobname>"
}), "\\u003c", "<"), "\\u003e", ">")
}
}
API Destinations
API Destinationsの設定はたいして難しいことはない。
払い出したAPIキーをaws_cloudwatch_event_connection
でAuthorization
ヘッダに設定しておこう。
resource "aws_cloudwatch_event_api_destination" "batch_failed" {
name = "slack"
invocation_endpoint = "https://slack.com/api/chat.postMessage"
http_method = "POST"
invocation_rate_limit_per_second = 1
connection_arn = aws_cloudwatch_event_connection.batch_failed.arn
}
resource "aws_cloudwatch_event_connection" "batch_failed" {
name = "slack-connection"
authorization_type = "API_KEY"
auth_parameters {
api_key {
key = "Authorization"
value = "Bearer ${var.slack_api_key}"
}
}
}
いざ、実行!
さて、これでジョブを意図的にテキトーに失敗させてみると、以下のような通知がSlackに上がってくる。
成功だ!これでジョブの失敗をしても即座に気付くことができる!
しかし、これではいつ開始したジョブが失敗したのかが分からない。
ジョブの開始日時は、$.detail.createdAt
で設定されてくるのだが、入力トランスフォーマーでこれを変換する手段がない。ちょっとイケてないんだよな……。