网站因为Apache2吃内存太多,死掉了



之前用C写的一个小巧的Home Server已经伴我数载,一直很稳定的为我的网站提供服务,而且内存只有10MB左右,却可以支持100多个并发请求。更多的并发请求需要增加额外一些内存。因为对wordpress支持最好的服务器程序就是apache2,而且burst.net最近老是给我的服务器重启,Home Server不像apache2那样作成服务会随机启动,所以导致网站无法访问,很不爽。

前些天高高兴兴地换上了Apache2,为了适应OpenVZ的小内存VPS,我已经把并发数设置的相当小了。MaxClients只有50而已。然而,悲剧却发生了,我突然无法连接我的VPS的SSH服务了,提示如下。

root@xiaoxia-pc:~# ssh xiaoxia.org
ssh_exchange_identification: Connection closed by remote host

奇怪了,我以为是密钥的问题。所以我登录了vePortal面板进行管理,发现vePortal也死掉了,控制台无法使用,进程也无法查看。但是奇怪的是,网站却可以正常打开,FTP也能使用,我用python搭建的实验室lab.xiaoxia.org也能正常运行,但是在使用http://lab.xiaoxia.org/server/request/ 页面上的服务执行命令时,失败了。原因如下:

500 Internal Error

Traceback (most recent call last):
File “WebGateway.py”, line 138, in getService
if not self.getController():
File “WebGateway.py”, line 113, in getController
method(*params)
File “controller/server.py”, line 20, in request
buf = self.process(path)
File “controller/server.py”, line 15, in process
return os.popen(cmd, “r”).read()
OSError: [Errno 12] Cannot allocate memory

我想应该是无法创建进程引起的吧,应该是服务提供商的问题。

过了一段时间,果然我又能重新登录SSH服务,但是发现apache异常退出了。我在猜测可能是它占用了太多的内存,出错了。

我开始想到的解决方法是,去掉默认的prefork工作模式,改为worker或者event。换了之后,我发现问题依旧,而且一旦出现内存用尽时,想进行什么操作都无用。这是OpenVZ的VPS最令人头痛的问题了吧,它的限制的内存是虚拟内存占用,而不是实际物理内存的占用。这跟XEN的VPS有本质的区别。但是相同价位的XEN的VPS配置又太低了,才128M的内存,更加不能做站点服务了。

今天无奈之下,就把apache2的最大连接数设为了5,我想这样不会出现内存用尽的问题了吧,算它一个进程使用50MB,50*5 = 250MB也不多,我可能还有200MB剩余呢。结果,新问题又来了。一段时间之后,apache2好像死掉了,访问网站停留在无限的等待中,普通的静态页面也不给我返回任何数据,跟死锁了一样,但是prefork模式是无锁的,无锁也死掉了,不清楚什么原因了。

查一下网络状态如下:

root@244754:~# netstat -ntp 
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 184.22.224.212:80       113.105.195.234:7976    SYN_RECV    -               
tcp        0      0 184.22.224.212:80       27.156.69.188:59759     SYN_RECV    -               
tcp        0      0 184.22.224.212:80       125.37.124.2:50740      SYN_RECV    -               
tcp        0      0 184.22.224.212:80       66.249.71.163:54024     SYN_RECV    -               
tcp        0      0 184.22.224.212:80       221.172.103.201:3887    SYN_RECV    -               
tcp        0      0 184.22.224.212:80       27.156.69.188:59668     SYN_RECV    -               
tcp        0      0 184.22.224.212:80       112.96.130.11:1630      SYN_RECV    -               
tcp        0      0 184.22.224.212:80       112.96.130.11:1634      SYN_RECV    -               
tcp        0      0 184.22.224.212:80       123.125.71.98:5598      SYN_RECV    -               
tcp        0      0 184.22.224.212:80       113.141.158.255:1393    SYN_RECV    -               
tcp        0      0 184.22.224.212:80       112.96.130.11:1635      SYN_RECV    -               
tcp        0      0 184.22.224.212:80       124.115.0.24:57274      SYN_RECV    -               
tcp        0      0 184.22.224.212:80       112.96.130.11:1636      SYN_RECV    -               
tcp        0      0 184.22.224.212:80       223.167.161.208:50234   SYN_RECV    -               
tcp        0      0 184.22.224.212:80       112.96.130.11:1623      SYN_RECV    -               
tcp        0      0 184.22.224.212:80       218.59.63.57:2061       SYN_RECV    -               
tcp      267      0 184.22.224.212:80       123.125.71.35:9661      CLOSE_WAIT  -               
tcp      401      0 184.22.224.212:80       58.60.36.30:31649       CLOSE_WAIT  -               
tcp        0  11680 184.22.224.212:80       123.125.71.107:8435     CLOSE_WAIT  32006/apache2   
tcp      267      0 184.22.224.212:80       123.125.71.103:44783    CLOSE_WAIT  -               
tcp      282      0 184.22.224.212:80       123.125.71.111:58854    CLOSE_WAIT  -               
tcp      178      0 184.22.224.212:80       184.22.224.212:34649    CLOSE_WAIT  -               
tcp      437      0 184.22.224.212:80       125.37.124.2:50895      ESTABLISHED -               
tcp      270      0 184.22.224.212:80       124.115.0.22:33928      CLOSE_WAIT  -               
tcp        0  11680 184.22.224.212:80       123.125.71.85:54145     CLOSE_WAIT  30177/apache2   
tcp       52      0 184.22.224.212:80       184.22.224.212:34867    CLOSE_WAIT  -               
tcp      178      0 184.22.224.212:80       184.22.224.212:36640    CLOSE_WAIT  -               
tcp      178      0 184.22.224.212:80       184.22.224.212:58669    CLOSE_WAIT  -               
tcp      242      0 184.22.224.212:80       123.126.50.78:40404     CLOSE_WAIT  -               
tcp      198      0 184.22.224.212:80       101.226.33.171:44614    CLOSE_WAIT  -               
tcp      267      0 184.22.224.212:80       123.125.71.101:1912     CLOSE_WAIT  -               
tcp      242      0 184.22.224.212:80       123.126.50.78:33343     CLOSE_WAIT  -               
tcp      490      0 184.22.224.212:80       58.60.36.30:31550       CLOSE_WAIT  -               
tcp      217      0 184.22.224.212:80       114.80.93.57:54796      CLOSE_WAIT  -               
tcp        0  10020 184.22.224.212:80       58.60.36.30:31549       LAST_ACK    -               
tcp      235      0 184.22.224.212:80       123.125.71.13:42558     CLOSE_WAIT  -               
tcp      184      0 184.22.224.212:80       184.22.224.212:37079    CLOSE_WAIT  -               
tcp        0      0 184.22.224.212:80       184.22.224.212:60631    ESTABLISHED -               
tcp        0      0 184.22.224.212:34867    184.22.224.212:80       FIN_WAIT2   -               
tcp      238      0 184.22.224.212:80       72.14.199.105:65465     CLOSE_WAIT  -               
tcp      178      0 184.22.224.212:80       184.22.224.212:53197    CLOSE_WAIT  -               
tcp      267      0 184.22.224.212:80       123.125.71.53:42012     CLOSE_WAIT  -               
tcp      434      0 184.22.224.212:80       211.147.4.6:55549       CLOSE_WAIT  -               
tcp      378      0 184.22.224.212:80       120.32.188.252:56564    ESTABLISHED -               
tcp      340      0 184.22.224.212:80       72.14.199.119:53982     CLOSE_WAIT  -               
tcp      184      0 184.22.224.212:80       184.22.224.212:60606    CLOSE_WAIT  -               
tcp      309      0 184.22.224.212:80       123.125.71.107:8492     CLOSE_WAIT  -               
tcp      309      0 184.22.224.212:80       123.125.71.31:63314     CLOSE_WAIT  -               
tcp      405      0 184.22.224.212:80       58.60.36.30:31811       CLOSE_WAIT  -               
tcp      242      0 184.22.224.212:80       123.126.50.78:35139     CLOSE_WAIT  -               
tcp      242      0 184.22.224.212:80       123.126.50.78:50240     CLOSE_WAIT  -               
tcp      267      0 184.22.224.212:80       123.125.71.25:51314     CLOSE_WAIT  -               
tcp      271      0 184.22.224.212:80       123.125.71.42:35513     CLOSE_WAIT  -               
tcp      282      0 184.22.224.212:80       123.125.71.20:6027      CLOSE_WAIT  -               
tcp      218      0 184.22.224.212:80       124.115.0.19:56002      CLOSE_WAIT  -               
tcp        0  11680 184.22.224.212:80       123.125.71.98:65517     CLOSE_WAIT  30144/apache2   
tcp        1      0 184.22.224.212:80       112.96.130.11:1628      CLOSE_WAIT  -               
tcp        0  21600 184.22.224.212:80       120.32.188.161:56437    ESTABLISHED 31999/apache2   
tcp      511      0 184.22.224.212:80       119.4.46.213:35722      CLOSE_WAIT  -               
tcp      282      0 184.22.224.212:80       123.125.71.110:63433    CLOSE_WAIT  -               
tcp      267      0 184.22.224.212:80       123.125.71.108:16581    CLOSE_WAIT  -               
tcp      505      0 184.22.224.212:80       221.233.39.110:3138     ESTABLISHED -               
tcp      301      0 184.22.224.212:80       113.219.105.246:2883    CLOSE_WAIT  -               
tcp      498      0 184.22.224.212:80       113.105.195.234:6984    CLOSE_WAIT  -               
tcp      239      0 184.22.224.212:80       72.14.199.118:61048     CLOSE_WAIT  -               
tcp        0      0 184.22.224.212:22       112.96.130.11:3893      TIME_WAIT   -               
tcp      207      0 184.22.224.212:80       124.115.0.171:49164     CLOSE_WAIT  -               
tcp      286      0 184.22.224.212:80       119.137.117.182:2705    CLOSE_WAIT  -               
tcp      267      0 184.22.224.212:80       123.125.71.88:30288     CLOSE_WAIT  -               
tcp        0  11680 184.22.224.212:80       123.125.71.42:35096     CLOSE_WAIT  30140/apache2   
tcp      281      0 184.22.224.212:80       123.125.71.96:50769     ESTABLISHED -               
tcp      179      0 184.22.224.212:80       123.125.67.152:5469     CLOSE_WAIT  -               
tcp      282      0 184.22.224.212:80       123.125.71.92:48498     CLOSE_WAIT  -               
tcp      282      0 184.22.224.212:80       123.125.71.44:38148     CLOSE_WAIT  -               
tcp      171      0 184.22.224.212:80       220.181.108.113:48580   CLOSE_WAIT  -               
tcp        0   7232 184.22.224.212:22       112.96.130.11:9606      ESTABLISHED 32620/0         
tcp      458      0 184.22.224.212:80       124.227.104.75:63147    ESTABLISHED -               
tcp        0      0 184.22.224.212:80       221.5.67.196:52738      ESTABLISHED -               
tcp      235      0 184.22.224.212:80       106.187.41.8:49376      CLOSE_WAIT  -               
tcp      240      0 184.22.224.212:80       124.115.4.201:32776     CLOSE_WAIT  -               
tcp     1737      0 184.22.224.212:80       61.131.125.240:2235     CLOSE_WAIT  -               
tcp      235      0 184.22.224.212:80       106.187.41.8:49375      CLOSE_WAIT  -               
tcp      425      0 184.22.224.212:80       119.146.220.35:8242     CLOSE_WAIT  -

真想用回自己写的服务器,但是好不容易转移到apache2了,又不甘心。

今晚,我换了worker-mpm的多线程工作模式,尝试使用ServerLimit把进程限制为1个,结果发现apache2还是会启动4个进程。不明白。我把最大连接数MaxClients设置为30,然后启动apache2,我发现有一个apache2的进程占用的内存达到250MB之多。

root@244754:/etc/apache2# ps aux|grep apa
root      1926  0.0  0.6   6104  3240 ?        Ss   00:22   0:00 /usr/sbin/apache2 -k start
www-data  1927  0.0  0.3   5464  1932 ?        S    00:22   0:00 /usr/sbin/apache2 -k start
www-data  1930  0.0  0.3   6096  1884 ?        S    00:22   0:00 /usr/sbin/apache2 -k start
www-data  1931  0.0  0.5 268628  3084 ?        Sl   00:22   0:00 /usr/sbin/apache2 -k start
root      1965  0.0  0.1   1876   660 pts/2    S+   00:22   0:00 grep --color=auto apa

250/30 = 8MB,这不正好是Linux下默认线程堆栈的大小吗?如果我把这个堆栈大小设置为800KB,那不就只占用我十分之一,即25MB的内存了?

上网查了一下,用ThreadStackSize来设置堆栈大小,我设置了512KB,这个应该影响不大吧,我自己写的Home Server里每个线程也只使用128KB的内存而已呢!!!但是就是担心apache2里的一些模块使用的堆栈内存比较多,导致进程异常退出。

结果,内存占用果然吃惊地了,我了。。。

root@244754:~# ps aux|grep apa
root      1501  0.0  0.6   6104  3240 ?        Ss   00:35   0:00 /usr/sbin/apache2 -k start
www-data  1504  0.0  0.3   5464  1932 ?        S    00:35   0:00 /usr/sbin/apache2 -k start
www-data  1505  0.0  0.3   6096  1884 ?        S    00:35   0:00 /usr/sbin/apache2 -k start
www-data  1506  0.0  0.4  22216  2400 ?        Sl   00:35   0:00 /usr/sbin/apache2 -k start
root      1540  0.0  0.1   1876   660 pts/0    S+   00:35   0:00 grep --color=auto apa

apache2的内存降下来了,php占用的内存还是蛮高的,

root@244754:~# ps aux|grep php
www-data  1544  6.0  6.1  55972 32276 ?        S    00:36   0:10 /usr/bin/php5-cgi
www-data  1548  1.4  3.8  43892 20160 ?        S    00:36   0:02 /usr/bin/php5-cgi
root      1559  0.0  0.1   1876   656 pts/0    S+   00:39   0:00 grep --color=auto php

不知道如何设置PHP占用的线程堆栈内存大小,但是有个方法似乎是通用的。ulimit!

root@244754:~# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 20
file size               (blocks, -f) unlimited
pending signals                 (-i) 16382
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) unlimited
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

设置stack size,改为512KB8MB实在是太浪费了。

root@244754:~# ulimit -s 512
root@244754:~# service apache2 restart
 * Restarting web server apache2     [ OK ]
root@244754:~# ps aux|grep php
www-data  1639 12.1  3.8  36384 20156 ?        S    00:39   0:00 /usr/bin/php5-cgi
root      1642  0.0  0.1   1876   656 pts/0    S+   00:39   0:00 grep --color=auto php

似乎有点儿效果,不过php占用内存一直比较大,可能是模块多了吧,特别是某些图形处理的模块,在Python里也吃很多的内存。

到现在总算松了一口气了,内存用尽的问题解决了,网站访问不了的问题也解决了。不过事情还没完呢,burst给我发了一封邮件,因为维护原因,我的VPS会在11月1日,6A.M. – 2P.M. PDT之间有可能4小时左右的停止服务,大概是在北京时间晚上9点之后?我不大会算。

现在配置的多线程小内存的apache2如下:

<IfModule mpm_worker_module>
StartServers       1
ServerLimit        4
MinSpareThreads    25
MaxSpareThreads    75
ThreadsPerChild    40
MaxClients         120 
MaxRequestsPerChild   1000
ThreadStackSize    500000
</IfModule>

我不是很懂这些参数的意思,但是靠字面上的理解,就这么的设置了。

运行状况:

root@244754:/etc/apache2# ps aux|grep apache
root      1888  0.0  0.6   6156  3288 ?        Ss   01:26   0:00 /usr/sbin/apache2 -k start
www-data  1889  0.0  0.3   5464  1928 ?        S    01:26   0:00 /usr/sbin/apache2 -k start
www-data  1892  0.0  0.3   6096  1988 ?        S    01:26   0:00 /usr/sbin/apache2 -k start
www-data  1893  0.3  1.4  23400  7472 ?        Sl   01:26   0:01 /usr/sbin/apache2 -k start
root      1949  0.0  0.1   1876   660 pts/3    S+   01:32   0:00 grep --color=auto apache

