serverlessとLambda

# serverlessとLambda

---

## 話すこと

- lambdaでできること
- serverless frameworkでできること

---

## Lambdaのusecase

- web application
- 定期的なtask処理
- Event処理

---

## Faas

- Function as a serviceの略
- AWS Lambdaが一番有名なFaasだが他にもいっぱいある
  - cloud functions(GCP)
  - Azure Functions(microsoft)

---

## Lambdaで使用可能な言語

- aws公式でサポートしている言語
  - node
  - Python
  - Ruby
  - Java
  - golang
- 基本何でも動く
  - custom runtimeを使えば何でも
  - https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-custom.html
  - lambdaでRustを動かしている事例もあり

---

## Lambdaの主な制限

- package size
  - 250MB(Layer含む)
  - 50MB(Layer含まない)
- functionのtimeout
  - 900秒(15分)
- accountごとの同時実行数
  - 1000

---

## アーキテクチャ設計における優先順位(推奨)

1. LambdaでできることはLambda(faas)でやる
2. Lambdaでできない(もしくは難しい)なら、ECSやsagemakerなどmanagedなコンテナ技術を使う
3. それでも難しい場合はec2を選択

---

## Common problems

- package sizeが大きくなってしまい50MBに収まらない
  - numpyやpandasのようなpackageを使う際によく起きる
  - 後述のlayerである程度解決
- 一つのfunctionで同時実行数を専有してしまい、他のfunctionが動かない
- cross compile問題
  - macでinstallしたpackageをそのままdeployしても動かない
  - lambdaが動作しているamazon linux用のbuildが必要

---

## LambdaのCost

- request
  - 1000000件/0.20 USD で、無料利用枠は 1 か月に1000000
- compute
  - 1GB-秒につき 0.00001667 USD で、無料利用枠は 400000GB-秒
  - 1GBのmemoryなら400000/86400=4.62962963なので4-5日間は絶えず処理していても無料

---

## Lambda layer

- 2018年のre:inventで発表された機能
- lambda functionを機能単位でlayerに切り出せる
- これまであったfat moduleの容量問題はこれで解決
- 1 functionにつけられるlayerは5まで

---

## Layer前

<img width="500" alt="slidescreenshot.jpeg" src="https://www.casleyconsulting.co.jp/wordpress/wp-content/uploads/2018/12/Lambda%E7%8F%BE%E7%8A%B6.png">

---

## Layer後

<img width="300" alt="slidescreenshot.jpeg" src="https://www.casleyconsulting.co.jp/wordpress/wp-content/uploads/2018/12/LambdaLayer%E4%BD%BF%E7%94%A8.png">

---

## Frameworkがやってくれること

- faasのorchestration
- faasといっても、functionだけでsystemが成立することはほぼありえない
- web applicationでもendpointだったりstorageだったりが必要になる

---

## 各種Framework

- AWS SAM
- zappa
- Apex
- serverless framework

---

## AWS SAM

- AWSが公式で提供しているFramework
- Serverless Application Model
- iaasにおけるterraformとcloudformationの関係性に近い

---

## zappa

- aws で serverless python web serviceを実現するflaskベースのOSSフレームワーク
- apigateway + LambdaのよくあるパターンをFlaskで書く
- GCPは対応していない
- 事例があんま多くなさそう

---

## Apex

- Lambdaのorchestration tool
- 面白い機能として、terraform integrationがある
  - apex infra applyでterraformを実行できる
- 事例があんま多くなさそう
- No longer maintainedと書いてありもう長くなさそう、、

---

# serverless framework

---

## serverless frameworkのいいところ

- コミュニティが強力
  - ググれば情報がたくさん出てくる
- pluginが多数あり、大抵のことはpluginを使えば実現できる
  - いざというときは自分でpluginを作ればいい
  - serverlessはnode製なのでnodeで実装
- eventから処理までがseamlessに書ける
- lambda+api gatewayというよくある組み合わせを爆速で作れる

---

# get start

```bash
$ npm install -g serverless
$ sls create -t aws-python3
```

* テンプレはいっぱいある
  * https://serverless.com/framework/docs/providers/aws/cli-reference/create/

---

## serverless frameworkのyamlに書けること

- provider
  - 環境変数
  - iam role
