Book Image

Mastering SaltStack - Second Edition

Book Image

Mastering SaltStack - Second Edition

Overview of this book

SaltStack is a powerful configuration management and automation suite designed to manage servers and tens of thousands of nodes. This book showcases Salt as a very powerful automation framework. We will review the fundamental concepts to get you in the right frame of mind, and then explore Salt in much greater depth. You will explore Salt SSH as a powerful tool and take Salt Cloud to the next level. Next, you’ll master using Salt services with ease in your infrastructure. You will discover methods and strategies to scale your infrastructure properly. You will also learn how to use Salt as a powerful monitoring tool. By the end of this book, you will have learned troubleshooting tips and best practices to make the entire process of using Salt pain-free and easy.
Table of Contents (20 chapters)
Mastering SaltStack Second Edition
Credits
Foreword
About the Author
About the Reviewer
www.PacktPub.com
Preface

Using states for configuration management


The files inside the /srv/salt/ directory define the Salt states. This is a configuration management format that enforces the state that a minion will be in: package X needs to be installed, file Y needs to look a certain way, service Z needs to be enabled and running, and so on. For example:

apache2: 
  pkg: 
    - installed 
  service: 
    - running 
  file: 
    - name: /etc/apache2/apache2.conf 

States may be saved in a single SLS file, but it is far better to separate them into multiple files, in a way that makes sense to you and your organization. SLS files can use include blocks that pull in other SLS files.

Using include blocks

In a large SLS tree, it often becomes reasonable to have SLS files include other SLS files. This is done using an include block, which usually appears at the top of an SLS file:

include: 
  - base 
  - emacs 

In this example, the SLS file in question will replace the include block with the contents of base.sls (or base/init.sls) and emacs.sls (or emacs/init.sls). This imposes some important restrictions on the user. Most importantly, the SLS files that are included may not contain IDs that already exist in the SLS file that includes them.

It is also important to remember that include itself, being a top-level declaration, cannot exist twice in the same file. The following is invalid:

include: 
  - base 
include: 
  - emacs 

Ordering with requisites

State SLS files are unique among configuration management formats in that they are both declarative and imperative. They are imperative, as each state will be evaluated in the order in which it appears in the SLS file. They are also declarative because states may include requisites that change the order in which they are actually executed. For instance:

web_service: 
  service.running: 
    - name: apache2 
    - require: 
      - pkg: web_package 
web_package: 
  pkg.installed: 
    - name: apache2 

If a service is declared, which requires a package that appears after it in the SLS file, the pkg states will be executed first. However, if no requirements are declared, Salt will attempt to start the service before installing the package, because its codeblock appears before the pkg codeblock. The following will require two executions to complete properly:

web_service: 
  service.running: 
    - name: apache2 
web_package: 
  pkg.installed: 
    - name: apache2 

Requisites point to a list of items elsewhere in the SLS file that affect the behavior of the state. Each item in the list contains two components: the name of the module and the ID of the state being referenced.

The following requisites are available inside Salt states and other areas of Salt that use the state compiler.

require

The require requisite is the most basic; it dictates that the state that it is declared in is not executed until every item in the list that has been defined for it has executed successfully. Consider the following example:

apache2: 
  pkg: 
    - installed 
    - require 
      - file: apache2 
  service: 
    - running 
    - require: 
      - pkg: apache2 
  file: 
    - managed 
    - name: /etc/apache2/apache2.conf 
    - source: salt://apache2/apache2.conf 

In this example, a file will be copied to the minion first, then a package installed, then the service started. Obviously, the service cannot be started until the package that provides it is installed. But Debian-based operating systems such as Ubuntu automatically start services the moment they're installed, which can be problematic if the default configuration files aren't correct. This state will ensure that Apache is properly configured before it is even installed.

watch

In the preceding example, a new minion will be properly configured the first time. However, if the configuration file changes, the apache2 service will need to be restarted. Adding a watch requisite to the service will force that state to perform a specific action when the state that it is watching reports changes.

apache2: 
  ...SNIP... 
  service: 
    - running 
    - require: 
      - pkg: apache2 
    - watch: 
      - file: apache2 
  ...SNIP... 

The watch requisite is not available for every type of state module. This is because it performs a specific action, depending on the type of module. For instance, when a service is triggered with a watch, Salt will attempt to start a service that is stopped. If it is already running, it will attempt either a reload: True, service.full_restart, or service.restart, as appropriate.