网站因为Apache2吃内存太多,死掉了》有32个想法

  1. 艳文

    我一直还以为你是使用nginx。。。。。apache好像是最耗内存的啊 openvz又要你配置得好才能不超标。。。。。xen好像会人性化一点 不会直接死掉进程。。

    回复
      1. = =

        好像是更轻量。。只是曾经用vps的时候从apache换到nginx突然发现内存压力一下轻松了好多。。可能是我apache优化不够。。但的确apache一直就被人说是功能强大但太臃肿了。。而且当时nginx有很多优化经验可以借鉴。。比如http://www.vpsee.com/2009/06/can-a-64mb-vps-serve-1000pvs-per-day/

        回复
        1. Xiaoxia 文章作者

          静态页面,1000pvs,其实用c写一个基于select模型的http传输服务器,基本上只用不到5MB的内存左右就能够招待1000pvs。而且有很多嵌入式的迷你Web服务器程序,都是小内存,高效率设计的,不会有像nginx那么强的代理功能。
          如果要考虑1000以上的并发量,就需要考虑使用基于event模型的技术了。
          使用apache主要是因为它比较普及,很多项目都离不开它,而且它具有很强的可配置能力,只不过默认安装的配置比较臃肿,需要优化。

          回复
    1. Xiaoxia 文章作者

      哇!!!不如贡献出来,我帮你配置一个OpenVZ,来出售VPS好了。。。我大概也了解了84的VZ配置方案。

      回复
            1. Xiaoxia 文章作者

              有IP,就是很慢,非常慢,一直等待无法打开主页的那种。应该是跟我用移动的网络有关???要不你用你的移动号码的手机上上试试?

              回复
  2. Heiher

    Nginx 可不吃内存。我们的配置一样的。

    USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
    root 1 0.0 0.2 2468 1324 ? Ss Dec05 0:00 init
    root 1142 0.0 0.1 5472 912 ? Ss Dec05 0:00 /usr/sbin/sshd
    nobody 1158 0.0 0.5 5396 3024 ? Ss Dec05 0:00 /usr/sbin/openvpn –writepid /var/
    root 1162 0.0 0.1 8708 1040 ? Ss Dec05 0:00 nginx: master process /usr/sbin/ng
    www-data 1163 0.0 0.5 8952 2960 ? S Dec05 0:20 nginx: worker process
    www-data 1164 0.0 0.5 8944 2956 ? S Dec05 0:19 nginx: worker process
    www-data 1169 0.0 0.5 8676 2868 ? S Dec05 0:16 nginx: worker process
    www-data 1170 0.0 0.5 8940 2968 ? S Dec05 0:17 nginx: worker process
    root 1174 0.0 0.5 30076 2908 ? Ss Dec05 0:12 php-fpm: master process (/etc/php5
    mysql 1198 0.0 3.2 57512 16820 ? Ssl Dec05 0:05 /usr/sbin/mysqld
    root 1645 0.0 0.5 8332 2744 ? Ss 04:54 0:00 sshd: fwd [priv]
    fwd 1656 0.0 0.7 10436 4196 ? S 04:54 0:02 sshd: fwd
    root 3242 0.0 0.5 8332 2744 ? Ss 14:09 0:00 sshd: fwd [priv]
    fwd 3253 0.0 0.5 9072 2784 ? S 14:09 0:00 sshd: fwd
    heiher 3292 0.1 4.0 45312 21112 ? S 14:37 0:01 php-fpm: pool heiher
    heiher 3295 0.0 3.9 44776 20764 ? S 14:37 0:01 php-fpm: pool heiher
    heiher 3296 0.1 3.9 44796 20800 ? S 14:37 0:01 php-fpm: pool heiher
    heiher 3297 0.0 4.0 45112 21088 ? S 14:37 0:01 php-fpm: pool heiher
    heiher 3298 0.0 3.6 43220 19156 ? S 14:37 0:00 php-fpm: pool heiher
    heiher 3299 0.1 3.9 44844 20896 ? S 14:37 0:01 php-fpm: pool heiher
    root 3314 0.0 0.5 8332 2768 ? Ss 15:02 0:00 sshd: heiher [priv]
    heiher 3325 0.0 0.3 8332 1700 ? S 15:02 0:00 sshd: heiher@pts/0
    heiher 3326 0.0 0.3 4488 1848 pts/0 Ss 15:02 0:00 -bash
    heiher 3340 0.0 0.1 2644 1008 pts/0 R+ 15:02 0:00 ps aux

    total used free shared buffers cached
    Mem: 524800 257048 267752 0 0 0
    -/+ buffers/cache: 257048 267752
    Swap: 0 0 0

    回复
    1. Xiaoxia 文章作者

      优化apache占内存也不多,关键是php占的内存。而且我用的php是non thread safe的,出错的机率不低。

      回复
  3. 艳文

    小虾。。我想换个lnmp搭建网站方案了。。那个VPS搭建的lnmp 每隔几天就出现502错误。。重启了lnmp也无效。。但直接重启VPS就可以。。好麻烦。。你博客的系统是什么啊?用什么搭建的呢?

    回复

回复 Xiaoxia 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据