I decided to start my migration by migrating my Rails application, timetrack. This migration would have been a lot easier except that this particular application is using 2-year old tools and still needs a lot of tweaking.
The main dependencies are MySQL and Ruby 1.8.7.
User Account and Ruby
I started by creating a new user account, just for the timetrack application.
useradd timetrack
I installed rvm, then tried to install ruby 1.8.7, from the ‘timetrack’ user account.
rvm install 1.8.7
The rvm script gave me a list of Debian dependencies to install first.
/usr/bin/apt-get install build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake libtool bison subversion pkg-config
After that, ruby 1.8.7 installed cleanly. Although I did get a warning that just compiling it on modern Linux required two extra patches and that I should strongly consider installing ruby 1.9, for better security.
Install MySQL
Installing MySQL was easy.
apt-get install mysql-server libmysqlclient-dev
I tuned it, using the parameters from Linode.
I created a new database, specifically for this application, then created a new database specific MySQL user account.
mysqladmin create timetrack
CREATE USER 'timetrack'@'localhost' IDENTIFIED BY 'xxxx';
GRANT ALL ON timetrack.* TO 'timetrack'@'localhost';
Finally, I could load the database backup, from Litho.
mysql -u timetrack -p timetrack < jmartin_desertflood_timetrack_2012-08-19_01h03m.Sunday.sql
Deploy the Application
Capistrano is quite powerful and should have made this the easiest part of the setup. But I never did build a really good deployment recipe, so it was only a partial help. I started by updating the config/deploy.rb file, to point to my new server and user account. Then I had to setup my 'timetrack' user account, to be able to access GitHub. Without it, I was getting errors on the cap deploy
step.
-
Create a new SSH key
ssh-keygen
-
Copy the contents of '.ssh/id_rsa.pub' to my Github keys. Then do an initial checkout, to make sure that SSH is okay with Github’s server.
git clone git@github.com:jmartindf/timetrack.git
-
Setup the required directories.
mkdir -p /home/timetrack/apps/timetrack/releases mkdir shared/pids mkdir shared/log
-
Deploy the app
cap deploy
-
Update local configurations
vim config/database.yml vim config/unicorn.conf.rb
-
Install required gem dependencies
bundle install
-
Start the app, to see if it works.
unicorn -E production -D -o 127.0.0.1 -c /home/timetrack/apps/timetrack/current/config/unicorn.conf.rb
And, it works. Amazing.
Start at boot
It’s all well and good to start it manually, but I really need it to start at boot. After some Googling, I found an init.d script for unicorn, that I could use as a basis for my own init.d script.
After tweaking it a bit, this is what it looks like. The key points are that it uses rvm and bundler, to get the dependencies right. (As mentioned before, I’m using some old gems and an old version of ruby.) It also uses the chdir and chuid flags, to start-stop-daemon
, to get everything running with the right permissions in the right folder. I’ve tested it and it does all work.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | #! /bin/sh
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Timetrack server"
NAME=timetrack
RVM=/home/timetrack/.rvm/bin/rvm
RVM_EXEC_ARGS="1.8.7 exec"
RAILS_DIR=/home/timetrack/apps/timetrack/current
DAEMON=$RVM
DAEMON_ARGS="$RVM_EXEC_ARGS bundle exec unicorn -c $RAILS_DIR/config/unicorn.conf.rb -E production -D"
PIDFILE=$RAILS_DIR/tmp/pids/timetrack.pid
USER=timetrack
SCRIPTNAME=/etc/init.d/$NAME
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
# unicorn needs some environment vars
. /etc/environment
# Create /var/run dir if missing
# if [ ! -d /var/run/unicorn ]; then
# mkdir -m 0755 /var/run/unicorn
# chown $USER /var/run/unicorn
# fi
symlink_resque() {
ln -sfT `$RVM $RVM_EXEC_ARGS which resque | tail -1`/server/public $RAILS_DIR/public/resque
}
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
symlink_resque
start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $USER --exec $DAEMON --test $RAILS_DIR --chdir $RAILS_DIR > /dev/null \
|| return 1
start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $USER --exec $DAEMON --chdir $RAILS_DIR -- \
$DAEMON_ARGS \
|| return 2
# Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend
# on this one. As a last resort, sleep for some time.
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Ensure the pidfile is cleared
rm -f $PIDFILE
return "$RETVAL"
}
#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
symlink_resque
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE
return 0
}
#
# Function that sends a SIGUSR2 to the daemon/service
#
do_graceful_reload() {
symlink_resque
start-stop-daemon --stop --signal USR2 --quiet --pidfile $PIDFILE
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
reload)
log_daemon_msg "Reloading $DESC" "$NAME"
do_reload
log_end_msg $?
;;
graceful-reload)
log_daemon_msg "Gracefully reloading $DESC" "$NAME"
do_graceful_reload
log_end_msg $?
;;
restart)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart|graceful-reload|reload}" >&2
exit 3
;;
esac
:
|
I activated the timetrack
init.d script, for the default run levels.
update-rc.d timetrack defaults
Nginx
My app is running, but it’s not on a port that the outside world can access. That’s where nginx comes in. I like nginx as a lightweight web server and in this case, I can easily use it to proxy traffic back and forth between my app. I just needed to create a very simple configuration file, for nginix.
vim /etc/nginx/sites-available/timetrack.desertflood.com
cd /etc/nginx/sites-enabled
ln -s /etc/nginx/sites-available/timetrack.desertflood.com
/etc/init.d/nginx start
Here’s what the file looks like.
upstream timetrack {
server 127.0.0.1:10100;
}
server {
listen 80;
server_name timetrack2.desertflood.com;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://timetrack;
}
}
And, just like that, I’ve migrated my first site from Joyent to Linode.