BFT名古屋 TECH BLOG

日々の業務で得た知識を所属するエンジニアたちがアウトプットしていきます。

【AWS・Slack・ワークフロー】障害対応にSlackのワークフローを使ってみた!

はじめに


こんにちは!
株式会社BFT名古屋支店・インフラ女子(?)のやまぐちです。

今まで名古屋支店ではGoogle Chatを情報共有の場として使っていたのですが、最近全社でSlackを使うようになり日々もっといい使い方はないかと試行錯誤をしています。
AWSで発生した障害の通知先もGoogle ChatからSlackに移行するにあたり、せっかくなら障害対応の情報共有を簡単にしたいと思い、Slackのワークフロー機能を使ってみました!

ちなみに作成する障害対応の情報共有とはあくまでも簡易的なものです。
今までは障害通知が来たらRedmineでチケットを発行し状況を記載していたので、その手間を省くべくボタンポチポチで確認結果を簡単に共有してみようと思います。

障害対応にSlackのワークフローを使ってみる


Slackのワークフロー機能って?

Slackのワークフローとは複数のステップで構成されたタスクを自動化する有料プランのツールです。
基本的には今流行りのノーコードなのでエンジニアでなくても実装は比較的簡単かと思います。

現在(2022/09)は以下のトリガーからワークフローを開始できます。

  • 添付ファイルとショートカットメニュー
    • メンバーの誰かが添付ファイルをアップしたり、ショートカットメニューを入力したりするとワークフローが始まる
  • チャンネルの新しいメンバー
    • チャンネルに新しいメンバーが追加されるとワークフローが始まる
  • 絵文字リアクション
    • チャンネルのメンバーがメッセージに特定の絵文字リアクションを追加するとワークフローが始まる
  • スケジュールされた日時
    • 設定した日時にこのワークフローが始まる
  • Webhook
    • 別のアプリやサービスからSlackにリクエストを送信するとワークフローが始まる

今回は上記のWebhookトリガーでワークフローを作成していきます。

Slackでワークフローを作る

記載する手順では以下の項目が前提条件です。

  • AWS側はCloudWatchのアラームが発生した際にSlackへ通知する仕組みを実装していること
  • Slackが無料プランではなく、有料のプロ、ビジネスプラス、Enterprise プランであること
  • すでにワークスペース内にワークフローを開始するチャンネルを作成していること

ワークフローはトリガーの設定も含めて

① Webhookで送信されるメッセージから変数を設定
② チャンネルにメッセージを投稿&対応する人へDMを送信
③ 一次確認結果をリストから選択してスレッドに投稿

という3つのステップで構成しています。

STEP1:Webhookで送信されるメッセージから変数を設定
  1. Slack上部にあるチャンネル名をクリックし、[ インテグレーション ] タブの [ ワークフローを追加する ] を押します。

  2. 起動したワークフロービルダーで [ 作成 ] を押しワークフロー名を入力して [ 次へ ] を押します。

  3. ワークフローを開始する方法として Webhookの [ 選択する ] を押します。

  4. 飛んできたエラーの内容をチャンネルに投稿したいので、[ 変数を追加する ] を押します。

  5. ここで追加する変数は "text" です。これはAWSから送信される内容が "text" で飛んでくるためです。データタイプをテキストのままにして [ 次へ ] を押します。

STEP2:チャンネルにメッセージを投稿&対応する人へDMを送信
  1. [ ステップを追加 ] を押して次のステップを作成します。

  2. 次にメッセージを送信の [ 追加 ] を押します。

  3. メッセージの送信先としてアラートを通知するチャンネルを入力し、メッセージを作成します。変数は右下の [ 変数を挿入する ] から選択してメッセージ内に含めてください。また、[ ボタンを含める ] にチェックをすることで以後のステップでそのボタンを押したユーザに対してDMを送るなどの操作ができます。[ 保存する ] を押して2ステップ目を終了します。

STEP3:一次確認結果をリストから選択してスレッドに投稿
  1. [ ステップを追加 ] を押して最後のステップを作成します。

  2. 今度はフォームを送信の [ 追加 ] を押します。

  3. このフォームの送信先に [ スレッドにメッセージを投稿する ] を選択、タイトルや質問を入力します。今回は定型文から選択したかったので質問のタイプを [ 一覧から選択 ] にし、選択肢を3つ作成しました。最後にスレッドにもメッセージを投稿するようにチェック&選択して [ 保存する ] を押します。

  4. これで準備完了です。右上の [ 公開する ] を押します。

  5. 表示された画面でWebhookをセットアップの下のURLをコピーします。このコピーされたURL宛てにAWSからメッセージを送信します。

