无服务器架构Serverless入门

结论: Serverless(无服务器架构)不等于没有服务器,而是把服务器的采购、装机、系统补丁、扩缩容全部交给云厂商,开发者只交付函数代码或容器镜像,按实际调用次数与执行时长付费。它最划算的场景是流量波动大、有明显空闲期、事件驱动型的业务;对长连接、超低延迟、长时间批处理的业务反而不占优势。

很多人第一次听到 Serverless 这个词,会下意识以为"不用服务器了"。实际上机房里那台物理机依然存在,只是它跑在别人家的云上,你再也看不到它的 IP 和 /var/log/messages。

对中小企业 IT 负责人和个人站长来说,Serverless 的真正价值在于两件事:一是把"半夜起来重启服务"这件事从你的待办清单里永久划掉;二是把固定支出变成随用量波动的可变支出。但如果把所有业务一股脑搬上去,账单和延迟可能会同时给你上课。

本文按"概念 → 计费 → 对比 → 场景 → 实操 → 避坑"的顺序讲清楚 Serverless(Serverless Architecture),所有命令和配置截至 2026 年均可直接复制执行。

Serverless 到底是什么?FaaS 与 BaaS 的区别

Serverless 是一组能力,不是一个产品。它由 FaaS(Function as a Service,函数即服务) 和 BaaS(Backend as a Service,后端即服务) 两部分拼起来:前者负责跑你的业务代码,后者负责让你不写后端代码也能有数据库、对象存储、鉴权、消息队列。

判断一个东西是不是 Serverless,可以用下面三条硬标准:

  1. 无需管理服务器:你不 SSH、不打补丁、不看内核参数,云厂商负责运行时下面的全部层次。
  2. 按用量计费且能缩容到零:没有请求时实例数为 0,费用为 0,而不是"一个月 24 小时都在扣钱"。
  3. 弹性由平台承担:突发流量来了平台自动扩容,你不需要提前估算买几台机器。
组成部分英文全称它解决什么典型产品形态你是否写业务代码
FaaSFunction as a Service事件触发执行的一段逻辑函数计算 / 云函数写,只写函数体
BaaSBackend as a Service数据存储、鉴权、消息、文件对象存储、Serverless 数据库、API 网关基本不写
触发器Trigger / Event Source让函数被谁调用HTTP、定时、消息队列、对象上传事件只做配置

需要强调的一点:FaaS 是 Serverless 的核心,但不是全部。只用了对象存储不能叫 Serverless 架构,只有"函数 + 托管后端服务 + 事件驱动"三者齐备,才真正享受到免运维和缩容到零的好处。

常见的误解还有"Serverless 一定比云服务器便宜"。这取决于你的负载曲线:一条平稳的高负载直线,Serverless 通常更贵;一条起伏剧烈、夜里接近零的折线,Serverless 通常能省一半以上。

Serverless 的计费模型:调用次数 × 时长 × 内存

Serverless 的账单由三个乘数决定,理解公式比记住单价更重要(具体单价截至 2026 年以各家官网实时报价为准)。

月度费用 ≈ 调用次数 × 单次费用 + 执行时长(GB·秒)× 单价 + 出网流量费 + 附加服务(网关/日志/数据库)费

其中"GB·秒"是关键:它把内存占用和执行时间捆绑计价。你给函数配 2048 MB 内存,跑 200 ms,计费量就是 2048/1024 × 0.2 = 0.4 GB·秒。这意味着内存配置不是越高越好,也不是越低越好——内存大则单价高但执行快,内存小则单价低但执行久,中间存在一个成本最低点。

计费因子含义常见默认值 / 范围省钱要点
调用次数每次触发计一次每月通常有免费额度合并小请求,避免一次业务拆成多次调用
执行时长从入口执行到返回按 1 ms 或 100 ms 递增阶梯减少冷启动与外部网络等待
内存配置决定单价与 CPU 配额128 MB ~ 3072 MB 或更高用压测找最低 GB·秒点,不要拍脑袋填
出网流量响应体出公网部分按 GB 计价静态资源走 CDN,不要在函数里回传大文件
空闲请求/预置实例为消除冷启动常驻实例可选,通常按时长加价只有核心链路才开,非核心链路关闭

下面这段 Python 脚本可以直接拿来估算账单,把你的实际参数替换掉即可(单价为示例占位值,务必替换成官网实时价格):

python
# serverless_cost_calc.py —— Serverless 月度成本粗算
# 单价为占位示例值,实际请以 2026 年各云官网实时报价为准
PRICE_PER_REQUEST = 0.0000013      # 元 / 次
PRICE_PER_GB_SECOND = 0.00012000   # 元 / GB·秒
GB_SECOND_FREE = 40000             # 每月免费 GB·秒示例

