Python サーバーレスフレームワークの比較 - Zappa vs Chalice

  • 19
    いいね
  • 0
    コメント

はじめに

この記事は、Serverless(2) Advent Calendar 2016 23日目の記事です。

Python サーバーレスフレームワーク

Pythonでサーバレスなアプリケーションを開発するためのフレームワークとしては、以下の2つが人気のようです。

今回はこの2つをざっくり比較してみます。

ちなみにNode.jsを使用するのであれば、Serverless FrameworkApexが有名です。
他にどういったサーバーレスフレームワークがあるかについては、以下のスライドが参考になります。
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を参照してください。

参考にしたもの

おわりに

Top 10 Python libraries of 2016 - Tryolabs BlogでもZappaが1位に輝いているように、2016年12月の時点ではchaliceよりもZappaの方が人気があるようです。

しかしここで見てきたように2つのフレームワークは特徴がはっきりしていて差別化されています。
用途に合わせて使い分けることで、Pythonサーバーレスフレームワーク界隈が盛り上がっていくといいですね。

この投稿は Serverless(2) Advent Calendar 201623日目の記事です。