ランサーズ(Lancers)エンジニアブログ > AWS > Lambda > Amazon ConnectとLambdaとSendGridで作る電話確認システム

Amazon ConnectとLambdaとSendGridで作る電話確認システム

inamuu|2019年06月19日
AWS

こんにちは、SREチームの稲村です。
最近Oculus Questを購入して、日々の運動不足解消に役立てています。
VRの世界は本当に凄いですね。

電話確認システムとは

さて、ランサーズでは、クライアント様とランサー様それぞれの存在証明の一つとして電話確認の実施を推奨しています。
具体的には、プロフィール画面で番号登録した電話から、弊社指定の番号へ電話をかけていただくことで電話確認が行われるシステムです。
今回はそのシステムをAmazon ConnectとLambdaとSendGridへ移行した話をご紹介します。

移行前の構成

移行前は下記のような構成になっておりました。

外部の業者で050番号を契約し、提供された認証情報を使って、AWS EC2のAsteriskを使ったIP-PBX環境が構築されていました。
ユーザーから電話がかかってくると、Asteriskサーバーまで接続されます。
その後、別のEC2のBatchサーバーにあるCakePHPで作成されたShellを使って電話番号のチェックを行い、問題なければDBのステータス更新を行います。
最後にユーザー様へのアナウンス用の音声ファイルの再生を行い、メールサーバー経由で確認結果のメールを送信して終了する仕組みでした。

なぜ移行することになったか

移行することになった経緯として、既存の電話確認のシステムには下記課題がありました。

  1. 電話確認を行うサーバーが1台のSPOFだった
  2. インフラと一部アプリケーションソースが構成管理されていなかった
  3. 検証環境が無かった
  4. 構築時のドキュメントが古くなり、新規での構築が容易では無かった

元々、弊社サービスを通じてランサー様に構築いただいた仕組みだったようですが、構築からすでに5年が経過しており、EC2インスタンスが弊社で唯一のt1系で動いていたこともあり、まずは構成管理を主目的におき、Ansibleでセットアップするところからスタートしました。
しかしながら、構築時のドキュメントが古くなってしまったため残っていたドキュメントの内容ではうまく動かず、新規で検証サーバーを構築するのが困難でした。
その後、しばらく手がつけられずにいたところ、別件でAWSの営業担当からAmazon Connectに関する情報を教えていただき、Lambda連携をすることでAmazon Connectで代替ができそうということを教えていただいたので、思い切ってシステム刷新に取り組むことにしました。

Amazon Connectとは

https://aws.amazon.com/jp/connect/

Amazon Connectはマネージドのコールセンターサービスです。
Webベースで電話の受電、架電がおこなえる上に、東京リージョンに対応しており、日本語のテキスト読み上げも対応しています。
この日本語のテキスト読み上げが自動音声としては大変優秀で、日本語で書いた漢字を含むテキストをそのまま読み上げてくれます。
電話番号は050と0120から選択でき、申請から数分で電話番号が発行されます。
また、元々コールセンター向けのサービスということもあり、受電時の問い合わせフローを作成できる専用のエディターがあり、その画面がとても良くできています。
※下記が問い合わせフローの専用エディターのイメージです。

みずほ銀行のようなメガバンクのコールセンターでも採用されているようです。

https://www.serverworks.co.jp/case/mizuhobank.html

移行後の構成

移行後は下記構成で構築いたしました。

電話の受電、およびユーザーの入力のロジック部分を全てAmazon Connectで設定しました。
と言っても、管理画面でマウスを使ってドラッグアンドドロップでロジックが構成できるので、Amazon Connetを触り始めて数時間で作成することができました。
また、AsteriskのagiというCGIに似たPythonで書かれたファイルと、CakePHPの電話確認用のShellを、LambdaでPythonに統一して作り直しました。
Lambdaでは極力シンプルになるようにして、DBへの処理とメール配信処理(SendGridのAPIを実行する処理)を行い、ユーザーに渡すメッセージのテキストを戻り値として返すようにしました。

Lambdaの起動

LambdaからRDSへ接続するにあたり、VPCに配置する必要があります。
VPCに配置されたLambdaは定期的に呼び出されている場合は終了せずに再利用されますが、しばらくすると終了してしまうことになります。
Amazon ConnectからLambdaへの接続の最大待機時間は8秒で、8秒以内にLambdaが起動しないと電話が突然終了してしまうため、下記を追加してCloudWatch Ruleで引数を何も渡さずに定期実行するようにしました。
※10分おきの設定で問題なく稼働することを確認しています。また、一ヶ月呼び出しても4300回あまりなので、費用もほとんど発生しません。

def lambda_handler(event, context):
    if 'Details' not in event:
        print('keys does not exist')
        return

Lambdaが定期的に呼び出されますが、DetailsがKeyで渡らない限りは何も処理をせずに終了します。
Lambdaの最後の戻り値はjsonでメッセージ用のテキストを渡すようにしており、そのままAmazon Connectに渡されます。
Amazon Connectの次のステップでLambdaから渡されたテキストを動的に読み上げすることで、意図したメッセージをユーザーにアナウンスすることができます。

メールの配信

Lambdaでメールの仕組みを一から作成するにはボリュームがでかいので、すでに社内で利用実績のあるSendGridを使ってメールを送信することにしました。

https://github.com/sendgrid/sendgrid-python

import os
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

message = Mail(
    from_email='from_email@example.com',
    to_emails='to@example.com',
    subject='Sending with Twilio SendGrid is Fun',
    html_content='<strong>and easy to do anywhere, even with Python</strong>')
try:
    sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
    response = sg.send(message)
    print(response.status_code)
    print(response.body)
    print(response.headers)
except Exception as e:
    print(str(e))

上記はREADMEのサンプルですが、見てわかるように大変シンプルなコードでメール配信が行えるようになりました。

その他

AmazonConnectからLambdaを呼び出すには、下記設定をAWS CLIから行う必要があります。

aws lambda add-permission --function-name function:XXXXX --statement-id 1 --principal connect.amazonaws.com --action lambda:InvokeFunction --source-account XXXXX --source-arn arn:aws:connect:ap-northeast-1:XXXXX:instance/XXXXX --profile XXXXX

https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html

まとめ

Amazon ConnectとLambdaとSendGridを使って、電話確認システムの移行が行えました。
検証からリリースまで数週間で出来たことを考えると、とてもスムーズに移行できたと思います。
普段スマートフォンに触れてはいるものの、電話の世界は奥深いのでまだまだ知らない部分が多々あります。
そういった点においても、電話部分をまるっと任せることができたのは大きく、今後機能拡張をすることになっても、Lambdaに手を入れることで簡単に実装することができるようになりました。
今後も新しいサービスには目を光らせて、マネージドにお任せできるところはお任せしていきたいです。