AWS EC2上如何监控Python脚本的已打开文件与磁盘带宽?
针对你在AWS EC2 Ubuntu 18.04上遇到的Python脚本磁盘读带宽过高、耗尽EBS burst_balance导致系统不稳定的问题,我整理了几个实用的定位工具和方法,帮你精准找到带宽消耗的源头:
这些工具不需要修改Python代码,直接在终端运行就能快速排查:
iostat:先确认整体磁盘的读负载情况,运行命令:
iostat -x 5-x显示扩展统计信息,5表示每5秒刷新一次。重点看rMB/s列,这就是每秒磁盘读取的MB数,先确认峰值是否和你观察到的130MiB/s匹配。iotop:实时查看进程级的IO占用,直接运行:
iotop界面会显示每个进程的
DISK READ速率,按大写O可以按IO使用率排序,快速找到占用读带宽最高的Python进程。记下对应的PID,后续可以进一步分析它的文件操作。lsof:查看目标进程打开的所有文件,比如你找到的Python进程PID是
1234,运行:lsof -p 1234 | grep REGREG类型代表普通磁盘文件,这样就能列出该进程正在读写的所有文件,结合iotop的带宽数据,就能初步锁定可疑文件。pidstat:针对特定进程做持续IO统计,比如监控PID为1234的进程:
pidstat -d 5 -p 1234-d表示聚焦IO统计,5秒刷新一次,能看到该进程的实时读带宽(kB_rd/s列,转成MB的话除以1024),方便观察带宽变化趋势。
如果想从脚本内部或单独监控脚本的文件读写行为,可以用这些方法:
psutil库:这是一个强大的跨平台系统监控库,能获取进程的IO统计和打开的文件列表。先安装:
pip install psutil可以写一个简单的监控脚本,或者嵌入到你的主程序中:
import psutil import time # 监控当前Python进程(如果监控其他进程,替换成目标PID) target_pid = psutil.Process().pid prev_read_bytes = psutil.Process(target_pid).io_counters().read_bytes while True: time.sleep(1) curr_read_bytes = psutil.Process(target_pid).io_counters().read_bytes read_speed_mb = (curr_read_bytes - prev_read_bytes) / 1024 / 1024 print(f"当前读带宽: {read_speed_mb:.2f} MB/s") print("已打开的文件:") for file in psutil.Process(target_pid).open_files(): print(f" {file.path}") prev_read_bytes = curr_read_bytes这个脚本会实时输出读带宽和当前打开的文件,帮你关联文件与带宽消耗。
自定义文件读取追踪器:如果想精准统计每个文件的总读取量,可以包装Python的
open函数,记录每个文件的读取字节数:import builtins import os import time # 存储每个文件的读取字节数 file_read_stats = {} original_open = builtins.open def tracked_open(path, *args, **kwargs): file_obj = original_open(path, *args, **kwargs) # 只跟踪读模式的文件 mode = args[0] if args else kwargs.get('mode', 'r') if 'r' in mode: abs_path = os.path.abspath(path) file_read_stats[abs_path] = file_read_stats.get(abs_path, 0) # 包装read方法 original_read = file_obj.read def tracked_read(size=-1): data = original_read(size) file_read_stats[abs_path] += len(data) return data file_obj.read = tracked_read # 同理包装readline、readlines等方法(如果需要) original_readline = file_obj.readline def tracked_readline(): line = original_readline() file_read_stats[abs_path] += len(line) return line file_obj.readline = tracked_readline return file_obj # 替换系统open函数 builtins.open = tracked_open # ---------------------- # 这里运行你的主脚本代码 # ---------------------- # 定期打印统计结果 while True: print("\n文件读取统计(MB):") # 按读取量从大到小排序 for path, bytes_read in sorted(file_read_stats.items(), key=lambda x: -x[1]): print(f" {path}: {bytes_read / 1024 / 1024:.2f} MB") time.sleep(5)这个方法能精准定位哪些文件被读取了大量数据,尤其是你提到的200个小文件,如果被反复读取,这里会清晰显示出来。
- 区分缓存读取与实际磁盘IO:Ubuntu会用空闲内存做文件缓存,有时候看似高读带宽,其实是从缓存读取,不会消耗EBS的burst_balance。可以用
vmstat 5查看bi列(单位KB),bi*1024就是每秒实际从磁盘读入的字节数,这个才是真正影响burst_balance的数值。 - 检查重复读取逻辑:你提到的200个小文件总容量最多60MB,持续半小时130MiB/s的读速率显然是重复读取导致的。用上面的工具找到被频繁读取的文件,再检查脚本中是否有循环读取、重复加载的逻辑。
- EBS卷类型优化:如果最终确认是业务需要持续高读IO,考虑将gp2卷换成gp3卷,gp3的burst性能更稳定,不会轻易耗尽burst_balance。
内容的提问来源于stack exchange,提问作者draufunddran

