Ubuntu环境下os.path.exists与os.makedirs结果不符问题排查求助
问题分析与解决方案
这是一个典型的竞态条件问题,不过结合你提到的AWS EBS存储和Zabbix的I/O等待告警,咱们得从代码和存储两个层面来拆解:
1. 代码层面的核心问题:竞态条件
你写的代码逻辑是先判断父目录是否存在,不存在就创建,但os.path.exists()和os.makedirs()这两个操作之间存在时间窗口——哪怕这个窗口很短,也可能有其他进程(或者你自己程序的其他线程)在这段时间里创建了目标目录。当os.makedirs()执行时,发现目录已经存在,就会抛出OSError: [Errno 17] File exists。
另外还要注意一种极端情况:如果目标路径不是目录而是一个文件,os.path.exists()会返回True,但os.makedirs()还是会报错,不过从你的报错信息看,路径是目录,所以主要还是竞态的锅。
2. AWS EBS与Zabbix告警的关联
Zabbix提示磁盘写入CPU I/O等待时间过高,说明你的EBS卷正处于高负载状态——可能是IOPS不够用、网络延迟高,或者存储本身出现短暂的性能波动。这种情况下,os.path.exists()的元数据检查和os.makedirs()的目录创建操作之间的时间间隔会被拉长,竞态条件触发的概率就会大幅提升。
虽然EBS是强一致性存储,但高负载下元数据的同步可能出现短暂延迟,也可能让os.path.exists()的结果和实际存储状态出现偏差,不过这不是主要原因,核心还是代码的竞态漏洞被存储问题放大了。
3. 是代码问题还是存储问题?
两者都有:
- 代码本身存在竞态缺陷,这是根本原因——哪怕存储性能再好,只要有并发操作,就可能触发这个错误;
- EBS的高I/O等待是诱因,它让竞态发生的概率变高,原本很少出现的错误变得频繁。
4. 解决办法
修复代码的竞态问题
最直接的方式是利用Python内置的安全机制,避免先检查后创建的逻辑:
- 方法一:使用
os.makedirs的exist_ok=True参数(Python 3.2+支持),这样即使目录已存在也不会报错:parent_dir = os.path.abspath(os.path.join(file_path, os.pardir)) os.makedirs(parent_dir, exist_ok=True) - 方法二:用try-except捕获特定错误,兼容更低版本的Python:
import errno parent_dir = os.path.abspath(os.path.join(file_path, os.pardir)) try: os.makedirs(parent_dir) except OSError as e: # 只忽略"目录已存在"的错误,其他异常正常抛出 if e.errno != errno.EEXIST: raise
排查EBS存储问题
- 检查EBS卷的IOPS配置:如果用的是通用型SSD (gp2/gp3),看看是否达到了IOPS上限,考虑升级到Provisioned IOPS SSD (io1/io2);
- 检查挂载点状态:确认EBS卷挂载稳定,没有出现短暂的断开重连情况;
- 分析系统I/O:用
iostat、iotop工具排查具体是哪个进程在占用大量I/O,优化读写逻辑。
内容的提问来源于stack exchange,提问作者adamczi
相关产品推荐
相关产品推荐

