第一章:Serverless 前世今生——从物理机到容器,再到无服务器架构的演进之路

聊 Serverless 之前,我想先带你回顾一段历史。

这段历史,说白了就是一部「开发者如何偷懒」的进化史。我入行那会儿,还在跟物理机较劲,谁能想到十几年后,我们连服务器长什么样都快忘了。

1.1 物理机时代:自己养马自己骑

2005 年左右,我刚毕业进了一家电商公司。那时候部署一个应用,流程是这样的:先写邮件给运维申请一台物理机,等上两三天,机器到了,然后自己扛到机房上架、插网线、装系统、配环境。

嗯,你没看错,开发者还得干运维的活。

那时候的痛点很明显:

  • 资源利用率低——一台机器跑一个应用,CPU 经常空闲 80% 以上
  • 扩容慢——双十一前要加机器,提前一个月就得申请
  • 环境不一致——开发环境能跑,测试环境挂了,生产环境又出幺蛾子

我记得最惨的一次:上线前夜发现生产环境的 Linux 内核版本跟测试环境不一样,一个系统调用行为不同,导致整个服务崩溃。那晚我们三个人熬到凌晨四点,就为了打一个内核补丁。

1.2 虚拟化时代:一匹马分给多人骑

VMware 和 Xen 的出现,算是救了老命。一台物理机可以跑多个虚拟机,每个虚拟机有自己的操作系统,互相隔离。

好处很明显:

  • 资源利用率从 20% 提升到 60% 以上
  • 扩容从「买机器」变成「创建虚拟机」,时间从几天缩短到几小时

但问题也来了:

  • 每个虚拟机都包含完整的操作系统,动辄几个 GB
  • 启动时间还是慢,冷启动要几分钟
  • 资源开销大,你想想看,跑 10 个虚拟机就得装 10 个 OS

我个人习惯:在虚拟化时代,我通常会预留 20% 的 CPU 余量,因为 Hypervisor 层的调度开销比你想象的要大。有一次线上 CPU 飙到 95%,我以为是应用问题,查了半天才发现是同一台物理机上的另一个虚拟机在跑批处理任务。

1.3 容器时代:轻装上阵

2013 年 Docker 横空出世,整个行业都炸了。

容器跟虚拟机的区别,说白了就是:

  • 虚拟机:每个应用带一个完整的操作系统
  • 容器:所有容器共享宿主机的操作系统内核

这意味着什么?

  • 容器镜像只有几百 MB,甚至几十 MB
  • 启动时间从分钟级降到秒级
  • 一台物理机可以跑上百个容器
# 一个简单的 Dockerfile 示例
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

这个 Dockerfile 只有 7 行,但包含了完整的运行环境。你想想看,以前用虚拟机做同样的事,得先装一个 Ubuntu 镜像,再装 Node.js,再配置环境变量……一套下来至少半小时。

我曾经踩过一个坑:容器虽然轻量,但安全隔离性不如虚拟机。有一次我在生产环境用了 privileged 模式运行容器,结果容器里的进程可以直接操作宿主机内核。嗯,后来被安全团队约谈了。记住:容器共享内核,这不是虚拟机。

1.4 编排时代:容器多了谁来管?

容器是好东西,但当你有了几十上百个容器,问题就来了:

  • 谁负责把容器调度到合适的机器上?
  • 容器挂了怎么办?
  • 流量来了怎么自动扩容?

这时候 Kubernetes 出现了。2015 年,Google 把 Borg 系统的经验开源出来,就是后来的 K8s。

K8s 的核心能力:

  • 自动调度:根据资源需求把 Pod 分配到合适的节点
  • 自愈:Pod 挂了自动重启
  • 自动扩缩容:根据 CPU/内存使用率自动调整副本数
  • 服务发现:Pod 之间通过 Service 名称通信

但 K8s 的学习曲线,说实话,挺陡的。我记得刚开始学的时候,光是理解 Pod、Service、Deployment、Ingress 这几个概念就花了一周。

1.5 无服务器时代:我只管写代码

到了 Serverless 时代,事情变得简单了。

你不需要关心:

  • 服务器在哪里
  • 操作系统是什么版本
  • 运行时环境怎么配置
  • 怎么扩容

你只需要:写代码,上传,完事。

以 AWS Lambda 为例:

// 一个简单的 Serverless 函数
exports.handler = async (event) => {
    const name = event.queryStringParameters?.name || 'World';
    return {
        statusCode: 200,
        body: `Hello, ${name}!`
    };
};

这段代码部署到 Lambda 后,AWS 会自动处理:

  • 请求来了自动启动容器执行函数
  • 没请求时自动缩容到零
  • 流量暴增时自动扩容到上千个并发实例

我个人的感受:第一次用 Serverless 部署一个 API 时,我盯着控制台看了半天——没有服务器列表,没有负载均衡配置,没有自动伸缩组。说实话,有点不习惯,但用了一个月后,我再也不想回去碰 K8s 了。

1.6 演进对比:一张表看懂

维度 物理机 虚拟机 容器 Serverless
资源粒度 整台机器 虚拟 CPU/内存 进程级 函数级
启动时间 小时级 分钟级 秒级 毫秒级
资源利用率 10-20% 40-60% 60-80% 接近 100%
运维成本 极高 极低
弹性伸缩 手动 半自动 自动 自动且瞬时
计费方式 包年包月 按小时 按小时 按调用次数+执行时间

1.7 避坑指南:Serverless 不是银弹

说了这么多 Serverless 的好处,我也得泼点冷水。

我曾经犯过的错:刚开始用 Serverless 时,我把一个需要运行 30 分钟的数据处理任务直接扔到 Lambda 上。结果 Lambda 最大执行时间是 15 分钟,任务跑到一半就被强制杀掉了。后来我才知道,Lambda 有超时限制,长时间任务得用 Step Functions 或者 ECS。

Serverless 的局限性:

  • 冷启动延迟:长时间没有请求后,第一个请求会有几百毫秒的延迟
  • 执行时间限制:AWS Lambda 最长 15 分钟
  • 内存限制:最大 10GB(虽然对大多数场景够用)
  • 无状态:函数之间不能共享内存状态
  • 调试困难:不能 SSH 到服务器上看日志

所以我的建议是:

  • 短任务、事件驱动的场景 → 用 Serverless
  • 长时间运行、有状态的服务 → 用容器
  • 需要 GPU 的计算任务 → 还是用物理机或虚拟机吧

1.8 小结

从物理机到 Serverless,我们走过了将近 20 年。这 20 年的演进,核心逻辑只有一个:让开发者专注于业务逻辑,而不是基础设施

你想想看,以前我们要自己管服务器、管网络、管存储、管中间件……现在呢?写个函数上传就完事了。这背后的技术复杂度,全被云厂商扛了。

下一章,我会带你深入 Serverless 的核心架构,看看一个函数请求从触发到返回,背后到底发生了什么。嗯,保证让你大开眼界。