- 教程
【免费下载链接】school-of-sre
At LinkedIn, we are using this curriculum for onboarding our entry-level talents into the SRE role.
本指南基于 School of SRE 课程
level102/system_troubleshooting_and_performance整理,面向以 Web API 服务为核心场景的 SRE 新人,系统讲解故障排查的方法论、必备 Linux 工具、性能分析与基准测试手段,并通过一个真实的 Python/Flask 内存泄漏排查案例演示"问题复现 → 信息收集 → 定位根因 → 验证修复"的完整闭环。读完本文,你将掌握一套可复用的系统故障排查流程,学会用top、sar、strace、lsof等命令快速定位问题,并能借助tracemalloc、ab等工具对服务进行性能剖析与压测验证。
课程定位与前置知识
这是 School of SRE 课程体系(位于 courses/level102/system_troubleshooting_and_performance/)中面向初级 SRE 的入门课程。它尝试为以下场景提供通用性的故障排查入门指引:
- 分析 API 失败
- 资源利用率问题(CPU、内存、磁盘、网络)
- 网络问题
- 硬件与操作系统问题
- 通过性能剖析(Profiling)与基准测试(Benchmarking)衡量系统整体性能
在开始本课程之前,官方建议你先掌握以下 Level 101 的基础内容:
- Linux 基础
- 系统设计
- 基础网络
- 指标与监控
本课程不涵盖的内容
为避免范围失控,本课程明确不涉及以下主题,它们由课程体系中的其他部分承担:
- 系统设计与架构(参见 systems_design 与 system_design)
- 编程实践(参见 python_web)
- 指标与监控(参见 metrics_and_monitoring)
- 操作系统基础(参见 linux_basics)
为什么 SRE 必须掌握故障排查
故障排查是运维与开发工作中最重要的组成部分之一,但它无法通过阅读一篇文章或完成一门在线课程就能学会——它是一个持续的学习过程,需要在以下场景中不断积累:
- 日常运维与开发
- 发现并修复应用 Bug
- 发现并修复系统与网络问题
- 性能分析与优化
排查问题前应具备的知识储备
从 SRE 的视角出发,要能够排查单机或分布式系统的问题,你需要在平时就建立以下认知:
- 充分了解你的资源:清楚主机的规格,如 CPU、内存、网络、磁盘等。
- 理解系统设计与架构:只有知道系统如何组织,才能判断故障出在哪一层。
- 确保关键指标被正确采集与展示:监控是排查的基础。
HP 创始人有一句名言:"What gets measured gets fixed"(能被度量的,就能被修复)。如果系统组件与性能指标被完整地采集,那么在问题发生的最早期成功排查的概率就会大大提高。这正是本课程把 指标与监控 列为前置课程的原因。
课程范围界定
不同类型的应用或服务没有统一的排查方法,故障可能发生在服务的任意一层。本课程将范围聚焦于Web API 服务这一典型场景。
说明:Linux 生态非常庞大,有成百上千种可用于系统故障排查的工具与实用程序,每种工具都有各自的优势与功能。本课程只覆盖一部分知名工具(要么 Linux 自带,要么来自开源社区),对工具的详细用法不做展开,建议通过
man手册页或在线资料深入学习。
课程内容全景
本课程共分五个部分,每一部分都有独立的 Markdown 文档,位于同一目录下,可按顺序学习:
- Introduction(本页)
- Troubleshooting(故障排查方法论):包含故障排查流程图、通用实践、通用主机问题
- Important tools to know(必备工具):重要 Linux 命令、日志分析工具
- Performance improvements(性能改进):性能分析命令、性能剖析工具、基准测试、扩展
- Troubleshooting Example(排查实战示例):内存泄漏排查全程演示
- Conclusion(总结):延伸阅读
故障排查:系统化方法论
排查系统故障有时非常棘手和耗时。这项实践中,我们需要检查一个服务的端到端流程:所有下游依赖、日志分析、内存泄漏、CPU 占用、磁盘 IO、网络故障、主机问题等。掌握一定的实践与工具,能更快地发现并缓解故障。
故障排查流程图
下面这张流程图(原图见 TroubleshootingFlow.jpg)是整个排查过程的高层指引,它是一个典型的闭环流程:
流程从Start开始,依次经历:
- Reproduce Problem(复现问题)→ 2.Gather Information(收集信息)→ 3.Understand Problem(理解问题)→ 4.Find Solution(找到解决方案)→ 5.Apply Fix(应用修复)
随后进入判断节点"Fix working?(修复是否生效?)":
- 若No,回到Reproduce Problem重新排查;
- 若Yes,进入Verify complete flow(验证完整流程)。
最后再次判断"Working?(是否正常运行?)":
- 若No,同样回到Reproduce Problem重新排查;
- 若Yes,流程以End结束。
这个闭环设计体现了排查的核心原则:修复未生效或未达预期时,必须回到起点重新复现与定位,而不是在错误的方向上继续深挖。
通用实践:五步定位 Web 应用故障
不同系统需要不同的排查方法。以下五个高层实践点针对"寻找 Web 应用故障并找到修复方案"这一目标展开:
1. 复现问题(Reproduce problem)
- 尝试复现失败的请求,例如重放失败的 http/https 请求;
- 检查请求的端到端流程,关注返回码:
3xx多为重定向,4xx多为未授权、错误请求、禁止访问等客户端问题,5xx多为服务端问题。根据返回码决定下一步排查方向; - 客户端侧的问题主要是静态内容缺失或存在 Bug,例如 JavaScript 脚本错误、损坏的图片、异步调用返回的损坏 JSON 等,这些会导致浏览器页面渲染不正确。
2. 收集信息(Gather Information)
- 在应用日志中查找错误/异常,例如 "Can't Allocate Memory"、
OutOfMemoryError、"disk I/O error"、DNS 解析错误等; - 检查应用与主机指标,寻找服务与主机图表中的异常:CPU 使用率从何时开始升高、内存使用率从何时开始升高、磁盘空间从何时开始减少、磁盘 IO 从何时开始增加、负载均值(load average)从何时开始飙升等。监控相关的细节可参考 指标与监控;
- 查找近期可能破坏系统的代码或配置变更。
3. 理解问题(Understand the problem)
- 尝试将收集到的数据与近期操作关联起来,例如配置/代码部署后日志中出现异常;
- 判断根因:是因为 QPS(每秒查询数)上升?是糟糕的 SQL 查询?还是最近的代码变更要求更好或更多的硬件?
4. 找到解决方案并应用修复(Find a solution and apply a fix)
- 基于以上发现,尽可能寻找快速修复方案,例如当错误/异常与变更相关时直接回滚(Rollback);
- 如果想向前修复(Fix forward),尝试在预发布(Staging)环境打补丁或热修复(Hotfix);
- 如果高 QPS 是系统失败的原因,尝试纵向扩容,按需增加资源(计算、存储、内存等);
- 必要时优化 SQL 查询。
5. 验证完整请求流程(Verify complete request flow)
- 再次发起请求,确保返回成功(返回码
2xx); - 检查日志,确保不再出现此前发现的异常/错误;
- 确保指标恢复正常。
通用主机问题排查
要判断主机健康状况是否正常,排查硬件故障或性能问题,可以尝试以下手段:
| 手段 | 用途 |
|---|---|
dmesg | 显示内核近期抛出的错误/故障,有助于发现硬件故障 |
lspci/lsblk/lscpu/lsscsi | 分别列出 PCI 设备、磁盘块设备、CPU 信息、SCSI 设备 |
/var/log/messages | 显示系统应用/服务相关的错误与警告,也包含内核问题 |
smartd | 检查磁盘健康状态 |
必备工具:常用 Linux 命令与日志分析平台
掌握以下命令能帮助你更快地定位问题(各命令的详细用法请查阅man手册页):
| 场景 | 命令 |
|---|---|
| 日志解析 | grep、sed、awk、cut、tail、head |
| 网络检查 | nc、netstat、traceroute/6、mtr、ping/6、route、tcpdump、ss、ip |
| DNS | dig、host、nslookup |
| 系统调用跟踪 | strace |
| 多主机并行执行 | GNUparallel、xargs+ssh |
| HTTP/S 检查 | curl、wget |
| 列出打开的文件 | lsof |
| 修改系统内核属性 | sysctl |
分布式系统下的批量执行工具
在分布式系统中,一些优秀的第三方工具可以帮助你在大量主机上同时执行命令/指令:
基于 SSH 的工具
- ClusterSSH:可以在多台主机上并行运行同一条命令;
- Ansible:编写 Ansible playbook,可在成百上千台主机上同时执行。
基于 Agent 的工具
- SaltStack:配置、状态与远程执行框架,提供了丰富的灵活性,可在大规模主机上执行模块;
- Puppet:面向 Linux、Unix、Windows 系统的自动化管理引擎,执行各类管理任务。
日志分析工具
这类工具支持用类 SQL 查询来解析和分析日志,并提供易用的 UI 界面来创建基于查询渲染各种图表的仪表盘:
- ELK(Elasticsearch、Logstash、Kibana):提供解析日志、索引日志、分析日志的完整工具包。日志经 Logstash 解析/过滤并索引到 Elasticsearch 后,几分钟内即可在 Kibana 中创建动态仪表盘,从而方便地对应用错误/异常/警告进行分析与关联;
- Azure Kusto(Azure Data Explorer):与 Elasticsearch + Kibana 类似的云服务,支持对海量日志进行索引,提供类 SQL 查询接口和动态仪表盘创建界面。
性能改进:分析、剖析、基准测试与扩展
性能工具是开发/运维生命周期中重要的一部分,对理解应用行为至关重要。SRE 通常使用这些工具评估服务将如何表现,并据此做出/提出改进建议。
性能分析命令
以下命令是做系统或服务性能分析时的必知命令:
| 命令 | 用途 |
|---|---|
top | 实时查看运行系统、进程、线程等 |
htop | 类似top,但更具交互性 |
iotop | 交互式磁盘 I/O 监控工具 |
vmstat | 虚拟内存统计探查工具 |
iostat | 设备和分区的输入/输出统计监控 |
free | 显示物理内存与交换内存信息 |
sar | 系统活动报告,报告 CPU、磁盘、内存、网络等多种指标 |
mpstat | 显示 CPU 利用率与性能信息 |
lsof | 提供打开文件列表及其所属进程信息 |
perf | 性能分析工具 |
性能剖析(Profiling)工具
剖析是服务性能分析的重要部分。多种剖析器可帮你找出最高频的代码路径(hot code-paths)、进行调试、内存剖析等,还能生成热力图(heatmap)来理解服务在负载下的代码性能:
- FlameGraph(火焰图):被剖析软件的图形化可视化工具,可快速准确地识别最高频代码路径;
- Valgrind:用于内存调试、内存泄漏检测与剖析的编程工具;
- Gprof(GNU profiler):混合使用插桩(instrumentation)与采样(sampling)的剖析工具——插桩用于收集函数调用信息,采样用于收集运行时剖析信息。
工程实践参考:LinkedIn 曾在其工程博客中介绍ODP(On-Demand Service Profiling,按需服务剖析基础设施),展示了在大型分布式系统中按需对线上服务进行剖析的实践思路。
基准测试(Benchmarking)
基准测试是衡量服务最佳性能的过程,例如:服务能承受多高的 QPS、负载上升时的延迟表现、主机资源利用率、load average 等。回归测试(即负载测试)在服务部署到生产环境之前是必须执行的。
常用工具:
| 工具 | 说明 |
|---|---|
ab(Apache Benchmark Tool) | 对 Web 应用模拟高负载并收集数据用于分析 |
httperf | 按指定速率向 Web 服务器发送请求并收集统计信息,可逐步加压直至找到饱和点 |
| Apache JMeter | 流行的开源 Web 应用性能测量工具,基于 Java,不仅可用于 Web 服务器,还可用于 PHP、Java、REST 等 |
wrk | 现代性能测量工具,可对 Web 服务器施压并输出延迟、每秒请求数、每秒传输量等指标 |
| Locust | 易用、可脚本化、可扩展的性能测试工具 |
局限性:以上工具属于合成负载(Synthetic Load)或压力测试,它们无法测量真实终端用户体验——无法感知终端用户因内存、CPU 不足或互联网连接质量差而对应用性能产生的影响。
工程实践参考:LinkedIn 工程博客曾介绍如何在整支舰队(fleet)范围内做全自动负载测试以消除 toil,以及如何利用RUM(Real User Monitoring,真实用户监控)数据可视化来弥补负载测试的上述局限、改善终端用户体验。
扩展(Scaling)
一个设计良好的系统,其性能上限也受限于可用资源。持续优化始终是保证资源在峰值期得到最优利用的必要手段。随着 QPS 的增长,系统需要扩容,方式主要有两种:
- 纵向扩展(Vertical):增加 CPU、内存、磁盘、GPU 等规格,但存在物理上限;
- 横向扩展(Horizontal):受限于应用设计与环境属性,但原则上可以近乎无限地扩展。
扩展一个 Web 应用通常需要以下一项或多项操作:
- 增加更多主机以分担服务器负载;
- 使用负载均衡器(Load Balancer)在服务器之间分发流量;
- 通过数据分片(Sharding)和增加只读副本(Read Replicas)来扩展数据库。
工程实践参考:LinkedIn 工程博客的《A Brief History of Scaling LinkedIn》一文详细介绍了 LinkedIn 应用栈的演进式扩展历史,是理解大规模扩展挑战的经典读物。
实战示例:用 tracemalloc 定位 Python/Flask 内存泄漏
这一节演示一个真实可操作的案例:一个带有内存泄漏 Bug 的 Flask Web 应用,每次请求内存都会持续增长,我们将使用 Python 内置的tracemalloc模块定位泄漏源头。
内存泄漏问题往往在服务运行数天、数周甚至数月后、直到服务变得无响应才被发现,除非重启服务或修复 Bug。这类服务的指标图会呈现持续上升的曲线,如下图所示(原图见 MemUsageChart.png):
内存泄漏本质上是应用对内存分配的管理失误:不再需要的内存没有被释放,随时间推移对象不断堆积,最终导致服务崩溃。通常情况下,这类未释放的对象会被垃圾回收器(Garbage Collector)自动回收,但有时由于 Bug,回收失败。调试可以帮助弄清应用存储内存主要被用在哪里,然后你就可以基于使用情况跟踪并过滤一切对象;如果发现某些对象未被使用但仍有引用,就可以通过删除它们来避免内存泄漏。
对于 Python 应用,内置的tracemalloc模块可以帮助精确定位对象首次被分配的位置。几乎每种语言都有(内置或外部的)工具/库来帮助发现内存问题,例如 Java 生态中著名的内存泄漏检测工具 Java VisualVM。
步骤 1:准备带 Bug 的 Flask 应用
假设:已经创建 Python 虚拟环境,并在其中安装了 Flask。
示例包含两个 Python 文件(代码截图见 FlaskCode.png):
app.py(Flask 主文件),一个存在内存泄漏 Bug 的最小应用:
from flask import Flask from fetchuserdata import user_data users_list = [] # 用于累积用户数据的列表(泄漏载体) # buggy_list = [] # 注释说明:此列表并非泄漏载体 app = Flask(__name__) @app.route("/") def index(): if user_data(users_list): # 每次请求都会向 users_list 追加数据 return f"Total users found {len(users_list)}" return "Failed to get user data" if __name__ == "__main__": app.run()fetchuserdata.py(数据模块):
def user_data(users_list): # 从数据库拉取用户数据并存入传入的列表 data = [("Foo", "Bar", "0123456789") for _ in range(5000)] # 每次调用构造 5000 条模拟数据 users_list.extend(data) # 内存泄漏根源:列表不断累积数据,直至耗尽内存 return True关键点在于users_list.extend(data)这一行:它作为模块级全局变量在app.py中被反复传入并扩展,每次 GET 请求都会向列表中追加 5000 条元组,而这些数据从未被清理。
步骤 2:启动应用并观察内存增长
在虚拟环境中启动应用(启动输出见 FlaskStart.png):
(flaskapp) [user1@user1 flaskapp]$ flask run启动后应用监听在http://127.0.0.1:5000/。此时用ps观察进程内存(见 MemUsage01.png):
$ ps auxwh | grep [f]lask初始内存约为26576 KB(约 26 MB)。
步骤 3:逐次请求观察内存缓慢攀升
每次发起 GET 请求,进程内存都会缓慢但持续地增长(见 MemUsage02.png),四次请求后的表现:
| 请求次数 | 响应内容 | 进程内存 |
|---|---|---|
| 第 1 次 | Total users found 5000 | 26948 KB(较初始 +372 KB) |
| 第 2 次 | Total users found 10000 | 27076 KB(+128 KB) |
| 第 3 次 | Total users found 15000 | 27120 KB(+44 KB) |
| 第 4 次 | Total users found 20000 | 27160 KB(+40 KB) |
内存只增不减,且响应中的用户总数在持续累加——这是"列表在全局范围内不断扩展"的直接证据。
步骤 4:用 ab 加压 10000 次请求验证
为了确认泄漏规模,使用 Apache 基准测试工具ab发起 10000 次请求:
$ ab -n 10000 -c 10 http://127.0.0.1:5000/压测完成后再次查看进程内存(见 MemUsage03.png):
$ ps auxwh | grep [f]lask此时 Flask 进程内存已从初始的26576 KB 暴涨到 419316 KB——从约 26 MB 跳到约419 MB,几乎增长15 倍。对于一个如此小的 Web 应用,这是非常惊人的内存增幅,明确证实了泄漏。
步骤 5:接入 tracemalloc 定位泄漏源头
tracemalloc模块可在特定时间点抓取内存快照,并对快照执行各种统计。在app.py中增加最小代码(fetchuserdata.py无需改动),新增一个/capture路由,用于在访问时抓取 tracemalloc 快照:
import tracemalloc from flask import Flask from fetchuserdata import user_data users_list = [] app = Flask(__name__) tracemalloc.start() # 启动内存跟踪 @app.route("/") def index(): if user_data(users_list): return f"Total users found {len(users_list)}" return "Failed to get user data" @app.route("/capture") def capture(): snapshot = tracemalloc.take_snapshot() # 抓取当前内存快照 top_stats = snapshot.statistics("lineno") # 按行号统计 result = [] for stat in top_stats[:10]: result.append(f"{stat.traceback}: {stat.size / 1024 / 1024:.1f} MB") return "<br>".join(result) if __name__ == "__main__": app.run()注:截图版示例代码位于 Tracemalloc01.png,此处为便于阅读整理的等价实现;核心思路一致——在快照后对分配对象按文件行号(
lineno)分组统计。
重启app.py(flask run)后,按以下顺序操作:
- 先访问
http://127.0.0.1:5000/capture,记录基线快照; - 再访问
http://127.0.0.1:5000/共 10000 次,让内存泄漏充分发生; - 最后再次访问
http://127.0.0.1:5000/capture,抓取第二次快照。
对比两次快照(见 Tracemalloc02.png 与 Tracemalloc03.png),最终快照会精确指出内存分配量最大的模块与行号:fetchuserdata.py的第 6 行(即users_list.extend(data)),在 10000 次请求后持有了约419 MB的内存。
排查结论与生产环境注意事项
以上示例展示了一个 Bug 如何导致内存泄漏,以及如何借助tracemalloc定位其源头。真实世界的应用远比这个示例复杂,请注意:
- 使用
tracemalloc会因自身开销而在一定程度上降低应用性能; - 在生产环境中使用需谨慎,建议仅在调试窗口期开启,或优先在预发布环境复现排查。
对于 Python 对象内存分配内部机制与内存泄漏调试的深入探讨,可参考 PyCon India 2019 的相关技术分享《Debug Memory Leak In Python Flask | Python Object Memory Allocation Internals》。
总结
复杂系统有太多可能出错的环节:糟糕的设计与架构、管理不善的代码、不合理的缓存策略、糟糕的数据库查询或架构、不恰当的资源使用、不合适的 OS 版本、监控不到位的系统、数据中心问题、网络故障等——任何一项都可能引发故障。
作为 SRE,掌握关键工具与命令、最佳实践、性能剖析、基准测试与扩展手段,将帮助你更快地完成故障排查与整体系统的性能改进。本课程的 Conclusion 中还列出了 LinkedIn 工程博客上的经典实战文章作为延伸阅读,主题包括:
- 用 Jemalloc 驯服 Venice 中的内存碎片
- 修复 Linux 文件系统性能回归
- 慢速 NFS 对数据系统的影响
- 运维中的"每天都是星期一"精神
这些来自一线 SRE 的实战记录,与本文的方法论和示例相互印证,值得在完成本课程后逐一精读。
课程学习路径建议
- Level 101 前置:Linux 基础 → 系统设计 → 基础网络 → 指标与监控;
- 本课程正文:Troubleshooting → Important tools → Performance improvements → Troubleshooting Example → Conclusion。
建议在学习本课程时,动手在你的实验环境里复现内存泄漏示例,亲手运行ab压测与tracemalloc快照,把方法论转化为可迁移的排查直觉。
- 教程
【免费下载链接】school-of-sre
At LinkedIn, we are using this curriculum for onboarding our entry-level talents into the SRE role.
相关推荐
KMSPico-2026用户常见问题解答:解决激活失败、安全警告等10大难题
KMSPico 2026用户常见问题解答:解决激活失败、安全警告等10大难题 KMSPico 2026是一款专业的Windows激活工具,能够为Windows
如何用RWKV模型轻松创作高质量中文小说?AI-Writer实用指南
如何用RWKV模型轻松创作高质量中文小说?AI Writer实用指南 想要体验AI智能写作的魅力吗?AI Writer是一个基于创新RWKV架构的中文预训练生成
AI 应用大模型AI 写作NLP本地部署掌握TCPdump与Wireshark:SRE必备的网络故障排查终极指南
掌握TCPdump与Wireshark:SRE必备的网络故障排查终极指南 在软件可靠性工程(SRE)领域,网络故障排查是确保系统稳定运行的关键技能。school
教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考