Rails Server Setup + App Deployment Using Moonshine

by Melvin Ram

Moonshine is an amazing new rails plugin by Rails Machine. It configures your server, gets it ready for your app by installing the necessary gems & native packages and deploys your app. Built on top of Capistrano & Puppet, it really takes most of the pain out of deployment.

In this blog post, I’m going to walk you through how to use Moonshine to go from a fresh installation of Ubuntu server 8.10 to being live with your app. So let’s get going:

NOTE: Support this blog by subscribing to the RSS Feed. It’s free.

Step 1. Initial server setup

You’ll need an Ubuntu box to get started. After the basic install you’ll need to run a few commands to get the box ready.

Log into your server via ssh & change your root password

ssh root@123.123.123.123
passwd

Change 123.123.123.123 with the IP of your server everywhere you see it.

Add a new user called rails and give it a password

adduser rails

Give it sudo permissions

visudo

And add this below “root ALL=(ALL) ALL

rails   ALL=(ALL) ALL

On your local machine:

ls ~/.ssh/

If it shows you id_rsa.pub, skip this next step (which generates the ssh):

ssh-keygen

Next, we’re going to copy your public key to your server so you can securely connect to it without having to enter your password. Again on your local machine, run:

scp ~/.ssh/id_rsa.pub rails@123.123.123.123:/home/rails/

Now on your server, run this:

mkdir /home/rails/.ssh
mv /home/rails/id_rsa.pub /home/rails/.ssh/authorized_keys
chown -R rails:rails /home/rails/.ssh
chmod 700 /home/rails/.ssh
chmod 600 /home/rails/.ssh/authorized_keys

It’s a good practice to change the SSH port for your machine so that’s what we’re going to do next:

nano /etc/ssh/sshd_config

Change “Port 22” to “Port 2222” or a different number

Save your file and close it (Ctrl+X, then Y and finally press enter-key)

Okay, now it’s time to setup IP Table Rules.

nano /etc/iptables.test.rules

In this file, enter your iptable rules. Here’s a decent I got fromhttp://articles.slicehost.com/assets/2007/9/4/iptables.txt. IPTables are like your firewall so all info is provided without warrantee. It’s your responsibility to use it wisely.

*filter

#  Allows all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
-A INPUT -i lo -j ACCEPT
-A INPUT -i ! lo -d 127.0.0.0/8 -j REJECT

#  Accepts all established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

#  Allows all outbound traffic
#  You can modify this to only allow certain traffic
-A OUTPUT -j ACCEPT

# Allows HTTP and HTTPS connections from anywhere (the normal ports for websites)
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT

#  Allows SSH connections
#
# THE -dport NUMBER IS THE SAME ONE YOU SET UP IN THE SSHD_CONFIG FILE
#
-A INPUT -p tcp -m state --state NEW --dport 2222 -j ACCEPT

# Allow ping
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT

# log iptables denied calls
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7

# Reject all other inbound - default deny unless explicitly allowed policy
-A INPUT -j REJECT
-A FORWARD -j REJECT

COMMIT

Save that file and close it. Then run this:

iptables-restore < /etc/iptables.test.rules
iptables-save > /etc/iptables.up.rules

Now to make this info stay when you restart the server, run:

nano /etc/network/interfaces

And enter a line below the “iface lo inet loopback” line, as shown below:

  ...
  auto lo
  iface lo inet loopback
  pre-up iptables-restore < /etc/iptables.up.rules

  # The primary network interface
  ...

Save & close that file and then run:

  /etc/init.d/ssh reload

Now it’s time to do some stuff on your local machine to make it easy to connect with your server.

On your local machine, open ~/.ssh/config file with textmate or your fav text editor:

mate ~/.ssh/config

and add your info using the template below:

Host blackbox
Hostname 123.123.123.123
User rails
Port 2222

Don’t forget to change your port number & IP as well as pick a name for your server that makes sense.

Save & close that file. Now you should be able to connect to your server by just typing:

ssh blackbox

