Thursday, 9 April 2015

Configuration management: Salt vs Puppet

I've spent some time experimenting with puppet and I have come to the conclusion that I don't much like it.

There are several reasons why this is the case but the main ones are the module system that it uses and the way it abstracts away the configuration details on the client.

 Note: I gave up on puppet after only a couple of days after reaching a point where there was an error somewhere which was impossible to understand because it had no logical behaviour. Therefore I don't profess to be an expert on puppet to any degree at all so any comments from here on are merely my opinion and will definitely include liberal amounts of my misunderstanding of how Puppet works. That is another one of the problems I had with Puppet. The errors it produces are cryptic and there are so many things going on in the box of smoke and mirrors that you can't see the workings of that it is extremely hard to comprehend and debug things. Especially as a n00b. 

From what I have managed to partially understand, Puppet ships with a bunch of pre-installed modules for common applications (such as apache) so you don't need to find and download the appropriate module.

This is all good of course until you need to do something that is not included by default. In that case you need to get on the Internet and find the module you need and install it. Sometimes there will be an "official" puppetlabs module and sometimes you need to trawl through many community provided modules of varying degrees of quality to find one that looks like it might do what you want. This in itself is not terrible but it does involve a lot more mucking about than I think should be required.

Once you have all your modules in order you need to start configuring stuff and this is where the frustration really kicks in.

Puppet uses a bunch of "manifest" files that must be created for each service, host, or groups of hosts. Manifests can include other manifests and everything must be carefully declared in the right places or else things just don't work, and most of the time you will get an obtuse error message that doesn't help a great deal in tracking things down.

Circular dependencies can be common without due care being taken.

For example, the straw that broke the proverbial camels back for me and turned me away from Puppet was where I had two similar hosts with two identical manifests that despite that would produce different results when trying to update them.

One would fail with "Error: Failed to apply catalog: Could not find dependency File[/etc/postfix/main.cf]" while the other one worked fine. This was strange to me because

    a) the file it was complaining about existed on both hosts and

    b) nowhere was I even trying to manage postfix.

I even went to the length of creating two completely empty manifests for the two hosts that still produced the same dissimilar results.

Somewhere in the "box of smoke and mirrors" something was going on but I could not for the life of me figure it out. I even posted a question on serverfault (I rarely need to go to that extreme) but the kind folks there were unable to help either.

So I just gave up because life is too short for that crap.

There are other reasons that I dislike puppet.

A major one is how it abstracts out configuration details for the hosts it manages.

To explain what I mean I will return to the example of apache from before. Now, I am fairly familiar with apache, and I know my way around the apache config files reasonably well. Consider this snippet:

ServerName webserver01.tuxnetworks.com

<VirtualHost first.example.com:80>
    ServerAdmin brett@mydomain.com
    DocumentRoot /var/www/first
</VirtualHost>

That is a pretty simple config for a global server name and a virtual host in apache.

Now, let's look at how you configure that in puppet.

class { 'apache':
default_mods   => true,
}

apache:servername { 'webserver01.tuxnetworks.com' }

apache::vhost { 'first.example.com':
  port    => '80',
  docroot => '/var/www/first',
}

Now this is just a really simple example, but you will note the syntax that you are required to learn and use is totally different to how the target application would normally be configured. Things get even more excitingly confusing when you are trying to configure a service that you are not familiar with. In such cases you are not only required to first figure out what the native settings you need to put in the config file should be but then you must also figure out the correct Puppet syntax for getting there.

I had to do that just yesterday to add another domain to a haproxy server.

You will also note that the ServerAdmin variable has not been set in the puppet example because the 5 minutes that I spent searching the Internet on how to do it came up with nothing. I'm sure with more time I could find the answer (and there is a good chance just adding "serveradmin => brett@mydomain.com'" in there would work) but the point is that I really don't want to have to deal with this level of abstraction for something that I already know how to do.

So, given that you already think that the concept of configuration management systems is great but you are not prepared to devote the rest of your life wrestling with puppet then what do you do?

 Well, another popular system is Chef, but after doing some research I concluded that Chef had almost as many idiocyncracies as Puppet so I have decided to explore the possibilities of saltstack.

So far I've managed to do most of what I want to do with salt and it has been far easier to get my head around it than I found with puppet, although there are still some conceptual things to understand as well as some gotchas that can stall things early on.

Come back later for a post on getting things up and running if you are interested.

The first good thing is that Salt does not require modules.

Another good thing is that Salt does not abstract away configuration details for most use cases.

Note: Salt has something called "pillars" that I have no understanding of. From what I have read these can get a bit more complex but for my purposes (installing packages & pushing configuration files) I have not yet needed to use those. If my requirements get more complex then I may have to visit them but the point is that as a new salt user you don't need to use them) 
Anyway, on a salt server, you would simply copy a working apache.conf over to a directory on the salt-master and the minions will just use it as is. Simples!

But what if you need to set some of those details on a per host basis I hear you ask? For example maybe I want to set the ServerName directive to be whatever the hostname of the server is?

In that case all I need to do is modify it like so:

ServerName {{servername}}

<VirtualHost first.example.com:80>
    ServerAdmin brett@mydomain.com
    DocumentRoot /var/www/first
</VirtualHost>


What I have done there is simply replaced the server name string to become a variable place holder that can be used by the jinja templating engine that salt uses to insert the proper data whenever the minion is updated.

I don't need to learn a whole new syntax because I can just use the standard Apache conf layout that I am already familiar with and make certain parts of it "variable" as required.

Salt really is in my humble opinion much simpler to get up and running (but that may just be because I'm a bit thick of course). Also no offence intended to the folks who love Puppet, I know it is widely used out there and I still have an inherited puppet system here at work. The bottom line is that OSS is all about choice after all.

That is not to say it is perfect however. The documentation leaves a lot to be desired for instance, which is a pretty big problem in itself.

Anyway, stay tuned for a quick start guide to SaltStack at a later date.