Commercial Products
Apr '09
21

Rudy, not your grandparent's deployment tool

posted by delano

NOTE: This post is historically accurate but out of date. See: Rudy 0.9 Released.

Rudy is a command-line development and deployment tool. It's been in private beta for about two months and today marks the day of the first public release [1]. The project is still in alpha status and many features have yet to be developed but it's already useful for exploring the features of Amazon EC2. In this post, I'll introduce Rudy's core features and demonstrate how these features can help build and manage environments in EC2.

A quick look

It's useful to know what working with Rudy looks like before getting into the details:

$ rudy startup            # Launch the default machine group, stage-app
$ rudy -e dev startup     # Launch the dev-app machine group
$ rudy machines           # List all instances running in stage-app
$ rudy -u root ssh        # SSH to root@m-us-east-1b-stage-app-01
$ rudy ssh uptime         # Execute the uptime command on app stage-app machines
$ rudy shutdown           # Terminate stage-app instances

See also:

$ rudy -h 
$ rudy show-commands
$ rudy COMMAND -h

Machine Groups

Rudy helps you build and manage machines in EC2 by organizing them into groups of environments and roles. These are called machine groups. You can run multiple machines with the same role. These are called positions. Rudy also supports running machine groups across availability zones. When you put all this together, you have a unique name for every machine. The default machine is:

         zone     env  role
          v        v    v   
    m-us-east-1b-stage-app-01
    ^                       ^
 "machine"                position

stage is the default environment and app is the default role, but these can be changed too (see Defaults below). These machine groups are configured using a Ruby DSL (domain-specific language) that looks like this:

machines do
  # Define two identical environments
  environment :dev, :stage do
    # Default properties for all roles
    ami "ami-235fba4a"
    positions 1

<span class="n">role</span> <span class="ss">:app</span> <span class="k">do</span>
  <span class="c1"># Create two identical instances in the :app role. </span>
  <span class="n">positions</span> <span class="mi">2</span>
  <span class="c1"># Elastic IP addresses to be associated on startup</span>
  <span class="n">addresses</span> <span class="s2">"11.22.33.44"</span><span class="p">,</span> <span class="s2">"55.66.77.88"</span>
<span class="k">end</span>

<span class="n">role</span> <span class="ss">:analyzer</span> <span class="k">do</span>
  <span class="c1"># The :analyzer machines can have their own AMI</span>
  <span class="n">ami</span> <span class="s2">"ami-********"</span>
  <span class="c1"># Define EBS volumes for this machine</span>
  <span class="n">disks</span> <span class="k">do</span>
    <span class="n">path</span> <span class="s2">"/data/disk1"</span> <span class="k">do</span>
      <span class="n">size</span> <span class="mi">4</span>
      <span class="n">device</span> <span class="s2">"/dev/sdr"</span>
    <span class="k">end</span>
  <span class="k">end</span>
<span class="k">end</span>

end
end

The configuration above describes the properties for 4 machine groups: dev-app, dev-analyzer, stage-app, and stage-analyzer. Since we've defined two app machines per environment, that makes 6 machines:

  • m-us-east-1b-dev-app-01
  • m-us-east-1b-dev-app-02
  • m-us-east-1b-dev-analyzer-01
  • m-us-east-1b-stage-app-01
  • m-us-east-1b-stage-app-02
  • m-us-east-1b-stage-analyzer-01

Routines

The machines configuration describes the whats or the "physical" characteristics of the environments. The routines configuration describes the repeatable processes.

routines do
  # Define routines for the stage environment
  environment :stage do

<span class="n">role</span> <span class="ss">:analyzer</span> <span class="k">do</span>
  <span class="c1"># Tell Rudy what to do when you run "rudy startup"</span>
  <span class="n">startup</span> <span class="k">do</span>
    <span class="c1"># Execute "uname" on the local machine</span>
    <span class="n">before_local</span> <span class="no">Rudy</span><span class="p">.</span><span class="nf">sysinfo</span><span class="p">.</span><span class="nf">user</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="ss">:git</span><span class="p">,</span> <span class="ss">:commit</span><span class="p">,</span> <span class="s1">'a'</span><span class="p">,</span> <span class="s1">'m'</span><span class="p">,</span> <span class="s2">"Rudy Startup"</span><span class="p">]</span>
    <span class="n">before_local</span> <span class="no">Rudy</span><span class="p">.</span><span class="nf">sysinfo</span><span class="p">.</span><span class="nf">user</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="ss">:git</span><span class="p">,</span> <span class="ss">:push</span><span class="p">]</span>
    <span class="n">disks</span> <span class="k">do</span>
      <span class="c1"># Create an EBS volume, attach it, format it, and mount it</span>
      <span class="n">create</span> <span class="s2">"/rudy/disk1"</span>
    <span class="k">end</span>
    <span class="n">after</span> <span class="ss">:username</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1">'/a/release/script'</span><span class="p">]</span>
    <span class="n">after</span> <span class="ss">:root</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="s1">'/etc/init.d/nginx'</span><span class="p">,</span> <span class="s1">'restart'</span><span class="p">]</span>
  <span class="k">end</span>
  <span class="n">shutdown</span> <span class="k">do</span>
    <span class="n">before</span> <span class="ss">:root</span> <span class="o">=&gt;</span> <span class="s1">'/another/custom/script'</span>
    <span class="n">before</span> <span class="ss">:root</span> <span class="o">=&gt;</span> <span class="p">[</span><span class="ss">:mysqladmin</span><span class="p">,</span> <span class="ss">:u</span><span class="p">,</span> <span class="s1">'root'</span><span class="p">,</span> <span class="s1">'shutdown'</span><span class="p">]</span>
    <span class="n">disks</span> <span class="k">do</span>
      <span class="c1"># Umount the volume, detach it, delete it</span>
      <span class="n">destroy</span> <span class="s2">"/rudy/disk1"</span>
    <span class="k">end</span>
    <span class="c1"># Rudy.sysinfo.user returns the process' current user (ENV['user'])</span>
    <span class="n">after_local</span> <span class="no">Rudy</span><span class="p">.</span><span class="nf">sysinfo</span><span class="p">.</span><span class="nf">user</span> <span class="o">=&gt;</span> <span class="s2">"date"</span>
  <span class="k">end</span>    
<span class="k">end</span>

end end

This routines configuration describes the processes for the stage-analyzer group.

Running a Routine

$ rudy -r analyzer startup
Starting stage-app

---  BEFORE SCRIPTS (local)  ------------------------------
Connecting to localhost
...
Starting m-us-east-1b-stage-app-01
Associating 11.22.33.44 to i-11111111
Starting m-us-east-1b-stage-app-02
Associating 55.66.77.88 to i-22222222
....

---  DISK ROUTINES  ---------------------------------------
...

---  AFTER SCRIPTS  ---------------------------------------
...

The following machines are now available:
m-us-east-1b-stage-app-01  ec2-11-22-33-44.compute-1.amazonaws.com
m-us-east-1b-stage-app-02  ec2-55-66-77-88.compute-1.amazonaws.com

Defaults

You may be wondering where the us-east-1b value comes from. This is an EC2 availability zone and it's one of several default values that Rudy assumes in order to function straight "out of the box". The default values can be changed too:

defaults do
  region :"us-east-1"
  zone :"us-east-1b"
  environment :stage
  role :app
  position "01"
  user ENV['USER'].to_sym
end

Conclusion / More to come

As I mentioned, the current release of Rudy (0.6) is in alpha status so it's not ready for production. There's a lot of work to do, including a lot of documentation to write, but hopefully you can already see the value of incorporating Rudy into your development process. For now, the README contains installation instructions and the RDocs are somewhat helpful too!

Notes

  • [1] Rudy has been available on GitHub since the project began, but this release is the first one with documentation.

I'm Delano Mandelbaum, the founder of Solutious Inc. I've worked for companies large and small and now I'm putting everything I've learned into building great tools. I recently launched a monitoring service called Stella.

You can also find me on:

-       Delano (@solutious.com)

Solutious is a software company based in Montréal. We build testing and development tools that are both powerful and pleasant to use. All of our software is on GitHub.

This is our blog about performance, development, and getting stuff done.

-       Solutious