Tag Archives: deployment

Generate Gzipped Assets in Rails 4.2 +

Doesn’t Rails generate gzipped assets on running rake assets:precompile? It did so, until Rails 4.2. What changed?

Rails uses sprockets gem, for compiling and serving web assets. Before Sprockets 3 was introduced, rake assets:precompile generated .gz versions for all the assets.

This change, seems backward, for several reasons

  1. Gzipping should always be enabled for static assets. It’s super charges serving of static content.
  2. Assets should be gzip’ed beforehand as part of the build process, rather than letting the webserver do it on the fly, saving on CPU cycles.
  3. Also, Nginx has the ability to serve static .gz files directly, by using the gzip_static on; directive.

The reason for dropping this, was its incompatibility with Apache.

Since Sprockets 3 dropped this feature, it means Rails had to loose it too. The Rails team, plans to bring it back as an opt-in. Nevertheless, what does one do in the meantime?

Here are the options to generate Gzipped Assets with Rails

  1. depends on parallel gem to use multiple CPU cores, but can easily be simplified
  2. gzip assets with Capistrano
  3. write an assets:gzip rake task that plugs into assets:precompile (mentioned below)
namespace :assets do
  desc "Create .gz versions of assets"
  task :gzip => :environment do
    zip_types = /\.(?:css|html|js|otf|svg|txt|xml)$/

    public_assets = File.join(
      Rails.root,
      "public",
      Rails.application.config.assets.prefix)

    Dir["#{public_assets}/**/*"].each do |f|
      next unless f =~ zip_types

      mtime = File.mtime(f)
      gz_file = "#{f}.gz"
      next if File.exist?(gz_file) && File.mtime(gz_file) >= mtime

      File.open(gz_file, "wb") do |dest|
        gz = Zlib::GzipWriter.new(dest, Zlib::BEST_COMPRESSION)
        gz.mtime = mtime.to_i
        IO.copy_stream(open(f), gz)
        gz.close
      end

      File.utime(mtime, mtime, gz_file)
    end
  end

  # Hook into existing assets:precompile task
  Rake::Task["assets:precompile"].enhance do
    Rake::Task["assets:gzip"].invoke
  end
end
Advertisements

Setting up Nginx with Unicorn for Rails

I have moved away from using Thin to using Unicorn in production for a number of reasons

Some articles to convince you of the same thing.

  1. Unicorn is Unix
  2. Unicorn Power
  3. Most important for me was the ability of Unicorn master and worker processes to respond to Unix signals. This means, zero downtime deployments.

You need more convincing? Let me know and we can talk!

So with that out of the way, let’s see how you can configure nginx to work with unicorn.

  1. Install nginx via the package manager. (For AMI Ec2:sudo yum install nginx, For Ubuntu : sudo apt-get install nginx)
  2. Use this template for nginx unicorn configuration and then fill in only the required details.
  3. Note that the way it differs from thin configuration is by giving the server value as a location to the socket.
worker_processes 1;
user nobody nogroup;

pid /tmp/nginx.pid;
error_log /tmp/nginx.error.log;

events {
  worker_connections 1024; # increase if you have lots of clients
  # Set this to on if you have more than 1 working processes
  # This will allow only one child to watch the pollset and accept
  # a connection to a socket
  accept_mutex off; # "on" if nginx worker_processes > 1
}

http {
  include mime.types;
  default_type application/octet-stream;
  access_log /tmp/nginx.access.log combined;

  # This tells Nginx to ignore the contents of a file it is sending
  # and uses the kernel sendfile instead
  sendfile on;

  # Set this to on if you have sendfile on
  # It will prepend the HTTP response headers before
  # calling sendfile()
  tcp_nopush on;

  # This disables the "Nagle buffering algorithm" (Nginx Docs)
  # Good for websites that send a lot of small requests that
  # don't need a response
  tcp_nodelay off;

  gzip on;
  gzip_http_version 1.0;
  gzip_proxied any;
  gzip_min_length 500;
  gzip_disable "MSIE [1-6]\.";
  gzip_types text/plain text/html text/xml text/css
             text/comma-separated-values
             text/javascript application/x-javascript
             application/atom+xml;

  upstream unicorn_server {
   # This is the socket we configured in unicorn.rb
   server unix:/var/rails/testapp/tmp/sockets/unicorn.sock »
   fail_timeout=0;
  }

  server {
    listen 80;
    client_max_body_size 4G;
    server_name _;
    keepalive_timeout 5;

    # Location of our static files
    root /home/ec2-user/testapp/public;

    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;

      # If you don't find the filename in the static files
      # Then request it from the unicorn server
      if (!-f $request_filename) {
        proxy_pass http://unicorn_server;
        break;
      }
    }    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /home/ec2-user/testapp/public;
    }
  }
}

If you have unicorn correctly configured then start unicorn, restart nginx and you should be set.