Setting Up Vagrant
This was written with Github flavored markdown.. It messes up here a little. See a better version: https://gist.github.com/joshteng/9901155
Download and Install Vagrant http://www.vagrantup.com/ and VirtualBox https://www.virtualbox.org/ or VMware before beginning
Set up Vagrant
*this guide assumes the use of Mac OS X Mountain Lion on local machine and Ubuntu 12.04 LTS x64 on Vagrant box. It is compatible with many other distros but hasn't been tested.
Step 1: Make and run box
vagrant init
vagrant box add <path to box directory or name according to https://vagrantcloud.com/>
vagrant up
vagrant ssh
Step 2: Forward ports
Vagrantfile
config.vm.network "forwarded_port", guest: 80, host: 8080
config.vm.network "forwarded_port", guest: 3000, host: 3000
Set up environment
You can choose to use configuration management/provisioning tools like Chef/Puppet for the following steps to set up your environment. But if you want to understand how it's done, I encourage you to do it manually.
Step 3: Install needed system libraries and Postgresql
sudo apt-get update
sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt-dev libcurl4-openssl-dev python-software-properties libpq-dev postgresql postgresql-contrib
Step 4: Install ruby
git clone git://github.com/sstephenson/rbenv.git .rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL
git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
exec $SHELL
rbenv install 2.1.0
rbenv global 2.1.0
rbenv rehash
Step 5: Install Bundler
echo "gem: --no-ri --no-rdoc" > ~/.gemrc
gem install bundler
rbenv rehash
Step 6: Install Node.js for Rails asset pipeline
sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs
Step 7: Install Nginx on server
sudo apt-get install nginx
Step 8: Set up postgres 'vagrant' role
sudo su - postgres
createuser --pwprompt #create a role called vagrant.. just leave a blank password if you're using Vagrant locally for development and make it a superuser when prompted!
psql
ALTER USER Postgres WITH PASSWORD 'vagrant'; --run this in psql..I actually don't know why I had to do this.
\q --exit psql
exit
Set up app
Step 9: Bundle install
cd /vagrant #this is where your rails app should be by default if you changed it, just go to the right path on your VM
bundle install
rbenv rehash
Step 10: Create and Migrate DB
bundle exec rake db:create
bundle exec rake db:migrate
- if there's encoding issues: https://gist.github.com/joshteng/9895494
- if you are facing cryptic errors (eg. wrong ELF class: ELFCLASS32) with bcrypt, pg etc+ gems just remove it from
/vendor/bundle
and re-runbundle install
- if the above still doesn't solve your problem, remove all the gems from your rails app by doing
rm -rf /vagrant/vendor/bundle
and then runbundle install
again. (unfortunately this takes ages depending on your internet connection and rubygems.org) - if you're getting error
FATAL: role "vagrant" does not exist
, you didn't do step 8 right
Step 11: Testing
bundle exec rails s
sudo service nginx start
http://localhost:3000 should work now
http://localhost:8080 should show nginx welcome page
Step 12: Set up unicorn
Gemfile
gem 'unicorn'
shell
bundle install
touch <app_root>/config/unicorn.rb
unicorn.rb
root = "/vagrant"
app_name = "YOUR_APP_NAME"
working_directory root
pid "#{root}/tmp/pids/unicorn.pid"
stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"
listen "/tmp/unicorn.#{app_name}.sock"
worker_processes 4
timeout 40
preload_app true
# Force unicorn to look at the Gemfile in the current_path
# otherwise once we've first started a master process, it
# will always point to the first one it started.
before_exec do |server|
ENV['BUNDLE_GEMFILE'] = "#{root}/Gemfile"
end
before_fork do |server, worker|
defined?(ActiveRecord::Base) and
ActiveRecord::Base.connection.disconnect!
# Quit the old unicorn process
old_pid = "#{server.config[:pid]}.oldbin"
if File.exists?(old_pid) && server.pid != old_pid
puts "We've got an old pid and server pid is not the old pid"
begin
Process.kill("QUIT", File.read(old_pid).to_i)
puts "killing master process (good thing tm)"
rescue Errno::ENOENT, Errno::ESRCH
puts "unicorn master already killed"
end
end
end
after_fork do |server, worker|
port = 5000 + worker.nr
child_pid = server.config[:pid].sub('.pid', ".#{port}.pid")
system("echo #{Process.pid} > #{child_pid}")
defined?(ActiveRecord::Base) and
ActiveRecord::Base.establish_connection
end
- Starting unicorn:
bundle exec unicorn -c /vagrant/config/unicorn.rb -p 3000
Set up Nginx
Step 13: Set up nginx.conf in rails config directory
touch <app_root>/config/nginx.conf
nginx.conf
upstream unicorn {
server unix:/tmp/unicorn.YOUR_APP_NAME.sock fail_timeout=0;
}
server {
listen 80 default deferred;
# server_name example.com;
root /vagrant/public;
try_files $uri/index.html $uri @unicorn;
location @unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://unicorn;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
Step 14: Symlink nginx.conf
sudo ln -s /vagrant/config/nginx.conf /etc/nginx/sites-enabled/<your_app_name>
Step 15: Delete Nginx default site
sudo rm /etc/nginx/sites-enabled/default
Step 16: Restart Nginx
sudo service nginx restart
- Start unicorn:
bundle exec unicorn -c /vagrant/config/unicorn.rb
- Site should work at http://localhost:8080 now (make sure unicorn is still running)
Make Unicorn a service for convenience
Step 17: Set up unicorn_init.sh
touch <app_root>/config/unicorn_init.sh
bundle install --binstubs #creates an executable in the bin directory for our unicorn_init shell script
unicorn_init.sh
#!/bin/sh
set -e
# Example init script, this can be used with nginx, too,
# since nginx and unicorn accept the same signals
# Feel free to change any of the following variables for your app:
TIMEOUT=${TIMEOUT-60}
APP_ROOT=/vagrant
PID=$APP_ROOT/tmp/pids/unicorn.pid
CMD="$APP_ROOT/bin/unicorn -D -c $APP_ROOT/config/unicorn.rb"
action="$1"
set -u
old_pid="$PID.oldbin"
cd $APP_ROOT || exit 1
sig () {
test -s "$PID" && kill -$1 `cat $PID`
}
oldsig () {
test -s $old_pid && kill -$1 `cat $old_pid`
}
case $action in
start)
sig 0 && echo >&2 "Already running" && exit 0
echo "ATTEMPTING TO START UNICORN"
ruby -v
$CMD
# sudo su -c "ruby -v" - postgres
# sudo su -c "$CMD" - vagrant
;;
stop)
sig QUIT && exit 0
echo >&2 "Not running"
;;
force-stop)
sig TERM && exit 0
echo >&2 "Not running"
;;
restart|reload)
sig HUP && echo reloaded OK && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
$CMD
;;
upgrade)
if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
then
n=$TIMEOUT
while test -s $old_pid && test $n -ge 0
do
printf '.' && sleep 1 && n=$(( $n - 1 ))
done
echo
if test $n -lt 0 && test -s $old_pid
then
echo >&2 "$old_pid still exists after $TIMEOUT seconds"
exit 1
fi
exit 0
fi
echo >&2 "Couldn't upgrade, starting '$CMD' instead"
$CMD
;;
reopen-logs)
sig USR1
;;
*)
echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
exit 1
;;
esac
Make unicorn_init.sh executable
chmod +x config/unicorn_init.sh
Step 18: Symlink unicorn_init.sh
sudo ln -s <app_root>/config/unicorn_init.sh /etc/init.d/unicorn_your_app_name
Step 19: Start up Unicorn
#shut down all instances of Unicorn (optional)
ps aux | grep unicorn #find unicorn processes
kill -9 <pid> #shut it down
##start up unicorn as a linux process
sudo service unicorn_your_app_name start #it's important to know which version of ruby will be used when running as different users. if you're facing errors, see below.
If you're getting this error:
/opt/vagrant_ruby/lib/ruby/site_ruby/1.8/rubygems.rb:900:in "report_activate_error": Could not find RubyGem unicorn (>= 0) (Gem::LoadError)
Your init script isn't using the right version of Ruby. Here are few things you can try:
- Modify shell script to run your command as vagrant
su -c "$CMD" - vagrant
instead of just$CMD
- Try
sudo service unicorn_app_name start
andservice unicorn_app_name start
To debug effectively:
ONE: Check the version of Ruby that is being used when attempting to boot up Unicorn through your init shell script
```sh
this is just a snippet of code in unicorn_init file
case $action in
start)
sig 0 && echo >&2 "Already running" && exit 0
echo "ATTEMPTING TO START UNICORN"
ruby -v #check the version of ruby you're using
$CMD
# sudo su -c "ruby -v" - vagrant #check the version of ruby you're using
# sudo su -c "$CMD" - vagrant
```
TWO: Tail Unicorn log output to figure out what other erros you might have
sh
tail -f /vagrant/log/unicorn.log
Finally
if you're running unicorn as root
, you will need to add root as a role for postgres (go through step 8 but add root instead of vagrant)
Voila! It should work now!
MISC
VirtualBox slowness issues on mac
- Speeding up Vagrant: http://kubaf.wordpress.com/2013/01/12/vagrant-speed-up-on-mac-os-x/
- Add following in Vagrantfile
ruby config.vm.provider :virtualbox do |vb| vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"] end
disclaimer: I can't seem to get any of these solutions to speed up my VM. If you managed to speed up your VM on a Mac with VirtualBox, please leave some comment and help us out! Thanks!
In the meantime, you might want to look into VMware instead of VirtualBox
Written by Josh Teng
Related protips
2 Responses
nice post, thanks
You're welcome!