简体   繁体   English

在AWS Lambda和API网关中部署Flask sqlalchemy应用程序

[英]Deploying Flask sqlalchemy apps in AWS lambda and API gateway

I am not able to find good resources which can help me understand how can i migrate my Flask and sqlalchemy apps to AWS lambda and API gateway and make it serverless. 我找不到很好的资源来帮助我理解如何将Flask和sqlalchemy应用程序迁移到AWS lambda和API网关并使之无服务器。 Like for instance below is a sample code taken from the flask_sqlalchemy documentation : 像下面这样的示例是从flask_sqlalchemy文档获取的示例代码:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):
        return '<User %r>' % self.username

Now how can i migrate this code to AWS lambda . 现在,我如何将该代码迁移到AWS lambda。 Is it even possible . 甚至有可能。 For instance the line app = Flask(__name__) should not be there right ? 例如,行app = Flask(__name__)应该不正确吗? If there is no app variable how am i going to initialize db variable ? 如果没有应用程序变量,我该如何初始化数据库变量?

Please can someone give me some intro or a link to a good tutorial which will clear these concepts? 请有人能给我一些介绍或指向可以清除这些概念的优秀教程的链接吗?

Many thanks in advance. 提前谢谢了。

To use a Flask/sqlalchemy app with Lambda, you need to wrap Flask in the Lambda dispatch model, and make sure sqlalchemy can access its database. 要将Flask / sqlalchemy应用程序与Lambda一起使用,您需要将Flask包装在Lambda调度模型中,并确保sqlalchemy可以访问其数据库。

Dispatching Lambda requests to Flask 将Lambda请求分派到Flask

You can integrate Chalice with Flask like this: 您可以将Chalice与Flask集成在一起,如下所示:

class ChaliceWithFlask(chalice.Chalice):
    """
    Subclasses Chalice to host a Flask app, route and proxy requests to it.
    """
    def __init__(self, flask_app, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.flask_app = flask_app
        self.trailing_slash_routes = []
        routes = collections.defaultdict(list)
        for rule in self.flask_app.url_map.iter_rules():
            route = re.sub(r"<(.+?)(:.+?)?>", r"{\1}", rule.rule)
            if route.endswith("/"):
                self.trailing_slash_routes.append(route.rstrip("/"))
            routes[route.rstrip("/")] += rule.methods
        for route, methods in routes.items():
            self.route(route, methods=list(set(methods) - {"OPTIONS"}), cors=True)(self.dispatch)

    def dispatch(self, *args, **kwargs):
        uri_params = self.current_request.uri_params or {}
        path = self.current_request.context["resourcePath"].format(**uri_params)
        if self.current_request.context["resourcePath"] in self.trailing_slash_routes:
            if self.current_request.context["path"].endswith("/"):
                path += "/"
            else:
                return chalice.Response(status_code=requests.codes.found, headers={"Location": path + "/"}, body="")
        req_body = self.current_request.raw_body if self.current_request._body is not None else None
        base_url = "https://{}".format(self.current_request.headers["host"])
        query_string = self.current_request.query_params or {}
        with self.flask_app.test_request_context(path=path,
                                                 base_url=base_url,
                                                 query_string=list(query_string.items()),
                                                 method=self.current_request.method,
                                                 headers=list(self.current_request.headers.items()),
                                                 data=req_body,
                                                 environ_base=self.current_request.stage_vars):
            flask_res = self.flask_app.full_dispatch_request()
        res_headers = dict(flask_res.headers)
        res_headers.pop("Content-Length", None)
        res_body = b"".join([c for c in flask_res.response])
        return chalice.Response(status_code=flask_res._status_code, headers=res_headers, body=res_body)

flask_app = flask.Flask(app_name)
# add routes, etc. to your Flask app here
app = ChaliceWithFlask(app_name, flask_app=flask_app)

Connecting sqlalchemy to the database 将sqlalchemy连接到数据库

You could access the database directly, but that means opening the database port to the Internet or placing your Lambda in a VPC (which makes it impossible for the Lambda to be accessible over the Internet). 您可以直接访问数据库,但这意味着打开数据库端口到Internet或将Lambda放在VPC中(这使得Lambda无法通过Internet访问)。 Also, traditional database drivers make assumptions about persistence of their connections that are not satisfied in Lambda. 此外,传统的数据库驱动程序还假设其连接的持久性在Lambda中无法满足。

AWS recently came out with the perfect solution for this - the AWS Aurora RDS Data API. AWS最近为此提出了一个完美的解决方案-AWS Aurora RDS Data API。 It's basically an AWS-authenticated SQL-over-HTTP tunnel. 它基本上是一个AWS认证的HTTP over HTTP隧道。 I wrote a SQLAlchemy adapter for it: sqlalchemy-aurora-data-api . 我为此编写了一个SQLAlchemy适配器: sqlalchemy-aurora-data-api After installing it, you can do: 安装后,您可以执行以下操作:

from sqlalchemy import create_engine

cluster_arn = "arn:aws:rds:us-east-1:123456789012:cluster:my-aurora-serverless-cluster"
secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:MY_DB_CREDENTIALS"

app = Flask(app_name)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+auroradataapi://:@/my_db_name'
engine_options=dict(connect_args=dict(aurora_cluster_arn=cluster_arn, secret_arn=secret_arn))
db = flask_sqlalchemy.SQLAlchemy(app, engine_options=engine_options)

First of all, in AWS Lambda, you don't use your Flask for routing anymore. 首先,在AWS Lambda中,您不再使用Flask进行路由。 Instead use AWS API Gateway for routing. 而是使用AWS API Gateway进行路由。 An example of the routing is shown below, from https://apievangelist.com/2017/10/23/a-simple-api-with-aws-dynamodb-lambda-and-api-gateway/ 下面显示了路由示例, 网址https://apievangelist.com/2017/10/23/a-simple-api-with-aws-dynamodb-lambda-and-api-gateway/

https://apievangelist.com/2017/10/23/a-simple-api-with-aws-dynamodb-lambda-and-api-gateway/

As you can see at the right end of the picture, the "Lambda" box shows the name of the Lambda function you have uploaded. 如您在图片的右端看到的,“ Lambda”框显示了您已上传的Lambda函数的名称。 For Lambda in Python, see https://docs.aws.amazon.com/lambda/latest/dg/python-programming-model-handler-types.html 有关Python中的Lambda,请参见https://docs.aws.amazon.com/lambda/latest/dg/python-programming-model-handler-types.html

Basically, the main thing in Python lambda is the: 基本上,Python lambda的主要功能是:

def handler_name(event, context): 
   ...
   return some_value

From the event and context you can get everything: path, HTTP method, headers, params, body, etc (like flask.request ). 从事件和上下文中,您可以获得所有信息:路径,HTTP方法,标头,参数,正文等(例如flask.request )。 You might also need to know there are two ways of doing Lambda the LAMBDA and LAMBDA_PROXY (see the Integration Request box in the first picture). 您可能还需要知道执行Lambda LAMBDALAMBDA_PROXY的 两种方法(请参见第一张图片中的Integration Request框)。

Short version difference is: 简短的版本差异是:

  • LAMBDA mode will preprocess request body automatically and gives your Lambda function a Python object in event . LAMBDA模式将自动预处理请求主体,并在event为Lambda函数提供一个Python对象。
  • LAMBDA_PROXY will give you raw HTTP request, you need to convert the content yourself inside the Lambda function. LAMBDA_PROXY将给您原始的HTTP请求,您需要在Lambda函数内部自己转换内容。

As for SQL Alchemy, all you need to do is to zip all the SQL Alchemy library code and its' dependency together with your Lambda function and upload it to the Lambda Console, it works without any modification. 对于SQL Alchemy,您所需要做的就是将所有 SQL Alchemy库代码及其依赖项与Lambda函数一起压缩,然后将其上传到Lambda控制台,它无需任何修改即可工作。

Please note that SQLite will not work in Lambda, as Lambda function has no filesystem access. 请注意,由于Lambda函数无法访问文件系统,因此SQLite 无法在Lambda中运行。 You should put the data somewhere else, eg Amazon RDS (with MySQL, PostgreSQL, whatever you like) and then make sure the Lambda is connected to the same VPC (Amazon internal router) with the RDS database. 您应该将数据放在其他地方,例如Amazon RDS(使用MySQL,PostgreSQL,随便使用什么),然后确保Lambda与RDS数据库连接到同一VPC(Amazon内部路由器)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM