Salt

What the jazz is it!?

  • Remote Execution
  • Configuration Management
  • Cloud Management & Orchestration
  • Data Gathering & Monitoring

The Code

  • Python 2.6 and 2.7
  • Apache2 license

Who uses it?

  • LinkedIn

  • PyPI

  • Hulu

  • Rackspace

  • nbviewer

  • HP Cloud

What does it run on?

  • Master
    • Linux
    • Mac
  • Minion
    • Linux
    • Mac
    • BSDs
    • Windows
    • Solaris
    • Others...



Architecture

Dah Intro

What's it made of?

  • Jinja2
  • M2Crypto
  • msgpack-python
  • pycrypto
  • PyYAML
  • pyzmq
  • Markupsafe
  • apache-libcloud (2014.1.0+)

The Master

  • Default transport is ZeroMQ
  • Encrypted with AES keypairs
  • Binary serialized w/ msgpack

Async Publishing

 

Async Return

 



Remote Execution

Dem Basicz

Execution Modules

  • Python modules that do something
  • Run shell commands or scripts
  • Minions execute modules locally
  • Call modules from the command line

 $ salt <TARGETS> module.function <ARGUMENTS>

Where in code?
salt/modules

test.py

 salt/modules/test.py
Where in code?

test.ping

$ salt '*' test.ping
web01.example.dev:
True
db01.example.dev:
True

Let's look at the code:

def ping():
return True

The minion runs the module locally and returns the values.

test.echo

$ salt '*' test.echo foo
web01.example.dev:
foo
db01.example.dev:
foo

Let's look at the code:

def echo(text):
return text

The master passes the arguments for the modules to use upon execution.



BUT


I don't need to run it on every minion, GOSH!

Targetting

  • Minions determine if they match
  • Minion ID: Glob, PCRE, List
  • Grains
  • Subnet
  • Define nodegroups on the master
  • Compound matching



Configuration Management

A Manage Who?


State Modules

  • Python modules that enforce something
  • Wrappers around execution modules
  • Minions execute state modules locally
  • Call state modules from the command line

$ salt '*' state.single module.function <ARGUMENTS>

Where in code?
salt/states

git.py

 salt/states/git.py
Where in code?

git.latest

$ salt 'web01*' state.single git.latest http://github.com/saltstack/salt.git target=/tmp/salt
web01.flexdeaf.dev:
----------  
    State: - git
    Name:      http://github.com/saltstack/salt.git
    Function:  latest
    Result:    True
    Comment:   Repository http://github.com/saltstack/salt.git cloned to /tmp/salt
    Changes:   new: http://github.com/saltstack/salt.git
               revision: 2d4772cc61e88d3384444cdf225b1c7b6a49512c


Summary
------------
Succeeded: 1
Failed:    0
------------
Total:     1

Execute a state module from the command line with state.single




Moar Architecture?

What Else Is Benefits?

Built-In Fileserver

  • Pluggable backend
  • Environment aware
  • Back with version control
  • Combine multiple backends

Minion Data Gathering

  • Grains
  • Mine
  • Peer

Authentication

  • Grant users permissions to specific modules or functions
  • Authenticate via external systems



The Pillar System

Sharing Targetted Data

Pillar

  • Dict. data given to specific minions
  • Use data in execution and states modules
  • Drive templated state configurations

Show me da' Pillah!

$ cat /srv/pillar/users.sls
users:
  salt:
    shell: /bin/bash
    home: /home/salt
    groups:
      - salt
  timoguin:
    sudouser: True
    shell: /bin/zsh
    groups:
      - admin
      - dev
      - salt
    ssh_auth:
      - ssh-rsa AAAAB3NzaC1yc2EAAAADAQA . . .
dotfiles: http://github.com/timoguin/dotfiles.git
psssttt. . . this data can also be generated via external systems. just enable an external pillar module that returns a dictionary



Cloud Management

What can do?

  • Manage private and public cloud services via APIs
  • Manage hypervisors via libvirt
  • Test your infrastructure locally with Vagrant

Cloud Modules

  • Python modules that call cloud APIs
  • Includes state and execution modules
  • Salt-cloud recently merged into Salt (2014.1.0+)
  • Call cloud modules from the command line

$ salt 'minion' cloud.create <CLOUD_CONFIG>             <INSTANCE_NAME> <API_ARGUMENTS>

Where in code?
salt/cloud

Providers

  • Modules that expose functionality for specific services
  • Configure multiple providers in cloud.providers.d
  • Each provider has similar, but slightly different config
