基于 SFF 一站式 Nodejs 服务管理平台实践
The following article is from 58技术 Author 杨益良
导语
本文阐述了基于Serverless搭建一站式node服务管理平台过程中,在开发框架、日志、监控、部署等方面遇到的问题及技术方案。
背景
1、node服务无法根据负载弹性伸缩,突发流量大时有雪崩风险。
2、node服务无法平滑高效迁移Serverless函数型服务。
3、全栈项目无统一规范,无统一构建部署流程及工具。
4、node服务运维困难,无完整监控、性能分析、故障排查手段。
让开发者专注业务开发,提升开发和运维效率。本平台整体功能结构如下:
全栈工程开发方案
1、前端工程与后端工程分别存放在client与server文件夹中,拥有独立的package.json文件,优点是对于依赖的npm包及运行时的文件依赖可以做到解耦合。
2、node端开发了静态资源插件egg-wb-static,该插件能够实现前后端工程无需mock数据或者mock api请求,达到开发阶段即联调的效果。
3、结合 npm script + 构建部署命令 + 不同部署环境的环境变量 + docker镜像,实现了构建阶段的依赖安装、打包,产出上线静态资源包+部署镜像。
前端工程化方案
方案一:
全局脚手架负责:初始化项目骨架。
项目中负责:
1) webpack工程化配置。
2) 构建依赖:babel相关、资源loader相关、webpack插件...
3) 运行依赖: vue、vue-router、vuex...
各项目独立,构建不依赖全局脚手架。
缺点:
脚手架只起到初始化项目骨架的功能,无法统一升级项目构建依赖及工程化配置。
方案二:
全局脚手架负责:
1) 初始化项目骨架。
2) webpack工程化配置。
3) 构建依赖:babel相关、资源loader相关、webpack插件...
项目中负责:
简洁工程化配置。
运行时依赖: vue、vue-router、vuex...
可以统一升级构建依赖及工程化配置。
缺点:
1) 多项目依赖不同版本全局脚手架问题。
2) 构建依赖在全局 很多babel-loader less-loader的模块查找机制 都是在当前执行命令的目录进行,需要通过hack的方式实现在全局脚手架的依赖中查找。
3) 影响webpack打包,在文件内容不变情况下,构建文件内容不一致。webpack打包时会对每一个模块进行包装都是与文件的位置相对于当前项目根目录的位置相关,所以如果项目和全局脚手架的之前的相对路径改变 即使代码没有改动,打包出的文件的hash值也会不同。
方案三:
结合方案一和方案二的优点,我们需要实现的是,可以统一升级项目构建依赖及工程化配置,并且各项目不依赖全局脚手架。
全局脚手架负责:初始化项目骨架。
构建集合包负责:
1) 构建依赖:babel相关、资源loader相关、webpack插件...
2) webpack工程化配置。
项目负责:
1) 运行时依赖: vue、vue-router、vuex...
2) 构建依赖:构建集合包。
3) 简洁工程化配置。
全局脚手架只负责初始化项目骨架,把管理构建依赖和工程化配置的功能单独抽离为构建集合包放入项目依赖中。
1) 可以方便用户统一升级构建及工程化配置。
2) 各项目之前无相互影响。
3) 所有依赖都在项目node_modules下,不影响webpack打包。
日志处理
日志的处理对于web服务至关重要,面对的问题是如何标准化用户的日志输出?如何在不影响业务的情况下进行准实时上报?如何能够让用户通过更方便的查询日志?SFF平台提供了如下解决方案:
1、提供日志输出模块。该模块负责标准化用户的日志输出格式,用户引用该模块进行日志的打印,格式如下:
3、日志收集
docker容器接管node服务标准输出流,将服务日志打到宿主机器。 宿主机器上Fluentd将日志收集到日志服务器。 再由logstash消费到Elasticsearch。 用户可以通过kibana对日志进行多方位查询。
进程监控
1、监控数据的收集
1) 内存占用
node的process模块提供了memoryUsage方法,返回 Node.js 进程的内存使用情况的对象,该对象每个属性值的单位为字节。
rss 是驻留集大小, 是给这个进程分配了多少物理内存,这些物理内存中包含堆、代码段、以及栈。
heapTotal:堆占用的内存,包括用到的和没用到的。
heapUsed:用到的堆的部分。
external 代表 V8 管理的,绑定到 Javascript 的 C++ 对象的内存使用情况。
cpu使用率 = 单位时间内cpu的使用时间/单位时间。
可以通过定时器 + process.cpuUsage返回单位时间内cpu的使用时间,但是这里注意单位时间的计算不能采用设置的定时器间隔时间,一方面存在程序运行是定时器执行延时,另一方面定时器时间不精确,因为process.cpuUsage返回的时间精确到微秒(百万分之一秒)。需要使用process.hrtime返回定时器执行精确的时间间隔。
3) cpu profile收集与火焰图展示
采用v8-profiler进行cpu profile 数据的收集,每秒采样次数在700次上下,该工具可记录每次采样时正在执行的函数及其调用栈,以树状结构的形式进行返回,其节点的关键数据结构如下:
{
"functionName": "(root)", // 函数名称
"url": "", // 文件位置
"lineNumber": 0, // 函数所在行数
"hitCount": 0, //采集次数
"id": , 节点id
children: [] // 子函数调用节点
}
depth:节点深度 = 树的深度。
hitCount:节点的采集次数 = 所有自己子节点采集次数+当前节点采集次数。
execTime:执行时间 = 该节点的采集次数 * 每次采集的单位时间。
percentage:执行占比 = 该节点的采集次数/该节点的父节点采集次数。
最后利用,用svg进行火焰图的渲染,每一个节点为一个渲染单元,按照节点深度和执行占比确定svg元素的定位与尺寸,深度为0的节点为火焰图最底层。
2、监控数据上报
以上阐述了监控数据的收集,下一个问题是数据的上报。我们的需求是用户能够监控部署在SFF平台上的node进程实例,每一个进程的cpu和内存情况,并可以指定进程进行内存快照和cpu profile的截取和分析。针对以上需求我们做了如下方案实现:
1) 数据的传输方式采用node进程与SFF服务建立TCP连接。
2) node进程的标识:docker机器名称 + 应用名称 + 进程id。
3) 存活的node进程维护在redis中,供用户查询。
4) SFF服务定时对存活进程下发采集cpu和内存的指令,并将数据上传到mongodb中存储(数据量小,保留2天)。
5) 内存快照与cpu profile 上传到公司云存储服务,保存文件地址到mongodb。
看似已经实现了功能,但是当SFF服务同时作为HTTP server和TCP server,并多实例(node多进程与多机器部署)部署时遇到了问题。用户发送的采集快照指令可能发送不到node进程中,原因如下图:
总结问题出现的原因有如下几点:
1) SFF平台多实例部署。
2) http命令连接的SFF服务实例是不确定的。
3) node进程连接的SFF服务实例是不确定的。
4) node进程的socket对象是不可在SFF实例间共享。
采用解决方案:利用Redis的订阅发布机制实现SFF服务间的通信,SFF实例启动时进行事件订阅,HTTP收集命令触发Redis事件发布,订阅的事件中判断指定node进程的socket是否在当前实例,如果在则发送tcp指令。
一键部署
SFF平台对于完整的全栈项目上线过程分为:镜像构建、部署静态资源、部署node服务,平台提供图形化手工操作流程。但如果在命令行中执行一条命令就自动化的完成以上步骤,就能够为开发者节省大量时间。
实现思路是在命令行工具中按流程调用SFF平台提供的接口,但需要解决的问题是识别用户身份,校验用户权限,因为SFF平台的接口通过cookie中信息识别用户身份,但是命令行中无法带cookie信息,也无法跳转登录界面。采用的解决方案是用户可以在平台上生成独一无二的一串密钥,并将密钥与用户身份存入mongodb中,用户可随时更新。本地命令行工具提供设置密钥的命令,设置密钥后,一键部署命令的请求都将带着密钥,SFF服务的校验权限中间件中增加对密钥的解析,进行身份认证。
Serverless方案对比
目前业内还没有serverless的统一标准,函数即服务在一些场景下有其优势,但是对于传统node开发的web服务来说,第一改造迁移成本巨大,第二开发维护成本和资源占用也会比统一的web服务成本高。所以SFF在Serverless基础上,实现的是web服务型函数,已有项目可以低成本快速迁移,不增加开发维护成本。
2、冷启动问题
Serverless是按需使用,弹性伸缩,理论上当函数访问频率低会被回收,再次被访问时,重新启动服务,目前各大云平台对于函数的访问都有一定冷启动时间。考虑到web服务的特殊性,SFF针对web服务型函数,默认维持1个实例的存活,解决web服务冷启动问题。
3、日志监控问题
各大云函数平台都实现独立实现了函数日志查询和函数调用监控,SFF针对web服务定义了标准的日志格式,结合公司内ELK日志解决方案,针对node服务定制了监控和性能分析手段。
总结
作者简介
杨益良,TEG基础体验技术部资深前端工程师,先后负责58集团Serverless、帮帮商家版、微聊、音视频等相关工作。
如有 Nodejs 相关企业实践,欢迎向公众号「Nodejs技术栈」投稿,Nodejs 企业实践集锦,点击下方 “阅读原文” 查看。