阶段二 · Serverless:抽象掉服务器

Serverless 与 FaaS

一句话总结

Serverless 不是"没有服务器", 而是"不用再管服务器": 平台按需拉起, 用完销毁, 闲时缩容到零, 只为真正执行的那段时间付费. 它把"服务器"这一层抽象掉了, 代价是冷启动, 调试变难和厂商绑定--用对场景是利器, 用错场景是负担.

前置回顾

第 01 篇的抽象阶梯里, Serverless 处在"集群"之上的一格--它抽象掉了"服务器"和"扩缩容". 本篇就来看清这一格: 它到底让我们不用操心什么, 又把复杂度转移到了哪(第 01 篇的核心追问). 第 03 篇再讲它在 Kubernetes 上怎么落地(Knative).

从一个浪费说起: 凌晨三点, 没人用, 但在付钱

先看传统部署的一个尴尬: 假设有一个"生成月度报表"的服务, 每月就月初被调用几次, 每次跑几分钟, 但为了那几次, 得 7x24 跑着一台(或几台)服务器. 凌晨三点, 月中, 没人用的时候--服务器照样开着, 照样计费.

更普遍的浪费: 为应对流量峰值, 按峰值容量备机器, 但大部分时间流量远低于峰值--多备的容量全在空转烧钱.

问题的本质: 为"容量"付费(机器开着就计费), 而不是为"实际使用"付费. 容量和使用之间的差, 就是被浪费的钱和资源. Serverless 要消灭的正是这个差.

传统服务器像包月租了一整间办公室: 不管用不用, 用多少, 月租照付.
Serverless 像按分钟计费的共享会议室: 要用时随时有, 进去开会才计费, 走了立刻不收费.
对那种"偶尔才开个会"的需求, 后者省得多.

Serverless 到底是什么: 三个核心特征

"Serverless(无服务器)"是个有点误导的名字--服务器当然还在, 只是不再需要感知和管理它们. 判断一个东西是不是真 Serverless, 看它是否同时具备三个特征:

特征 含义 对比传统
事件驱动 代码由"事件"触发执行(HTTP 请求, 消息到达, 文件上传, 定时器) 传统服务常驻进程, 一直监听
缩容到零 没有请求时, 实例数可以降到 0--完全不占资源 传统服务至少留 1 个实例常驻
按用量付费 只为代码实际执行的时间付费(常精确到毫秒), 不执行不花钱 传统按"机器开着的时长"付费

这三点环环相扣: 因为事件驱动, 平台知道"现在没事干"→ 于是缩容到零 → 于是不执行就不计费. 三者合起来, 才实现了"为使用而非为容量付费".

两种形态: FaaS 与 BaaS

Serverless 通常以两种形态出现, 分工不同:

FaaS(函数即服务) BaaS(后端即服务)
全称 Function as a Service Backend as a Service
提供什么 一段函数代码(处理某类事件) 什么都不写, 直接用现成的后端能力
例子 AWS Lambda, 阿里云函数计算, Google Cloud Functions 托管数据库, 认证服务(Auth0), 对象存储(S3), 消息队列
一句话 "把代码跑成 Serverless" "把整个后端组件外包成 Serverless"

FaaS 是 Serverless 最具代表性的形态--也是本篇和第 03 篇 Knative 的重点. 它的模型极简: 写一个函数, 声明它被什么事件触发, 平台负责其余一切(拉起, 扩缩, 销毁, 计费).

# 一个典型的 FaaS 函数(伪代码): 只写这段逻辑
def handler(event, context):
# event 里是触发信息(比如上传的文件, HTTP 请求体)
image = event['file']
thumbnail = make_thumbnail(image) # 业务逻辑
save(thumbnail)
return {"status": "ok"}

# 不用写: 服务器, 监听端口, 扩缩容, 负载均衡, 进程管理...
# 平台看到"有文件上传"事件 → 自动拉起这个函数 → 跑完 → 销毁

绕不开的代价: 冷启动

"缩容到零"听起来很美, 但它带来 Serverless 最著名的痛点--冷启动(Cold Start). 逻辑很直接: 缩容到零意味着实例数 = 0, 这时来了一个请求, 平台得现场拉起一个新实例, 加载运行时, 加载代码, 初始化(连数据库, 读配置), 然后才能开始处理请求. 这段"从零准备好"的时间, 就是冷启动延迟(几百毫秒到几秒). 如果实例已经是热的(刚处理过请求, 还没被销毁), 直接处理 = 热启动, 很快.

这是个无法回避的根本权衡: 想要"闲时零成本", 就得接受"被唤醒时有延迟". 对延迟不敏感的场景(异步处理, 定时任务, 低频接口), 冷启动无所谓; 但对要求稳定低延迟的在线核心链路(比如用户下单的主接口), 偶尔几秒的冷启动可能不可接受.

缓解手段有: 保留少量"预热实例"(但这就牺牲了一部分"缩容到零"的省钱), 用更轻量的运行时, 减小代码体积和初始化开销. 缓解冷启动往往意味着放弃一部分省钱--又是复杂度/成本的权衡(第 01 篇).

适用边界: 什么场景适合, 什么场景别碰

这是本篇最该记住的部分. Serverless 不是万能的, 它有清晰的"甜区"和"雷区":

