前言
本文作者:远哥制作
一、什么是 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
吧
- 本地
PyCharm
创立一个新的Flask
我的项目
- 手动创立内容为
Flask
的requirements.txt
- 依照配置文档创立
serverless.yml
,例如本我的项目理论应用的残缺内容,首次应用可自行酌情简化 - 将密匙写入
.env
(当然,部署的时候也能够抉择微信扫码受权)
TENCENT_SECRET_ID=<rm>
TENCENT_SECRET_KEY=<rm>
这样基于 Serverless
的 Flask Demo
就部署实现了,接下来持续依照本人的形式写剩下的代码。
三、maimai_DX
maimai 是一款街机音游。
在这里放一张动图自行领会一下,原始素材来自「外录 maimai」QZKago Requiem Re:MASTER ALLPERFECT Player: Ruri*R
- 日本官网
- 海内官网
在国内,只能从微信公众号中查看问题,而且每次进页面都须要微信的受权登录,并且外面存储的记录有条数限度,相册
只存最新 10 条,游戏记录
只存最新 50 条(就是一个队列,先进先出的那种)。这就是本我的项目的初衷,本人打进去的每一次问题都应该保留好。
舞萌查分器
成绩展现了,前端 Fomantic-UI
,后端 Flask
+MySQL
。gh
开源地址:https://github.com/yuangezhizao/maimai_DX_CN_probe,欢送 watch
、star
、fork
& pr
!
目前实装了如下性能:
- wechat_archive 中蕴含
主页
,游戏数据
,相册
和游戏记录
:对原始网页进行了批改,并且增加了Highcharts
库可视化曲线显示变动 - record 蕴含
记录(分页)
和差别(分页)
:即自写的疾速预览页面,是查看历史记录和问题变动的十分实用的性能 - 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.so
,brotli.py
中再以 import _brotli
的模式导入,不过又出新问题了,云端会导入报错ModuleNotFoundError: No module named '_brotli'"
以后
SCF
的执行环境建设在以下根底上:规范CentOS 7.2
为了解决问题尝试在 linux 环境下打包,拿起手头的 CentOS 8.2
云主机开始操作
pip3 install -r requirements.txt -t ./layer --upgrade
zip -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 os
dirs = os.listdir('/opt')
for file in dirs:
print(file)
layer
这才豁然开朗,打包时须要在以后门路间接打包。上传之后“层”更新为版本 2
,然而 ModuleNotFoundError: No module named '_brotli'
报错仍旧,并且确认 _brotli.cpython-38-x86_64-linux-gnu.so
文件理论存在。
而在 CentOS
和 macOS
上本地导入均没有问题,这可就犯难了,又想到很有可能是 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 Interface
即Web 服务器网关接口
,它是为Python
语言定义的 Web
服务器和 Web
应用程序或框架之间的一种简略而通用的接口
b. sl_handler.py
,就是默认的入口文件
import app # Replace with your actual application
import 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_wsgi
from 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
而非 https
的 URL
在视图函数中重定向到 url_for
所生成的链接都是 http
,而不是 https
……其实这个问题 Flask
的文档 Standalone WSGI Containers 有形容到
说到底这并不是 Flask
的问题,而是 WSGI
环境所导致的问题,举荐的办法是应用 中间件,官网也给出了 ProxyFix
from werkzeug.middleware.proxy_fix import ProxyFix
app.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. 响应数据压缩
不论是 IIS
、Apache
还是 Nginx
,都提供有压缩性能。毕竟本人在用的云主机外网上行只有1M
带宽,压缩后对于缩短首屏工夫的成果晋升极为显著。对于 Serverless
,响应数据是通过API Gateway
传输到客户端,那么压缩也应该是它所具备的能力(尽管外网速度大幅度提高,然而该压缩还是得压缩),然而并没有找到……看到某些 js
框架原生有提供压缩性能,于是打算增加 Flask
自行压缩的性能。简略来讲,通过订阅 @app.after_request
信号并调用第三方库 brotli
的compress
办法即可(
在写之前去 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
文件夹是映射到 /
门路下的,所以再加上 release
、prepub
和test
拜访就天然 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 # (必填) 组件名称,此处为 postgresql
name: maimai_DX_CN_probe # (必选) 组件实例名称.
org: yuangezhizao # (可选) 用于记录组织信息,默认值为您的腾讯云账户 appid,必须为字符串
app: yuangezhizao # (可选) 用于记录组织信息. 默认与 name 雷同,必须为字符串
stage: dev # (可选) 用于辨别环境信息,默认值是 dev
inputs:
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-devel
pip install psycopg2
后果
import psycopg2
File "/opt/psycopg2/__init__.py", line 51, in <module>
from psycopg2._psycopg import ( # noqa
ImportError: 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 中文网!