def monthly_cost(calls_per_month, avg_ms, memory_mb):
    gb = memory_mb / 1024
    gb_second = calls_per_month * gb * (avg_ms / 1000)
    billable = max(gb_second - GB_SECOND_FREE, 0)
    request_fee = calls_per_month * PRICE_PER_REQUEST
    resource_fee = billable * PRICE_PER_GB_SECOND
    # 打印两个候选内存配置,便于对比择优
    return {
        "requests": round(request_fee, 2),
        "gb_second_total": round(gb_second, 2),
        "resource": round(resource_fee, 2),
        "total": round(request_fee + resource_fee, 2),
    }

if __name__ == "__main__":
    for mem in (512, 1024, 2048):
        # 内存翻倍通常执行时间缩短,这里用经验值近似
        avg_ms = {512: 420, 1024: 210, 2048: 120}[mem]
        r = monthly_cost(3_000_000, avg_ms, mem)
        print(f"内存 {mem:>5} MB | 平均 {avg_ms:>4} ms | "
              f"GB·秒 {r['gb_second_total']:>10} | 月度合计 {r['total']:>8} 元")

跑出来的结果通常会颠覆直觉:512 MB 看着便宜,但因为跑得慢,总 GB·秒反而最多;2048 MB 常常是这条曲线的底部。这正是为什么上 Serverless 之前必须做一轮内存—耗时矩阵压测。

Serverless 与容器、虚拟机的对比

选 Serverless 还是选云服务器 / Kubernetes,本质是选"你要掌控到哪一层"。控制力越强,自由度越高,但要操的心也越多。

维度物理机 / 云服务器容器(Docker / K8s)Serverless 函数
启动速度分钟级秒级毫秒级(热) / 百毫秒~秒级(冷)
缩容到零不支持,一直在计费支持但需配置原生支持
单请求成本恒定支出恒定 + 浮动严格按用量
最长执行时间不限不限通常 5~15 分钟上限
状态保持内存/磁盘随意用支持有状态服务无状态,外部存储持久化
运维负担最重中等最轻
适合负载平稳、持续高负载微服务集群突发、事件驱动

一句话选型判据:如果你的服务每天有超过 8 小时处于低负载,先把这部分流量抽出来做函数化改造,剩下的稳态底座继续留在云主机或 K8s 上,这就是现实中最常见的混合云架构。

还要注意一条硬约束:函数有最长执行时间上限(各厂商通常在 5~15 分钟之间,截至 2026 年以官网文档为准)。跑 3 小时的数据清洗任务、需要常驻内存的高速缓存、WebSocket 长连接,天生不适合直接套在 FaaS 上。

什么场景适合 Serverless,什么场景不适合

答案是先画出你的流量曲线,再看是不是事件驱动。

适合 Serverless 的典型场景:

  • Webhook / 回调接收:支付回调、Git 推送钩子、第三方通知,流量稀疏且突发。
  • 定时任务:每天凌晨的数据汇总、报表生成、证书到期巡检,天然缩容到零。
  • 数据处理流水线:图片上传后自动压缩打水印、视频转码触发、日志清洗入库。
  • 轻量 API / BFF 层:面向前端的接口聚合层,QPS 中等且白天夜里差异巨大。
  • 原型验证与活动页:不确定能不能跑起来的新业务,先零成本上线试水。

不适合 Serverless 的典型场景:

  • 长时间批处理:受执行时长上限限制,需拆分或改用容器批处理服务。
  • 低延迟核心交易链路:冷启动抖动可能带来几十到几百毫秒的额外延迟。
  • 长连接服务:WebSocket、消息推送、游戏房间服务器需要常驻连接。
  • 依赖本机磁盘 / 特殊驱动的程序:函数环境只有临时目录,且不可预装内核模块。
  • License 绑定到主机指纹的软件:无稳定主机标识会触发授权失败。

冷启动是怎么回事,怎么优化

冷启动(Cold Start)是指函数在一段时间没有请求后,平台需要重新分配计算资源、拉取你的代码包或镜像并初始化运行时,这段时间不计入你的业务逻辑,却计入用户的等待时长。 热调用可以直接命中已有实例,冷调用则需要多付这笔"开机费"。

冷启动主要由三块开销构成,按可从你手里优化的程度排序:

  1. 代码包加载:代码包和依赖越大越慢,是优化空间最大的一块。
  2. 运行时初始化:Java / .NET 的 JVM/CLR 启动明显慢于 Python / Node.js。
  3. VPC / 网络准备:把函数放进私有网络会额外增加网卡挂载耗时。

可落地的优化清单:

bash
# 1) 精简依赖包:只打包运行时真正需要的部分(Python 示例)
pip install --target ./package -r requirements.txt --no-compile --no-cache-dir

# 2) 剔除测试目录、__pycache__、.git,减小代码包体积
find ./package -type d -name "__pycache__" -prune -exec rm -rf {} +
find ./package -type d -name "tests" -prune -exec rm -rf {} +

# 3) 进入 package 目录打包,避免出现多余的一层路径
cd package && zip -r -q ../function.zip . -x "*.pyc" "*.dist-info/*" && cd ..
zip -q -g function.zip handler.py

