EventBridgeを使ってAWS Batchのステータス変化をSlack通知してみた

こんにちは、ライクル事業部の石崎です。
ライクルではバッチ処理に主にAWS Batchを使用しています。 バッチの開始終了時にSlackに通知するようにしており、現状はバッチの処理に混ざって通知処理が書かれています。 通知部分だけインフラ側に任せたいと思い、EventBridgeを使った二通りの通知方法を試してみました。
既存のインフラはCDKで管理しているため、今回もCDKを使っていきます。

Batchのステータス変化の通知を行いますが、別の通知をする際にもEventBridgeは使えるので参考になればと思います。

先にざっくりまとめ

  • Chatbotは簡単だけどメッセージがカスタマイズできず、何のjobがどんなステータスになったのかわからない。
  • EventBridge API Destinationはある程度メッセージをカスタマイズできるけど、CDKv2.10.0ではCloudFormationと書き心地はあまり変わらない。
  • もっと柔軟にしたかったらLambdaを使うのが良さそう。

今回作成したサンプルはこちら。
GitHub - ishizakit/sample-cdk-notice-batch-status-changed

Event Bridgeとは

Event BridgeとはAWSのサービスの各種イベントをLambdaなどのAWSサービスに送信できるサービスです。

AWS Chatbotを使った通知

まずはAWS Chatbotを使ってSlack通知する方法の紹介です。 ChatbotとはSNSのトピックをトリガーにSlackやAmazon Chimeに簡単に通知ができるAWSのサービスです。

Chatbotの概要ページにあるこちらの図だと、今回はPublisherがAWS Batch、Chat Room NotificationsがSlackへの通知になります。 https://d1.awsstatic.com/developer-tools/Product-Page-Diagram_AWS-Chatbot_How-It-Works_v2.88e5f0ea07c7294d52373b34b5fe7a5a0c449407.png

コードの説明

CDKのコードを順に説明します。 実際に使ったサンプルはこちらにあります。 sample-cdk-notice-batch-status-changed/sample-cdk-aws-chatbot-stack.ts at main · ishizakit/sample-cdk-notice-batch-status-changed · GitHub

Topicの作成

AWS Batchのイベントを受け取るSNSのTopicを作成します。

const topic = new sns.Topic(this, "sns-topic", {
  topicName: "batch-status-changed",
});

ChatbotのSlack設定の作成

Chatbotがメッセージを送信するSlackのワークスペースとチェンネルを設定します。 slackWorkspaceIdslackChannelId はSlackをブラウザで開いたときのURLから取得できます。

new chatbot.SlackChannelConfiguration(this, "chatbot", {
  slackChannelConfigurationName: "batch-status-changed",
  slackWorkspaceId: process.env.SLACK_WORKSPACE_ID || "",
  slackChannelId: process.env.SLACK_CHANNEL_ID || "",
  notificationTopics: [topic],
});

EventRuleの作成

何のサービスのどんなイベントのときにどこにイベントを送信するのか設定します。

new events.Rule(this, "events-rule", {
  ruleName: "batch-status-changed-to-sns",
  targets: [new targets.SnsTopic(topic)],
  eventPattern: {
    source: ["aws.batch"],
    detailType: ["Batch Job State Change"],
    detail: {
      status: ["RUNNING", "SUCCEEDED", "FAILED"],
    },
  },
});

eventPatternはサービスやイベントの種類によって代わります。 なので今回とは別のイベントを扱いたい場合は、最初はCDKで作らずにAWSコンソールの Amazon EventBridge > ルール > ルール作成 を使うのがおすすめです 画像のようにサービス名やイベントの種類を選択すればイベントパターンが生成されるので、それを参考にCDKの設定ができます。 またこの画面ではサンプルイベントを使ってイベントパターンが該当するかテストすることもできます。

f:id:so-technologies:20220131184204p:plain
イベントパターン定義画面

Slackのメッセージ

Chatbotで送信するとこのようにステータスが変わったということ、変わったjobのIDしか載っていません。 このメッセージだとステータスが何に変わったのかわからず、jobのIDにはjobの名前が含まれていないため、通知を見ただけだと何が失敗したのかもわかりません。

f:id:so-technologies:20220131175836p:plain
Chatbotで送信したメッセージ

Q: AWS Chatbot 通知にカスタムフォーマットを追加できますか? いいえ。AWS Chatbot の通知のフォーマットをカスタマイズすることはできません。

