Saltstackfordevops
Saltstackfordevops
Aymen El Amri
This book is for sale at http://leanpub.com/saltstackfordevops
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Every Book Has A Story, This Story Has A Book . . . . . . . . . . . . . . . . . . . . . . 2
To Whom Is This Book Addressed ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Conventions Used In This Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
How To Properly Enjoy This Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
How To Contribute To This Book ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
About The Author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Configuration Management And Data Center Automation . . . . . . . . . . . . . . . . . 6
DevOps Tooling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Advanced Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Installation For Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Practical Installation Case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Troubleshooting salt-minion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Troubleshooting Ports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Using salt-call . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
A Note About Redhat Enterprise Linux 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Basic Concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
YAML : The Human Readable Serialization Language . . . . . . . . . . . . . . . . . . . . 35
Jinja: A Templating Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
The Master: salt-master . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
The Minion: salt-minion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Expanding Salt with salt-syndic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Key Management with salt-key . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Salt Runners Interface: salt-run . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
The Data Aspect of Salt: Grains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Targeting grains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
Extending The Minion Features With saltutil . . . . . . . . . . . . . . . . . . . . . . . . 60
Formating Outputs with outputter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Describing Configurations and States . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
The Top File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Using Master’s Pillars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
Remote Execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Event-Based Execution With Reactors . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
From States To Formulas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
SaltStack And Vagrant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
SaltStack And Docker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Salt Cloud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Afterword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
CONTENTS 1
Dedication
I dedicate this to my mother and father who were always there for me.
Preface
I tried SaltStack first time when I saw my team taking more than 3 days to configure hosted platforms
at each deployment. After setting it up, the same procedure was taking less than 1/2 hour.
Through this book, it’s your turn to discover SaltStack, I will be your guide.
I wish you a pleasant reading.
• If you are a fan of configuration management, automation and the DevOps culture
• if you are a system administrator working on DevOps or SysOps
• If you are a developer seeking to join the new movement
This book is addressed to you. Configuration management are one the most used tools in DevOps
environments.
• And if you are new to configuration management softwares, this book is also addressed to
beginners.
Preface 4
• This book uses italic font for technical words such as librairies, modules, languages names.
The goal is to get your attention when you are reading and help you identify them.
• You will find two icons, I have tried to be as simple as possible so I have chosen not to use too
many symbols, you will only find:
time and do your own on-line research. Learning can be serial so understanding a topic require the
understanding of an other one.
Through this book, you will learn how to install configure and use SaltStack. Just before finishing
the book, you will go through a chapter where good examples of a practical use cases are explained.
Through this chapter, try to showcase your acquired understanding, and no, it will not hurt to go
back to previous chapters if you are unsure or in doubt.
Finally, try to be pragmatic and have an open mind if you encounter a problem. The resolution
begins by asking the right questions.
1 \ ^__^
2 \ (--)\_______
3 (__)\ )\/\
4 ||----w |
5 || ||
Configuration management tools like Salt are useful to the use cases quoted below and can do more
than this.
In fact, with the adoption of agile development methods, the process of development; test and
deployment of a software component has accelerated, therefore methods of management have
become faster, more automated and more adapted to changes.
DevOps Evolution
Even if many specialists consider provisioning, change management and automation a business
matter, not an IT one but to make this happen, some special technical skills are required. That’s why
new positions, teams or rather culture in the IT industry have emerged: DevOps.
The illustration below (taken from Wikipedia) shows the essence of the DevOps philosophy.
DevOps as the intersection of development (software engineering), technology operations and quality assurance
(QA)
Automation is important to the success of critical IT processes that are part of the life cycle
of a product, including provision, change management, release management, patch management,
compliance and security. Therefore, having the right technical skills is important to any “lazy but
pragmatic SysAdmin”.
Introduction 8
This book will help you to learn one of the most known IT automation configuration management
and infrastructure automation/orchestration tools.
DevOps Tooling
Currently, several FOSS and proprietary automation and configuration management tools exists.
Choosing one of these tools is based on several criteria.
Choice Criteria
• Performance : Among memory consumption, speed of execution and adaptation to increas-
ingly complexes architectures, several performance criteria could help you decide choosing
the right tool.
• License : You may choose between FOSS and proprietary software. Most of the existing
software are Open Source. It remains to be seen what FOSS license you should choose: GPL,
BSD, Apache, MIT..etc
• Programming Language : A such tool is coded using a programming language, but it does not
mean that users will manage and automate operations and servers using the same language.
For example, SaltStack is written in Python but its users use Jinja and YAML ..etc
Most of tools are written in Python, Ruby, Java or Golang, but one can also find perl, C and C ++
based tools.
Thanks to Wikipedia5 and its contributors for the this comparison concerning the portability of the
following tools.
• Documentation, Support and Latest Stable Release: Keep in mind that the quantity and
the quality of the official documentation, forums, groups, and paid support differs from a
tool to another. A good thing to do is to see the date of the latest stable release, some tools
are no more updated which can cause security risks, lack of support, bugs and problems of
integrations with other tools.
Popular tools
Among the popular tools we can find : Ansible, CFEngine, Puppet/Chef and SaltStack.
5
http://en.wikipedia.org/wiki/Comparison_of_open-source_configuration_management_software
Introduction 10
Ansible : Combines multi-node deployment and ad-hoc task execution. It manages nodes with SSH
and requires Python (2.4 or later). It uses JSON and YAML modules and states as descriptions. It is
built on Python but its modules can be written in any language. Ansible6 is used by Spotify, Twitter,
NASA and Evernote.
Puppet : Puppet7 is based on a custom declarative language to describe the system configuration,
it uses a distributed client-server paradigm, and a library for configuration tasks. Puppet requires
the installation of a master server and client agents on every system that needs to be managed. It is
used by Vmware, Cisco, Paypal and SalesForce.
SaltStack : Salt8 is what the next chapters of this book will detail. It is used by Rackspace,
Photobucket, Nasa, LinkedIn, Hulu, HP Cloud Services, Harvard University, CloudFlare ..etc
6
http://www.ansible.com/home
7
http://puppetlabs.com/
8
http://saltstack.com/
 Getting Started With SaltStack
1 \ ^__^
2 \ (--)\_______
3 (__)\ )\/\
4 ||----w |
5 || ||
Presentation
Salt is an Open Source project, you can read and modify its source code under the Apache license.
Its source code is available on github9 .
SALSTACK Inc. is the company behind Salt, it was founded by Thomas Hatch, the original creator
of SaltStack.
Salt is used by Apple inc, Rackspace, Photobucket, NASA, LinkedIn, Hulu, HP Cloud Services, Cloud
Flare and other know companies.
SaltStack Logo
Salt fundamentally improves the way SysAdmins, integrators and DevOps configure and manage
all aspects of a modern data center infrastructure.
9
https://github.com/saltstack/salt
 Getting Started With SaltStack 12
It provides a different approach to some existing alternatives such as speed and adaptation to the
size of a cloud. Several recognized businesses use SaltStack to orchestrate and control their cloud
servers and infrastructure and automate the “DevOps Toolchain”.
It is built on a platform running relatively fast while enabeling remote-controlling distributed infras-
tructures, code and data. A layer of security is established while having two-way communication
between the different components of the platform (masters, minions ..etc).
The following chapters are conceived for begginers and experienced system administrators, DevOps
professionals and developers seeking to manage and configure multiple servers/applications and
software platforms easily.
The infrastructure to manage can be in-premise virtual machines, cloud (Amazon EC2 instances,
Rackspace ..etc), containers (e.g. Docker) or bare-metal machines as well as hosted applications and
platforms that rely on configuration files.
All you need is a root access, a good understanding of the environment to manage and some basic
knowledge (command line, basic commands, linux ..etc).
Even if it is possible to use a web access to manage Salt but the use of the command line is always
more adapted to our needs for several reasons such as speed and efficiency. If you are familiar with
CLIs, understanding Salt commands and its syntax will be easier.
Salt is portable and works with these systems:
According to the official website, other systems and distributions will be compatible in the future.
If you want to stay informed just follow the development branches10 .
In the following sections, we will be most of the time using Linux. If you are using Macos, there is
no real differences for the installation but if you are using Windows, I recommend using Ubuntu on
Windows, this is optional if you would like to keep using Windows shell.
Windows users, please follow this guide11 to install Ubuntu and Bash.
You can find some diffrences in the installation of Salt according to your system: Windows, FreeBSD
or Solaris..etc. Overall, principles and usage are the same.
You can use Salt installed on an operating system to manage other systems (A Linux to manage a
Solaris or a BSD to manage a Windows … etc.).
The installation part of this book will cover Redhat and Debian.
Be sure to check the documentation (docs.saltstack.com) for the installation and the specific use with
your particular operating system.
A brief summary
SaltStack is based on some special components:
A master can manage configurations or execute remote commands on one or more minions. These
operations are based on SLS files, and these files are calling Salt modules, grains and/or pillars.
Salt could be used either from the command line (Salt CLI) or in executable scripts (Salt API).
The various components of SaltStack will be explained in this book, some definitions appeal others,
that’s why we need - in the first order - to have a global view about Salt.
10
https://github.com/saltstack/salt/branches/all
11
https://www.howtogeek.com/249966/how-to-install-and-use-the-linux-bash-shell-on-windows-10/
 Getting Started With SaltStack 14
Salt + Vagrant
Salty Vagrant is a Vagrant plugin that allows you to use Salt as a provisioning tool. You can use
formulas and existing configurations to create and build development environments.
The simplest way to use Salty Vagrant is configuring it to work in masterless mode. Details are
explained in the official Vagrant documentation12 .
Through this book, you will learn how to interface Vagrant with Salt in order to automate the
provisioning of virtual machines.
Salt Cloud
Salt Cloud 13 is a public cloud provisioning tool created to integrate Salt to the major cloud
infrastructure providers (AWS, Rackspace, GCP, Azure ..etc) in order to facilitate and accelerate
the supply process.
Salt Cloud allows managing a cloud infrastructure based on maps and profiles of virtual machines.
This means that many virtual machines in the cloud can be managed easier and faster.
We will see in this book how to integrate Salt with some Cloud providers (e.g. Linode, AWS)
Halite
Halite14 is the client-side web interface (Salt GUI). It connects and operates a SaltStack infrastructure.
This tool is a graphical complement, but it is not indispensable for the functioning of Salt. For best
results, Halite works with Hydrogen and higher versions.
 Salt + Docker
Salt can be used to provision and manage Docker containers (Dockerng / Dockerio). We are going
to see toegether how to configure and manage Docker containers using Salt.
12
https://docs.vagrantup.com/v2/provisioning/salt.html
13
https://github.com/saltstack/salt-cloud
14
https://github.com/saltstack/halite
 Getting Started With SaltStack 15
