はじめに
この記事は、Serverless(2) Advent Calendar 2016 23日目の記事です。
Python サーバーレスフレームワーク
Pythonでサーバレスなアプリケーションを開発するためのフレームワークとしては、以下の2つが人気のようです。
- awslabs/chalice: Python Serverless Microframework for AWS
- Miserlou/Zappa: Serverless Python Web Services
今回はこの2つをざっくり比較してみます。
ちなみにNode.jsを使用するのであれば、Serverless FrameworkやApexが有名です。
他にどういったサーバーレスフレームワークがあるかについては、以下のスライドが参考になります。
Unlimited Frameworks
chalice
Python Serverless Microframework for AWS
- Amazon製だけあってAWS専用のフレームワーク
- 簡単に開発できるように設計されていて、FlaskやBottoleのようなデコレータベースの軽量フレームワーク
- IAMポリシーを自動生成してくれる
- GatewayとLambda全てのAPIを網羅していない
- S3やDynamoDBなどの他のAWSリソースへのアクセスもできない
- API Gatewayに特化している
用意してあるコマンドはこれだけ。
Commands:
deploy
gen-policy
generate-sdk
local
logs
new-project
url
実際のchaliceを使ったソースコードをHow to create a thumbnail API service with AWS Lambdaを参考にみてみます。
写真をPOSTで受け取ってサムネイルを生成する処理をapp.py
が行なってます。
import base64
import uuid
from subprocess import Popen, PIPE
import boto3
from chalice import BadRequestError, Chalice
app = Chalice(app_name='thumbnail-service')
app.debug = True # TODO: Disable on production
S3 = boto3.client('s3')
S3_BUCKET = '' # TODO: Replace with valid bucket name
@app.route('/', methods=['POST'])
def index():
body = app.current_request.json_body
image = base64.b64decode(body['data'])
format = {'jpg': 'jpeg', 'png': 'png'}[body.get('format', 'jpg').lower()]
mode = {'max': '', 'min': '^', 'exact': '!'}[body.get('mode', 'max').lower()]
width = int(body.get('width', 128))
height = int(body.get('height', 128))
cmd = [
'convert', # ImageMagick Convert
'-', # Read original picture from StdIn
'-auto-orient', # Detect picture orientation from metadata
'-thumbnail', '{}x{}{}'.format(width, height, mode), # Thumbnail size
'-extent', '{}x{}'.format(width, height), # Fill if original picture is smaller than thumbnail
'-gravity', 'Center', # Extend (fill) from the thumbnail middle
'-unsharp',' 0x.5', # Un-sharpen slightly to improve small thumbnails
'-quality', '80%', # Thumbnail JPG quality
'{}:-'.format(format), # Write thumbnail with `format` to StdOut
]
p = Popen(cmd, stdout=PIPE, stdin=PIPE)
thumbnail = p.communicate(input=image)[0]
if not thumbnail:
raise BadRequestError('Image format not supported')
filename = '{}_{}x{}.{}'.format(uuid.uuid4(), width, height, format)
S3.put_object(
Bucket=S3_BUCKET,
Key=filename,
Body=thumbnail,
ACL='public-read',
ContentType='image/{}'.format(format),
)
return {
'url': 'https://s3.amazonaws.com/{}/{}'.format(S3_BUCKET, filename)
}
Zappa
Serverless Python Web Services
- WSGIアプリケーションをデプロイする
- なのでchaliceと違って、AWS以外のクラウドにも対応可能
- 多機能なフルスタックなフレームワーク
- "ハイブリッド"アプリケーションに必要となる、AWSイベントソース全てを自動サポート
それではchalice同様に、POSTで受け取った写真のサムネイル生成を行うアプリケーションをBuilding Serverless Microservices with Zappa and Flask - Gun.ioを参考にみてみます。
import base64
import boto3
import calendar
import io
from datetime import datetime, timedelta
from flask import Flask, request, render_template
from PIL import Image
s3 = boto3.resource('s3')
BUCKET_NAME = 'your_public_s3_bucket'
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
new_file_b64 = request.form['b64file']
if new_file_b64:
# Decode the image
new_file = base64.b64decode(new_file_b64)
# Crop the Image
img = Image.open(io.BytesIO(new_file))
img.thumbnail((200, 200))
# Tag this filename with an expiry time
future = datetime.utcnow() + timedelta(days=10)
timestamp = str(calendar.timegm(future.timetuple()))
filename = "thumb.%s.jpg" % timestamp
# Send the Bytes to S3
img_bytes = io.BytesIO()
img.save(img_bytes, format='JPEG')
s3_object = s3.Object(BUCKET_NAME, filename)
resp = s3_object.put(
Body=img_bytes.getvalue(),
ContentType='image/jpeg'
)
if resp['ResponseMetadata']['HTTPStatusCode'] == 200:
# Make the result public
object_acl = s3_object.Acl()
response = object_acl.put(
ACL='public-read')
# And return the URL
object_url = "https://{0}.s3.amazonaws.com/{1}".format(
BUCKET_NAME,
filename)
return object_url, 200
else:
return "Something went wrong :(", 400
return render_template('upload.html')
chaliceと違ってWSGIアプリケーションをデプロイできるので、写真アップロード画面なども一緒に用意できます。
またAWSイベントソースを利用することで、S3へのアップロードをフックにサムネイル処理をノンブロッキングに実行することもできます。
# zappa_settings.yml
---
dev:
app_function: your_project.main.app
events:
- function: your_project.users.thumbnailer
event_source:
arn: arn:aws:s3:::your_public_s3_bucket
events:
- s3:ObjectCreated:*
# your_project/users.py
import Pillow
def thumbnailer(event, context):
""" Upon PUT, thumbnail! """
# Get the bytes from S3
in_bucket = event['Records']['s3']['bucket']['name']
key = event['Records']['s3']['object']['key']
image_bytes = s3_client.download_file(in_bucket, key, '/tmp/' + key).read()
# Thumbnail it
size = (250, 250)
thumb = ImageOps.fit(image_bytes, size, Image.ANTIALIAS)
# Put it back on S3
s3_client.put_object(
ACL='public-read',
Body=thumb,
Key=key + 'thumbnail.jpg',
Bucket='avatar-bucket')
詳しくはServer-less Framework Comparison - Zappa Versus Chaliceを参照してください。
参考にしたもの
- Server-less Framework Comparison - Zappa Versus Chalice
- How to create a thumbnail API service with AWS Lambda
- Building Serverless Microservices with Zappa and Flask - Gun.io
- AWS Lambda + API Gateway + Chalice(Python2)でLINE BOT - c-bata web
おわりに
Top 10 Python libraries of 2016 - Tryolabs BlogでもZappaが1位に輝いているように、2016年12月の時点ではchaliceよりもZappaの方が人気があるようです。
しかしここで見てきたように2つのフレームワークは特徴がはっきりしていて差別化されています。
用途に合わせて使い分けることで、Pythonサーバーレスフレームワーク界隈が盛り上がっていくといいですね。