Introdução a Serverless para Pythonistas

Lucas Costa

TRILHA PYTHON

Serverless?

Serverless Architecture Principles

  1. Use a compute service to execute code on demand (FaaS)
  2. Write single-purpose stateless functions
  3. Design push-based, event-driven pipelines
  4. Create thicker, more powerful front-ends
  5. Embrace third-party services

Peter Sbarski

“Serverless Architectures on AWS: With examples using AWS Lambda”

FaaS - Function as a Service

On Premises

IaaS

FaaS

PaaS

AWS EC2/ECS, Azure VMs, Google Compute Engine, OpenStack, VMware

Heroku, OpenShift, GC App Engine, AWS Beanstalk, Azure App Service

AWS Lambda, Azure Functions, Webtask
Google Cloud Functions, IBM OpenWhisk

Datacenter

Event-driven Pipelines

Powerful Front-ends / Third-party Services

"Serverless" by Badri Janakiraman on martinfowler.com

Serverless Architectures

Compute as Back-end

Serverless Architectures

Compute as Back-end - A Cloud Guru

Serverless Architectures

Legacy API Wrapper

Serverless Architectures

Hybrid

Serverless Architectures

GraphQL

Serverless Architectures

Compute as Glue

Serverless Architectures

Real-time Processing

  • Cost
  • Scalability
  • Productivity
  • Experimentation
  • Time to Market
  • Limits
  • Debbuging/Testing
  • Vendor Issues

Serverless Python

Main Providers

*

AWS Lambda

Azure
Functions

Cloud
Functions

OpenWhisk

( JS only )

( experimental )

Serverless Python

Weapon of Choice

AWS Lambda

Serverless Python

AWS Lambda Pricing

1 million requests per month are free

$0.20 per 1 million requests thereafter ($0.0000002 per request)

400,000 GB-seconds of compute time per month free

Serverless Python

AWS Lambda Programming Model

import json

def hello(event, context):
    body = {
        "message": "Your function executed successfully!",
        "input": event
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response

handler.py

Serverless Python

AWS Lambda Context

import time
def get_my_log_stream(event, context):       
    print("Log stream name:", context.log_stream_name)
    print("Log group name:",  context.log_group_name)
    print("Request ID:",context.aws_request_id)
    print("Mem. limits(MB):", context.memory_limit_in_mb)
    time.sleep(1) 
    print("Time remaining (MS):", context.get_remaining_time_in_millis())

Serverless Python

AWS Lambda Logging

import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def my_logging_handler(event, context):
    logger.info('got event{}'.format(event))
    logger.error('something went wrong')
    return 'Hello World!'  

CloudWatch

Serverless Python

AWS Lambda Function Errors

def always_failed_handler(event, context):
    raise Exception('I failed!')
{
  "errorMessage": "I failed!",
  "stackTrace": [
    [
      "/var/task/lambda_function.py",
      3,
      "my_always_fails_handler",
      "raise Exception('I failed!')"
    ]
  ],
  "errorType": "Exception"
}
  • Dead Letter Queue (SQS/SNS)​
  • State Machine (custom error handling)

Handling

Serverless Python

AWS Step Functions

Serverless Python

AWS Step Functions

Serverless Python

Chalice

Frameworks

Serverless Python

The Framework

  • Open Source
  • Language Agnostic
  • Multi-Platform
  • $3M Funding
  • CLI
  • Beyond FaaS
  • Pluggable
  • Extensive docs
  • Great community

Service

Organization unit, project file (multiple services for a single application).

Function

Independent unit of deployment, code deployed in the cloud.

Events

Anything that triggers a Function (HTTP request, timers, uploads, streams, etc.)

Resources

Infrastructure components (DB Table, Storage, etc.)

Concepts

Stages

Deployment environments (dev, staging, production, etc.)

Getting Started

> pip install --upgrade --user awscli
> aws configure

Install and configure AWS CLI

> npm install -g serverless

Install Serverless

> serverless create --template aws-python3

Create Python service

> serverless deploy

Deploy! 😎

serverless.yml

service: service-name

provider:
  name: aws
  region: us-east-1
  runtime: python3.6
  timeout: 300
  memorySize: 128
  environment:
    SERVICE_NAME: ${self:service}
    STAGE: ${opt:stage}
    MY_ENV_VAR: ${self:custom.env.myEnvVar}

custom:
  env: ${file(env.yml)}
package:
  individually: true

functions:
  hello:
    handler: handler.hello
  events:
    - http:
      path: /
      method: get
  package:
    include:
      - handler.py
      - include_me_dir/**
    exclude:
      - include_me_dir/not_this_one.py

serverless.yml ➭ CloudFormation template

WSGI Plugin

from flask import Flask
app = Flask(__name__)


@app.route("/cats")
def cats():
    return "Cats"


@app.route("/dogs/<id>")
def dog(id):
    return "Dog"
service: flask-example

provider:
  name: aws
  runtime: python3.6

plugins:
  - serverless-wsgi

functions:
  api:
    handler: wsgi.handler
    events:
      - http: ANY {proxy+}

custom:
  wsgi:
    app: api.app
Flask==0.11.1

api.py

serverless.yml

requirements.txt

Serverless Python

Dependencies

project
├── package1
|   ├── .requirements
|   ├── handler1.py
|   └── requirements.txt
├── package2
|   ├── .requirements
|   ├── handler2.py
|   └── requirements.txt
├── utils
|   ├── .requirements
|   ├── helper.py
|   └── requirements.txt
└── serverless.yml
import json
import os
import sys

import pkg1dependency
from utils import helper

# Add requirements to PATH
file_path = os.path.realpath(__file__)
file_dir = os.path.dirname(file_path)
requirements_path = os.path.join(file_dir, '.requirements')
sys.path.append(requirements_path)

def handler1(event, context):
    data = helper.function(event)
    response = pkg1dependency(data)
    return response

handler1.py

Serverless Python

Dependencies

project
├── package1
|   ├── .requirements
|   ├── __init__.py
|   ├── handler1.py
|   └── requirements.txt
├── package2
|   ├── .requirements
|   ├── __init__.py
|   ├── handler2.py
|   └── requirements.txt
├── utils
|   ├── .requirements
|   ├── __init__.py
|   ├── helper.py
|   └── requirements.txt
└── serverless.yml
import json

import package1  # noqa
import pkg1dependency
from utils import helper

def handler1(event, context):
    data = helper.function(event)
    response = pkg1dependency(data)
    return response
import os
import sys

# Add requirements to PATH
file_path = os.path.realpath(__file__)
file_dir = os.path.dirname(file_path)
requirements_path = os.path.join(file_dir, '.requirements')

__init__.py

handler1.py

Serverless Python

Dependencies

install:
   find . -name "*requirements.txt" -not -path "*node_modules*"
     -exec bash -c 'pip3 install -r {} -t $$(dirname {})/.requirements' \;

uninstall:
   find . -name "*requirements.txt" -not -path "*node_modules*"
     -exec bash -c 'rm -rf $$(dirname {})/.requirements' \;

devinstall: install
   pip3 install -r requirements-dev.txt

Makefile

Serverless Python

References

SERVERLESS

BLACK

is
the
new

lucascosta

@lucasrcosta

@lucasrcosta

Lucas Costa

lucas@sugarads.io

Obrigado

slides.com/lucasrcosta/tdc2017

Serverless Introduction for Pythonistas

By lucasrcosta

Serverless Introduction for Pythonistas

  • 344