欢迎来到思维库

思维库

老杨教你快速揪出系统链路里的那个"拖后腿"的家伙

时间:2025-11-03 20:22:48 出处:探索阅读(143)

运维人对于自己负责的老杨路里业务线架构理解决定了自己的薪资和可替代性.

老杨见过太多的"盲人摸象"式排查。其实找到系统链路中的教快薄弱环节,真的速揪不需要凭运气和直觉。

今天老杨就来聊聊如何避免盲人摸象式的出系排查。

老杨的统链核心思路

我的核心逻辑很简单——先搞清楚调用关系,用数据定位问题区域,个拖然后从资源和网络层面确认根因。后腿伙听起来简单,老杨路里但魔鬼都在细节里。教快

老杨用的速揪啥工具

说实话,工具选择这事儿得接地气:

监控指标: Prometheus + Grafana(这个组合基本是出系标配了)链路追踪: SkyWalking居多(国内用得多,中文文档也全)日志收集: 有条件上ELK,统链简单场景直接kubectl logs压测工具: hey、个拖wrk这些轻量级的后腿伙就够用系统诊断: top、iostat、老杨路里vmstat、ss这些老朋友数据库分析: MySQL的慢查询日志配合pt-query-digest老杨的五步排查法

第一步:把服务调用关系搞清楚

这是最关键的一步。很多人上来就开始瞎猜,其实你得先知道A服务到底调用了哪些下游服务。免费信息发布网

如果你用的是SkyWalking或者Jaeger,它们都有现成的服务拓扑图。我一般会先用API拉取服务列表:

复制# 获取所有服务名称 $ curl -s http://jaeger-query:16686/api/services | jq .1.2.

输出大概是这样:

复制[ "order-service", "user-service", "payment-service", "product-service" ]1.2.3.4.5.6.

没有链路追踪系统也没关系,在K8s环境下可以通过service来大致了解调用关系:

复制$ kubectl get svc -A -o wide | head -51. 复制NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE prod order-svc ClusterIP 10.96.23.12 <none> 80/TCP 120d prod user-svc ClusterIP 10.96.45.88 <none> 80/TCP 120d ...1.2.3.4.

有了这个"怀疑名单",后面的排查就有方向了。

第二步:用指标数据找热点

这一步是要找出到底哪个服务的延迟异常高或者错误率飙升。Prometheus在这里就派上用场了。

我经常用的一个查询是看各服务的95分位延迟:

复制$ curl -s http://prometheus:9090/api/v1/query?query=histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le,service))1.

假设得到的结果是:

复制{ "status":"success", "data":{"result":[ {"metric":{"service":"order-service"},"value":[1620000000,"0.42"]}, {"metric":{"service":"payment-service"},"value":[1620000000,"1.85"]}, {"metric":{"service":"user-service"},"value":[1620000000,"0.10"]} ]} }1.2.3.4.5.6.7.8.

一眼就能看出来,payment-service的95分位延迟是1.85秒,比其他服务高出太多了。这基本就锁定了问题范围。

同时我也会查一下错误率:

复制$ curl -s http://prometheus:9090/api/v1/query?query=sum(rate(http_requests_total{code=~"5.."}[5m])) by (service)1.

第三步:用链路追踪还原慢调用的详细过程

有了怀疑目标,接下来就要看具体是哪个环节慢了。链路追踪在这里特别有用,能告诉你时间都花在哪儿了。

复制# 查询payment-service的慢请求追踪 $ curl -s http://jaeger-query:16686/api/traces?service=payment-service&limit=5&duration=300000 | jq .data[0]1.2.

典型的网站模板输出可能是:

复制{ "traceID":"abcd1234", "spans":[ {"operationName":"HTTP GET /order","duration":120000, "process":{"serviceName":"order-service"}}, {"operationName":"HTTP POST /payment/charge","duration":110000, "process":{"serviceName":"payment-service"}}, {"operationName":"SELECT FROM payments","duration":105000, "process":{"serviceName":"mysql"}} ] }1.2.3.4.5.6.7.8.

这个追踪结果就很清晰了:总耗时120ms,其中110ms花在payment服务的charge操作上,而charge操作里又有105ms是在等MySQL的SELECT查询。问题基本定位到数据库层面了。

第四步:看日志确认具体异常

光有数字还不够,得看看具体的错误信息和异常栈。

复制$ kubectl logs deploy/payment-deployment -n prod --tail 200 | grep -i error1.

经常能看到这样的日志:

复制2024-12-01T10:12:05Z ERROR PaymentProcessor.java:78 - Timeout when calling db: SELECT * FROM payments WHERE user_id=? (timeout 1000ms) 2024-12-01T10:12:06Z WARN RateLimiter - Connection retry attempt 3/3 failed ...1.2.3.

到这里基本就能确认是数据库连接超时导致的问题了。

第五步:从系统资源层面确认根因

最后还得确认是应用逻辑的问题,还是底层资源不够用了。

复制# 看Pod的资源使用情况 $ kubectl top pod payment-deployment-xxxxx -n prod # 如果能SSH到宿主机,看看IO情况 $ iostat -x 1 21.2.3.4.5.

假设得到:

复制# kubectl top pod输出 NAME CPU(cores) MEMORY(bytes) payment-deployment-xxxxx 900m 1.2Gi # iostat输出显示磁盘IO等待时间很高 Device r/s w/s await sda 10.2 85.3 60.321.2.3.4.5.6.7.

如果看到await这么高(60ms),基本就是磁盘IO成为瓶颈了。可能是数据库所在的存储性能跟不上,或者有其他进程在疯狂写磁盘。

几个常见坑和对应的排查命令

(1) CPU被限流了(很容易被忽略)

这个问题特别隐蔽,容器看起来CPU使用率不高,但实际上被cgroup限流了。IT技术网

复制# 找到容器ID $ docker ps --format {{.ID}} {{.Names}} | grep payment c3f2a7b9db9a payment-deployment-xxxxx # 查看CPU统计 $ cat /sys/fs/cgroup/cpu,cpuacct/docker/c3f2a7b9db9a/cpu.stat1.2.3.4.5.6.

如果看到:

复制nr_periods 240 nr_throttled 56 throttled_time 480000001.2.3.

nr_throttled大于0就说明被限流了。这时候要么调整Pod的resource limits,要么考虑换到CPU资源更充足的节点。

(2) 网络延迟和丢包

有时候问题出在网络层面,特别是在容器环境下。

复制# 从Pod内部ping一下下游服务 $ kubectl exec -it payment-pod -- ping -c 5 mysql-0.mysql.prod.svc.cluster.local # 测试网络带宽 $ iperf3 -c 10.10.0.5 -p 5201 -t 101.2.3.4.5.

如果ping的时延超过几十毫秒,或者iperf3显示带宽明显偏低,就要怀疑网络或者CNI插件的问题了。

(3) 数据库慢查询

数据库问题基本占了我遇到过的性能问题的一半以上。

复制# 看看有多少慢查询 $ mysql -e "SHOW GLOBAL STATUS LIKE Slow_queries;" # 用pt-query-digest分析慢日志(这个工具真的好用) $ pt-query-digest /var/log/mysql/slow.log | head -501.2.3.4.5.

pt-query-digest的输出特别直观:

复制# Query 1: 0.12 QPS, 1.45x concurrency, ID 0x8DFD0D6B15D10734... # Time range: 2024-12-01T09:00:00 to 2024-12-01T10:00:00 # Response time: 95% 1.2s, 99% 2.5s SELECT user_id, payment_status FROM payments WHERE created_at > ?1.2.3.4.

一看就知道这个查询占用了绝大部分数据库时间,而且响应时间很高。

写一个快速诊断的示例脚本

把上面这些检查项串起来,写成一个脚本,这样每次遇到问题都能快速过一遍:

复制#!/bin/bash # quick_diagnose.sh - 老杨的快速诊断脚本 SERVICE_NS=prod SERVICE_NAME=payment-deployment PROM_URL=http://prometheus:9090 JAEGER_URL=http://jaeger-query:16686 echo"=== 资源使用情况 ===" kubectl top pod -l app=${SERVICE_NAME} -n ${SERVICE_NS} || echo"无法获取资源信息" echo -e "\n=== 延迟情况 ===" curl -s "${PROM_URL}/api/v1/query?query=histogram_quantile(0.95,sum(rate(http_request_duration_seconds_bucket{service="payment-service"}[5m])) by (le))" | jq -r .data.result[0].value[1] // "无数据" | awk {printf "95分位延迟: %.2fsn", $1} echo -e "\n=== 最新错误日志 ===" kubectl logs -l app=${SERVICE_NAME} -n ${SERVICE_NS} --tail 20 | grep -i "error\|exception\|timeout" | tail -5 echo -e "\n=== 节点IO情况 ===" NODE=$(kubectl get pod -l app=${SERVICE_NAME} -n ${SERVICE_NS} -o jsonpath={.items[0].spec.nodeName}) if [ ! -z "$NODE" ]; then echo"节点: $NODE" ssh $NODE"iostat -x 1 1" 2>/dev/null || echo"无法连接到节点" fi1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.

这个脚本跑一遍,基本就能对问题有个大概的了解了。

决策优先级:先修什么后修什么

排查出问题后,还要决定先解决哪个。我一般按这个优先级:

影响面最大的先修:如果错误率很高,影响大量用户,这个优先级最高下游问题优先于上游:链路追踪显示是数据库慢,就先优化数据库,别在应用层绕弯子资源瓶颈优先于代码优化:CPU、IO、网络这些基础资源问题不解决,代码优化效果有限止血优先于根治:比如先把超时时间调短、重试次数减少,避免雪崩,然后再慢慢优化建立长效机制,别让问题重复出现

光解决问题还不够,还得防止类似问题再次发生:

设置监控告警:关键服务的P95延迟、错误率都要有告警阈值,出问题了第一时间知道。建立SLO:比如订单服务的P99延迟不超过500ms,支付服务的可用性不低于99.9%。有了具体目标,优化就有方向了。定期压测:每次发版前,或者定期在低峰期做一次压测,确保系统能承受预期的流量。记录排查过程:每次解决问题后,都把排查过程和解决方案记录下来。下次遇到类似问题,直接参考之前的经验。

分享到:

温馨提示:以上内容和图片整理于网络,仅供参考,希望对您有帮助!如有侵权行为请联系删除!

友情链接: