An orchestrator for Chef
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.
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...
motherbrain comes with an
initcommand 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.rbmotherbrain 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:
Notice that there's one task in the help output called
appwhich doesn't map to any of those bulletpoints. Let's take a look at the plugin our
initcommand created:
stack_order do bootstrap 'app::default' endcomponent 'app' do description "Myface application" versioned
group 'default' do recipe 'myface::default' end end
A plugin consists of a few things:
stack_orderdeclares the order to bootstrap component groups
componentcreates a namespace for different parts of your application
descriptionprovides a friendly summary of the component
versioneddenotes that this component is versioned with an environment attribute
groupdeclares a group of nodes
recipedefines 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.jsonand 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_tutorialusing 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
defaultrecipe in our plugin, which ends up adding both the
webserverand
databaserecipes 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' endcomponent '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_tutorialusing 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.
If your cookbook is written using the "service orchestration" pattern, motherbrain can make your plugin even simpler.
component "app" do description "Myface Application" versionedservice "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.stateattribute to 'start' and then do a partial chef run on all nodes that have
myface::appin 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.
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.htmlin your clone of swagger-ui. In the top menu bar, paste in your MB server's address (and port) plus
swagger_doc.jsonand 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.
If you'd like to contribute, please see our contribution guidelines first.