@udit and I grow out our neckbeards a bit in this demo, where we’ll setup a rails app, push it to github and deploy to a linode server with Capistrano.
I think @scottmuc would be proud of this one.
TL;DR
I can’t say often enough how fortunate I am to work with so many awesome people @ThoughtWorks.
@udit has been playing with a linode server and was cool enough to drop some knowledge on me about standing up and deploying to a server.
We spent a few evenings pairing at Coupa Cafe in Palo Alto and this tutorial was the outcome.
If you see a 500 error in your browser,
then it may mean that the index.html isn’t present.
You’ll be deleting that symlink later anyway.
Postgres - Install and setup
terminal
1234567891011121314
root@li349-144:~# add-apt-repository ppa:pitti/postgresql
root@li349-144:~# apt-get update
root@li349-144:~# apt-get install postgresql libpq-dev
root@li349-144:~# sudo -u postgres psql
postgres=# \password
Enter new password: # Press enter to skip
Enter it again: # Press enter to skip
postgres=# create user capteste with password 'captestepasswordthing';
CREATE ROLE
postgres=# create database capteste_production owner capteste;
CREATE DATABASE
Postfix for mail.
terminal
12345
root@li349-144:~# apt-get install postfix
# You'll be prompted with menu.
# Select 'Internet Site'
# Leave the default system mail name on the next screen.
We want to stop using root as soon as possible (it can be dangerous if you’re like me and aren’t fully dev-ops savvy).
So we’re going to create a new user and set the permissions of that user.
## Add a user for deployment. We'll call it deployer
root@localhost:~# adduser deployer
Adding user `deployer' ...
Adding new group `deployer' (1000) ...
Adding new user `deployer' (1000) with group `deployer' ...
Creating home directory `/home/deployer' ...
Copying files from `/etc/skel' ...
# Push enter to move through these next questions.
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for deployer
Enter the new value, or press ENTER for the default
Full Name []:
Room Number []:
Work Phone []:
Home Phone []:
Other []:
Is the information correct? [Y/n]
# Add a trusted usergroup and name it admin.
root@localhost:~# addgroup admin
Adding group `admin' (GID 1001) ...
Done.
# Add the deployer to the admin group
root@localhost:~# adduser deployer admin
Adding user `deployer' to group `admin' ...
Adding user deployer to group admin
Done.
Set permissions for deployer
terminal
1
root@localhost:~# visudo
scroll down and add deployer permissions to be the same as root
terminal
123
# User privilege specification
root ALL=(ALL:ALL) ALL
deployer ALL=(ALL:ALL) ALL
Save and exit the file
control + x
Security Permissions
Change the ssh port.
Do not allow root login from the outside.
terminal
1
sudo vim /etc/ssh/sshd_config
terminal vim
12345
# Change this from Yes to no.
PermitRootLogin no
# Change your port access. Our Example uses 3030
Port 3030
Exit sshd_config :wp
Reload your settings for them to take effect.
terminal
123456
root@localhost:~# /etc/init.d/ssh reload
Rather than invoking init scripts through /etc/init.d, use the service(8)
utility, e.g. service ssh reload
Since the script you are attempting to invoke has been converted to an
Upstart job, you may also use the reload(8) utility, e.g. reload ssh
Logout of your box
123
root@localhost:~# exit
logout
Connection to 96.126.100.112 closed.
Login as Deployer
terminal
123
$ ssh deployer@96.126.100.112
deployer@96.126.100.112's password:
# Enter your password
Install RVM and Ruby
This next command includes all the stuff that is needed for rvm (at the time of this post).
You can generate this list manually by running ‘rvm requirements’.
123456789
deployer@localhost:~$ sudo /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
... a whole bunch of stuff ...
# When you come to this
Do you want to continue [Y/n]?
# Push Y and move on.
... more stuff ...
Install Rvm
terminal
12345678
deployer@localhost:~$ curl -L https://get.rvm.io | bash -s stable --ruby
... some stuff ...
# I got this red error msg.
# Udit assured me that this isn't a huge deal.
# ..even if it looks odd, just move on.
Please note that `rvm 2 ...` was removed, try `2` instead. ( see: 'rvm usage' )
Open your .bashrc and add rvm.
terminal
1
deployer@localhost:~$ vi ~/.bashrc
Paste this into your bash profile. This loads ruby from rvm.
Check to see that rvm and Ruby installed. If they were, you’ll see their location.
terminal
123456
deployer@localhost:~$ which rvm
/home/deployer/.rvm/bin/rvm
# In my case ruby didn't install. Hence no response.
deployer@localhost:~$ which ruby
deployer@localhost:~$
Ruby usually gets installed rvm, but mine happened to fail. So I’ll install Ruby 1.9.3.
terminal
123456789
deployer@localhost:~$ rvm install 1.9.3
... a whole bunch of stuff ...
# When it finishes. Check your ruby location and version.
deployer@localhost:~$ which ruby
/home/deployer/.rvm/rubies/ruby-1.9.3-p194/bin/ruby
deployer@localhost:~$ ruby -v
ruby 1.9.3p194 (2012-04-20 revision 35410) [i686-linux]
Setup Github and Key
You can choose to setup your keys in a few different ways, this is just one method.
Connect to github through your box and accept github as a trusted source.
terminal
123456
deployer@localhost:~$ ssh git@github.com
The authenticity of host 'github.com (207.97.227.239)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,207.97.227.239' (RSA) to the list of known hosts.
Permission denied (publickey).
Setup your Key
terminal
123456789101112131415161718192021222324
deployer@localhost:~$ cd ~/.ssh
deployer@localhost:~/.ssh$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/deployer/.ssh/id_rsa): # Push enter
Enter passphrase (empty for no passphrase): # Push enter
Enter same passphrase again: # Push enter
Your identification has been saved in /home/deployer/.ssh/id_rsa.
Your public key has been saved in /home/deployer/.ssh/id_rsa.pub.
The key fingerprint is:
# ... some stuff will be here with a little graphic ...
+-------------------+
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
+-------------------+
Check to see if your pub key is there.
terminal
12
deployer@localhost:~/.ssh$ ls
id_rsa id_rsa.pub known_hosts
Print your pub key, then copy it ( I used my mouse to highlight and copy it ).
terminal
12345
deployer@localhost:~/.ssh$ cat id_rsa.pub
ssh-rsa AxWHOLExBUNCHxOFxRANDOMxLETTERSxANDxNUMBERSxsANDxTHENxSOMExMOREfdsa
# Exit the directory
deployer@localhost:~/.ssh$ cd
In your App, uncomment the asset-pipeline option in /Capfile.
/Capfile
12345
load 'deploy'
# Uncomment if you are using Rails' asset pipeline
load 'deploy/assets' #uncomment this line.
Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
load 'config/deploy' # remove this line to skip loading any of the default tasks
In your App, modify the new file, /config/deploy.rb.
In the railscast, Ryan makes a fresh app and adds /config/database.yml to the .gitignore before his first commit.
Capteste wasn’t in the same situation, so we had change my approach a bit.
Note: This approach may not be the most efficient/effective, but it worked.
As directed in the last line above, we need to modify our configuation.
Fortunately we only have 1-file to change.
Log back into your box as deployer and modify database.yml.
terminal
12345
$ ssh deployer@96.126.100.112
deployer@96.126.100.112's password: # enter your password
# Open database.yml on your box
deployer@li333-112: vim ~/apps/capteste/shared/config/database.yml
Modify database.yml to include only production.
terminal vim database.yml
12345678
production:adapter:postgresqlencoding:unicodedatabase:capteste_productionpool:5host:localhost# ADD this line.username:captestepassword:captestepasswordthing
Note: If you had to add databse.yml later on, the way we did in this app,
then you’ll have to do this everytime you run $ cap deploy:setup.
Deploy:cold and :check
terminal
123456789101112131415
$ cap deploy:cold
* executing `deploy:cold'
* executing `deploy:update'
** transaction: start
* executing `deploy:update_code'
updating the cached checkout on all servers
executing locally: "git ls-remote git@github.com:matthewcopeland/capteste.git master"
command finished in 3801ms
* executing "if [ -d /home/deployer/apps/capteste/shared/cached-copy ]; then cd /home/deployer/apps/capteste/shared/cached-copy && git fetch -q origin && git fetch --tags -q origin && git reset -q --hard 43fd9aea3d669a988e91213e58cb33efa2b0cd3a && git clean -q -d -x -f; else git clone -q git@github.com:matthewcopeland/capteste.git /home/deployer/apps/capteste/shared/cached-copy && cd /home/deployer/apps/capteste/shared/cached-copy && git checkout -q -b deploy 43fd9aea3d669a988e91213e58cb33efa2b0cd3a; fi"
servers: ["96.126.100.112"]
Password: # Enter the deployer password
# ... lots of stuff .....
command finished in 2252ms
You may have errors. Try this to check dependencies.
terminal
123456789101112131415161718192021222324
$ cap deploy:check
* executing `deploy:check'
* executing "test -d /home/deployer/apps/capteste/releases"
servers: ["96.126.100.112"]
Password: # Enter your Password
[96.126.100.112] executing command
command finished in 2661ms
* executing "test -w /home/deployer/apps/capteste"
servers: ["96.126.100.112"]
[96.126.100.112] executing command
command finished in 204ms
* executing "test -w /home/deployer/apps/capteste/releases"
servers: ["96.126.100.112"]
[96.126.100.112] executing command
command finished in 647ms
* executing "which git"
servers: ["96.126.100.112"]
[96.126.100.112] executing command
command finished in 334ms
* executing "test -w /home/deployer/apps/capteste/shared"
servers: ["96.126.100.112"]
[96.126.100.112] executing command
command finished in 411ms
You appear to have all necessary dependencies installed
Remove old Nginx files
Nginx comes has a welcome screen (or a 500error in some cases when index.html isn’t present).
You’ll need to login to your box as deployer, delete these files and make sure the app restarts correctly.
terminal
12345678910111213141516171819202122
deployer@li333-112:~$ sudo rm /etc/nginx/sites-enabled/default
[sudo] password for deployer:
deployer@li333-112:~$ sudo service nginx restart
Restarting nginx: nginx.
# This helps to ensure that unicorn restarts correctly.
# Be sure to replace 'capteste' with your app's name.
deployer@li333-112:~$ sudo update-rc.d unicorn_capteste defaults
[sudo] password for deployer:
Adding system startup for /etc/init.d/unicorn_capteste ...
/etc/rc0.d/K20unicorn_capteste -> ../init.d/unicorn_capteste
/etc/rc1.d/K20unicorn_capteste -> ../init.d/unicorn_capteste
/etc/rc6.d/K20unicorn_capteste -> ../init.d/unicorn_capteste
/etc/rc2.d/S20unicorn_capteste -> ../init.d/unicorn_capteste
/etc/rc3.d/S20unicorn_capteste -> ../init.d/unicorn_capteste
/etc/rc4.d/S20unicorn_capteste -> ../init.d/unicorn_capteste
/etc/rc5.d/S20unicorn_capteste -> ../init.d/unicorn_capteste
deployer@li333-112:~$ sudo service nginx restart
Restarting nginx: nginx.
deployer@li333-112:~$ exit
logout
Connection to 96.126.100.112 closed.
Check your site in a browser.
Update your site and Deploy
Make a change to your app.
Push the change to github.
terminal
123456
$ git add .
$ git commit -m "Making a change."
$ git push
# Deploy again
$ cap deploy
Another big thanks again to @udit for the awesome knowledge sharing and pairing.
Another thanks to devops gurus like @scottmuc for making it look easy.
Oh yeah, and the kind peeps who wrote the articles listed at the top.