前言

本文作者:远哥制作

一、什么是 Serverless Framework

Serverless Framework 是业界十分受欢迎的无服务器利用框架,开发者无需关怀底层资源即可部署残缺可用的 Serverless 利用架构。Serverless Framework 具备资源编排、主动伸缩、事件驱动等能力,笼罩编码、调试、测试、部署等全生命周期,帮忙开发者通过联动云资源,迅速构建 Serverless 利用

没错,就像几天前看到的《Serverless 之歌》外面所说 I'm gonna reduce your ops,它能大幅度加重运维压力,那就开始入手吧!留神开发环境需 Node.js 10.0+,一键全局装置:npm install -g serverless

二、腾讯云 Flask Serverless Component 简介

腾讯云 Flask Serverless Component,反对 Restful API 服务的部署

依照常规首先来部署 demo

  1. 本地 PyCharm 创立一个新的 Flask 我的项目

  1. 手动创立内容为 Flaskrequirements.txt
  2. 依照配置文档创立 serverless.yml,例如本我的项目理论应用的残缺内容,首次应用可自行酌情简化
  3. 将密匙写入 .env(当然,部署的时候也能够抉择微信扫码受权)
TENCENT_SECRET_ID=<rm>TENCENT_SECRET_KEY=<rm>

这样基于 ServerlessFlask Demo 就部署实现了,接下来持续依照本人的形式写剩下的代码。

三、maimai_DX

maimai 是一款街机音游。

在这里放一张动图自行领会一下,原始素材来自「外录 maimai」QZKago Requiem Re:MASTER ALLPERFECT Player: Ruri*R

  • 日本官网
  • 海内官网

在国内,只能从微信公众号中查看问题,而且每次进页面都须要微信的受权登录,并且外面存储的记录有条数限度,相册只存最新 10 条,游戏记录只存最新 50 条(就是一个队列,先进先出的那种)。这就是本我的项目的初衷,本人打进去的每一次问题都应该保留好。

舞萌查分器

成绩展现了,前端 Fomantic-UI,后端 Flask+MySQLgh 开源地址:https://github.com/yuangezhizao/maimai_DX_CN_probe,欢送 watchstarfork & pr

目前实装了如下性能:

  1. wechat_archive中蕴含 主页游戏数据相册游戏记录:对原始网页进行了批改,并且增加了 Highcharts 库可视化曲线显示变动
  2. record蕴含 记录(分页)差别(分页):即自写的疾速预览页面,是查看历史记录和问题变动的十分实用的性能
  3. info蕴含 铺面列表:即全副铺面根底信息,输入到一个页面中,不便页面内搜寻

开发过程

接下来将依照工夫的程序,形容一下开发过程中遇到的问题以及如何解决

1. Serverless Framework Component 配置文件

Serverless Framework 当初是 V2 版本,也就是说不能因循之前版本的 serverless.yml 配置文件,须要从新对照文档批改。

a. 之前版本会依据 requirements.txt 主动下载第三方库到我的项目目录下的 .serverless 文件夹下的 requirements 文件夹以加入最终的依赖打包,压缩成 zip 文件再最终上传至云函数运行环境

b. 最新版本不再主动下载,须要自行处理。官网示例的参考用法:hook

  src:    # TODO: 装置python我的项目依赖到我的项目当前目录    hook: 'pip3 install -r requirements.txt -t ./requirements'    dist: ./    include:      - source: ./requirements        prefix: ../ # prefix, can make ./requirements files/dir to ./    exclude:      - .env      - 'requirements/**'

正文写的很分明,应用 hook 去依据 requirements.txt 下载第三方库到我的项目目录下的 requirements 文件夹,防止第三方库导致本地文件夹管理混乱。而后 include 中指定了我的项目目录下的 requirements 文件夹在云端的 prefix,即对于云端的云函数运行环境,requirements 文件夹中的第三方库和我的项目目录是同级的,能够失常导入应用。当然了,本地运行应用的是全局的第三方库,并未用到我的项目目录下的 requirements 文件夹。

2. 层治理概述

前者(指 b)是一个很正当的设计,不过在理论环境中却发现了新的问题。完全一致的配置文件

  src:    hook: 'pip3 install -r ./src/requirements.txt -t ./src/requirements'    dist: ./src    include:      - source: ./requirements        prefix: ../    exclude:      - .env

在 macOS 下胜利部署之后,云端的云函数编辑器中看到 requirements 文件夹不存在,第三方库和我的项目目录是同级的,确实没问题。

不过在 Windows 下胜利部署之后,云端的云函数编辑器中看到了 requirements 文件夹?也就是说第三方库和我的项目目录非同级,于是拜访就会呈现 no module found 的导入报错了……

重复尝试批改 prefix 等配置项到最初也没有调试胜利,因而在这里提出两种解决办法:

a. 批改配置文件如下,让本地的第三方库和我的项目目录同级存在

  src:    hook: 'pip3 install -r ./src/requirements.txt -t ./src'    dist: ./src    exclude:      - .env

不过随着我的项目和第三方库的扩充文件夹会越来越多,十分不便于管理

b. 应用云函数提供的

尽管 sls deploy 部署的速度很快,然而如果能够在部署时只上传我的项目代码而不去解决依赖不就更好了嘛,这样跨终合作端开发只须要关怀我的项目代码就 ok 了,再也不须要治理依赖!

并且还有一点,想在 SCF 控制台中在线编辑函数代码须要将部署程序包放弃在 10MB 以下,不要认为十兆很大,很快就用光也是可能的

具体如何操作呢?那就是要将第三方库文件夹间接打包并创立为层,则在函数代码中可间接通过 import 援用,毕竟有些非凡库比方 Brotli,Windows 下没有 vc++ 的话就只能去https://lfd.uci.edu/~gohlke/pythonlibs下载 wheel 装置。

macOS 下失常装置之后会失去 _brotli.cpython-39-darwin.sobrotli.py 中再以 import _brotli 的模式导入,不过又出新问题了,云端会导入报错ModuleNotFoundError: No module named '_brotli'"

以后 SCF 的执行环境建设在以下根底上:规范 CentOS 7.2

为了解决问题尝试在 linux 环境下打包,拿起手头的 CentOS 8.2 云主机开始操作

pip3 install -r requirements.txt -t ./layer --upgradezip -r layer.zip ./layer

而后就能够把打包的 layer.zip 下载到本地再传上去了,临时能够一劳永逸了。

对了,配置文件能够移除 hook 并增加 layers

  src:    src: ./src    exclude:      - .env      - '__pycache__/**'  layers:    - name: maimai_DX_CN_probe      version: 3
已绑定层的函数被触发运行,启动并发实例时,将会解压加载函数的运行代码至 /var/user/ 目录下,同时会将层内容解压加载至 /opt 目录下。若需应用或拜访的文件 file,搁置在创立层时压缩文件的根目录下。则在解压加载后,可间接通过目录 /opt/file 拜访到该文件。若在创立层时,通过文件夹进行压缩 dir/file,则在函数运行时需通过 /opt/dir/file 拜访具体文件

体验更快的部署速度吧!因为第三方库曾经打包在“层”中了

然而奇怪的是,在云端导入任意第三方库均会报错,于是调试着查看 path

for path in sys.path:    print(path)/var/runtime/python3/var/user/opt/var/lang/python3/lib/python36.zip/var/lang/python3/lib/python3.6/var/lang/python3/lib/python3.6/lib-dynload/var/lang/python3/lib/python3.6/site-packages/var/lang/python3/lib/python3.6/site-packages/pip-18.0-py3.6.egg

再查看 opt

import osdirs = os.listdir('/opt')for file in dirs:   print(file)layer

这才豁然开朗,打包时须要在以后门路间接打包。上传之后“层”更新为版本 2,然而 ModuleNotFoundError: No module named '_brotli' 报错仍旧,并且确认 _brotli.cpython-38-x86_64-linux-gnu.so 文件理论存在。

而在 CentOSmacOS 上本地导入均没有问题,这可就犯难了,又想到很有可能是 python 版本的问题,于是去寻找现成 3.6 的环境,比方这里:

再再次上传之后“层”更新为版本 3,拜访胜利!课题终于解决,原来是须要雷同版本Python 3.6 运行环境

3.自定义入口文件

components源码tencent-flask/src/_shims/中的文件每次都会被一成不变地从新打包上传到云端云函数中,目前有两个文件

a. severless_wsgi.py,作用是 converts an AWS API Gateway proxied request to a WSGI request.
WSGI的全称是Python Web Server Gateway InterfaceWeb 服务器网关接口,它是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简略而通用的接口

b. sl_handler.py,就是默认的入口文件

import app  # Replace with your actual applicationimport severless_wsgi# If you need to send additional content types as text, add then directly# to the whitelist:## serverless_wsgi.TEXT_MIME_TYPES.append("application/custom+json")def handler(event, context):    return severless_wsgi.handle_request(app.app, event, context)

针对于本人的我的项目,应用了 Flask工厂函数,为了防止每次都要在云端云函数编辑器中从新批改,最好的办法是自定义入口文件:

import severless_wsgifrom maimai_DX_CN_probe import create_app  # Replace with your actual application# If you need to send additional content types as text, add then directly# to the whitelist:## serverless_wsgi.TEXT_MIME_TYPES.append("application/custom+json")def handler(event, context):    return severless_wsgi.handle_request(create_app(), event, context)

再指定 执行办法serverless_handler.handler,就 ok 了

4. url_for 输入 http 而非 httpsURL

在视图函数中重定向到 url_for 所生成的链接都是 http,而不是 https……其实这个问题 Flask 的文档 Standalone WSGI Containers有形容到

说到底这并不是 Flask 的问题,而是 WSGI 环境所导致的问题,举荐的办法是应用中间件,官网也给出了 ProxyFix

from werkzeug.middleware.proxy_fix import ProxyFixapp.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)

然而是从X-Forwarded-Proto中取的值,apigw中其为http,因而并不能间接应用这个ProxyFix
因为Flask的社区还算欠缺,参考资料很多前人都铺好了路,所以间接去Stack Overflow搜解决办法,Flask url_for generating http URL instead of https
问题呈现的起因如图:Browser ----- HTTPS ----> Reverse proxy(apigw) ----- HTTP ----> Flask
因为本人在apigw设置了前端类型https,也就是说Browser端是不可能应用http拜访到的,通过打印environ可知

{  "CONTENT_LENGTH": "0",  "CONTENT_TYPE": "",  "PATH_INFO": "/",  "QUERY_STRING": "",  "REMOTE_ADDR": "",  "REMOTE_USER": "",  "REQUEST_METHOD": "GET",  "SCRIPT_NAME": "",  "SERVER_NAME": "maimai.yuangezhizao.cn",  "SERVER_PORT": "80",  "SERVER_PROTOCOL": "HTTP/1.1",  "wsgi.errors": <__main__.CustomIO object at 0x7feda2224630>,  "wsgi.input": <_io.BytesIO object at 0x7fed97093410>,  "wsgi.multiprocess": False,  "wsgi.multithread": False,  "wsgi.run_once": False,  "wsgi.url_scheme": "http",  "wsgi.version": (1, 0),  "serverless.authorizer": None,  "serverless.event": "<rm>",  "serverless.context": "<rm>",  "API_GATEWAY_AUTHORIZER": None,  "event": "<rm>",  "context": "<rm>",  "HTTP_ACCEPT": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",  "HTTP_ACCEPT_ENCODING": "gzip, deflate, br",  "HTTP_ACCEPT_LANGUAGE": "zh-CN,zh;q=0.9,en;q=0.8",  "HTTP_CONNECTION": "keep-alive",  "HTTP_COOKIE": "<rm>",  "HTTP_ENDPOINT_TIMEOUT": "15",  "HTTP_HOST": "maimai.yuangezhizao.cn",  "HTTP_SEC_FETCH_DEST": "document",  "HTTP_SEC_FETCH_MODE": "navigate",  "HTTP_SEC_FETCH_SITE": "none",  "HTTP_SEC_FETCH_USER": "?1",  "HTTP_UPGRADE_INSECURE_REQUESTS": "1",  "HTTP_USER_AGENT": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36",  "HTTP_X_ANONYMOUS_CONSUMER": "true",  "HTTP_X_API_REQUESTID": "5bcb29af2ca18c1e6d7b1ec5ff7b5427",  "HTTP_X_API_SCHEME": "https",  "HTTP_X_B3_TRACEID": "5bcb29af2ca18c1e6d7b1ec5ff7b5427",  "HTTP_X_QUALIFIER": "$LATEST"}

HTTP_X_FORWARDED_PROTO对应apigw里的变量是HTTP_X_API_SCHEME,故解决办法如下:app.wsgi_app = ReverseProxied(app.wsgi_app)