Okay, we’ve got one final thing we need to get your initial server setup. That’s getting your server access to your github account (which you don’t need but most people have it so you should probably get one, even if it’s just to contribute to open source stuff.)

Once you’ve ssh’d into your server using the above command, generate your ssh key similar to how to you did it on your local machine:

ssh-keygen

It will ask you if you want to give it a password. I usually don’t give it one and I don’t know if that’s a good/bad thing. In anycase, when you’re done, you want to output the public key it generates by running this:

cat /home/rails/.ssh/id_rsa.pub

Copy the key that is outputted and enter it into your github account which you can usually do at:https://github.com/account

Alright, you’ve now completed step 1, which was getting the initial server ready.

Step 2 – Adding moonshine to your project & configuring it.

In this step, you’ll add the moonshine plugin to your project & configure it properly so it knows what you want it to do on your server.

On your local machine, go to your rails project directory and add the plugin:

ruby script/plugin install git://github.com/railsmachine/moonshine.git

Add all the gems your project needs to your environment.rb file.

Next, run the moonshine generator to create the necessary files:

ruby script/generate moonshine

This should make an output like this:

  After the Moonshine generator finishes don't forget to:

  - Edit config/moonshine.yml
  Use this file to manage configuration related to deploying and running the app:
  domain name, git repos, package dependencies for gems, and more.

  - Edit app/manifests/application_manifest.rb
  Use this to manage the configuration of everything else on the server:
  define the server 'stack', cron jobs, mail aliases, configuration files

        create  app/manifests
        create  app/manifests/templates
        create  app/manifests/application_manifest.rb
        exists  app/manifests/templates
        create  app/manifests/templates/README
        exists  config
        create  config/moonshine.yml
        create  config/gems.yml

As the output suggests, the next thing you should do is edit config/moonshine.yml

Edit config/moonshine.yml

First give your app a name (such as your_app_name), pick where the files of your app will reside (by setting deploy_to) & tell moonshine where to grab your application from (by setting repository)

  :application: your_app_name
  :deploy_to: /srv/your_app_name
  :repository: git@github.com:username/your_app_name.git

Next, if your app has directories in the public directory that need to stay persistent, uncomment this:

:app_symlinks:
  - uploads

If you don’t, the files will be removed every time you deploy since it gets the files from your git repo and it won’t be there.

You’ll also probably want to uncomment the local_config section, which will upload your local copy of /config/database.yml to the server, while bypassing your git repo.

:local_config:
  - config/database.yml

You probably want to leave the next section (:shared_children:) alone. It basically keeps everything the same.

Now save your file and from your terminal/command-line, run this command to generate a /config/gems.yml file, which will contain a list of gems that your app needs (based on what you specified in config/environment.rb)

rake moonshine:gems

Like the comments say, if your gems depend on native packages, you need to specify it next with :apt_gems. For example:

:apt_gems:
  :paperclip:
    - imagemagick

Next, change Passenger & MySQL settings to suit your app & hardware. For example, on a 256MB ram box, you might lower the max_pool_size to 2 or increase it to 8 for 1GB ram server.

Finally, if your app uses SSL, you’ll need to set your ssl properties with something like this:

:ssl:
  :ip:
  :certificate_file:
  :certificate_key_file:
  :certificate_chain_file:
  :vhost_extra:
  :only:

Set the :only: to true if your entire site should be ssl protected. If you are self-signing, can you can use:self_signed: true like this:

:ssl:
  :self_signed: true

When all is done, you might have a file that looks like this:

  :ruby: ree
  :application: yourapp
  :user: rails
  :group: rails
  :ssl:
    :self_signed: true
  :deploy_to: /srv/yourapp
  :domain: yourapp.com
  :repository: git@github.com:username/reponame.git
  :app_symlinks:
    - assets
  :local_config:
    - config/database.yml
  :shared_children:
  - system
  - log
  - pids
  - config
  :apt_gems:
    :paperclip:
      - imagemagick
  :passenger:
    :max_pool_size: 3
    :use_global_queue: true
  :mysql:
    :innodb_buffer_pool_size: 128M