$ cat /etc/salt/cloud.providers.d/do.conf
do:
api_key: <DIGITAL_OCEAN_API_KEY>
client_key: <DIGITAL_OCEAN_CLIENT_ID>
location: New York 2
provider: digital_ocean
ssh_key_file: /home/salt/.ssh/id_rsa
ssh_key_name: salt@master01

Common Functions

  • List available images
  • List regions
  • List instance sizes
  • List current cloud instances
  • Display instance details
  • Create instances
  • Destroy instances

Profiles

  • Define profiles to quickly spin up instances
  • Define instance values based on the provider options
  • Configure multiple profiles in cloud.profiles.d
$ cat /etc/salt/cloud.profiles.d/digital_ocean.conf
ubuntu_512MB_ny2:
provider: do
image: Ubuntu 12.04.3 x64
size: 512MB
location: New York 2
private_networking: True
minion:
master: master01.foo.bar
grains:
role: crazymachine



Orchestration

Events

  • Salt internals heavily tied to event system
  • Fire events from master or minion
  • Fire events programmatically
  • Consume the event bus on master or minions


$ salt '*' event.fire '{"data":"my event data"}' 'tag'

Where in code?
salt/modules/event.py

Reactor

  • Match based on namespaced event tags
  • Configure master to respond to events
  • Use event data programmatically




Where configured on master?
/etc/salt/master.d/reactor.conf

Match event tags, Define reactions


$ cat /etc/salt/master.d/reactor.conf
reactor:

  - 'salt/minion/*/start':         
    - /srv/reactor/start.sls
    - /srv/reactor/monitor.sls

  - 'salt/cloud/*/destroyed':
    - /srv/reactor/decommision.sls

  - 'salt/key':
    - /srv/salt/haproxy/react_new_minion.sls

- 'whatsup/cowabunga':
- /srv/salt/supahstate.sls

Use event data in reactions


$ cat /srv/salt/haproxy/react_new_minion.sls

{% if data['act'] == 'accept' and data['id'].startswith('web') %} add_new_minion_to_pool: cmd.state.sls: - tgt: 'haproxy*' - arg: - haproxy.refresh_pool - 'pillar={new_minion: {{ data['id'] }}}' {% endif %}



Adhocs Reports

playing with Salt's remote execution modules

Examples

  • Disk Usage
  • Network Interfaces
  • Users
  • Services
  • Grains

Outputters

  • YAML
  • JSON
  • raw
  • pprint
  • Write. Your. Own.


