独角兽吃记忆

时间:2011-11-29 06:42:56

标签: ruby-on-rails diskspace unicorn

我在亚马逊有一个m1.small实例,我的rails应用程序在其上运行8GB硬盘空间。它运行平稳了2周,然后崩溃说内存已满。 应用程序正在rails 3.1.1,unicorn和nginx上运行

我根本不明白13G的用途是什么?
我杀死了独角兽,'free'命令显示了一些空闲空间,而df仍然说100%
我重新启动了实例,一切正常。

免费(杀死独角兽之前)

             total       used       free     shared    buffers     cached  
Mem:       1705192    1671580      33612          0     321816     405288  
-/+ buffers/cache:     944476     760716   
Swap:       917500      50812     866688 

df -l(在杀死独角兽之前)

Filesystem           1K-blocks      Used Available Use% Mounted on  
/dev/xvda1             8256952   7837520         4 100% /  
none                    847464       120    847344   1% /dev  
none                    852596         0    852596   0% /dev/shm  
none                    852596        56    852540   1% /var/run  
none                    852596         0    852596   0% /var/lock  
/dev/xvda2           153899044    192068 145889352   1% /mnt  
/dev/xvdf             51606140  10276704  38707996  21% /data  

sudo du -hc --max-depth = 1(在杀死独角兽之前)

28K ./root  
6.6M    ./etc  
4.0K    ./opt  
9.7G    ./data  
1.7G    ./usr  
4.0K    ./media  
du: cannot access `./proc/27220/task/27220/fd/4': No such file or directory  
du: cannot access `./proc/27220/task/27220/fdinfo/4': No such file or directory  
du: cannot access `./proc/27220/fd/4': No such file or directory  
du: cannot access `./proc/27220/fdinfo/4': No such file or directory  
0   ./proc  
14M ./boot  
120K    ./dev  
1.1G    ./home  
66M ./lib  
4.0K    ./selinux  
6.5M    ./sbin  
6.5M    ./bin  
4.0K    ./srv  
148K    ./tmp  
16K ./lost+found  
20K ./mnt  
0   ./sys  
253M    ./var  
13G .  
13G total   

免费(杀死独角兽之后)

             total       used       free     shared    buffers     cached    
Mem:       1705192     985876     **719316**          0     365536     228576    
-/+ buffers/cache:     391764    1313428    
Swap:       917500      46176     871324  

df -l(杀死独角兽之后)

Filesystem           1K-blocks      Used Available Use% Mounted on  
/dev/xvda1             8256952   7837516         8 100% /  
none                    847464       120    847344   1% /dev  
none                    852596         0    852596   0% /dev/shm  
none                    852596        56    852540   1% /var/run  
none                    852596         0    852596   0% /var/lock  
/dev/xvda2           153899044    192068 145889352   1% /mnt  
/dev/xvdf             51606140  10276704  38707996  21% /data  

unicorn.rb

rails_env = 'production'  

working_directory "/home/user/app_name"  
worker_processes 5  
preload_app true  
timeout 60  

rails_root = "/home/user/app_name"  
listen "#{rails_root}/tmp/sockets/unicorn.sock", :backlog => 2048  
# listen 3000, :tcp_nopush => false  

pid "#{rails_root}/tmp/pids/unicorn.pid"  
stderr_path "#{rails_root}/log/unicorn/unicorn.err.log"  
stdout_path "#{rails_root}/log/unicorn/unicorn.out.log"  

GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)  

before_fork do |server, worker|  
  ActiveRecord::Base.connection.disconnect!  

  ##  
  # When sent a USR2, Unicorn will suffix its pidfile with .oldbin and  
  # immediately start loading up a new version of itself (loaded with a new  
  # version of our app). When this new Unicorn is completely loaded  
  # it will begin spawning workers. The first worker spawned will check to  
  # see if an .oldbin pidfile exists. If so, this means we've just booted up  
  # a new Unicorn and need to tell the old one that it can now die. To do so  
  # we send it a QUIT.  
  #  
  # Using this method we get 0 downtime deploys.  

  old_pid = "#{rails_root}/tmp/pids/unicorn.pid.oldbin"  
  if File.exists?(old_pid) && server.pid != old_pid  
    begin  
      Process.kill("QUIT", File.read(old_pid).to_i)  
    rescue Errno::ENOENT, Errno::ESRCH  
      # someone else did our job for us  
    end  
  end  
