Serverless架构已经越来越成熟,但很多人对它的配置还停留在基础层面,只知道写函数、部署函数,对很多高级配置和优化不了解。今天,详细聊聊Serverless的配置,从基础的函数配置,到高级的性能优化、安全配置、监控告警、成本优化等,一步步深入,帮助大家把Serverless配置到最佳状态。
先说说背景。我使用Serverless架构已经两年多了,在好几个生产项目中使用了Serverless,踩了很多坑,也积累了很多配置和优化的经验。最开始,我也只是用最基础的配置,写个函数,部署上去,能跑就行。但后来,随着项目越来越复杂,流量越来越大,各种问题也来了,比如冷启动慢、性能差、成本高、安全问题等。
于是,我开始深入研究Serverless的各种配置,从基础到高级,一步步优化,慢慢地,把函数的性能提升了,成本降下来了,安全性也提高了。今天,就把这些配置经验,分享出来。
需要说明的是,不同的Serverless平台,配置方式可能不太一样,比如AWS Lambda、阿里云函数计算、腾讯云SCF、华为云函数工作流等,各有各的配置方式。但核心的配置项和优化思路,是相通的。今天,我会以通用的方式来讲,不针对某个特定的平台,大家可以根据自己用的平台,对应到具体的配置项。
一、基础配置:把函数跑起来
首先,从最基础的配置开始,这些是把函数跑起来必须的配置。
1. 函数名称和描述
函数名称,是函数的唯一标识,在同一个平台、同一个区域内,不能重复。函数名称,要起得有意义,能看出这个函数是干什么的,比如user-login、order-create、image-process等,不要起function1、test、hello这种没有意义的名字。
函数描述,是对函数功能的简要说明,方便以后维护的时候,快速知道这个函数是干什么的。虽然描述不是必须的,但建议一定要写,尤其是函数多了之后,光看名字可能记不住,有描述就清楚多了。
2. 运行时和代码入口
运行时(Runtime),就是函数的运行环境,比如Node.js、Python、Java、Go、PHP、.NET等,不同的语言,对应不同的运行时。选择运行时的时候,要根据你的代码语言来选,同时也要考虑性能和生态。
一般来说,Node.js和Python的生态最完善,开发效率最高,适合大多数场景;Go和Rust的性能最好,冷启动最快,适合对性能要求高的场景;Java的生态也很完善,适合企业级应用,但冷启动比较慢。
代码入口,就是函数的执行入口,比如Node.js的index.handler,表示index.js文件中的handler函数。代码入口要配置正确,不然函数会找不到入口,执行失败。
3. 内存和超时时间
内存大小,是Serverless函数最重要的配置之一。Serverless平台,一般是按内存比例分配CPU的,内存越大,CPU越强,函数执行越快,但成本也越高。内存太小,函数执行慢,甚至可能内存溢出;内存太大,成本高,浪费资源。
所以,要根据函数的实际需求,设置合适的内存。可以做一些测试,看看函数在不同内存下的执行时间和成本,找到性价比最高的配置。一般来说,计算密集型的函数,需要大一些的内存;IO密集型的函数,内存可以小一些。
超时时间,就是函数的最大执行时间,超过这个时间,函数会被强制终止。超时时间要设置合理,太短的话,函数正常执行也可能被超时终止;太长的话,函数卡住的时候,会浪费资源,增加成本。
一般来说,API类的函数,超时时间可以短一些,比如3-5秒,因为用户在等着响应,太长了用户体验不好;定时任务和数据处理类的函数,超时时间可以长一些,比如5-15分钟,但也要注意,大部分平台对函数的最大执行时间有限制,比如最多15分钟,超过就不行了。
4. 触发器配置
触发器,就是触发函数执行的方式,比如API网关触发器、定时触发器、对象存储触发器、消息队列触发器、数据库触发器等。一个函数,可以配置多个触发器,不同的触发器,触发函数执行的条件不一样。
配置触发器的时候,要注意:
- API网关触发器:要配置好路径、方法、请求参数、鉴权方式等。
- 定时触发器:要配置好Cron表达式,确定触发的时间和频率。
- 对象存储触发器:要配置好触发的存储桶、事件类型(比如上传、删除)、前缀和后缀过滤等。
- 消息队列触发器:要配置好监听的队列、批量大小、并发数等。
触发器配置正确了,函数才能在正确的时机被触发执行。
5. 环境变量
环境变量,是函数运行时的环境变量,可以用来配置一些不适合硬编码在代码里的信息,比如数据库连接地址、API密钥、环境标识(开发/测试/生产)等。
用环境变量的好处是,同一份代码,可以在不同的环境中运行,只需要配置不同的环境变量就行,不需要修改代码。而且,敏感信息(比如密码、密钥)放在环境变量里,比硬编码在代码里更安全。
配置环境变量的时候,要注意:
- 敏感信息,要用平台提供的密钥管理服务加密,不要明文存储。
- 不同的环境(开发/测试/生产),配置不同的环境变量。
- 环境变量的命名,要规范,比如大写字母加下划线,比如
DBHOST、APIKEY。
二、进阶配置:提升函数的性能和稳定性
基础配置搞定了,函数能跑起来了,接下来,就是进阶配置,提升函数的性能和稳定性。
1. 预置并发和预热
前面提到过,冷启动是Serverless最常见的性能问题。解决冷启动的一个重要配置,就是预置并发(Provisioned Concurrency)。
预置并发,就是预先创建一定数量的函数实例,保持常驻,这样,请求进来的时候,直接用预置的实例,不需要冷启动。预置并发的数量,可以根据函数的请求量来设置,比如平时请求量不大,预置2-5个就够了;如果请求量比较大,可以预置更多。
预置并发虽然要收费,但对于对延迟要求高的应用来说,是非常值得的。而且,现在很多平台,都有预置并发的优惠,成本也不算太高。
如果不想用预置并发,也可以用定时预热的方式,就是配置一个定时触发器,每隔几分钟调用一次函数,让函数实例保持活跃,不会被释放。这种方式,成本比预置并发低,但稳定性不如预置并发,因为平台可能还是会释放实例,而且实例数量也不可控。
2. 层(Layer)和依赖优化
函数的代码和依赖,如果太大,会导致冷启动慢,因为加载代码和依赖需要时间。所以,要优化函数的代码和依赖,减小体积。
一个重要的优化方式,就是使用层(Layer)。层,是一种共享代码和依赖的方式,可以把公共的依赖、库、工具等,打包成一个层,然后多个函数可以共用这个层,不需要每个函数都打包一份。
使用层的好处:
- 减小函数代码包的体积,加快冷启动。
- 公共依赖只需要打包一次,多个函数共用,减少重复。
- 依赖更新的时候,只需要更新层,不需要更新每个函数。
除了使用层,还要优化依赖本身:
- 只引入需要的依赖,不要引入整个库,比如用lodash,只引入需要的函数,不要引入整个lodash。
- 移除不需要的依赖,定期检查package.json,把不用的依赖删掉。
- 对于Node.js,可以用webpack、esbuild等工具,打包代码,tree-shaking掉无用的代码,减小体积。
- 对于Python,可以用--no-deps参数,只安装需要的包,不要安装可选依赖。
3. 数据库连接池和长连接
如果函数需要访问数据库,数据库连接的管理,是一个很重要的配置。因为Serverless函数是无状态的,每次执行完,实例可能会被释放,数据库连接也会断开。如果每次函数执行,都重新建立数据库连接,会很耗时,也会给数据库造成很大的连接压力。
解决方法是使用数据库连接池,并且把连接池放在函数外面,全局初始化。这样,当函数实例被复用时,连接池也会被复用,不需要每次都重新建立连接。
比如,在Node.js中:
// 在函数外面初始化连接池
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
connectionLimit: 10, // 连接池大小
});
// 函数内部使用连接池
exports.handler = async (event) => {
const conn = await pool.getConnection();
try {
const [rows] = await conn.query('SELECT * FROM users');
return rows;
} finally {
conn.release();
}
};这样,第一次冷启动的时候,建立连接池,后面的请求复用这个连接池,性能会好很多。
另外,要注意连接池的大小,不要设置得太大,因为函数实例可能有很多个,每个实例都有一个连接池,如果每个连接池都很大,总的连接数可能会超过数据库的最大连接数,导致数据库崩溃。要根据函数的并发数和数据库的最大连接数,合理设置连接池的大小。
如果数据库连接数确实是个问题,可以考虑使用数据库代理(比如AWS RDS Proxy、阿里云的数据库代理等),代理会管理数据库连接,函数只需要连接代理,不需要直接连接数据库,这样可以大大减少数据库的连接数。
4. 异步处理和消息队列
对于不需要立即返回结果的操作,比如发送邮件、发送短信、生成报表、处理图片等,不要在API函数里同步执行,因为这样会增加API的响应时间,也会增加函数的执行时间和成本。
更好的方式是,把这些操作异步化,API函数只负责接收请求,把消息发到消息队列,然后立即返回,由另一个函数异步消费消息队列,执行这些耗时的操作。
这样,API的响应时间会很短,用户体验好,而且异步函数可以慢慢处理,不需要赶时间,成本也低。而且,消息队列还能起到削峰填谷的作用,当请求量突然增大的时候,消息先存在队列里,异步函数慢慢消费,不会因为突然的流量高峰而崩溃。
所以,在架构设计的时候,要区分同步操作和异步操作,同步操作在API函数里执行,异步操作放到消息队列里,由异步函数处理。
5. 缓存配置
缓存,是提升性能、降低成本的重要手段。在Serverless架构中,可以在多个层面使用缓存:
- 函数内缓存:把不经常变化的数据,缓存在函数的全局变量里,这样,函数实例被复用时,就不需要重新获取数据,直接用缓存就行。比如,配置信息、字典数据、不经常变化的业务数据等,都可以缓存在函数内。
- 外部缓存:使用Redis、Memcached等缓存服务,把数据缓存起来,多个函数实例可以共享缓存。对于数据量大、需要跨实例共享的数据,适合用外部缓存。
- CDN缓存:对于静态资源,比如图片、CSS、JS、视频等,可以用CDN缓存,用户直接从CDN节点获取资源,不需要请求函数,大大减轻函数的压力,也提升了用户体验。
- API网关缓存:很多API网关,支持缓存API的响应,对于不经常变化的API,可以在API网关层缓存响应,请求直接从网关返回,不需要调用函数,提升性能,降低成本。
合理使用缓存,可以大大提升函数的性能,降低成本。但要注意缓存的更新和失效,避免缓存的数据过期,导致问题。
三、高级配置:安全、监控和成本优化
进阶配置搞定了,接下来,就是高级配置,包括安全配置、监控告警、成本优化等,这些是让函数稳定、安全、低成本运行的重要保障。
1. 安全配置
安全,是Serverless架构中非常重要的一环。虽然平台会负责基础设施的安全,但应用层面的安全,还是需要开发者自己配置。
重要的安全配置:
- 最小权限角色:给函数配置执行角色的时候,一定要遵循最小权限原则,只给函数需要的最小权限,不要给管理员权限,也不要给不需要的权限。比如,函数只需要读某个存储桶,就只给读权限;只需要查询某张表,就只给查询权限。
- VPC配置:如果函数需要访问VPC内的资源(比如私有数据库、内部服务),要把函数配置到VPC里,并且配置正确的安全组,只允许函数访问需要的资源,不要开放不必要的端口和访问。
- 密钥管理:敏感信息(比如数据库密码、API密钥、Token等),不要硬编码在代码里,也不要明文存在环境变量里,要用平台提供的密钥管理服务(KMS)加密,函数运行的时候再解密获取。
- 输入验证:对函数的所有输入,都要做严格的验证和过滤,防止注入攻击(SQL注入、命令注入、XSS等)。不要信任任何输入,所有输入都要当作不可信的来处理。
- 依赖安全:定期检查函数依赖的第三方库,有没有安全漏洞,及时更新有漏洞的依赖。不要使用来源不明的依赖。
- 限流和熔断:在API网关层,配置限流,防止恶意请求或者流量突增,把函数打垮。配置熔断,当下游服务不可用的时候,快速失败,避免函数一直等待,浪费资源。
2. 监控和告警
监控和告警,是保证函数稳定运行的重要手段。只有监控到位,才能及时发现问题,及时处理,避免小问题变成大故障。
重要的监控指标:
- 调用次数:函数的调用次数,包括总调用次数、成功次数、失败次数。
- 错误率:函数的错误率,失败次数/总调用次数。错误率突然升高,说明可能有问题。
- 执行时间:函数的执行时间,包括平均执行时间、最大执行时间、P95/P99执行时间。执行时间突然变长,说明可能有性能问题。
- 内存使用:函数的内存使用量,包括平均内存、最大内存。内存使用接近配置的内存上限,说明可能需要增加内存。
- 冷启动次数:函数的冷启动次数,冷启动次数多,说明实例复用率低,可能需要增加预置并发或者预热。
- 并发数:函数的并发数,同时执行的实例数量。并发数突然升高,说明流量突增,需要关注。
- 资源使用:比如数据库连接数、缓存命中率、消息队列堆积数等。
配置好监控指标之后,还要配置告警,当指标异常的时候,及时通知相关人员。比如:
- 错误率超过1%,告警。
- P99执行时间超过5秒,告警。
- 内存使用率超过80%,告警。
- 消息队列堆积超过1000条,告警。
- 函数连续调用失败3次,告警。
告警的方式,可以是短信、邮件、电话、钉钉/企业微信/飞书消息等,根据问题的严重程度,选择不同的告警方式。严重的问题,用电话告警;一般的问题,用消息告警。
另外,还要配置日志,把函数的运行日志都收集起来,方便出问题的时候,通过日志排查问题。日志要记录详细,包括请求参数、执行步骤、错误信息、耗时等。
3. 成本优化配置
成本,是使用Serverless架构时需要重点关注的。虽然Serverless按需付费,理论上成本低,但如果配置不好,成本可能比传统服务器还高。
成本优化的配置:
- 合理设置内存:前面提到过,内存是影响成本的重要因素。要根据函数的实际需求,设置合适的内存,不要太大,也不要太小。可以做一些测试,找到性价比最高的内存配置。
- 优化执行时间:执行时间越长,成本越高。优化代码,减少不必要的计算和IO,让函数执行得越快越好。执行时间减少一半,成本就减少一半。
- 减少不必要的调用:在API网关层,做限流、鉴权、参数校验、缓存等,把无效的请求挡在外面,不要让它们调用函数。
- 合理使用预置并发:预置并发虽然能解决冷启动问题,但也是要收费的。不要设置太多预置并发,根据实际请求量,设置合适的数量,够用就行。
- 使用预留实例和节省计划:很多平台,对于长期使用的函数,提供预留实例或者节省计划,比按需付费便宜很多。如果函数的请求量比较稳定,可以考虑购买预留实例或者节省计划,降低成本。
- 优化其他服务的使用:Serverless的成本,不只是函数的成本,还包括API网关、数据库、存储、消息队列等其他服务的成本。要优化这些服务的使用,比如数据库选合适的规格,存储设置生命周期,消息队列合理设置批量大小等。
- 设置预算告警:在平台上设置预算和告警,当费用达到预算的一定比例时,及时通知,避免月底看到账单才发现超支。
4. 灰度发布和版本管理
Serverless函数的部署,虽然很方便,但也可能部署出问题的代码,导致线上故障。所以,要配置好灰度发布和版本管理。
- 版本管理:每次部署函数,都创建一个新版本,保留历史版本,不要覆盖。这样,出了问题,可以快速回滚到旧版本。
- 别名管理:用别名来指向不同的版本,比如
dev别名指向开发版本,test别名指向测试版本,prod别名指向生产版本。发布的时候,只需要把别名指向新版本,不需要修改调用方的配置。 - 灰度发布:发布新版本的时候,不要一下子全量发布,先把一小部分流量切到新版本,比如10%,观察一段时间,看看有没有问题,比如错误率、执行时间、业务指标等。如果没问题,再逐步扩大流量,比如50%、100%。如果有问题,马上把流量切回旧版本,回滚。
- 流量配置:很多平台,支持在别名上配置流量比例,比如90%的流量走版本1,10%的流量走版本2,这样就可以很方便地实现灰度发布。
灰度发布和版本管理,能大大降低发布的风险,即使新版本有问题,也只会影响一小部分用户,而且可以快速回滚。
四、不同场景的配置建议
最后,根据不同的应用场景,给一些配置建议,帮助大家根据自己的场景,配置合适的参数。
1. API服务场景
API服务,是Serverless最常见的场景。对于API服务,重点关注响应时间、可用性、并发能力。
配置建议:
- 运行时:Node.js或者Go,Node.js生态好,开发快;Go性能好,冷启动快。
- 内存:512MB-1GB,根据实际情况调整。
- 超时时间:3-5秒,API响应要快,不要让用户等太久。
- 预置并发:根据请求量,设置2-10个预置并发,保证响应速度。
- 缓存:在API网关层和函数内,合理使用缓存,减少后端调用。
- 异步化:耗时的操作,异步化,放到消息队列里处理。
- 限流熔断:配置限流和熔断,保护后端服务。
- 监控告警:重点监控错误率、响应时间、并发数。
2. 定时任务场景
定时任务,比如每天凌晨的数据统计、报表生成、数据备份等,也是Serverless的常见场景。对于定时任务,重点关注执行时间、成功率、成本。
配置建议:
- 运行时:Python或者Node.js,开发效率高。
- 内存:根据数据量和计算量,1GB-4GB,数据量大的话,内存可以大一些。
- 超时时间:5-15分钟,给足够的执行时间,但也不要太长,避免卡住的时候浪费资源。
- 预置并发:不需要,因为定时任务是定时触发的,冷启动一次没关系。
- 重试:配置重试,失败了自动重试,保证成功率。
- 异步处理:如果任务量大,可以拆分成多个小任务,并行处理。
- 监控告警:重点监控执行时间、成功率、失败次数。
3. 事件驱动场景
事件驱动场景,比如文件上传后自动处理、消息队列消费、Webhook处理等,也是Serverless的常见场景。对于事件驱动场景,重点关注吞吐量、延迟、可靠性。
配置建议:
- 运行时:根据处理的内容选择,图片处理可以用Node.js或者Python,数据处理可以用Go或者Python。
- 内存:根据处理的数据大小,512MB-2GB,大文件处理的话,内存可以大一些。
- 超时时间:根据处理时间,1-15分钟。
- 并发数:配置合适的并发数,根据下游服务的处理能力,不要把下游打垮。
- 批量处理:消息队列消费的话,配置合适的批量大小,一次处理多条消息,提升吞吐量。
- 死信队列:配置死信队列,处理失败的消息,进入死信队列,避免消息丢失,方便后续排查和重试。
- 监控告警:重点监控处理延迟、成功率、消息堆积数。
4. 工具类应用场景
工具类应用,比如图片处理、PDF转换、二维码生成、短链接生成等,请求量不大,但需要高可用。对于工具类应用,重点关注成本、可用性、冷启动。
配置建议:
- 运行时:Node.js或者Python,开发快。
- 内存:256MB-512MB,工具类应用一般不需要太大内存。
- 超时时间:1-3分钟。
- 预热:配置定时预热,保持实例活跃,避免冷启动。
- 缓存:处理结果可以缓存,相同的请求直接返回缓存。
- 监控告警:重点监控错误率、执行时间。
五、写在最后
Serverless架构,配置项很多,从基础的函数配置,到进阶的性能优化,再到高级的安全、监控、成本优化,每一项配置,都会影响函数的性能、稳定性、安全性和成本。只有把这些配置都做好了,才能真正发挥Serverless的优势,让函数跑得又快、又稳、又安全、又便宜。
今天,我们从基础到高级,详细聊了Serverless的各种配置,包括函数名称和描述、运行时和代码入口、内存和超时时间、触发器、环境变量等基础配置,预置并发和预热、层和依赖优化、数据库连接池、异步处理和消息队列、缓存等进阶配置,安全配置、监控告警、成本优化、灰度发布和版本管理等高级配置,最后还给出了不同场景的配置建议。
希望这些配置经验,能帮助大家把自己的Serverless函数,配置到最佳状态。当然,不同的平台,配置方式可能不一样,大家可以根据自己用的平台,对应到具体的配置项,核心的思路和原则,是相通的。
最后,想说的是,配置不是一成不变的,要根据函数的实际运行情况,不断调整和优化。定期查看监控和成本,看看有没有可以优化的地方,持续迭代,让函数的性能越来越好,成本越来越低。
如果有什么问题或者不同的看法,欢迎在评论区交流。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录