温馨提示×

如何在Ubuntu上解决Nginx内存泄漏

小樊
37
2025-11-15 11:04:40
栏目: 智能运维

在 Ubuntu 上排查与修复 Nginx 内存泄漏的实用步骤

一、快速确认与止损

  • 观察是否为单个 worker 持续增长:运行 top -p $(pgrep nginx | tr '\n' ',')ps -o pid,rss,command -p $(pgrep nginx),按 RSS 排序,定位异常 PID
  • 判断是否被 OOM Killer 终止:dmesg -T | grep -i 'oom\|kill' | grep <PID>,若频繁出现,说明系统内存紧张,需要先降压再查根因。
  • 区分“泄漏”与“配置/缓存导致的高占用”:检查是否开启了大块缓存或过大的请求头/缓冲配置,例如 proxy_cache_path ... max_size=...client_header_buffer_sizelarge_client_header_buffersproxy_buffers
  • 临时止血(不影响现网连接):执行 nginx -s reload 平滑重启释放已分配但未回收的缓冲;必要时 systemctl restart nginx
  • 应急限流与降载:
    • 限制单 IP 并发:limit_conn_zone $binary_remote_addr zone=perip:10m; limit_conn perip 20;
    • 临时关闭高内存功能:proxy_cache off;(或调小 ssl_session_cache)。
      以上步骤可快速判断问题类型并稳住现场,为后续精确定位争取时间。

二、定位根因

  • 抓取异常 worker 的内存分布:pmap -x <PID>,关注是否有超大匿名映射;结合 /proc/<PID>/smaps 查看具体段与 RSS 贡献。
  • 深入取证:用 gdb 连到进程并导出可疑内存段内容,辅助判断是否为业务/模块分配未释放。示例:
    • gdb -p <PID>
    • dump binary memory ./mem.bin 0x7fa1d0b57000 0x7FA1D0B70000
  • 检查共享内存与缓存占用:
    • 共享内存段:ipcs -m | grep nginx
    • 缓存配置:grep -r "proxy_cache\|fastcgi_cache" /etc/nginx/
  • 打开 Nginx 调试日志 复现问题:error_log /var/log/nginx/error.log debug;(仅在排查窗口内开启,避免日志膨胀)。
  • 二分法禁用第三方模块/动态库:临时移出或注释 load_moduleinclude 的模块目录,逐步定位问题来源。
  • 若观察到 reload 后内存约为原来的 2 倍且不再增长,多与 glibc 内存归还策略 有关(free 后未立即归还 OS),并非严格泄漏;可结合后续“修复”中的 jemalloc/malloc_trim 思路处理。
    以上方法结合系统工具与 Nginx 内部信息,通常能定位到具体模块或配置项。

三、常见根因与修复对照表

症状 可能原因 快速验证 修复建议
个别 worker RSS 持续攀升 第三方模块/扩展内存管理不当 gdb 导出内存、二分禁用模块 升级/修复模块;必要时改用稳定版本或移除
大请求头或大 Cookie 导致占用高 client_header_buffer_size/large_client_header_buffers 过大 抓包或日志统计请求头大小 client_header_buffer_size 设为 4k–16k;仅对大头部启用 large_client_header_buffers 4 16k;
反向代理/静态资源缓冲过大 proxy_buffersproxy_buffer_size 配置偏大 检查配置与连接并发 适度下调缓冲;启用 sendfile on; tcp_nopush on; 减少用户态拷贝
HTTPS 会话缓存过大 ssl_session_cache 过大或超时过长 检查 ssl_session_cachessl_session_timeout 改为 shared:SSL:20m; ssl_session_timeout 10m;
reload 后内存约翻倍且不回落 glibc 延迟归还内存 多次 reload 观察 RSS 是否稳定为 2x 使用 jemalloc 并在合适位置调用 mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", ...);或谨慎在 master 中调用 malloc_trim(0)(需自编译)
缓存/连接数过多 proxy_cache_path max_size 过大、连接并发高 检查缓存命中率与连接数 设置合理 max_size/inactive,并限制并发连接

上述对照覆盖了最常见的配置与模块类问题,优先从“请求头/缓冲/缓存/SSL/第三方模块”五类入手,命中率最高。

四、修复与优化落地

  • 配置优化(示例):
    • 请求头:client_header_buffer_size 8k; large_client_header_buffers 4 16k;
    • 反向代理:proxy_buffers 8 16k; proxy_buffer_size 16k; sendfile on; tcp_nopush on;
    • 缓存:proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m;
    • SSL:ssl_session_cache shared:SSL:20m; ssl_session_timeout 10m;
  • 版本与模块治理:
    • 优先使用稳定版 Nginx 与可信模块;排查期间用二分法禁用第三方模块,确认问题后再逐个恢复。
    • 若确认是 glibc 归还延迟 且影响可接受,考虑切换 jemalloc 并在合适位置调用 mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", ...);或在 master 流程中调用 malloc_trim(0)(需自编译,谨慎评估)。
  • 资源与稳定性:
    • 合理设置 worker_processes auto;worker_connections;必要时用 cgroups 限制 Nginx 内存上限,避免影响整机稳定性。
      这些改动能显著降低内存占用峰值并提升稳定性,同时保留必要的吞吐能力。

五、验证与长期观测

  • 回归压测与对比:使用 wrk -t12 -c400 -d30s http://example.com/ 对比优化前后 RSS 增长曲线 与错误率。
  • 监控与告警:建立对 每个 worker RSS缓存命中率连接数 的监控(如 Prometheus + Grafana),设置异常增长告警。
  • 日志审计:保留 error.log debug 的排查窗口日志,定位完成后恢复常规级别,避免磁盘压力。
    通过压测与持续观测,验证修复效果并防止问题复现。

0