よくある質問 にある通り、フォーマットのカスタマイズができないため、ChatbotでBatchのステータスを通知するのは向いていなさそうです。

EventBridge API Destinationを使った通知

EventBridge API DestinationはEventBridgeのイベントをAWSサービスに送る代わりにHTTPリクエストにできる機能です。 この機能とSlackAPIを使用してイベントの内容をSlackに通知をします。

コードの説明

CDKのコードを順に説明します。 実際に使ったサンプルはこちらにあります。 sample-cdk-notice-batch-status-changed/sample-cdk-api-destination-stack.ts at main · ishizakit/sample-cdk-notice-batch-status-changed · GitHub

CDKv2.10.0ではEventBridge API DestinationはL1でのサポートとなっているため、大部分がCloudFormationとあまり変わらない書き心地になっています。 文字の大文字小文字の違いでエラーになる罠があるため注意が必要です。

こちらの記事を参考に作成しました。 blog.msysh.me

ロールの作成

EventBridgeがHTTPリクエストを送るためのロールを作成します。

const eventRole = new iam.Role(this, "role", {
  roleName: "eventsInvokeApiRole",
  assumedBy: new iam.ServicePrincipal("events.amazonaws.com"),
});
eventRole.addToPolicy(
  new iam.PolicyStatement({
    resources: ["*"],
    actions: ["events:InvokeApiDestination"],
  })
);

接続先の作成

EventBridgeの接続先(認証方法や鍵の設定)を作成します。 今回はWebhookを使用しますが認証の設定は必須なため適当なものにしてあります。 authParameters以下のプロパティはアッパーキャメルケースじゃないと動作しないため注意が必要です。

const connection = new events.CfnConnection(this, "events-connection", {
  name: "no-auth",
  authorizationType: "API_KEY",
  authParameters: {
    // アッパーキャメルケースじゃないとエラーになる
    ApiKeyAuthParameters: {
      ApiKeyName: "Authorization",
      ApiKeyValue: "hoge",
    },
  },
});

送信先の作成

エンドポイントのURLとHTTPメソッドを指定して送信先を作成します。

const destination = new events.CfnApiDestination(
  this,
  "events-destination",
  {
    name: "slack-webhook-destination",
    connectionArn: connection.attrArn,
    httpMethod: "POST",
    invocationEndpoint: process.env.SLACK_WEBHOOK_URL || "",
  }
);

EventRuleの作成

inputTransformerを使ってBatchのイベントの値を引用したHTTPリクエストのボディを作ります。 eventPatternのプロパティ名はケバブケースでないとイベントをキャッチできないので注意が必要です。

new events.CfnRule(this, "events-rule", {
  name: "batch-status-changed-to-slack",
  targets: [
    {
      id: "slack-destination",
      arn: destination.attrArn,
      roleArn: eventRole.roleArn,
      inputTransformer: {
        inputPathsMap: {
          name: "$.detail.jobName",
          status: "$.detail.status",
        },
        inputTemplate: `
        {
          "channel": "${process.env.SLACK_CHANNEL_NAME}",
          "username": "${process.env.SLACK_BOT_NAME}",
          "icon_url": "${process.env.SLACK_BOT_ICON}",
          "attachments": [
            {
              "fallback": "<name>のステータスが変わりました",
              "pretext": "<name>のステータスが変わりました",
              "color": "#00DD00",
              "fields": [
                {
                  "title": "ジョブ名 : <name>",
                  "value": "ステータス : <status>"
                }
              ]
            }
          ]
        }`,
      },
    },
  ],
  eventPattern: {
    // ケバブケースじゃないと動かない
    source: ["aws.batch"],
    "detail-type": ["Batch Job State Change"],
    detail: {
      status: ["RUNNING", "SUCCEEDED", "FAILED"],
    },
  },
});

Slackのメッセージ

EventBridge API Destinationだとメッセージのカスタマイズができるため、失敗したジョブの名前やステータスをメッセージ内に記載できます。 ステータスやジョブよって色を変えるような分岐処理はできないため、やりたい場合はメッセージの種類だけEventRuleを作成する必要があります。 分岐処理が複雑になるようであればLambdaを使うのが良さそうです。

f:id:so-technologies:20220131175739p:plain
EventBridge API Destinationで送信したメッセージ

まとめ

今回はChatbotとEventBridge API Destinationの2種類の通知方法を試して違いについてお話しました。 ログを監視してバッチの開始終了を通知するなど他にもいろいろな方法があると思うので、機会があれば他の方法も試してみたいと思います。