专栏名称: 运维帮
互联网技术分享平台,分享的力量。帮主一直坚信技术可以改变世界,从毕业到现在干了15年运维,有许多话要和你说。
目录
相关文章推荐
51好读  ›  专栏  ›  运维帮

Python 进程内存增长解决方案

运维帮  · 公众号  · 运维  · 2017-07-18 19:28

正文


张炎泼 (XP)


白山云科技合伙人兼研发副总裁,绰号XP。


张炎泼先生于2016年加入白山云科技,主要负责对象存储研发、数据跨机房分布和修复问题解决等工作。以实现100PB级数据存储为目标,其带领团队完成全网分布存储系统的设计、实现与部署工作,将数据“冷”“热”分离,使冷数据成本压缩至1.2倍冗余度。


张炎泼先生2006年至2015年,曾就职于新浪,负责Cross-IDC PB级云存储服务的架构设计、协作流程制定、代码规范和实施标准制定及大部分功能实现等工作,支持新浪微博、微盘、视频、SAE、音乐、软件下载等新浪内部存储等业务;2015年至2016年,于美团担任高级技术专家,设计了跨机房的百PB对象存储解决方案:设计和实现高并发和高可靠的多副本复制策略,优化Erasure Code降低90%IO开销。


表现


运行环境:

# uname –a

Linux ** 3.10.0-327.el7.x86_64 #1 SMP Thu Nov 19 22:10:57 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

# python2 --version

Python 2.7.5

# cat /etc/*-release

CentOS Linux release 7.2.1511 (Core)


python进程在大量请求的处理过程中,内存持续升高。负载压力下降之后,内存并未下降。

# ps aux | grep python2

USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

root     124910 10.2  0.8 5232084 290952 ?      Sl   Mar17 220:37 python2 offline.py restart

#                                                        ~~~~~~

#                                                        290M 内存占用


解决方法


三个步骤确定问题所在:


  • 确认当时程序是否有异常行为;

  • 排除行为异常后,查看python内存使用情况,确认所有相关对象的回收情况;

  • 排除垃圾回收等python内部内存泄漏问题后,则可以定位到libc的malloc实现问题;


确定后可直接替换malloc模块为tcmalloc:

LD_PRELOAD = "/usr/lib64/libtcmalloc.so" python x.py


问题定位过程解读


gdb-python:搞清楚python程 序在做什么


首先确定python在做什么,是否有大内存消耗任务正在运行,或出现死锁等异常行为。


从gdb-7开始,gdb支持用python实现gdb扩展,可以像调试c程序一样,用gdb对python程序检查线程、调用栈等;且可同时打印python代码和内部c代码的调用栈。


这对于定位是python代码问题还是其底层c代码问题,有很大帮助。


准备gdb

首先安装python的debuginfo:

# debuginfo-install python-2.7.5-39.el7_2.x86_64


如果缺少debuginfo,当运行后续步骤时,gdb会提示,按提示安装完成即可:

Missing separate debuginfos, use: debuginfo-install python-2.7.5-39.el7_2.x86_64


接入gdb

可直接用gdb attach到1个python进程,查看其运行状态:

# gdb python 11122


attach之后进入gdb,基本检查步骤如下:


查看线程

(gdb) info threads

Id   Target Id         Frame

206  Thread 0x7febdbfe3700 (LWP 124916) "python2" 0x00007febe9b75413 in select () at ../sysdeps/unix/syscall-template.S:81

205  Thread 0x7febdb7e2700 (LWP 124917) "python2" 0x00007febe9b75413 in select () at ../sysdeps/unix/syscall-template.S:81

204  Thread 0x7febdafe1700 (LWP 124918) "python2" 0x00007febe9b75413 in select () at ../sysdeps/unix/syscall-template.S:81

203  Thread 0x7febda7e0700 (LWP 124919) "python2" 0x00007febe9b7369d in poll () at ../sysdeps/unix/syscall-template.S:81


一般加锁、死锁情况存在时,会有线程卡在xx_wait等函数上。


之前用该方法定位了1个python-logging模块引起的死锁问题:

在多线程进程中运行fork,导致logging的锁被锁住后fork到新进程、但解锁线程没有fork到新进程而导致死锁。


查看调用栈

如果发现某线程有问题,切换到此线程上,查看调用栈确定具体执行步骤,使用 bt 命令

(gdb) bt

#16 0x00007febea8500bd in PyEval_EvalCodeEx (co= , globals= , locals=locals@entry=0x0, args= ,

argcount =argcount@entry=1, kws =0x38aa668, kwcount =2, defs =0x3282a88, defcount =2, closure =closure@entry=0x0) at /usr/src/debug/Python-2.7.5/Python/ceval.c:3330

...

#19 PyEval_EvalFrameEx (

f =f@entry=Frame 0x38aa4d0, for file t.py, line 647, in run ( part_num =2, consumer =<.../>


bt 命令不 仅可以看到c的调用栈,还会显示python源码的调用栈。 上图中,frame-16是c的调用栈,frame-19显示python源代码的所在行。


如果只查看python代码的调用栈,则使用 py-bt 命令:

(gdb) py-bt

#1

#3 Frame 0x3952450, for file /usr/lib64/python2.7/site-packages/twisted/internet/epollreactor.py, line 379, in doPoll(self=<.../>

l = self._poller.poll(timeout, len(self._selectables))

#7 Frame 0x39502a0, for file /usr/lib64/python2.7/site-packages/twisted/internet/base.py, line 1204, in mainLoop (self=<.../>


py-bt 显示python源码的调用栈、调用参数以及所在行的代码。


coredump

如果要进行长时间跟踪,最好 coredump下python程序的全部进程信息,之后再分析core文件,避免影响正在运行的程序。

(gdb) generate-core-file


这条命令将当前gdb attach的程序dump到其运行目录,命名为 core. ,然后使用gdb 加载该core文件,进行打印堆栈、查看变量等分析,无需attach到正在运行的程序:

# gdb python core.


其他命令

其他命令可以在gdb输入 py 查看,与gdb的命令对应,例如:

(gdb) py

py-bt           py-list         py-print      python

py-down        py-locals      py-up         python-interactive


  • py-up、py-down 可移动到python调用栈的上一个或下一个frame;

  • py-locals 用来打印局部变量……


gdb中也可用 help 命令 查看帮助:

(gdb) help py-print

Look up the given python variable name, and print it


在这次追踪过程中,用gdb-python排除了程序逻辑问题。接下来继续追踪内存泄漏问题。

pyrasite: 连接进入python程序


pyrasite可以直接连上一个正在运行的python程序,打开一个类似ipython的交互终端来运行命令、检查程序状态。


这为调试提供了极大的方便。


安装:

# pip install pyrasite

...

# pip show pyrasite

Name: pyrasite

Version: 2.0

Summary: Inject code into a running Python process

Home-page: http://pyrasite.com

Author: Luke Macken

...


连接到有问题的python程序,开始收集信息:

pyrasite-shell

>>>








请到「今天看啥」查看全文