Lambda 및 Amazon Connect에서 RDS 장애 시 Slack 알림 및 전화 연락

개요


  • RDS로 장애가 일어났을 때 Slack에 통지하는 동시에 전화도 걸고 싶었기 때문에 Lambda와 Connect를 사용해 구현했습니다.
  • 단순히 전화를 걸 뿐만 아니라, 복수인에게 전화를 해 1명째가 대응 가능하면 다른 사람에게는 전화하지 않는다는 것도 실현하고 싶었기 때문에 그것도 실장했습니다.
  • 실제로 실장해 보고 신경이 쓰이는 세세한 점(후술)은 있습니다만, 그것은 서서히 개선해 나가려고 생각하고 있습니다.

  • 처리 흐름


  • RDS 이벤트에서 트리거
  • SNS가 수신하여 Slack 알림 용 Lambda (Lambda1) 및 전화 연락 용 Lambda (Lambda2)를 호출합니다.
  • Slack 알림 용 Lambda는 RDS 이벤트의 내용 (어떤 RDS에서 어떤 장애인지)을 Slack에 통지
  • 전화 연락 Lambda는 Connect를 호출하여 전화를 겁니다.
  • 누가 대응 가능한지 판정도 실시해 Slack에 통지





  • 전제


  • RDS 이벤트, SNS 및 Connect 설정과 같은 설정은 여기에 설명되지 않습니다.
  • Lambda 또는 Connect 실행 권한을 적절하게 부여하십시오.
  • Lambda의 코드가 망설이는 부분이 있을까 생각합니다만 용서해 주세요.

  • 설정 흐름


  • Connect 설정(생략)
  • Connect 쿼리 흐름 설정
  • Lambda 만들기
  • SNS 생성(생략)
  • RDS 이벤트 설정 (생략)

  • Connect 문의 흐름


  • 아래와 같이 만들었습니다.
  • ログ記録動作の設定 로그를보고 싶었기 때문에 활성화
  • 音声の設定 는 적당하게(뒤에서는 Polly 사용하고 있는 것 같네요)
  • プロンプトの再生에서 Lambda에서 호출 될 때 전달되는 텍스트를 읽도록 설정
  • 顧客の入力を取得する에서 번호를 입력하고 해당하는 경우 1, 해당하지 않으면 2로 분기하도록 설정
  • 問い合わせ属性の設定로 대응할지 어떨지를 변수로서 갖게한다
  • 나중에 Lambda에서이 속성을 얻습니다.




  • 람다


  • 전화 연락 용 Lambda 만 설명합니다.
  • 로깅 설정을 적절하게 수행하는 것이 좋습니다.
  • 파이썬으로 작성했습니다.
  • import os
    import json
    import logging
    import boto3
    from datetime import datetime, timedelta
    from urllib.request import Request, urlopen
    from urllib.error import URLError, HTTPError
    
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    
    # Lambdaの環境変数からConnectで取得した発信元電話番号とインスタンスIDと問い合わせフローIDを取得
    SOURCE_PHONE_NUMBER = os.getenv('SOURCE_PHONE_NUMBER')
    INSTANCE_ID = os.getenv('INSTANCE_ID')
    CONTACT_FLOW_ID = os.getenv('CONTACT_FLOW_ID')
    
    # Lambdaの環境変数からSlack通知用のWebHookURL取得
    Slack_Webhook_URL = os.environ['SLACK_WEBHOOK_URL']
    
    # Lambdaの環境変数から電話を掛ける相手の番号と名前を取得
    destination_phone_number = [os.getenv('DESTINATION_NUMBER_1'),os.getenv('DESTINATION_NUMBER_2'),os.getenv('DESTINATION_NUMBER_3')]
    person_name = [os.getenv('NAME_1'),os.getenv('NAME_2'),os.getenv('NAME_3')]
    
    def lambda_handler(event, context):
        # SNSから取得されるRDSイベントの内容
        rds_event_message = json.loads(event['Records'][0]['Sns']['Message'])
    
        # RDSイベントのメッセージをセット
        target = (rds_event_message["Source ID"])
        event_message = (rds_event_message["Event Message"])
        tel_message = "データベースで障害です。" + "対象のデータベースは " + target + "で、" + "障害内容は " + event_message + "です。"
    
        # 誰も対応しないということを初期設定    
        isCorrespond = "false"
    
        # ここからConnectの処理
        connect = boto3.client('connect' , region_name='ap-northeast-1')
    
        for number,name in zip(destination_phone_number,person_name):
            # connectで電話を掛ける
            contact = connect.start_outbound_voice_contact(
                DestinationPhoneNumber=number,
                ContactFlowId=CONTACT_FLOW_ID,
                InstanceId=INSTANCE_ID,
                SourcePhoneNumber=SOURCE_PHONE_NUMBER,
                Attributes={
                    'message': tel_message ,
                    'isCorrespond': 'false'  # ここはもしかしたら不要かもしれません
                }
            )
    
            # 電話を掛け始めて一旦待つ
            time.sleep(60)
    
            # Connectの結果を取得
            contact_id = contact['ContactId']
            attributes = connect.get_contact_attributes(
                InstanceId=INSTANCE_ID,
                InitialContactId=contact_id)
    
            # 対応者がいるかどうかの結果をセット
            isCorrespond = attributes['Attributes']['isCorrespond']
    
            if (isCorrespond == "true"):
                # 対応者あり
                slack_post_correspond("[対応者]" + name)
                break;
            else:
                # 対応者がいない
                continue;
    
        # 全員に電話を掛けても対応者不在
        if(isCorrespond == "false"):
            slack_post_correspond("対応者不在")
    
    # Slackに対応者情報投稿
    def slack_post_correspond(message):
        slack_message = {
            'text': "*" + message + "*",
        }
    
        req = Request(Slack_Webhook_URL, json.dumps(slack_message).encode('utf-8'))
        try:
            response = urlopen(req)
            response.read()
            logger.info("Message posted to %s", Slack_Webhook_URL)
            return {
                'statusCode': 200
            }
        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)
    

    신경이 쓰이는 세세한 점


  • 전화 연락처의 번호나 이름을 설정하는 부분이 이마이치한 느낌은 있습니다만, 자주 변동하는 것은 아니기 때문에 좋다고 하고 있습니다. 이상은 DB에서 얻고 싶은 곳입니다.
  • Connect로 전화를 걸는 처리의 호출은 sleep에서 강제적으로 기다리고 있으므로, Connect측에서 무언가 좋은 느낌으로 제어 할 수 있는 것이 있으면 좋다고 생각하고 있습니다.
  • RDS에서 복수 이벤트가 발생했을 때 복수 전화가 걸려 버려 (Lambda가 복수 실행된다), 와챠와챠해 버리므로 개선하고 싶은 곳입니다.

  • 소감


  • 여러가지 개선하고 싶다고 느끼는 곳은 있었습니다만, Connect를 사용해 장해시의 전화 연락을 실장 할 수 있어서 좋았습니다.
  • 이것의 응용을 할 수 있으면 EC2 장해시의 1차 대응이 자동화 할 수 있을 것 같다고 느끼고 있습니다.
  • Connect 대단해!

  • 마지막으로


  • 처음 Qiita에 기사를 게시했기 때문에 다양한 용서하십시오
  • 좋은 웹페이지 즐겨찾기