Twilio × Lambda × API Gateway で PRIMARY HANDLER FAILS を制御する

イタンジエンジニア、濱田です。

今回は、twilioを使っているサービスに障害が発生したときに、slackに通知したり、twilioにかかってきた電話を転送する仕組みを、aws lambda と aws api gatewayで実現する方法についてお話します。

背景

弊社では、twilioを使った自動応答サービスを展開しています。 このサービスに障害が発生したときに、次の3つを行う必要があります。 (この場合の障害は、サービスで起こったもので、twilio先生の障害ではありません。)

  1. そもそもの障害の検知
  2. twilioの050番号にかかってくる電話を、クライアントの03番号等に安全に転送してあげる
  3. 転送が行われたかの通知

1番目の検知は、様々な方法があるのでここでは割愛します。 2番、3番を実現するために、PRIMARY HANDLER FAILS を使う方法がありますよね。

かつては、サービスに障害が起きた時のために電話番号毎に作ったxmlファイルをs3に置き、そのurlを設定していました。 これでは、転送先の電話番号がクライアント毎に異なるため、大量のxmlの管理が発生してしまいます。 しかも、転送の通知をSMSに通知する設定をしており、社内の担当者が変わった時には、全てのファイルのSMSの送信先を変更するという作業が伴うものでした。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2016-12-07-19-06-17

そこで、aws lambda と aws api gatewayを使って、 

  1. twilioの転送結果をslackに通知して、エンジニアチーム全員で監視
  2. 障害発生時のアクションも、gateway側に設定して、一括管理

することにしました。

構成

今回の構成です。

cloudcraft-web-app-reference-architecture-1

ネットワーク設定等妥協して、lambdaと api gatewayが2セットあります。。。。

  • PRIMARY HANDLER FAILS に 設定される api gateway の endpoint
  • 上記のend pointから実行されるlambda1: slack通知と、転送先の電話番号のような情報を返す
  • lambda1が叩く、転送先の電話番号のような情報を取得するための、api gate way の endpoint
  • 転送先の電話番号のような情報を、RDSから取得して返すlambda2

実際のコード

lambdaの使い方、api gatewayの使い方、そして RDSから情報を取ってくる api gateway と lambdaの部分は省略します。

まずslack通知と、かかってきた電話の転送先を返すlambdaです。 今回はpythonです。

# -*- coding: utf-8 -*-
import urllib
import json

def lambda_handler(event, context):
    slack_url = SLACK_URL
    # アカウント情報取得は省略
    ## resultに入っている
    # slackに通知する時の文言
    text = """=======================================
    なんかおかしいから、〇〇クライアントにかかってきた番号を転送したよ!
    Phone_number: {0}
    client_name: {1}""".format(event['param']['To'], result["client_name"])

    params = {"text": text, "channel": "#channel", "username":"tenso-shitayo"}
    f = urllib.urlopen(slack_url, json.dumps(params))

    # ”とかで囲う必要があった
    twilio_phone = '"' + result["phone_number"] + '"'
    to_phone = '"' + result["transfer_phone"] + '"'

    # ここで返した値を、gateway側で設定する。
    return {'twilio_phone':twilio_phone, 'to_phone':to_phone}

次にapi gateway

まず統合リクエストの本文マッピングテンプレートを、application/jsonに変更し、下記コードにします。 ここで、paramsをlambdaにわたす際の整形を行います。

{
  "param": {
#foreach( $key in $input.params().querystring.keySet() )
    "$key": "$input.params().querystring.get($key)"#if( $foreach.hasNext ),#end
#end
  }
}

次に統合レスポンスの、本文マッピングテンプレートをapplicaiton/xmlに変更し、下記コードにします。 ここで、twilioさんにやってもらいたいことを記述します。

今回は、転送と、転送先が全て通話中だった場合に、謝罪してもらっています。

#set($inputRoot = $input.path('$'))
<Response>
    <Dial callerId=$inputRoot.twilio_phone >$inputRoot.to_phone</Dial>
    <Say>"ただいまお電話が大変込み合っております。しばらくたってから、おかけ直し下さいませ。"</Say>
</Response>

最後に、メソッドレスポンスの、200のレスポンス文を、application/xml emptyモデルにしてデプロイ

後は、PRIMARY HANDLER FAILS に api gateway の endpointを設定しておけば、転送と、転送したことの通知をslackに通知してくれます。

まとめ

lambda と api gatewayを使えば、twilio の PRIMARY HANDLER FAILSの選択肢はどんどん増えて行く気がします。

後は、Serverless Framework等で管理できるといいのですが。。。。 それはこれからがんばります。