Rails koos Unicorni ja Nginxiga
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.
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.
God:
- tõmmatakse käima local.stardist: /usr/local/bin/god -c /srv/w/app/config/meedia.god
- jälgib, et Unicorn käiks
- jälgib, et Redis käiks
- jälgib, et 3 Resque protsessi käiks
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.
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.
- Mass-virtualhosting configuration for Nginx
- Sets up virtualhosts automatically, in the following way:
- test1.laas.tjs.ee -> /srv/laas/test1
- 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
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