Created
December 22, 2010 16:17
-
-
Save bdeterling/751703 to your computer and use it in GitHub Desktop.
Instructions I used to set up a new Ubuntu server with rvm, passenger, rails, etc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
These instructions have two purposes: 1) my own notes so I can more easily set up a new machine next time; 2) help for a Rails n00b to get a machine and a project set up without having to make all the same mistakes I did. | |
This walks you through setting up a remote Linux server and creating a Rails 3 project locally, using Github and Capistrano to collaborate and deploy. | |
Local -> means on your machine which should be a Unix variant (some OS X specifics may have snuck in) | |
Server -> means the server | |
Local -> | |
setup a /etc/hosts entry to map the IP to a simple name, hereafter 'server' | |
ssh root@server <enter root password> | |
-- Add user and set up for ssh public/private key login -- | |
Server -> | |
adduser you | |
# answer various prompts | |
su - you | |
mkdir .ssh | |
cd .ssh | |
create authorized_keys file | |
if you don't have an existing key (~/.ssh/id_rsa and id_rsa.pub) | |
Local -> | |
ssh-keygen -t rsa -C "ssh key for your-name" | |
it will create ~/.ssh/id_rsa and id_rsa.pub | |
end | |
copy contents of local id_rsa.pub to server .ssh/authorized_keys | |
chmod 600 authorized_keys | |
cd .. | |
chmod 700 .ssh | |
Local -> | |
attempt ssh server-name (or ssh login@server-name if not the same) | |
if login is different, create .ssh/config add these lines | |
Host server | |
user user-on-server | |
create one section for each way you may log in, i.e. IP address, official host name, your /etc/hosts entry | |
-- Add user to sudoers so we can disable root login -- | |
Server -> | |
su - <enter root password> | |
vi /etc/sudoers | |
copy ROOT...all line and change to <username> | |
-- Test sudo | |
Server -> | |
log out and back in | |
sudo su - <enter your password> | |
should get root prompt | |
-- Disable root login -- | |
Server -> | |
sudo vi /etc/ssh/sshd_config | |
set PermitRootLogin to no | |
set PasswordAuthentication to no # only allow ssh keys | |
ps -ef|grep sshd | |
kill -HUP <process id of sshd> | |
-- Set up other users as needed and add to sudoers | |
-- Install Shorewall (firewall) | |
# You could use iptables as the firewall, but I don't understand it and Shorewall has a nice | |
# web admin interface. | |
@ Using instructions from: http://www.wowtutorial.org/tutorial/16.html | |
Server -> | |
sudo apt-get install shorewall | |
sudo vi /etc/default/shorewall | |
set startup = 1 | |
*sudo cp /usr/share/doc/shorewall/default-config/* /etc/shorewall/ | |
* I actually copied this from another server since it had the rules I wanted already | |
-- Install Webmin to make admin of Shorewall less of a pain | |
# Webmin is the aforementioned web interface for Shorewall and lots of other Linux tools | |
@ Using instructions from: http://www.ubuntugeek.com/webmin-installation-and-configuration-in-ubuntu-linux.html | |
Server -> | |
sudo apt-get install perl5 libnet-ssleay-perl | |
# look up the latest version of webmin if this is way in the future | |
cd /tmp | |
wget http://prdownloads.sourceforge.net/webadmin/webmin-1.510.tar.gz | |
tar xvfz webmin-1.510.tar.gz | |
cd webmin-1.510 | |
sudo sh setup.sh | |
defaults except: | |
port: 10184 # this can be anything; changed from default for obscurity | |
user: tadmin # also can be anything; changed from default | |
password: <same as root> | |
use ssl: y | |
start at boot: y | |
Browser -> | |
Go to https://server:10184/ (assuming you mapped server to the IP, otherwise use IP) | |
log in | |
Networking -> Shorewall Firewall | |
Zones | |
Add Zone | |
id: net | |
type: ipv4 | |
leave rest blank | |
Interfaces | |
Add interface | |
Interface: eth0 | |
Zone: net | |
Broadcast: Automatic | |
uses dhcp, reject packets on blacklist, check for illegal tcp, | |
log w/impossible sources, check for broadcast source | |
Default policy | |
Add | |
$FW net ACCEPT | |
net $FW DROP info | |
net all DROP info | |
all all REJECT info | |
Stopped (allow various machines I can get to access when the firewall is stopped; everything else will be blocked) | |
eth0 99.0.0.1 | |
Firewall Rules | |
# It's kind of tedious to do this through the UI | |
# Instead you can edit /etc/shorewall/rules | |
*** But run check firewall in the browser after editing to make sure you didn't screw it up | |
Server -> | |
Contents of rules (minus some comments at top): | |
# Drop Ping from the "bad" net zone.. and prevent your log from being flooded.. | |
Ping/ACCEPT net:99.0.0.1 all # allow you home to ping | |
# Permit all ICMP traffic FROM the firewall TO the net zone | |
Ping/DROP net $FW | |
ACCEPT $FW net icmp # ping from inside | |
SSH/ACCEPT net:99.0.0.1 fw # home ssh | |
ACCEPT net:99.0.0.1 fw tcp 10878 # home webmin | |
Web/ACCEPT all all # everyone can hit port 80 | |
ACCEPT all all tcp 3000,4567 # everyone can hit rails, sinatra defaults | |
... add rules for other IP addresses as needed | |
Browser -> | |
Run check firewall from webmin | |
!!! make sure you're still logged in via ssh at this point in case something is screwed up !!! | |
try ssh from various hosts to makes sure they're allowed | |
Start firewall | |
try ssh from allowed hosts in separate window | |
try more webmin pages to make sure that's allowed | |
try ssh from other hosts to makes sure they're blocked | |
Server -> | |
-- Install MySQL | |
sudo apt-get install mysql-server | |
# left root password blank for now | |
** Would highly recommend not opening up MySQL port via firewall; set root password if you do | |
-- Install apache2 | |
# There is an Apache module called Phusion Passenger that is the defacto standard for serving | |
# Rails applications. | |
sudo apt-get install apache2 | |
-- Install git | |
sudo apt-get install git-core | |
-- Create a deploy user | |
# This will be used by the deploy scripts so we don't have to use our own user names and passwords | |
sudo adduser deploy | |
... answer questions | |
-- Install RVM system-wide | |
# RVM is a way to easily run multiple versions of Ruby and to isolate gems within sets so | |
# you can keep track of what your using. | |
# You could install it either locally for the deploy user or system-wide so all users can | |
# access it. I chose system-wide. | |
sudo bash < <( curl -L http://bit.ly/rvm-install-system-wide ) | |
-- Set up rvm for users | |
# RVM uses an rvm group to control permissions; it's nice to make this the primary group of | |
# all the users so everyone can read/write stuff in the common directories without having to | |
# switch all the time. | |
get id of rvm group from /etc/group | |
change /etc/passwd so everyone's primary group is rvm | |
Add to everyone's .profile: | |
[[ -s "/usr/local/lib/rvm" ]] && . "/usr/local/lib/rvm" # This loads RVM into a shell session. | |
log out and back in for profile and group to take effect | |
-- Install Ruby | |
# 1.9.2 is the way to go for Rails 3. | |
# Unless otherwise noted, I think this can be done as non-root. Better to try that and fail than to create | |
# some weird root dependency that you don't find out about until later. | |
-- Install gcc | |
* Needed for Ruby 1.9 | |
sudo apt-get install gcc | |
-- Install other Ruby dependencies | |
aptitude install build-essential bison openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf | |
rvm install 1.9.2-p0 | |
rvm --default use 1.9.2 # seems to make this the default for all users | |
-- Install Passenger | |
# As mentioned, this is an Apache module that groks Rails. | |
rvm use 1.9.2 | |
gem install passenger | |
/usr/local/rvm/gems/ruby-1.9.2-p0/bin/passenger-install-apache2-module | |
# This will configure Passenger; read what it says in case there are dependencies | |
# unique to your situation | |
Install missing dependencies listed by above: | |
sudo apt-get install libcurl4-openssl-dev | |
sudo apt-get install apache2-prefork-dev | |
sudo apt-get install libapr1-dev | |
sudo apt-get install libaprutil1-dev | |
Re-run after fixing missing dependencies: | |
/usr/local/rvm/gems/ruby-1.9.2-p0/bin/passenger-install-apache2-module | |
# Per instructions from passenger (kinda), create /etc/apache2/mods-available/passenger.load: | |
# This configures the Passenger module. | |
LoadModule passenger_module /usr/local/rvm/gems/ruby-1.9.2-p0/gems/passenger-3.0.2/ext/apache2/mod_passenger.so | |
PassengerRoot /usr/local/rvm/gems/ruby-1.9.2-p0/gems/passenger-3.0.2 | |
PassengerRuby /usr/local/rvm/wrappers/ruby-1.9.2-p0/ruby | |
cd /etc/apache2/mods-enabled | |
# The general idea is that there is an available and an enabled directory for modules and sites. | |
# You put everything in available and then link the stuff into enabled that you want to use. | |
ln -s ../mods-available/passenger.load passenger.load | |
sudo /etc/init.d/apache2 restart | |
Browser -> can double-check non-Rails by going to http://server. Should get default Apache It Worked! page | |
-- Setup web apps | |
# There are lots of structures you could use but this one is in some tutorials and seems to make | |
# sense. /webapps is where the actual apps go; /websites contains links to the appropriate directories | |
# in the apps; Passenger only knows about /websites although somehow it can still access the others | |
sudo mkdir /websites | |
sudo chgrp rvm /websites | |
sudo mkdir /webapps | |
sudo chgrp rvm /webapps | |
sudo chmod 775 /web* # allow rvm group to write | |
-- Install a test Sinatra app | |
# Not strictly required, but good for future use and to validate the Passenger configuration without | |
# introducing Rails into the picture yet. | |
gem install sinatra | |
# I use the same structure for Sinatra as for Rails. Under the main directory (sinatra_test) are | |
# directories for releases with the most recent release being linked to current which is what | |
# Passenger actually uses. You keep a few recent releases so you can go back just by changing the | |
# symlink. | |
mkdir -p /webapps/sinatra_test/current/public | |
# Passenger deals with 'rack' applications. That's a standard interface for webapps that are generally | |
# in Ruby but don't have to be. You can actually create a web server in 3 lines of Ruby code that | |
# conforms to the Rack standard. | |
# Both Rails and Sinatra follow the rack standard which is why Passenger can treat them the same. | |
# The config.ru file is the entry into the rack app. | |
edit /webapps/sinatra_test/current/config.ru: | |
# before 1.9 you needed "require 'rubygems'" here | |
# as of 1.9, ./ is required and require_relative doesn't work for some reason | |
require './sinatra_test' | |
run Sinatra::Application | |
edit /webapps/sinatra_test/current/sinatra_test.rb | |
require 'sinatra' | |
# On prior versions I had to add these lines for some reason, but | |
# doesn't seem to matter now | |
#before do | |
# request.env['PATH_INFO'] = '/' if request.env['PATH_INFO'].empty? | |
#end | |
get '/' do | |
"Hello there world!" | |
end | |
# Link the public directory to /websites for Passenger | |
ln -s /webapps/sinatra_test/current/public /websites/sinatra_test | |
# There are lots of options for setting up Apache vhosts with Passenger. | |
# Normally you would create a sites-available/vhost for every vhost, like | |
# test.domain.com, stage.domain.com, etc. If there is no domain, everything | |
# goes through the IP so I think it can only handle one vhost. But you can | |
# use suburis to map apps like http://server/app1, http://server/app2. | |
-- Setup Passenger to know about the app | |
# Most of this will need to be done as root | |
cd /etc/apache2/sites-available | |
edit 'apps' | |
<VirtualHost *:80> | |
ServerName <ip address of server> | |
DocumentRoot /websites | |
<Directory /websites> | |
Options -Indexes FollowSymLinks MultiViews | |
AllowOverride None | |
Allow from all | |
</Directory> | |
RackBaseURI /sinatra_test | |
<Directory /websites/sinatra_test> | |
Options -MultiViews | |
</Directory> | |
</VirtualHost> | |
cd ../sites-enabled | |
remove 000-default or whatever it is | |
* the sites-available copy will still be there | |
sudo ln -s ../sites-available/apps 001-apps | |
# the 001 is for when you have multiple vhosts, you can control | |
# the order of preference | |
# restart apache | |
/etc/init.d/apache2 restart | |
or | |
sudo apachectl restart | |
go to http://server/sinatra_test | |
# Should see hello there world or whatever | |
-- Setup Mail | |
# Used webmin for this | |
Go to unused modules | |
Choose postfix | |
Prompts you to install it | |
Didn't really change any configuration | |
- did change to only allow smtp from server (rather than network) | |
- also turned on sasl something or other | |
Added Shorewall firewall rule to allow smtp from <firewall> to <any> | |
* Not sure this was needed; think firewall -> any is open by default | |
# Use mailx for testing | |
sudo apt-get install mailutils | |
mailx some email address you own | |
# see if you got it | |
# Had problems with sending from Rails; I think it's due to no valid domain name | |
# Options are gmail, sendgrid, bluesky, etc | |
-- Setup Github | |
# Information on using git and Github. | |
# There is some first-time setup stuff here that you should do like configuring | |
# your name and e-mail; I left it out below. | |
http://help.github.com/creating-a-repo/ | |
http://toolmantim.com/thoughts/setting_up_a_new_rails_app_with_git | |
On Github -> | |
Create repository <myproject> # hereafter just myproject | |
Add collaborators as needed | |
Local -> | |
In local projects directory (I use ~/projects) | |
# This assumes you have installed rvm and ruby 1.9.2 locally | |
rvm use 1.9.2 | |
# A gemset is a way of isolating all the dependencies for a project. Before they existed, | |
# you would end up with 100 gems installed system wide and would not know which ones went | |
# with which project. Now you can install gems per-project so you know what you're using. | |
rvm gemset create myproject | |
rvm use 1.9.2@myproject | |
# create an empty Rails project | |
rails new myproject | |
cd myproject | |
# It's hard to remember to set your gemset when working multiple projects. .rvmrc lets | |
# you have it automatically set it when you cd into your project directory. | |
edit .rvmrc to say 1.9.2@myproject | |
# Stage all of the Rails project. Rails is smart enough to add stuff like logs to .gitignore | |
# which keeps it from being checked in. This does not actually commit the files, only | |
# moves them to the staging directory. | |
git add . | |
# Now actually commit stuff. It's still only in your local repository though. | |
git commit -m "First commit" | |
# Set up the remote (central) repository that will be shared | |
# Substitute your github username | |
git remote add origin [email protected]:<your-github-username>/myproject.git | |
# Actually push your commits to the remote repository. origin is the name of the | |
# remote (it could be anything but that's the standard). master is the default branch. | |
# For serious multi-user projects you will want to learn about branching and | |
# rebasing. Terrible stuff in svn, but a major benefit of git. | |
git push origin master | |
# A gem is more or less like a jar file in Java (think apache-commons-lang.jar), but | |
# includes information about where the source is, what it depends on, etc. | |
# Gemfile is a list of your project's dependencies. This is a relatively new concept | |
# based on the bundler packaging tool. Before bundler, you had to manually load gems | |
# and set up the same versions on your various machines. Devs could have different | |
# versions which could cause problems. | |
edit Gemfile | |
uncomment mysql # the database gem | |
uncomment capistrano # used for deploying easily | |
uncomment ruby-debug19 # the ruby debugger | |
Add these (not required, but might be useful in the future): | |
gem 'foreigner' # commands to set foreign keys in migrations | |
gem 'dynamic_form' # this used to be built-in, e.g. f.error_messages | |
gem 'jquery-rails' # by default Rails uses prototype but JQuery is better | |
# Load new gems and update Gemfile.lock which includes all the dependencies | |
bundle update | |
# Rails by default uses Prototype, but JQuery has become more popular. | |
# This will install various JQuery components and change the rails.js file | |
# to use JQuery instead of Prototype. | |
rails generate jquery:install --ui | |
# Instead of using git add/commit/push, I normally use git-gui. It pops up | |
# a window that lets you see everything that changed since the last commit. | |
# Select the files in top left, cmd-t to stage them, then enter a comment and | |
# commit. Then push to send them to the remote server. | |
stage, commit, and push your Gemfile changes | |
-- Set up databases | |
edit config/database.yml; change to use mysql | |
add stage environment so you should have dev, test, stage, and production | |
e.g. | |
production: | |
adapter: mysql | |
database: myproject_prod | |
username: root | |
password: | |
host: localhost | |
pool: 5 # skip this for non-prod | |
timeout: 5000 # skip this for non-prod | |
# commit and push changes | |
Server -> | |
mysql -u root | |
create database myproject_stage | |
create database myproject_prod | |
-- Setup deploy process | |
# Capistrano is a gem that lets you automate a lot of tasks on a remote server. It's nice | |
# enough that you can automate it for one server, but once you have it set up, you can | |
# just add more servers to the list and it will deploy to all of them, e.g. if you use | |
# load balancing. It works by ssh'ing into the server and running the commands. | |
@ Using: | |
http://help.github.com/capistrano/ | |
http://railstips.org/blog/archives/2008/12/14/deploying-rails-on-dreamhost-with-passenger/ | |
-- Setup deploy user on server for Github access | |
Server -> | |
# Github lets you create a deply ssh key so you don't have to use passwords | |
# to grab the latest versions. | |
sudo su - deploy | |
ssh-keygen -t rsa -C "deploy key for myproject" | |
default directory | |
passphrase same as deploy user (or whatever) | |
# So anyone can deploy but it will run as the deploy user: | |
add other user's public keys to ~deploy/.ssh/authorized_keys | |
On Github -> | |
add deploy key (public, i.e. id_rsa.pub) to github repository under admin -> deploy keys | |
Server -> | |
chown deploy:rvm authorized_keys (if not done earlier) | |
chmod 600 authorized_keys (if not done earlier) | |
ssh [email protected] | |
enter passphrase | |
should get message saying it worked | |
Local -> | |
# test logging in as deploy user | |
ssh deploy@server | |
Server -> | |
# Install bundler in base 1.9.2 install on server | |
rvm use 1.9.2 | |
gem install bundler | |
# Setup gemset to match local | |
rvm gemset create myproject | |
# Technically this wasn't necessary but I got errors on a normal | |
# deploy because it couldn't install the debugger, so I installed | |
# it as root ahead of time. You could change Gemfile to only use | |
# debugger or development and test instead. | |
as root: | |
rvm 1.9.2@myproject | |
gem install ruby-debug19 | |
Local -> | |
# Add settings for stage environment. We'll use stage to test stuff on | |
# the server before deploying to production. | |
cp config/environments/production.rb config/environments/stage.rb | |
# change local requests to true. When false, it gives a generic clean | |
# error message; when true, it gives a stacktrace. | |
# Set up the project to use Capistrano | |
capify . | |
# Update lots of stuff in config/deploy.rb. Too much to document, just | |
# copy the existing one in the future. | |
# Important things are: | |
* use stage as default | |
* make sure rvm_type is system, not user since we're doing a systemwide rvm | |
# Use cap to set up the directory structure on the server. Given the config/deploy.rb | |
# file this will set up /webapps/myproject/stage/releases, current, etc | |
cap deploy:setup | |
# Validate everything | |
cap deploy:check | |
# Do the first deploy | |
cap deploy | |
# Assuming stage worked, set up production | |
RAILS_ENV=production cap deploy:setup # same thing but for production | |
RAILS_ENV=production cap deploy | |
# Checkin deploy changes (for other users, deploying only cares about code that's checked in) | |
git commit -a -m "Deploy stuff" | |
git push | |
-- Setup Rails apps with Passenger | |
Server -> | |
As deploy user: | |
# Link the apps to the /websites directory for Passenger | |
cd /websites | |
ln -s /webapps/myproject/stage/current/public myproject_stage | |
ln -s /webapps/myproject/production/current/public myproject | |
As root: | |
* This assumes you set up the Sinatra test app. If not, follow those instructions | |
* for the stuff in /etc/apache2. | |
cd /etc/apache2/sites-enabled | |
edit 001-apps add the following inside the <VirtualHost> tags | |
RailsBaseURI /myproject_stage | |
<Directory /websites/myproject_stage> | |
Options -MultiViews | |
</Directory> | |
RailsBaseURI /myproject | |
<Directory /websites/myproject> | |
Options -MultiViews | |
</Directory> | |
cd /webapps/myproject/stage/current | |
# should prompt to use .rvmrc the first time | |
bundle install | |
# This should already be done but for some reason this triggers passenger to use the right gemset | |
# Do the same for production: | |
cd /webapps/myproject/production/current | |
bundle install | |
# Restart apache | |
apachectl restart | |
# Try http://server/myproject_stage and http://server/myproject | |
-- Making changes (no database changes) | |
Local -> | |
# Make your changes | |
rails s to run the server and test (http://localhost:3000) | |
Commit and push | |
cap deploy | |
# Test stage | |
RAILS_ENV=production cap deploy | |
# Test prod | |
-- Making changes (with database changes) | |
Local -> | |
rails generate migration name_of_migration | |
edit db/migrate/<file that was generated> | |
add create_table, add_column, etc | |
# Rake is like make on steroids; it does build type stuff but pretty much | |
# any other scripted task also. | |
# Migrate the changes, i.e. alter table, create table, etc | |
rake db:migrate | |
# test - should not need a server restart | |
git commit, push your changes | |
# Update stage, migrate stage database, deploy | |
# This can also be done in multiple steps (update_code, migrate, deploy) | |
cap deploy:migrations | |
# test stage | |
# Migrate and deploy production | |
RAILS_ENV=production cap deploy:migrations |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment