第一章: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 的核心架构,看看一个函数请求从触发到返回,背后到底发生了什么。嗯,保证让你大开眼界。