用oss2上传日志前必须确认RAM子账号AK/SK、正确Endpoint、环境变量读取凭证;只上传静止且非临时文件,标记已传避免重复;crontab需用绝对路径并切换工作目录;大文件须分片上传并校验MD5。

用 oss2 上传日志前必须确认凭证和权限
直接写上传逻辑却报 InvalidAccessKeyId 或 AccessDenied 是最常见卡点。不是代码写错了,而是 OSS 的 AccessKey 和 Bucket 权限没配对。
- AccessKey 必须是 RAM 子账号(非主账号)的 AK/SK,且该子账号已绑定自定义策略,明确授予目标
Bucket的oss:PutObject和oss:GetObject权限 - 不要把
Endpoint写成控制台首页地址(如https://oss.console.aliyun.com),得用真实地域 Endpoint,例如华东1是https://oss-cn-hangzhou.aliyuncs.com - 敏感信息别硬编码——用环境变量读取:
os.getenv('OSS_ACCESS_KEY_ID'),然后在 crontab 或 systemd service 里提前export
归档逻辑要避开正在写入的日志文件
直接 glob.glob('*.log') 然后上传,大概率会传一半失败或内容不全,因为日志轮转工具(如 logrotate 或应用自身)可能正 hold 着文件句柄。
- 只处理满足「修改时间早于当前时间 5 分钟」且「文件大小不再增长」的文件:用
os.stat(filepath).st_mtime判断 + 连续两次stat比较大小(间隔 1 秒)确认是否静止 - 跳过以
.tmp、.swp、~结尾的临时文件,也跳过正在被flock锁住的文件(可用lsof -f -- /path/to/file检查,但脚本里建议用try/except捕获PermissionError更轻量) - 上传成功后,用
os.rename(filepath, filepath + '.uploaded')标记,避免下次重复上传;别直接os.remove(),留个缓冲期
用 crontab 跑脚本时 PATH 和工作目录容易错
crontab 默认 PATH 极简(通常只有 /usr/bin:/bin),找不到 python3 或虚拟环境里的解释器;且默认工作目录是用户 home,脚本里写的相对路径全失效。
- crontab 条目必须写绝对路径:
0 2 * * * cd /home/user/log-archive && /usr/bin/python3 /home/user/log-archive/upload.py >> /home/user/log-archive/cron.log 2>&1 - 脚本开头加
import os; os.chdir(os.path.dirname(__file__)),确保后续open()、glob基于脚本所在目录解析 - 如果用了 virtualenv,直接调用
/path/to/venv/bin/python,别依赖系统python3—— pip 安装的oss2不在系统 Python site-packages 里
上传大日志时得开分片上传和断点续传
单个日志文件超过 100MB 还用 bucket.put_object(),会内存爆满、超时失败、重试时从头开始——OSS 默认单次上传上限 5GB,但实际得靠分片。
立即学习“Python免费学习笔记(深入)”;
- 判断文件大小 > 100MB 就走
bucket.resumable_upload(),传参加store=oss2.ResumableStore(root='/tmp/oss-upload-checkpoints'),避免断点信息写进项目目录 - 务必设超时:
oss2.Bucket(auth, endpoint, bucket_name, connect_timeout=30, app_name='log-archive'),否则默认连接超时是 60 秒,网络抖动就卡死 - 上传后校验 MD5:
oss2.utils.content_md5(open(filepath, 'rb').read())和 OSS 返回的resp.headers.get('x-oss-hash-crc64ecma')不等价,得用oss2.utils.md5_for_file()计算再比对
实际跑起来最麻烦的从来不是上传动作本身,而是日志文件状态判断和 cron 环境隔离——这两处出问题,脚本看起来“执行了”,但什么都没传上去,还查不出错在哪。


