Conclusion
The general presentation of Salt is not enough to start using Salt, but it is required if you are not
familiar with concepts like configuration management and data center automation.
Installation
Introduction
Installation differs according to the operating system: Arch Linux, Debian, Fedora, FreeBSD, Gentoo,
OS X, RHEL / CentOS / Scientific Linux / Amazon Linux / Oracle Linux, Solaris, Ubuntu, Windows
and SUSE …etc.
Salt is made of masters and slaves typically masters and minions. In the jargon used by the editor,
salt-master is the master and salt-minion is the minion. A syndic called “salt-syndic” is part of the
package and its role consists of connecting masters.
Dependencies
Salt has external dependencies and they are:
Depending to the chosen transport method (ZeroMQ or RAET ), dependencies may vary :
• ZeroMQ:
– ZeroMQ >= 3.2.0
– pyzmq >= 2.2.0 - ZeroMQ Python bindings
– PyCrypto - The Python cryptography toolkit
– M2Crypto - “Me Too Crypto” - Python OpenSSL wrapper
• RAET :
– libnacl - Python bindings to libsodium
– ioflo - The flo programming interface raet and salt-raet is built on
Installation 17
• mako - an optional parser for Salt states (configurable in the master settings)
• gcc - for dynamic Cython module compiling
If you will use salt-cloud for cloud infrastructure management or dockerng with Docker machines,
you may install other packages and you will have other dependencies, this is explained later in the
book.
By default, Salt uses ZeroMQ. Its official website15 defines it as :
ZeroMQ \zero-em-queue\, \ØMQ: - Connect your code in any language, on any platform. - Carries
messages across inproc, IPC, TCP, TPIC, multicast. - Smart patterns like pub-sub, push-pull, and
router-dealer. - High-speed asynchronous I/O engines, in a tiny library. - Backed by a large and active
open source community. - Supports every modern language and platform. - Build any architecture:
centralized, distributed, small, or large. - Free software with full commercial support.
Installation
If your server has Internet access, this shell script will be convenient to automate the installation of
the stable version:
or:
Note that the last version downloaded directly from Github is the development version. If
you need a version for your production systems, do not do this.
You can also try the Pythonic way:
15
http://zeromq.org/
Installation 18
Advanced Installation
The saltbootstrap script16 offers plenty of command line options.
The installation script can be called as follows:
1 sh bootstrap-salt.sh –h
• stable (default)
• daily (specific to Ubuntu)
• git
Examples:
1 bootstrap-salt.sh
2 bootstrap-salt.sh stable
3 bootstrap-salt.sh daily
4 bootstrap-salt.sh git
5 bootstrap-salt.sh git develop
6 bootstrap-salt.sh git v0.17.0
7 bootstrap-salt.sh git 8c3fadf15ec183e5ce8c63739850d543617e4357
Script Options:
16
http://docs.saltstack.com/en/latest/topics/tutorials/salt_bootstrap.html
Installation 19
Masterless Salt
Installing a masterless salt is about installing a salt-minion without a salt-master. Functionally, it
makes no difference if your goal is to test.
This is a great way to quickly test the basics of SaltStack. The execution of Salt different commands
are done using salt-call that runs on the minion and displays the execution traces. More details about
salt-call will be addressed later.
The minion contains extended features to enable it to run in standalone mode. An autonomous
minion can be used to do a certain number of things:
If it is your first time to use and learn Salt, you may find some new words that you will not
understand for now but they will be explained later.
The idea of running both on the same machine is very useful for quickly testing all the functionalities
of Salt while keeping the master/slave or master/minion model.
Using “PIP”
Since SaltStack is available on PyPi repository, it can be installed using pip (Python Package
Management System):
This will not work unless pip is installed. Assuming that your OS package manager is yum:
For RHEL 6:
Then, install salt-master and salt-minion on their respective machines (or both of them on your
localhost).
On the master server :
Further details about RHEL installation are available in the official documentation18 .
If you want your master to start-up with the OS:
1 chkconfig salt-master on
To start it:
1 chkconfig salt-minion on
2 service salt-minion start
A master or a minion service is a daemon that can be started respectively by the following
commands:
1 salt-master -d
2 salt-minion -d
In the list of system processes, you should find the process running the following command for the
master:
18
http://salt.readthedocs.org/en/latest/topics/installation/rhel.html
Installation 22
1 /usr/bin/python /usr/bin/salt-master
and
1 /usr/bin/python /usr/bin/salt-minion
1 kill -9 3139
2 kill -SIGTERM 3139
1 pkill salt-master
2 pkill salt-minion
3 pkill salt-syndic
Normally the dependencies will be automatically installed and in the opposite case install them
manually, reinstall Salt or start a reconfiguration of packages dependencies using the package
manager of your operating system.
1 wget http://dl.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm
2 rpm -i epel-release-5-4.noarch.rpm
3 rm epel-release-5-4.noarch.rpm
1 virtualenv /path/to/your/virtualenv
1 source /path/to/your/virtualenv/bin/activate
Create the needed configuration files for the master and the minion:
1 mkdir -p /path/to/your/virtualenv/etc/salt
2 cp ./salt/conf/master /path/to/your/virtualenv/etc/salt/master
3 cp ./salt/conf/minion /path/to/your/virtualenv/etc/salt/minion
This is not our main topic, I invite you to learn more about using virtualenv in the on-line
documentation19 .
1 /etc/apt/sources.list.d
19
http://docs.saltstack.com/en/latest/topics/development/hacking.html
Installation 24
1 /etc/apt/sources.list
and to finish :
1 apt-get update
2 apt-get install salt-master
3 apt-get install salt-syndic
4 apt-get install salt-minion
Upgrading SaltStack
According to the method used to install Salt, upgrading could be different from one package manager
to another.
The important point to remember in this part is that the master must be upgraded first,
followed by the minions.
The compatibility between an upgraded master and minions having an earlier version is managed
by SaltStack, so everything will work perfectly except the case where the update concerns security
or vulnerabilities patches on minions.
Just use :
1 salt --versions
1 Salt: 2014.7.0
2 Python: 2.7.3 (default, Mar 14 2014, 11:57:14)
3 Jinja2: 2.6
4 M2Crypto: 0.21.1
5 msgpack-python: 0.1.10
6 msgpack-pure: Not Installed
7 pycrypto: 2.6
8 libnacl: Not Installed
9 PyYAML: 3.10
10 ioflo: Not Installed
11 PyZMQ: 13.1.0
12 RAET: Not Installed
13 ZMQ: 3.2.3
14 Mako: 0.7.0
Configuration And Troubleshooting
Introduction
In the remainder of this guide, we will use a Linux virtual machine where we will install the salt-
master and the salt-minion (in the same machine). The basic concepts are explained here so that
there will be practically neither functional nor conceptual differences between having master and
minion in the same machine and having them in two different machines.
The only differences that might exist are easily noticeable such as the configuration of IP addresses
(interfaces).
For the configuration, we will follow those key steps:
Configuration files for master and minion are in the default configuration directory:
1 /etc/salt
1 # Set the file client. The client defaults to looking on the master server for
2 # files, but can be directed to look at the local file directory setting
3 # defined below by setting it to local.
4 # file_client: remote
1 file_client: local
You can find more detail in the salt-minion section of the 4th chapter.
Configuration Of salt-master
In the configuration file /etc/salt/master the default address for the listening interface (binding) is
set to 0.0.0.0:
1 interface: 0.0.0.0
1 interface: 127.0.0.1
Make sure that your system settings are well configured, think of hosts file for example.
In our case we will keep the default value of the interface which means 0.0.0.0 and the defaults ports
values:
1 publish_port: 4505
2 ret_port: 4506
publish_port is the network port and ret_port is the port of the execution and the feedback of
minions.
Same thing for file_roots, we are just keeping its default value:
Configuration And Troubleshooting 28
1 file_roots:
2 base:
3 - /srv/salt/
The base environment is always required and must contain the top file. It is basically the entry point
for Salt and will be used if no environment is specified. This will be detailed later.
1 file_roots:
2 base:
3 - /srv/salt/
4 dev:
5 - /srv/salt/dev/subfolder1/
6 - /srv/salt/dev/subfolder2/
7 prod:
8 - /srv/salt/prod/subfolder1
9 - /srv/salt/prod/subfolder2
1 file_roots:
2 base:
3 - /srv/salt/
We will focus instead on how those environments are actually implemented and for that we will a
state tree for each one of them. We are going to have multiple state trees designating each one of
our environments.
For the time being, restart the master:
Configuration And Troubleshooting 29
Configuration Of salt-minion
The address of master machine should be configured in the minion configuration file /etc/salt/min-
ion.
1 master: 127.0.0.1
Keep in mind that the value of master_port should coincide with the value of ret_port
previously configured on the master.
A minion may have a nickname, you are free to change it by uncommenting id: and changing its
value but by default the minion id/nickname is the name of the machine (obtained with the linux
command hostname).
This value is stored in the file:
1 /etc/salt/minion_id
Post Installation
A very important thing to do after the installation and before the configuration is to let the master
accept the minion. To do this, you should use salt-key.
Salt-key is the public key management system server. A master can communicate with a minion
only when the public key of the latter is processed and accepted by salt-key.
SaltStack automatically handles the key generation for running minions, the server will be notified
of the presence of a minion via salt-key.
A minion can have three different states :
• **Accepted **: The public key of the minion is accepted and thus registered on the server.
• **Unaccepted **: The public key of the minion has not been accepted yet.
• **Rejected **: This is usually a problem of installation or configuration.
After having set up the master and the minion, type the following command to list all minions and
their associated states:
Configuration And Troubleshooting 30
1 salt-key -L
or
Accepted minions are ready to receive orders from the the master. Otherwise, use the same command
but followed by the following parameters and the id of the minion:
1 salt-key -a <name_of_the_minion>
1 salt-key -A
Or
1 salt-key --accept-all
Example:
To add a minion mynode we should type the following command:
1 salt-key -a 'mynode'
name of the minion and mynode are used for those examples. You should change it by the
name of the minion you are using . Remember salt-key -L can help you list the names of all minions.
• All of your salt-master and salt-minion are installed with the with satisfied dependencies
• Python version is compatible with the version of SaltStack salt-master and salt-minion should
be running with the root user
• Communication and execution ports are well configured and opened
• The configuration of any firewall between a master and a minion should be taken into account
(Example : check your iptables).
The first test to do is to ping the minion from the master machine:
1 salt « * » test.ping
Troubleshooting salt-master
The first diagnostic step is to launch the salt-master process in debug mode :
1 salt-master -L debug
The output of this command is very important because it contains helpful information about the
majority of the problems you can encounter when using Salt.
Troubleshooting salt-minion
Like salt-minion, salt-master can be also executed in debug mode.
1 salt-minion -L debug
The output will be the set of debug, warning and error messages.
Troubleshooting Ports
For salt-master, both TCP ports 4505 and 4506 should be opened. By running debug mode,
information about those ports should be seen in the debug output.
For salt-minion, there are no specific ports to be opened but connectivity must be established :
Configuration And Troubleshooting 32
1 nc -v -z salt.master.ip.address 4505
2 nc -v -z salt.master.ip.address 4506
In the case where a salt-master and a salt-minion are installed on two different production machines,
most of the cases your server is behind a firewall, check that it allows the establishment of the
connection between them.
On a Redhat system, iptables must allow the following configuration:
21
If you have troubles with setting your firewall using iptable file, consider using “ufw”
(Uncomplicated FireWall).
Using salt-call
salt-call is a good way to debug. The command can be used for example with the states or highstates.
Example:
When salt-call is executed on a minion, no salt-master is called during the execution. This command
allows the local execution, development assistance and more verbose output.
If you want more verbosity, increase the log level.
Example:
“The main difference between using salt and using salt-call is that salt-call is run from
the minion, and it only runs the selected function on that minion. By contrast, salt is
run > from the master, and requires you to specify the minions on which to run the
command using salt’s targeting system.”
21
http://en.wikipedia.org/wiki/Uncomplicated_Firewall
Configuration And Troubleshooting 33
1 /usr/bin/python26
Basic Concepts
Python
Salt is coded in Python22 and also provides a Python client interface. Python is an object-oriented,
multi-paradigm and multi-platform programming language.
It favors the structured, object-oriented, functional and imperative programming. It is strongly typed
and has an automatic memory management with garbage collection and an exceptions management
system; thus it is similar to Perl, Ruby, Scheme, Smalltalk, and Tcl.
All Python releases are Open Source23 . Historically, most but not all Python releases have also been
GPL compatible.
It runs on most computing platforms, mainframe supercomputers, from Windows to Unix through
GNU/Linux, Mac OS, or Android, iOS and also with Java or .NET. It is designed to maximize
programmer productivity by providing high-level tools and a simple syntax.
It is also appreciated by the educators who find it a language with syntax that is clearly separated
from the low-level mechanisms, a language that enables easy introduction to basic programming
concepts.
Python is also widely used in scripting and operations (ops). Several DevOps and system administra-
tors adopted this language as their primary language for scripting because as it has a large standard
library, providing tools suitable for many types of tasks.
Salt is coded in Python and offers the power and flexibility that this language has. Even if Salt can
be used only from the command line, you can also use its Python interface and call all its commands
from Python scripts. Mastering Python is not obligatory but it is recommended to be at least initiated
with this programming language.
A “hello world” in *Python can be written this way:
1 #!/usr/bin/env python
2 print ( 'hello world !')
One of the advantages of working with Python is its community support, its active community and
its large number of users.
22
http://www.python.org
23
http://opensource.org/
Basic Concepts 35
• Comments start with the hash sign (#) and extend all along the line
• It is possible to include a JSON syntax in a YAML syntax
• Lists of items are denoted by a dash (-) followed by a space - one item per line
• Arrays are represented as “key: value” - one pair per line
• Scalar (strings) can be enclosed in double quotes (“) or single (‘)
• Several documents together in a single file are separated by three hyphens (—); three dots (…)
are optional to mark the end of a document within a stream
• The indentation, with spaces, shows a tree structure
• Tab characters are never allowed as indentation
• etc ..
YAML is easier to read by a human than JSON, it is more focused on data serialization with less
documentation in comparison with XML.
Example:
Basic Concepts 36
1 myApp 1.0:
2 end_of_maintenance: 2014-01-01
3 is_stable: true
4 release_manager: "James Dean"
5 description:
6 This a stable version
7 latest_beta: ~
8 latest_minor: 1.0.17
9 archives: { source1: [zip, tgz], source2: [zip, tgz] }
10
11 myApp 1.1:
12 end_of_maintenance: 2014-06-01
13 is_stable: true
14 release_manager: 'Chuck Norris'
15 description:
16 Chuck Norris can program a computer to breath
17 latest_beta: null
18 latest_minor: 1.2.4
19 archives:
20 source1:
21 - zip
22 - tgz
23 source2:
24 - zip
25 - tgz
It is a text-based template language and thus can be used to generate any markup as
well as source code. It is licensed under a BSD License. The Jinja template engine allows
Basic Concepts 37
customization of tags, filters, tests, and globals. Also, unlike the Django template engine,
Jinja allows the template designer to call functions with arguments on objects. Jinja,
like Smarty, also ships with an easy-to-use filter system similar to the Unix pipeline.
37 </body>
38 </html>
Jinja is quite easy to use, and its use with SaltStack is not complicated, this is one of the strengths
of Jinja and therefore Salt.
SaltStack uses this templating engine with files like state files.
The advantage of using Jinja is the use of control structures like conditional or redundant elements.
The use of these structures (if then, for … in, range from … to … … etc.) makes the repetitive or the
conditional templating tasks accessible, fast and easy to use.
In a state file (SLS: SalStack states), we can employ both YAML and Jinja.
Below is are SLS files:
1 /home/user/project/integ/myApp/4.2.2/myScript.py:
2 file.managed:
3 - source: salt://integ/myApp/4.2.2/myScript.py
4 - mode: 644
1 grains['destination_dir']: /home/user/project
1 {{ grains['destination_dir'] }}/integ/myApp/4.2.2/myScript.py:
2 file.managed:
3 - source: salt://integ/myApp/4.2.2/myScript.py
4 - mode: 644
The output is the same, since Jinja templating engine will render the code between {{ and }} and will
replace it by /home/user/project.
Don’t worry if you haven’t understood the whole code above; you will.. For the moment,
just focus on how to use Jinja.
Jinja Loading Util which belongs to salt.renderers.jinja module is the tool that reads and parses Jinja
template files/code and it “process” the code to give it its value.
Since Salt Formulas are pre-written Salt states, Jinja is used to write formula states and it is very
helpful.
Basic Concepts 39
salt-master as its name suggests is the master, salt-minion is the listening target. Generally,
at least in this documentation, we denote by salt-master or salt-minion a machine apart (it can be
physical or virtual ..etc). The case where both are installed in the same machine (our localhost) is
an exception - for testing purposes, this does not change almost nothing at a functional level. That
is why we always assume that each salt-master or salt-minion is considered as a machine.
salt-master is the master daemon that controls one or more minions and can communicate with
other masters (this will be clearer for you with salt-syndic section).
By default, salt-master is configured using the file:
1 /etc/salt/master
Of course you can change the location of the configuration file but we recommend to keep
it by default for the moment to avoid all confusions.
salt-master is launched using the command:
1 salt-master [options]
1 salt-master
or
1 salt-master -d
The latter command will start the master as a daemon that will launch multiple salt-master
processes, even if ‘worker_threads’ is set to ‘1’.
You can use one of the following options (provided by SaltStack in the official on-line documenta-
tion):
1 --version
2 #Print the version of Salt that is running.
3 --versions-report
4 #Show program's dependencies and version number, and then exit
5 -h, --help
6 #Show the help message and exit
7 -c CONFIG_DIR, --config-dir=CONFIG_dir
8 #The location of the Salt configuration directory. This directory contains the c\
9 onfiguration files for Salt master and minions. The default location on most sys\
10 tems is /etc/salt.
11 -u USER, --user=USER
12 #Specify user to run salt-master
13 -d, --daemon
14 #Run salt-master as a daemon
15 --pid-file PIDFILE
16 #Specify the location of the pidfile. Default: /var/run/salt-master.pid
17 -l LOG_LEVEL, --log-level=LOG_LEVEL
18 #Console logging log level. One of all, garbage, trace, debug, info, warning, er\
19 ror, quiet. Default: warning.
20 --log-file=LOG_FILE
21 #Log file path. Default: /var/log/salt/master.
22 --log-file-level=LOG_LEVEL_LOGFILE
23 #Logfile logging log level. One of all, garbage, trace, debug, info, warning, er\
24 ror, quiet. Default: warning.
Generally, the master starts as a daemon except when it is launched in debugging mode:
Basic Concepts 41
1 salt-master -l debug
The case of a masterless minion, lets you use Salt’s configuration management for a single
machine, but not to execute remotely commands or to communicate with other machines.
Note that masterless minion is rich with functionalities, you can even use it to stand up a salt-master
via states or what we call “Salting a salt master”.
Generally a salt-minion is the responsible for commands execution. But to do that, it must have the
accepted state, which means that the salt-master cans communicate securely with it. The latter uses
a key system to communicate safely with one or more minions.
The default configuration file of a minion is in the following file:
1 /etc/salt/minion
1 salt-minion [options]
1 --version
2 # Print the version of Salt that is running.
3 --versions-report
4 # Show program's dependencies and version number, and then exit
5 -h, --help
6 # Show the help message and exit
7 -c CONFIG_DIR, --config-dir=CONFIG_dir
8 #The location of the Salt configuration directory. This directory contains the c\
9 onfiguration files for Salt *master* and minions. The default location on most s\
10 ystems is /etc/salt.
11 -u USER, --user=USER
12 #Specify user to run salt-minion
13 -d, --daemon
Basic Concepts 42
1 salt-minion -d
1 salt-minion -l debug
Just uncomment the part dedicated to salt-syndic configuration and note that:
A basic/standard example of a Salt topology using salt-syndic will have a top master (a machine
where only salt-master is installed) and lower-level machines where the master and the syndic
should be installed and configured. The machine/node sitting in the bottom of this hierarchic
topology does not need neither a master nor a syndic, it only needs a configured minion.
Through the topology described above, when a command is issued from a higher-level master, the
listening syndic(s) - those having a lower level - will receive those orders and propagate them to
their minions and listening syndic(s) having a lower-level.
Returned data and executions statuses are generated and dispatched-back using the same
reversed hierarchy route.
In some cases, large infrastructure typologies the feedback of a connected minion, master or a
syndic could take time to roll up, this could cause a timeout for the execution even if the concerned
command is executed in reality but the response driving up through the infrastructure may take
time to arrive. To fix this, just add the following configuration to the master configuration file:
1 syndic-wait: 1
Basic Concepts 44
Another important configuration variable needs to be set to True, the official Salt documentation24
says that :
The master that the syndic connects to sees the syndic as an ordinary minion, and
treats it as such. the higher level master will need to accept the syndic’s minion key
like any other minion. This master will also need to set the order_masters value in the
configuration to True. The order_masters option in the configuration on the higher level
master is very important, to control a syndic extra information needs to be sent with
the publications, the order_masters option makes sure that the extra data is sent out.
Example: A salt-master communicates with a salt-minion only when this key system allows it. The
illustration below shows how a salt-minion can be accepted through Salt process.
An accepted salt-minion
1 salt-key [options]
1 --version
2 # Print the version of Salt that is running.
3 --versions-report
4 # Show dependencies and version number
5 -h, --help
6 # Show the help message
7 -c CONFIG_DIR, --config-dir=CONFIG_dir
8 # The location of the Salt configuration directory. This directory contains the \
9 configuration files for Salt master and minions. The default location is /etc/sa\
10 lt.
11 -q, --quiet
12 # Suppress output
Basic Concepts 47
13 -y, --yes
14 # Answer 'Yes' to all questions presented, defaults to False
15 --log-file=LOG_FILE
16 # Log file path. Default: /var/log/salt/minion.
17 --log-file-level=LOG_LEVEL_LOGFILE
18 # Logfile logging log level. One of all, garbage, trace, debug, info, warning, e\
19 rror, quiet.
20 # Default: warning.
21 --out
22 # Pass in an alternative outputter to display the return of data.
23 # This outputter can be any of the available outputters: grains, highstate, json\
24 , key, overstatestage, pprint, raw, txt, yaml .
25 # Some outputters are formatted only for data returned from specific functions; \
26 for instance, the grains outputter will not work for non-grains data.
27 # If an outputter is used that does not support the data passed into it, then Sa\
28 lt will fall back on the pprint outputter and display the return data using the \
29 Python pprint standard library module.
30 --out-indent OUTPUT_INDENT, --output-indent OUTPUT_INDENT
31 # Print the output indented by the provided value in spaces. Negative values dis\
32 able indentation. Only applicable in outputters that support indentation.
33 --out-file=OUTPUT_FILE, --output-file=OUTPUT_FILE
34 # Write the output to the specified file.
35 --no-color
36 # Disable all colored output
37 --force-color
38 # Force colored output
39 -l ARG, --list=ARG
40 # List the public keys. The args pre, un, and unaccepted will list unaccepted/un\
41 signed keys. acc or accepted will list accepted/signed keys. rej or rejected wil\
42 l list rejected keys. Finally, all will list all keys.
43 -L, --list-all
44 # List all public keys. (Deprecated: use --list all)
45 -a ACCEPT, --accept=ACCEPT
46 # Accept the specified public key (use --include-all to match rejected keys in a\
47 ddition to pending keys). Globs are supported.
48 -A, --accept-all
49 # Accepts all pending keys.
50 -r REJECT, --reject=REJECT
51 # Reject the specified public key (use --include-all to match accepted keys in a\
52 ddition to pending keys). Globs are supported.
53 -R, --reject-all
54 # Rejects all pending keys.
Basic Concepts 48
55 --include-all
56 # Include non-pending keys when accepting/rejecting.
57 -p PRINT, --print=PRINT
58 # Print the specified public key.
59 -P, --print-all
60 # Print all public keys
61 -d DELETE, --delete=DELETE
62 # Delete the specified key. Globs are supported.
63 -D, --delete-all
64 # Delete all keys.
65 -f FINGER, --finger=FINGER
66 # Print the specified key's fingerprint.
67 -F, --finger-all
68 # Print all keys' fingerprints.
69 --gen-keys=GEN_KEYS
70 # Set a name to generate a keypair for use with Salt
71 --keysize=KEYSIZE
72 # Set the keysize for the generated key, only works with the '--gen-keys' option\
73 , the key size must be 2048 or higher, otherwise it will be rounded up to 2048. \
74 The default is 2048.
Some of those commands (available on the official SaltStack documentation) will be used in the next
chapter.
There will be no possible communication between a master and a minion unless the salt-key
system allows it. Be sure that all minions are accepted, before starting anything else.
1 salt-run RUNNER
A good example of using this is when you want to list your minions’ states (up or down).
Type the next command to show all minions and the status of everyone.
Basic Concepts 49
1 salt-run manage.status
1 salt-run manage.up
1 salt-run manage.down
Another useful usage is when you want to clear Salt cache (this could be helpful also for
debugging/troubleshooting).
1 salt-run cache.clear_all
Function Description
state Execute overstate functions
survey A general map/reduce style salt runner for aggregating results
returned by several different minions.
test This runner is used only for test purposes and servers no production
purpose
thin The thin runner is used to manage the salt thin systems.
virt Control virtual machines via Salt
winrepo Runner to manage Windows software repo
Example : We want to add the key env_name (for environment name) and give it the value of prod
(for production).
This will add a the key/value to grains configuration file. We can do a test :
1 mynode:
2 - integ
To delete ‘env_name’ :
Dictionaries and lists in Python are powerful and simple data structures. Python offers
multiple functionalities to easily manage them.
Creating and adding elements to a list is simple :
grains.append is like Python’s list.append will create a list if it does not exist.
What we have added, could be viewed using grains.get:
Basic Concepts 52
Once all of those elements were added, we can now check all of the grains we have on our minion.
Remember that there every minion has already default grains (os, cpu architecture ..etc).
To view all of them, just type:
This will print a list of keys that the minion knows their values.
Example:
Basic Concepts 53
1 mynode:
2 - biosreleasedate
3 - biosversion
4 - cpu_flags
5 - cpu_model
6 - cpuarch
7 - defaultencoding
8 - defaultlanguage
9 - domain
10 - fqdn
11 - fqdn_ip4
12 - fqdn_ip6
13 - gpus
14 - host
15 - id
16 - ip_interfaces
17 - ipv4
18 - ipv6
19 - kernel
20 - kernelrelease
21 - localhost
22 - manufacturer
23 - master
24 - mem_total
25 - nodename
26 - num_cpus
27 - num_gpus
28 - os
29 - os_family
30 - osarch
31 - oscodename
32 - osfinger
33 - osfullname
34 - osmajorrelease
35 - osrelease
36 - path
37 - productname
38 - ps
39 - pythonpath
40 - pythonversion
41 - saltpath
42 - saltversion
Basic Concepts 54
43 - saltversioninfo
44 - serialnumber
45 - server_id
46 - shell
47 - virtual
All of the above output are the list of keys, to print out their values, you should type:
Here is an example of all the grains (keys, values) that the minion mynode has:
1 mynode
2 biosreleasedate: 03/05/2009
3 biosversion: VirtualBox
4 cpu_flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat p\
5 se36 clflush mmx fxsr sse sse2 syscall nx rdtscp lm constant_tsc up rep_good pni\
6 monitor ssse3 lahf_lm
7 cpu_model: Intel(R) Celeron(R) CPU G530 @ 2.40GHz
8 cpuarch: x86_64
9 defaultencoding: UTF8
10 defaultlanguage: en_US
11 domain: localdomain
12 fqdn: mynode
13 fqdn_ip4:
14 fqdn_ip6:
15 gpus:
16 model:
17 VirtualBox Graphics Adapter
18 vendor:
19 unknown
20 host: mynode
21 id: mynode
22 ip_interfaces: {'lo': ['127.0.0.1'], 'eth0': ['10.0.0.1']}
23 ipv4:
24 10.0.0.1
25 127.0.0.1
26 ipv6:
27 ::1
28 fe88::a00:72ff:fee8:64b
29 kernel: Linux
30 kernelrelease: 2.6.32-431.1.2.el6.x86_64
Basic Concepts 55
31 localhost: mynode
32 master: 10.0.254.254
33 mem_total: 1500
34 nodename: mynode
35 num_cpus: 1
36 num_gpus: 1
37 os: ScientificLinux
38 os_family: RedHat
39 osarch: x86_64
40 oscodename: Carbon
41 osfinger: Scientific Linux-6
42 osfullname: Scientific Linux
43 osmajorrelease:
44 6
45 4
46 osrelease: 6.4
47 path: /sbin:/usr/sbin:/bin:/usr/bin
48 productname: VirtualBox
49 ps: ps -efH
50 pythonpath:
51 /usr/bin
52 /usr/lib64/python26.zip
53 /usr/lib64/python2.6
54 /usr/lib64/python2.6/plat-linux2
55 /usr/lib64/python2.6/lib-tk
56 /usr/lib64/python2.6/lib-old
57 /usr/lib64/python2.6/lib-dynload
58 /usr/lib64/python2.6/site-packages
59 /usr/lib/python2.6/site-packages
60 /usr/lib/python2.6/site-packages/setuptools-0.6c11-py2.6.egg-info
61 pythonversion: 2.6.6.final.0
62 saltpath: /usr/lib/python2.6/site-packages/salt
63 saltversion: 0.17.4
64 saltversioninfo:
65 0
66 17
67 4
68 serialnumber: 0
69 server_id: 332009357
70 shell: /bin/sh
71 virtual: VirtualBox
As you can see, salt-minion knows a lot of information about the system and the environment on
Basic Concepts 56
which it is installed, like Python version, the serial number or IP addresses of different machine’s
interfaces.
If you are interested in viewing only the machine’s interfaces, use ip_interfaces (that figure in
grains.item output ) like this:
This will give you only the IP interface data. Only requested data will be printed out:
1 ip_interfaces:
2 eth0:
3 - 10.0.0.1
4 lo:
5 - 127.0.0.1
It is also possible to refine your output like getting only the IP of eth0:
Result:
1 mynode:
2 ip_interfaces:
3 eth0:
4 - 10.0.0.1
5 lo:
6 - 127.0.0.1
7 kernel:
8 Linux
Adding, modifying or deleting grains can be made directly on the default configuration file:
Basic Concepts 57
1 /etc/salt/grains
The configuration file is written in YAML. Neglecting standards compliance may lead to
some problems.
You can add strings, lists or dictionaries. Open the grains file or the configuration file with you
favorite editor and add :
1 key: value
1 my_list:
2 - my_first_value
3 - my_second_value
It could be a dictionary:
1 my_dict:
2 my_first_key: my_first_value
3 my_second_key: my_second_value
By using YAML syntax you can create complex data structures with list and dict and of course int,
string and boolean types ..etc
Here is an example :
1 include:
2 - apache
3
4 mod-php:
5 pkg.installed:
6 - name: libapache2-mod-php
7 - require:
8 - pkg: apache
9
10 a2enmod php:
11 cmd.run:
12 - unless: ls /etc/apache2/mods-enabled/php.load
13 - require:
Basic Concepts 58
14 - pkg: mod-php5
15 - watch_in:
16 - module: apache-restart
17
18 /etc/php/apache2/php.ini:
19 file.managed:
20 - source: salt://prog/php_ini
21
22 service.running:
23 - name: {{ apache.service }}
24 - enable: True
Notice that Jinja syntax is used near the name of the running service. We are going to use
it again in many of the following examples, if you are not familiar with it, normally following the
examples will be enough to understand its basics. If it is not the case, please refer to the official
documentation25 . If you want to master the use of Jinja, I recommend using it with Flask26
Targeting grains
In the previous examples we used SaltStack commands with the asterisk ‘ * ‘. The ‘*’ replaces all
machines or minions that salt-master has in its base. It is possible to perform a targeting minions on
which commands will be executed Salt.
When we type the next command:
SaltStack, more precisely the ‘salt-master’ will get the kernel name for all of the minion that it knows,
in other words, all of accepted salt-minions, which means minions that will respond with True when
this command is executed:
In all of the last example, since we are using one master and one minion (mynode), executing the
last command will have the next output:
25
http://jinja.pocoo.org/docs/dev/
26
http://flask.pocoo.org/
Basic Concepts 59
1 mynode:
2 kernel:
3 Linux
Once another minion is added (following the same configuration and process as the first minion),
the output will be different and will show other machines.
1 mynode:
2 kernel:
3 Linux
4
5 another_node:
6 kernel:
7 Darwin
You can also target minions having specific properties (grains data), like OS architecture; OS family;
kernel name or simply the name of the minion..etc
Every SysAdmin knows that regular expressions (Regex) are necessary to “survive some
situations”. When it comes to targeting, regular expressions could be helpful. If you are not familiar
with Regex, it is important to begin learn them. Note that SaltStack is using PCRE : Perl Compatible
Regular Expressions.
Other types of targeting, like compound matchers, are provided.
The following table explains well the list of compound matchers:
Note that all of those targeting expressions and methods could be used in the ‘top.sls’ file.
Top file will be explained hereinafter.
1 {
2 "mynode": {
3 "kernel": "Linux",
4 "ip_interfaces": {
5 "lo": [
6 "127.0.0.1"
7 ],
8 "eth0": [
9 "10.0.0.1"
10 ]
11 }
12 }
13 }
Basic Concepts 62
Other displaying formats could be used, according official documentation, Salt uses this list of output
modules:
grains
Special outputter for grains.
highstate
Outputter for displaying results of state runs. The return data from the highstate command is a
standard data structure which is parsed by the highstate outputter to deliver a clean and readable
set of information about the highState run on minions.
json_out
Display return data in JSON format. The output format can be configured in two ways: Using the
–out-indent CLI flag and specifying a positive integer or a negative integer to group JSON from
each minion to a single line.
key
Display salt-key output. The salt-key command makes use of this outputter to format its output.
nested
Recursively display nested data. This is the default outputter for most execution functions.
newline_values_only
Display values only, separated by newlines.
no_out
Display no output.
no_return
Display output for minions that did not return. This outputter is used to display notices about which
minions failed to return when a salt function is run with -v or –verbose.
overstatestage
Display clean output of an overstate stage. This outputter is used to display overState stages, and
should not be called directly.
pprint_out
Python pretty-print (pprint). The python pretty-print system was once the default outputter.
raw
Display raw output data structure. This outputter simply displays the output as a Python data
structure, by printing a string representation of it.
txt
Basic Concepts 63
Simple text outputter. The txt outputter has been developed to make the output from shell commands
on minions appear as they do when the command is executed on the minion.
virt_query
virt.query outputter
yaml_out
Display return data in YAML format. This outputter defaults to printing in YAML block mode for
better readability.
1 file_roots:
2 base:
3 - /srv/salt/
When using Salt, some rules should be respected, like the obligation to set a top.sls file in the base
environment. This file must be created on:
1 /srv/salt/top.sls
Why?
When using Salt with the default configuration, a single environment called base is set up by default:
Basic Concepts 64
That’s why the top.sls file should be under the base environment “file_roots”:
1 /srv/salt/
1 base:
2 '*':
3 - <SLS_name>
1 /srv/salt/vim.sls
If we want to assign the latter to all minions (‘’) in the *base environment, we need to have a top.sls
file containing:
1 base:
2 '*':
3 - vim
Note that you don’t need to write the whole name of the SLS file, writing “vim” instead of
vim.sls is sufficient.
When changing the path of vim.sls, think of changing it in the top.sls file also.
If the vim.sls path is:
1 /srv/salt/salt_vim/vim.sls
1 base:
2 '*':
3 - salt_vim/vim
Since our base environment is /srv/salt we don’t need to type all of the path of vim.sls
which is /srv/salt/salt_vim/vims.sls. You can just use salt_vim/vim, cf. line 3 in the last example.
The content of top.sls file depends on the declaration of the environments in the salt-master
configuration file.
If you want to declare other environments different from the base environment like in the next
example:
1 file_roots:
2 base:
3 - /srv/salt/base
4 development:
5 - /srv/salt/development
6 pre-production:
7 - /srv/salt/pre-production
8 production:
9 - /srv/salt/production
the top.sls file should reference all of the different declared environments:
1 base:
2 '*':
3 - vim
4
5 development:
6 '*dev*':
7 - dev_sls
8
9 pre-production:
10 '*preprod*':
11 - prepprod_sls
12
13 production:
14 '*production*':
15 - prod_sls
The top.sls file will be called during the execution of a “highstate” and this is by the way its most
important purpose.
Basic Concepts 66
Salt will recursively look for top.sls files in the folder representing the base environment and any
other folders representing the other different environments.
In the last example, the file vim.sls will be applied to all minions. The file dev_sls.sls will be applied
to minions having an id containing preprod and so on ..
We could also use the compound matchers in a top.sls file:
1 base:
2 'mynode* and G@os:CentOS or E@myminion.*':
3 - match: compound
4 - vim
Like grains, viewing the list of available pillars could be done using:
1 /etc/salt/master
1 #pillar_roots:
2 #base:
3 # - /srv/pillar
1 mkdir /srv/pillar
1 touch /srv/pillar/top.sls
We are going to create a file where we will store some sensible data (mysql password as an example):
1 touch /srv/pillar/passwords.sls
In the case where the password will be used by all of available minions, the top.sls file will contain:
1 base:
2 '*':
3 - passwords
Notice the use of ‘’ to tell Salt that the password will be used by all *minions. Notice also
that we are not obliged to write password.sls. Salt will recognize that passwords make reference to
password.sls file.
For now, let’s add the password of MySql to the passwords.sls file:
1 mysql_password: aZ?e14E373F7123$aBP
After configuring and storing data, it is recommended to make a refresh for pillars using:
Generally, pillars are intended to be used in SLS files (Jinja) and can be called using different ways.
Simple structures:
1 {{ pillar['key'] }}
Nested structures:
1 {{ pillar['key_a']['key_b'] }}
Conditionally, if “key_a:key_b” is not configured (no value returned), the “value_c” will be used:
1 {{ salt['pillar.get']('key_a:key_b', 'value') }}
1 {{ pillar.get('key', {}).items() }}
Remote Execution
Remote execution is one the most important SaltStack features. Using CLI, you could use the different
matchers to to filter minions:
Basic Concepts 69
Example:
Remote execution can be also used in SLS files. The following example launch two remote controls.
The second command (echo) only starts when the first is executed.
1 run_script:
2 cmd.run:
3 - name: /path/to/myscript
4 - cwd: /etc
5 - stateful: True
6
7 run_second_script:
8 cmd.wait:
9 - name: echo 'Finished running script'
10 - cwd: /etc
11 - watch:
12 - cmd: run_script
1 reactor:
2 - 'salt/minion/*/start':
3 - /srv/reactor/sync_grains.sls
4 - /srv/reactor/mysql.sls
1 salt/minion/*/start
is activated, the “reactor” system will trigger the activation of the different SLS files:
1 - /srv/reactor/sync_grains.sls
2 - /srv/reactor/mysql.sls
The role of the first file (sync_grains.sls) is to synchronize the grains at salt-minion start-up. This is
its content:
1 sync_grains:
2 local.saltutil.sync_grains:
3 - tgt: {{ data['id'] }}
1 {% if data['id'] == mysql_minion %}
2 highstate_run:
3 local.state.highstate:
4 - tgt: mysql_minion
5 {% endif %}
The system of reactors enables management of multiple types of events associated with:
• Authentication
• Minion service upstart
• Key management
• Jobs management
• Cloud Management
You can find the rest of the list on the official documentation of SaltStack27
A central col-
lection of formula repositories for SaltStack.
Here is a list of some useful Salt formulas that you can find in the official git repository:
Why Vagrant
According to the official website29 , Vagrant is a tool for :
• Developers
If you are a developer, Vagrant will isolate dependencies and their configuration within
a single disposable, consistent environment, without sacrificing any of the tools you are
used to working with (editors, browsers, debuggers, etc.). Once you or someone else
creates a single Vagrantfile, you just need to Vagrant up and everything is installed
and configured for you to work. Other members of your team create their development
environments from the same configuration, so whether you are working on Linux, Mac
OS X, or Windows, all your team members are running code in the same environment,
against the same dependencies, all configured the same way. Say goodbye to “works on
my machine” bugs.
• Operations engineers
29
http://vagrantup.com
Basic Concepts 74
If you are an operations engineer, Vagrant gives you a disposable environment and
consistent workflow for developing and testing infrastructure management scripts.
You can quickly test things like shell scripts, Chef cookbooks, Puppet modules, and
more using local virtualization such as VirtualBox or VMware. Then, with the same
configuration, you can test these scripts on remote clouds such as AWS or RackSpace
with the same workflow. Ditch your custom scripts to recycle EC2 instances, stop
juggling SSH prompts to various machines, and start using Vagrant to bring sanity
to your life.
• Designers
If you are a designer, Vagrant will automatically set everything up that is required
for that web app in order for you to focus on doing what you do best: design. Once
> a developer configures Vagrant, you don’t need to worry about how to get that
application running ever again. No more bothering other developers to help you fix
your environment so you can test designs. Just check out the code, vagrant up, and
start designing.
Once docker-py is installed, you can start working with dockerio module.
The problem with dockerio is that it is deprecated and since the version 2015.8.0, develop-
ment is only done on dockerng, so I highly recommend to learn dockerng rather than dockerio.
The module dockerng will be supported for the future and it allows you to create,build,manipulate
and orchestrate Docker images.
According to the official documentation of Salt:
We have received a lot of feedback on our Docker support. In the process of implement-
ing recommended improvements, it became obvious that major changes needed to be
made to the
functions and return data. In the end, a complete rewrite was done.
Salt Cloud
Salt Cloud is built-in to Salt and is configured on and executed from your Salt master.
Salt can provision systems on cloud hosts / hypervisors : - Public clouds : Google Cloud, Amazon
Web Services, Linode - Private clouds: Vmware Private Cloud, OpenStack, CloudStack
After provisioning cloud hosts, they are managed using Salt like every other host.
To manage Cloud hosts SaltStack uses Providers, Profiles and Maps.
When using salt-cloud, normally you should follow 4 steps:
If you do not have python-libcloud installed, you should also install it:
Basic Concepts 77
If you are installing salt-cloud for development, make sure to install apache-libcloud:
1 /etc/salt/cloud
1 /etc/salt/master
2 /etc/salt/minion
We are going to detail the usage of Providers, Profiles and Maps in the next part of this section, but
the concepts are simple:
15 #
16 #script: bootstrap-salt
17
18
19 ##########################################
20 ##### Logging Settings #####
21 ##########################################
22
23 # The location of the master log file
24 #
25 #log_file: /var/log/salt/cloud
26
27
28 # The level of messages to send to the console.
29 # One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'.
30 #
31 # Default: 'info'
32 #
33 #log_level: info
34
35
36 # The level of messages to send to the log file.
37 # One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'.
38 #
39 # Default: 'info'
40 #
41 #log_level_logfile: info
42
43
44 # The date and time format used in log messages. Allowed date/time formating
45 # can be seen here:
46 #
47 # http://docs.python.org/library/time.html#time.strftime
48 #
49 #log_datefmt: '%Y-%m-%d %H:%M:%S'
50
51
52 # The format of the console logging messages. Allowed formatting options can
53 # be seen here:
54 #
55 # http://docs.python.org/library/logging.html#logrecord-attributes
56 #
Basic Concepts 79
The file is commented so no need to go through all definition but some important ones:
Salt Bootstrap
1 salt-bootstrap.sh
1 https://github.com/saltstack/salt-bootstrap
The bootstrap script install dependencies and the salt-minion on the new Cloud virtual machine.
You can find this file here:
Basic Concepts 80
1 /usr/lib/python2.7/dist-packages/salt/cloud/deploy/bootstrap-salt.sh
And if you are been using Vagrant and Docker with SaltStack as provisioner, you could find the
bootstrap-salt.sh in other locations:
1 locate bootstrap-salt.sh
2
3 /opt/vagrant/embedded/gems/gems/vagrant-1.7.4/plugins/provisioners/salt/bootstra\
4 p-salt.sh
5 /usr/lib/python2.7/dist-packages/salt/cloud/deploy/bootstrap-salt.sh
6 /var/lib/docker/aufs/diff/62f00290f429277acf1c5695a2e0321e8e96dfb18b50fc4451b111\
7 26f632045d/usr/lib/python2.7/dist-packages/salt/cloud/deploy/bootstrap-salt.sh
I am explaining this part because I recommend activating logs the first time you will use Salt Cloud.
It will help you to keep traces and learn from them.
The master log file is located in
1 /var/log/salt/cloud
In the Salt Cloud configuration file, you can set the nature of logging, you have several choices:
garbage, trace, debug, info, warning, error or critical.
I recommend also to activate the debugging mode in the console output. Same thing you have to
choose the degree of the verbosity: garbage, trace, debug, info, warning, error or critical.
If you are familiar with Python and you want to change the format of the date when logging, you
must change this line:
1 log_granular_levels:
2 'salt': 'debug',
3 'salt.modules': 'error'
4 'saltcloud': 'trace'
5
6 ### Salt Cloud Providers
7
8 Providers are configuration files where credentials for each used Cloud provider\
9 are provided (examples: ec2 configuration).
10 If you wan to use Digital Ocean, you should create a file in:
11
12 ``` bash
13 /etc/salt/cloud.providers.d/digital_ocean.conf
Then you should edit it in order to have a similar configuration to the next one:
1 do:
2 provider: digital_ocean
3 minion:
4 master: your_server_ip
5
6 # DigitalOcean Access Token
7 personal_access_token: your_access_token
8
9 # The name of your SSH key
10 ssh_key_name: salt-master-root-key
11
12 # The rivate key for your Digital Ocean account \
13
14 ssh_key_file: /root/.ssh/id_rsa
Note that the Digital Ocean example provided below could be different from AWS
configuration file. Every provider has its own configuration schema.
1 /etc/salt/cloud.profiles
1 /etc/salt/cloud.profiles.d/*conf
1 ubuntu_rackspace:
2
3 provider: rackspace
4 image: Ubuntu 14.04
5 size: 1024 server
6 script: salt-bootstrap
7 minion:
8 master: salt.example.com
9 append_domain: webs.example.com
10 grains:
11 role: webserver
1 azure-ubuntu:
2 provider: azure-west
3 image: 'Ubuntu-14_04-LTS-amd64-server-20140724-en-us-30GB'
4 ssh_username: user_name
5 ssh_password: user_password
6 ssh_pubkey: /root/azure.pub
7 media_link: 'https://example.blob.core.windows.net/vhds'
8 slot: production
9 size: Small
10 tty: True
11 sudo: True
1 ubuntu_small:
2 - web
3 - rethindb
4 ubuntu_medium:
5 - nginx
6 - mongodb
7 ubuntu_large:
8 - mysql
9 - mariadb
1 ubuntu_micro:
2 - web1:
3 grains:
4 grain_1: value_1
5 grain_2: value_2
6 - web2:
7 grains:
8 grain_1: value_1
9 grain_2: value_2
1 ubuntu_micro:
2 - web1:
3 minion:
4 log_level: error
5 - web2:
6 minion:
7 log_level: debug
and of course the map file can contains both of minions and grains options:
Basic Concepts 84
1 ubuntu_micro:
2 - web1:
3 minion:
4 log_level: error
5 grains:
6 grain_1: value_1
7 grain_2: value_2
8 - web2:
9 minion:
10 log_level: debug
11 grains:
12 grain_1: value_1
13 grain_2: value_2
1 /etc/salt/cloud.maps.d/*.conf
1 mkdir -p /etc/salt/cloud.maps.d
Map files can be called to roll out virtual machines using salt-cloud command with the -m option:
1 salt-cloud -m /etc/salt/cloud.maps.d/my_map.conf
1 salt-cloud -m /etc/salt/cloud.maps.d/my_map.conf -P
If you want to destroy all virtual machines that are not mentioned in the map file, use the -H option
(–hard):
1 salt-cloud -m /etc/salt/cloud.maps.d/my_map.conf -P -H
If you really want to use this dangerous option, you should activate it the cloud configuration file:
1 enable_hard_maps: True
1 ubuntu_micro:
2 - vm_1:
3 make_master: True
4 - vm_2
5 make_master: True
6 - vm_3
7 make_master: True
8 - vm_4
9 make_master: True
In this example, you can notice that creating 4 Cloud virtual machines.
When using a Map file, all bootstrapped minions will answer to the newly created salt-
master.
To make any of the bootstrapped minions answer to the another salt-master (not the newly
created one), you should add this condition to the configuration file.
Here is a simple example where the created vm_3 minion responds to a different salt-master and
not to the vm_1 salt-master:
1 centos_small:
2 - vm_1:
3 make_master: True
4 - vm_2
5 - vm_3:
6 minion:
7 master: master_vm_3.example.com
8 local_master: True
Salt Cloud uploads some files that are specific to salt-minion on your cloud VM, for debugging and
troubleshooting reasons, it could be a good idea to keep them, because by default they are deleted.
Basic Concepts 86
1 /tmp/.saltcloud/
Changing the log level to be more verbose is also a good idea if you want to troubleshoot Salt, you
can update your configurations in Salt Cloud configuration file:
Some problems may come from version compatibility as said in the official documentation:
Version Compatibility One of the most common issues that Salt Cloud users run into is
import errors. These are often caused by version compatibility issues with Salt.
Releases after 0.17.x (0.18 or greater) should not encounter issues as Salt Cloud has been
merged into Salt itself.
You cloud have problems due to the fact that the salt-cloud is not updated, in this case you can type:
Basic Concepts 87
1 salt-cloud -u
A successful update will show you a similar output to the following one:
1 Success:
2 ----------
3 Files updated:
4 - /etc/salt/cloud.deploy.d/bootstrap-salt.sh
5 - /usr/lib/python2.7/dist-packages/salt/cloud/deploy/bootstrap-salt.sh
Real World Examples
Introduction
One of the strengths of SaltStack is its richness and the large number of functionalities it offers. The
previous chapters have introduced the main concepts about Salt however the purpose of this book is
not to detail all about SaltStack but to bring you to the world of configuration management, remote
execution, infrastructure automation using the different Salt modules.
Knowledge is not enough and practicing is important. That’s why it is a good idea to practice, and
the next practical examples will help you.
Vagrant Quick-Start
Note that we are using Ubuntu 14.04 for the next tutorial.
To install and use Vagrant, first thing you need to do is installing VirtualBox:
Install Vagrant:
1 mkdir vagrantbox
2 cd vagrantbox
1 config.vm.box = "precise32"
If you want to choose another OS/Distro or architecture, please visit this repository of Vagrant boxes.
Example:
1 config.vm.box = "precise32"
1 vagrant up
1 vagrant ssh
1 wget https://releases.hashicorp.com/vagrant/1.8.1/vagrant_1.8.1_x86_64.deb
2 dpkg -i vagrant_1.8.1_x86_64.deb
30
https://www.vagrantup.com/downloads.html
Real World Examples 90
This will download an Ubuntu VirtualBox image and create three virtual machines for
you. One will be a salt-master named master and two will be salt-minion instances
named minion1 and minion2. Each salt-minion will point to master and the their
respective keys will already be accepted. Because the keys are pre-generated and reside
in the repository, please be sure to regenerate new keys if you use this for production
purposes.
1 su
2 salt-key --list-all
You will find that you have 2 minions in the same Vagrant machine.
Let the master accept the two minions:
1 salt-key --accept-all
After accepting the two keys, you can verify by listing all minions:
Real World Examples 91
1 Accepted Keys:
2 minion1
3 minion2
4 Denied Keys:
5 Unaccepted Keys:
6 Rejected Keys:
1 mkdir salt-vagrant_example
2 cd salt-vagrant_example
3 vagrant box add precise64 http://files.vagrantup.com/precise64.box
1 #The minion configuration file. Please copy the default minion configuration fil\
2 e from #/etc/salt/minion to your work directory.
3 salt/minion
4 #The minion grains
5 salt/minion.d/vagrant.conf
6 #The root folder where we will create the "top.sls" file and the other state fil\
7 es.
8 salt/roots
9 salt/roots/top.sls
10 salt/roots/apache/apache.sls
1 salt-vagrant_example
2 ├── salt
3 │ ├── minion
4 │ ├── minion.d
5 │ │ └── vagrant.conf
6 │ └── roots
7 │ ├── apache2
8 │ │ ├── apache2.conf
9 │ │ └── apache2.sls
10 │ ├── top.sls
11 └── Vagrantfile
• Mount your Salt file root (synchronize salt/roots (in the host) to /srv/salt/ (in the guest). ).
• Tell Vagrant that we are using SaltStack as a provisioning automation tool.
When we say “host” and “guest”, we are referring respectively to your machine and to
Vagrant machine.
The Vagrant configuration file (Vagrantfile) will look like this:
Real World Examples 93
1 Vagrant.configure("2") do |config|
2 ## Choose your base box
3 config.vm.box = "precise64"
4
5 ## For masterless, mount your salt file root
6 config.vm.synced_folder "salt/roots/", "/srv/salt/"
7 config.vm.synced_folder "salt/minion.d/", "/etc/salt/minion.d/"
8
9 ## Use all the defaults:
10 config.vm.provision :salt do |salt|
11
12 salt.minion_config = "salt/minion"
13 salt.run_highstate = true
14 salt.grains_config = "salt/minion.d/vagrant.conf"
15
16 end
17 end
1 cp /etc/salt/minion ./salt/minion
In this example, you will be using Salt in its masterless mode, that’s why you need to update your
minion configuration:
1 file_client: local
The Apache installation SLS file should be called by your local top.sls file.
1 # Install Apache
2 apache2:
3 pkg.installed:
4 - name: apache2
5 - file: apache2
6 # Start the Apache service
7 service.running:
8 - enable: True
9 - require:
10 - pkg: apache2
11 # Copy the configuration template to */etc/apache2/apache2.conf* and render grai\
12 ns values.
13 file.managed:
14 - name: /etc/apache2/apache2.conf
15 - source: salt://apache2/apache2.conf
16 - template: jinja
17 - require:
18 - pkg: apache2
1 LockFile ${APACHE_LOCK_DIR}/accept.lock
2 PidFile ${APACHE_PID_FILE}
3 Timeout {{ grains['apache_timeout'] }}
4 KeepAlive {{ grains['keep_alive'] }}
5 MaxKeepAliveRequests {{ grains['MaxKeepAliveRequests'] }}
6 KeepAliveTimeout {{ grains['KeepAliveTimeout'] }}
7
8 <IfModule mpm_prefork_module>
9 StartServers 5
10 MinSpareServers 5
11 MaxSpareServers 10
12 MaxClients 150
13 MaxRequestsPerChild 0
14 </IfModule>
15
16 <IfModule mpm_worker_module>
17 StartServers 2
18 MinSpareThreads 25
19 MaxSpareThreads 75
20 ThreadLimit 64
21 ThreadsPerChild 25
Real World Examples 95
22 MaxClients 150
23 MaxRequestsPerChild 0
24 </IfModule>
25
26 <IfModule mpm_event_module>
27 StartServers 2
28 MinSpareThreads 25
29 MaxSpareThreads 75
30 ThreadLimit 64
31 ThreadsPerChild 25
32 MaxClients 150
33 MaxRequestsPerChild 0
34 </IfModule>
35
36 User www-data
37 Group www-data
38 AccessFileName .htaccess
39
40 <Files ~ "^\.ht">
41 Order allow,deny
42 Deny from all
43 Satisfy all
44 </Files>
45
46 DefaultType None
47 HostnameLookups Off
48 ErrorLog ${APACHE_LOG_DIR}/error.log
49 LogLevel warn
50 Include mods-enabled/*.load
51 Include mods-enabled/*.conf
52 Include ports.conf
53 LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" v\
54 host_combined
55 LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
56 LogFormat "%h %l %u %t \"%r\" %>s %O" common
57 LogFormat "%{Referer}i -> %U" referer
58 LogFormat "%{User-agent}i" agent
59 Include conf.d/
60 Include sites-enabled/
1 Timeout {{ grains['apache_timeout'] }}
2 KeepAlive {{ grains['keep_alive'] }}
3 MaxKeepAliveRequests {{ grains['MaxKeepAliveRequests'] }}
4 KeepAliveTimeout {{ grains['KeepAliveTimeout'] }}
1 salt/minion.d/vagrant.conf
1 grains:
2
3 apache_timeout: 600
4 keep_alive: On
5 MaxKeepAliveRequests: 150
6 KeepAliveTimeout: 10
1 vagrant up
At this step, in the virtual machine, SaltStack (precisely salt-minion) will be installed and
configured (minion configuration file) and state files will be copied (into /srv/salt/ ).
Log in:
1 vagrant ssh
Now, you need to run SaltStack in your virtual machine in order to provision it :
After executing state.highstate call, Apache will be installed and configured into your Vagrant VM.
The lat
Real World Examples 97
Before moving to another example, please note that while writing this book, SaltStack was
not supported by Vagrant : The installation of Salty Vagrant was mandatory. Now there is no need
for this, SaltStack is provided directly by Vagrant. In the following tutorial, we are not using Salty
Vagrant anymore.
If you have the latest version of SaltStack, so it is sure that you will not need Salty Vagrant plugin. In
the next example, we are going to use SaltStack to automate the provisioning of a Vagrant machine.
The main goal is to create a development machine for OwnCloud.
First of all, type:
1 vagrant init
Apart from Vagrantfile, created automatically after typing the last command, create the other
necessary folders and files in order to have a similar tree:
1 ├── salt
2 │ ├── formulas
3 │ │ └── owncloud
4 │ ├── minion.yml
5 │ └── roots
6 │ └── top.sls
7 └── Vagrantfile
1 salt/formulas/owncloud/
Note that the tree should be like the next after extracting OwnCloud archive:
Real World Examples 98
1 ├── salt
2 │ ├── formulas
3 │ │ └── owncloud
4 │ │ ├── init.sls
5 │ │ ├── map.jinja
6 │ │ ├── mysql.sls
7 │ │ ├── python-mysqldb.sls
8 │ │ └── repo.sls
9 │ ├── minion.yml
10 │ └── roots
11 │ └── top.sls
12 └── Vagrantfile
In the minion configuration (minion.yml), we should configure the master to localhost and file_-
client to local:
1 master: localhost
2 file_client: local
3
4 file_roots:
5 base:
6 - /srv/salt
7 - /srv/formulas
1 base:
2 '*':
3 - owncloud
1 vagrant up
You can find all of the code in a GitHub repository31 that I created for this reason.
Use it by executing these command:
31
https://github.com/eon01/OwnCloud_Vagrant_SaltStack
Real World Examples 100
The first line of the next code will add a new job, the second part is the output (you should see
something similar to this wit “True” as “result”).
By default, the added job will not be enabled, we should add do this:
Real World Examples 101
We can now see our job when we list all of the scheduled tasks:
A last check:
Real World Examples 102
Our last test should show us that test is added at the end of the file temp every second: Success !
Now monitoring a website is not very different from what we have done in the last example. We are
just going to change the used command and capitalize on the scheduling feature because generally
monitoring needs repetitive checks.
What we are going to execute as a scheduled job is just an example. Let’s check it:
1 wget "www.eon01.com" --timeout 30 -O - 2>/dev/null | grep "dev & ops" || echo "T\
2 he site is down" | mail -s "Your site is down" [email protected]
The last command will send an email to [email protected] when the string dev & ops will not be
found: in other words, the site is down or not responding within 30 seconds (notice –timeout with
wget).
Let’s add this command the SaltStack schedule function:
Real World Examples 103
Now, the minion will send an email if the site is down and the check will be done every 10 seconds.
As I said, this is an example, you can imagine many others, like monitoring disk with df, monitoring
memory with free .. etc
I recommend to do a double check on every scheduled task:
1 * check the used resource (memory, disk ..etc) used by the *minion* after config\
2 uring the scheduled task.
3 * check the log file in : */var/log/salt/minion*
SaltStack schedule module offers other possibilities like copying one job to another :
Real World Examples 104
Deleting jobs:
Disabling a job:
This part of SaltStack is well explained, so I recommend you to see the other commands in [the offi-
cial documentation (https://docs.saltstack.com/en/latest/ref/modules/all/salt.modules.schedule.html)
Apart from the advantage of the rapidity of installation and configuration provided by
formulas, they are also maintained by an active community32 .
If you want to put again into practice the acquired knowledge through this book, this example could
help you. In my point of view and I am sure this is shared with most of you, using formulas is quite
easy but we are going to write our own states to automate the installation of Wordpress using a
LAMP stack.
Note that this example is just a fast prototype, if you want to set it up in production environment,
you should focus more on details.
Configurations
We use two Debian servers, with Apache2, MySQL-5.5, php5, the files constituting the Wordpress
and wp-cli 33 . The latter will be helpful to automate the installation and configuration of Worpdress.
Our two Debian servers are in the same subnet. The first one will host the salt-master and the other
will host the salt-minion.
Considering that the IP of the master is:
32
https://github.com/saltstack-formulas/
33
https://github.com/wp-cli/wp-cli
Real World Examples 105
1 10.10.10.41
We should start by changing the minion configuration file in order to tell salt-minion about the IP
of salt-master :
1 master: 10.10.10.41
1 file_roots:
2 base:
3 - /srv/pillar
For the grains stored in the minion, we have the possibility to use files under:
1 /etc/salt/minion.d/*.conf
1 /etc/salt/minion.d/web.conf
This file and the data stored within will be used later:
1 grains:
2 apache_timeout: 600
3 keep_alive: On
4 MaxKeepAliveRequests: 150
5 KeepAliveTimeout: 10
6 html_dir: /var/www/html
7 web_user: www-data
8 db_name: wp_db
9 blog_url: 10.10.10.41/blog
10 blog_title: DevOps Blog
11 admin_email: [email protected]
1 mysql_password: TCaXs54rVaVujKqhpEL6
2 wp_password: zBQHFEKPJuTUuJRmbNhW
1 /srv/pillar/passwords.sls
1 /srv/salt/apache2/
2 ├── apache2.conf
3 └── apache2.sls
1 apache2:
2 pkg.installed:
3 - name: apache2
1 service.running:
2 - name: apache2
3 - enable: True
1 file.managed:
2 - name: /etc/apache2/apache2.conf
3 - source: salt://apache2/apache2.conf
4 - template: jinja
5 - require:
6 - pkg: apache2
1 /etc/apache2/apache2.conf
1 salt://apache2/apache2.conf
salt:// points to the base directory declared in file_roots. In our case, the previous notation
is equivalent to the following:
1 /srv/salt/apache2/apache2.conf
1 LockFile ${APACHE_LOCK_DIR}/accept.lock
2 PidFile ${APACHE_PID_FILE}
3 Timeout {{ grains['apache_timeout'] }}
4 KeepAlive {{ grains['keep_alive'] }}
5 MaxKeepAliveRequests {{ grains['MaxKeepAliveRequests'] }}
6 KeepAliveTimeout {{ grains['KeepAliveTimeout'] }}
7
8 <IfModule mpm_prefork_module>
9 StartServers 5
10 MinSpareServers 5
Real World Examples 108
11 MaxSpareServers 10
12 MaxClients 150
13 MaxRequestsPerChild 0
14 </IfModule>
15
16 <IfModule mpm_worker_module>
17 StartServers 2
18 MinSpareThreads 25
19 MaxSpareThreads 75
20 ThreadLimit 64
21 ThreadsPerChild 25
22 MaxClients 150
23 MaxRequestsPerChild 0
24 </IfModule>
25
26 <IfModule mpm_event_module>
27 StartServers 2
28 MinSpareThreads 25
29 MaxSpareThreads 75
30 ThreadLimit 64
31 ThreadsPerChild 25
32 MaxClients 150
33 MaxRequestsPerChild 0
34 </IfModule>
35
36 User www-data
37 Group www-data
38 AccessFileName .htaccess
39
40 <Files ~ "^\.ht">
41 Order allow,deny
42 Deny from all
43 Satisfy all
44 </Files>
45
46 DefaultType None
47 HostnameLookups Off
48 ErrorLog ${APACHE_LOG_DIR}/error.log
49 LogLevel warn
50 Include mods-enabled/*.load
51 Include mods-enabled/*.conf
52 Include ports.conf
Real World Examples 109
Should not be confused the Apache2 variables like ${APACHE_LOCK_DIR} and Jinja2
variables like {{ grains[‘apache_timeout’] }}.
What we have finally is :
1 apache2:
2 pkg.installed:
3 - name: apache2
4 - file: apache2
5 service.running:
6 - enable: True
7 - require:
8 - pkg: apache2
9 file.managed:
10 - name: /etc/apache2/apache2.conf
11 - source: salt://apache2/apache2.conf
12 - template: jinja
13 - require:
14 - pkg: apache2
You may ask the question if our server was running Redhat or FreeBSD (or any other GNU/Linux
family) instead of Debian. This is why the grain os_family could be helpful in this case.
1 {% if grains['os_family']=="Debian" %}
2 [Debian specefic configuraton]
3 { % endif %}
To create a generic configuration, I recommend you to see the file map.jinja in saltstack-formula
github repository:
Real World Examples 110
43 }, merge=salt['grains.filter_by']({
44 '14.04': {
45 'confext': '.conf',
46 'default_site': '000-default.conf',
47 'default_site_ssl': 'default-ssl.conf',
48 'use_require': True,
49 },
50 '14.10': {
51 'confext': '.conf',
52 'default_site': '000-default.conf',
53 'default_site_ssl': 'default-ssl.conf',
54 'use_require': True,
55 },
56 }, grain='lsb_distrib_release', merge=salt['pillar.get']('apache:lookup'))) %}
In this case, the SLS file could use variables that adapt to many contexts :
1 /srv/salt/apache2/apache2.sls
1 backup_conf:
2 cmd.wait:
3 - name: "cp /etc/apache2/apache2.conf /etc/apache2/apache2.conf.backup"
4 - watch:
5 - pkg: apache2
1 cp /etc/apache2/apache2.conf /etc/apache2/apache2.conf.backup
1 - watch:
2 - pkg: apache2
Afterwards, we create a user for the web server. This is a recommended step for better security as
you may know. The user will have the name www-data and will be added to the www-data group.
1 user_creation:
2 group:
3 - www-data
4 user:
5 - www-data
6 - home: /var/www/html/
7 - groups:
8 - www-data
9 - require:
10 - group: www-data
1 {% if grains['os_family']=="debian" %}
2 apache2:
3 pkg.installed:
4 - name: apache2
5 - file: apache2
6 service.running:
7 - enable: True
8 - require:
9 - pkg: apache2
10 file.managed:
11 - name: /etc/apache2/apache2.conf
12 - source: salt://apache2/apache2.conf
13 - template: jinja
14 - require:
15 - pkg: apache2
16
17 backup_conf:
18 cmd.wait:
Real World Examples 113
1 /srv/salt/mysql/
2 └── mysql.sls
In the SLS file we will tell Salt to install mysql-server, php5-mysql and python-mysqldb.
1 mysql:
2 pkg.installed:
3 - name: mysql-server
4 service.running:
5 - enable: True
6 - require:
7 - pkg: mysql-server
8
9 php5-mysql:
10 pkg.installed:
11 - name: php5-mysql
12
13 python-mysqldb:
14 pkg.installed
Like SaltStack is based on Python, the package python-mysqldb will help us manage the installation,
configuration and all the operations where a Python based software should connect to a Mysql
database.
Real World Examples 114
When we want to install a package, we are not obliged to write its name in “ - name ” if the
identifier has the same name as the package. Look a the difference between mysql python-mysqldb
installation.
However, while not mandatory, it is highly recommended that you set a password for the MySQL
administrative root user. My method is to prepopulate password fields before running the install
process.
1 debconf:
2 cmd.run:
3 - name: sudo debconf-set-selections <<< "mysql-server mysql-server/root_pass\
4 word password {{ pillar['mysql_password'] }}"
5 debconf_bis:
6 cmd.run:
7 - name: sudo debconf-set-selections <<< "mysql-server mysql-server/root_pass\
8 word_again password {{ pillar['mysql_password'] }}"
The package “phpMyAdmin” is automatically installed with the MySQL server on Debian server,
just make accessible from web browser.
1 /var/www/html/phpmyadmin
exists.
Otherwise, it will create a symbolic link from the phpMyAdmin installation directory to the root
directory of websites. Since some prefer to have
1 /var/www/
1 /var/www/html/
that’s why the html_dir is a grain. This means that you can adapt your configuration according to
user’s preferences.
To create a symbolic link, SaltStack gives us the the possibility to use file.symlink function
instead of executing the system command ln -s.
1 /path/to/file:
2 file.symlink:
3 - target: /path/to/target
1 create_db:
2 cmd.run:
3 - name: mysql -u root -p{{ pillar['mysql_password'] }} -e "CREATE DATABASE I\
4 F NOT EXISTS {{ grains['db_name'] }}"
1 /etc/salt/minion.d/web.conf
1 wget https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.p\
2 l -O mt.pl; chmod +x mt.pl; perl mt.pl > mt.log
Because we can execute GNU/Linux commands directly from SaltStack, we are going to include the
last command in our configuration files:
Real World Examples 116
1 mysql_tuner:
2 cmd:
3 - run
4 - cwd: /tmp
5 - name: "wget https://raw.githubusercontent.com/major/MySQLTuner-perl/master\
6 /mysqltuner.pl -O mt.pl; perl mt.pl>mt.log"
7 - require:
8 - service: mysql
1 debconf:
2 cmd.run:
3 - name: sudo debconf-set-selections <<< "mysql-server mysql-server/root_pass\
4 word password {{ pillar['mysql_password'] }}"
5
6 debconf_bis:
7 cmd.run:
8 - name: sudo debconf-set-selections <<< "mysql-server mysql-server/root_pass\
9 word_again password {{ pillar['mysql_password'] }}"
10
11
12 mysql:
13 pkg.installed:
14 - name: mysql-server
15 service.running:
16 - enable: True
17 - require:
18 - pkg: mysql-server
19
20 php5-mysql:
21 pkg.installed:
22 - name: php5-mysql
23
24 python-mysqldb:
25 pkg.installed
26
27 {% if 0 == salt['cmd.retcode']('test -d {{ grains['html_dir'] }}/phpmyadmin') %}
28
29 sl_phpmyadmin:
30 cmd.run:
31 - name: "ln -s /usr/share/phpmyadmin {{ grains['html_dir'] }}/phpmyadmin"
Real World Examples 117
32 - require:
33 - service: mysql
34 {% endif %}
35
36 create_db:
37 cmd.run:
38 - name: mysql -u root -p{{ pillar['mysql_password'] }} -e "CREATE DATABASE I\
39 F NOT EXISTS {{ grains['db_name'] }}"
40
41 mysql_tuner:
42 cmd:
43 - run
44 - cwd: /tmp
45 - name: "wget https://raw.githubusercontent.com/major/MySQLTuner-perl/master\
46 /mysqltuner.pl -O mt.pl; perl mt.pl>mt.log"
47 - require:
48 - service: mysql
1 /srv/salt/php5/
2 └── php5.sls
1 php5:
2 pkg.installed:
3 - name: php5
4 php5-cli:
5 pkg.installed:
6 - name: php5-cli
In the last configuration, Salt is asked to install php5-cli and php5. The first is needed to run dynamic
web pages in PHP under Apache2 and the second is necessary to execute PHP commands (In our
case it will be used with wp-cli).
1 /srv/salt/wordpress/
2 └── wp-cli.sls
The second step is to tell Salt to create the directory of our Wordpress installation:
1 {{ grains['html_dir'] }}/blog:
2 file.directory:
3 - user: www-data
4 - group: www-data
5 - mode: 755
6 - makedirs: False
7 - recurse:
8 - user
9 - group
10 - mode
1 /var/www/html/blog
The last directory should be owned (for security reasons) by www-data user and the www-data
group, it will have 755 as a chmod configuration. The usage of recurse will apply this configuration
(user, group and mode) recursively.
With makedirs the directory will not be created if it is already there.
We will then download wp-cli and use it to download the Wordpress files to html_dir.
1 wp_cli_download:
2 cmd.run:
3 - cwd: {{ grains['html_dir'] }}/blog
4 - name: curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/pha\
5 r/wp-cli.phar > wp-cli.phar
6
7 wp_cli_mod:
8 cmd.run:
9 - cwd: {{ grains['html_dir'] }}/blog
10 - name: chmod +x wp-cli.phar
11
12 wp_cli_path:
13 cmd.run:
14 - cwd: {{ grains['html_dir'] }}/blog
Real World Examples 119
A Wordpress website is based on the configuration in the wp-config.php file. We will check if the
file already exists and in this case we will delete it to re-create and re-configure it:
Note that other wp-cli commands can be found in its online official documentation. By typing the
command wp, you will have an almost full control of your Wordpress website.
All commands issued by Salt must be executed with the user www-data and this is why we use:
1 user: {{ grains['web_user'] }}
1 /etc/salt/minion.d/*.conf
1 /etc/salt/minion.d/web.conf
1 grains:
2 apache_timeout: 600
3 keep_alive: On
4 MaxKeepAliveRequests: 150
5 KeepAliveTimeout: 10
6 html_dir: /var/www/html
7 web_user: www-data
8 db_name: wp_db
9 blog_url: 10.10.10.41/blog
10 blog_title: DevOps Blog
11 admin_email: [email protected]
Here is another example to use ‘Curl’ with detailed output about your infrastructure, network and
application.
First create this file:
1 echo -e \
2 “time_namelookup: %{time_namelookup}\n
3 time_connect: %{time_connect}\n
4 time_appconnect: %{time_appconnect}\n
5 time_pretransfer: %{time_pretransfer}\n
6 time_redirect: %{time_redirect}\n
7 time_starttransfer: %{time_starttransfer}\n
8 ----------\n
9 time_total: %{time_total}\n” > curl_detailed_response.conf
It is recommended to go back to the the Scheduling Monitoring Tasks Using SaltStack part of this
book.
Well, you can also imagine other scenarios to monitor where you will put in practice what you have
learned through this book.
Conclusion
Finally, you need to execute:
to execute the installation of your Wordpress blog with Apache2, PHP and Mysql. Everything will be
configured like you have set it. A good manner to do things with Salt is to debug before launching
anything especially if you are running hotfixes in a production environment:
This part of SaltStack For DevOps detailed the various steps in order to practice a concrete example
of an installation and a post-installation of a LAMP stack with a website that runs, but the security
and production environments constraints were not taken into consideration at 100%, so be careful,
test before you deploy and think before you type.
Docker Quick-Start
To run Docker, I used Ubuntu 14.04 and a 64-bit installation (required), and I followed the official
tutorial from Docker documentation. Normally you can run it on any other Ubuntu version or
distributions without problems unless your Linux Kernel is older than 3.10.
Just type the following command to be sure:
Real World Examples 122
1 uname -r
1 touch /etc/apt/sources.list.d/docker.list
then
1 apt-get update && apt-get purge lxc-docker && apt-get install docker-engine
otherwise, for the next instructions you should use the root user:
1 su
For more details about installing Docker on Ubuntu check the official documentation34 .
If you think about this, there are many ways to work with SaltStack and Docker:
• Install a masterless minion inside the Docker guest and use salt-call to have an established
remote-execution environment inside Docker.
• Do the same thing but mount your local /srv/salt/ to your guest
• Control the guest salt-minion with the host salt-master
In this book I tried to keep it stupidly simple (KISS) and I avoided unnecessary complexity, that’s
why we will consider the first scenario and then you will see that it is easy to use the second.
Let’s create the guest salt-minion:
Create your Dockerfile with your favorite code editor (vi, vim, nano ..etc) and as a basic example,
let’s put this code:
1 FROM ubuntu:14.04
2
3 MAINTAINER Aymen El Amri [email protected]
4
5 RUN apt-get update
6
7 RUN DEBIAN_FRONTEND=noninteractive apt-get install -y -q wget
8 RUN wget -O - https://repo.saltstack.com/apt/ubuntu/14.04/amd64/latest/SALTSTACK\
9 -GPG-KEY.pub | sudo apt-key add -
10 RUN echo "deb http://repo.saltstack.com/apt/ubuntu/14.04/amd64/latest trusty mai\
11 n"|tee -a /etc/apt/sources.list
12
13 RUN apt-get update
14 RUN DEBIAN_FRONTEND=noninteractive apt-get install -y -q salt-minion
15
16 ADD minion /etc/salt/
17
18 RUN service salt-minion start
19
20 EXPOSE 8080
21 CMD /bin/bash
Real World Examples 124
This Dockerfile will install Ubuntu, salt-minion and will copy the minion configuration file
to /etc/minion on the Docker machine before starting salt-minion.
Make sure your are in the same directory containing Dockerfile and build your image that we will
call docker-salt:
The last commands will run the image called docker-salt on a container that we called
docker-salt-container. The Docker guest machine will have docker-salt-host as a host name (/etc/host-
name).
Make sure your container is up.
When you type
1 docker ps
In general CONTAINER ID(in our case fa4112583e73) could be used also when working with
Docker.
To test if everything is ok, let’s execute salt-call inside the container.
1 local:
2 ----------
3 SSDs:
4 biosreleasedate:
5 /dev/mem: No such file or directory
6 biosversion:
7 /dev/mem: No such file or directory
8 cpu_flags:
9 - fpu
10 - vme
11 - de
12 - pse
13 - tsc
14 - msr
15 - pae
16 - mce
17 - cx8
18 - apic
19 - sep
20 - mtrr
21 - pge
22 - mca
23 - cmov
24 - pat
25 - pse36
26 - clflush
27 - dts
28 - acpi
29 - mmx
30 - fxsr
31 - sse
32 - sse2
33 - ss
34 - ht
35 - tm
36 - pbe
37 - syscall
38 - nx
Real World Examples 126
39 - pdpe1gb
40 - rdtscp
41 - lm
42 - constant_tsc
43 - arch_perfmon
44 - pebs
45 - bts
46 - rep_good
47 - nopl
48 - xtopology
49 - nonstop_tsc
50 - aperfmperf
51 - eagerfpu
52 - pni
53 - pclmulqdq
54 - dtes64
55 - monitor
56 - ds_cpl
57 - vmx
58 - est
59 - tm2
60 - ssse3
61 - fma
62 - cx16
63 - xtpr
64 - pdcm
65 - pcid
66 - sse4_1
67 - sse4_2
68 - movbe
69 - popcnt
70 - tsc_deadline_timer
71 - aes
72 - xsave
73 - avx
74 - f16c
75 - rdrand
76 - lahf_lm
77 - abm
78 - ida
79 - arat
80 - epb
Real World Examples 127
81 - xsaveopt
82 - pln
83 - pts
84 - dtherm
85 - tpr_shadow
86 - vnmi
87 - flexpriority
88 - ept
89 - vpid
90 - fsgsbase
91 - tsc_adjust
92 - bmi1
93 - avx2
94 - smep
95 - bmi2
96 - erms
97 - invpcid
98 cpu_model:
99 Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz
100 cpuarch:
101 x86_64
102 domain:
103 fqdn:
104 docker-salt-host
105 fqdn_ip4:
106 - 172.17.0.10
107 fqdn_ip6:
108 gpus:
109 host:
110 docker-salt-host
111 hwaddr_interfaces:
112 ----------
113 eth0:
114 02:42:ac:11:00:0a
115 lo:
116 00:00:00:00:00:00
117 id:
118 docker-salt-host
119 init:
120 unknown
121 ip4_interfaces:
122 ----------
Real World Examples 128
123 eth0:
124 - 172.17.0.10
125 lo:
126 - 127.0.0.1
127 ip6_interfaces:
128 ----------
129 eth0:
130 - fe80::42:acff:fe11:a
131 lo:
132 - ::1
133 ip_interfaces:
134 ----------
135 eth0:
136 - 172.17.0.10
137 - fe80::42:acff:fe11:a
138 lo:
139 - 127.0.0.1
140 - ::1
141 ipv4:
142 - 127.0.0.1
143 - 172.17.0.10
144 ipv6:
145 - ::1
146 - fe80::42:acff:fe11:a
147 kernel:
148 Linux
149 kernelrelease:
150 3.16.0-38-generic
151 locale_info:
152 ----------
153 defaultencoding:
154 None
155 defaultlanguage:
156 None
157 detectedencoding:
158 ANSI_X3.4-1968
159 localhost:
160 docker-salt-host
161 lsb_distrib_codename:
162 trusty
163 lsb_distrib_description:
164 Ubuntu 14.04.3 LTS
Real World Examples 129
165 lsb_distrib_id:
166 Ubuntu
167 lsb_distrib_release:
168 14.04
169 manufacturer:
170 /dev/mem: No such file or directory
171 master:
172 salt
173 mdadm:
174 mem_total:
175 7636
176 nodename:
177 docker-salt-host
178 num_cpus:
179 4
180 num_gpus:
181 0
182 os:
183 Ubuntu
184 os_family:
185 Debian
186 osarch:
187 amd64
188 oscodename:
189 trusty
190 osfinger:
191 Ubuntu-14.04
192 osfullname:
193 Ubuntu
194 osrelease:
195 14.04
196 osrelease_info:
197 - 14
198 - 4
199 path:
200 /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
201 productname:
202 /dev/mem: No such file or directory
203 ps:
204 ps -efHww
205 pythonexecutable:
206 /usr/bin/python
Real World Examples 130
207 pythonpath:
208 - /usr/bin
209 - /usr/lib/python2.7
210 - /usr/lib/python2.7/plat-x86_64-linux-gnu
211 - /usr/lib/python2.7/lib-tk
212 - /usr/lib/python2.7/lib-old
213 - /usr/lib/python2.7/lib-dynload
214 - /usr/local/lib/python2.7/dist-packages
215 - /usr/lib/python2.7/dist-packages
216 pythonversion:
217 - 2
218 - 7
219 - 6
220 - final
221 - 0
222 saltpath:
223 /usr/lib/python2.7/dist-packages/salt
224 saltversion:
225 2015.8.3
226 saltversioninfo:
227 - 2015
228 - 8
229 - 3
230 - 0
231 serialnumber:
232 /dev/mem: No such file or directory
233 server_id:
234 324894828
235 shell:
236 /bin/sh
237 virtual:
238 physical
239 virtual_subtype:
240 Docker
241 zmqversion:
242 4.0.4
1 FROM ubuntu:14.04
2
3 MAINTAINER Aymen El Amri [email protected]
4
5 RUN apt-get update
6
7 RUN DEBIAN_FRONTEND=noninteractive apt-get install -y -q wget
8 RUN wget -O - https://repo.saltstack.com/apt/ubuntu/14.04/amd64/latest/SALTSTACK\
9 -GPG-KEY.pub | sudo apt-key add -
10 RUN echo "deb http://repo.saltstack.com/apt/ubuntu/14.04/amd64/latest trusty mai\
11 n"|tee -a /etc/apt/sources.list
12
13 RUN apt-get update
14 RUN DEBIAN_FRONTEND=noninteractive apt-get install -y -q salt-minion
15
16 RUN apt-get install python-dev -y
17 RUN wget https://bootstrap.pypa.io/get-pip.py
18 RUN python get-pip.py
19 RUN /usr/bin/yes | pip install psutil
20
21 ADD minion /etc/salt/
22
23 RUN service salt-minion start
24
25 EXPOSE 8080
26 CMD /bin/bash
Build and run then you can get monitoring information easily:
• Boot time:
Output
1 {
2 "local": 1450992213
3 }
Output
1 {
2 "local": 25.0
3 }
• Disk I/O:
Output
1 {
2 "local": {
3 "read_time": 819864,
4 "write_bytes": 3998978048,
5 "read_bytes": 1336084480,
6 "write_time": 2441316,
7 "read_count": 54610,
8 "write_count": 146160
9 }
10 }
• disk_partition_usage
• cpu_times
• disk_partition_usage
• disk_partitions
• disk_usage
• get_pid_list
• get_users
• network_io_counters
• num_cpus
• proc_info <pid>
• swap_memory
• top
• total_physical_memory
• virtual_memory
Real World Examples 133
1 docker ps -l
Note that if you want to stop your container you should use the following command:
Stopping a container is not removing it. If you would like to remove it type:
1 docker rm docker-salt-container
Now we want to install packages inside the running container using SaltStack. Well, it is simple,
let’s install vim:
Output:
Please note that if the container exits you will lose data: vim will not be installed. You
should commit changes to your container. Type the next command:
This will commit the changes happened in the container with the mentioned id to the image called
docker-salt. Of course replace 5ac064ad5d91 by you container id (get it with docker ps command).
Note that docker-salt is the image name.
Let’s verify if everything is committed to the image, we stop the image, remove it, run it again and
get vim version. If we will have the version then the package is installed (even after removing the
container) and if we are not going to see a version, this certainly means that vim is not installed.
Linode Pricing
Follow those steps to set up a Linode key that will be used with Salt Cloud:
• Go to Linode35
• Create a free account
• After confirming the account from your email, log in
• Click on My Profile
• Re-authenticate to unlock the settings
• Click on API keys
• Generate a key (I gave it the name salt_cloud_demo)
Save the generated key on your local machine since you will not be able to view it again
on the web manager.
Please note that I will use this file structure when I work with Salt Cloud:
35
https://manager.linode.com/session/signup
Real World Examples 137
1 /etc/salt/
2 ├── cloud
3 ├── cloud.conf.d/
4 ├── cloud.deploy.d/
5 ├── cloud.maps.d/
6 ├── cloud.profiles.d/
1 /etc/salt/cloud.providers.d/linode.providers.conf
1 linode-config:
2 provider: linode
3 apikey: O61KWcOQ
4 password: *************
5 ssh_pubkey: ssh-rsa xzt4Gg3WjHZgFvRU8EMUyVSxwwilKogg0TK0BkbY0gq+IADKMxhuq9yW4O\
6 nxlWcO+n2VkdaFZlAIh+/Sll7HdfmBclvGbw== root@eonSpider
7 ssh_key_file: /root/.ssh/id_rsa
As you can see in the configuration, we are using the apikey provided by Linode with the password.
You should also configure the ssh_pubkey that you are using on your machine (in this example, I
am executing Salt Cloud command from my local machine on a remote Linode server). If you do
not have a public key, generate a key pair (public/private) using this command:
1 ssh-keygen
1 linode-config:
2 ----------
3 linode:
4 ----------
5 Linode 1024:
6 ----------
7 bandwidth:
8 2000
9 disk:
10 24576
11 driver:
12 extra:
13 ----------
14 get_uuid:
15 id:
16 1
17 name:
18 Linode 1024
19 price:
20 10.0
21 ram:
22 1024
23 uuid:
24 03e18728ce4629e2ac07c9cbb48afffb8cb499c4
25 Linode 16384:
26 ----------
27 bandwidth:
28 16000
29 disk:
30 393216
31 driver:
32 extra:
33 ----------
34 get_uuid:
35 id:
36 7
37 name:
38 Linode 16384
39 price:
40 160.0
41 ram:
42 16384
Real World Examples 139
43 uuid:
44 a93683ef7f41a3a7ccb14777d95f03bb6b046ec6
45 Linode 2048:
46 ----------
47 bandwidth:
48 3000
49 disk:
50 49152
51 driver:
52 extra:
53 ----------
54 get_uuid:
55 id:
56 2
57 name:
58 Linode 2048
59 price:
60 20.0
61 ram:
62 2048
63 uuid:
64 5a1c572b48a26aa78799facaad413d901a73a673
65 Linode 32768:
66 ----------
67 bandwidth:
68 20000
69 disk:
70 786432
71 driver:
72 extra:
73 ----------
74 get_uuid:
75 id:
76 8
77 name:
78 Linode 32768
79 price:
80 320.0
81 ram:
82 32768
83 uuid:
84 b3586dbcfcb67d3aba9093c472b852bc860846a6
Real World Examples 140
85 Linode 4096:
86 ----------
87 bandwidth:
88 4000
89 disk:
90 98304
91 driver:
92 extra:
93 ----------
94 get_uuid:
95 id:
96 4
97 name:
98 Linode 4096
99 price:
100 40.0
101 ram:
102 4096
103 uuid:
104 c08ae025461ecc5f845ede0d95e1cb3e61a8f13b
105 Linode 49152:
106 ----------
107 bandwidth:
108 20000
109 disk:
110 1179648
111 driver:
112 extra:
113 ----------
114 get_uuid:
115 id:
116 9
117 name:
118 Linode 49152
119 price:
120 480.0
121 ram:
122 49152
123 uuid:
124 954b931001923ab473861c7d427863c3b1df8de7
125 Linode 65536:
126 ----------
Real World Examples 141
127 bandwidth:
128 20000
129 disk:
130 1572864
131 driver:
132 extra:
133 ----------
134 get_uuid:
135 id:
136 10
137 name:
138 Linode 65536
139 price:
140 640.0
141 ram:
142 65536
143 uuid:
144 ca78fb5bfed412bd33d434dbd06340de377423c8
145 Linode 8192:
146 ----------
147 bandwidth:
148 8000
149 disk:
150 196608
151 driver:
152 extra:
153 ----------
154 get_uuid:
155 id:
156 6
157 name:
158 Linode 8192
159 price:
160 80.0
161 ram:
162 8192
163 uuid:
164 9501e716d5ee4502bbeaecf0d4daa2fe2862200f
165 Linode 98304:
166 ----------
167 bandwidth:
168 20000
Real World Examples 142
169 disk:
170 1966080
171 driver:
172 extra:
173 ----------
174 get_uuid:
175 id:
176 12
177 name:
178 Linode 98304
179 price:
180 960.0
181 ram:
182 98304
183 uuid:
184 d0dc3fdd2a6cdf54aff0e0213e493dff6d80e5cf
You will see the images that you can use on your Linode server:
1 linode-config:
2 ----------
3 linode:
4 ----------
5 Arch Linux 2015.02:
6 ----------
7 driver:
8 extra:
9 ----------
10 64bit:
11 1
12 pvops:
13 1
14 get_uuid:
15 id:
16 138
17 name:
Real World Examples 143
60 pvops:
61 1
62 get_uuid:
63 id:
64 127
65 name:
66 CentOS 6.5
67 uuid:
68 f12d308795a507cc73a3cf5f7aacdf2d86fbcf4a
69 CentOS 7:
70 ----------
71 driver:
72 extra:
73 ----------
74 64bit:
75 1
76 pvops:
77 1
78 get_uuid:
79 id:
80 129
81 name:
82 CentOS 7
83 uuid:
84 87e25228eddb0c32db87b502b3bf4a9aaf64d15c
85 Debian 6:
86 ----------
87 driver:
88 extra:
89 ----------
90 64bit:
91 1
92 pvops:
93 1
94 get_uuid:
95 id:
96 78
97 name:
98 Debian 6
99 uuid:
100 87d8bc1d0236a365f444138cf4158e404c121e75
101 Debian 7:
Real World Examples 145
102 ----------
103 driver:
104 extra:
105 ----------
106 64bit:
107 1
108 pvops:
109 1
110 get_uuid:
111 id:
112 130
113 name:
114 Debian 7
115 uuid:
116 2619a13cd4e5bc5f10fa04b617231c7085167e6a
117 Debian 8.1:
118 ----------
119 driver:
120 extra:
121 ----------
122 64bit:
123 1
124 pvops:
125 1
126 get_uuid:
127 id:
128 140
129 name:
130 Debian 8.1
131 uuid:
132 57ccf3e88161c13987b6f98976c7bdf791237dbf
133 Fedora 20:
134 ----------
135 driver:
136 extra:
137 ----------
138 64bit:
139 1
140 pvops:
141 1
142 get_uuid:
143 id:
Real World Examples 146
144 122
145 name:
146 Fedora 20
147 uuid:
148 b69c15db35d7b987ecf8d6d206c47b22b97e2e81
149 Fedora 21:
150 ----------
151 driver:
152 extra:
153 ----------
154 64bit:
155 1
156 pvops:
157 1
158 get_uuid:
159 id:
160 134
161 name:
162 Fedora 21
163 uuid:
164 35a3258a19bbaf60f137ffbefcf06475e145a0d8
165 Fedora 22:
166 ----------
167 driver:
168 extra:
169 ----------
170 64bit:
171 1
172 pvops:
173 1
174 get_uuid:
175 id:
176 141
177 name:
178 Fedora 22
179 uuid:
180 fd93443f93e0248202f9f2e2dad0935697537f20
181 Gentoo 2013-11-26:
182 ----------
183 driver:
184 extra:
185 ----------
Real World Examples 147
186 64bit:
187 1
188 pvops:
189 1
190 get_uuid:
191 id:
192 118
193 name:
194 Gentoo 2013-11-26
195 uuid:
196 aec7ded0f24b21dc761d3eacaa2f696c07067a56
197 Gentoo 2014.12:
198 ----------
199 driver:
200 extra:
201 ----------
202 64bit:
203 1
204 pvops:
205 1
206 get_uuid:
207 id:
208 137
209 name:
210 Gentoo 2014.12
211 uuid:
212 a69dd4adb372e2b1599c4fab23994df70ca3253f
213 Slackware 13.37:
214 ----------
215 driver:
216 extra:
217 ----------
218 64bit:
219 1
220 pvops:
221 1
222 get_uuid:
223 id:
224 87
225 name:
226 Slackware 13.37
227 uuid:
Real World Examples 148
228 8ad9944af892c7a355f43062b0056652e4aea626
229 Slackware 13.37 32bit:
230 ----------
231 driver:
232 extra:
233 ----------
234 64bit:
235 0
236 pvops:
237 1
238 get_uuid:
239 id:
240 86
241 name:
242 Slackware 13.37 32bit
243 uuid:
244 83c081a5c8d2355942b6cd1b03efee145559c546
245 Slackware 14.1:
246 ----------
247 driver:
248 extra:
249 ----------
250 64bit:
251 1
252 pvops:
253 1
254 get_uuid:
255 id:
256 117
257 name:
258 Slackware 14.1
259 uuid:
260 076e7f332f204bf9ae08896bf3d10a2e464a054b
261 Ubuntu 12.04 LTS:
262 ----------
263 driver:
264 extra:
265 ----------
266 64bit:
267 1
268 pvops:
269 1
Real World Examples 149
270 get_uuid:
271 id:
272 126
273 name:
274 Ubuntu 12.04 LTS
275 uuid:
276 f5cadba59311f026e739794ae454d04228323aa9
277 Ubuntu 14.04 LTS:
278 ----------
279 driver:
280 extra:
281 ----------
282 64bit:
283 1
284 pvops:
285 1
286 get_uuid:
287 id:
288 124
289 name:
290 Ubuntu 14.04 LTS
291 uuid:
292 18be6ebe9bb4f9a818f95a522ac213cfdf295e84
293 Ubuntu 15.04:
294 ----------
295 driver:
296 extra:
297 ----------
298 64bit:
299 1
300 pvops:
301 1
302 get_uuid:
303 id:
304 139
305 name:
306 Ubuntu 15.04
307 uuid:
308 28c371ac941cf095abd16c29591d271d2417a925
309 Ubuntu 15.10:
310 ----------
311 driver:
Real World Examples 150
312 extra:
313 ----------
314 64bit:
315 1
316 pvops:
317 1
318 get_uuid:
319 id:
320 144
321 name:
322 Ubuntu 15.10
323 uuid:
324 e9884caca236aa493211b58cecfe5513f1f949fc
325 openSUSE 13.1:
326 ----------
327 driver:
328 extra:
329 ----------
330 64bit:
331 1
332 pvops:
333 1
334 get_uuid:
335 id:
336 120
337 name:
338 openSUSE 13.1
339 uuid:
340 e676f73133eb146eafb0df9218ef6520f5b01b56
341 openSUSE 13.2:
342 ----------
343 driver:
344 extra:
345 ----------
346 64bit:
347 1
348 pvops:
349 1
350 get_uuid:
351 id:
352 135
353 name:
Real World Examples 151
You can request other information like the different location that you can choose when deploying a
new Linode machine:
1 linode-config:
2 ----------
3 linode:
4 ----------
5 Atlanta, GA, USA:
6 ----------
7 country:
8 US
9 driver:
10 id:
11 4
12 name:
13 Atlanta, GA, USA
14 Dallas, TX, USA:
15 ----------
16 country:
17 US
18 driver:
19 id:
20 2
21 name:
22 Dallas, TX, USA
23 Frankfurt, DE:
24 ----------
25 country:
26 ??
27 driver:
28 id:
29 10
30 name:
31 Frankfurt, DE
Real World Examples 152
74 8
75 name:
76 Tokyo, JP
Salt Cloud offers many others possibilities to interface with Linode API, the following list of
functionalities can be used:
1 salt.cloud.clouds.linode.avail_images(call=None)
2 salt.cloud.clouds.linode.avail_locations(call=None)
3 salt.cloud.clouds.linode.avail_sizes(call=None)
4 salt.cloud.clouds.linode.boot(name=None, kwargs=None, call=None)
5 salt.cloud.clouds.linode.clone(kwargs=None, call=None)
6 salt.cloud.clouds.linode.create(vm_)
7 salt.cloud.clouds.linode.create_config(kwargs=None, call=None)
8 salt.cloud.clouds.linode.create_disk_from_distro(vm_, linode_id, swap_size=None)
9 salt.cloud.clouds.linode.create_private_ip(vm_, linode_id)
10 salt.cloud.clouds.linode.create_swap_disk(vm_, linode_id, swap_size=None)
11 salt.cloud.clouds.linode.destroy(name, call=None)
12 salt.cloud.clouds.linode.get_config_id(kwargs=None, call=None)
13 salt.cloud.clouds.linode.get_configured_provider()
14 salt.cloud.clouds.linode.get_datacenter_id(location)
15 salt.cloud.clouds.linode.get_disk_size(vm_, swap, linode_id)
16 salt.cloud.clouds.linode.get_distribution_id(vm_)
17 salt.cloud.clouds.linode.get_ips(linode_id=None)
18 salt.cloud.clouds.linode.get_linode(kwargs=None, call=None)
19 salt.cloud.clouds.linode.get_linode_id_from_name(name)
20 salt.cloud.clouds.linode.get_password(vm_)
21 salt.cloud.clouds.linode.get_plan_id(kwargs=None, call=None)
22 salt.cloud.clouds.linode.get_private_ip(vm_)
23 salt.cloud.clouds.linode.get_pub_key(vm_)
24 salt.cloud.clouds.linode.get_swap_size(vm_)
25 salt.cloud.clouds.linode.get_vm_size(vm_)
26 salt.cloud.clouds.linode.list_nodes(call=None)
27 salt.cloud.clouds.linode.list_nodes_full(call=None)
28 salt.cloud.clouds.linode.list_nodes_min(call=None)
29 salt.cloud.clouds.linode.list_nodes_select(call=None)
30 salt.cloud.clouds.linode.reboot(name, call=None)
31 salt.cloud.clouds.linode.show_instance(name, call=None)
32 salt.cloud.clouds.linode.show_pricing(kwargs=None, call=None)
33 salt.cloud.clouds.linode.start(name, call=None)
34 salt.cloud.clouds.linode.stop(name, call=None)
35 salt.cloud.clouds.linode.update_linode(linode_id, update_args=None)
Real World Examples 154
You can find more details about this on the Salt Linode documentation page36 .
To configure the master of the new minion machine you can just put your IP. To get your
IP without leaving your terminal, you can use the following command: curl icanhazip.com.
According to our description below, the Salt Cloud profile file will be similar to the next one:
1 ubuntu_linode:
2 provider: linode-config
3 size: Linode 1024
4 image: Ubuntu 14.04 LTS
5 location: Frankfurt, DE
6 minion:
7 master: 20a1:e35:9beb:6a00:2293:5229:2400:a57f # This is an IP v6.
8 grains:
9 role: testing_server
36
https://docs.saltstack.com/en/latest/ref/clouds/all/salt.cloud.clouds.linode.html
Real World Examples 155
1 testing_server:
2 ----------
3 _uuid:
4 None
5 deployed:
6 True
7 driver:
8 extra:
9 ----------
10 ALERT_BWIN_ENABLED:
11 1
12 ALERT_BWIN_THRESHOLD:
13 10
14 ALERT_BWOUT_ENABLED:
15 1
16 ALERT_BWOUT_THRESHOLD:
17 10
18 ALERT_BWQUOTA_ENABLED:
19 1
20 ALERT_BWQUOTA_THRESHOLD:
21 80
22 ALERT_CPU_ENABLED:
Real World Examples 158
23 1
24 ALERT_CPU_THRESHOLD:
25 90
26 ALERT_DISKIO_ENABLED:
27 1
28 ALERT_DISKIO_THRESHOLD:
29 10000
30 BACKUPSENABLED:
31 0
32 BACKUPWEEKLYDAY:
33 0
34 BACKUPWINDOW:
35 0
36 CREATE_DT:
37 2016-02-25 13:49:49.0
38 DATACENTERID:
39 10
40 DISTRIBUTIONVENDOR:
41 Ubuntu
42 ISKVM:
43 1
44 ISXEN:
45 0
46 LABEL:
47 testing_server
48 LINODEID:
49 1682323
50 LPM_DISPLAYGROUP:
51 PLANID:
52 1
53 STATUS:
54 0
55 TOTALHD:
56 24576
57 TOTALRAM:
58 1024
59 TOTALXFER:
60 2000
61 WATCHDOG:
62 1
63 id:
64 1682323
Real World Examples 159
65 image:
66 None
67 name:
68 testing_server
69 private_ips:
70 public_ips:
71 - 1.1.1.1
72 size:
73 None
74 state:
75 3
The result:
1 testing_server:
2 True
1 ubuntu_linode_test:
2 provider: linode-config
3 size: Linode 1024
4 image: Ubuntu 14.04 LTS
5 location: Frankfurt, DE
6 minion:
7 master: 92.169.23.1
8 grains:
9 role: testing_server
10
11 ubuntu_linode_production:
12 provider: linode-config
13 size: Linode 1024
14 image: Ubuntu 14.04 LTS
15 location: Frankfurt, DE
16 minion:
Real World Examples 160
17 master: 92.169.23.1
18 grains:
19 role: production_server
You can also create two separate files ubuntu_linode_test and ubuntu_linode_production.
To create multiple Linode machines having the same profile, you can just simply type:
Note that you can use the -P to create the two machines in parallel.
1 /etc/salt/cloud.maps.d/
Imagine we had already set several profiles with Linode as a cloud provider. - linode_1024 - linode_-
2048 - linode_4096
and we would like to create three environments: - Developement - Staging - Production
and in every environment we need 3 servers: - Nginx - Mysql - Redis
This map file will simplify the creation of this infrastructure:
1 linode_1024:
2 - redis_dev
3 - redis_staging
4 - redis_production
5 linode_2048:
6 - nginx_dev
7 - nginx_staging
8 - nginx_production
9 linode_4096:
10 - mysql_dev
11 - mysql_staging
12 - mysql_production
1 /etc/salt/cloud.maps.d/my_infrastructure.maps.conf
1 --out-file
1 salt-cloud -d minion_server
To reboot it:
To stop it:
1 salt-cloud --query
To clone a VM:
Real World Examples 162
You have probably noticed that sometimes with use -a and sometimes -f. The difference is that:
1 all providers:
2 - reboot
3 ec2:
4 - start
5 - stop
6 joyent:
7 - stop
8 linode:
9 - start
10 - stop
Functions are more specific to each provider (or driver) and need specific names due to the difference
between Cloud providers APIs, but there are three universal functions:
• list_nodes: To get general information about the instances for a given provider
• list_nodes_full: Same thing as list_nodes but with more information
• list_nodes_select: Returns select information about the instances for the given provider
This is a complete list of what you can do with Linode Rest API :
Real World Examples 163
1 salt.cloud.clouds.linode.avail_images(call=None)
2 salt.cloud.clouds.linode.avail_locations(call=None)
3 salt.cloud.clouds.linode.avail_sizes(call=None)
4 salt.cloud.clouds.linode.boot(name=None, kwargs=None, call=None)
5 salt.cloud.clouds.linode.clone(kwargs=None, call=None)
6 salt.cloud.clouds.linode.create(vm_)
7 salt.cloud.clouds.linode.create_config(kwargs=None, call=None)
8 salt.cloud.clouds.linode.create_disk_from_distro(vm_, linode_id, swap_size=None)
9 salt.cloud.clouds.linode.create_private_ip(vm_, linode_id)
10 salt.cloud.clouds.linode.create_swap_disk(vm_, linode_id, swap_size=None)
11 salt.cloud.clouds.linode.destroy(name, call=None)
12 salt.cloud.clouds.linode.get_config_id(kwargs=None, call=None)
13 salt.cloud.clouds.linode.get_configured_provider()
14 salt.cloud.clouds.linode.get_datacenter_id(location)
15 salt.cloud.clouds.linode.get_disk_size(vm_, swap, linode_id)
16 salt.cloud.clouds.linode.get_distribution_id(vm_)
17 salt.cloud.clouds.linode.get_ips(linode_id=None)
18 salt.cloud.clouds.linode.get_linode(kwargs=None, call=None)
19 salt.cloud.clouds.linode.get_linode_id_from_name(name)
20 salt.cloud.clouds.linode.get_password(vm_)
21 salt.cloud.clouds.linode.get_plan_id(kwargs=None, call=None)
22 salt.cloud.clouds.linode.get_private_ip(vm_)
23 salt.cloud.clouds.linode.get_pub_key(vm_)
24 salt.cloud.clouds.linode.get_swap_size(vm_)
25 salt.cloud.clouds.linode.get_vm_size(vm_)
26 salt.cloud.clouds.linode.list_nodes(call=None)
27 salt.cloud.clouds.linode.list_nodes_full(call=None)
28 salt.cloud.clouds.linode.list_nodes_min(call=None)
29 salt.cloud.clouds.linode.list_nodes_select(call=None)
30 salt.cloud.clouds.linode.reboot(name, call=None)
31 salt.cloud.clouds.linode.show_instance(name, call=None)
32 salt.cloud.clouds.linode.show_pricing(kwargs=None, call=None)
33 salt.cloud.clouds.linode.start(name, call=None)
34 salt.cloud.clouds.linode.stop(name, call=None)
35 salt.cloud.clouds.linode.update_linode(linode_id, update_args=None)
1 https://eu-west-1.console.aws.amazon.com/ec2/v2/home?region=eu-west-1#KeyPairs:s\
2 ort=keyName
If you are using a different region, change the eu-west-1 by the region you are considering. In the
next example we are using us-east-1 as the region:
1 https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#KeyPairs:sort=keyName
To check which key name corresponds to the used key pair, use this command (after installing aws
kit):
1 ec2-fingerprint-key kp.pem
This will help you match your key pair to the fingerprint and allows you to check the name of your
key.
In this example our key is called kp and it is located under:
1 /etc/salt/kp.pem
Now go and get your security credentials for accessing your ec2 instances, you can find the id here:
1 https://console.aws.amazon.com/iam/home?#security_credential
You can not get your key from there, but normally you would have kept this in a secret place, if you
lost this, you should generate two other key pairs.
You should also know other things like the name of the security group you want to use and your
ssh_username:
Another thing to set up is the ssh_interface that could have two different values:
1 ec2-private:
2
3 # Set up the location of the salt master
4 minion:
5 master: saltmaster.myhost.com
6
7 # Set up grains information, which will be common for all nodes using this pro\
8 vider
9 grains:
10 env: test
11
12 # Specify whether to use public or private IP for deploy script.
13 ssh_interface: private_ips
14
15
16 # Set the EC2 access credentials
17 id: 'use-instance-role-credentials'
18 key: 'use-instance-role-credentials'
19
20 # Make sure this key is owned by root with permissions 0400.
21 private_key: /etc/salt/test_key.pem
22 keyname: test_key
23
24 securitygroup: default
25
26 # This is optional but you can set up your default region and availability zon\
27 e (optional)
28 location: eu-west-1
29 availability_zone: eu-west-1c
30
31 # Salt Cloud will use this user name to deploy, it depends on your AMI.
Real World Examples 166
1 ec2-public:
2
3 minion:
4 master: saltmaster.myhost.com
5
6 ssh_interface: public_ips
7
8 id: 'use-instance-role-credentials'
9 key: 'use-instance-role-credentials'
10
11 private_key: /etc/salt/test_key.pem
12 keyname: test_key
13
14 securitygroup: default
15
16 location: eu-west-1
17 availability_zone: eu-west-1c
18
19 ssh_username: ubuntu
20
21 iam_profile: 'my other profile name'
22
23 provider: ec2
• Previously, the suggested provider for aws ec2 was the aws provider. This has been deprecated
in favor of the ec2 provider.
Real World Examples 167
• The provider parameter in cloud provider definitions was renamed to driver (since the version
2015.8.0).
1 provider: ec2-private
2 image: ami-a609b6d5
3 size: t2.micro
4 ssh_username: ubuntu
1 volumes:
2 - { size: 10, device: /dev/sdf }
Suppose we want to add two other volumes while choosing the iops (Input/Output per second), we
could add a similar configuration to the next one:
1 volumes:
2 - { size: 10, device: /dev/sdf }
3 - { size: 300, device: /dev/sdg, type: io1, iops: 3000 }
4 - { size: 300, device: /dev/sdh, type: io1, iops: 3000 }
Note that to use an EBS optimised ec2 instance we should declare it:
1 ebs_optimized: True
We can also add tags to our new instance and it will be applied to all ec2 instances created using
this profile:
1 sync_after_install: grains
One thing that I usually automate is setting my own configurations, like the .vimrc file, you can
automate things like this by adding a script that will be executed:
1 script: /etc/salt/cloud.deploy.d/configure_vim.sh
Network configuration is also accessible using Salt Cloud, here is an example where the primary
IP address is the private address and the ec2 instance will have a public IP (not an elastic IP) with
subnet id and a security group id:
1 network_interfaces:
2 - DeviceIndex: 0
3 PrivateIpAddresses:
4 - Primary: True
5 AssociatePublicIpAddress: True
6 SubnetId: subnet-142f4bdd
7 SecurityGroupId:
8 - sg-750af531
1 allocate_new_eips: True
1 del_root_vol_on_destroy: True
When a machine is terminated, we want to delete all not-root EBS volumes for an instance:
1 del_all_vol_on_destroy: True
1 base_ec2_private:
2 provider: ec2-private
3 image: ami-a609b6d5
4 size: t2.micro
5 ssh_username: ubuntu
6
7 volumes:
8 - { size: 10, device: /dev/sdf }
9 - { size: 300, device: /dev/sdg, type: io1, iops: 3000 }
10 - { size: 300, device: /dev/sdh, type: io1, iops: 3000 }
11
12 tag: {'env': 'test', 'role': 'redis'}
13
14 sync_after_install: grains
15
16 script: /etc/salt/cloud.deploy.d/configure_vim.sh
17
18 network_interfaces:
19 - DeviceIndex: 0
20 PrivateIpAddresses:
21 - Primary: True
22 #auto assign public ip (not EIP)
23 AssociatePublicIpAddress: True
24 SubnetId: subnet-813d4bbf
25 SecurityGroupId:
26 - sg-750af531
27
28 del_root_vol_on_destroy: True
29 del_all_vol_on_destroy: True
When we want to create a similar profile to the last one but we would like to change one or two
options, we can use extends like in the following example:
1 base_ec2_private:
2 provider: ec2-private
3 image: ami-a609b6d5
4 size: t2.micro
5 ssh_username: ubuntu
6
7 volumes:
8 - { size: 10, device: /dev/sdf }
9 - { size: 300, device: /dev/sdg, type: io1, iops: 3000 }
Real World Examples 170
The last example was just provided to help you understand the use of extends, it was not
tested.
Like we have done when we saw how to use Salt Maps with Linode, nothing is different from using
it with aws:
Real World Examples 171
1 ec2_private:
2 - redis
3 - mysql
4
5 ec2_public:
6 - web_1
7 - web_2
1 salt-cloud -m /etc/salt/cloud.map.app -P
Salt Cloud allows getting, setting and deleting tags after launching the ec2 instance using the instance
name (or the instance id):
To enable termination protection, Salt Cloud can be used like in the following command:
using Salt Cloud from command line allows adding volumes and specific configurations like
choosing a snapshot to create a volume:
Creating a simple volume in a specific zone:
Adding size:
Real World Examples 172
Choosing a snapshot:
This is basically a small project that I started on Github37 that contains some basic SaltStack
commands, may be the most used ones and it is suitable for people who want to start learning
practical examples quickly. The Github repository is public and everyone can contribute to it.
37
https://github.com/eon01/SaltStackCheatSheet
SaltStack Cheat Sheet 174
Debugging
1 # Debugging the master
2 salt-master -l debug
3
4 # Debugging the minion
5 salt-minion -l debug
6
7 # Restarting the minion without cache
8 stop master/minion
9 rm -rf /var/cache/salt
10 start master/minion
SaltStack Documentation
SaltStack Cheat Sheet 175
Compound Matchers
Other examples:
SaltStack Cheat Sheet 176
Packages Manipulation
1 # Installation
2 salt '*' pkg.install apache2
3
4 # Latest version installation
5 salt '*' pkgutil.latest_version mysql-common
6
7 # Removing package(s)
8 salt '*' pkg.remove vim
9
10 # Purging package(s)
11 salt '*' pkg.purge apache2 mysql-server
SaltStack Cheat Sheet 177
Using Grains
1 # Syncing grains
2 salt '*' saltutil.sync_grains
3
4 # Available grains can be listed by using the ‘grains.ls’ module:
5 salt '*' grains.ls
6
7 # Grains data can be listed by using the ‘grains.items’ module:
8 salt '*' grains.items
9
10 # Grains have values that could be called via ‘grains.get <grain_name>’ (path is\
11 the name of a grain)
12 salt '*' grains.get path
Syncing Data
1 # Syncing grains
2 salt '*' saltutil.sync_grains
3
4 # Syncing everything from grains to modules, outputters, renderers, returners, s\
5 tates and utils.
6 salt '*' saltutil.sync_all
1 # Apache example
2
3 # Checking if service is available
4 salt '*' service.available apache2
5
6 # Manipulating Apache2 service
7 salt '*' service.status apache2
8 salt '*' service.start apache2
9 salt '*' service.restart apache2
10 salt '*' service.stop apache2
Network Management
1 # Get IP of your minion
2 salt '*' network.ip_addrs
3
4 # Ping a host from your minion
5 salt '*' network.ping localhost
6
7 # Traceroute a host from your minion
8 salt '*' network.traceroute localhost
9
10 # Get hostname
11 salt '*' network.get_hostname
12
13 # Modify hostname to 'myNode'
14 salt '*' network.mod_hostname myNode
15
16 # Information on all of the running TCP connections
17 salt '*' network.active_tcp
18
19 # Return the arp table from the minion
20 salt '*' network.arp
21
22 # Test connectivity
23 salt '*' network.connect google-public-dns-a.google.com port=53 proto=udp timeou\
24 t=3
25
26 # Get default route
27 salt '*' network.default_route
28
29 # Execute dig
SaltStack Cheat Sheet 179
Job Management
1 # List active jobs
2 salt-run jobs.active
3
4 # List all jobs with the id and other information
5 salt-run jobs.list_jobs
6
7 # List multiple information about the job with the id:20151101225221651308 like \
8 the result output
9 salt-run jobs.lookup_jid 20151101225221651308
10
11 # Kill the job with the id:20151101225221651308
12 salt 'server' saltutil.kill_job 20151101225221651308
SaltStack Cheat Sheet 180
Scheduling Feature
1 # Schedule a job called "scheduled_job"
2 salt '*' schedule.add scheduled_job function='cmd.run' job_args="['']" seconds=10
3
4 # Enable the job
5 salt '*' schedule.enable_job scheduled_job
6
7 # Disable the job
8 salt '*' schedule.disable_job scheduled_job
Testing States
1 salt '*' state.highstate test=True
2 salt '*' state.sls test=True
3 salt '*' state.single test=True
Load testing
1 # Starting 20 minions
2 wget https://raw.githubusercontent.com/saltstack/salt/develop/tests/minionswarm.\
3 py; python minionswarm.py -m 20 --master salt-master;
1 # Source: https://docs.saltstack.com/en/latest/ref/states/highstate.html#state-d\
2 eclaration
3
4 # Standard declaration
5 <ID Declaration>:
6 <State Module>:
7 - <Function>
8 - <Function Arg>
9 - <Function Arg>
10 - <Function Arg>
11 - <Name>: <name>
12 - <Requisite Declaration>:
13 - <Requisite Reference>
14 - <Requisite Reference>
15
16
17 # Inline function and names
18 <ID Declaration>:
19 <State Module>.<Function>:
20 - <Function Arg>
21 - <Function Arg>
22 - <Function Arg>
23 - <Names>:
24 - <name>
25 - <name>
26 - <name>
27 - <Requisite Declaration>:
28 - <Requisite Reference>
29 - <Requisite Reference>
30
31
32 # Multiple states for single id
33 <ID Declaration>:
34 <State Module>:
35 - <Function>
36 - <Function Arg>
37 - <Name>: <name>
38 - <Requisite Declaration>:
39 - <Requisite Reference>
40 <State Module>:
41 - <Function>
42 - <Function Arg>
SaltStack Cheat Sheet 182
43 - <Names>:
44 - <name>
45 - <name>
46 - <Requisite Declaration>:
47 - <Requisite Reference>
Introduction
Recently I saw a question on Reddit38 that caught my attention.
If you have a starter or an intermediate level, without following some basic guidelines and respecting
best practices, any task could be exhausting.
I will try to share and comment some of that Reddit comments and other best practices. I am also
trying to give some general recommendations for you as a new user of Salt.
Pillars are used to store data, especially sensitive data that you won’t share with all minions. They
are stored in the master and shared only with the targeted minion.
Pillars are called using the top.sls file.
1 tree /srv/pillar/
2 /srv/pillar/
3 ├── sched
4 │ └── init.sls
5 └── top.sls
1 base:
2 'os:Ubuntu':
3 - nginx
Choosing meaningful names for state files like nginx here and using it to only store Nginx related
data is a good habit to have when working with pillars. Keeping the configuration file as simple as
possible, would help other users to maintain and ameliorate it.
38
https://www.reddit.com/r/saltstack/comments/3maxi3/what_did_you_guys_wish_you_knew_before_setting_up/
Best Practices 184
Organizing Grains:
Salt grains are stored by the default in the folder:
1 etc/salt/grains
1 /etc/salt/minion
1 /etc/salt/config/app1/roles.conf
1 /etc/salt/config/app2/roles.conf
1 /etc/salt/config/app3/roles.conf
1 include:
2 - /etc/salt/config/app1/roles.conf
3 - /etc/salt/config/app2/roles.conf
4 - /etc/salt/config/app3/roles.conf
1 grains:
2 variable1: value1
3 variable2: value2
Don’t forget to type grains: in the beginning of the file and then you will be able to declare your
grains without having errors during the execution of state.sls or state.highstate.
Salt modules are python files and writing your own modules is not that difficult as it seems to be.
Python is an easy-to-learn language. It is real that Jinja files become complex sometimes but it is up
to you to see and compare what should be better for your configuration management.
You could choose to keep Jina configuration or write your own execution modules in function of
many criteria:
• Readability
• Complexity
• Execution time
• Maintainability
• Modularity
I would not recommend to use one of them over the other unless I can attach the choice to a
specific context and then choose in function of some criteria like the ones listed below. This could
be controversial, from a side using Jinja was supposed to make things easier while in some cases
using Python could be faster to code. In both cases, keep things as simple as possible.
The documentation is wrong all the time. It is also very often incomplete. Be prepared
to read the > source code. If you don’t know Python yet, you need to start learning. ∼
bbbryson
In my humble opinion, SaltStack documentation is not disastrous at this point but it is not sufficient,
which means that you will need to search for other content like blog posts and books. This is normal
and I think most of us are used to work like this: looking at the official documentation at the first
time and the moving to practical examples posted in community blogs.
You do not need to go to the official website to search for a documentation, like Linux man pages
just type use it form your terminal:
Best Practices 186
This will output all the documentation. However, if you need pkg module documentation, type the
following command:
and you will see just the documentation to help you use pkg with Salt.
Try also:
1 'status.all_status:'
2
3 Return a composite of all status data and info for this minion.
4 Warning: There is a LOT here!
5
6 CLI Example:
7
8 salt '*' status.all_status
9
10
11 'status.cpuinfo:'
12
13 Return the CPU info for this minion
14
15 CLI Example:
16
17 salt '*' status.cpuinfo
18
19
20 'status.cpustats:'
21
22 Return the CPU stats for this minion
23
24 CLI Example:
25
Best Practices 187
68 CLI Example:
69
70 salt '*' status.diskusage # usage for all filesystems
71 salt '*' status.diskusage / /tmp # usage for / and /tmp
72 salt '*' status.diskusage ext? # usage for ext[234] filesystems
73 salt '*' status.diskusage / ext? # usage for / and all ext filesystems
As you can see, the documentation is quite simple and comes with examples ready to use, like disk
usage for / and /tmp
or CPU statistics:
As a beginner, following and mastering the official examples is quite enough, in my opinion, to
practice Salt and master more complicated examples. So no worries with the documentation, in
the contrary, I recommend to follow and understand the official documentation before moving to
something else. The official examples, follows the best practices, do not hesitate to copy and modify
them for your needs.
Following Guidelines
I would like to see at least a recommended guideline from somebody. The ability to have
a standard (and maybe some namespacing on forumlas) would be much appreciated.
I totally agree with this, some guidelines and namespacing are mentioned in the official documen-
tation. Without standards, everyone could have it own namespaces conventions and sharing code
could be less efficient without this.
Here is the SLS file namespace that the official documentation describes:
The namespace for SLS files when referenced in top.sls or an include declaration follows a few simple
rules:
b. Because slashes can be represented as dots, SLS files can not contain dots in the name
besides the dot for the SLS suffix. The SLS file webserver_1.0.sls can not be matched, and
webserver_1.0 would match the directory/file webserver_1/0.sls
3. A file called init.sls in a subdirectory is referred to by the path of the directory. So,
webserver/init.sls is referred to as webserver.
4. If both webserver.sls and webserver/init.sls happen to exist, webserver/init.sls will be ignored
and webserver.sls will be the file referred to as webserver.
Working on a SaltStack Formula would need to pay more attention to namespacing since it is shared
and maintained in a collaborative way (usually on Github). Some efforts are being made to do this.
Best Practices 190
Contributors on Github
SaltStack has also published an official and complete list of conventions39 to be used when creating
a formula. I recommend to focus on this when writing your first Formula.
39
https://docs.saltstack.com/en/latest/topics/development/conventions/formulas.html
Best Practices 191
The execution of the last command will output the process id of every instance of Mongo if it is
already running.
In this example, I am running a Docker machine that starts a Mongo instance:
1 $ ps -aux|grep docker
2
3 root 4060 0.0 0.3 401224 23756 ? Ssl 15:06 0:01 /usr/bin/docker\
4 daemon
5 root 4218 0.0 0.2 210368 17920 ? Sl 15:06 0:00 docker-proxy -p\
6 roto tcp -host-ip 0.0.0.0 -host-port 28017 -container-ip 172.17.0.1 -container-p\
7 ort 28017
8 root 4225 0.0 0.2 144832 18284 ? Sl 15:06 0:00 docker-proxy -p\
9 roto tcp -host-ip 0.0.0.0 -host-port 27017 -container-ip 172.17.0.1 -container-p\
10 ort 27017
1 #Command
2 salt 'myNode' service.status mongod
3 #Output
4 myNode:
5 4239
If I would to run this from a Python script using SaltStack API, I will code something similar to this:
Best Practices 192
1 #!/usr/bin/env python
2
3 # Import the Salt client library
4 import salt.client
5
6 # Create a local client object
7 client = salt.client.LocalClient()
8
9 # Make calls with the cmd method
10 ret = client.cmd('myNode', 'service.status', ['mongod'])
11
12 # Print the pid of the 'Mongo' database instance
13 print ret
1 {'myNode': '4239'}
If you are familiar with Python you will notice that the last output is a dict (a dict or a dictionary
is a mapping object maps hashable values to arbitrary objects). You could also notice that we can
directly access 4239 by changing:
1 print ret
1 ret['myNode']
1 #!/usr/bin/env python
2
3 # Import the Salt client library
4 import salt.client
5
6 # Create a local client object
7 client = salt.client.LocalClient()
8
9 # make calls with the cmd method
10 ret = client.cmd('myNode', 'service.status', ['mongod'])
11
Best Practices 193
12 if ret:
13 exit(0)
14
15 exit(1)
The last script could be interfaced with Jenkins or another continuous integration server since the
build will automatically fail when the exit value is not 0.
We trust you have received the usual lecture from the local System Administrator. It
usually boils > down to these three things: #1) Respect the privacy of others. #2) Think
before you type. #3) With great power comes great responsibility.
When working with automation and remote execution tools, operations becomes easier than before
but risky : You would like to remove /data from a specific server (myNode) but you forgot to target
it and put ‘’ instead of *‘myNode’.
This is irreversible, you removed /data directory from all your servers. When working with states
remember to test before making changes. SaltStack has a test interface that will simulate the
execution of a state and report the same result:
40
https://github.com/eon01/SaltStackCheatSheet
Best Practices 194
If an environment has info in top.sls in base, it will always override all other top.sls.
Was not describing the real case : When checking the code he found that there is no distinction
between base and any other name, and no specific decision on ordering that seems alphabetical
Even though in the top.sls and file_roots, “qa” is second down, because it is last
alphabetically, that overrides all other top.sls files in all other environments.
The documentation is clear but the code seems to do another thing: merge and overwrite in
alphabetical order.
The issue was opened on May 2, 2014 and it was fixed. Even if this issue has not any effect on my
servers, but problems can happen. If there are any recommendations to say here, it is actually general
recommendation that every SysAdmin should know like:
SaltStack documentation is increasingly becoming richer and the number of blogs and tutorials
about it is increasing. If you started using SaltStack since 1 or 2 years you have probably noticed the
significant increase of online resources.
If you need to ask questions or need the community support, you can use Github, Salt IRC channel,
the official Google group and StackExchange (generally StackOverflow).
If you are looking for news, updates and speeches, you can subscribe to the official Salt blog and its
YouTube channel.
44
https://github.com/saltstack/salt
Community And Support 197
Official Resources
This is the full list of the official on-line resources:
• Github45 : The official github git repository where you can find the source code, follow the
issues and of course contribute ..etc
• Documentation46 : The official SaltStack documentation. This is may be the most helpful on-
line resource for SaltStack users.
• IRC channel47 : The official IRC channel where you can find volunteers that may have answers
for your questions.
• IRC logs48 : Where you can find all of the IRC discussions history.
• Google discussion group49 : The official discussion group and mailing list. Actually more than
5K topics were opened. You may get a good quality support as many of SaltStack users are
using this online ressource.
• Youtube channel50 : The official Youtube channel where you can find news, speeches and some
tutorials.
• SaltStack blog51 : The official blog.
• SaltConf52 : A website dedicated for the Salt conference (speeches, trainings, certifications).
• Linkedin group53 : The official Linkedin discussion group where you can share links, ask
questions and find good quality tutorials and blog posts.
• SaltStack events54 : A dedicated page for official events and events organised by partners.
Community Ressources
You can visit the community page55 where you can find the resources provided above and maybe
some other useful links.
SaltStack Reddit56 is a also a very good place where you can find useful links and where you can ask
your questions and contribute to the community. This reddit has more than 1k users and it’s getting
more and more poupular everyday.
45
https://github.com/saltstack/salt
46
http://docs.saltstack.com
47
http://webchat.freenode.net/?channels=salt
48
http://irclog.perlgeek.de/salt/
49
https://groups.google.com/forum/#!forum/salt-users
50
http://www.youtube.com/saltstack
51
http://www.saltstack.com/salt-blog
52
http://saltconf.com/
53
https://www.linkedin.com/groups/4877160
54
http://saltstack.com/events/
55
http://saltstack.com/community/
56
https://www.reddit.com/r/SaltStack
Community And Support 198
• Amsterdam57
• Atlanta58
• Austin59
• Birmingham, AL60
• Bucharest61
• Charlotte62
• Chicago63
• China64
• Dallas65
• Denver66
• Durham, NC67
• Germany68
• The SaltStack LinkedIn group69
• London70
• Los Angeles71
• Michigan72
• Minneapolis73
• Montreal74
• Nashville75
• New York City76
57
http://www.meetup.com/Amsterdam-Salt-Stack/
58
http://www.meetup.com/Atlanta-SaltStack-Meetup/
59
http://www.meetup.com/Austin-Saltstack-User-Group/
60
http://www.meetup.com/Birmingham-SaltStack-Users-Group/
61
http://www.meetup.com/Bucharest-SaltStack-Meetup/
62
http://www.meetup.com/Charlotte-SaltStack-Meetup/
63
http://www.meetup.com/SaltStack-Chicago/
64
http://saltstack.cn/
65
http://www.meetup.com/SaltStack-Dallas-Meetup/
66
http://www.meetup.com/Denver-SaltStack-Users-Group
67
http://www.meetup.com/TriSalt/
68
mailto:[email protected]
69
https://www.linkedin.com/groups?home=&gid=4877160
70
http://www.meetup.com/SaltStack-user-group-London
71
http://www.meetup.com/SaltStack-Los-Angeles-Meetup/
72
http://www.meetup.com/AWS-Michigan/
73
http://www.meetup.com/SaltStack-Minneapolis-Meetup/
74
http://www.meetup.com/Montreal-SaltStack-Meetup/
75
http://www.meetup.com/Nashville-SaltStack-Users-Group/
76
http://www.meetup.com/SaltStack-NYC/
Community And Support 199
• Paris77
• Portland78
• Salt Lake City79
• San Francisco80
• Sao Paulo81
• Seattle82
• Silicon Valley83
• Stockholm84
• Sydney85
• Washington DC86
• Zurich87
SaltStackInc
SaltStackInc is based at 3400 N. Ashton Blvd, Suite 110 Lehi, UT 84043 and they can be contacted by
phone +1 801.207.7440 or by email [email protected]
SaltStack offers also paid consulting and support services to SaltStack Enterprise customers, training
session, on-site trainings, quick-start workshops certifications (SaltStack Certified Engineer).
77
http://www.meetup.com/Paris-Salt-Meetup/events/221368010/
78
http://www.meetup.com/Portland-SaltStack-Users-Group/
79
http://www.meetup.com/SaltStack-user-group-Salt-Lake-City/
80
http://www.meetup.com/Salt-Stack-DevOps/
81
http://www.meetup.com/saltstackbrasil/
82
http://www.meetup.com/Seattle-SaltStack-Meetup/
83
http://www.meetup.com/SaltStack-user-group-Silicon-Valley/
84
http://www.meetup.com/SaltStack-Stockholm/
85
http://www.meetup.com/Sydney-SaltStack-User-Group/
86
http://www.meetup.com/NoVA-Saltstack/
87
http://www.meetup.com/Zurich-SaltStack/
Afterword
DevOps is a culture that aims to improve the process of development, integration and deployment
with more collaboration and transparency between the Dev and the Ops teams.
SaltStack is a comprehensive tool that also allow you to simplify your infrastructure manipulation
and master the flow between multiple environments (development, integration, staging and pro-
duction).
In these environments, we use other software such as Vagrant, Docker, Jenkins, Rundeck..etc
SaltStack will allow you to interface with many of these softwares to be present throughout your
“DevOps Toolchain”.
I hope that SaltStack For DevOps has brought you what you were looking for.
88
https://en.wikipedia.org/wiki/DevOps#/media/File:Devops.svg
89
https://commons.wikimedia.org/wiki/File:Salt_Crystals.JPG
90
https://en.wikipedia.org/wiki/Comparison_of_open-source_configuration_management_software