1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【ServerlessFramework】slsでカンタンにAPIサーバーを公開するチュートリアル

Last updated at Posted at 2024-02-13

slsを利用してLambda x APIGatewayでカンタンにAPIサーバーを公開するチュートリアルです。

元となるリポジトリはこちら

node.jsのインストール

# nvm インストール
# https://github.com/nvm-sh/nvm#installing-and-updating
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash

# v16系のLTSをインストール
nvm install --lts=gallium

# v16系のLTSをデフォルトに設定
nvm alias default lts/gallium

# 確認
nvm list

サーバーレスフレームワークのインストール

# インストール
npm install -g serverless

# slsコマンドが利用できるか確認
sls -v

プロジェクト作成

# --template: AWSをプロバイダとしてPython3でプロジェクトを作成する場合は aws-python3 を指定
# --name: 命名規則は ^[a-zA-Z][0-9a-zA-Z-]+$
# --path: プロジェクトを作成するパスを指定します。
sls create --template aws-python3 --name sample-app --path sample-app

# プロジェクトにcd
cd sample-app

Serverless Python Requirements プラグインの追加

serverless-python-requirements はrequirements.txt で指定したライブラリをLambdaにバンドルすることができます。

# インストール
sls plugin install -n serverless-python-requirements

serverless.ymlplugins にインストールしたプラグインを設定し、pythonライブラリのビルドをコンテナで行うオプション ( dockerizePip: true )と、ライブラリをレイヤーとしてデプロイする設定 ( layer: true ) を設定します。

# --- sample-app/serverless.yml ---

# ... 略 ...
custom:
    PythonRequirements: # requirements.txt に記載したpythonライブラリをビルドする設定
        dockerizePip: true # pythonライブラリのビルドをdockerで行う設定
        layer: true # pythonライブラリをレイヤーとしてデプロイする設定
plugins:
  - serverless-python-requirements

serverless.yml の設定

package にはlambdaパッケージに含めるファイルをパターンで定義します。

# --- sample-app/serverless.yml ---
package:
  patterns:
    - '!**' # すべてのファイルをexclude
    - 'static/**'  # staticディレクトリは以下をinclude
    - 'src/**'     # srcディレクトリは以下をinclude
    - 'main.py' # main.pyをinclude

resources の配下にはCloudFormationを記述できます。
lambdaに適用するロールを resources 配下に定義します。

# --- sample-app/serverless.yml ---

resources:
  Resources:
    ApiServerRole: # ManagedPolicyArnsとPoliciesを両方利用した例
      Type: AWS::IAM::Role
      Properties:
        RoleName: SampleApp-ApiServerRole
        AssumeRolePolicyDocument:
          Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action:
                - sts:AssumeRole
              Principal:
                Service:
                  - lambda.amazonaws.com
        ManagedPolicyArns:
          - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole 
        Policies:
          - PolicyName: ApiServerPolicy
            PolicyDocument:
              Version: "2012-10-17"
              Statement:
                - Resource: "*"
                  Effect: Allow
                  Action:
                    - "s3:*"
                - Resource: "*"
                  Effect: Allow
                  Action:
                    - "dynamodb:*"
                    - "dax:*"

functions にlambda関数の設定を記述します。

今回はAPIGatewayを利用してAPIサーバーとしてlambdaを利用するので、EventにはAPIGatewayの設定を記述します。
APIGatewayの設定方法は HTTP APIREST API の2種類が用意されていますが、今回は REST API を利用します。
REST APIとHTTP APIの違い

# --- sample-app/serverless.yml ---

functions:
  api:
    handler: main.handler  # main.pyのhandlerをハンドラとして利用する
    timeout: 29
    role: ApiServerRole  # resources配下に実装したロール名
    events:
      # すべてのメソッド・リクエストを受け取る
      - http:
          path: /
          method: ANY
          cors: true  # CORSを許可する
      - http:
          path: /{path+}
          method: ANY
          cors: true
    environment:  # 環境変数の定義
      API_GATEWAY_BASE_PATH: "/${self:provider.stage}"  # provider.stage の値を利用
    layers:
      # serverless-python-requirementsプラグインで作成したLayerを利用する設定を記述
      # https://www.serverless.com/plugins/serverless-python-requirements#lambda-layer
      - Ref: PythonRequirementsLambdaLayer

provider にawsの設定を記述しましょう

# --- sample-app/serverless.yml ---

provider:
  name: aws
  runtime: python3.9
  stage: ${opt:stage, "dev"}  # ステージ名 (--stageオプションがなければdev)
  region: "ap-northeast-1"  # デプロイするリージョン
  deploymentBucket:
    name: "sls-deploy-gcappr58"  # デプロイアーティファクトを保存するs3バケット
  apiGateway:  # API Gateway v1 REST APIの設定
    # バイナリファイルを返却できるようにする設定
    binaryMediaTypes:
      - '*/*'

アプリ実装

requirements.txtの作成

cat <<EOF > requirements.txt
fastapi~=0.99.1
python-jose[cryptography]~=3.3.0
passlib[bcrypt]~=1.7.4
alembic~=1.11.1
PyMySQL~=1.1.0
SQLAlchemy~=2.0.17
mangum~=0.17.0
boto3~=1.27.0
EOF

アプリの実装

rm handler.py
touch main.py  # serverless.ymlのfunctions.api.handlerに指定したファイル

# lambda関数にインクルードされるファイルを作成
mkdir src static
touch src/env.py static/index.html
# --- sample-app/main.py ---

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from mangum import Mangum

from src.env import get_env

app = FastAPI(
    redoc_url="/api/redoc",
    docs_url="/api/docs",
    openapi_url="/api/docs/openapi.json",
    root_path=get_env().api_gateway_base_path,
)
allow_origins = ["*"]


@app.get("/api/hello")
def hello():
    env = get_env()
    return {"hoge": env.api_gateway_base_path}

app.mount("/", StaticFiles(directory=f"./static", html=True), name="static")

handler = Mangum(app)
# --- sample-app/src/env.py ---

from functools import lru_cache
from pydantic import BaseSettings

class Environment(BaseSettings):
    api_gateway_base_path: str = "/dev"

@lru_cache
def get_env() -> Environment:
    return Environment()
<!-- *** sample-app/static/index.html *** -->

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>Hello World</h1>
</body>
</html>

デプロイ

# デプロイ
sls deploy --stage prd

# 出力されたURLの /prd/api/docs にアクセスしてみましょう
# https://xxxxxxxxxxxx/prd/api/docs

# 削除
sls remove --stage prd

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?