FaaS - Cold Start
触发时机
类似于
LoadingCache
首次
请求- 容器实例在服务请求后被
回收
启动过程
容器创建
- 当所有容器实例都在处理请求时,需要向集群申请创建新的容器
- 函数计算平台会支持多种语言的运行时
- 这些运行时一般来说会打包成一个镜像,然后以
DeamonSet
的方式运行在 Kubernetes 中 - 在冷启动时,会根据不同的参数请求,
动态挂载
所需的运行时到对应的运行路径
- 这些运行时一般来说会打包成一个镜像,然后以
代码包 / 层依赖
- 是整个冷启动
耗时比较长
的过程 - 函数计算本身不具备
持久化
的能力,代码包和层依赖通常都是从其它存储服务端
拉取 - 代码包通常是
压缩包
的形式,下载到本地后,再解压
环境变量 / 参数文件
耗时相对较短
- 主流的函数计算平台往往提供了
环境变量注入
的能力,发生在冷启动阶段 - 运行时以及容器本身还需要准备一些参数配置文件
VPC 打通 / 资源准备
- 如果用户还为函数接入了私有网络,还需要为容器进行一些
VPC 网络打通
的初始化工作 - 如果用户使用了类似
分布式文件系统
等功能,还需要进行挂载
运行时初始化
- 通常指的是云厂商标准的 Runtime 环境的启动过程
- 受编程语言类型的影响比较大(JVM 比较慢)
用户代码初始化
- 用户的自定义逻辑代码的加载,即某些业务逻辑 init 的过程
- 函数计费都是从程序执行开始计费的,即 JVM 的准备过程是不收费的
启动时长
资源调度
和容器创建
,一般在秒级
完成- 代码包和依赖层的
下载
过程,取决于代码大小以及是否有加速 VPC
网络的打通
过程,主要是弹性网卡
和路由
的下发耗时
,通常在秒级
- 运行时与用户代码初始化过程
启动优化
平台侧
资源调度 / 容器创建
- 容器启动时会先检查本地有无相关镜像,如没有,需要从
远端仓库
拉取 - 传统的容器运行时需要将
全量
的镜像数据下载
后再解压
,实际可能仅需要部分数据
- 容器启动耗时比较长
- 如果集群规模过大,下载解压环节会造成较大的网络、磁盘读写压力
镜像加速
:主要解决传统容器需要提前下载
镜像再启动而导致耗时较长
的问题按需加载
- 使用
加速镜像版本
,配置加速规则和标签 - 通过按需加载的模式,
免于全量下载镜像
,避免带宽浪费,提高分发效率
- 使用
P2P 加速
- 利用
计算节点
的内网
带宽资源,在节点之间分发镜像,降低镜像仓库的压力
- 利用
多镜像仓库
- 进一步缓解
单仓库
的存储和服务压力
- 进一步缓解
代码包 / 层依赖
- 压缩包
缓存
- 通过实现
本地缓存
来提高代码包以及层依赖的获取速度 - 在函数第一次冷启动处理某个请求时,会将代码包下载到 Node 上,并通过
特殊编码
进行标记 - 当
同一个 Node
上有其它函数实例需要使用同一份代码时,可以根据标记码看本地是否有缓存
- 通过实现
- 常用依赖
内置化
- 根据用户的使用场景,将一些常用的依赖提前内置化
- 这些常用的依赖一般都会在构造容器
基础镜像
时一起构建
- 极致
压缩
- 对代码包进行
二次压缩
,让代码包变得更小
- 对代码包进行
跨 VPC 加速
- 函数运行在一个
专有
的 VPC 集群内,如果函数需要访问用户 VPC 集群内的资源 - 传统方式:通过
动态创建弹性网卡 ENI
来实现跨 VPC
的访问- 弹性网卡的
创建耗时
比较久,导致冷启动耗时急剧增加 - 弹性网卡占用用户 VPC 的
IP 资源
,如果没有空闲的资源,会导致创建失败 - 造成弹性网卡较大的浪费
- 弹性网卡的
在集群 VPC 内部创建
代理
- 核心在于使用
IP 隧道技术
- 平台可以在
保持函数原始 IP 地址
的情况下,将流量通过Proxy
代理节点的方式触达用户端 VPC
- 平台可以在
- 函数平台会为关联了 VPC 的函数创建轻量的
虚拟机
- 将
弹性网卡
绑定到代理虚拟机
上,所有关联该 VPC 网络的流量最终都会通过 Proxy 进行发送
- 将
- 为了确保服务的稳定,Proxy 通常采用
主备
的方式进行 - 使用该方式,可以通过
监听
用户在 Console关联 VPC
的操作时,提前进行创建
- 在函数第一次处理请求时,只需要
转发
,可以带来一个数量级
的性能提升
- 在函数第一次处理请求时,只需要
- 瑕疵:需要额外消耗 Proxy 的 Node 资源
提前预加载
基于
函数调用
的场景进行提前预测
基于
函数版本
进行预测
提前
加载镜像
- 对于一些大镜像(如 10G),
提前分发
,然后通过单机侧的 Agent 上报标记 - 当流量到来时,直接分发到预加载过的 Node 的容器上,以快速应对函数的执行处理
用户侧
- 合理控制
代码包
大小- 拉取代码包是整个冷启动过程中
最耗时
的部分
- 拉取代码包是整个冷启动过程中
- 选择性能较高的
运行时
- Python/Node.js/Go > JVM
- 成本可控范围内合理使用
预留实例
- 可以为某个函数根据实际需要申请
固定数量
的预留实例 - 每个实例都已经提前准备好函数代码,直到用户
主动释放
,都会始终处于常驻
状态- 当函数收到请求后,会
优先调度
到预留实例
并且以热启动
的方式进行处理 - 直到该函数的所有预留实例都处于
工作状态
,才会将请求调度到一个非预留实例
以冷启动
的方式执行
- 当函数收到请求后,会
- 使用预留实例来解决冷启动的问题,需要考虑
闲置成本
- 可以为某个函数根据实际需要申请
定时激活
延时敏感较高的函数实例- 通过一个
定时触发器
来提前预热
服务,而函数平台通常会有一定的时间来保留热的容器 - 如果函数涉及到
业务处理
,可以通过标志位
,来区分预热
和非预热
的请求
- 通过一个
- 合理利用
本地缓存
- 适用于 AI 场景下的数据训练任务
- 每次数据拉取前都提前从本地磁盘空间判断数据是否已经存在,可以避免每次数据拉取造成的启动延迟
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.