问题发现
现象
在nginx+uwsgi+python环境中,启动mysqld服务,发生服务无法正常启动现象(闪退)。而在shell终端中可以正常启动mysqld服务。说明“nginx+uwsgi+python”这个环境限制了某些资源,导致mysqld服务启动失败。
uwsgi错误日志(/var/log/uwsgi.log)
1 | Starting MySQL. ERROR! The server quit without updating PID file (/store/xxx.pid) |
mysql错误日志(/{mysqldatadir}/{hostname}.err)
1 | 2016-09-22 16:21:41 28084 [Note] InnoDB: Using atomics to ref count buffer pool pages |
分析
根据mysql错误日志提示,mysql在初始化InnoDB引擎时,申请“Buffer Pool”内存失败,此处的mmap是用来申请内存的(allocate申请内存时,小于128KB的在栈上进行分配内存,大于128K的,从堆上分配内存)。
使用shell终端正常启动mysqld服务,查看mysqld占用内存大小发现需要2GB左右的虚拟内存,如下所示:
1 | ps aux | grep mysqld | grep -v grep |
由此可以看出,“nginx+uwsgi+python”这个组合在其中某个环节对内存做了内存限制导致内存不能正常分配;nginx父进程为init,uwsgi父进程也为init,二者之间使用管道进行通信,所以不可能由nginx限制uwsgi进程资源。而uwsgi以fork形式调用python,fork过程中可以限制资源,python进程的资源被限制后再启动mysqld服务,就会导致mysqld分配内存失败,从而出现上述的错误现象。
解决
根据上诉分析,找到了问题的原因出在uwsgi对进程资源进行了限制,所以将uwsgi.ini中关于资源限制的配置去掉,即可解决。
在uwsgi.ini中去掉limit-as 256
选项。
原理
uwsgi.ini中资源限制项包括limit-as
,limit-nproc
,limit-port
等,他们对进程资源的限制的访问获取都是通过系统接口getrlimt和setrlimit来实现的(POSIX API)。
1 |
|
核心结构体
1 | struct rlimit{ |
rlim_cur
为soft limit是指内核所能支持的资源上限。比如对于RLIMIT_NOFILE(一个进程能打开的最大文件数,内核默认是1024),soft limit最大也只能达到1024。对于RLIMIT_CORE(core文件的大小,内核不做限制),soft limit最大能是unlimited。
rlim_max
为hard limit在资源中只是作为soft limit的上限。当你设置hard limit后,你以后设置的soft limit只能小于hard limit。要说明的是,hard limit只针对非特权进程,也就是进程的有效用户ID(effective user ID)不是0的进程。具有特权级别的进程(具有属性CAP_SYS_RESOURCE),soft limit则只有内核上限。
可限制资源列表
资源 | 描述 |
---|---|
RLIMIT_AS | 进程的最大虚拟内存空间,字节为单位 |
RLIMIT_CORE | 内核转存文件的最大长度 |
RLIMIT_CPU | 最大允许的CPU使用时间,秒为单位 |
RLIMIT_DATA | 进程数据段的最大 |
RLIMIT_FSIZE | 进程可建立的文件的最大长度 |
RLIMIT_LOCKS | 进程可建立的锁和租赁的最大值 |
RLIMIT_MEMLOCK | 进程可锁定在内存中的最大数据量,字节为单位 |
RLIMIT_MSGQUEUE | 进程可为POSIX消息队列分配的最大字节数 |
RLIMIT_NICE | 进程可通过setpriority() 或 nice()调用设置的最大完美值 |
RLIMIT_NOFILE | 指定比进程可打开的最大文件描述词大一的值,超出此值,将会产生EMFILE错误 |
RLIMIT_NPROC | 用户可拥有的最大进程数 |
RLIMIT_RTPRIO | 进程可通过sched_setscheduler 和 sched_setparam设置的最大实时优先级 |
RLIMIT_SIGPENDING | 用户可拥有的最大挂起信号数 |
RLIMIT_STACK | 最大的进程堆栈,以字节为单位 |