Where in code?

 salt/output

    Runners

    • Master-only modules
    • Parse output of executions and present reports





    Where in code?

     salt/runners



      Every Execution

      =

      Unique Job ID

      Job Cache

      • Commands initialized from the master get a unique ID
      • Can be stored externally
      • Utility modules for interacting with the Job System


      $ salt-run jobs.list_jobs
      '20140221224203119172':
      Arguments:
      - apache2
      Function: service.enabled
      Start Time: 2014, Feb 21 22:42:03.119172
      Target: '*'
      Target-type: glob
      User: sudo_timoguin



        list em:

        $ salt-run jobs.list_jobs
        '20140221224203119172':
        Arguments:
        - apache2
        Function: service.enabled
        Start Time: 2014, Feb 21 22:42:03.119172
        Target: '*'
        Target-type: glob
        User: sudo_timoguin


        get deets:

        $ salt-run jobs.lookup_jid 20140221224203119172
        master01:
        None
        dev01:
        None
        log01:
        None
        web01:
        None
        redis01:
        None
        es01:
        None
        db01:
        None



        The State System

        let's actually use all this data. . .



        Multiple* Layers of Abstraction

        *seven of them, to be exact. . .





        [insert diagram]

        Layer 1: function call

        • Called with state.single

        Layer 2: low chunk

        • Bottom of the state compiler
        • What a state.single call is compiled into
        • Called with state.low
         $ salt '*' state.low '{name: vim, state: pkg, fun: installed}'

        Layer 3: low state

        • List of low chunks evaluated in order
        • Final say in the order of execution
        • Accessed via state.show_lowstate

        Layer 4: high data

        • Raw data structure of rendered SLS formulas
        • Before order of evaluation is determined
        • Accessed via state.show_highstate and state.show_sls

        Layer 5: sls

        • Not required to be executed
        • Flexible data generation
        • Call single SLS formulas with state.sls

        Layer 6: highstate

        • Complete config for a minion
        • SLS formulas assigned to minion targets
        • Called with state.highstate

        Layer 7: overstate

        • Define which minions execute states in which order
        • Called via the state.overstate runner


        Cool.


        How do I actually apply and enforce these states?

        Highstate & Top.sls

        • Assign SLS formulas to minions

        $ cat /srv/salt/top.sls
        base:
        '*':
        - hosts
        - users
        'web*':
        - apache
        - websites
        - kibana
        'db*':
        - postgres
        - mysql
        Generate and ingest external data through Salt's Master Tops system if ya wanna. . .

        Pillar & Top.sls

        • Assign Pillar data to minions

        $ cat /srv/pillar/top.sls
        base:
        '*':
        - users
        'web*':
        - apache
        - websites



        Put it all in a Git!

        fileserver_backend:
          - git
          
        gitfs_remotes:
          - git://github.com/saltstack/salt-states.git
          - git+ssh://git@github.com/timoguin/private-states.git
          - file:///root/silly-states.git
          
        ext_pillar:
          - git: master git+ssh://git@bitbucket.com/timoguin/pillar.git
        

        SLS Formulas


        <Includes declaration>

        <Extends declaration>
        <SLS ID>:
        <State>:
        - fun: <Function>
        - args: <Args>
        - <Requisites>
        <SLS ID>:
        <State>.<Function>:
        - args: <Args>
        - <Requisites>

        java.sls

        java7-ppa:
        pkgrepo.managed:
        - ppa: webupd8team/java
        - require_in:
        - pkg: jdk7

        module.run:
        - name: pkg.refresh_db

        jdk7-accept-license:
        cmd.run:
        - name: echo oracle-java7-installer shared/accepted-oracle-license-v1-1 select true | /usr/
        - unless: "debconf-get-selections | grep -q shared/accepted-oracle-license-v1-1"
        - user: root

        jdk7:
        pkg.installed:
        - name: oracle-java7-installer
        - requires:
        - cmd: jdk7-accept-license
        - pkgrepo: java7-ppa

        git.sls

        git:
        pkg.installed:
        {% if grains['os'] == 'Ubuntu' %}
        - name: git-core
        {% else -%}
        - name: git
        {% endif -%}



        Get Deeper

        Python Client

        • REST interface with salt-api
        • Call libraries directly from Python
          • salt.client.LocalClient()
          • salt.client.Caller()
          • salt.runner.RunnerClient()
          • salt.cloud.CloudClient()

        Scaling Vertically

        • Flexible hierarchy
        • Master of master of master of master
        • Syndic pass-through

        Scaling Horizontally

        • Multi-master
        • Segmentation / grouping of masters

        Masterless

        • file_client: local
        • Put file_roots on the minion and access everything locally with salt-call
        • Popular with Vagrant



        Teach me to module!

        Custom Module Basics

        • __virtual__()
        • __private_modules()
        • public_modules()
        • Execution Modules: Return arbitrary values
        • State Modules: Return valid highstate data


        Pillar Modules

        Write a module with an ext_pillar function that returns a dictionary.

        def ext_pillar( minion_id, pillar, *args, **kwargs ):
            my_pillar = {}
            # Do stuff
            return my_pillar
        

        External pillar is configured on the master

        ext_pillar:
          - hiera: /etc/hiera.yaml
          - cmd_yaml: cat /etc/salt/yaml
          - cmd_json: "echo {'arg':'value'}" 
          - reclass:
              inventory_base_uri: /etc/reclass   
        

        Returners

        • Send job output to pluggable backends
        • Lots built-in
        • Analyze data from job executions externally


          Where in code?
          salt/returners

          Renderers

          Your life can be more than YAML!

          • #!py
          • #!jinja
          • #!mako
          • #!pydsl
          • #!pyobjects

          Where in code?

          salt/renderers

          Templating

          • Get some logic in there
          • Iterate over Pillar data
          • Fall back to sane default values

          Extending


          • _modules/
          • _states/
          • _renderers/
          • _returners/
          • _grains/



          Enuf Academical!

          wut about practikal

          Deploying Django


          States

            • Web server
            • Virtualenv
            • Pip
            • Version control
            • Django execution modules

           Bootstrapping Source Code

          • Define repos in Pillar
          • Developers can manually call Salt states to bootstrap team repos
          • Git, Mercurial, SVN

          Managing Vagrant

          • Built-in Salt provisioner
          • Pre-seed and distribute keys
          • Define bootstrap options
          • Define Pillar data
          • Distribute minion/master configs



          Questions?

          More Info


          @timoguin

          timoguin on Freenode

          867-5309


          #salt on Freenode

          reddit.com/r/saltstack

          PyTN2014: Salt

          By Tim O'Guin

          PyTN2014: Salt

          Overview of Salt's core functionality, largely targeted towards Python developers.

          • 1,382