Passenger-Enterprise replacement

103 views
Skip to first unread message

Jorge Herskovic

unread,
Nov 30, 2017, 11:20:43 AM11/30/17
to Canvas LMS Users
Hi,

As far as we can tell, Phusion changed their pricing model for Passenger Enterprise. They now charge by the GB/month. Their professional version would cost us 230% of what we're paying now, per server. Their Enterprise version would cost us approximately 10x per server.

This gave me the impetus to move to unicorn+nginx. I will document the changes we made to get it to work below (on Ubuntu 16.04). This is for the development version.

1. Add gem 'unicorn' to Gemfile.d/development.rb
2. Rerun bundle install
3. Create config/unicorn.rb as below (adapted from a number of different websites, mostly this one, and Stack Overflow responses...)
# Set the current app's path for later reference. Rails.root isn't available at
# this point, so we have to point up a directory.
require 'concurrent'

app_path = File.expand_path(File.dirname(__FILE__) + '/..')

worker_processes (ENV['RAILS_ENV'] == 'production' ? Concurrent.processor_count : 1)

# You can listen on a port or a socket. Listening on a socket is good in a
# production environment, but listening on a port can be useful for local
# debugging purposes.
listen app_path + '/tmp/unicorn.sock', backlog: 64

# For development, you may want to listen on port 3000 so that you can make sure
# your unicorn.rb file is soundly configured.
listen(3000, backlog: 64) if ENV['RAILS_ENV'] == 'development'

# After the timeout is exhausted, the unicorn worker will be killed and a new
# one brought up in its place. Adjust this to your application's needs. The
# default timeout is 60. Anything underhttps://launchschool.com/blog/setting-up-your-production-server-with-nginx-and-unicorn3 seconds won't work properly.
timeout 60

# Set the working directory of this unicorn instance.
working_directory app_path

# Set the location of the unicorn pid file. This should match what we put in the
# unicorn init script later.
pid app_path + '/tmp/unicorn.pid'

# You should define your stderr and stdout here. If you don't, stderr defaults
# to /dev/null and you'll lose any error logging when in daemon mode.
stderr_path app_path + '/log/unicorn.log'
stdout_path app_path + '/log/unicorn.log'

# Load the app up before forking.
preload_app true

# Garbage collection settings.
GC.respond_to?(:copy_on_write_friendly=) &&
  GC.copy_on_write_friendly = true

# If using ActiveRecord, disconnect (from the database) before forking.
before_fork do |server, worker|
  defined?(ActiveRecord::Base) &&
    ActiveRecord::Base.connection.disconnect!
end

# After forking, restore your ActiveRecord connection.
after_fork do |server, worker|
  defined?(ActiveRecord::Base) &&
    ActiveRecord::Base.establish_connection

At this point Canvas should run under unicorn by executing 
bundle exec unicorn_rails -c config/unicorn.rb 

in the main Canvas directory. You'll find Canvas running on port 3000 of whichever machine you're on (in development mode; production mode will create a UNIX socket instead)

From then on, it's fairly straightforward. You'll need to forward connections to unicorn. My site definition in nginx follows (in my case, /etc/nginx/sites-available/canvas symlinked to /etc/nginx/sites-enabled):
upstream unicorn_canvas {
        server 127.0.0.1:3000 fail_timeout=4; # DEVELOPMENT
        @ server unix://PATH/TO/YOUR/CANVAS/tmp/unicorn.sock fail_timeout=4; #PRODUCTION
}

server {
  listen 80;
  server_name [whatever you want];

  keepalive_timeout 300;

  client_max_body_size 1G;

  root /PATH/TO/YOUR/CANVAS/public; # Set this to the public folder location of YOUR Canvas installation

  try_files $uri/index.html $uri.html $uri @unicorn;

  location @unicorn {
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header Host $http_host;
          proxy_set_header X-Forwarded_Proto $scheme;
          proxy_redirect off;
          # This passes requests to unicorn, as defined in /etc/nginx/nginx.conf
          proxy_pass http://unicorn_canvas;
          proxy_read_timeout 300s;
          proxy_send_timeout 300s;
  }


}

