Rails koos Unicorni ja Nginxiga

Allikas: Kuutõrvaja
                                        Roheline.jpg Toores. Ehk seda pala võib täiendada.

Sissejuhatus

RailStation (õigemini Rack Station, kuna ka teised raamistikud ja toores Rack on toetatud) on lihtne server, mis koosneb järgnevatest kihtidest:

Lühidalt on aga lugu selline

Nginx server

  • serveerib staatilisi faile railsi /public kaustast
  • kõik muu suunab UNIXi socketile

Idee on selline, et kõige ees on Nginx, mis teeb dünaamilist mass-virtualhostimist ja kõikide RAKENDUS.zoo.tartu.ee domeenimede jaoks üritab leida sobivat UNIXi socketit, kuhu päring edasi anda.

EENet RailStation.png

God:

  • tõmmatakse käima local.stardist: /usr/local/bin/god -c /srv/w/app/config/meedia.god
  • jälgib, et Unicorn käiks (ja vajadusel stardib unicorn protsessid käima ka)
  • jälgib, et Redis käiks
  • jälgib, et 3 Resque protsessi käiks

Unicorni server:

  • käib plain-useri õigustes
  • käima tõmmatud Railsi kodukaustast: bundle exec unicorn -c unicorn.conf -D -E <environment>
  • kill -USR2 teeb no-downtime restardi, st Unicorn tõmbab uue masteri käima ja see loadib koodi ja kui ta esimese lapse teeb, siis too saadab vanale masterile surmasõnumi. kasutajate jaoks downtime ei ole.

Godi pluss on see, et tema konf on Ruby script, mistõttu on ta ülipaindlik.

Teine võimalus on kasutada God asemel Blessingut.

Blessing hooliseb Socketite olemasolu eest (blessing on inglise keeles ükssarvikute kari), mis on käivitatud selliselt, et leiab üles kõik /srv/w/*/config/unicorn.conf failid ja igaühe kohta tõmbab üne Unicorni käima. Seejärel jääb 10s intervalliga jälgima, kas faili muutmisaeg on muutunud, et Unicorni restartida.

Blessing on võimalik, et nõdram. Eesmärk on see, et ta monitoorib shell-glob mustriga kaustapuud (nt /srv/w/*/unicorn.conf) ja kui avastab mõne uue unicorn.conf faili, siis tõmbab uue Unicorni käima. Kui mõni ära kaob, tapab protsessi maha jne.

Uniko.png

nginxi ja Unicorni mass-virtual-hostingu konfinäite:

Seal see Unicorni conf on suht generic ja sobib praktiliselt üks-ühele iga Railsi jaoks, mis Unixi socketi kaudu ühendub front-end serveriga.


  1. Mass-virtualhosting configuration for Nginx
  2. Sets up virtualhosts automatically, in the following way:
  3. test1.laas.tjs.ee -> /srv/laas/test1
  4. app.tjs.ee -> /srv/prod/test1

nginx_mass_vhosts.conf

server {
    # enable one of the following if you're on Linux or FreeBSD
    listen 80 default deferred; # for Linux

    # If you have IPv6, you'll likely want to have two separate listeners.
    # One on IPv4 only (the default), and another on IPv6 only instead
    # of a single dual-stack listener.  A dual-stack listener will make
    # for ugly IPv4 addresses in $remote_addr (e.g ":ffff:10.0.0.1"
    # instead of just "10.0.0.1") and potentially trigger bugs in
    # some software.
    # listen [::]:80 ipv6only=on; # deferred or accept_filter recommended

    client_max_body_size 4G;

    # Change to your domain
    server_name .tjs.ee;

    # ~2 seconds is often enough for most folks to parse HTML/CSS and
    # retrieve needed images/icons/frames, connections are cheap in
    # nginx so increasing this is generally safe...
    keepalive_timeout 5;

    # Set vhost to default to host header
    set $vhost $host;

    # Split up the host header
    # NB! Modify to your domain
    if ($vhost ~ "^(w{3}\.)?(.*?)\.(.*?)?\.?tjs\.ee$") {
      set $vhost $2;
      set $dir $3;
    }

    # If dir is empty, set it to prod
    if ($dir ~ "^$") {
      set $dir prod;
    }
    
    root /srv/$dir/$vhost/public;

    # Prefer to serve static files directly from nginx to avoid unnecessary
    # data copies from the application server.
    try_files $uri/index.html $uri.html $uri @app;

    location @app {
      # an HTTP header important enough to have its own Wikipedia entry:
      #   http://en.wikipedia.org/wiki/X-Forwarded-For
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

      # enable this if and only if you use HTTPS, this helps Rack
      # set the proper protocol for doing redirects:
      # proxy_set_header X-Forwarded-Proto https;

      # pass the Host: header from the client right along so redirects
      # can be set properly within the Rack application
      proxy_set_header Host $http_host;

      # we don't want nginx trying to do something clever with
      # redirects, we set the Host: header above already.
      proxy_redirect off;


      proxy_pass http://unix:/srv/$dir/$vhost/tmp/sockets/unicorn.sock;
    }

    # Maintenance
    if (-f $document_root/maintenance.html) {
      return 503;
    }
    error_page 503 @maintenance;
    location @maintenance {
      rewrite ^(.*)$ /maintenance.html break;
    }

    # Rails error pages
    error_page 500 504 /500.html;
  }

unicorn.conf

# Per-app unicorn configuration

basedir = File.absolute_path(File.dirname(__FILE__))
working_directory basedir
worker_processes 4
pid "#{basedir}/tmp/pids/unicorn.pid"
stderr_path "#{basedir}/log/unicorn.log"
listen "#{basedir}/tmp/sockets/unicorn.sock", :backlog => 2048
timeout 30

preload_app true

##
# REE
# http://www.rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
if GC.respond_to?(:copy_on_write_friendly=)
  GC.copy_on_write_friendly = true
end


before_fork do |server, worker|
  # the following is highly recomended for Rails + "preload_app true"
  # as there's no need for the master process to hold a connection
  defined?(ActiveRecord::Base) and
    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 = basedir + '/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|
  ##
  # Unicorn master loads the app then forks off workers - because of the way
  # Unix forking works, we need to make sure we aren't using any of the parent's
  # sockets, e.g. db connection

  ActiveRecord::Base.establish_connection
  # Redis and Memcached would go here but their connections are established
  # on demand, so the master never opens a socket
end

Kasutamine

RailStationi (RS) kasutamiseks on vaja kahte asja

  • laadida rakenduse kood RS serveris kausta: /srv/w/NIMI
  • Koostada unicorn.conf

Iga 10 sekundi järel kontrollib RS uute rakenduste tekkimist ja käivitab need automaatselt. Samuti taaskäivitatakse ja suletakse rakendusi vastavalt vajadusele.

Lingid

https://github.com/blog/517-unicorn

http://www.opensourcerails.com/ hunnik railsi tarkvara