Loading... # Nginx 反向代理配置错误导致磁盘空间耗尽故障复盘 # 一、事件概述 ## 1. 事件背景 2026 年 3 月 31 日晚,作者在 Hetzner 小型服务器上部署了数字产品下载服务。服务器配置为 4GB RAM 和 40GB 磁盘空间,提供包括一个 2.2GB 大文件在内的多个文件下载功能。系统架构采用 Haskell 程序处理静态文件服务(包含授权验证),前端使用 nginx 反向代理。 ## 2. 影响范围 ### A. 影响用户数 数百名付费用户 ### B. 影响时长 20:40 - 23:00,持续约 2 小时 ### C. 影响功能 - 所有文件下载功能完全中断(前 2 小时) - 大文件(2.2GB)下载失败(后续) ## 3. 严重程度 P1 级故障(核心业务受损,付费用户无法访问已购买产品) # 二、事件时间线 ## 1. 故障发生(20:40) ### A. 现象描述 产品发布后几分钟内,数百名用户同时访问服务器,系统立即出现磁盘空间不足错误。 ### B. 监控告警 日志中出现重复错误信息: ``` user error (Unexpected reply to: MAIL "<...> at kanjideck.com", Expected reply code: 250, Got this instead: 452 "4.3.1 Insufficient system storage") ``` Grafana 显示磁盘使用率 100%(40GB/40GB),df -h 显示 /dev/sda 100% 使用。 ## 2. 应急响应(20:40-21:00) ### A. 初步排查 使用 du -sh 分析磁盘占用,发现: - /var/lib 下的 Plausible Analytics(ClickHouse 数据库):8.5GB - /nix/store(NixOS 系统配置和可执行文件):15GB 剩余约 20GB 空间来源不明,未进一步分析。 ### B. 处理措施 尝试清理 Nix store: ``` nix-collect-garbage -d ``` 因磁盘空间不足而失败。 清理日志释放空间: ``` journalctl --vacuum-time=1s ``` 尝试清理 ClickHouse 日志表: ``` clickhouse-client -q "TRUNCATE TABLE system.query_log" ``` 因磁盘空间不足失败。 ### C. 效果评估 临时释放少量空间,但系统仍持续消耗磁盘空间,根因未定位。 ## 3. 扩容方案(21:00-23:00) ### A. 实施措施 Hetzner 无更大规格实例可用,采用方案 B:购买独立 Volume 挂载 /nix/store。 按照 NixOS Wiki 指南迁移 store: ``` fileSystems."/nix" = { device = "/dev/disk/by-label/nix"; fsType = "ext4"; neededForBoot = true; options = [ "noatime" ]; }; ``` ### B. 效果评估 服务器重启后,磁盘使用率降至 50%,服务恢复。但大文件下载问题未解决。 ## 4. 根本解决(次日) ### A. 问题定位 用户反馈大文件下载中途失败。通过调整 proxy_max_temp_file_size 从默认的 1024m 提升到 5000m,大文件可下载。 使用 lsof +L1 发现 14.5GB 已删除但仍被 nginx 占用的临时文件: ``` lsof +L1 | grep nginx # 显示大量 /tmp/nginx_proxy/ 目录下的 deleted 文件 ``` ### B. 根本原因 nginx 默认启用响应缓冲,将后端服务器返回的大文件内容缓存到临时文件。proxy_max_temp_file_size 默认值 1024m 限制单个临时文件大小,但多用户并发时产生大量临时文件。 ### C. 解决方案 在 nginx 配置中禁用缓冲: ``` extraConfig = '' proxy_buffering off; proxy_max_temp_file_size 0; ''; ``` ### D. 验证结果 磁盘使用率降至 20%,不再出现波动。 ```mermaid sequenceDiagram participant U as 用户 participant N as Nginx participant T as 临时文件 participant H as Haskell 应用 participant D as 磁盘 U->>N: 下载 2.2GB 文件 N->>H: 代理请求 H-->>N: 返回文件流 N->>T: 写入临时文件(缓冲) T->>D: 占用 1GB 空间 Note over D: 并发 20 用户 = 20GB D-->>N: 磁盘满 N-->>U: 下载失败 ```  # 三、问题分析 ## 1. 直接原因 nginx 的 proxy_max_temp_file_size 配置不当。默认值 1024m 允许单个请求缓冲最多 1GB 到临时文件,多用户并发下载时快速耗尽磁盘空间。 ## 2. 根本原因(5 Whys 分析) ### A. 为什么出现这个问题? nginx 默认启用响应缓冲机制,当后端响应较大时,会将部分内容写入临时文件。 ### B. 为什么没有及时发现? 部署前未进行负载测试,未能模拟多用户并发下载大文件场景。 ### C. 为什么配置不当? 对 nginx proxy_buffering 和 proxy_max_temp_file_size 参数理解不足,仅粗略阅读文档。 ### D. 为什么应急处理效率低? 压力下未能冷静分析问题,急于清理空间而非定位根因。 ## 3. 深层反思 - 文档阅读不够仔细,误以为提高 proxy_max_temp_file_size 可解决问题 - 缺少生产环境部署前的压力测试和容量规划 - 缺少应急处理经验,未建立故障排查清单 # 四、解决方案 ## 1. 临时方案 ### A. 实施措施 - 清理 journalctl 日志释放紧急空间 - 将 /nix/store 迁移到独立 Volume ### B. 效果评估 快速恢复服务,但未根治问题,磁盘使用率仍波动至 65%。 ## 2. 永久方案 ### A. 配置修改 禁用 nginx 响应缓冲: ``` proxy_buffering off; proxy_max_temp_file_size 0; ``` ### B. 效果评估 磁盘使用率稳定在 20%,不再出现临时文件占用问题。 ## 3. 预防措施 - 部署前进行负载测试,模拟真实用户并发场景 - 仔细阅读 nginx 文档,理解每个配置参数的含义 - 建立磁盘空间监控告警,设置阈值(如 80%) - 准备应急排查清单(lsof +L1、du -sh、df -h) # 五、经验总结 ## 1. 做得好的地方 - 快速响应用户,主动说明问题正在处理 - 在资源受限情况下,找到扩容方案(独立 Volume) - 最终定位并解决根本原因 ## 2. 需要改进的地方 - 应急处理时缺乏系统性思考,急于行动而非分析 - 对关键配置参数理解不深入 - 缺少部署前的测试验证环节 ## 3. 技术知识点 - nginx proxy_buffering:控制是否缓冲后端服务器响应 - proxy_max_temp_file_size:设置临时文件最大大小,0 表示完全禁用 - lsof +L1:查找已删除但仍被进程占用的文件 ## 4. 最佳实践建议 - 大文件下载场景应禁用 nginx 缓冲,使用流式传输 - 生产部署前必须进行负载测试 - 建立故障排查流程文档,减少应急时的决策压力 - 监控不仅是可用性,还应包含资源使用趋势 *** ## 参考资料 1. [Running out of Disk Space in Production](https://alt-romes.github.io/posts/2026-04-01-running-out-of-disk-space-on-launch.html) 2. [Nginx ngx_http_proxy_module Documentation](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_max_temp_file_size) 3. [NixOS Storage Optimization - Moving the store](https://nixos.wiki/wiki/Storage_optimization#Moving_the_store) 最后修改:2026 年 04 月 17 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