end  


after_fork do |server, worker|  
  ActiveRecord::Base.establish_connection  
  worker.user('rails', 'rails') if Process.euid == 0 && rails_env == 'production'  
end  

5 个答案:

答案 0 :(得分:15)

我刚刚发布了'unicorn-worker-killer'宝石。这使您可以根据1)最大请求数和2)处理内存大小(RSS)来杀死Unicorn worker,而不会影响请求。

它非常易于使用。无需外部工具。首先,请将此行添加到Gemfile

gem 'unicorn-worker-killer'

然后,请将以下几行添加到config.ru

# Unicorn self-process killer
require 'unicorn/worker_killer'

# Max requests per worker
use Unicorn::WorkerKiller::MaxRequests, 10240 + Random.rand(10240)

# Max memory size (RSS) per worker
use Unicorn::WorkerKiller::Oom, (96 + Random.rand(32)) * 1024**2

强烈建议随机化阈值,以避免一次性杀死所有工人。

答案 1 :(得分:9)

我认为您正在考虑内存使用情况和磁盘空间使用情况。它看起来像Unicorn和它的孩子使用大约500 MB的内存,你看第二个“ - / + buffers / cache:”数字来查看真正的空闲内存。就磁盘空间而言,我的赌注会出现在某种日志文件或类似的东西上。您应该在数据目录中执行du -h以找出使用如此多存储的确切内容。作为最后的建议,一个鲜为人知的事实是,如果Ruby分配它,它永远不会将内存返回给操作系统。它仍然在内部使用它,但是一旦Ruby抓住了一些内存,让它将未使用的内存返回给操作系统的唯一方法就是退出进程。例如,如果碰巧有一个进程将内存使用量增加到500 MB,即使请求完成且GC循环已运行,您也无法再次使用该500 MB。但是,Ruby将为未来的请求重用已分配的内存,因此不太可能进一步增长。

最后,谢尔盖提到上帝要监视过程记忆。如果您对使用它感兴趣,那么已有一个很好的配置文件here。请务必阅读associated article,因为这个上帝配置假定您拥有的unicorn配置文件中有关键内容。

答案 2 :(得分:3)

正如普雷斯顿所说,你没有内存问题(超过40%免费),你有一个完整的磁盘问题。 du报告大部分存储都在/ root / data中使用。

您可以使用find来识别非常大的文件,例如,以下内容将显示该目录下大于100MB的所有文件。

sudo find /root/data -size +100M

如果unicorn仍在运行,lsof(LiSt Open Files)可以显示正在运行的程序或特定进程集(-p PID)正在使用的文件,例如:

sudo lsof | awk  '$5 ~/REG/ && $7 > 100000000 { print }'

将显示大小超过100MB的打开文件

答案 3 :(得分:2)

你可以设置god来监视你的独角兽工人,如果他们吃太多记忆就会杀死他们。然后Unicorn主进程将分叉另一个worker来替换这个。问题解决了。 : - )

答案 4 :(得分:0)

如果您使用的是newrelic,请尝试删除应用的newrelic。 Newrelic rpm gem本身泄漏了内存。我遇到了同样的问题,我几乎在10天左右摸不着头脑来解决这个问题。

希望能帮助你。

我联系newrelic支持团队,下面是他们的答复。

  

感谢您与我们联系。我对这令人沮丧的事感到非常抱歉   你有过的经历。作为一个性能监控工具,我们的   意图是“首先不伤害”,我们非常关注这些问题   严重。

     

我们最近确定了这个问题的原因并且已经发布了   补丁来解决它。 (见https://newrelic.com/docs/releases/ruby)。我们   希望您考虑使用此修复程序继续使用New Relic进行监视。   如果您对此感兴趣,请确保至少使用   从现在开始,v3.6.8.168。

     

如果您有任何其他问题或疑虑,请告诉我们。   我们迫切希望解决这些问题。

即使我尝试更新newrelic gem,但它仍然会泄漏内存。最后我必须删除重写,虽然它是一个很棒的工具,但我们不能以这样的代价使用它(内存泄漏)。

希望能帮助你。