class ReverseProxied(object):    def __init__(self, app):        self.app = app    def __call__(self, environ, start_response):        scheme = environ.get('HTTP_X_FORWARDED_PROTO')        if scheme:            environ['wsgi.url_scheme'] = scheme        return self.app(environ, start_response)app = Flask(__name__)app.wsgi_app = ReverseProxied(app.wsgi_app)

5. 响应数据压缩

不论是IISApache还是Nginx,都提供有压缩性能。毕竟本人在用的云主机外网上行只有1M带宽,压缩后对于缩短首屏工夫的成果晋升极为显著。对于Serverless,响应数据是通过API Gateway传输到客户端,那么压缩也应该是它所具备的能力(尽管外网速度大幅度提高,然而该压缩还是得压缩),然而并没有找到……看到某些js框架原生有提供压缩性能,于是打算增加Flask自行压缩的性能。简略来讲,通过订阅@app.after_request信号并调用第三方库brotlicompress办法即可(
在写之前去gh上看看有没有现成的轮子拓展,果然有……刚开始用的是Flask-Zipper,起初换成Flask-Compress解决了问题
实测3.1 MB的数据采纳brotli压缩算法减至76.1 kB

6. apigw 三种环境不同门路所产生的影响

默认的映射如下:

ID环境名拜访门路
1公布release
2预公布prepub
3测试test

因为配置的static_url_path"",即static文件夹是映射到/门路下的,所以再加上releaseprepubtest拜访就天然404
因而绑定了自定义域名应用自定义门路映射,并将公布环境的拜访门路设置成/,这样再拜访公布环境就没有问题了

ID环境名拜访门路
1公布/
2预公布prepub
3测试test

7. 同时拜访公有网络外网

云函数中能够利用到的云端数据库有如下几种

  • 云数据库CDB,须要公有网络拜访,尽管能够通过外网拜访然而能走内网就不走外网
  • PostgreSQL for Serverless(ServerlessDB),这个是官网给Serverless配的pg数据库
  • 云开发TCB中的MongoDB,没记错的话须要开明内测权限拜访

因为本人是从旧网站迁徙过去的,数据临时还没有迁徙,因而间接拜访原始云数据库CDB,在云函数配置所属网络所属子网即可。然而此时会无法访问外网,一种解决办法是开启公网拜访公网固定IP,就能够同时拜访内网和外网资源了。对于配置文件,本我的项目是单实例利用也就是说我的项目中只引入一个组件,部署时只生成一个组件实例。然而如果想引入数据库的话,就得新增组件了,目前在Flask Components中并没有提供数据库相干的配置项,因而须要我的项目中引入多个组件,部署时生成多个组件实例。也很简略,创立一个含有serverless.yml的新文件夹,用来配置postgresql

component: postgresql # (必填) 组件名称,此处为 postgresqlname: maimai_DX_CN_probe # (必选) 组件实例名称.org: yuangezhizao # (可选) 用于记录组织信息,默认值为您的腾讯云账户 appid,必须为字符串app: yuangezhizao # (可选) 用于记录组织信息. 默认与name雷同,必须为字符串stage: dev # (可选) 用于辨别环境信息,默认值是 devinputs:  region: ap-beijing # 可选 ap-guangzhou, ap-shanghai, ap-beijing  zone: ap-beijing-3 # 可选 ap-guangzhou-2, ap-shanghai-2, ap-beijing-3  dBInstanceName: maimai_DX_CN_probe  #  projectId: 0  dBVersion: 10.4  dBCharset: UTF8  vpcConfig:    vpcId: vpc-mrg5ak88    subnetId: subnet-hqwa51dh  extranetAccess: false

而后在终端cd到这个目录再执行sls deploy即可胜利部署postgresql

yum install python3-devel postgresql-develpip install psycopg2

后果

import psycopg2File "/opt/psycopg2/__init__.py", line 51, in &lt;module&gt;from psycopg2._psycopg import (                     # noqaImportError: libpython3.6m.so.1.0: cannot open shared object file: No such file or directory

下列问题处于解决之中:

  • http 强制跳转 https
  • 测试环境推送至生产环境

至此,本文就完结了,欢送交换!

One More Thing

立刻体验腾讯云 Serverless Demo,支付 Serverless 新用户礼包 ???? serverless/start

欢送拜访:Serverless 中文网!