适合 Serverless 的场景 不适合的场景
流量波动大 / 不可预测(自动扩缩省心) 稳定高并发(常驻服务器单位成本更低)
事件驱动的零散任务(图片处理, Webhook, 定时 ETL) 对延迟极敏感的核心链路(冷启动受不了)
低频但需要随时可用(月度报表那种) 长时间运行的任务(FaaS 通常有执行时长上限)
快速试验 / MVP(不想先搭一套基础设施) 需要持久连接 / 大量本地状态(函数无状态, 易销毁)

成本陷阱: 高频稳定负载用 Serverless 可能更贵

一个常见误区是"Serverless 总是更便宜". 按用量付费的单价, 通常比常驻服务器的单价高--它贵在"灵活". 算笔账: 如果服务几乎一直在满负荷跑, 那"按执行时间 x 较高单价"很可能超过"包月一台机器".

Serverless 省钱的前提是负载有明显的波峰波谷或低频, 让"缩容到零"真正省下大量闲置成本. 负载越平稳越饱和, Serverless 的成本优势越小甚至倒挂. 选型前务必按真实流量曲线算账, 别想当然.

其他要权衡的代价

除了冷启动和成本, Serverless 还转移来几项复杂度(回应第 01 篇"复杂度守恒"):

  • 调试和可观测性变难: 函数转瞬即逝, 无固定实例, 传统"登录服务器看日志"行不通, 更依赖可观测性系列那套指标/日志/追踪--而且追踪一条跨多个函数的请求尤其有挑战.
  • 厂商绑定(Vendor Lock-in): 各家 FaaS 的函数签名, 事件格式, 配套服务都不同, 深度用了某家就很难迁走. 这正是第 03 篇 Knative 想缓解的--把 Serverless 建在开放的 K8s 上.
  • 无状态约束: 函数随时被销毁, 不能依赖本地内存/磁盘保存状态, 状态必须外置(数据库, 缓存).
  • 执行时长 / 资源上限: FaaS 平台通常限制单次执行时长和内存, 跑不了重型长任务.

快速回顾

  • Serverless 不等于没有服务器, 而是"不用管服务器"; 要消灭的是"为容量付费而非为使用付费"的浪费
  • 三大特征: 事件驱动, 缩容到零, 按用量付费--环环相扣, 共同实现"为使用付费"
  • 两种形态: FaaS(把函数跑成 Serverless, 如 Lambda), BaaS(把后端组件外包成 Serverless, 如托管数据库/认证)
  • 冷启动是"缩容到零"的代价: 实例从 0 拉起需时间; 延迟敏感的核心链路要谨慎, 缓解手段往往以牺牲省钱为代价
  • 甜区: 流量波动大/事件驱动零散任务/低频/快速试验; 雷区: 稳定高并发(可能更贵), 延迟敏感, 长任务, 强状态
  • 其他代价: 调试与可观测变难, 厂商绑定, 无状态约束, 执行时长上限--复杂度守恒

Serverless 和容器, Kubernetes 是替代关系吗?

不是替代, 是不同抽象层次的选择, 而且常常叠加. Kubernetes 管理容器化的常驻服务, Serverless 运行事件驱动的临时函数--很多系统两者都用: 核心常驻服务跑在 K8s 上, 边缘的零散任务(图片处理, Webhook, 定时清理)用 FaaS.

更妙的是, Serverless 本身可以建在 Kubernetes 之上(第 03 篇 Knative 就是), 底层还是容器, 只是上面套了一层"自动扩缩到零 + 事件驱动"的抽象. 回到第 01 篇: 它们是抽象阶梯上的不同格子, 不是二选一.

函数这么小, 又频繁冷启动, 会不会比单体还难维护?

有这个风险, 是真实存在的反模式. 如果把一个系统拆成几百个互相调用的微小函数, 会得到一个"分布式单体"的更碎版本: 调用关系复杂, 端到端追踪困难, 冷启动叠加, 认知负荷爆炸.

所以 FaaS 不是"拆得越碎越好"--它适合边界清晰, 相对独立, 事件触发的任务单元. 对于内聚的核心业务, 常驻服务(K8s 上的微服务)往往是更好的选择. 粒度的把握, 本身就是 Serverless 用得好不好的关键.

动手练习

  1. 创建最简 FaaS 函数: 在任意一家云上创建一个最简单的 FaaS 函数(如"返回当前时间"), 用 HTTP 触发它, 体验"没写任何服务器代码就有了一个可访问的接口".
  2. 感受冷启动: 让函数闲置一段时间后再调用, 留意第一次明显比后续慢--亲身感受冷启动.
  3. 成本测算: 给一个真实场景算账: 假设某服务日均流量曲线已知, 粗略对比"常驻一台小机器"和"按 FaaS 用量付费"的月成本, 找出二者的盈亏平衡点.
  4. 甜区/雷区判断: 列出工作中 2-3 个任务, 用本篇的"甜区/雷区"表判断它们适不适合 Serverless, 并说明理由.
  5. 复杂度转移分析: 思考一个了解的 Serverless 应用, 它把哪些复杂度转移给了平台? 又留下了哪些新麻烦(调试, 绑定等)?