|
|
@@ -0,0 +1,366 @@ |
|
|
### Deploy Rails app to digitalocean with nginx, unicorn, capistrano & postgres |
|
|
|
|
|
Create droplet of your liking (ubuntu 12.10 x32) |
|
|
|
|
|
ssh to root in terminal with your server ip |
|
|
|
|
|
ssh [email protected] |
|
|
|
|
|
Add ssh fingerprint and enter password provided in email |
|
|
|
|
|
Change password |
|
|
|
|
|
passwd |
|
|
|
|
|
Create new user |
|
|
|
|
|
adduser username |
|
|
|
|
|
Set new users privileges |
|
|
|
|
|
visudo |
|
|
|
|
|
Find user privileges section |
|
|
|
|
|
# User privilege specification |
|
|
root ALL=(ALL:ALL) ALL |
|
|
|
|
|
Add your new user privileges under root & cntrl+x then y to save |
|
|
|
|
|
username ALL=(ALL:ALL) ALL |
|
|
|
|
|
Configure SSH |
|
|
|
|
|
nano /etc/ssh/sshd_config |
|
|
|
|
|
Find and change port to one that isn't default(22 is default: choose between 1025 |
|
|
..65536) |
|
|
|
|
|
Port 22 # change this to whatever port you wish to use |
|
|
Protocol 2 |
|
|
PermitRootLogin no |
|
|
|
|
|
Add to bottom of sshd_config file after changing port (cntrl+x then y to save) |
|
|
|
|
|
UseDNS no |
|
|
AllowUsers username |
|
|
|
|
|
Reload ssh |
|
|
|
|
|
reload ssh |
|
|
|
|
|
Don't close root! Open new shell and ssh to vps with new username(remember the port, or you're locked out!) |
|
|
|
|
|
ssh -p 1026 [email protected] |
|
|
|
|
|
Update packages on virtual server |
|
|
|
|
|
sudo apt-get update |
|
|
sudo apt-get install curl |
|
|
|
|
|
install latest stable version of rvm |
|
|
|
|
|
curl -L get.rvm.io | bash -s stable |
|
|
|
|
|
load rvm |
|
|
|
|
|
source ~/.rvm/scripts/rvm |
|
|
|
|
|
install rvm dependencies |
|
|
|
|
|
rvm requirements |
|
|
|
|
|
Install ruby 2.0.0 |
|
|
|
|
|
rvm install 2.0.0 |
|
|
|
|
|
Use 2.0.0 as rvm default |
|
|
|
|
|
rvm use 2.0.0 --default |
|
|
|
|
|
install latest version of rubygems if rvm install didn't |
|
|
|
|
|
rvm rubygems current |
|
|
|
|
|
install rails gem |
|
|
|
|
|
gem install rails --no-ri --no-rdoc |
|
|
|
|
|
Install postgres |
|
|
|
|
|
sudo apt-get install postgresql postgresql-server-dev-9.1 |
|
|
gem install pg -- --with-pg-config=/usr/bin/pg_config |
|
|
|
|
|
Create new postgres user |
|
|
|
|
|
sudo -u postgres psql |
|
|
create user username with password 'password'; |
|
|
alter role username superuser createrole createdb replication; |
|
|
create database projectname_production owner username; |
|
|
|
|
|
Install git-core |
|
|
|
|
|
sudo apt-get install git-core |
|
|
|
|
|
Install bundler |
|
|
|
|
|
gem install bundler |
|
|
|
|
|
setup nginx |
|
|
|
|
|
sudo apt-get install nginx |
|
|
nginx -h |
|
|
cat /etc/init.d/nginx |
|
|
/etc/init.d/nginx -h |
|
|
sudo service nginx start |
|
|
cd /etc/nginx |
|
|
|
|
|
local unicorn setup |
|
|
|
|
|
Add unicorn to the gemfile |
|
|
create unicorn.rb & unicorn_init.sh file |
|
|
chmod +x config/unicorn_init.sh |
|
|
|
|
|
nginx.conf (change projectname and username to match your directory structure!) |
|
|
upstream unicorn { |
|
|
server unix:/tmp/unicorn.projectname.sock fail_timeout=0; |
|
|
} |
|
|
|
|
|
server { |
|
|
listen 80 default deferred; |
|
|
# server_name example.com; |
|
|
root /home/username/apps/projectname/current/public; |
|
|
|
|
|
location ^~ /assets/ { |
|
|
gzip_static on; |
|
|
expires max; |
|
|
add_header Cache-Control 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; |
|
|
} |
|
|
|
|
|
config/unicorn.rb |
|
|
|
|
|
root = "/home/username/apps/projectname/current" |
|
|
working_directory root |
|
|
pid "#{root}/tmp/pids/unicorn.pid" |
|
|
stderr_path "#{root}/log/unicorn.log" |
|
|
stdout_path "#{root}/log/unicorn.log" |
|
|
|
|
|
listen "/tmp/unicorn.projectname.sock" |
|
|
worker_processes 2 |
|
|
timeout 30 |
|
|
|
|
|
# Force the bundler gemfile environment variable to |
|
|
# reference the capistrano "current" symlink |
|
|
before_exec do |_| |
|
|
ENV["BUNDLE_GEMFILE"] = File.join(root, 'Gemfile') |
|
|
end |
|
|
|
|
|
config/unicorn_init.sh |
|
|
|
|
|
#!/bin/sh |
|
|
### BEGIN INIT INFO |
|
|
# Provides: unicorn |
|
|
# Required-Start: $remote_fs $syslog |
|
|
# Required-Stop: $remote_fs $syslog |
|
|
# Default-Start: 2 3 4 5 |
|
|
# Default-Stop: 0 1 6 |
|
|
# Short-Description: Manage unicorn server |
|
|
# Description: Start, stop, restart unicorn server for a specific application. |
|
|
### END INIT INFO |
|
|
set -e |
|
|
|
|
|
# Feel free to change any of the following variables for your app: |
|
|
TIMEOUT=${TIMEOUT-60} |
|
|
APP_ROOT=/home/username/apps/projectname/current |
|
|
PID=$APP_ROOT/tmp/pids/unicorn.pid |
|
|
CMD="cd $APP_ROOT; bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E production" |
|
|
AS_USER=username |
|
|
set -u |
|
|
|
|
|
OLD_PIN="$PID.oldbin" |
|
|
|
|
|
sig () { |
|
|
test -s "$PID" && kill -$1 `cat $PID` |
|
|
} |
|
|
|
|
|
oldsig () { |
|
|
test -s $OLD_PIN && kill -$1 `cat $OLD_PIN` |
|
|
} |
|
|
|
|
|
run () { |
|
|
if [ "$(id -un)" = "$AS_USER" ]; then |
|
|
eval $1 |
|
|
else |
|
|
su -c "$1" - $AS_USER |
|
|
fi |
|
|
} |
|
|
|
|
|
case "$1" in |
|
|
start) |
|
|
sig 0 && echo >&2 "Already running" && exit 0 |
|
|
run "$CMD" |
|
|
;; |
|
|
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" |
|
|
run "$CMD" |
|
|
;; |
|
|
upgrade) |
|
|
if sig USR2 && sleep 2 && sig 0 && oldsig QUIT |
|
|
then |
|
|
n=$TIMEOUT |
|
|
while test -s $OLD_PIN && test $n -ge 0 |
|
|
do |
|
|
printf '.' && sleep 1 && n=$(( $n - 1 )) |
|
|
done |
|
|
echo |
|
|
|
|
|
if test $n -lt 0 && test -s $OLD_PIN |
|
|
then |
|
|
echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds" |
|
|
exit 1 |
|
|
fi |
|
|
exit 0 |
|
|
fi |
|
|
echo >&2 "Couldn't upgrade, starting '$CMD' instead" |
|
|
run "$CMD" |
|
|
;; |
|
|
reopen-logs) |
|
|
sig USR1 |
|
|
;; |
|
|
*) |
|
|
echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>" |
|
|
exit 1 |
|
|
;; |
|
|
esac |
|
|
|
|
|
Add capistrano and rvm capistrano to gemfile |
|
|
|
|
|
gem 'capistrano' |
|
|
gem 'rvm-capistrano' |
|
|
|
|
|
Create capfile & config/deploy.rb files |
|
|
|
|
|
capify . |
|
|
|
|
|
deploy.rb |
|
|
|
|
|
require "bundler/capistrano" |
|
|
require "rvm/capistrano" |
|
|
|
|
|
server "123.123.123.123", :web, :app, :db, primary: true |
|
|
|
|
|
set :application, "projectname" |
|
|
set :user, "username" |
|
|
set :port, 22 |
|
|
set :deploy_to, "/home/#{user}/apps/#{application}" |
|
|
set :deploy_via, :remote_cache |
|
|
set :use_sudo, false |
|
|
|
|
|
set :scm, "git" |
|
|
set :repository, "[email protected]:username/#{application}.git" |
|
|
set :branch, "master" |
|
|
|
|
|
|
|
|
default_run_options[:pty] = true |
|
|
ssh_options[:forward_agent] = true |
|
|
|
|
|
after "deploy", "deploy:cleanup" # keep only the last 5 releases |
|
|
|
|
|
namespace :deploy do |
|
|
%w[start stop restart].each do |command| |
|
|
desc "#{command} unicorn server" |
|
|
task command, roles: :app, except: {no_release: true} do |
|
|
run "/etc/init.d/unicorn_#{application} #{command}" |
|
|
end |
|
|
end |
|
|
|
|
|
task :setup_config, roles: :app do |
|
|
sudo "ln -nfs #{current_path}/config/nginx.conf /etc/nginx/sites-enabled/#{application}" |
|
|
sudo "ln -nfs #{current_path}/config/unicorn_init.sh /etc/init.d/unicorn_#{application}" |
|
|
run "mkdir -p #{shared_path}/config" |
|
|
put File.read("config/database.example.yml"), "#{shared_path}/config/database.yml" |
|
|
puts "Now edit the config files in #{shared_path}." |
|
|
end |
|
|
after "deploy:setup", "deploy:setup_config" |
|
|
|
|
|
task :symlink_config, roles: :app do |
|
|
run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml" |
|
|
end |
|
|
after "deploy:finalize_update", "deploy:symlink_config" |
|
|
|
|
|
desc "Make sure local git is in sync with remote." |
|
|
task :check_revision, roles: :web do |
|
|
unless `git rev-parse HEAD` == `git rev-parse origin/master` |
|
|
puts "WARNING: HEAD is not the same as origin/master" |
|
|
puts "Run `git push` to sync changes." |
|
|
exit |
|
|
end |
|
|
end |
|
|
before "deploy", "deploy:check_revision" |
|
|
end |
|
|
|
|
|
Capfile |
|
|
|
|
|
load 'deploy' |
|
|
load 'deploy/assets' |
|
|
load 'config/deploy' |
|
|
|
|
|
Shake hands with github |
|
|
|
|
|
# follow the steps in this guide if receive permission denied(public key) |
|
|
# https://help.github.com/articles/error-permission-denied-publickey |
|
|
ssh [email protected] |
|
|
|
|
|
Add ssh key to digitalocean |
|
|
|
|
|
cat ~/.ssh/id_rsa.pub | ssh -p 22 [email protected] 'cat >> ~/.ssh/authorized_keys' |
|
|
|
|
|
Create repo and push to github |
|
|
|
|
|
# Add config/database.yml to .gitignore |
|
|
cp config/database.yml config/database.example.yml |
|
|
git init |
|
|
git add . |
|
|
git commit -m "Inital Commit" |
|
|
git remote add origin [email protected]:username/reponame |
|
|
git push origin master |
|
|
|
|
|
deployment |
|
|
|
|
|
cap deploy:setup |
|
|
# edit /home/username/apps/projectname/shared/config/database.yml on server |
|
|
cap deploy:cold |
|
|
|
|
|
after deploy:cold |
|
|
|
|
|
sudo rm /etc/nginx/sites-enabled/default |
|
|
sudo service nginx restart |
|
|
sudo update-rc.d -f unicorn_projectname defaults |
|
|
|
|
|
Resources from Railscasts/digital ocean documentation. |
|
|
For use if puppet or chef is a little over your head. |
|
|
I know you can spawn up a rails app using nginx and unicorn with mysql, but you don't learn much that way! |
|
|
|
|
|
Hopefully I didn't miss any steps, although I'm sure I did. Please leave comments if you run into troubles. |