ruby on rails - 거의 모든 RAM을 채우는 Unicorn 메모리 사용




ruby-on-rails (2)

근본적으로 3 가지 문제가 있습니다.

1) 유니콘이 꾸준히 모든 RAM을 가득 채우고있어 수동으로 작업자를 제거하게되었습니다.

2) 유니콘은 고정 된 수의 근로자 (7 명)를 지정 했음에도 불구하고 어떤 이유로 든 추가 근로자를 산란시키는 것 같습니다. 이것은 부분적으로 RAM이 쌓이게되어 수동으로 작업자를 제거하게됩니다.

3) 제 경우에는 가동 중지 시간을 전혀 사용할 수 없습니다. 때로는 변경 사항을 선택하기도하고 때로는 게이트웨이 시간 초과가 발생합니다. 각 배포는 매우 스트레스가 많은 상황이됩니다.

저는 노동자들이 요청을 처리하기를 기다리지 않고 노동자들을 죽이기 때문에 Monit을 사용하는 것을 좋아하지 않습니다.

그럼,이게 정상인가요? 유니콘을 사용하여 배포하는 다른 사람들이 RAM이 제어 할 수 없을 정도로 커지는 것과 동일한 문제가 있습니까?

또한 근로자의 수는 정의 된 근로자의 수와 일치하지 않습니다.

다른 대안으로 유니콘 워커 ( Unicorn Eating Memory)를 읽은 후에 시도해 볼 유니콘 워커 (unicorn worker killer)가 있습니다.

작은 업데이트 :

그래서 New Relic이 메모리가 거의 95 %라는 것을 알게되었습니다. 그래서 나는 노동자를 죽여야 만했다. 흥미롭게도 아래의 그래프에서 볼 수 있듯이 그 작업자를 죽이면 메모리가 상당히 많이 다운되었습니다.

그게 뭐야?

참고로 여기에 내 unicorn.rbunicorn.rb 있습니다. 누군가가 내게 어딘가에 실수가 있다고 말해주는 것을 좋아할 것입니다.

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

당신은 두 가지 문제가있는 것처럼 보입니다. 1) 오래된 유니콘 노동자를 일으키는 우아한 재시작과 오래된 주인이 주변에 머물러 있도록하는 데 오류가 있습니다. 2) 귀하의 앱 (유니콘이 아닌)이 메모리를 누설하고 있습니다.

전자의 경우 before_fork 코드를 보면 예제 config 의 메모리 제약 방식을 사용하고있는 것처럼 보입니다. 그러나 .oldbin 파일 이름에 오타가 있기 때문에 ( .oldbin 에는 관계없는 .oldbin ) 사용자가 존재하지 않는 파일에서 pid를 읽을 수 없으므로 이전 프로세스에 신호를 보내지 마십시오.

나중에, 당신은 조사하고 훈련해야 할 것입니다. 시간이 지남에 따라 데이터를 누적하는 의미를 캐싱하기 위해 앱을 살펴보십시오. 요청에서 요청에 이르는 데이터 참조를 유지할 수있는 전역, 클래스 바 및 클래스 인스턴스 vars의 모든 사용을 신중하게 검토하십시오. 일부 메모리 프로파일을 실행하여 메모리 사용을 특성화하십시오. 작업자가 상한선보다 크게 커질 때 작업자를 죽임으로써 메모리 누출을 줄일 수 있습니다. unicorn-worker-killer 는 이것을 쉽게 만듭니다.


unicorn-worker-killer 를 사용하면 많은 RAM을 소비하는 작업자를 쉽게 죽일 수 있습니다. :)