Labelhub 是一家致力于为人工智能企业提供完善的数据与模型解决方案公司,可以帮助 AI 企业更好的管理数据,从而提高其核心 AI 产品迭代速度,Labelhub 拥有优秀的敏捷团队,开发领域涉及机器学习、模型训练以及软件应用。目前已经与多家大中型企业进行深度合作,在行业相关比赛中也多次获奖。
Labelhub 团队在业务极速扩张的时期,选择使用腾讯云 Serverless
技术来打造一个轻量的内部运维及数据可视化系统。通过使用 Tencent Serverless Framework,基于云上 Serverless 服务(云函数及触发器等),无需配置和部署,即可快速开发一套定制化数据可视化系统,腾讯云 Serverless
技术不仅满足了业务发展的需求,而且不需要耗费太多的人力和资金成本,是 Labelhub 的不二之选。
在实践中,作者对腾讯云 Serverless 产品逐渐产生了浓厚的兴趣,希望能把自己对腾讯云 Serverless 技术的理解,以及如何使用腾讯云 Serverless 技术进行项目开发的实践经验分享给大家。
前言
我所在的团队开发了一款针对人工智能企业数据标注产品Labelhub,目前正式的销售工作处于摸索阶段,对于目标客户,产品的定位,一直没有很好进行梳理。随着业务的逐渐开展,平台的运维安全也并没有进行系统的监控管理。因此我考虑将产品的业务数据、服务器数据及应用监控数据做一个基本的内部运维及数据可视化系统。
虽然有很多的开源工具选择,但是都避免不了部署及配置,二次开发也比较麻烦,因此考虑自行开发一套简单的按内部需求完全定制化的系统。
最终,我决定使用 Serverless
来打造这样一个轻量的内部系统,Serverless
无疑是时下最热的 IT 词汇之一,作为一种新型的互联网软件产品架构,虽然早在 2012 年就被提出,但随着近几年容器技术、IoT
、区块链以及 5G技术的快速发展,Serverless
的概念也借势得以迅速发展。
它究竟能够带来什么,它的红利是否有门槛,离我们究竟有多远,值不值得现在开始投入,我会通过这一个项目的一系列文章来和大家一起探讨,从一名普通开发者的角度去看看 Serverless
。
概要说明
我希望此系列文章能够浅显易懂,我会尽可能详细的介绍我在开发这个系统的过程中,对于Serverless
的理解。通过这一系列文章,我希望能够提供给大家的内容包括:
- 如何开发一个
real world serverless app
- 对于服务拆分的理解,如何更有效的利用资源
- 现有的BaaS迁移至FaaS的设想
(如果想到更多会继续补全)
此系列文章是基于 腾讯Serverless Framework 工具,同时后端使用 Python
进行开发
项目构建
搭建后端项目结构
Serverless Framework
的文档中心里,框架支持里目前有 Flask
以及 Django
,按照文档示例中进行,会发现部署一个简单的 rest api
十分容易,整个过程如官方宣称的极速部署。 但看一下项目的结构会发现,只有一个 yml
配置文件、一个依赖文本和一个 app.py
文件,由于配置文件中会使用 hooks
参数将依赖安装到当前文件夹,这是一个非常简单的一个示例。 在搭建后端项目目录的时候,通常情况下本地开发时并不需要考虑以下两点:
- 三方库的引入
- 公共函数的调用
对于 Serverless
,则需要考虑这两个问题。
当在示例中执行 sls deploy
,部署成功后可以在控制台查看函数代码,此时会发现依赖文件也在文件列表中,这很好的解释了为什么说 一个函数是一个应用
。 但是实际开发过程中,我们不可能每一个功能模块都会去安装一次依赖,因此我们可以借助公共模块来解决这个问题。但是公共模块如何进行引入呢。 由于 Serverless cli Component v2
已经取消了 include
配置,对于 v1
可以很轻松的使用 include
配置将公共组件包含在函数中,从而各子函数能够很方便的进行调用。而对于 v2
,其实我们可以通过 Layer
来解决这个问题。
对于 v1 和 v2 的区别以及详细介绍,可参考Serverless Framework Cli的版本进化
模块的拆分,我们则可以通过应用管理中的多实例管理来进行。
项目实践
项目根目录的处理
创建项目文件夹
mkdir labelhub-dashboard
在项目根目录下应用配置文件
cd labelhub-dashboard
touch serverless.yml
在应用配置文件中只用定义应用的名称
app: labelhub-dashboard
公共文件及三方依赖目录的处理
在根目录文件夹下创建公共模块文件夹
mkdir common
我们将依赖以及公共函数放到common
中便于其他模块进行调用
# labelhub-dashboard/common
touch requirements.txt
# 创建数据库连接工具类
touch dataUtils.py
由于只是测试,也可以直接在 common
下运行 pip install pymysql -t ./
将依赖安装到当前路径下。
这里使用pymysql来连接数据库进行测试
编辑 dataUtils.py
文件:
import pymysql
class MysqlUtils:
def __init__(self):
self.getConn({
'host': 'xxx',
'user': 'xxx',
'port': 3306,
'db': 'xxx',
'password': 'xxx'
})
def getConn(self, conf):
self.connection = pymysql.connect(
host=conf['host'],
user=conf['user'],
password=conf['password'],
port=conf['port'],
db=conf['db'],
charset='utf8',
cursorclass=pymysql.cursors.DictCursor,
autocommit=1
)
def doAction(self, stmt, data):
try:
self.connection.ping(reconnect=True)
cursor = self.connection.cursor()
cursor.execute(stmt, data)
result = cursor
cursor.close()
return result
except Exception as e:
try:
cursor.close()
except:
pass
return False
准备就绪,就可以进行部署了。前面说过,因为 common
我们会作为 公共函数及三方库
存放的地方,因此我们需要用 Layer
组件来进行部署。在 common
中创建配置文件
touch serverless.yml
编辑配置文件:
component: layer # 注意,这里使用的是layer组件
name: common-layer
org: labelhub-dashboard
app: labelhub-dashboard
stage: dev
inputs:
name: commonfiles # 记住这个名字
region: ap-guangzhou
src:
src: ./
exclude:
- .env
runtimes:
- Python3.6
description: packages
然后执行 sls deploy
,Layer
层部署成功后,会出现函数的详细信息,需要注意其中version
字段的值,部署完成后我们开始创建功能模块目录。
功能模块目录的处理
在根目录下创建一个测试子模块
mkdir user-data
创建测试文件
touch index.py
编辑测试文件
from mysqlUtils import MysqlUtils
import json
db = MysqlUtils()
def get_users():
search_stmt = (
"SELECT * FROM `user` limit 100;"
)
result = db.doAction(search_stmt, ())
if result == False:
return False
return result
def main_handler(event, context):
result = get_users()
data = [{'id': user['id'], 'name': str(user['name']), 'created_at': user['created_at'].strftime('%Y-%m-%d %H:%M:%S')} for user in result.fetchall()]
return data
这里有两点需要说明:
- 明明
mysqlUtils
是在common
文件夹中,而这里却直接引入mysqlUtils
,稍后在配置文件中会说明 - 查询函数
get_users()
为何写在这里。其实也可以写在mysqlUtils.py
中,但是因为mysqlUtils.py
是在Layer
层,而Layer
层的部署目前使用情况来看,比函数组件部署要耗时更长,因此我把它放在需要使用的函数文件中。即尽量不去修改common
里的文件。
准备就绪后就可以部署函数了。首先仍然是创建配置文件:
touch serverless.yml
编辑配置文件:
component: scf # 注意,这里使用的是scf组件
name: userdata
stage: dev
app: labelhub-dashboard
org: labelhub-dashboard
#组件参数
inputs:
name: ${name}-${stage}-${app} #函数名称
src:
src: ./ #代码路径
exclude:
- .env
handler: index.main_handler #入口
runtime: Python3.6 # 云函数运行时的环境
region: ap-guangzhou # 云函数所在区域
layers:
- name: commonfiles
version: 1
events: # 触发器
- apigw: # 网关触发器
parameters:
endpoints:
- path: /
method: GET
通过配置文件,我们可以发现,其中的 layers
配置中的 name
以及 version
,就是在部署 common
时的名称和部署成功后的版本号。
最后执行 sls deploy
完成部署,直接访问生成的 url
地址,可以查看到正确的返回信息。
小结
最终文件夹的结构为:
labelhub-dashboard/
------------------serverless.yml
------------------common/
------------------------requirements.txt
------------------------serverless.yml
------------------------mysqlUtils.py
------------------user-data/
---------------------------serverless.yml
---------------------------index.py
在部署 user-data
函数时,我们看到引入 mysqlUtils
,是通过直接引入的方式,而在 user-data
函数的配置文件中可以看到我们使用了对应的 layers
配置。从这里可以看出,在函数的配置中,layers
其实就相当于 v1
中的 include
配置,默认 Layer
组件中的文件与函数文件在相同目录下。
其实我们可以将所有文件创建好后,在根目录中执行 sls deploy --all
来一次性进行部署,但在使用过程中会出现函数组件部署报错,找不到对应的 Layer
组件,这也是因为函数组件部署过程中会去读取 layers
的配置,而通过实际使用过程中发现 Layer
组件的部署几乎都会比函数组件慢很多,因此官方也是建议优先部署 Layer
组件,之后再统一部署函数组件。那么我们就需要考虑,函数的应用根目录,是否可以和公共组件目录同级,这样在使用 sls deploy --all
的时候,才可以避免同时更新 Layer 层。
至此,简单的后端开发环境就已经搭建好了,相比于官方提供的文档的示例,完善了对于实际开发过程中三方依赖和公共函数的处理,后续开发过程中若需要更多依赖,只需要在对应的组件中进行添加即可,而对于功能模块的扩展,也只需要创建相应的目录去进行扩展。
后面的文章,我会详细介绍对于单个模块功能的开发过程,更多依赖的添加进行尝试,尽可能按照实际开发过程来进一步优化项目的结构。
One More Thing
立即体验腾讯云 Serverless Demo,领取 Serverless 新用户礼包 👉 serverless/start
欢迎访问:Serverless 中文网!