【问题标题】:Unicorn Eating Memory独角兽吃记忆
【发布时间】:2012-01-08 13:31:51
【问题描述】:

我在亚马逊有一个 m1.small 实例,它有 8GB 硬盘空间,我的 rails 应用程序在该实例上运行。它顺利运行了 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  

独角兽.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  

【问题讨论】:

    标签: ruby-on-rails diskspace unicorn


    【解决方案1】:

    如果您使用的是 newrelic,请尝试为您的应用删除 newrelic。 Newrelic rpm gem 本身泄漏内存。我遇到了同样的问题,我花了将近 10 天的时间才弄清楚这个问题。

    希望对你有所帮助。

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

    感谢您与支持人员联系。我对令人沮丧的事情深表歉意 你有过的经历。作为性能监控工具,我们的 意图是“首先不伤害”,我们非常重视这类问题 认真的。

    我们最近确定了此问题的原因并发布了 补丁来解决它。 (见https://newrelic.com/docs/releases/ruby)。我们 希望您会考虑通过此修复恢复使用 New Relic 进行监控。 如果您有兴趣这样做,请确保您至少使用 v3.6.8.168 从现在开始。

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

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

    希望对你有所帮助。

    【讨论】:

      【解决方案2】:

      我刚刚发布了“unicorn-worker-killer”gem。这使您可以根据 1) 最大请求数和 2) 进程内存大小 (RSS) 来杀死 Unicorn 工作者,而不会影响请求。

      它真的很容易使用。无需外部工具。首先,请将此行添加到您的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
      

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

      【讨论】:

      • 它只适用于独角兽。将尝试在假期创造彩虹工人杀手!
      • 有趣.. 感谢您的宝石。
      • 克里希纳,如果您对这颗宝石有任何疑问或问题,请告诉我!
      【解决方案3】:

      正如 Preston 所说,您没有内存问题(超过 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

      【讨论】:

      • sudo lsof ... 非常方便。谢谢。
      【解决方案4】:

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

      最后,谢尔盖提到上帝监控进程内存。如果你有兴趣使用它,已经有一个很好的配置文件here。请务必阅读associated article,因为独角兽配置文件​​中有关键的东西,这个神配置假设你有。

      【讨论】:

      • 谢谢普雷森。我确信没有生成正在消耗内存的日志。这周末我会试试上帝或Monit。
      【解决方案5】:

      你可以设置god 来监视你的独角兽工人,如果他们吃太多内存就杀死他们。然后独角兽主进程将派生另一个工作人员来替换这个工作人员。问题解决了。 :-)

      【讨论】:

      • 所以你说独角兽如果没有人监视它会继续吃内存吗?
      • 感谢 sergei 的快速回复。
      • @KrishnaprasadVarma 我是说可能会发生泄漏。您的代码可能会泄漏,或者 ruby​​ 本身,或者某些 3rd-party gem。调试这些可能是……不平凡的。停止违规过程并重新开始会容易得多。
      猜你喜欢
      • 2015-05-05
      • 2014-01-15
      • 2012-01-05
      • 1970-01-01
      • 2014-08-19
      • 2013-01-10
      • 2012-09-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多