10个 AWS 生产事故教会我真实世界的 SRE
发布: (2026年1月9日 GMT+8 00:25)
13 min read
原文: Dev.to
Source: Dev.to
请提供您想要翻译的正文内容,我将按照要求保留源链接、格式和技术术语,仅翻译文本部分。谢谢!
1️⃣ 3 AM Wake‑up Call – 403 Errors
症状
- CloudWatch 警报:4XX 错误升高。
- 流量看起来正常,但 ≈ 30 % 的请求返回 403。
我的猜测
- API Gateway 限流或 IAM 权限问题。
实际原因
- 代码部署更改了 JWT 验证逻辑。
- 来自旧版移动应用(仍有约 30 % 用户在使用)的令牌被拒绝。
解决方案
# Roll back the problematic deployment
aws deploy rollback \
--application-name my-app \
--deployment-group-name prod
# Add backward‑compatible token validation
# (code change – omitted for brevity)
快速行动
- 回滚了部署。
- 为旧令牌添加兼容性。
- 设置了 CloudWatch 指标来监控 应用版本分布。
2️⃣ 5XX 高峰流量期间的激增
症状
- 5XX 错误激增;负载均衡器健康检查通过。
- ≈ 15 % 的请求失败。
我的猜想
- 后端服务不堪重负。
实际原因
- 在流量激增期间,Lambda 函数因 冷启动 超时,API Gateway 返回 504 Gateway Timeout。
解决方案
# 为热点函数启用预置并发
aws lambda put-provisioned-concurrency-config \
--function-name my-function \
--qualifier $LATEST \
--provisioned-concurrent-executions 100
- 在 API Gateway 集成中实现指数退避。
快速行动
- 为对流量敏感的 Lambda 启用预置并发。
- 为并发执行接近限制添加 CloudWatch 警报。
3️⃣ Route 53 故障转移至未就绪的次要区域
症状
- 下午2点,Route 53 故障转移将所有流量路由到次要区域,导致该区域迅速超负荷。
我的猜测
- 认为主区域出现了故障。
实际原因
- 安全组的更改阻塞了 Route 53 健康检查端点。
- 服务本身是健康的,但 Route 53 无法验证。
解决方案
# Allow Route 53 health‑checker IP ranges
curl https://ip-ranges.amazonaws.com/ip-ranges.json |
jq -r '.prefixes[] | select(.service=="ROUTE53") | .ip_prefix' |
while read cidr; do
aws ec2 authorize-security-group-ingress \
--group-id sg-xxxxxx \
--protocol tcp \
--port 443 \
--cidr "$cidr"
done
# Quick health‑check test
curl -v https://api.example.com/health
快速行动
- 将 Route 53 健康检查器的 IP 范围添加到安全组。
- 实施了内部健康检查,既验证端点可访问性 并且 实际服务健康状态。
4️⃣ “Connection Pool Exhausted” Errors – RDS
症状
- 应用日志:“connection pool exhausted”。
- RDS 指标:CPU ≈ 20 %,连接数远低于
max_connections。
我的想法
- 需要在数据库上增加
max_connections。
实际原因
- 应用在异常后未释放连接,导致池中出现 僵尸连接。
解决方案
# Example context manager to ensure proper cleanup
from contextlib import contextmanager
@contextmanager
def db_cursor(conn):
cur = conn.cursor()
try:
yield cur
finally:
cur.close()
conn.commit()
- 添加了连接超时设置、熔断器逻辑,以及用于监控池健康的 CloudWatch 仪表盘。
快速行动
- 实现了上述上下文管理器。
- 将警报阈值设为 70 % 的池利用率(而不是 95 %)。
5️⃣ Lambda “Rate Exceeded” Errors During a Batch Job
症状
- Lambda 函数在处理批处理作业时出现 Rate exceeded 错误。
- 作业完全停止。
我的猜想
- 我们触及了 AWS 服务限制。
实际原因
- 批处理作业在没有退避的情况下执行了 10 000 并发 DynamoDB 写入,在几秒钟内耗尽了表的写入容量。
解决办法
import time, random
from botocore.config import Config
def exponential_backoff_retry(func, max_retries=5):
for attempt in range(max_retries):
try:
return func()
except Exception as e:
backoff = (2 ** attempt) + random.random()
time.sleep(backoff)
if attempt == max_retries - 1:
raise e
# Use the built‑in retry config for the AWS SDK
config = Config(
retries={
'max_attempts': 10,
'mode': 'standard'
}
)
快速行动
- 使用重试辅助函数包装 DynamoDB 写入。
- 为写入容量启用 DynamoDB 自动扩缩。
6️⃣ ALB 将健康实例标记为不健康
症状
- ALB 偶尔将实例标记为不健康 → 某些请求出现 502 错误。
我的想法
- 实例在负载下确实出现故障。
实际原因
- 健康检查间隔为 5 秒,超时时间为 2 秒。
- 短暂的 CPU 峰值导致健康检查未能响应,产生误报(假阴性)。
解决方案
# Adjust target‑group health‑check settings
aws elbv2 modify-target-group \
--target-group-arn arn:aws:elasticloadbalancing:... \
--health-check-interval-seconds 30 \
--health-check-timeout-seconds 5 \
--healthy-threshold-count 3 \
--unhealthy-threshold-count 3
- 将健康检查端点设为 轻量级(不进行数据库查询)。
最佳实践
| ✅ 做 | ❌ 不做 |
|---|---|
只验证进程存活的健康检查(例如 /ping)。 | 执行耗时操作的健康检查(例如完整的数据库查询)。 |
快速行动
- 更新了健康检查配置。
- 部署了轻量级的
/ping端点。
7️⃣ P99 延迟峰值至 8 秒 在低流量期间
症状
- P99 延迟飙升至 8 秒,而 P50 仍保持在 200 毫秒,在安静时段。
我的猜测
- 后端数据库性能下降。
实际原因
- Lambda 冷启动。函数在空闲期间被终止,导致下一个请求的启动时间很长。
解决方案
# 为对延迟敏感的函数启用预置并发
aws lambda put-provisioned-concurrency-config \
--function-name api-handler \
--qualifier $LATEST \
--provisioned-concurrent-executions 200
# 使用 EventBridge 调度(每 5 分钟一次的 cron 表达式)保持函数热启动
aws events put-rule \
--name WarmLambdaRule \
--schedule-expression "rate(5 minutes)"
- 将部署包大小减少了 ≈60 %(移除未使用的库)。
快速行动
- 为面向用户的 API 应用了预置并发。
- 安排周期性的 “ping” 调用。
- 优化了包大小。
8️⃣ DynamoDB ProvisionedThroughputExceededException 在报告生成期间
症状
- 写入成功,但在每日报告生成时读取失败,出现 ProvisionedThroughputExceededException。
我认为
- 需要提升读取容量单位。
实际情况
- 报告使用了 Scan 操作且未进行分页,导致产生 热点分区,在几秒钟内耗尽所有读取容量。
解决方案
def paginated_query(table, key_condition):
items = []
last_evaluated_key = None
while True:
if last_evaluated_key:
response = table.query(
KeyConditionExpression=key_condition,
ExclusiveStartKey=last_evaluated_key
)
else:
response = table.query(
KeyConditionExpression=key_condition
)
items.extend(response["Items"])
last_evaluated_key = response.get("LastEvaluatedKey")
if not last_evaluated_key:
break
return items
- 在可能的情况下改用 Query。
- 实现分页和指数退避。
- 启用 DynamoDB 自动扩缩容,并添加 复合排序键 以实现高效的访问模式。
快速行动
- 将报告任务重构为使用上述分页查询。
- 为表开启自动扩缩容。
9️⃣ 过于激进的健康检查导致误报
- 过于激进的健康检查间隔可能导致本应健康的实例被标记为不健康。
- 平衡:频率要足以捕捉真实故障,但又不能过于紧密,以致瞬时峰值触发误报。
🔟 总体经验与要点
| 事件 | 核心教训 |
|---|---|
| 1 – JWT validation | 版本兼容性 很重要;在发布过程中始终支持旧版客户端。 |
| 2 – Lambda cold starts | 预置并发 + 预热调度可缓解延迟峰值。 |
| 3 – Route 53 health checks | 安全组规则必须允许健康检查器的 IP 范围。 |
| 4 – DB connection pools | 强制正确清理;在池子饱和前充分监控使用情况。 |
| 5 – DynamoDB rate limits | 从 第 1 天 起就实现指数退避和重试。 |
| 6 – ALB health checks | 保持健康检查端点轻量化;将深度检查与路由检查分离。 |
| 7 – P99 latency | 冷启动主导尾部延迟;预置并发是解药。 |
| 8 – DynamoDB hot partitions | 优先使用 Query 而非 Scan,分页,并设计为键分布均匀。 |
| 9 – Aggressive health checks | 间隔过紧会导致误报;调节阈值和超时。 |
| 10 – (Overall) | 可观测性优先——在问题发生前进行监控、告警和假设验证。 |
通过把每一次事件视为学习机会并采用有纪律、可观测的修复措施,你可以将混乱的生产抢修转变为可预测、弹性的运维。
事件 1 – 蓝绿部署期间的 502 错误
症状
- 5 % 的请求在每次部署时都因 502 Bad Gateway 错误而失败,尽管使用了蓝绿部署策略。
初始假设
- 实例关闭得太快。
根本原因
- ALB 上的 连接排空超时(注销延迟)设置为 30 秒,而某些 API 调用耗时可达 60 秒。
- ALB 在请求进行中终止了这些连接,导致 502 错误。
解决方案
# 增加连接排空超时(注销延迟)
aws elbv2 modify-target-group-attributes \
--target-group-arn arn:aws:elasticloadbalancing:region:account-id:targetgroup/name/xxxxxxxxxxxx \
--attributes Key=deregistration_delay.timeout_seconds,Value=120
- 添加了一个部署健康检查,以验证没有正在进行的请求被丢弃。
已采取的快速行动
- 增加了注销延迟。
- 在应用程序中实现了优雅关闭流程(停止接受新请求,完成已有请求)。
- 添加了部署前验证步骤。
教训
- 连接排空超时必须 长于最长请求延迟。
- 定期监控 P99 延迟并相应地设置超时。
Source:
事件 2 – 部署脚本导致安全组状态不一致
症状
- 部署脚本在执行到一半时失败,导致安全组处于不一致状态。
- 无法 SSH 登录实例,也无法回滚部署。
我的初步想法
- 手动修复安全组。
实际原因
- 自动化脚本 没有回滚机制,并在未测试的情况下直接修改了生产环境的安全组。
解决办法
# 使用 Session Manager 会话连接到受影响的实例
aws ssm start-session --target i-1234567890abcdef0
-
描述当前安全组(供参考)。
-
原子化地进行更改——例如,添加所需的规则:
aws ec2 authorize-security-group-ingress \ --group-id sg-12345 \ --ip-permissions IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges='[{CidrIp=0.0.0.0/0}]' -
验证 更改是否生效。
-
在验证成功后 再删除旧规则。
更佳做法
- 使用 AWS CloudFormation(或其他 IaC 工具)来管理安全组,以确保更新具备原子性和版本控制。
快速采取的措施
- 在所有实例上启用 Systems Manager Session Manager。
- 将安全组管理切换至 CloudFormation。
- 实施变更审批工作流。
教训
- 切勿在生产环境中手动修改安全组,单一次错误就可能导致被锁定。
- 使用 基础设施即代码(Infrastructure‑as‑Code) 并借助 Session Manager 作为安全网。
让这更容易的工具
当事故发生时,速度至关重要。我编写了一个 Incident Helper 脚本,用于自动化事故响应中的重复性工作:
- 收集相关的 CloudWatch 日志。
- 检查服务健康状态。
- 识别常见的 AWS 配置错误。
真正的教训
- 记录 每个事件。
- 构建并维护 runbooks。
- 定期 测试 fail‑over 程序。
- 每周与团队进行事后回顾讨论。
下一个事件已经排定,只是你不知道何时。做好准备才是关键。