- cloudformationのstack
- custom
- layer
- function

---

## provider

```
provider:
  name: aws
  runtime: python3.7
  stage: ${opt:stage, self:custom.defaultStage}
  environment: ${file(config/environments/${self:provider.stage}.yml)}
  region: ${self:provider.environment.REGION}
  timeout: ${self:provider.environment.TIMEOUT, "120"}
  memorySize: ${self:provider.environment.MEMORY_SIZE, "256"}
  # bucketの設定だが、serverSideEncryptionはserverless-deployment-bucketが必要
  deploymentBucket:
    name: ${self:provider.stage}-${self:service}-deployment
    serverSideEncryption: AES256
  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "iam:ListAccountAliases"
      Resource: "*"
```

---

## function

- 呼び出す関数とその発火するeventを定義
- eventから処理までseamlessに書ける

---

## function

```yaml
# spotのeventを処理する
ec2_spot:
  handler: handlers/main.spot_notify
  events:
    - cloudwatchEvent:
        event:
          source:
            - "aws.ec2"
          detail-type:
            - "EC2 Spot Instance Interruption Warning"

# 定期的に処理する
cost_service:
  handler: handlers/main.cost_notify
  events:
    - schedule: "cron(0 0 2 * ? *)"
```

---

## function

```python

def spot_notify(event, context):
    instance_id = event.get("detail").get("instance-id")
    ...
    return


def cost_notify(event, context):
    ...
    return
```

* eventの中にserviceから送信される情報が入っている

---

## ここまで書くとlocalで関数をdebug実行可能

```
$ sls invoke local --function ${関数名} -d '${callbackされるjson}'
```

* 静的言語だとできない模様(javaとか)

---

## custom

- yaml内で使う変数の定義
  - ${self:custom.XXXXX}でyaml内で参照できる
- pluginの設定

---

## custom

```
custom:
  defaultStage: dev
  pythonRequirements:
    ...
  localstack:
    ...
  rust:
    ...

```

---

## Layer

```
layers:
  lib:
    name: ${self:service}-lib-lambda-layer-${self:provider.stage}
    path: layers/lib
    package:
      exclude:
        - 'python/lib/__pycache__/**'

func_a:
  handler: handlers/hoge.func_a
  layers:
      # ↑で独自で作ったlayer
    - {Ref: LibLambdaLayer}
      # serverless-python-requirementsが自動で作ってくれるlayer
    - {Ref: PythonRequirementsLambdaLayer}
  ...
```

---

## 鉄板のplugin

- serverless-python-requirements
  - pipの管理をうまくやってくれる
    - requirements.txtからzipを生成してくれたり
    - pipenvにも対応している
  - layer化すると便利(packageを共通化できる)
- serverless-offline
  - apigateway+lambdaに類似したセットをlocalで実行できる
- serverless-iam-roles-per-function
  - lambda関数ごとに権限を分離できる

---

## その他の使ってるplugin

- serverless-deployment-bucket
  - deployに必要なbucketの暗号化
- serverless-rust
  - serverlessでrustのcodeをbuild, deployしてくれる
- serverless-localstack
  - localstack(仮想aws)を使ったtestを実行

---

# web apiを作る際の組み合わせ例

---

# awsgiとflaskでweb api(yaml)

```yaml
functions:
  flask:
    handler: handlers/flask.handler
    events:
      - http:
          path: /{any+}
          method: ANY
```

---

# awsgiとflaskでweb api(コード)

```py
import awsgi
from flask import (
    Flask,
    jsonify,
)

app = Flask(__name__)


@app.route('/index')
def index():
    return jsonify(status=200, message='OK')

@app.route('/submit', methods=['POST'])
def submit():
    return request.get_data()

def handler(event, context):
    return awsgi.response(app, event, context)
```

---

# awsgiとflaskでweb api

* ANYにすることで、許可するpathやmethodをApplication側でハンドリングできる
* Lambda関数を量産する必要がない
* local開発ではserverless-offlineを使えばapi gatewayのendpointをlocalで起動できる
* RestのAPIを作る場合はこれが一番ラクだと思う
  * もちろんgraphqlも使える

```yaml
plugins:
  - serverless-python-requirements
  - serverless-offline
  ...
```

---

# 時間があったらdemoします