Edit app/manifests/application_manifest.rb

In the application_manifest.rb, you’ll specify a few things:

  1. Which recipe(s) you want to use
  2. Packages you haven’t already specified
  3. Any rake commands you want run the first time

Recipes

The file that is generated uses the default_stack recipe. What this means is it will install Apache, Passenger, MySQL, Rails, NTP, Cron & Postfix. Checkout vendor/plugins/moonshine/lib/moonshine/manifest/rails.rb to see the actual method. For many apps, the default will work fine.

TODO: Add instructions for what to have in place of the default_stack if I want to use sqlite as my database (if my app is really small).

Additional Packages

If your app needs to have a package installed that hasn’t been specified yet, this is the place to add it. For example, if your app uses BackgroundRB, you’d specify it here.

TODO: Add example of how backgroundrb might be installed.

Run rake commands

You might need to run a rake command after your app has been deployed to initial things. This might be the place to add it.

TODO: Add example. Something I might want to do is do a mysql dump before I move forward… but this is just hypothetical.

Capify your project

Once you’ve got your app configured, capify your app by running the following code while inside your rails app directory:

capify .

Now open config/deploy.rb and replace it’s content with this:

server "blackbox", :app, :web, :db, :primary => true

Replace blackbox with the name you give your server in your ~/.ssh/config file.Next, store the files to your repo and push it to your git repo:

git add . && git commit -am "added moonshine" && git push

Step 3 – Deploy

Once all the configuration is done, you’ll see all your hard work pay off.

First thing you’ll do is setup the server. From your rails app root, run this:

cap deploy:setup

Once that is done, run this:

cap deploy

At this point, you should be done and your app should be live.

Step 3.5 – Add SSL

If your site uses SSL, this next section will get you setup with it. After you’ve deployed your app with the self-signed method as described above, ssh into your server and generate a new certificate request:

ssh blackbox
sudo openssl req -new > mynewsite.csr

