実際にサーバレスアプリケーションを作ってみよう
ここでは実際にサーバレスで構築したシステムを例に取り、構築方法をご紹介します。
実際に手を動かすとメリットを感じやすいと思います。
少し特異な例ですが、コンタクトセンターに掛かってきた電話(呼)が取り切れなかった際に応答する、IVR(Interactive Voice Response: 自動音声応答)システムの構築をサーバレスで行いました。
このシステムを作成する方法をご紹介します。
サーバーレスアプリケーション用開発ツール
AWS上にアプリケーションを構築する場合のツール選定はこちらを参照してください。
今回は開発言語はPythonとし、フレームワークはZappaを使用します。
Zappaは特にWSGI(Web Server Gateway Interface)を使用したWebアプリケーションを開発する際に非常にシンプルで素早く開発ができると思います。Pythonのみで完結する点も魅力です(ZappaはWebアプリケーション以外の用途でも利用可能です)。より複雑なアーキテクチャを組む場合は、Serverless Frameworkも検討してみてください。
今回使用する環境
音声部分にはTwilioを、Twilioで使用されるAPIはAWSのAPI Gateway及びLambdaを使用します。
事前にTwilioとAWSのアカウントの設定をし、以下を準備しておいてください。
項目 | 内容 | 記事中での値 |
---|---|---|
IAM | CLI/SDKが使用可能で、AdministratorAccessポリシーが適用されたIAMユーザの作成 |
[profile] twilio |
DNS | 独自ドメインの取得 | test.com |
DNS | AWS Route53 ホストゾーンの設定、NSレコード設定 | - |
証明書 |
AWS Certificate Managerで*.ドメイン名 の証明書を取得(CloudFrontで使用されるため、バージニア北部で取得する) |
[arn] arn:aws:acm:us-east-1:XXXXXXX:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx |
S3 | デプロイ用S3バケットの作成 |
[バケット名] develop-twilio.test.com |
IAMユーザーは作成したら、開発用PCの~/.aws/credentials
に以下のように設定してください。
[twilio] aws_access_key_id = XXXXXXXXX aws_secret_access_key = XXXXXXXXXXXXXXX
また、開発はMacにて実施します。pyenvをインストールしておいてください。
サーバレスでアプリケーション構築
開発環境準備
まず、Pythonによる開発環境を準備します。
プロジェクト用ディレクトリで以下のコマンドを実行し、venvによる仮想環境を作成・有効化します。
なお、執筆時点でLambdaでサポートされているPythonのランタイムは2.7
もしくは3.6
であるため、今回は3.6
を使用します。
# Python 3.6.8のインストール pyenv install 3.6.8 # pyenvにより、pythonのバージョンを3.6.8に pyenv local 3.6.8 # venvというディレクトリに仮想環境を作成 python -m venv venv # 仮想環境を有効化 venv/bin/activate
次に、プロジェクトに必要なパッケージを仮想環境にインストールします。
# pipのバージョンアップ (venv)$ pip install --upgrade pip # Webアプリケーションフレームワークとしてflaskを使用 (venv)$ pip install flask # サーバーレスフレームワークとしてzappaを使用 (venv)$ pip install zappa # twilio用SDK (venv)$ pip install twilio # パッケージ管理 (venv)$ pip freeze > requirements.txt
以上で準備は完了です。
APIの実装
ここからAPIを実装します。といっても、通常のWebアプリケーションと内容は全く変わりません。
ここでは、Twilio Programmable Voiceを使用して、簡単なText to Speechを実装します。
import os from flask import Flask, request from werkzeug.exceptions import Forbidden from typing import Callable from functools import wraps from twilio.request_validator import RequestValidator from twilio.twiml.voice_response import VoiceResponse # flask初期化 app = Flask(__name__) def get_app_env() -> str: """ 環境変数APP_ENVの取得 """ app_env = os.environ.get('APP_ENV') if app_env is None: app_env = 'local' return app_env def validate_twilio_request(f: Callable) -> Callable: """ Twilioからのリクエストであることを認証 """ @wraps(f) def decorated_function(*args, **kwargs) -> Callable: # 開発環境は認証をしない if get_app_env() == 'local' or get_app_env() == 'develop': return f(*args, **kwargs) # 環境変数に設定されているTWILIO_AUTH_TOKENを使用してバリデーション実施 validator = RequestValidator(os.environ.get('TWILIO_AUTH_TOKEN')) request_valid = validator.validate( request.url, request.form, request.headers.get('X-TWILIO-SIGNATURE', '') ) if request_valid: return f(*args, **kwargs) else: raise Forbidden return decorated_function @app.route('/api/init', methods=['POST']) @validate_twilio_request def twilio_init() -> str: """ コールがあった際、最初にhookされるAPI 発番号を取得し、発声する """ response = VoiceResponse() # 発番の取得 caller_id = request.form['Caller'] # Aliceに話をさせる。 response.say(caller_id + 'からのお電話を頂いています。', voice='alice', language='ja-JP') # 次のAPIへリダイレクト(実際は条件分岐に基づき、リダイレクトされる) response.redirect('/api/next') return str(response) @app.route('/api/next', methods=['POST']) @validate_twilio_request def twilio_next() -> str: """次に呼ばれるAPI""" response = VoiceResponse() # Aliceに話をさせる。 response.say('では、さようなら。', voice='alice', language='ja-JP') # 1秒待って response.pause() # 呼を切断 response.hangup() return str(response) if __name__ == '__main__': app.run(debug=True)
Twilioで取得した電話番号に電話が架電されると、/api/init
に POST
させます。/api/init
では発信者番号を取得して、電話番号をオウム返ししています。その後、/api/next
へリダイレクト(301/302ではなく、Twilioが音声を発し終わったら、/api/next
をPOST
させる)させ、呼を切断する、という動作を実装しています。
AWSへデプロイ
AWSへデプロイする前に、AWSへどのような環境でデプロイするのか設定を記述する必要があります。Zappaではzappa_settings.json
もしくはzappa_settings.yml
に設定を記載します。
以下ではdevelop
というステージでデプロイしています。
{ "develop": { "domain": "develop-twilio.test.com", "apigateway_enabled": true, "certificate_arn": "arn:aws:acm:us-east-1:XXXXXXX:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx", "app_function": "run.app", "keep_warm": true, "profile_name": "twilio", "s3_bucket": "develop-twilio.test.com", "aws_environment_variables": { "APP_ENV": "develop", "TWILIO_AUTH_TOKEN": "xxxxxxxxxxxxxxxx", "TZ": "Asia/Tokyo" }, "runtime": "python3.6", "aws_region": "ap-northeast-1", "log_level": "DEBUG", "memory_size": 256, "timeout_seconds": 30 } }
各項目の意味は想像がつくものが多いと思いますが、詳細はこちらを参照してください。
aws_environment_variables
に設定しているTWILIO_AUTH_TOKEN
はTwilioの画面から取得してください。
では、実際にデプロイします。
(venv) $ zappa deploy develop ... Deployment complete!: https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/develop
メッセージを見ると分かりますが、IAMユーザーが作成されたり、S3にファイルがアップロードされたり、API Gatewayがデプロイされたりしています。実施はS3にファイルがアップロードされ、CloudFormationを通じて各サービスがデプロイされています。
ここまでで、API Gatewayまではデプロイされました。コマンドの最後に出力されているhttps://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/develop
は実際に可動するURLで、独自ドメイン(カスタムドメイン)を設定しない場合、https://APIゲートウェイのFQDN/ステージ
というURLになります。
次に、カスタムドメインの設定をします。
(venv) $ zappa certify develop --yes ... Created a new domain name with supplied certificate. Please note that it can take up to 40 minutes for this domain to be created and propagated through AWS, but it requires no further work on your part. Certificate updated! (venv) $
これでRoute53に(内部的に使用されている)CloudFrontディストリビューションのCNAMEレコードが作成され、しばらくするとhttps://develop-twilio.test.com/api/init
等にアクセスできるようになります。
Twilioの設定
次にTwilioの設定をします。
Programmable Voiceの画面から電話番号を購入します。
購入した電話番号のWebhookを設定します。
Programmable Voiceの画面から電話番号に対する設定をします。
先程購入した電話番号をクリックします。
音声およびFaxの通話着信時の動作をWebhook
で、URLをhttps://develop-twilio.test.com/api/init
、メソッドはHTTP POST
を設定し、保存します。
これで設定は完了です。購入した050番号に電話をすると、Lambdaで実装した自動音声が流れます。
最後に
小規模な業務システム開発におけるサーバレスアーキテクチャの使い所と実例について説明させていただきました。文中で触れた用途以外にも、発想しだいで様々な用途が考えられると思います。うまく活用することで、スピーディーでマネージドサービスの恩恵を享受できる開発に繋がると思います。
サーバレスは「まずやってみる」ということが非常に大事だと思います。やってみることでイメージがつかめると発想の幅が大きく広がると思いますので、是非ご活用頂けますと幸いです。