Minion-Specific Data With etcd

So I've been spending a fair amount of my free time lately learning more about salt, docker, and CoreOS. Salt has been treating very well. I mostly only use it at home, but more opportunities to use it at work are near.

The first I remember really hearing about Docker was when one of my co-workers tried using it for one of his projects. I didn't really spend much time with it until after SaltConf earlier this year (where lots of others brought it up). I'm pretty excited about Docker. I generally go out of my way to make sure my stuff will work fine on various versions of Linux, and Docker makes testing on various platforms insanely easy.

CoreOS is one of my more recent discoveries. I stumbled upon it in the wee hours of the night a few weeks ago, and I've been very curious to see how CoreOS and my fairly limited knowledge of Docker could help me. For those of you who haven't heard of CoreOS yet, it's kinda like a "hypervisor" for Docker containers with some very cool clustering capabilities.

I was able to attend a SaltStack and CoreOS meetup this past week. Most of the CoreOS developers stopped by on their way to GopherCon, and we all got to see a very cool demo of CoreOS in action. It was very cool to see everything in action.

One of the neat projects that the CoreOS folks have given us is called etcd. It is a "highly-available key value store for shared configuration and service discovery." I'm still trying to figure out how to effectively use it, but what I've seen of it is very cool. Automatic leader election, rapid synchronization, built-in dashboard, written in Go.

Anyway, I wanted to be able to use information stored in an etcd cluster in my Salt states. techhat committed some initial support for etcd in Salt about a month ago, but the pillar support was a bit more limited than I had hoped. Last night I submitted a pull request for getting minion-specific information out of etcd. This won't be available for a little while--it's only in the develop branch for now.

To use it, you'll need a couple of things in your Salt master's configuration file (/etc/salt/master). First, you must configure your etcd host and port. In order to use this information in our pillar, we need to configure this using a named profile. We'll call the profile "local_etcd":

local_etcd:
  etcd.host: 127.0.0.1
  etcd.port: 4001

Now we can tell Salt to fetch pillar information from this etcd server as so:

ext_pillar:
  - etcd: local_etcd root=/salt

Be sure to restart your Salt master after making these modifications. Let's add some information to etcd to play with:

etcdctl set salt/foo/bar baz
etcdctl set salt/foo/baz qux

After doing so, you should be able to grab this information from any minion's pillar:

salt "*" pillar.items foo
test1:
    ----------
    foo:
        ----------
        bar:
            baz
        baz:
            qux
test2:
    ----------
    foo:
        ----------
        bar:
            baz
        baz:
            qux

Ok, that's great! We've achived shared information between etcd and our Salt pillar. But what do we do to get minion-specific data out of etcd? Well, we need to start by modifying our master's configuration again. Replace our previous ext_pillar config with the following:

ext_pillar:
  - etcd: local_etcd root=/salt/shared
  - etcd: local_etcd root=/salt/private/%(minion_id)s

Note that the original etcd root changed from /salt to /salt/shared. We do this so we don't inadvertently end up with all minion-specific information from etcd in the shared pillar. Now let's put the sample data back in (again, noting the addition of shared/):

etcdctl set salt/shared/foo/bar baz
etcdctl set salt/shared/foo/baz qux

To override the value of one of these keys for a specific minion, we can use that minion's ID in the key:

etcdctl set salt/private/test2/foo/baz demo

Now when we inspect our pillar, it should look like this:

salt "*" pillar.items foo
test1:
    ----------
    foo:
        ----------
        bar:
            baz
        baz:
            qux
test2:
    ----------
    foo:
        ----------
        bar:
            baz
        baz:
            demo

Notice that the value for foo.baz is qux for minion test1, while its value is demo for test2. Success!