# 4) 查看最终体积,建议控制在 50 MB 以内(解压后常控制在 250 MB 内)
ls -lh function.zip

除了瘦身,还有三条工程手段:把数据库连接放在函数入口之外做全局复用(让连接池在实例存活期间复用,而不是每次调用都新建);对核心链路开启预置并发实例(常驻少量实例消除冷启动,代价是失去缩容到零);把重量级依赖改为层(Layer)或容器镜像预热。

与之相对的另一个坑是并发失控。函数一旦压测通过上线,突发流量可能瞬间打到上游数据库。务必配置预留并发上限,保护数据库不被自己的弹性打垮。

Serverless 快速上手:从 Hello World 到公网 API

下面给出一个可直接落地的 Python 函数示例:入口接收 JSON,做一次简单校验后返回结构化结果。事件结构中你可以拿到 HTTP 方法与请求体。

python
# handler.py —— Python 3.12 函数入口示例
import json
import time


def _response(status: int, body: dict) -> dict:
    """统一构造 HTTP 响应结构,便于网关识别"""
    return {
        "statusCode": status,
        "headers": {"Content-Type": "application/json; charset=utf-8"},
        "body": json.dumps(body, ensure_ascii=False),
    }


def main_handler(event, context):
    # event 中包含 httpMethod、body、queryString 等字段
    try:
        payload = json.loads(event.get("body") or "{}")
    except json.JSONDecodeError:
        return _response(400, {"ok": False, "error": "invalid JSON body"})

    name = payload.get("name", "").strip()
    if not name:
        return _response(422, {"ok": False, "error": "field 'name' is required"})

    # 业务逻辑:这里只做一次轻量处理,重活应丢给消息队列异步执行
    started = time.time()
    result = {"message": f"hello, {name}", "length": len(name)}
    cost_ms = round((time.time() - started) * 1000, 2)

    return _response(200, {"ok": True, "cost_ms": cost_ms, "data": result})

配套的部署配置文件,定义了运行时、内存、超时时间与定时触发器(YAML 语法,字段命名参考常见 Serverless 框架规范):

yaml
# serverless.yml —— 函数、触发器与网关配置
service: serverless-demo

provider:
  name: cloud-vendor          # 替换为你的云厂商标识
  runtime: python3.12
  region: ap-guangzhou
  memorySize: 1024            # MB,需与 CPU 配额联动调整
  timeout: 30                 # 秒,上限以厂商文档为准
  environment:
    LOG_LEVEL: INFO

functions:
  hello:
    handler: handler.main_handler
    description: "Serverless 入门示例函数"
    events:
      # HTTP 触发器:对外暴露 REST 接口
      - http:
          path: /hello
          method: POST
      # 定时触发器:每天 02:00 执行一次(Cron 五段式)
      - timer:
          cron: "0 0 2 * * *"
          enable: true

plugins:
  - serverless-python-packaging

本地先自测再部署,可以省掉大量云端排错时间:

bash
# 本地安装依赖并跑一次单元自检
python -m pip install -r requirements.txt
python -c "
import handler
evt = {'body': '{\"name\": \"ops\"}'}
print(handler.main_handler(evt, None))
"

# 通过框架 CLI 部署(以 serverless framework 为例)
npx serverless deploy --stage prod

# 部署后立即验证线上返回码与耗时
curl -s -o /dev/null -w "http_code=%{http_code} time=%{time_total}s\n" \
  -X POST https://example.com/hello \
  -H "Content-Type: application/json" \
  -d '{"name":"ops"}'

部署完成后别急着收工,把下面三项纳入日常检查:函数错误率、平均执行时长、GB·秒用量环比。第三项是唯一能提前发现代码改崩导致账单暴涨的指标。

常见误区 / 排错提示

  1. 误以为 "无服务器 = 绝对零运维":你不用管操作系统,但仍要管代码质量、依赖安全、幂等设计、并发上限和费用告警。把监控和账单告警配上是上线前的必要条件。
  2. 在函数里写本地文件并期望下次还在:函数的 /tmp 临时目录在实例销毁后失效,且不同实例不共享。持久化必须走对象存储或 Serverless 数据库。
  3. 把冷启动当成 Bug 反复排查:第一次调用慢属正常。判断方法是同一函数连续调用两次对比耗时,第二次明显变快即为冷启动而非逻辑问题。
  4. 超时时间设成最大值"以防万一":超时设得越大,异常卡死时烧掉的钱越多,也越容易拖垮调用方。应按 P99 耗时 × 2 设置,并在入口做超时自检。
  5. 函数重试导致重复扣款 / 重复写库:事件源失败会自动重试,函数必须做幂等。用业务唯一键做去重,或把写操作改成"先查后写 + 唯一索引约束"。
  6. 忽略并发对下游的冲击:函数一瞬间扩出上千实例,数据库连接池直接被打爆。务必设置预留并发与最大实例数,并在数据库前加连接池中间件。