AWS側のLambdaにWebhookのURLを設定する

AWSでCloudWatchアラームからSlackへ通知する仕組みは以下の記事を参照ください。記事はログイン通知の話ですが構成は同じでLambdaのコードが異なるだけです。

bftnagoya.hateblo.jp

Webhook用のURLは環境変数で設定するようにLambdaを記載しています。リソース監視とログ監視用のLambdaを作成していますがこちらはログ監視用のコードです。

import boto3
import json
import logging
import os
import datetime
import calendar

from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# Slack hook URL
HOOK_URL = os.environ['HOOKURL']
# Slack Channel Name
SLACK_CHANNEL = os.environ['CHANNEL']
# 抽出するログデータの最大件数
OUTPUT_LIMIT = 5
# 何分前までを抽出対象期間とするか
TIME_FROM_MIN = 10

def lambda_handler(event, context):
    logger.info("Event: " + str(event))
    message = json.loads(event['Records'][0]['Sns']['Message'])

    logs = boto3.client('logs')

    # MetricNameとNamespaceをキーにメトリクスフィルタの情報を取得する。
    metricfilters = logs.describe_metric_filters(
        metricName = message['Trigger']['MetricName'] ,
        metricNamespace = message['Trigger']['Namespace']
    )

    #ログストリームの抽出対象時刻をUNIXタイムに変換(取得期間は TIME_FROM_MIN 分前以降)
    #終了時刻はアラーム発生時刻の1分後
    timeto = datetime.datetime.strptime(message['StateChangeTime'][:19] ,'%Y-%m-%dT%H:%M:%S') + datetime.timedelta(minutes=1)
    u_to = calendar.timegm(timeto.utctimetuple()) * 1000
    #開始時刻は終了時刻のTIME_FROM_MIN分前
    timefrom = timeto - datetime.timedelta(minutes=TIME_FROM_MIN)
    u_from = calendar.timegm(timefrom.utctimetuple()) * 1000

    # ログストリームからログデータを取得
    response = logs.filter_log_events(
        logGroupName = metricfilters['metricFilters'][0]['logGroupName'] ,
        filterPattern = metricfilters['metricFilters'][0]['filterPattern'],
        startTime = u_from,
        endTime = u_to,
        limit = OUTPUT_LIMIT
    )
    
    # メッセージを整形
    postText = ''
    for e in response['events']:
        text = '''
        {logStreamName}
        {message}
        '''.format( logStreamName=str(e['logStreamName']),
                    message=str(e['message'])).lstrip()
        postText += text

    # Slackの形式にする
    msg = {
        "channel": SLACK_CHANNEL,
        "username": "AWS CondorU",
        "text": postText,
    }
    
    # メッセージ送信
    req = Request(HOOK_URL, json.dumps(msg).encode('utf-8'))
    try:
        response = urlopen(req)
        response.read()
        logger.info("Message posted to %s", msg['channel'])
    except HTTPError as e:
        logger.error("Request failed: %d %s", e.code, e.reason)
    except URLError as e:
        logger.error("Server connection failed: %s", e.reason)

アラームを発生させてワークフローを開始する

CloudWatchアラームが上がるようにすると…
設定したチャンネルにメッセージが投稿されました!
私がテスト用に監視しているログに出力した「ERROR TEST そろそろ眠い」という文字列も問題なくメッセージに埋め込まれています。

[ 対応します ] ボタンを押すとボタンを押した人の名前が表示されます。

次にSlackBotから送信されるDMの [ フォームを開く ] を押します。
設定したフォームが開くので確認結果をリストから選択して [ Submit ] を押します。

スレッドにフォームの内容が投稿されました。

終わりに


Slackはアプリを追加してやりたいことをいろいろ実装できる、まさにエンジニア好みのツールですね!
今回はWebhookを契機にワークフローを開始してみましたが、それ以外での契機やステップでタスクを作成するなどいろいろ試してみたいと思います。

以上、ここまで読んでいただきありがとうございました~ ^ ^