The amazing world of configuration management software is really well populated these days. You may already have looked at Puppet, Chef or Ansible but today we focus on SaltStack. Simplicity is at its core, without any compromise on speed or scalability. In fact, some users have up to 10,000 minions or more. In this article, we're going to give you a look at what Salt is and how it works.
Salt architecture Salt remote execution is built on top of an event bus, which makes it unique. It uses a server-agent communication model where the server is called the salt master and the agents the salt minions. Salt minions receive commands simultaneously from the master and contain everything required to execute commands locally and report back to salt master. Communication between master and minions happens over a high-performance data pipe that use ZeroMQ or raw TCP, and messages are serialized using MessagePack to enable fast and light network traffic. Salt uses public keys for authentication with the master daemon, then uses faster AES encryption for payload communication. State description is done using YAML and remote execution is possible over a CLI, and programming or extending Salt isn’t a must. Salt is heavily pluggable; each function can be replaced by a plugin implemented as a Python module. For example, you can replace the data store, the file server, authentication mechanism, even the state representation. So when I said state representation is done using YAML, I’m talking about the Salt default, which can be replaced by JSON, Jinja, Wempy, Mako, or Py Objects. But don’t freak out. Salt comes with default options for all these things, which enables you to jumpstart the system and customize it when the need arises.
Terminology It's easy to be overwhelmed by the obscure vocabulary that Salt introduces, so here are the main salt concepts which make it unique.
You’ll find a great overview of all of this on the official docs.
- salt master - sends commands to minions
- salt minions - receives commands from master
- execution modules - ad hoc commands
- grains - static information about minions
- pillar - secure user-defined variables stored on master and assigned to minions (equivalent to data bags in Chef or Hiera in Puppet)
- formulas (states) - representation of a system configuration, a grouping of one or more state files, possibly with pillar data and configuration files or anything else which defines a neat package for a particular application.
- mine - area on the master where results from minion executed commands can be stored, such as the IP address of a backend webserver, which can then be used to configure a load balancer
- top file - matches formulas and pillar data to minions
- runners - modules executed on the master
- returners - components that inject minion data to another system
- renderers - components that run the template to produce the valid state of configuration files. The default renderer uses Jinja2 syntax and outputs YAML files.
- reactor - component that triggers reactions on events
- thorium - a new kind of reactor, which is still experimental.
- beacons - a little piece of code on the minion that listens for events such as server failure or file changes. When it registers on of these events, it informs the master. Reactors are often used to do self healing.
- proxy minions - components that translate Salt Language to device specific instructions in order to bring the device to the desired state using its API, or over SSH.
- salt cloud - command to bootstrap cloud nodes
- salt ssh - command to run commands on systems without minions
Installation Salt is built on top of lots of Python modules. Msgpack, YAML, Jinja2, MarkupSafe, ZeroMQ, Tornado, PyCrypto and M2Crypto are all required. To keep your system clean, easily upgradable and to avoid conflicts, the easiest installation workflow is to use system packages. Salt is operating system specific; in the examples in this article, I’ll be using Ubuntu 16.04 [Xenial Xerus]; for other Operating Systems consult the salt repo page. For simplicity's sake, you can install the master and the minion on a single machine, and that's what we'll be doing here. Later, we'll talk about how you can add additional minions.
- To install the master and the minion, execute the following commands:
$ sudo su
# apt-get update
# apt-get upgrade
# apt-get install curl wget
# echo "deb [arch=amd64] http://apt.tcpcloud.eu/nightly xenial tcp-salt" > /etc/apt/sources.list
# wget -O - http://apt.tcpcloud.eu/public.gpg | sudo apt-key add -
# apt-get clean
# apt-get update
# apt-get install -y salt-master salt-minion reclass
- Finally, create the directory where you’ll store your state files.
# mkdir -p /srv/salt
- You should now have Salt installed on your system, so check to see if everything looks good:
# salt --version You should see a result something like this:
salt 2016.3.4 (Boron)
Alternative installations If you can’t find packages for your distribution, you can rely on Salt Bootstrap, which is an alternative installation method, look below for further details.
Configuration To finish your configuration, you'll need to execute a few more steps:
Repeat this process of installing salt-minion and accepting the keys to add new minions to your environment. Consult the documentation to get more details regarding the configuration of minions or more generally this documentation for all salt configuration options.
- If you have firewalls in the way, make sure you open up both port 4505 (the publish port) and 4506 (the return port) to the Salt master to let the minions talk to it.
- Now you need to configure your Minion to connect to your master. Edit the file /etc/salt/minion.d/minion.conf and Change the following lines as indicated below:
# Set the location of the salt master server. If the master server cannot be
# resolved, then the minion will fail to start.
# If multiple masters are specified in the 'master' setting, the default behavior
# is to always try to connect to them in the order they are listed. If random_master is
# set to True, the order will be randomized instead. This can be helpful in distributing
# Explicitly declare the id for this minion to use, if left commented the id
# will be the hostname as returned by the python call: socket.getfqdn()
# Since salt uses detached ids it is possible to run multiple minions on the
# same machine but with different ids, this can be useful for salt compute
# Append a domain to a hostname in the event that it does not exist. This is
# useful for systems where socket.getfqdn() does not actually result in a
# FQDN (for instance, Solaris).
... As you can see, we're telling the minion where to find the master so it can connect -- in this case, it's just localhost, but if that's not the case for you, you'll want to change it. We've also given this particular minion an id of saltstack-m01; that's a completely arbitrary name, so you can use whatever you want. Just make sure to substitute in the examples!
- Before being able you can play around, you'll need to restart the required Salt services to pick up the changes:
# service salt-minion restart
# service salt-master restart
- Make sure services are also started at boot time:
# systemctl enable salt-master.service
# systemctl enable salt-minion.service
- Before the master can do anything on the minion, the master needs to trust it, so accept the corresponding key of each of your minion as follows:
- Before accepting it, you can validate it looks good. First inspect it:
# salt-key -f saltstack-m01
- Then compare it with the minion key:
# salt-call --local key.finger
- It looks the same, so go ahead and accept it:/span>
salt-key -a saltstack-m01
Remote execution Now that everything's installed and configured, let's make sure it's actually working. The first, most obvious thing we could do with our master/minion infrastructure is to run a command remotely. For example we can test whether the minion is alive by using the test.ping command:
# salt 'saltstack-m01' test.ping
True As you can see here, we're calling salt, and we're feeding it a specific minion, and a command to run on that minion. We could, if we wanted to, send this command to more than one minion. For example, we could send it to all minions:
# salt '*' test.ping
True In this case, we have only one, but if there were more, salt would cycle through all of them giving you the appropriate response. So that should get you started. Next time, we'll look at some of the more complicated things you can do with Salt.