Skip to content

RiotGamesMinions/motherbrain

Repository files navigation

motherbrain

Build Status

motherbrain is an orchestration framework for Chef. In the same way that you would use Chef's Knife command to create a single node, you can use motherbrain to create and control an entire application environment.

Other Documentation

Requirements

  • Ruby 2.0.0+
  • Chef Server 10 or 11, or Hosted Chef

Installation

Install motherbrain via RubyGems:

gem install motherbrain

We don't recommend including motherbrain in your Gemfile.

Before using motherbrain, you'll need to create a configuration file with mb configure:

Enter a Chef API URL:
Enter a Chef API Client:
Enter the path to the client's Chef API Key:
Enter a SSH user:
Enter a SSH password:
Config written to: '~/.mb/config.json'

You can verify that motherbrain is installed correctly and pointing to a Chef server by running mb plugin list --remote:

$ mb plugin list --remote

** listing installed and remote plugins...

Getting Started

motherbrain comes with an init command to help you get started quickly. Let's pretend we have an app called MyFace, our hot new social network. We'll be using the myface cookbook for this tutorial:

$ git clone https://github.com/reset/myface-cookbook
$ cd myface
myface$

We'll generate a new plugin for the cookbook we're developing:

myface$ mb plugin init
      create  bootstrap.json
      create  motherbrain.rb

motherbrain plugin created.

Take a look at motherbrain.rb and bootstrap.json,
and then bootstrap with:

  mb myface bootstrap bootstrap.json

To see all available commands, run:

  mb myface help

myface$

That command created a plugin for us, as well as told us about some commands we can run. Plugins live within cookbooks in a file named motherbrain.rb. Notice that each command starts with the name of our plugin. Once we're done developing our plugin and we upload it to our Chef server, we can run plugins from any cookbook on our Chef server.

Lets take a look at all of the commands we can run on a plugin:

myface$ mb myface
using myface (1.1.8)

Tasks:
  mb myface app [COMMAND]       # Myface application
  mb myface bootstrap MANIFEST  # Bootstrap a manifest of node groups
  mb myface help [COMMAND]      # Describe subcommands or one specific subcommand
  mb myface nodes               # List all nodes grouped by Component and Group
  mb myface provision MANIFEST  # Create a cluster of nodes and add them to a Chef environment
  mb myface upgrade             # Upgrade an environment to the specified versions

There are a few things plugins can do:

  • Bootstrap existing nodes and configure an environment
  • Provision nodes from a compute provider, such as Amazon EC2, Vagrant, or Eucalyptus
  • List all nodes in an environment, and what they're used for
  • Configure/upgrade an environment with cookbook versions, environment attributes, and then run Chef on all affected nodes
  • Run plugin commands, which abstract setting environment attributes and running Chef on the nodes

Notice that there's one task in the help output called app which doesn't map to any of those bulletpoints. Let's take a look at the plugin our init command created:

stack_order do
  bootstrap 'app::default'
end

component 'app' do
  description "Myface application"
  versioned

  group 'default' do
    recipe 'myface::default'
  end
end

A plugin consists of a few things:

  • stack_order declares the order to bootstrap component groups
  • component creates a namespace for different parts of your application
    • description provides a friendly summary of the component
    • versioned denotes that this component is versioned with an environment attribute
    • group declares a group of nodes
      • recipe defines how we identify nodes in this group

This plugin is enough to get our app running on a single node. Let's try it out. Edit bootstrap.json and fill in a hostname to bootstrap:

{
  "nodes": [
    {
      "groups": ["app::default"],
        "hosts": ["box1"]
    }
  ]
}

And then we'll bootstrap our plugin to that node:

myface-cookbook$ knife environment create motherbrain_tutorial
myface-cookbook$ mb myface bootstrap bootstrap.json -e motherbrain_tutorial

using myface (0.4.1)

  [bootstrap] searching for environment
  [bootstrap] Locking chef_environment:motherbrain_tutorial
  [bootstrap] performing bootstrap on group(s): ["app::default"]
  [bootstrap] Unlocking chef_environment:motherbrain_tutorial
  [bootstrap] Success

That's it! But that's not much different from using knife bootstrap, and it took a lot more work.

myface-cookbook$ ls recipes/
database.rb     default.rb      webserver.rb
myface-cookbook$ cat recipes/default.rb
include_recipe "myface::webserver"
include_recipe "myface::database"

We're currently using the default recipe in our plugin, which ends up adding both the webserver and database recipes to our node's runlist. Let's change the automatically-generated plugin to better fit the architecture for our application:

stack_order do
  bootstrap 'app::db'
  bootstrap 'app::web'
end

component 'app' do
  description "Myface application"
  versioned

  group 'web' do
    recipe 'myface::webserver'
  end

  group 'db' do
    recipe 'myface::database'
  end
end

Note that we're bootstrapping the nodes in order, and since our web server depends on a database, we'll want to bootstrap the database first.

And then change our bootstrap manifest to bootstrap 2 nodes instead of 1:

{
  "nodes": [
    {
      "groups": ["app::web"],
      "hosts": ["box1"]
    },
    {
      "groups": ["app::db"],
      "hosts": ["box2"]
    }
  ]
}

And then run the bootstrap again:

myface-cookbook$ mb myface bootstrap bootstrap.json -e motherbrain_tutorial

using myface (0.4.1)

  [bootstrap] searching for environment
  [bootstrap] Locking chef_environment:motherbrain_tutorial
  [bootstrap] performing bootstrap on group(s): ["app::db"]
  [bootstrap] performing bootstrap on group(s): ["app::web"]
  [bootstrap] Unlocking chef_environment:motherbrain_tutorial
  [bootstrap] Success

That's it! We now have our application deployed to 2 nodes.

Service Commands

If your cookbook is written using the "service orchestration" pattern, motherbrain can make your plugin even simpler.

component "app" do
  description "Myface Application"
  versioned

  service "app" do
    service_group "app"
    service_recipe "myface::service"
    service_attribute "myface.app.state"
  end

  group "app" do
    recipe "myface::app"
  end
end

To start the service, you would run mb myface service app.app start. This would set the myface.app.state attribute to 'start' and then do a partial chef run on all nodes that have myface::app in their default runlist, using an override runlist of myface::app_service. The same command could be used to stop, restart, or change to any other state that your service recipe supports.

For each service resource in your cookbook, you should use a single attribute to define the desired state (stopped, started, restarted). The default that motherbrain will look for is component_name.service_name.state (although you can use anything you like).

This resource should also be in a dedicated recipe that only works with your services.

Swagger

When running as a server, MB mounts various enpoinds using the Grape library. For convenience, the tool Swagger has also been integrated into MB's REST API.

First, clone the Swagger UI project.

Next, start your MB server. The only requirement here is a properly defined configuration file:

bundle exec bin/mbsrv

Next, open up dist/index.html in your clone of swagger-ui. In the top menu bar, paste in your MB server's address (and port) plus swagger_doc.json and click Explore.

For a local server, running on the default port, the URL would look like "http://localhost:26100/swagger_doc.json".

That's all! You should now be able to explore the REST API of MB using Swagger.

Authors

If you'd like to contribute, please see our contribution guidelines first.