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