Tag: RoR

Running GitLab 7.1 using Puma instead of a Unicorn

Warning

Warning! Before you do this, please read why you should'nt: why did gitlab 6 switch back to unicorn?

So now, when

you-have-been-warned

let's get started...

Gemfile updates

Nothing special here. Just add:

gem 'puma'

and then:

# From /home/gitlab/gitlab
sudo bundle install --no-deployment
sudo -u gitlab -H bundle install --deployment --without development test postgres

Puma config

Create a puma.rb file in your gitlab config dir and copy/paste this:

app_path = File.expand_path(File.dirname(File.dirname(__FILE__)))

rails_env = ENV['RAILS_ENV'] ||  'production'
environment rails_env

threads 4, 32
workers 2

daemonize true
bind                 "unix://#{app_path}/tmp/puma/sock"
state_path           "#{app_path}/tmp/puma/state"
pidfile              "#{app_path}/tmp/puma/pid"
activate_control_app "unix://#{app_path}/tmp/puma/ctlsock"
stdout_redirect      "#{app_path}/log/puma_access.log", "#{app_path}/log/puma_error.log"

preload_app!

and

mkdir /home/git/gitlab/tmp/puma

At this point, you should be able to execute puma worker:

# You should execute this from a git user
cd /home/git/gitlab && exec bundle exec puma -C /home/git/gitlab/config/puma.rb

[4419] Puma starting in cluster mode...
[4419] * Version 2.9.0 (ruby 2.1.2-p95), codename: Team High Five
[4419] * Min threads: 4, max threads: 32
[4419] * Environment: production
[4419] * Process workers: 2
[4419] * Preloading application
[4419] * Listening on unix:///home/git/gitlab/tmp/puma/sock

Init Script

To manage my Pumas I use Jungle. You can read more about it here. From this point, I assume that you have figured out a way to autostart GitLab Puma process (if not, you'll have to start it each time manually - good luck!).

Unfortunately it is not all. Default GitLab init script (provided with GitLab sources) will try to run Unicorn, so we need to silent it (but we need to keep the Sidekiq part).

To do so, we have to change the /etc/init.d/gitlab script.

start_gitlab() method (line 165) - we have to comment the else case:

  # Then check if the service is running. If it is: don't start again.
  if [ "$web_status" = "0" ]; then
    echo "The Unicorn web server already running with pid $wpid, not restarting."
  # else
    # Remove old socket if it exists
    # rm -f "$socket_path"/gitlab.socket 2>/dev/null
    # Start the web server
    # RAILS_ENV=$RAILS_ENV bin/web start
  fi

wait_for_pids() method (line 78) - we have to remote the first condition from while loop. We no longer check for web_server_pid_path:

wait_for_pids(){
  # We are sleeping a bit here mostly because sidekiq is slow at writing it's pid
  i=0;
  while [ ! -f $sidekiq_pid_path ]; do
    sleep 0.1;
    i=$((i+1))
    if [ $((i%10)) = 0 ]; then
      echo -n "."
    elif [ $((i)) = 301 ]; then
      echo "Waited 30s for the processes to write their pids, something probably went wrong."
      exit 1;
    fi
  done
  echo
}

There are some other places that you could modify - so you would not get any warnings, but hey! Those are just warnings. What I did above is an absolute minimum for starting/stopping Sidekiq without any Unicorn errors.

Nginx server block

And an example Nginx server block (virtual host) config:

upstream git.server.name {
  server unix:///home/git/gitlab/tmp/puma/sock;
}

server {
  server_name git.server.name;
  client_max_body_size 32M;

  keepalive_timeout 5;

  root /home/git/gitlab/public;
  access_log /var/log/nginx/git.server.name.access.log;
  error_log  /var/log/nginx/git.server.name.error.log;

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

    if (-f $request_filename) {
      break;
    }

    if (!-f $request_filename) {
      proxy_pass http://git.server.name;
      break;
    }
  }
}

After that - you are ready to go! Good luck!

Nginx (and Puma behind) maintenance mode for Rack/Rails applications with Capistrano

Same for Apache + Passenger: Apache (Passenger) Maintenance mode for Ruby on Rails application with Capistrano

There is a time, when we need to switch our apps into maintenance mode. Maybe it is because of some data processing stuff, maybe because of backups, deployment or whatever good reason you might have. To be honest it doesn’t matter why. What does matter, is how we should handle working users of our apps. Even if you use a zero downtime deployment using Puma (great article on howto here), sometimes you just need to shutdown your app for few minutes.

Of course all the downtimes should take place when there is the smallest amount of users online. In most cases it might be a good idea to switch application off in the middle of the night (or on Sunday, etc.), but this won’t solve our primary problem: what should we show users that are already online?

The worst scenario ever would be showing them nothing (for example by shutting down whole application server). Users probably will think, that something bad happened. Much better idea is to show users a maintenance page with some sort of information like “Temporary down for maintenance”. It would be even better, if such a page would automatically display when needed.

In order to obtain this, we need to do two things:

  1. Configure our Nginx server block
  2. Add an additional Capistrano task

503 configuration for your Nginx server block (virtual host)

Add this at the end of your server block configuration (before the last curly bracket). It will test if a maintenance.txt file exists in a tmp directory, and if so, it will serve a 503 error page. It is worth pointing out, that all the assets will be served normally (we ignore them):

# Set a maintenance page
error_page 503 @maintenance;

location @maintenance {
  if (!-f $request_filename) {
    rewrite ^(.*)$ /503.html break;
  }
}

And in a location / section of the same file:

location / {
  # If we request a file that exists (assets/images/etc) serve them
  if (-f $request_filename) {
    break;
  }

  # If we request anything else - put 503 when needed
  if (-f $document_root/../tmp/maintenance.txt){
    return 503; 
  }

  # Here should go rest of this section
}

Capistrano hookup

To automate turning maintenance page on and off, I use a set of simple Capistrano tasks, enclosed in a Nginx namespace:

namespace :nginx do
  desc 'Switch current project into maintenance mode'
  task :lock do
    on primary :db do
      within release_path do
        execute :touch, 'tmp/maintenance.txt'
      end
    end
  end

  desc 'Turn off current project maintenance mode'
  task :unlock do
    on primary :db do
      within release_path do
        execute :rm, '-f tmp/maintenance.txt'
      end
    end
  end
end

Usage example (in my deploy.rb):

namespace :deploy do
  before :starting, 'nginx:lock'
  # Some other tasks...
  after :finished, 'nginx:unlock'
end

Good luck and as few maintenance downtime as possible!

Copyright © 2024 Closer to Code

Theme by Anders NorenUp ↑