Finally, you'll need a way to run this whole thing together. I use systemctl instead of init.d, so I'm not sure what value you'd find in mine. Start unicorn as described above, start nginx, and you should be serving Canvas with a rolling-restart enabled, heavy-duty application server for free.

Hope this helps
Jorge




Christopher Bennell

unread,
Nov 30, 2017, 11:27:28 AM11/30/17
to Canvas LMS Users
Other than rolling restarts, what are the advantages of Unicorn over Passenger open source?

Graham Ballantyne

unread,
Nov 30, 2017, 11:39:27 AM11/30/17
to canvas-l...@googlegroups.com
Thanks for the write up, Jorge. Phusion hasn’t told us of any pricing changes to Enterprise but I’ll be following up with them for sure. 

We’re a RHEL shop; if you can share your systemctl setup, I’d be interested in seeing it. 

-- 
Graham Ballantyne 
IT Services 
Simon Fraser University 
--

---
You received this message because you are subscribed to the Google Groups "Canvas LMS Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to canvas-lms-use...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jorge Herskovic

unread,
Nov 30, 2017, 2:27:51 PM11/30/17
to Canvas LMS Users
Multi-process pools are supported as well. As far as I can tell, that’s a Passenger Enterprise version, and is pretty much necessary for performance. But rolling restarts are the killer feature.

Jorge Herskovic

unread,
Nov 30, 2017, 2:29:43 PM11/30/17
to Canvas LMS Users
Pricing is on their website. They charge per GB of RAM/month now, instead of a per-server fee. As you may expect, it can be a lot more expensive.

I’ll post my meager systemctl stuff later. It needs improvement :)

Graham Ballantyne

unread,
Dec 1, 2017, 3:04:03 PM12/1/17
to canvas-l...@googlegroups.com
I contacted their sales department and received the following. We're grandfathered in at our current licensing for the time being; if you're also a current customer I'd suggest checking into it. Our costs would go from ~$4500USD/year to either $17000USD/year for the business license or $75000USD/year for enterprise.

Hi Graham,

Thanks for reaching out to us about this, we're happy to answer any of your questions.

You are correct, we changed our pricing model for new customers, at the end of July. This change was made after reviewing customer feedback on pricing and what we offer currently in Passenger Enterprise, through support and the future features and improvements we have planned. We often heard that the per server model didn't appropriately accommodate anyone working in the cloud or containers. Our RAM based pricing model also allows us to improve on our previous iteration for pay-as-you-go pricing, which is coming in a few months.

I want to assure you that the price and model of your subscription is unaffected at this time, you are grandfathered into the per server model (and the educational institute discount) for the time being. We do not foresee, at least within the next couple years, a time where we would change your pricing model. If that time comes eventually, you will be thoroughly notified beforehand.

We want to thank you for reaching out with these questions, we appreciate you and your team being on board with Passenger, and please get back to us if you have any other questions or comments.
> --
>
> ---
> You received this message because you are subscribed to the Google Groups
> "Canvas LMS Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an email
> to canvas-lms-use...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Nick_K

unread,
Dec 4, 2017, 2:48:40 AM12/4/17
to Canvas LMS Users
In your scripts, do you have production environment enabled? Because, if you have production enabled, you need to have compiled the assets and have them served though nginx.

I have published something similar a long time ago. It is here if it helps you more:
https://groups.google.com/forum/#!searchin/canvas-lms-users/unicorn/canvas-lms-users/B84Ihp-agbE/BUQCOVMAwuUJ

Jorge Herskovic

unread,
Dec 5, 2017, 11:15:57 AM12/5/17
to Canvas LMS Users
Yes; good point. Serving them through nginx requires you to tweak one configuration item, for the nginx equivalent of X-Sendfile.

config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'



(I put this in my production-local.rb)

Adon Irani

unread,
Jan 8, 2018, 6:08:30 PM1/8/18
to Canvas LMS Users
Hi everyone,
Thanks for this.

I was able to get Unicorn running in production with a few minor modifications. Also the unicorn.rb script is missing the "end" likely due to a copy paste

What i'm wondering now is how to get unicorn to start automatically on system boot (on Ubuntu).
I found a number of references online, and was mostly close -- but the rails environment is throwing me for a bit of a loop.

Any leads?

I'm also wondering about the rolling restarts script. But i will look into it next.

Much appreciated!

Best,
Adon


On Thursday, November 30, 2017 at 11:20:43 AM UTC-5, Jorge Herskovic wrote:

Adon Irani

unread,
Jan 8, 2018, 8:27:05 PM1/8/18
to Canvas LMS Users
Sweet. I got it working...
basically using this reference: https://gist.github.com/foyo23/2360035
and updating with my /var/canvas config settings

/etc/init.d/unicorn then update-rc.d unicorn enable

#! /bin/sh

### BEGIN INIT INFO
# Provides:          unicorn
# Required-Start:    $local_fs $remote_fs $network $syslog
# Required-Stop:     $local_fs $remote_fs $network $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the unicorn web server
# Description:       starts unicorn
### END INIT INFO

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

DAEMON="bundle exec /var/canvas/vendor/bundle/ruby/2.4.0/bin/unicorn_rails"
DAEMON_OPTS="-c /var/canvas/config/unicorn.rb -E production -D"
NAME=unicorn
DESC="Unicorn app for canvas"
APP_DIR=/var/canvas/
PID=/var/canvas/tmp/unicorn.pid

case "$1" in
  start)
  echo -n "Starting $DESC: "
  cd $APP_DIR
  $DAEMON $DAEMON_OPTS
  echo "$NAME."
  ;;
  stop)
  echo -n "Stopping $DESC: "
        kill -QUIT `cat $PID`
        echo "$NAME."
        ;;
  restart)
  echo -n "Restarting $DESC: "
        kill -QUIT `cat $PID`
        sleep 1
        cd $APP_DIR
        $DAEMON $DAEMON_OPTS
        echo "$NAME."
        ;;
  reload)
        echo -n "Reloading $DESC configuration: "
        kill -HUP `cat $PID`
        echo "$NAME."
        ;;
  *)
  echo "Usage: $NAME {start|stop|restart|reload}" >&2
  exit 1
  ;;
esac

exit 0


Adon Irani

unread,
Jan 12, 2018, 10:40:03 PM1/12/18
to Canvas LMS Users
In case it helps, here's my nginx sites config which implements X-Accel-Redirect... (prior to adding that header by unicorn.log was getting X-Accel-Mapping header missing errors)

upstream unicorn_canvas {
server unix:/var/canvas/tmp/unicorn.sock fail_timeout=0;
}

server {
        listen 209.95.48.183:80;
        server_name lms.atriskmedia.com;

location ^~ /.well-known/acme-challenge/ {
allow all;
root /var/www/letsencrypt;
        }

location / {
return 301 https://lms.atriskmedia.com$request_uri;
}
}


server {
        listen 209.95.48.183:443 http2;

ssl on;
ssl_certificate /etc/letsencrypt/live/lms.atriskmedia.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/lms.atriskmedia.com/privkey.pem;

client_max_body_size 1G;
client_body_temp_path  /home/canvaslms/canvaslms/tmp/nginx/client_body 1;

        root /var/canvas/public;
try_files $uri/index.html $uri.html $uri @unicorn;

        server_name lms.atriskmedia.com;

keepalive_timeout 300;

location /protected_files/ {
alias /var/canvas/;
internal;
}

location @unicorn {
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 2048 8k;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded_Proto $scheme;
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
proxy_set_header X-Accel-Mapping /var/canvas/=/protected_files/;
proxy_redirect off;
}

        access_log /var/log/nginx/canvaslms.access.log main;
        error_log /var/log/nginx/canvaslms.error.log;

error_page 403 /403.html;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html { root /var/www/html; }
location = /404.html { root /var/www/html; }
location = /403.html { root /var/www/html; }
}
Reply all
Reply to author
Forward
0 new messages