复现并消除瓶颈
本实验在真正的 VM 中进行
这个环境不是 Pod,而是由 KubeVirt 启动的虚拟机。Linux 内核独立运行,
systemd 会实际管理服务,docker 也不是模拟程序,而是真正的 Docker 引擎。
通过 docker run 启动的容器会成为实际进程,docker exec 和 docker logs
也都可以照常运行。
过去,本实验在 Pod 内运行。由于环境撤销了全部内核权限,启动容器的步骤 无法执行,因此只能学习手动解开镜像归档的变通办法。现在不再需要变通。
有两点需要了解。
- 首次启动大约需要 1 分钟。 这是因为 VM 需要启动并安装 Docker。 它比 Pod 实验(通常 40 秒)更慢。
- 不提供浏览器预览。 进入 VM 的连接只开放一个评分端口。
如果启动了 Web 服务器,请在 VM 内使用
curl进行确认。
目标
特意制造并复现瓶颈,利用分层假设缩小范围,消除瓶颈后以数字确认改进幅度,并计算承担目标流量所需的实例数。产物放在 /root/lt3/ 下。
为什么重要
寻找瓶颈不是依靠感觉,而是按顺序逐级检查:socket buffer → thread pool queue → connection pool wait → DB lock wait → processing。本实验制造的是其中最前端的瓶颈,即“一次只能处理一个请求”的并发限制。此时出现的信号非常重要:即使并发提高 10 倍,吞吐量也几乎不变,只有延迟增加 10 倍。 亲眼看到吞吐量受限时等待时间会相应增加这一关系后,在生产环境遇到 Connection is not available, request timed out after 30000ms 之类的日志时,就会知道首先查看哪里。最后一步还会学习一条原则:不要直接使用测量值。只使用测得最大吞吐量的 70%,其余留作余量。
准备
请先执行 mkdir -p /root/lt3。可用镜像仅限预先下载的 python:3.12-alpine、nginx:1.27-alpine、alpine:3.20、busybox:1.36。
步骤
- 创建
/root/lt3/slow.py。要求:使用标准库http.server的HTTPServer(一次处理一个请求);每个请求执行time.sleep(0.05)产生 50ms 延迟;响应正文包含slow-app字符串。使用python:3.12-alpine镜像运行该脚本,容器名设为lt-slow,host 端口设为 127.0.0.1:8086。curl http://127.0.0.1:8086/的响应中必须看到slow-app。 - 以并发 1 进行测量,保存到
/root/lt3/slow-c1.txt。正常情况下,p95 至少为 0.04 秒,吞吐量不超过 40 RPS(50ms 延迟串行处理,理论值约 20 RPS)。 - 以并发 10 进行测量,保存到
/root/lt3/slow-c10.txt。需要确认:吞吐量小于并发 1 时的 2 倍(几乎不变),p95 至少增加 2 倍。吞吐量受限时,等待时间会相应增加。 - 在
/root/lt3/hypothesis.md中写出至少 3 个瓶颈候选项,每项以-或1.开头。候选项必须来自不同层级(并发/thread/worker、CPU、connection pool、lock contention、IO/network 中至少三个层级)。每个候选项还要写明如何验证。 - 创建
/root/lt3/fast.py。要求:使用ThreadingHTTPServer(或ThreadingMixIn)支持并发处理;保持 50ms 延迟不变;响应正文包含fast-app。容器名为lt-fast,host 端口为 127.0.0.1:8087。以并发 10 测量并保存到/root/lt3/fast-c10.txt,吞吐量必须至少为slow-c10.txt的3 倍。 - 对 fast app 也以并发 1测量,生成
/root/lt3/fast-c1.txt,然后在/root/lt3/compare.csv中整理四行。格式为app,concurrency,rps,p95,行分别为slow,1、slow,10、fast,1、fast,10。值必须读取自各结果文件。 - 在
/root/lt3/capacity.txt中写四行。target_rps=300(目标峰值流量)measured_rps=——compare.csv中 fast/10 行的 rpssafe_rps=—— 测量值的 70%,舍去小数(整数)instances=—— ceil(300 ÷ safe_rps),即向上取整后的整数
- 在
/root/lt3/report.md中编写报告。需要四节:瓶颈原因、证据(测量值)、措施、容量结论。正文必须原样包含改进前吞吐量、改进后吞吐量、所需实例数这三个数字。
参考
- 容器运行示例:
docker run -d --name lt-slow -p 127.0.0.1:8086:8080 -v /root/lt3:/app python:3.12-alpine python /app/slow.py(脚本内绑定容器内部端口 8080)。 - server 未启动时,使用
docker logs lt-slow检查。若 Python handler 漏掉Content-Lengthheader,client 会等待连接关闭,导致测量失真。 - 测量示例:
hey -n 100 -c 1 http://127.0.0.1:8086/ > /root/lt3/slow-c1.txt 2>&1、hey -n 200 -c 10 ...;fast 较快,建议使用-n 1000 -c 10左右。 - 向上取整计算:
awk -v t=300 -v s="$SAFE" 'BEGIN{n=t/s; r=int(n); if(n>r) r=r+1; print r}'。 - 常见错误 1:把改进理解为“减少延迟”。保持 50ms 延迟不变,必须消除等待。延迟不变而吞吐量大幅增加才是正确结果。
- 常见错误 2:对
safe_rps四舍五入。必须向下舍去,而instances则必须向上取整。 - 常见错误 3:在第 8 步报告中只写“吞吐量大幅增加”之类的句子。证据必须是数字。
启动慢速应用
创建 /root/lt3/slow.py。要求:使用标准库 http.server 的 HTTPServer(一次处理一个请求);每个请求执行 time.sleep(0.05) 产生 50ms 延迟;响应正文包含 slow-app 字符串。使用 python:3.12-alpine 镜像运行该脚本,容器名设为 lt-slow,host 端口设为 127.0.0.1:8086。curl http://127.0.0.1:8086/ 的响应中必须看到 slow-app。
/root/lt3/slow.py 必须是一个为每个请求加入人为延迟、且一次只处理一个请求的 server。响应正文必须包含 slow-app 字符串,容器名为 lt-slow,host 端口为 8086。
在并发 1 下测量 baseline
以并发 1 进行测量,保存到 /root/lt3/slow-c1.txt。正常情况下,p95 至少为 0.04 秒,吞吐量不超过 40 RPS(50ms 延迟串行处理,理论值约 20 RPS)。
保存到 /root/lt3/slow-c1.txt。加入 50ms 延迟后,p95 应至少为 40ms,吞吐量应在每秒 20 次左右。数值异常时,请先检查目标端口。
将并发提高 10 倍
以并发 10 进行测量,保存到 /root/lt3/slow-c10.txt。需要确认:吞吐量小于并发 1 时的 2 倍(几乎不变),p95 至少增加 2 倍。吞吐量受限时,等待时间会相应增加。
保存到 /root/lt3/slow-c10.txt。串行处理时,吞吐量几乎不变,等待时间则会增加。如果同时观察到这两点,说明瓶颈在并发能力。
提出瓶颈假设
在 /root/lt3/hypothesis.md 中写出至少 3 个瓶颈候选项,每项以 - 或 1. 开头。候选项必须来自不同层级(并发/thread/worker、CPU、connection pool、lock contention、IO/network 中至少三个层级)。每个候选项还要写明如何验证。
在 /root/lt3/hypothesis.md 中写出来自不同层级的三个候选项及各自验证方法。按顺序检查请求处理路径上的 queue,可避免候选项重复。没有验证方法的假设不算假设。
改为并发处理
创建 /root/lt3/fast.py。要求:使用 ThreadingHTTPServer(或 ThreadingMixIn)支持并发处理;保持 50ms 延迟不变;响应正文包含 fast-app。容器名为 lt-fast,host 端口为 127.0.0.1:8087。以并发 10 测量并保存到 /root/lt3/fast-c10.txt,吞吐量必须至少为 slow-c10.txt 的3 倍。
/root/lt3/fast.py 必须能并发处理请求。保持延迟本身不变。本次改进的核心不是降低延迟,而是消除等待。容器名为 lt-fast,端口为 8087。
改进前后对照表
对 fast app 也以并发 1测量,生成 /root/lt3/fast-c1.txt,然后在 /root/lt3/compare.csv 中整理四行。格式为 app,concurrency,rps,p95,行分别为 slow,1、slow,10、fast,1、fast,10。值必须读取自各结果文件。
fast app 也要再以并发 1 测量一次,然后在 /root/lt3/compare.csv 中整理四行。值必须读取自各结果文件。
计算所需实例数
在 /root/lt3/capacity.txt 中写四行。
target_rps=300(目标峰值流量)measured_rps=——compare.csv中 fast/10 行的 rpssafe_rps=—— 测量值的 70%,舍去小数(整数)instances=—— ceil(300 ÷ safe_rps),即向上取整后的整数
在 /root/lt3/capacity.txt 中写入目标、测量值、安全吞吐量和实例数。直接使用测得的最大值将没有余量,而实例数必须向上取整。
编写分析报告
在 /root/lt3/report.md 中编写报告。需要四节:瓶颈原因、证据(测量值)、措施、容量结论。正文必须原样包含改进前吞吐量、改进后吞吐量、所需实例数这三个数字。
在 /root/lt3/report.md 中写明原因、证据、措施和容量结论。证据必须是数字而不是句子,因此请把前面步骤得到的值原样写入正文。