Letting nginx automatically detect new rails apps

Posted by glenn on Tuesday, April 08, 2008

Posted in nginx, capistrano, rubyonrails, serveradministration

nginx-automatically-detect-app

Following on from my post yesterday explaining how to let god automatically detect any new rails apps is few capistrano and config tweaks. This time the changes will let nginx use the same config file we created to so that setting up a new upstream and rails config is as simple as a call to deploy:setup in capistrano.

Make sure you've setup app.god

These changes have a dependency on the changes from my previous post, so be sure to follow the steps on creating app.god to dynamically configure god for rails

Create a new nginx task for capistrano

Within your deploy.rb you want to insert this snippet:

namespace :deploy do

  after "deploy:setup", "deploy:nginx:setup" 

  namespace :nginx do
    desc 'Setup nginx upstream config'
    task :setup, :roles=>:app do
      @apps = []
      eval(open("./config/app.god").read)
      nginx_upstream_config = []
      @apps.each do |app|
        upstream_name = app.class.to_s.downcase.gsub(/.*::/,"")
        nginx_upstream_config << "upstream #{upstream_name} {\n" 
        app.ports.each do |port|
          nginx_upstream_config << "\tserver 127.0.0.1:#{port};\n" 
        end
        nginx_upstream_config << "}\n" 

        nginx_upstream_config << <<-EOF
        server {
          listen 80;
          client_max_body_size 50M;
          server_name #{app.server_names};
          root #{deploy_to}/current/public;
          access_log  /var/log/nginx.#{application}.access.log  main;

          if (-f $document_root/system/maintenance.html) {
            rewrite  ^(.*)$  /system/maintenance.html last;
            break;
          }

          location / {
            proxy_set_header  X-Real-IP  $remote_addr;
            proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_redirect false;
            proxy_max_temp_file_size 0;

            if (-f $request_filename) { 
              break; 
            }

            if (-f $request_filename/index.html) {
              rewrite (.*) $1/index.html break;
            }

            if (-f $request_filename.html) {
              rewrite (.*) $1.html break;
            }

            if (!-f $request_filename) {
              proxy_pass http://#{upstream_name};
              break;
            }
          }

          error_page   500 502 503 504  /500.html;
          location = /500.html {
            root   #{deploy_to}/current/public;
          }
        }        
        EOF

      end

      put nginx_upstream_config, "#{shared_path}/config/nginx.conf" 
      sudo "ln -sf #{shared_path}/config/nginx.conf #{deploy_to}/current/nginx.conf" 
      sudo "mkdir -p /etc/nginx" 
      sudo "ln -sf #{deploy_to}/current/nginx.conf /etc/nginx/#{application}.conf" 
      sudo "/etc/init.d/nginx restart" 
    end
  end

end

I'll run through what is happening here. Within deploy:nginx:setup we first instantiate the @apps variable, so that the app.god we created previously has an instance to insert itself into. We then read in and execute app.god, which means @apps will be populated with our application now.

Now for each application (why would we have more than one? I'll explain at the end) we loop over and create an nginx upstream definition, with each of the ports specified within app.god. Next is to create the standard nginx/rails config (feel free to change to suit your own needs), push it up to the server, and then link it into /etc/nginx/application_name.conf.

Required changes in nginx

The only thing you need to do within your nginx.conf file is add the following line at the end of your http service definition:

include /etc/nginx/*.conf;

And now, when you run:

cap deploy:setup

Capistrano will not just setup the rails directory structures, but will also update the nginx config to make it aware of your new application. It also means that your application specific nginx config now resides in your rails directory structure. Should you decide to take the application off the server, the nginx config disappears when the app does.

So why do you use @apps to specify the application?

Well with the god config from the previous post, god is dealing with every rails application on the server and stores the list of them within @apps. However, with this in most cases we are only dealing with one (the app we are deploying) so it's somewhat redundant. I say in most cases because there have been times when we've deployed the same application twice on the same server, running on different ports and accessible from different URLs. This meant we could do the following in app.god:

class Rubypond

  def initialize
    @ports = [8061, 8062, 8063]
  end
end

@apps << Rubypond.new

class RubypondLabs
  attr_reader :name, :ports, :prefix

  def initialize
    @name = "jetstream" 
    @ports = [8071, 8072, 8073, 8074]
    @prefix = "/labs" 
  end
end

@apps << RubypondLabs.new

Next up, how to extend what we've done so far to manage backgroundrb for us too.

Glenn Gillen is a ruby and ruby on rails developer with clients in London, New York, Los Angeles, and Australia. Ruby Pond specialise in social networking development, and social and online marketing. Contact Ruby Pond if you wish to discuss hiring Glenn or one of our other developers for your own project.
blog comments powered by Disqus