Serverless 实践:从零构建云原生应用
·
Serverless 实践:从零构建云原生应用
一篇涵盖 Serverless 核心概念、主流平台选型和实战开发的完整指南,帮助你掌握无服务器架构的设计与落地。
目录
- 一、Serverless 概述
- 二、主流 Serverless 平台对比
- 三、AWS Lambda 实战
- 四、阿里云函数计算实战
- 五、Serverless Framework 统一开发
- 六、生产级最佳实践
- 七、总结
一、Serverless 概述
1.1 什么是 Serverless
Serverless(无服务器)是一种云计算执行模型,云提供商动态管理服务器资源分配,开发者只需关注业务代码。
┌─────────────────────────────────────────────────────────────────┐
│ Serverless 架构演进 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 传统架构 容器化 Serverless │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 物理服务器 │ ──▶ │ 虚拟机/容器 │──▶ │ 函数/服务 │ │
│ │ │ │ │ │ │ │
│ │ - 购买硬件 │ │ - 管理OS │ │ - 只写代码 │ │
│ │ - 运维机房 │ │ - 配置网络 │ │ - 按需付费 │ │
│ │ - 固定成本 │ │ - 调度容器 │ │ - 自动扩缩 │ │
│ │ - 手动扩容 │ │ - 部分弹性 │ │ - 事件驱动 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ 运维负担:高 ──────────────▶ 中 ──────────────▶ 低 │
│ 灵活性:低 ──────────────▶ 中 ──────────────▶ 高 │
│ 成本优化:差 ──────────────▶ 中 ──────────────▶ 好 │
└─────────────────────────────────────────────────────────────────┘
1.2 Serverless 核心特点
| 特点 | 说明 | 收益 |
|---|---|---|
| 无需管理服务器 | 云厂商管理基础设施 | 降低运维成本 |
| 按需付费 | 按执行次数和时长计费 | 成本优化,按实际使用付费 |
| 自动弹性伸缩 | 根据请求量自动扩缩容 | 应对流量峰值无需预判 |
| 事件驱动 | 由事件触发执行 | 松耦合架构 |
| 无状态 | 每次执行独立 | 简化开发模型 |
1.3 Serverless 适用场景
适合的场景:
- API 后端:RESTful API、GraphQL 服务
- 数据处理:ETL、实时数据流处理
- 定时任务:定时调度、批处理作业
- 事件处理:消息队列处理、文件上传处理
- Webhook 处理:第三方服务回调
- AI 推理:模型推理、图像处理
不适合的场景:
- 长时间运行任务:超过函数执行时间限制(通常15分钟)
- 高延迟敏感:冷启动可能导致延迟
- 复杂状态管理:有状态应用不适合
- 高频低延迟:数据库连接池等难以复用
1.4 FaaS vs BaaS
| 类型 | 说明 | 示例 |
|---|---|---|
| FaaS (Function as a Service) | 函数即服务,运行业务代码 | AWS Lambda、阿里云函数计算 |
| BaaS (Backend as a Service) | 后端即服务,提供现成服务 | Firebase、Supabase、云数据库 |
完整的 Serverless 应用通常是 FaaS + BaaS 的组合。
二、主流 Serverless 平台对比
2.1 平台概览
| 平台 | 提供商 | 特点 | 适用场景 |
|---|---|---|---|
| AWS Lambda | AWS | 最成熟、生态最完善 | 企业级应用、AWS 生态 |
| 阿里云函数计算 | 阿里云 | 国内领先、中文文档 | 国内业务、阿里云生态 |
| 腾讯云 SCF | 腾讯云 | 微信生态集成 | 小程序、微信相关业务 |
| Azure Functions | Microsoft | 企业集成、.NET 支持 | 企业应用、Microsoft 生态 |
| Google Cloud Functions | 与 GCP 深度集成 | 大数据、AI 应用 | |
| Cloudflare Workers | Cloudflare | 边缘计算、全球分布 | 全球业务、边缘场景 |
2.2 详细对比
| 维度 | AWS Lambda | 阿里云函数计算 | 腾讯云 SCF |
|---|---|---|---|
| 运行时支持 | Node.js、Python、Java、Go、.NET、Ruby | Node.js、Python、Java、PHP、Go、.NET | Node.js、Python、Java、PHP、Go、.NET |
| 执行时间限制 | 15分钟 | 10分钟(可调整) | 15分钟 |
| 内存配置 | 128MB - 10GB | 128MB - 3GB | 128MB - 3GB |
| 冷启动时间 | 100ms - 1s | 100ms - 500ms | 100ms - 500ms |
| 触发器 | API Gateway、S3、DynamoDB、SQS | API网关、OSS、Table Store、MNS | API网关、COS、CMQ、定时触发器 |
| 定价 | 按请求+执行时间 | 按请求+执行时间 | 按请求+执行时间 |
| 免费额度 | 100万请求/月 | 100万请求/月 | 100万请求/月 |
2.3 选型建议
| 场景 | 推荐平台 | 理由 |
|---|---|---|
| 海外业务 | AWS Lambda | 全球覆盖、生态成熟 |
| 国内业务 | 阿里云函数计算 | 网络延迟低、合规性好 |
| 小程序后端 | 腾讯云 SCF | 微信生态深度集成 |
| 边缘计算 | Cloudflare Workers | 全球边缘节点 |
| 多云部署 | Serverless Framework | 统一开发体验 |
三、AWS Lambda 实战
3.1 创建第一个 Lambda 函数
控制台创建步骤
- 登录 AWS 控制台,进入 Lambda 服务
- 点击「创建函数」
- 选择「从头开始创作」
- 配置:函数名称、运行时(Node.js 18.x)、架构(x86_64)、执行角色
- 编写代码并部署
// index.mjs
export const handler = async (event) => {
console.log('Event:', JSON.stringify(event, null, 2));
const response = {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: 'Hello from Lambda!',
input: event,
}),
};
return response;
};
CLI 创建示例
# 创建项目目录
mkdir my-lambda && cd my-lambda
# 创建函数代码
cat > index.mjs << 'EOF'
export const handler = async (event) => {
return {
statusCode: 200,
body: JSON.stringify({ message: 'Hello Lambda!' }),
};
};
EOF
# 打包
zip function.zip index.mjs
# 创建函数
aws lambda create-function \
--function-name hello-world \
--runtime nodejs18.x \
--role arn:aws:iam::YOUR_ACCOUNT_ID:role/lambda-role \
--handler index.handler \
--zip-file fileb://function.zip
3.2 配置 API Gateway 触发器
# 创建 REST API
API_ID=$(aws apigateway create-rest-api --name 'My API' --query 'id' --output text)
# 获取根资源 ID
ROOT_ID=$(aws apigateway get-resources --rest-api-id $API_ID --query 'items[0].id' --output text)
# 创建资源
RESOURCE_ID=$(aws apigateway create-resource \
--rest-api-id $API_ID \
--parent-id $ROOT_ID \
--path-part 'hello' \
--query 'id' --output text)
# 创建方法并集成 Lambda
aws apigateway put-method \
--rest-api-id $API_ID \
--resource-id $RESOURCE_ID \
--http-method GET \
--authorization-type NONE
aws apigateway put-integration \
--rest-api-id $API_ID \
--resource-id $RESOURCE_ID \
--http-method GET \
--type AWS_PROXY \
--integration-http-method POST \
--uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:ACCOUNT_ID:function:hello-world/invocations
# 部署 API
aws apigateway create-deployment --rest-api-id $API_ID --stage-name prod
3.3 Lambda 最佳实践
代码组织结构
my-lambda-project/
├── src/
│ ├── handlers/
│ │ ├── get-user.js
│ │ └── create-user.js
│ ├── services/
│ │ └── user-service.js
│ └── utils/
│ └── response.js
├── tests/
├── serverless.yml
└── package.json
响应格式化工具
// src/utils/response.js
export const success = (data, statusCode = 200) => ({
statusCode,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({ success: true, data }),
});
export const error = (message, statusCode = 500, code = 'ERROR') => ({
statusCode,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({ success: false, error: { code, message } }),
});
四、阿里云函数计算实战
4.1 使用 fun 工具创建项目
# 安装 fun
npm install -g @alicloud/fun
# 初始化项目
fun init -n my-fc-project alinode
# 项目结构
my-fc-project/
├── template.yml # 资源定义
├── index.js # 函数代码
└── package.json
# 本地调试
fun local invoke my-function
# 部署
fun deploy
4.2 函数计算配置示例
# template.yml
ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
my-service:
Type: 'Aliyun::Serverless::Service'
Properties:
Description: My FC Service
my-api:
Type: 'Aliyun::Serverless::Function'
Properties:
Handler: index.handler
Runtime: nodejs16
CodeUri: ./
MemorySize: 512
Timeout: 60
EnvironmentVariables:
NODE_ENV: production
Events:
httpTrigger:
Type: HTTP
Properties:
AuthType: ANONYMOUS
Methods:
- GET
- POST
4.3 常用触发器配置
# 定时触发器
Events:
timerTrigger:
Type: Timer
Properties:
CronExpression: '0 0 2 * * *'
Enable: true
# OSS 触发器
Events:
ossTrigger:
Type: OSS
Properties:
BucketName: my-bucket
Events:
- oss:ObjectCreated:*
Filter:
Key:
Prefix: images/
Suffix: .jpg
# MNS 消息队列触发器
Events:
mnsTrigger:
Type: MNSTopic
Properties:
TopicName: my-topic
Region: cn-hangzhou
五、Serverless Framework 统一开发
5.1 安装与初始化
# 安装
npm install -g serverless
# 创建项目
serverless create --template aws-nodejs --path my-project
# 项目结构
my-project/
├── serverless.yml # 配置文件
├── handler.js # 函数代码
└── package.json
5.2 完整配置示例
# serverless.yml
service: my-serverless-api
frameworkVersion: '3'
provider:
name: aws
runtime: nodejs18.x
region: us-east-1
stage: ${opt:stage, 'dev'}
environment:
NODE_ENV: ${self:provider.stage}
DB_HOST: ${ssm:/my-app/db-host}
iam:
role:
statements:
- Effect: Allow
Action:
- dynamodb:GetItem
- dynamodb:PutItem
Resource: arn:aws:dynamodb:${self:provider.region}:*:table/my-table
functions:
getUser:
handler: src/handlers/users/get.handler
events:
- httpApi:
path: /users/{id}
method: get
memorySize: 256
timeout: 10
createUser:
handler: src/handlers/users/create.handler
events:
- httpApi:
path: /users
method: post
processImage:
handler: src/handlers/images/process.handler
events:
- s3:
bucket: my-images-bucket
event: s3:ObjectCreated:*
memorySize: 1024
timeout: 60
plugins:
- serverless-offline
resources:
Resources:
UsersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: users
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
5.3 常用命令
# 本地开发
serverless offline
# 部署
serverless deploy --stage prod
# 部署单个函数
serverless deploy function -f getUser
# 查看日志
serverless logs -f getUser --stage prod --tail
# 调用函数
serverless invoke -f getUser --data '{"id": "123"}'
# 删除服务
serverless remove --stage prod
六、生产级最佳实践
6.1 冷启动优化
冷启动原因:函数首次调用需要初始化运行环境、加载代码和依赖。
| 策略 | 说明 | 效果 |
|---|---|---|
| 使用 Provisioned Concurrency | 预留并发实例 | 完全消除冷启动 |
| 减少依赖 | 精简 node_modules | 减少加载时间 |
| 使用轻量运行时 | Node.js/Python 优于 Java | 加快初始化 |
| 代码优化 | 延迟加载非必需模块 | 加快启动 |
| 保持函数热度 | 定时 ping 函数 | 避免容器回收 |
// 优化前:启动时加载所有依赖
const axios = require('axios');
const moment = require('moment');
export const handler = async (event) => {
const response = await axios.get('https://api.example.com');
return response.data;
};
// 优化后:按需加载
export const handler = async (event) => {
const axios = require('axios'); // 只加载需要的
const response = await axios.get('https://api.example.com');
return response.data;
};
6.2 数据库连接管理
// 正确做法:复用连接池
const { Pool } = require('pg');
let pool = null;
const getPool = () => {
if (!pool) {
pool = new Pool({
host: process.env.DB_HOST,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
max: 2, // 函数环境连接数限制
});
}
return pool;
};
export const handler = async (event) => {
const pool = getPool();
const result = await pool.query('SELECT * FROM users');
return result.rows;
};
6.3 错误处理与重试
class AppError extends Error {
constructor(message, statusCode, code) {
super(message);
this.statusCode = statusCode;
this.code = code;
}
}
export const handler = async (event) => {
try {
const { id } = event.pathParameters;
if (!id) {
throw new AppError('Missing user ID', 400, 'MISSING_ID');
}
const user = await getUserById(id);
if (!user) {
throw new AppError('User not found', 404, 'NOT_FOUND');
}
return success(user);
} catch (err) {
console.error('Error:', err);
return error(err.message, err.statusCode || 500, err.code || 'ERROR');
}
};
6.4 监控与日志
const logger = {
info: (message, data = {}) => {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
level: 'INFO',
message,
requestId: process.env.AWS_REQUEST_ID,
...data,
}));
},
error: (message, data = {}) => {
console.error(JSON.stringify({
timestamp: new Date().toISOString(),
level: 'ERROR',
message,
requestId: process.env.AWS_REQUEST_ID,
...data,
}));
},
};
export const handler = async (event) => {
logger.info('Handler started');
try {
const result = await processEvent(event);
logger.info('Handler completed', { resultCount: result.length });
return result;
} catch (error) {
logger.error('Handler failed', { error: error.message });
throw error;
}
};
6.5 安全最佳实践
| 实践 | 说明 |
|---|---|
| 最小权限原则 | 只授予函数必要的 IAM 权限 |
| 环境变量加密 | 敏感信息使用 SSM Parameter Store 或 Secrets Manager |
| VPC 配置 | 访问私有资源时放入 VPC |
| 输入验证 | 验证所有外部输入,防止注入攻击 |
| 依赖更新 | 定期更新依赖,修复安全漏洞 |
七、总结
本文系统介绍了 Serverless 架构的核心知识和实践方法:
核心要点回顾:
- Serverless 是趋势:无需管理服务器、按需付费、自动弹性伸缩
- 平台选型:根据业务场景选择合适的云平台
- 开发实践:掌握函数编写、触发器配置、本地调试
- 性能优化:冷启动优化、连接复用、代码精简
- 生产保障:错误处理、监控日志、安全防护
最佳实践清单:
- 函数保持单一职责,代码简洁
- 复用连接池,避免每次创建
- 按需加载依赖,减少冷启动时间
- 使用结构化日志,便于排查问题
- 配置重试策略,处理临时故障
- 监控函数执行时间和内存使用
- 定期更新依赖,修复安全漏洞
- 敏感信息使用加密存储
下一步学习方向:
- Serverless 数据库(DynamoDB、MongoDB Atlas)
- 事件驱动架构设计
- Step Functions 编排复杂工作流
- Serverless 安全最佳实践
Serverless 让开发者专注于业务逻辑,而非基础设施。从小场景开始尝试,逐步积累经验。
如果这篇文章对你有帮助,欢迎点赞收藏!有问题欢迎评论区讨论。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)