Serverless Frameworkを使ってAWS LambdaにQRコードを返すAPIをデプロイする
サーバレスアーキテクチャ(AWS Lambda, Azure Functions, Google CloudFunctionsなど)の構成管理ができるServerless Frameworkを使ってみたかったので、任意の文字列をQRコード化して返すAPIを作ってデプロイしてみました。
環境は下記のとおりです。
本記事でわかること
- 使用するライブラリにネイティブバイナリが含まれている場合のLambdaの使い方
- APIがバイナリデータを返す場合のAPI Gatewayの設定方法
- python-qrcodeの使い方
- Binary Support for API Integrations with Amazon API Gateway | AWS Compute Blogの構成をServerless Frameworkで再現する方法(一部再現不可)
Serverless Frameworkのインストール
$ npm install -g serverless
$ sls create --template aws-python --path qrcode-service
python環境の構築
$ cd qrcode-service
$ pyenv install 2.7
$ pyenv virtualenv 2.7 qrcode-service
$ pyenv local qrcode-service
$ pip install -r requirements.txt
requirements.txtは下記の通り。
olefile==0.44
Pillow==4.0.0
qrcode==5.3
six==1.10.0
QRコードを返す関数の作成
受け取った文字列をQRコード化して、base64エンコードした結果を返す関数を作成します。
handler.py
import base64
from io import BytesIO
import os
import sys
sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'vendor'))
sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'vendor/PIL'))
import qrcode
def create(event, context):
params = event['query']
img = qrcode.make(params['text'])
bufferd = BytesIO()
stdout_buffer = getattr(sys.stdout, 'buffer', None)
img.save(bufferd)
image_base64 = base64.b64encode(bufferd.getvalue()).decode('utf-8')
return image_base64
serverless.ymlを書く
基本的にはこちらのブログの記事の構成を再現する内容です。
Binary Support for API Integrations with Amazon API Gateway | AWS Compute Blog
serverless.yml
service: qrcode-service
provider:
name: aws
runtime: python2.7
stage: dev
region: ap-northeast-1
package:
exclude:
- build_packages.sh
- Dockerfile
- requirements.txt
functions:
qrcode:
handler: handler.create
events:
- http:
path: qrcode
method: get
integration: lambda
request:
parameters:
querystrings:
text: true
passThrough: WHEN_NO_TEMPLATES
template:
image/png: |
#define( $loop )
{
#foreach($key in $map.keySet())
#set( $k = $util.escapeJavaScript($key) )
#set( $v = $util.escapeJavaScript($map.get($key)).replaceAll("\\'", "'") )
"$k":
"$v"
#if( $foreach.hasNext ) , #end
#end
}
#end
{
#set( $map = $input.params().querystring )
"query": $loop
}
ローカル実行で動作確認
Serverless Frameworkにはローカル実行する機能があるので、デプロイしなくても下記のようにして動作確認ができます。
$ sls invoke local -f qrcode -d '{ "query": { "text": "Kappa" } }' | sed 's/"//g' | base64 -d > kappa.png
$ open kappa.png
デプロイ用に外部ライブラリパッケージをまとめる
Lambdaで外部ライブラリを使う場合は外部ライブラリも含めてzipにする必要があります。 しかし、使用するライブラリにネイティブバイナリが含まれている場合は、MacでビルドしてしまうとDarwin用のモジュールになってしまってLambda上では動作しません。 今回は、MacもEC2もCPUは同じx64系ということで、amazonlinuxのDocker Imageを使ってMac上でビルドしてしまうことにします。
Docker Imageのビルド
Dockerfileは下記の通りです。
FROM amazonlinux
WORKDIR /work
RUN set -x && \
yum install -y python27-devel python27-pip gcc gcc-c++ cmake && \
yum install -y libtiff-devel libjpeg-devel libzip-devel freetype-devel \
lcms2-devel libwebp-devel tcl-devel tk-devel && \
pip install --upgrade pip
ビルドします。
$ docker build . -t qrcode-service
外部ライブラリのビルド
外部ライブラリをdocker container上でビルドします。
$ mkdir vendor
$ cp requirements.txt vendor
$ docker run -v "$(pwd)"/vendor:/work qrcode-service pip install -r requirements.txt -t .
AWSにデプロイする
Serverless Frameworkのコマンド一発で完了……といきたいところですが、Serverless Frameworkがバックエンドで使用しているCloudFormationがバイナリサポートの設定に対応していないらしく、コマンド一発で完了とはいきませんでした。 しかたがないので一部の設定は別途行います。
1. まずはデプロイする
下記のコマンドでデプロイを実行します。
$ sls deploy
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading service .zip file to S3 (6.26 MB)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............................
Serverless: Stack update finished...
Service Information
service: qrcode-service
stage: dev
region: ap-northeast-1
api keys:
None
endpoints:
GET - https://u5uko0b1ne.execute-api.ap-northeast-1.amazonaws.com/dev/qrcode
functions:
qrcode: qrcode-service-dev-qrcode
上記の例だとu5uko0b1ne
がAPI IDになるので控えておきます。
デプロイが完了した時点でで下記のようにリクエストを送るとbase64エンコードされたまま結果が返ってくるのが確認できます。
$ curl -X GET -H "Accept: image/png" -H "Content-Type: image/png" "https://u5uko0b1ne.execute-api.ap-northeast-1.amazonaws.com/dev/qrcode?text=Kappa"
"iVBORw0KGgoAAAANSUhEUgAAASIAAAEiAQAAAAB1xeIbAAABhklEQVR4nO2YTW6DMBCFv6m9d26Qo5gb9Ky9AT5KDhAJ9kbThW3kkkrthvDnWSBiPoknZzR+PFH+rvDxDwga1ahGNapRe6ckl0W60QJjWek21XUJyquq6gDaA9JhVFVVf1Lv13UJaiw9Hu4x/wEidntdZ6bs4rfgJvviO/eq/mSUf1ike+cbL0uVvncKjACYKAB18+9V/bGpvPdBADDz+iRrvbFRpWQx2wVMXEItU1iRkg6QbpTU7dnb3/KDvas/KkXy8T1GtXdaXL1TxQ/z072qPzaV5r3gnoJ/2Ejo5pnjYjmJ96r+2FTaXWW06bOKfMxmky++30bXFahq5uQhM0CaPr2LbeasSeXdnQs/QH1pe78aRR2ZYbRea33/FqrOMY0SbpPgB6PSbavr1FRxMuMN/BcIgOCMEm7PkqrtVf25KFWN4B8idaq2va4zUssMmfA5QLgrkvK1jXRdiXKq2qfbSSqfs7WuM1OvPsfFdOBmy9l8zmrUMsf8tVqO2ahGNapRh6e+AZkLzhog49P2AAAAAElFTkSuQmCC"
2. バイナリサポートの有効化
API Gatewayがリクエストの送受信でバイナリデータを扱えるようにバイナリサポートを有効にします。
$ aws apigateway update-rest-api \
--rest-api-id u5uko0b1ne \
--patch-operations op=add,path=/binaryMediaTypes/image~1png
u5uko0b1ne
はそれぞれのAPI IDで置き換えるようにしてください。
3. API GatewayにLambda関数を呼び出す権限を与える
手動で作ると問題ないのに、CloudFormationで構成したときに上手く動かないので悩んでいたのですが、次の記事によるとCloudFormationで構成した場合は、AWS Consoleから権限を与える必要があるそうです。(他に方法はないのでしょうか?)
4. 再度のデプロイ
再度、デプロイします。
$ sls deploy
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading service .zip file to S3 (6.26 MB)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..............
Serverless: Stack update finished...
Service Information
service: qrcode-service
stage: dev
region: ap-northeast-1
api keys:
None
endpoints:
GET - https://u5uko0b1ne.execute-api.ap-northeast-1.amazonaws.com/dev/qrcode
functions:
qrcode: qrcode-service-dev-qrcode
APIを叩いてみると今度はバイナリデータが返ってくることが確認できました。
$ curl -X GET -H "Accept: image/png" -H "Content-Type: image/png" "https://u5uko0b1ne.execute-api.ap-northeast-1.amazonaws.com/dev/qrcode?text=Kappa"
�PNG
IHDR""u���IDATx��Mn�0����wn�����>J �FӅm�J���Y b>�'g4~<Q���ըF5�Q{�$�E��cY�6�u ʫ����aTUU�R��u j,=�1�"v{]g����&��;��d��X�{�K��w
��(u��U��A0��$k��Q�d1�L\B-SX���n������?*E��=F�wZ\�S��ӽ�?6�������H���b9����T�]e�鳊|�f�/��F��j��!3@�>��m�I�ݝ
?@}i{�E�a�^k}���1�n����nϒ��U�(U��"u����3R�
��9@�+��t]�r�ڧ�I*����3S�>��t�f��|�j�2��Z�٨F5�Q����
� ���IEND�B`�i
以上です。
リポジトリ
完成品が下記になります。
参考
Binary Support for API Integrations with Amazon API Gateway | AWS Compute Blog