A Comparison: Chef vs Otter
Chef and Otter are both infrastructure automation products, and if you only read the marketing bullets, they’d probably sound identical. But they’re quite different products, and this article compares and contrasts the products and the philosophies behind them.
Moreover, there’s nothing wrong with having and using both – in fact, many organizations have Puppet, Chef, and Otter, because they all serve different needs for different groups.
While we are not Chef experts, we’ve done a ton of research on Chef when building Otter and, more recently, for this article. Please let us know if something is inaccurate.
Already familiar with Chef? The terminology comparison may help you get a quicker understanding of Otter.
Quick Aside: Chef vs Puppet
Unlike Puppet, which was built as a Linux Configuration Manager, Chef was built as a “systems integration framework” to assist infrastructure automation specialists (originally, the consultants who built Chef itself) configure Linux-based servers and components.
If that sounds like six of one, half a dozen of the other, that’s because ultimately, Puppet and Chef are different approaches to the same, Linux configuration problem, and ultimately achieve the same results:
- In Puppet, you write a manifest file using PuppetScript that declares a desired state of configuration.
- In Chef, you write a recipe using Ruby that describes the steps required to configure a server.
We mention all this because the reasons that people choose Puppet over Chef, or choose Chef over Puppet have been documented for years, and these were key considerations when we designed Otter.
The ones that Otter directly addresses are as follows:
- Puppet’s “model” approach is too limiting compared to Chef’s “code” approach
- Puppet’s apparent random application of resources is unintuitive
- Chef requires a proficiency in Ruby to do anything but trivial configuration tasks
- Chef’s architecture and components are much more complex to understand and manage
- Chef’s complexity can lead to large code bases and complicated environments
- Chef has a very steep learning curve and requires training
In addition, Otter addresses challenges that both Puppet and Chef share, including Windows and Orchestration.
Linux Configuration Management for Ruby Developers
Chef was built by experts, for experts; as such, ease of use was never a design consideration.
Requiring expertise is not a problem when your organization is fortunate enough to hire only experts, like many of the early and outspoken Chef adopters. However, most organizations simply can’t dedicate the resources to build and maintain the required, in-house expertise.
The “maintenance” is key — even if the initial “DevOps” team is successful in rolling out a complex system, the learning curve of that system will even more complex than a greenfield installation of Chef.
Otter was built with ease of use in mind, and actively encourages its users to build simple plans that others can maintain.
The Windows Challenge
Although Chef and the community have built a lot of Windows modules, the tool was never designed with Windows configuration management in mind, and Windows support often feels second-class.
Windows and Linux are very different; not just technologically, but philosophically. Although Windows has been lately offering administrators better command-line, scripting, and detachable components, they will always be two very different operating systems.
Otter was built with from the start with first-class Windows support (including tight integration with PowerShell), and was built using Windows-friendly technologies (.NET), and doesn’t need to go through layers of Ruby for core functionality.
The Orchestration Challenge
Although you can create recipes using Ruby, doing orchestration – that is, getting specific servers to do specific things in a specific order, like deploying large, multi-tier applications – is extremely challenging and awkward with Chef.
This is because servers managed by Chef are responsible for configuring themselves; essentially, they routinely ask the central Chef repository for the latest Ruby scripts, and then run them as needed. This model means that the central server cannot tell which servers should do things in which order.
While Chef’s new Delivery Product attempts to make this easier, it’s still an afterthought that utilizes the same, self-configuration model.
Otter’s execution engine approach does not have this challenge by its very nature: the central Otter server runs commands remotely on each remote server, and can explicitly control the order in which things happen on servers.
Lack of Visibility
Chef’s learning curve is a barrier to implementing the collaboration aspects of DevOps; with knowledge concentrated to just a few, expert engineers, those specialists become a bottleneck in getting information on how infrastructure is modeled, and how servers are configured.
Otter was built to visualize complex configurations and let anyone dig into the details, while at the same time giving administrators the flexibility to restrict sensitive configuration.
Change Management and Compliance
DevOps isn’t only about faster cycles, it’s about responsible cycles, which sometimes means process.
Chef’s developer-heavy design means that change management is handled in developer-friendly way (i.e. through a Git repository), and compliance isn’t even an afterthought — it’s a separate product.
For some developer-heavy organizations, the idea of automatically applying configuration changes to dozens of production servers by syncing a Git repository is a dream. For governance and compliance personnel in enterprise organizations, that’s a nightmare.
Compliance is built in to Otter. Servers can be configured to automatically remediate drift or simply report on it and give you the option to schedule a job to automatically remediate the drift. These are all first class concepts, as opposed to afterthoughts.
Chef’s Ruby Libraries vs Otter Plans
Ruby is a general-purpose programming language, which makes it difficult to compare and contrast to OtterScript, which is a language designed specifically for the purpose of configuration management and infrastructure automation.
There’s no question that a general-purpose programming language offers near-infinite flexibility, and that’s precisely why Otter has such tight PowerShell/scripting integration: when the high-level execution engine isn’t the right tool for the job, just PSExec what you need.
Simplicity aside, OtterScript does have distinct advantages over Ruby, and most other general-purpose programming languages. Of course, with the visual code editor, you won’t have to spend any time learning a specific syntax.
Implicit, Quote, and Swim Strings
Data is a first-class concept in Otter, and to help contain values without worrying about escaping, OtterScript offers several syntaxes for string literals:
- Implicit – no quotes at all
- Quoted – single- or double-quoted
- Swim – multi-line strings while choosing your own delimiter
And OtterScript uses the grave apostrophe (`) as the escape character instead of a backslash, which avoids so many headaches when trying to represent file paths.
Seamless Remote Execution & The Context Stack
OtterScript does not need to run on remote servers in order to orchestrate them. The execution engine already has the concept of remote execution built-in, which means running a PowerShell script on all configured servers is as simple as this:
foreach server in @AllServers { PSExec >> Literal Powershell goes here >>; }
The above statement used a context-setting statement above; although this isn’t something you’d do in a configuration plan, in an orchestration plan, the notion of the context stack makes writing complex orchestration plans simple.
# Register all of our servers foreach server in @AllServers { if $ServerName() != KEYREGSV1 { set $ServerToRegister = $ServerName(); set $ServerKey = $FileContents(C:\hdars\key.fil); set $RegKey = ""; # Register on Key Server # Change the context to the Key Server, get the registration key, # and save it to the key for server KEYREGSV1 { for directory c:\keyutils\v18\bin\working { Create-File tmpkey.fil ( Contents: $ServerKey, Overwrite: true); PSExec >> $RegKey = Register-External-Cert | --server $ServerToRegister | --filename tmpkey.fil >>; } } if $RegKey == "" { Log-Warning Unable to get key for $ServerToRegister; } else { PSExec >> Set-Registration-Key | --source KEYREGSV1.hdars.local | --key $RegKey >>; } } }
Built-in Execution Status
Executions (i.e. plan/OtterScript runs) can end with one of three statuses: normal, warning, or error.
By default, the execution status works exactly like you’d expect – an uncaught error will terminate with an error status, and operations that log warnings will yield a warn status. But you can also explicitly set the execution status at any point during a plan execution with a simple statement (warn, force warn, or error).
Unlike a “throw” statement, this does not impact control flow (unless you explicitly test for it in an If/Else block), only reporting.
Logging as a first-class concept
Logging is such a first-class concept in Otter that it’s not only an in-built statement, but the blocks you create actually directly translate to a collapsible logging scope.
Just add a comment at the top of a block, and that will become the collapsible scope.
Asynchronous Execution (Parallelism)
Multi-threading and parallelism is never easy in any language, but we took inspiration from one of the simplest and most powerful patterns: async/await.
# Execute Database Nodes foreach server in @ServersInRole(database-nodes) { # these will take an eternity to run, so run in background with async { PSExec >> some really long-running script >>; } } # Execute Web Nodes foreach server in @ServersInRole(web-nodes) { PSExec >> some reasonably short script >>; } # Hopefully database servers are caught up by now... # but wait just in case await;
You can even set and await on a specific async token, as to allow for multiple groups. And best of all, error handling “just works” with this pattern – just put a try/catch around the await.
Retry and Timeout as a first-class concept
In most general-purpose programming languages, a retry requires nesting a loop, a try/catch, and a couple if/else statements. With otter, it’s a single statement:
with retry=3 { PSExec >> Some interfrastic script that fails sometimes >>; }
You can also safely timeout operations in Otter using the same type of statement:
with timeout=100 { PSExec >> Some bad script that could take a lifetime >>; }
Unlike Ruby’s Terminate blocks (aka Ruby’s Most Dangerous API), Otter operations rely on cancellation tokens to gracefully terminate without putting the entire runtime in an unknown state.
Chef Terminology vs Otter Terminology
If you’re already familiar with Chef, this may help explain similar concepts.
Chef | Otter | Notes |
---|---|---|
Attributes | Configuration | |
Chef Client | Agent | Same, except Otter agents are used only for Windows servers |
Chef Console | Web Application | See Architecture & Components |
Chef Repository | Otter database | See Architecture & Components |
Chef Server | Otter server | See Architecture & Components |
Chef Solo | Romp | This is essentially a stand-alone version of the execution engine that runs plans on disk |
Cookbooks | Raft or Extension | Highly reusable components are generally created as extensions, whereas bundled configuration is created as a raft |
Data bags | Configuration Variables | |
Knife | – | Otter does not currently have a stand-alone CLI, as everything is done from the web app, but with demand we can add one |
Node | Server | |
Ohai | – | this is effectively a collection run of a Configuration Plan |
Recipe | Configuration Plan | |
Resource | Operation or Asset | |
Role | Server Role |