2013-08-10 13 views
14

New Relic Process snapshotUnicorn Speichernutzung füllt fast die ganze RAM

Es gibt im Wesentlichen drei Probleme hier:

1) Unicorn scheint ständig alle RAM füllte werden, was mir Arbeiter manuell zu entfernen.

2) Unicorn scheint aus irgendeinem Grund zusätzliche Arbeiter hervorzubringen, obwohl ich eine feste Anzahl von Arbeitern angegeben habe (7 von ihnen). Dies verursacht teilweise den RAM-Aufbau, wodurch ich Arbeiter auch manuell entferne.

3) Keine Ausfallzeiten Einsatz ist in meinem Fall unzuverlässig. Manchmal nimmt es die Änderungen auf, manchmal bekomme ich Gateway Timeouts. Jeder Einsatz wird zu einer sehr stressigen Situation.

Ich mag es nicht wirklich, Monit zu verwenden, weil es Arbeiter tötet, ohne darauf zu warten, dass Arbeiter ihre Anfragen erfüllen.

Also, ist das normal? Haben andere Leute, die Unicorn einsetzen, das gleiche Problem, wenn der RAM einfach unkontrolliert wächst?

Und auch wo Arbeiter die Anzahl der Arbeiter spawned nicht die Anzahl der Arbeiter definiert?

Die andere Alternative ist Einhorn Worker Killer, die ich nach dem Lesen Unicorn Eating Memory ausprobieren würde.

Tiny Update:

enter image description here

So kam es zu einem Punkt, wo New Relic wurde mir die Erinnerung zu sagen war fast 95%. Also musste ich einen Arbeiter töten. Interessanterweise brachte das Töten dieses Arbeiters die Erinnerung um einiges herunter, wie aus der Grafik unten ersichtlich ist.

Was ist los?

Als Referenz, hier ist meine unicorn.rb und unicorn_init.sh. Würde mich freuen, wenn mir jemand sagt, dass da irgendwo ein Fehler ist.

unicorn.rb

root = "/home/deployer/apps/myapp/current" 
working_directory root 
pid "#{root}/tmp/pids/unicorn.pid" 
stderr_path "#{root}/log/unicorn.stderr.log" 
stdout_path "#{root}/log/unicorn.log" 

listen "/tmp/unicorn.myapp.sock" 
worker_processes 7 
timeout 30 

preload_app true 

before_exec do |_| 
    ENV["BUNDLE_GEMFILE"] = '/home/deployer/apps/myapp/current/Gemfile' 
end 

before_fork do |server, worker| 
    # Disconnect since the database connection will not carry over 
    if defined? ActiveRecord::Base 
    ActiveRecord::Base.connection.disconnect! 
    end 

    old_pid = "#{root}/tmp/pids/unicorn.pid.oldbin`" 
    if old_pid != server.pid 
    begin 
     sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU 
     Process.kill(sig, File.read(old_pid).to_i) 
    rescue Errno::ENOENT, Errno::ESRCH 
    end 
    end 
    sleep 1 
end 

after_fork do |server, worker| 
    # Start up the database connection again in the worker 
    if defined?(ActiveRecord::Base) 
    ActiveRecord::Base.establish_connection 
    end 

    Redis.current.quit 
    Rails.cache.reconnect 
end 

unicorn_init.sh

#!/bin/sh 
set -e 

# Feel free to change any of the following variables for your app: 
TIMEOUT=${TIMEOUT-60} 
APP_ROOT=/home/deployer/apps/myapp/current 
PID=$APP_ROOT/tmp/pids/unicorn.pid 
CMD="cd $APP_ROOT; BUNDLE_GEMFILE=/home/deployer/apps/myapp/current/Gemfile bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E production" 
AS_USER=deployer 
set -u 
OLD_PIN="$PID.oldbin" 

sig() { 
    test -s "$PID" && kill -$1 `cat $PID` 
} 

oldsig() { 
    test -s $OLD_PIN && kill -$1 `cat $OLD_PIN` 
} 

run() { 
    if [ "$(id -un)" = "$AS_USER" ]; then 
    eval $1 
    else 
    su -c "$1" - $AS_USER 
    fi 
} 

case "$1" in 
start) 
    sig 0 && echo >&2 "Already running" && exit 0 
    run "$CMD" 
    ;; 
stop) 
    sig QUIT && exit 0 
    echo >&2 "Not running" 
    ;; 
force-stop) 
    sig TERM && exit 0 
    echo >&2 "Not running" 
    ;; 
restart|reload) 
    sig USR2 && echo reloaded OK && exit 0 
    echo >&2 "Couldn't reload, starting '$CMD' instead" 
    run "$CMD" 
    ;; 
upgrade) 
    if sig USR2 && sleep 2 && sig 0 && oldsig QUIT 
    then 
    n=$TIMEOUT 
    while test -s $OLD_PIN && test $n -ge 0 
    do 
     printf '.' && sleep 1 && n=$(($n - 1)) 
    done 
    echo 

    if test $n -lt 0 && test -s $OLD_PIN 
    then 
     echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds" 
     exit 1 
    fi 
    exit 0 
    fi 
    echo >&2 "Couldn't upgrade, starting '$CMD' instead" 
    run "$CMD" 
    ;; 
reopen-logs) 
    sig USR1 
    ;; 
*) 
    echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>" 
    exit 1 
    ;; 
esac 
+0

wie haben Sie diese Grafik zu erzeugen? – ant

Antwort

9

Sie erscheinen zwei Probleme haben: 1) Sie haben Fehler bei der Koordinierung des ordnungsgemäßen Starts bringen alte Einhorn Arbeiter und den alten Meister umherzubleiben; 2) Ihre App (kein Einhorn) verliert Speicher.

Für ersteres an Ihrem before_fork Code suchen, es scheint, dass Sie den Speicher-beschränkenden Ansatz von the example config jedoch verwenden, können Sie einen Tippfehler in den .oldbin Dateinamen (eine Fremdrück Zecke am Ende), was bedeutet, Sie signalisieren niemals den alten Prozess, weil Sie die PID nicht aus einer nicht existierenden Datei lesen können.

Für die später werden Sie untersuchen und bohren müssen.Suchen Sie in Ihrer App nach Cache-Semantiken, die im Laufe der Zeit Daten sammeln. Überprüfen Sie sorgfältig die Verwendung von globalen Variablen, Klassenvariablen und Klasseninstanzvariablen, die Datenverweise von Anforderung zu Anforderung beibehalten können. Führen Sie einige Speicherprofile aus, um Ihre Speicherbelegung zu charakterisieren. Sie können das Speicherleck mindern, indem Sie Mitarbeiter töten, wenn sie größer als eine Obergrenze werden. unicorn-worker-killer macht dies einfach.

+0

Danke! Ich werde es sofort reparieren und zurück melden. Schätze die Zeit. –

+0

Kann dieser Einhorn-Arbeiter-Killer kommerziell genutzt werden? – tulio84z

+0

@dbenhur können Sie etwas weiter erklären, was "anmutige Neustart" und diese Speicherbeschränkung Ansatz ist? – tulio84z

3

Verwendung unicorn-worker-killer Dies macht es einfacher Arbeiter zu töten, die eine Menge von RAMs verbrauchen :)