It will ask you to fill in a bunch of info that will need to match the info you provide your SSL provider (such as GoDaddy). The key one that you’ll want to pay attention to is Common Name. That needs to be your domain name (without the https://). For my app, i didn’t include the www and I’m not sure if that makes a difference. Next lets move these to a better location:

mkdir /home/rails/certs
mv mynewsite.csr /home/rails/certs/mynewsite.csr
mv privkey.pem /home/rails/certs/privkey.pem

Once done, output your certificate request by doing this:

cd /home/rails/certs/
cat mynewsite.csr

Copy that and enter it when your SSL provider asks for it.

Once your SSL provider approves your SSL, they’ll provide you with two files. One will be the certificate file and the second will be the certificate chain file. For godaddy, they provide a zip file that contains two files: yourdomain.com.crt & gd_bundle.crt. Save these two files in a directory called certs on your local machine. Change into that directory and copy the files to your server by running this on your local machine:

scp * blackbox:/home/rails/certs/

This should copy the files to /home/rails/certs/ on your server.

The final step is to update config/moonshine.yml, commit it to the git repo and deploy again. Open up config/moonshine.yml and replace:

  :ssl:
    :self_signed: true

with

:ssl:
  :ip: 123.123.123.123
  :certificate_file: /home/rails/certs/yourdomain.com.crt
  :certificate_key_file: /home/rails/certs/privkey.pem
  :certificate_chain_file: /home/rails/certs/gd_bundle.crt

Save & close this file. Next update your git repo.

git add config/moonshine.yml
git commit -m "Updated moonshine config file with SSL info"
git push

Now it’s time to deploy but we have one more tiny step. When you were creating the certificate request, it asked you to enter a password in. Apache will ask for that password every single time it wants to restart and moonshine won’t be able to enter this in for you. So we’re going to remove that password from the private key. (for more info) So log into your server & remove it by doing:

ssh blackbox
cd certs
cp privkey.pem privkey.pem.bak
openssl rsa -in privkey.pem.bak -out privkey.pem

This will ask you to enter your password that you entered while generating the certificate request. When you’re done, you’re ready to deploy again:

cap deploy

That’s it! You should be good to go now. And if you’re in the market for managed hosting, particularly for your Rails apps, definitely check out RailsMachine, creators of moonshine!

PS: I originally wrote this guide as part of the moonshine wiki. Feel free to head out there and add to it. It’ll help everyone involved.

{ 3 trackbacks }

Rockstar mentality is a GOOD thing for Rails
May 4, 2009 at 2:01 am
Ennuyer.net » Blog Archive » I am way behind on my rails link blogging. Link dump and reboot.
May 9, 2009 at 10:42 am
Flow » Blog Archive » Daily Digest for May 16th - The zeitgeist daily
May 16, 2009 at 9:37 am

{ 13 comments… read them below or add one }

Lee Jones April 29, 2009 at 6:33 pm

Nice to see an article on moonshine out there. We’ve been trying it out recently. Any thoughts on automating step 1? We do something like this on every server. It would be nice to just input the temp password from slicehost and run a script to set up a new user with ssh access, change the ssh port, set up firewall etc.

Melvin Ram April 29, 2009 at 7:14 pm

Yes, Jesse is working on that. I think he might have an update sometime in the near future. I don’t have an exact ETA but I’d go ahead and watch the project on github for updates. I’ll also update this page once it’s done as well.

Nicholas Henry April 29, 2009 at 11:17 pm

Rails Machine have written some plugins for managing iptables and ssh. Checkout: http://github.com/railsmachine/moonshine_iptabl... and http://github.com/railsmachine/moonshine_ssh/tr.... I haven't had a chance to check them out yet, but hopefully provides most of what your looking for.

melvinram April 29, 2009 at 11:22 pm

Thanks! I'll check it out.

Pau May 10, 2009 at 12:45 am

I'm trying to use moonshine to deploy my rails application but I have a problm that I'm not able to solve.

When I try to run “cap deploy:setup” it outputs an error saying “Please specify the name of your application, set :application, 'foo'”

I don't understand why I'm having that error because the application name is set on the config/moonshine.yml file.

Thank's in advance!

melvinram May 10, 2009 at 10:10 am

Can you add your moonshine.yml file to pastie.org and add a link here. I've never gotten that error so I'll just have to look to see your moonshine.yml file.

Also, add an issue to http://github.com/railsmachine/moonshine/issues so someone else with the same issue will be able to see it and view all the responses to it in the future. Definitely add the link to the pastie in it as well.

Pau May 10, 2009 at 12:34 pm

Thanks for the quick reply :)

http://pastie.org/473615

melvinram May 10, 2009 at 6:07 pm

Your files look pretty close to mine. I don't see any issues with them. What does your config/deploy.rb file look like? Did you remove all the stuff in it and just have one line that looks something like this:

<pre> server “blackbox”, :app, :web, :db, :primary => true </pre>

Pau May 10, 2009 at 7:44 pm

is just [server "masylum", :app, :web, :db, :primary => true] where masylum is the equivalent to blackbox on my .ssh/config

Maybe I need to install/upgrade any gem?

Pau May 10, 2009 at 10:21 pm

I did all the steps again and right now it works.

The only difference is that I upgraded my gems and this “Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }” appeared to my Capfile.

:)

dmix May 19, 2009 at 10:16 pm

Thanks for the well written & designed tutorial.

I had to upgrade from Ubuntu 8.04 to 8.10, Moonshine:Manifest required it.

Other then that it went surprisingly smoothly. I love how it does everything – it even creates the mysql user/db based on database.yml.

Melvin Ram May 20, 2009 at 10:45 am

Hi Dan, I’m glad you found it useful. And yes, Ubuntu 8.10 is the minimum requirement for this.

Ryan November 12, 2009 at 6:30 pm

Hey Melvin, can you still put custom cap functions in the the deploy file?
like:
desc “Clear Action Cache”
task :clear_action_cache do
run ” cd #{release_path} && rake tmp:cache:clear”
end

or one to reset/rebuild sphinx?

Leave a Comment