0
0

More than 1 year has passed since last update.

AWS Batch on Fargateのサーバレスなバッチ実行環境をTerraformで自動構築する(モニタリング編)

Posted at

はじめに

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_connectionAuthorizationヘッダに設定しておこう。

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に上がってくる。
キャプチャ3.png
成功だ!これでジョブの失敗をしても即座に気付くことができる!

しかし、これではいつ開始したジョブが失敗したのかが分からない。
ジョブの開始日時は、$.detail.createdAtで設定されてくるのだが、入力トランスフォーマーでこれを変換する手段がない。ちょっとイケてないんだよな……。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0