As of version 2016.3, the following states modules support using the watch requisite: service, pkg, cmd, event, module, mount, supervisord, docker, dockerng, etcd, tomcat, and test.

onchanges

The onchanges requisite is similar to watch, except that it does not require any special support from the state module that is using it. If changes happen, which should only occur when a state completes successfully, then the list of items referred to with onchanges will be evaluated.

onfail

In a simple state tree, the onfail requisite is less commonly used. However, a more advanced state tree, which is written to attempt alerting the user, or to perform auto-correcting measures, can make use of onfail. When a state is evaluated and fails to execute correctly, every item listed under onfail will be evaluated. Assuming that the PagerDuty service is properly configured via Salt and an apache_failure state has been written to use it, the following state can notify the operations team if Apache fails to start:

apache2: 
  service: 
    - running 
    - onfail 
      - pagerduty: apache_failure 

use

It is possible to declare default values in one state and then inherit them into another state. This typically occurs when one state file has an include statement that refers to another file.

If an item in the state that is being used has been redeclared, it will be overwritten with the new value. Otherwise, the item that is being used will appear unchanged. Requisites will not be inherited with use; only non-requisite options will be inherited. Therefore, in the following SLS, the mysql_conf state will safely inherit the user, group, and mode from the apache2_conf state, without also triggering Apache restarts:

apache2_conf: 
  file: 
    - managed 
    - name: /etc/apache2/apache2.conf 
    - user: root 
    - group: root 
    - mode: 755 
    - watch_in: 
      - service: apache2 
mysql_conf: 
file: 
  - managed 
    - name: /etc/mysql/my.cnf 
    - use: 
      - file: apache2_conf 
    - watch_in: 
      - service: mysql 

prereq

There are some situations in which a state does not need to run, unless another state is expected to make changes. For example, consider a web application that makes use of Apache. When the codebase on a production server changes, Apache should be turned off, so as to avoid errors with the code that has not yet finished being installed.

The prereq requisite was designed exactly for this kind of use. When a state makes use of prereq, Salt will first perform a test run of the state to see if the items referred to in the prereq are expected to make changes. If so, then Salt will flag the state with the prereq as needing to execute.

apache2: 
  service: 
    - running 
    - watch: 
      - file: codebase 
codebase: 
  file: 
    - recurse 
...SNIP... 
shutdown_apache: 
  service: 
    - dead 
    - name: apache2 
    - prereq: 
      - file: codebase 

In the preceding example, the shutdown_apache state will only make changes if the codebase state reports that changes need to be made. If they do, then Apache will shutdown, and then the codebase state will execute. Once it is finished, it will trigger the apache2 service state, which will start up Apache again.

Inverting requisites

Each of the aforementioned requisites can be used inversely, by adding _in at the end. For instance, rather than state X requiring state Y, an SLS can be written so that state X declares that it is required by state Y, as follows:

apache2: 
  pkg: 
    - installed 
    - require_in: 
      - service: apache2 
  service: 
    - running 

It may seem silly to add inverses of each of the states but there is in fact a very good use case for doing so: include blocks.

SLS files cannot use requisites that point to a code that does not exist inside them. However, using an include block will cause the contents of other SLS files to appear inside the SLS file. Therefore, generic (but valid) configuration can be defined in one SLS file, included in another, and modified to be more specific with a use_in requisite.

Extending SLS files

In addition to an include block, state SLS files can also contain an extend block that modifies SLS files that appear in the include block. Using an extend block is similar to a use requisite, but there are some important differences.

Whereas a use or use_in requisite will copy defaults to or from another state, the extend block will only modify the state that has been extended.

# cat /srv/generic_apache/init.sls 
apache2_conf: 
  file: 
  - managed 
    - name: /etc/apache2/apache2.conf 
    - source: salt://apache2/apache2.conf 
(In django_server/init.sls) 
include: 
- generic_apache 
extend: 
  apache2_conf: 
    file: 
    - source: salt://django/apache2.conf 
(In image_server/init.sls) 
include: 
  - generic_apache 
extend: 
  apache2_conf: 
    file: 
      - source: salt://django/apache2.conf 

The preceding example makes use of a generic Apache configuration file, which will be overridden as appropriate for either a Django server or a web server that is only serving images.