5.1 服务模型:IaaS / PaaS / FaaS

一句话

三者不是三代技术,而是把责任边界切在不同深度的三刀:IaaS 切在虚拟硬件,PaaS 切在运行时与部署单元,FaaS 切到单次函数调用;切得越深,你少管的越多、可控的越少,单价里含的「平台溢价」越高。

责任边界:一条线,三个位置

同一套业务,三种模型下你要接手的东西完全不同:

你负责 IaaS PaaS FaaS
机房、硬件、虚拟化 云 云 云
操作系统内核与补丁 你 云 云
运行时(JDK / Go / Python) 你 云(版本可选) 云(版本有限)
依赖与系统库 你 你(构建进制品) 你(打进层/包)
进程生命周期、健康检查 你 平台(部分) 平台
扩缩容决策 你 你(配规则)或平台 平台(到 0)
容量规划与闲置成本 你 你(实例数) 平台(按调用付费)
网络、负载均衡、证书 你(安全组/自建 LB) 平台(部分) 平台
可观测性 你装 agent 平台给日志/指标 只有平台暴露的那些

🔑 判断一个服务属于哪一层,只问一句:**内核补丁谁打、进程挂了谁拉起、扩容谁决定。**三个答案都在你手里是 IaaS;都在平台手里是 FaaS;中间那档是 PaaS。

托管 K8s(EKS / ACK / GKE)常被当成 PaaS 卖,但责任线其实在「节点」:控制面托管、数据面(节点内核、kubelet、CNI)仍是你的,所以它更接近 CaaS——比 IaaS 少管控制面,比 PaaS 多管内核。

一句话判据:三种模型的「用户视角」

模型 你拿到的东西 你的第一个动作 你不知道的事
IaaS 一台能 SSH 的机器 装运行时、写 systemd unit 物理机在哪、邻居是谁
PaaS 一个部署单元(应用 + 运行时) git push 或上传制品 跑在哪台机器、几台机器
FaaS 一个函数 + 触发器 写 handler、配事件源 进程什么时候起、起几个、跑多久

计费:三种完全不同的成本函数

模型 计费单位 闲置时 成本随什么涨 典型省钱手段
IaaS 实例 × 时长(秒/小时) 照收 峰值规格 × 时长 预留/包年、竞价实例、右规格
PaaS 实例规格 × 时长(含平台溢价) 照收 实例数 × 时长 自动伸缩、只开必要中间件
FaaS 调用次数 + GB-秒(内存 × 时长) 为 0 调用量 × 单次时长 × 内存 降内存、缩时长、减少调用

FaaS 的月成本可以直接写出来:

月成本 = 调用次数 × (单次时长s × 内存GB × GB-秒单价 + 每次调用单价)

IaaS 的月成本则是:

月成本 = 峰值所需实例数 × 实例月单价        ← 注意是峰值,不是均值

这两个公式的差别只有一处,却决定了选型:FaaS 为均值付费,IaaS 为峰值付费。

算一笔账:决定胜负的是峰均比,不是 QPS

设定(单价为公开价目表的量级,用于比较结构,不是报价):每次调用 200ms;FaaS 按 $0.0000167/GB-秒 + $0.20/百万次;IaaS 1 vCPU / 2 GiB 按量 $31/月。两种负载画像各算一遍:

平均并发 峰均比 IO 型(0.05 vCPU / 0.25 GiB)FaaS / IaaS CPU 型(1 vCPU / 1.7 GiB)FaaS / IaaS
10 10× $136 / $403 $771 / $3,100
100 10× $1,358 / $3,875 $7,709 / $31,000
1000 10× $13,578 / $38,750 $77,088 / $310,000
10 2× $136 / $93 $771 / $620
100 2× $1,358 / $775 $7,709 / $6,200
1000 2× $13,578 / $7,750 $77,088 / $62,000
100 1.1× $1,358 / $434 $7,709 / $3,472

🔑 峰均比 ≥ 5 时,FaaS 开始占优;峰均比接近 1(稳态高负载)时,IaaS/预留便宜 1.5–3 倍。「QPS 低所以用 FaaS」这句话漏了后半句:低 QPS 且稀疏才省钱;低 QPS 但 7×24 常驻,FaaS 只是把固定成本换成了更贵的按量单价。

上面的表可以复算——把峰值并发按峰均比折成实例数,就是全部逻辑:

package main

import (
	"fmt"
	"math"
)

const (
	gbSecond = 0.0000166667 // $/GB-秒
	perReq   = 0.20 / 1e6   // $/次
	instMo   = 31.0         // $/月,1 vCPU / 2 GiB
	durSec   = 0.2          // 每次执行 200ms
	secMo    = 730 * 3600.0
)

func faas(avgC, memGB float64) float64 { // 为平均值付费
	busy := avgC * secMo
	return busy*memGB*gbSecond + busy/durSec*perReq
}

func iaas(avgC, cpuPerRun, memGB, duty float64) float64 { // 为峰值付费
	peak := avgC / duty
	inst := math.Max(peak*cpuPerRun, peak*memGB/2)
	return math.Max(1, math.Ceil(inst)) * instMo
}

func main() {
	cpu, mem := 0.05, 0.25 // IO 型:每次执行 0.05 vCPU / 256MB
	for _, duty := range []float64{0.1, 0.5, 0.9} {
		for _, c := range []float64{10, 100, 1000} {
			fmt.Printf("并发 %-5.0f 峰均比 %-4.1f FaaS $%-8.0f IaaS $%-8.0f\n",
				c, 1/duty, faas(c, mem), iaas(c, cpu, mem, duty))
		}
	}
}

输出(节选,与上表 IO 型一致):

并发 10    峰均比 10.0 FaaS $136      IaaS $403
并发 100   峰均比 10.0 FaaS $1358     IaaS $3875
并发 1000  峰均比 10.0 FaaS $13578    IaaS $38750
并发 100   峰均比 2.0  FaaS $1358     IaaS $775
并发 1000  峰均比 1.1  FaaS $13578    IaaS $4309

第二个隐藏变量是打包率:256MB 的函数能在一个 2GiB 实例里并排跑 8 个,而 CPU 密集的函数只能跑 1 个。FaaS 按实际内存计费,IaaS 按整机规格计费——函数越轻、越 IO 密集,FaaS 越占便宜;越能吃满整机,IaaS 越便宜。

FaaS 的五个硬约束

约束 量级 / 口径 后果 绕法
冷启动 毫秒级(Go/Node 预热池)到秒级(JVM/Spring 初始化) P99 抖动,首请求超时 预置并发、快照启动、把初始化挪到全局作用域、改用轻运行时
单次执行时长 公开口径上限 15 分钟(各平台不同,有的 5–10 分钟) 长任务被强杀,重试放大副作用 拆成编排 + 多个函数,或改用 Job/容器
无状态 本地盘为临时目录,实例随时回收 会话、缓存、临时文件丢失 外置 Redis/对象存储;把状态显式化
并发与连接 账号级并发配额;长连接支持有限 突发被限流;WebSocket 不适用 申请提额、长连接走网关/容器
事件源绑定 触发器、IAM、VPC 配置与平台强绑定 迁移成本高 把业务逻辑与 handler 解耦,触发器薄层化

冷启动的根源不是「容器启动慢」(容器启动是 ms 级),而是运行时初始化:JVM 要加载类、Spring 要扫注解、Python 要 import 一堆包。所有「消灭冷启动」的手段,本质都是在复用已经初始化好的进程(预热池、快照恢复)。

可观测性与调试:能力随抽象递减

能力 IaaS PaaS FaaS
SSH / 登录机器 ✅ 部分(跳板/调试容器) ❌
抓包 ✅ 受限 ❌
CPU/内存 profile ✅ 随时 部分平台支持 只在调用期间、按平台
本地复现 完全一致 基本一致 难(事件源、IAM、超时都要模拟)
日志 自建采集 平台采集 平台采集(有大小/保留限制)
排障粒度 进程/线程/系统调用 实例 单次调用

⚠️ FaaS 的排障盲区是冷启动与平台调度:同一次调用可能落在热实例(20ms)也可能冷启动(3s),日志里看不出区别。没有平台的冷启动指标,你会把「P99 抖动」误判成「下游慢」。

锁定:锁的不是代码,是周边

层次 迁移要改什么 锁定强度
IaaS 镜像可导出、IaC 换 provider 低(主要是网络/IAM)
CaaS K8s 清单基本可搬,存储类/网络插件要换 中
PaaS 构建方式、运行时版本、附加服务(DB/缓存)绑定 中高
FaaS 触发器、IAM、事件格式、部署工具、运行时限制 高

决策表

你的情况 选 理由
需要改内核参数、装 agent、特殊网络 IaaS 平台不给你这层权限
标准 Web 服务、流量平稳、团队小 PaaS 少运维,实例数可控
事件驱动、流量稀疏或脉冲、异步任务 FaaS 闲置成本为 0
长连接、WebSocket、有状态会话 CaaS / IaaS FaaS 的连接模型不匹配
单次任务 > 15 分钟 Job / 容器 超出 FaaS 时长上限
稳态高负载、能吃满整机 IaaS + 预留 单位算力最便宜
需要 GPU、特殊硬件 IaaS FaaS 基本不提供
已有 K8s 与平台团队 CaaS + FaaS(Knative/OpenFaaS) 复用调度与流水线

反模式

反模式 后果
用 QPS 单指标决定 FaaS/IaaS 忽略峰均比与打包率,稳态负载上 FaaS 后账单翻倍
把 Spring Boot 直接搬上 FaaS 每次冷启动都重跑初始化,P99 从 80ms 变 3s
在函数里做长任务(视频转码、大批量导出) 超时被强杀,重试导致重复扣费/重复写入
在函数实例内存会话或本地缓存 实例回收即丢,行为随机
把 FaaS 当「不用运维」 触发器、IAM、VPC、配额、冷启动都要人管
只算计算费,不算出网费与请求费 流量型业务的账单大头在出网,见 5.6 云成本结构
无预置并发就承诺 P99 SLA 冷启动直接把尾延迟打穿

自检

Q1 一个批处理任务每天跑 2 小时、每次 40 分钟、内存 2 GiB,团队想用 FaaS。你会怎么算、怎么答?

▸ 答案:先算占空比:2/24 ≈ 8.3%,峰均比高,成本上 FaaS 占优。但 40 分钟单次执行会撞上 FaaS 的时长上限(公开口径常见 15 分钟,平台不同),所以不能把整批塞进一个调用,必须拆成「编排 + 多个 ≤15 分钟的函数」或直接用 Job/容器。结论:成本模型支持 FaaS,执行模型不支持——选型要同时过这两关。若拆分后每次调用有大量固定开销(拉数据、建连),拆分的总成本可能反超一台按小时计费的实例。

Q2 有人说「我们 QPS 只有 5,肯定用 FaaS 便宜」。这句话缺了哪三个变量?

▸ 答案:缺 ① 峰均比/占空比(5 QPS 是 7×24 还是每天 10 分钟);② 单次时长与内存(决定 GB-秒);③ 打包率(每次执行占多少 vCPU/内存,能否填满一台实例)。5 QPS 的常驻服务,FaaS 的按量单价往往高于一台小实例的月费;反之每天只跑 10 分钟的 5 QPS,FaaS 便宜一个量级。

Q3 团队把 Java 服务原样搬上 FaaS,压测发现 P50 25ms、P99 3.2s。为什么?列出两条治本手段。

▸ 答案:P50 是热实例,P99 是冷启动——JVM 启动 + 类加载 + Spring 上下文初始化都在调用路径上。治本:① 预置并发/预热池,让请求落在已初始化的实例;② 降低初始化成本:换轻量框架、把重初始化移到构建期(CDS/AppCDS、GraalVM native image)、减少启动期远程调用。加内存只能缩短一部分时间,不解决「每次新进程都要重新初始化」。

Q4 托管 K8s 应该算 IaaS 还是 PaaS?你的判断依据是什么?

▸ 答案:看责任线——控制面(API server/etcd/调度器)由云托管,但节点内核、kubelet、CNI、节点补丁、容量仍是你的,所以它是 CaaS,介于两者之间。判断依据始终是那三问:内核补丁谁打(你)、进程挂了谁拉起(k8s,但 k8s 本身你运维)、扩容谁决定(你配 HPA/Cluster Autoscaler,云只提供能力)。如果选了「托管节点池 + 自动伸缩」,责任线会向 PaaS 移动一截,但不会到 FaaS。

Q5 一个业务白天峰值是夜间的 12 倍,团队已经有一套 K8s。你会建议全量上 FaaS 吗?

▸ 答案:不建议「全量」。峰均比 12 的确适合 FaaS 的成本结构,但已有 K8s 意味着调度、发布、监控、网络都已沉没成本;更优的是按负载分层:脉冲型、无状态、事件驱动的部分(图片处理、webhook、定时任务)用 FaaS/Knative,长连接与核心交易留在 CaaS。判断标准是「这块负载的峰均比 × 是否无状态 × 是否值得多一套运维」,而不是统一技术栈的美感。

跨篇相关:容器与虚拟机的隔离边界决定 FaaS 多租户能不能信,见 6.1 容器与虚拟机的本质差异;FaaS 的伸缩由平台控制器完成,其模型见 7.1 声明式 API 与控制器模式;地域选择与可用区见 5.3 地域与可用区;弹性策略见 5.4 弹性伸缩与自动扩缩容;托管与自建的取舍见 5.5 托管服务与自建的取舍;成本结构见 5.6 云成本结构;厂商锁定与迁移见 5.7 云厂商锁定与迁移;部署形态的另一种切法见 33.3 Serverless / FaaS;单位经济与容量规划见 39.1 成本模型与单位经济、39.2 容量规划(待补)。