ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux服务器性能数据分析脚本实战:从采集到瓶颈定位

Linux服务器性能数据分析脚本实战:从采集到瓶颈定位 接手新项目的第一周我就遇到了典型的“盲人摸象”场景线上服务每到晚上九点半用户侧响应时间就从200毫秒飙升到2秒监控面板上CPU使用率确实打满了但打开top只能看到当下那一刻的快照问题发生时人根本盯不住现场。后来我把服务器性能数据的采集、落盘、聚合分析全部脚本化才把“某个时段CPU高”逐步定位到“某个定时任务在做全量日志聚合”。这篇博文就是把我自己写的这套服务器性能数据分析脚本的思路、代码、踩坑经验完整分享一下适合运维工程师、后端开发、SRE以及所有需要搞清楚“服务器到底行不行”的人参考。我始终觉得监控面板只能告诉你“服务器生病了”数据分析脚本才能告诉你“病因是什么”。与其买一堆重型监控系统不如先用脚本把性能数据的底子打好——数据有了后面无论是做告警、做报告还是做容量规划都是水到渠成的事。1. 性能数据分析到底在解决什么问题1.1 监控不等于分析别把两张皮混为一谈很多团队已经装了zabbix或者Prometheus但真到了排查线上卡顿的时候依然要靠人肉登录服务器敲命令。原因很简单监控系统擅长“当前值超标了告警”但不擅长“过去三天这个指标是怎么变化的”“CPU高的时候到底谁在跑”“明天这个趋势会不会打满”。这些问题的答案恰恰需要一套能对历史性能数据做回放和分析的机制也就是脚本要干的事。我在实际项目中遇到最多的三类需求是这样的第一类是性能波动溯源。业务方反馈“每天晚上都慢”你需要在时间轴上找到那段时间里CPU、内存、磁盘、网络各自的表现再看是哪个进程在搞事。第二类是容量规划。领导问你“双十一扛不扛得住”你不能拍脑袋得有“当前峰值是X增长速率是Y按这个速度Z天后到达瓶颈”的推算依据。第三类是变更评估。发版之后性能有没有劣化光靠感觉不行得有发版前后的指标对比。这三类需求其实都可以用一套“采集 分析”的脚本链路来覆盖。采集脚本负责把服务器每一刻的状态变成结构化数据分析脚本负责从结构化的历史数据里找出规律和异常。这就是整个项目的核心价值。1.2 不同角色关心的性能数据其实是不同的可能有人会觉得性能数据不就是CPU、内存、磁盘、网络那几张图嘛。实际接触下来你会发现不同角色关心的重点完全不同这也决定了脚本要有能力从同一份原始数据里抽出不同维度的结论。运维最关心的是系统级指标负载均衡、磁盘IO延迟、网络丢包率、文件句柄数。后端开发更关心进程级甚至线程级指标哪个Java进程的GC线程在偷吃CPU、哪个worker线程阻塞了。管理层关心的是趋势和结论“要不要扩容”“什么时候扩”“扩几台”。我自己最常用的做法是采集脚本把系统级指标和进程级指标都落盘分析脚本再按需聚合——原始数据颗粒度够细后面想怎么切就怎么切。举个很具体的例子。有一次线上服务出现间歇性卡顿系统级CPU、内存看起来都正常后来把进程级数据翻出来一看Nginx某个worker进程的CPU时间片间歇性飙高再往下查发现是access_log切割脚本在同一时刻做了大文件的行数统计。如果没有进程级数据这种问题靠肉眼看top基本不可能复盘。2. 脚本选型和数据采集的方案设计2.1 先想清楚要采集哪些指标再去写代码做采集脚本最容易犯的错就是一上来就写代码想到什么抓什么。我建议先列一个指标清单明确每个指标解决什么问题。下面是我在Linux服务器上常用的采集维度CPU使用率整体使用率、用户态/系统态占比、load average1分钟/5分钟/15分钟内存状态总量、已用、可用、buff/cache、swap使用量磁盘IO每秒读写次数、吞吐量、await时间、util利用率、队列长度网络状态各网卡的吞吐量、丢包率、连接数特别是TIME_WAIT状态的数量进程快照CPU占用前10的进程、内存占用前10的进程记录PID、CPU%、MEM%、启动时间、完整命令行这六类指标涵盖了绝大多数性能问题的排查需要。实际项目中我还会根据业务特点加一些定制采集项比如Java服务会额外采集GC日志、JVM堆使用量数据库服务器会采集慢查询数量等。核心思路是宁可多存一些暂时用不上的字段也不要等出了问题时发现该有的数据没采。2.2 采集工具怎么选系统命令还是psutil在Linux环境下采集脚本的技术选型通常有三个方向纯Shell脚本、Python脚本、或者Shell采集加Python分析混搭。我自己的推荐是混搭——采集阶段能用系统命令就用系统命令分析阶段必须用Python或awk做聚合。Shell配合sar、vmstat、mpstat、pidstat这些命令几乎是零额外依赖任何Linux机器上都能跑。更关键的是这些命令输出的格式稳定非常适合脚本批量处理。举个例子vmstat的每行输出字段位置永远不变用awk就能精准切列省去了自己解析文件的麻烦。Python的psutil库确实更优雅取CPU、内存、进程信息都只要几行代码但缺点是目标服务器上不一定装了psutil生产环境装第三方库要走审批流程对于临时排查来说太重了。所以我的习惯是系统级指标用Shell采集进程级明细用top -b -n 1和ps配合采集复杂的统计分析交给Python来做。这样既保证了兼容性又让后续分析有足够强的处理能力。2.3 一套真实可用的采集脚本直接抄作业下面这套采集脚本我用了很长时间结构很简单但非常稳。它的核心逻辑是在后台每隔10秒采集一次数据输出为CSV格式方便后续分析。直接存成collect_perf.sh加执行权限后运行即可。#!/bin/bash # collect_perf.sh - 服务器性能数据采集脚本 # 用法: nohup bash collect_perf.sh /var/log/perf_data 10 DATA_DIR${1:-/var/log/perf_data} INTERVAL${2:-10} TIMESTAMP$(date %Y%m%d_%H%M%S) SYSLOG_FILE${DATA_DIR}/sys_${TIMESTAMP}.csv PROC_FILE${DATA_DIR}/proc_${TIMESTAMP}.csv MAX_LINES500000 mkdir -p $DATA_DIR # CSV文件头 echo timestamp,cpu_usage,cpu_user,cpu_sys,load1,load5,load15,mem_total,mem_used,mem_available,mem_buff_cache,swap_used,disk_read_kbs,disk_write_kbs,disk_await,disk_util,net_rx_kbs,net_tx_kbs,tcp_timewait $SYSLOG_FILE echo timestamp,pid,proc_name,pid_cpu,pid_mem,cmdline $PROC_FILE while true; do TS$(date %Y-%m-%d %H:%M:%S) # 用vmstat获取CPU和内存的核心指标 VMS$(vmstat 1 2 | tail -1) CPU_IDLE$(echo $VMS | awk {print $15}) CPU_USER$(echo $VMS | awk {print $13}) CPU_SYS$(echo $VMS | awk {print $14}) CPU_USAGE$((100 - CPU_IDLE)) # load average直接读uptime输出 LOADS$(cat /proc/loadavg | awk {print $1,$2,$3}) # 内存指标从/proc/meminfo读取 MEM_TOTAL$(grep MemTotal /proc/meminfo | awk {print $2}) MEM_AVAIL$(grep MemAvailable /proc/meminfo | awk {print $2}) MEM_USED$((MEM_TOTAL - MEM_AVAIL)) MEM_BUFF$(grep -E ^Buffers|^Cached /proc/meminfo | awk {sum$2} END {print sum}) SWAP_USED$(grep SwapTotal /proc/meminfo | awk {print $2}) # 磁盘IO用iostat的最后一组采样 DISK$(iostat -x 1 2 | tail -1 | awk {print $6,$7,$14}) # 网络吞吐量从/proc/net/dev采样计算 NET_RX1$(cat /sys/class/net/eth0/statistics/rx_bytes 2/dev/null || cat /sys/class/net/(ls /sys/class/net | head -1)/statistics/rx_bytes) NET_TX1$(cat /sys/class/net/eth0/statistics/tx_bytes 2/dev/null || cat /sys/class/net/(ls /sys/class/net | head -1)/statistics/tx_bytes) sleep 1 NET_RX2$(cat /sys/class/net/eth0/statistics/rx_bytes 2/dev/null || cat /sys/class/net/(ls /sys/class/net | head -1)/statistics/rx_bytes) NET_TX2$(cat /sys/class/net/eth0/statistics/tx_bytes 2/dev/null || cat /sys/class/net/(ls /sys/class/net | head -1)/statistics/tx_bytes) NET_RX_KB$(((NET_RX2 - NET_RX1) / 1024)) NET_TX_KB$(((NET_TX2 - NET_TX1) / 1024)) # TIME_WAIT连接数 TW_COUNT$(ss -tan state time-wait | wc -l) echo $TS,$CPU_USAGE,$CPU_USER,$CPU_SYS,$LOADS,$MEM_TOTAL,$MEM_USED,$MEM_AVAIL,$MEM_BUFF,$SWAP_USED,$DISK,$NET_RX_KB,$NET_TX_KB,$TW_COUNT $SYSLOG_FILE # 采集CPU和内存占用最高的前10个进程 { echo --- echo $TS top -b -n 1 | head -20 | tail -17 ps aux --sort-%cpu | head -11 } $PROC_FILE # 防止数据文件无限膨胀超过MAX_LINES就滚动 if [ $(wc -l $SYSLOG_FILE) -gt $MAX_LINES ]; then mv $SYSLOG_FILE ${SYSLOG_FILE}.old mv $PROC_FILE ${PROC_FILE}.old echo ——log rotated at $TS—— ${SYSLOG_FILE}.old # 重新创建新文件并写入表头 echo timestamp,cpu_usage,cpu_user,cpu_sys,load1,load5,load15,mem_total,mem_used,mem_available,mem_buff_cache,swap_used,disk_read_kbs,disk_write_kbs,disk_await,disk_util,net_rx_kbs,net_tx_kbs,tcp_timewait $SYSLOG_FILE fi sleep $INTERVAL done这套脚本的输出就是两个文件一个系统级指标CSV一个进程级快照文件。系统级指标格式非常规整便于直接进入分析阶段进程级快照则保留了top和ps的原始格式需要人工复盘的时候直接打开看也很方便。2.4 采样间隔和持续时间怎么定别再拍脑袋采样间隔决定了你能捕捉到多短的现象也决定了数据量的大小。我个人的经验是分场景选择日常巡检场景建议30秒到60秒采一次连续采集几天数据量不大且足够看趋势。故障定位场景建议1秒到2秒采一次只采集问题发生的那个时段争取捕捉到瞬时尖峰。容量评估场景建议5分钟采一次持续采集1到4周维度少一点没关系主要是看长期趋势和增长速率。这里我想特别强调一个很容易被忽略的点采样的持续时间一定要大于出问题的时间尺度。如果你要排查的是每天晚上持续1小时的慢请求那采集脚本至少要连续跑3天才能覆盖业务波峰波谷的完整规律。很多人跑个十几分钟没发现问题就放弃了这其实是在做无用功。3. 数据分析的核心方法与瓶颈定位3.1 先看趋势再找拐点最后定位异常段数据采回来之后最忌讳的就是直接打开文件一行行看。几千行CSV人工根本看不过来必须让脚本替你做第一轮筛选。我常用的Python分析脚本会先做三件事加载CSV、重采样到分钟级、计算每个指标的趋势基线。比如load average分析脚本会计算整个周期的均值、P90、P95和最大值同时检测连续N分钟超过阈值的“高位区间”。如果发现某天的21:30到22:00持续高位脚本就会自动把这个时间段标记为“异常窗口”然后单独输出这个窗口内的明细数据。这比我人工从监控图里用眼睛找要快得多。趋势分析还有一个好处就是能发现那些缓慢恶化的问题。我记得有一次帮客户看数据库服务器平均响应时间从月中的10毫秒慢慢涨到了月末的18毫秒每天涨一点点完全不会触发告警。但用脚本拉出两周的趋势图那条接近45度的斜线非常刺眼。顺着这个趋势往下查发现数据量增长后某些SQL的索引失效了。这种“温水煮青蛙”式的性能劣化只有脚本分析才能快速发现。3.2 CPU问题的两层定位法从“高”到“谁干的”如果CPU使用率打满光知道CPU高没有任何意义必须往下拆两层第一层拆CPU的时间片构成。看用户态CPU和系统态CPU的比例就能判断是业务计算密集还是内核调用频繁。用户态高通常意味着有程序在做大量运算系统态高则可能是频繁的系统调用、上下文切换或者中断处理。vmstat里还有个字段叫cscontext switch如果这个数字非常大说明进程在不断切换很可能是锁竞争或者线程池配置不合理。第二层拆进程。用mpstat或者pidstat找到CPU占用最高的进程再结合这个进程的运行特征判断是不是异常。脚本层我会用ps按CPU排序取前10同时把每个进程对应的完整命令行记录下来。有一次排查内存泄漏正是靠脚本记录的进程快照发现某个Python进程的RSS值在持续上涨而restart次数始终为0最终定位到是代码里一个全局缓存在无界增长。CPU分析有一个常见的误区不能只看某时刻的CPU尖峰要看持续时长。如果CPU只是偶发了两分钟的高可能是某个批量任务正常跑完就结束不算故障。但如果CPU高持续了30分钟以上且用户态占比超过70%大概率是有死循环或者计算逻辑出现问题。分析脚本里我一般会设置一个“持续性检测”——CPU使用率高于85%且持续时间超过15分钟才判定为一次“CPU持续告警事件”靠这个过滤掉了大量无意义的瞬时抖动。3.3 内存和磁盘分析不要被“内存不足”四个字骗了内存分析最容易踩的坑是把buff/cache当成“已用内存”来报警。Linux内核会把空闲内存的一部分用作文件缓存这部分内存在应用需要时可以立刻释放。所以分析内存时一定要看MemAvailable而不是简单地用MemTotal减去MemFree。采集脚本里我特意加上了MemAvailable字段就是为了避免这个误判。真正的内存问题通常有两个征兆swap使用量持续上升说明物理内存确实吃紧系统在把进程内存页换到磁盘上某个进程的RSS值单调递增且进程重启后回落说明大概率存在内存泄漏。我分析内存问题时会先用脚本把所有进程按RSS排序找到那个“体重”持续增加的进程再结合它的命令行和重启历史做判断。磁盘IO分析的重点是await和util。util接近100%说明磁盘已经接近饱和但还要区分是持续饱和还是间歇性饱和。持续饱和通常是业务量的真实增长或者慢日志把磁盘拖垮间歇性饱和往往和定时任务有关。我记得有个客户半夜两点磁盘util冲到99%白天只有30%后来脚本把时间维度一拉才发现是凌晨的备份任务和日志归档任务撞在同一个时间窗口里了。这在大型服务器上是常见问题多个定时任务同时触发IO叠加后就是一次尖峰。网络指标里我最关注的是TIME_WAIT连接数。高并发短连接场景下TIME_WAIT状态如果持续堆积到几万个新的连接就可能因为没有可用端口而失败。脚本里专门加了这个字段就是为了在这种问题发生时能立刻看到证据。分析脚本可以进一步做趋势对比如果TIME_WAIT数随并发量成比例增长且居高不下就需要考虑调整内核网络参数了。4. 从原始数据到可视化报告的一站式方法4.1 用Python脚本把CSV变成一份摘要报告采集数据多了以后分析结果如果只停留在终端输出累眼还容易错过重点。我最常用的做法是写一个Python脚本定期把当天的数据读进来统计关键指标输出成一份Markdown或者HTML摘要报告。下面是一个简化的分析脚本示例#!/usr/bin/env python3 # analyze_perf.py - 性能数据分析脚本 import csv, sys from collections import defaultdict from datetime import datetime def load_sys_data(filepath): data [] with open(filepath, r) as f: reader csv.DictReader(f) for row in reader: row[timestamp] datetime.strptime(row[timestamp], %Y-%m-%d %H:%M:%S) row[cpu_usage] int(row[cpu_usage]) row[load1] float(row[load1]) row[mem_available] int(row[mem_available]) row[disk_await] float(row[disk_await]) data.append(row) return data def summarize(data): stats {} stats[cpu_avg] sum(d[cpu_usage] for d in data) / len(data) cpu_sorted sorted(d[cpu_usage] for d in data) stats[cpu_p95] cpu_sorted[int(len(cpu_sorted) * 0.95)] stats[load_max] max(d[load1] for d in data) mem_min min(d[mem_available] for d in data) stats[mem_min_available_mb] mem_min // 1024 await_high [d[timestamp] for d in data if d[disk_await] 20] stats[disk_await_high_count] len(await_high) if await_high: stats[disk_await_first_high] await_high[0] # 连续高位区间检测 high_runs [] run_start None for d in data: if d[cpu_usage] 80: if run_start is None: run_start d[timestamp] else: if run_start: run_end d[timestamp] duration (run_end - run_start).total_seconds() / 60 if duration 5: high_runs.append((run_start, run_end, duration)) run_start None stats[cpu_high_runs] high_runs return stats if __name__ __main__: stats summarize(load_sys_data(sys.argv[1])) print(fCPU平均使用率: {stats[cpu_avg]:.1f}% P95: {stats[cpu_p95]}%) print(fload最大值: {stats[load_max]}) print(f最低可用内存: {stats[mem_min_available_mb]}MB) print(f磁盘await20ms次数: {stats[disk_await_high_count]}) for start, end, dur in stats[cpu_high_runs][:5]: print(fCPU持续高位: {start} - {end} 时长{dur:.0f}分钟)这个脚本的输出就是一行行的结论不需要肉眼看几千行原始CSV。报告里的“CPU持续高位区间列表”尤其有价值它能直接告诉你看哪段时间的日志、查哪段时间的业务逻辑。把这些结果接入钉钉或者邮件系统每天早上定时推给相关同事一个最基础的性能日报就成型了。4.2 原始数据保留多久仓库怎么清理数据分析里有一个容易被忽略的工程问题数据量增长之后怎么办。按10秒采一次每天8640行系统级CSV加约4000次进程快照一个月下来大概会占用几百MB到1GB不等。如果是多台服务器体积会线性增长。我的经验是制定一个分级保留策略。原始CSV保留最近30天用于近期的故障回溯超过30天的数据聚合成小时级汇总表只保留平均值、最大值、最小值、P95这几个统计字段这个级别的数据保留6个月超过6个月的直接删除或者丢到冷存储。做这个策略的原因很简单性能问题排查基本聚焦在最近一两周内更早的数据只用来做趋势判断精确到秒级的原始数据毫无意义。滚动清理可以通过简单的find加crontab实现也可以用Python脚本在采集时顺手判断。关键是要把“保留原始数据”和“控制磁盘占用”平衡好别等服务器跑了一个月才发现数据盘被采集日志塞满。4.3 要不要上Grafana什么时候上脚本分析不等于排斥可视化平台。我自己在服务器数量超过10台之后就引入了Prometheus加Grafana做实时监控面板但脚本分析依然保留着——因为Grafana擅长的是“现在怎么样”而脚本擅长的是“过去到底发生了什么”。两者定位不同不冲突。Lightweight方案可以这样过渡刚开始数据量小就用采集脚本加Python分析等到需要长时间盯业务高峰了就部署Prometheus node_exporter接入Grafana再往后需要做历史数据回放和复杂报告脚本依然是最灵活的工具。Grafana的查询语法虽然支持downsample和聚合但要做“连续高位区间检测”这类自定义逻辑远不如写一段Python方便。5. 自动化调度与告警阈值设计5.1 crontab定时任务搜集群里的定时执行方案分析脚本要发挥长期价值必须自动化。我通常设计三个crontab任务第一个任务负责启动采集脚本并确保它不会重复运行。由于采集脚本本身有循环最好用nohup加后台方式启动。如果服务器重启了crontab的reboot时间可以自动把采集脚本拉起来reboot sleep 30 nohup bash /opt/scripts/collect_perf.sh /var/log/perf_data 10 /var/log/perf_data/collect.log 21 第二个任务负责每天凌晨跑一次数据分析把前一天的摘要报告生成出来。为了确保采集数据完整分析任务一定要放在采集数据跨越零点之后比如凌晨1点30分。第三个任务负责定时清理过期数据我一般放在每周日凌晨4点避开业务高峰。这里要特别提醒一个crontab的坑crontab默认的PATH非常精简直接在里面调python3或者iostat可能找不到可执行文件。稳妥的写法是在脚本开头加上完整的PATH声明#!/bin/bash PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin另外如果用了conda或者pyenv之类的Python环境crontab里必须写解释器的绝对路径不然会出现“任务不执行但手动执行正常”的诡异现象。5.2 告警阈值怎么定给指标一个“合理范围”而不是一个固定值阈值定多少最合理这是新手最容易纠结的问题。我的建议是不要拍脑袋而是基于历史数据算出基线。比如CPU使用率先采集两周以上的数据取正常的P95作为参考值再给这个参考值加上20到30个百分点的缓冲。例如正常业务P95是50%那告警阈值可以设在80%左右。这个方法比网上直接抄一个“CPU大于80%就告警”靠谱得多因为每台服务器的业务特征完全不同。同理磁盘await的告警阈值一般在20-30毫秒超过这个范围说明IO路径存在明显延迟。但是SSD和机械盘的阈值又不一样SSD的await通常是个位数毫秒机械盘即使在正常状态下也可能有10-20毫秒。所以阈值要分类型配置。我自己的告警脚本长这样逻辑非常简单但实测很有效#!/bin/bash # alert_check.sh - 性能阈值告警脚本 DATA_FILE$(ls -t /var/log/perf_data/sys_*.csv | head -1) LAST_LINE$(tail -1 $DATA_FILE) CPU_USAGE$(echo $LAST_LINE | awk -F, {print $2}) LOAD_1$(echo $LAST_LINE | awk -F, {print $5}) MEM_AVAIL_MB$(echo $LAST_LINE | awk -F, {print $9}) DISK_AWAIT$(echo $LAST_LINE | awk -F, {print $15}) if [ $CPU_USAGE -gt 85 ]; then echo CPU告警: ${CPU_USAGE}% at $(date) /var/log/perf_data/alerts.log fi if [ $LOAD_1 -gt 4 ]; then echo Load告警: ${LOAD_1} at $(date) /var/log/perf_data/alerts.log fi if [ ${MEM_AVAIL_MB} -lt 1048576 ]; then echo 内存告警: 可用内存不足${MEM_AVAIL_MB}MB at $(date) /var/log/perf_data/alerts.log fi通过crontab每分钟跑一次这个脚本告警日志就自动积累下来了。如果接了钉钉或者企业微信的Webhook再把告警动作改成发消息就行。但要注意告警一定要设持续时间过滤避免“单次尖峰就轰炸”的问题。我在生产环境里的做法是告警触发后至少持续3分钟才真正发出通知3分钟内恢复了就只记录不通知。5.3 防重复执行与进程守卫别让脚本克死自己采集脚本如果出现重复运行会出现两个进程同时写同一个文件数据就会错乱。我推荐在脚本启动时加一个PID锁LOCK_FILE/var/run/collect_perf.lock if [ -f $LOCK_FILE ] kill -0 $(cat $LOCK_FILE) 2/dev/null; then echo collect_perf.sh already running, exit exit 1 fi echo $$ $LOCK_FILE trap rm -f $LOCK_FILE EXIT这段代码的含义是如果锁文件存在且对应PID的进程还活着就认为脚本已经在运行直接退出。脚本退出时通过trap清理锁文件避免锁残留。生产环境里脚本崩溃、锁文件残留的情况我遇到过好几次trap清理这个细节必须有。还有一个细节是日志输出。采集脚本的nohup输出要重定向到指定日志文件不要直接丢弃不然脚本报错时你连日志都找不到。告警脚本的日志也要分开记录别跟性能数据混在一个文件里。6. 常见问题排查与独家避坑经验6.1 脚本采着采着没数据了多半是这几个原因我自己踩过的坑里采集中断最常见的原因是服务器重启之后采集进程没有拉起。所以crontab里的reboot一定要配好而且要用sleep 30延迟启动等系统的网络和服务就绪后再拉起采集否则iostat和ss这些命令可能暂时不可用导致采集失败。第二个常见原因是磁盘空间被数据占满。有一些机器原本磁盘余量就紧张采集脚本又格外老实10秒钟写一次数据一个月下来可能多占用1G左右。如果不加控制最终就是数据写入失败但脚本浑然不觉。所以一定要在采集脚本里加上磁盘空间检测剩余空间低于阈值就自动清理最老的日志文件。第三个原因比较隐蔽iostat和vmstat依赖sysstat包有些精简安装的服务器根本没装。脚本执行到iostat时直接报command not found但前面的数据还在正常采集导致CSV某几列出现空值分析阶段一算就报错。我的建议是脚本开头做一次依赖检查发现缺了就明确提示安装不要默默吞掉错误继续跑。6.2 时间戳对齐多台服务器分别采集时间不同步分析全废我第一次做多台服务器联合分析的时候遇到了一个让人哭笑不得的问题A服务器和B服务器的时间差了5分钟两边采集的数据时间轴对不上分析脚本里用时间关联CPU高负载和慢请求时就完全对不上号。排查了一圈才发现是其中一台服务器NTP失效了。所以采集脚本在每轮循环的开头一定要先读取系统时间再打时间戳同时定时任务里建议配置NTP同步检查。分析脚本在合并多台服务器数据的时候统一用UTC时间戳不做本地时区转换这样也能避免时区带来的偏移。数据落盘时除了记录时间戳我还会额外记录一个time_source字段方便追溯是哪台服务器上报的数据。6.3 采集本身会影响性能吗结论是可以忽略但要注意总有人担心采集脚本影响线上服务性能这个担心有道理但容易过度。像vmstat、iostat这类命令本质上就是读取/proc下的虚拟文件CPU开销在毫秒级别完全可以忽略。真正要注意的是别在循环里频繁调用top -b -n 1这个命令会遍历所有进程开销比读取/proc文件大得多。top一次大概几毫秒如果每1秒跑一次确实会引入可感知的CPU消耗。我的做法是把top的采集频率降到和主循环一样主循环10秒一次top也10秒一次。这样带来的额外CPU开销在0.1%以下对于生产服务器来说完全可以接受。更精细的方案是把进程快照的采集间隔独立拉开比如系统级指标每10秒采一次进程级快照每60秒采一次这样在保证数据充足的前提下进一步降低采集开销。6.4 MySQL和Java进程的特殊采集技巧如果服务器上跑的是MySQL我建议额外采集threads_connected和slow_queries这两个指标。连接的线程数持续飙升在锁竞争或者连接池配置问题中是重要的印证信号。Java服务则建议采集GC日志重点观察Full GC的频率和时长一次Full GC超过1秒对响应时间的影响就是灾难级的。Java进程采集有一个特别的地方直接用top看Java进程的CPU%是整机所有线程的总和看不出是哪根线程在折腾。分析的时候可以加一步——用top -H -p 找到CPU最高的几个线程ID再把线程ID转成十六进制去jstack线程栈里找到对应线程的名字和堆栈。这个“线程级定位”的思路很实用脚本做好这一层数据记录排查问题时能省很多事。7. 代码组织和工程化脚本不只是能用还要能维护7.1 把参数抽出去别写死在一堆代码里采集脚本和分析脚本写完之后如果只在自己机器上跑怎么写都无所谓。但一旦要部署到多台服务器上代码规范就显得很重要。我的经验是把所有可配置的东西统一放进一个配置文件脚本运行时读配置不要散落在各个地方的判断逻辑里。# config.sh - 性能采集配置 DATA_DIR/var/log/perf_data COLLECT_INTERVAL10 PROC_TOP_N10 CPU_ALERT_THRESHOLD80 LOAD_ALERT_THRESHOLD4 MEM_ALERT_THRESHOLD_MB20480 RETENTION_DAYS30 SCRIPT_LOG/var/log/perf_data/script.log这样在不同服务器上部署时只需要改配置文件的参数不用动代码。尤其是指标阈值、采集间隔、保留天数这些每次部署都要变的参数写代码里就相当于给自己挖坑。7.2 注释和日志规范半年之后你还能看懂自己的脚本给脚本写注释这件事听起来很基础实际操作中最容易被忽略。我自己有一个体会三个月前写的分析脚本如果不写清楚输入输出的字段含义三个月后我自己都要靠回忆才能看懂。所以我坚持在脚本的头部加一个文档块说明这个脚本是干什么的、输入参数是什么、输出文件是什么格式、示例用法是什么。另外脚本的关键输出一定要带日志哪怕只是简单地打印到screen。我在生产环境里遇到过告警脚本正常运行但这个告警被忽略了的情况因为没有日志记录根本不知道告警是什么时候发的、为什么发。加一行echo加时间戳的日志排查问题时就能按时间对齐事件序列这是非常值得做的工程投入。7.3 从单机脚本到小型平台的扩展路径当脚本从1台服务器扩展到10台、50台的时候每台服务器各自跑一套脚本的模式就会变得难以管理。这时候可以考虑做一个小型的采集汇总层每台服务器依然是本地采集但分析脚本通过SSH批量拉取各服务器的CSV到一台中控机器上进行汇总。这样做的好处是每台服务器只做最轻量的事汇总和分析集中在中控机上便于权限控制。再往后就是引入时序数据库了。这里我特别推荐先用简单方案跑通流程不要一上来就上分布式系统。先用CSV文件把正确的采集、分析逻辑验证好后续需要横向扩展时把采集脚本的写入端从CSV换成TSDB的HTTP接口就行分析逻辑基本可以复用。我见过不少团队在数据量只有几十GB的时候就开始上大数据组件最后发现维护成本远高于收益反而数据全乱了。一步一步来不丢人。我做服务器性能数据分析脚本最大的体会是工具链可以很简单但数据意识一定要有。不管用什么脚本、什么平台只要能把性能数据稳定地采下来、按合理的逻辑分析、形成可追溯的报告就已经解决了80%的运维难题。甚至很多时候一套Shell加Python的组合比那些昂贵的商业监控软件还好使——因为它完全长在你的业务场景里。最后分享一个我自己的习惯每次做完一次性能问题复盘我都会把分析脚本再改一版把这次发现的新规律沉淀成可复用的判断条件。脚本越用越聪明服务器性能问题也会越查越顺手。
返回列表