saltstack

status

$ID:               定义state的名称,通常采用与描述的对象保持一致的方法,如apachenginx等
   $State:      须管理对象的类型,详见http://docs.saltstack.com/ref/states/all/index.html
      - $state: states      定制对象的状态

salt \* state.sls dhcp   使用
salt \* state.sls dhcp/aaa   使用dhcp目录下的aaa.sls
salt \* state.sls dhcp test=True   测试,不会修改minion
salt \* state.highstate

/usr/lib/python2.6/site-packages/salt/states        映射到modules
/usr/lib/python2.6/site-packages/salt/modules
如果使用states,需要在/srv/salt 下编写sls文件
state
确定你的文件路径
file_roots:
  base:
    - /srv/salt
保证你有入口文件
state文件包

vim /srv/salt/dhcp/init.sls
include:
  - dhcp.python_devel        包含dhcp目录下的python_devel.sls
cat /srv/salt/dhcp/python_devel.sls
dhcp:
  pkg.installed
salt \* state.sls dhcp


[root@node1 salt]# cat top.sls
base:
  '*':
    - testtest.sls
test是一个sls文件的名字 或者是一个目录的名字(该目录下必须有init.sls这个文件)
[root@node1 salt]# cat test.sls
httpd: #id(默认这个位置对应的是name的值)
  pkg: #state
    - installed #state的函数

state目录
/usr/lib/python2.6/site-packages/salt/states
使用该state
salt 'node2' state.highstate

sls文件的中的任务执行顺序可以使用关键字控制(requirehttpd:        ID
  pkg:        states模块
    - installed        模块的函数
  service.running:
    - require:        需要,依赖
      - pkg: httpd
指定执行某一个sls文件(文件的扩展名.sls不需要写出来)
[root@node1 salt]# salt 'node2' state.sls test
针对配置文件设置监听,实现自动重启
###############################################
httpd:
  pkg:
    - installed
  service:
    - running
    - require:
      - pkg: httpd
    - watch:            观察,文件有变化就启动
      - file: /etc/httpd/conf/httpd.conf
/etc/httpd/conf/httpd.conf:
  file:
    - managed
    - source: salt://httpd.conf
    - require:
      - pkg: httpd
################################################
apache:
  pkg:
    - name: httpd
    - installed

  service.running:
    - name: httpd
    - require:
      - pkg: httpd
    - watch:
      - file: apache_config

apache_config:
  file.managed:
    - name: /etc/httpd/conf/httpd.conf
- source: salt://httpd.conf
- template: jinjaapache_config控制的文件用jinja渲染
    - require:
      - pkg: httpd

httpd.conf




1、    安装软件包
2、    修改配置文件
3、    启动服务
https://docs.saltstack.com/en/latest/ref/states/all/index.html

vim user.sls
{%for user in ['test1','test1','test3','test4']%}        渲染
{{user}}:                                    变量
user.present
{%endfor%}                                结束(如果是ifendif)

一次调用多个sls文件
vim /srv/salt/top.sls
base:
  ‘*-    user
-    apache_start
salt \* state.highstate


源码安装nginx
nginx_source:
 file.recurse:    # file.managed:用于单文件,file.recurse用于目录
  - name: /tmp/
  - source: salt://nginx/
extract_nginx:
cmd.run:
  - cwd: /tmp
  - names:                # 多命令用names,单命令用name
    - tar xf nginx-1.6.2.tar.gz
    - tar xf pcre-8.35.tar.gz
  - require:
    - file: nginx_source
nginx_compile:
cmd.run:
  - cwd: /tmp/nginx-1.6.2
  - names:
    - ./configure --prefix=/usr/local/nginx --with-pcre=/tmp/pcre-8.35 && make
    - make install
  - require:
    - cmd: extract_nginx

自定义
state就是用来控制sls文件与module的映射
state默认路径   /usr/lib/python2.6/site-packages/salt/states

mkdir -pv /srv/salt/_states
[root@node1 _states]# cat wjj.py
def bs(name, x):                                定义函数
    ret = {                                定义一个字典
            'name': name,           
            'changes': {},
            'result': False,
            'comment': 'test_state' 描述的字段
            }
    try:
        result = x*x                做一些事情
        ret['changes']['new'] = result
        ret['result'] = True
    except:                                出现异常怎么办
        ret['changes']['new'] = 'jisuan cuwu'
        return ret
注意你的函数的第一个参数应该永远是name
同步一下你的修改
salt '*' saltutil.sync_all

使用你自定义的state
定义sls文件
[root@node1 _states]# cat /srv/salt/jisuan.sls
test:
  wjj:
    -  bs
    -  x: 3
salt 'node2' state.sls jisuan

tips:在state里面引用salt命令
__salt__['模块.函数'](参数)

#encoding=utf-8
import logging
import os

log = logging.getLogger(__name__)

def touch(name, path, file):
    ret = {
            'name': name,
            'changes': {},
            'result': True,
            'comment': 'test_state'
            }
    #log.error(name)
    #log.error(path)
    try:
        with open(path+'/'+str(file), 'w') as f:
            f.write('my test!')
    except Exception, e:
        log.error(str(e))

    return ret

def delete(name, path, file):
    ret = {
            'name': name,
            'changes': {},
            'result': True,
            'comment': 'test_state'
            }
    cmd='rm -fr ' + path + '/' + file
    os.system(cmd)
    ret['result']=True
    return ret

pillar

https://docs.saltstack.com/en/latest/topics/pillar/index.html

修改/etc/salt/master配置中的pillar_opts:TureFalse来定义是否开启或禁用这项功能
目录 /srv/pillar
vim /srv/pillar/top.sls
base:
  '*':
    - vimrc
    - data
    - pkg

vim /srv/pillar/data.sls
bind:
  port: 53
  listen-on: any

vim /srv/pillar/vimrc.sls
{% if grains['id'].endswith('server') %}  id标识以server结尾的  类似的startswith
vimrc: salt://edit/vimrc1
{% else %}
vimrc: salt://edit/vimrc2
{% endif %}

vim /srv/pillar/pkg.sls
{% if grains['os'] == 'RedHat' %}    根据不同的操作系统,定义不同的包名
apache: httpd
git: git
{% elif grains['os'] == 'Debian' %}
apache: apache2
git: git-core
{% endif %}
刷新生效 salt '*' saltutil.refresh_pillar
查看     salt \* pillar.items

使用pillar
可以在state、模板文件中引用,模板格式为“{{pillar变量}}”,例如:
{{ pillar['appname'] }}(一级字典)
{{ pillar['flow']['maxconn'] }}(二级字典)或 {{ salt['pillar.get']('flow: 'maxconn', {}) }}
Python API格式如下:pillar['flow']['maxconn']
                                     pillar.get(' flow:appname', {})

1、在statesls文件
vim /srv/salt/down_vimrc.sls
/root/.vimrc:
  file.managed:
- source: {{ pillar['vimrc'] }}   调用之前定义的pillar
或者- source: {{ salt['pillar.get']('vimrc','salt://edit/vimrc2') }}
                          pillarkey,默认值
salt \* state.show_sls down_vimrc  查看详细的变量
2salt -I "nginx:80" cmd.run "ls"   -I 指定pillar
3pillar的临时定义
vim /srv/salt/pillar_temp.sls
{%set u = pillar['u_name']%}        之前没有这个pillar
{{u}}:
  user.present

salt 'node2' state.sls pillar_temp pillar='{"u_name": "v1"}'    临时指定一个pillar

job

[root@salt-master salt]# salt -I "nginx:80" cmd.run "sleep 100" -v
Executing job with jid 20151011154835704857
salt-run 管理job
salt-run -d |grep jobs
salt-run jobs.active   查看当前正在运行的jobs
salt-run jobs.list_job 20130916125524463507     指定jid查看jobs详细信息    
salt-run jobs.list_jobs      #查看所有jobs信息                
salt-run jobs.lookup_jid 20130916125524463507  #指定jid查询jobs结果
salt-run jobs.print_job   #指定jid查询jobs详细信息
-------------------------------------------------
通过saltutil模块管理job
salt \* sys.doc saltutil |grep job
salt '*' saltutil.find_cached_job <job id>  #查询job cache信息
salt 'node2' saltutil.find_job 20150810003021849521  通过jid查找
salt 'node2' saltutil.is_running cmd.run  通过函数名查找
salt 'node2' saltutil.kill_job 20150810003232388165  杀死一个job
saltutil.running #查看minion当前正在运的jobs
saltutil.find_job<jid> #查看指定jid的job(minion正在运的jobs)
saltutil.signal_job<jid> <single> #给指定的jid进程发送信号
saltutil.term_job <jid> #终指定的jid进程(信号为15)
saltutil.kill_job <jid> #终指定的jid进程(信号为9)

return存储结果

每台minion跟存储服务器连接后发送返回数据。在大规模的Minion环境下并不适合企业级应用。也有网友通过event事件实现Master端直接Return到存储服务器。参考地址为:http://pengyao.org/salt-stack_master_retuner_over_event_system.html

使用redis 存储
修改minion/etc/salt/minion配置文件
redis.db: '0'                    #redis数据库
redis.host: 'vps.shencan.net'    #redis主机(ip地址和域名都行)
redis.port: 6379                 #redis端口
minion按照redis客户端  pip install redis
python -c 'import redis; print redis.VERSION'
salt 'Minion' cmd.run 'hostname' --return redis

mysql存储
/usr/lib/python2.6/site-packages/salt/returners/
具体的配置   cat mysql.py
vim /etc/salt/minion(冒号后有空格)
mysql.host:  '10.255.254.221'
mysql.user:  'salt'
mysql.pass:  '123'
mysql.db:  'salt'
mysql.port:  3306

10.255.254.221这台mysql服务器上
启动mysql服务
设置mysql用户,salt,密码,123
GRANT ALL PRIVILEGES ON *.* TO 'salt'@'%' IDENTIFIED BY 123’;

把以下sql语句保存到/tmp/salt.sql文件中,执行mysql -u root < /tmp/salt.sql
(创建一个salt库,然后在salt库里面创建两个表jidssalt_returns)
GRANT ALL PRIVILEGES ON *.* TO 'salt'@'%' IDENTIFIED BY 123’;

CREATE DATABASE  `salt`
      DEFAULT CHARACTER SET utf8
      DEFAULT COLLATE utf8_general_ci;

USE `salt`;

DROP TABLE IF EXISTS `jids`;
    CREATE TABLE `jids` (
      `jid` varchar(255) NOT NULL,
      `load` mediumtext NOT NULL,
      UNIQUE KEY `jid` (`jid`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `salt_returns`;
    CREATE TABLE `salt_returns` (
      `fun` varchar(50) NOT NULL,
      `jid` varchar(255) NOT NULL,
      `return` mediumtext NOT NULL,
      `id` varchar(255) NOT NULL,
      `success` varchar(10) NOT NULL,
      `full_ret` mediumtext NOT NULL,
      `alter_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
      KEY `id` (`id`),
      KEY `jid` (`jid`),
      KEY `fun` (`fun`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `salt_events`;
    CREATE TABLE `salt_events` (
    `id` BIGINT NOT NULL AUTO_INCREMENT,
    `tag` varchar(255) NOT NULL,
    `data` varchar(1024) NOT NULL,
    `alter_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    `master_id` varchar(255) NOT NULL,
    PRIMARY KEY (`id`),
    KEY `tag` (`tag`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

保证你的python能够操作mysql数据库
yum install MySQL-python -y

使用mysql作为returner,将结果放入mysql
[root@node1 ~]# salt 'node2' test.ping --return mysql
node2:
    True
----------------------------------------------------------------
可以通过saltschedule去收集minion上的信息。
schedule:
  uptime:
    function: status.uptime
    seconds: 60
    returner: mysql
  meminfo:
    function: status.meminfo
    minutes: 5
    returner: mysql

api web

用户

# chmod 755 /etc/salt/master /var/run/salt  /var/cache/salt
修改master配置文件
external_auth:
  pam:
    a1:
      - '*':
        - test.*
        - cmd.*
http://docs.saltstack.com/en/latest/topics/eauth/index.html#acl-eauth
切换a1用户进行测试
salt -a pam * test.ping
salt -a pam * cmd.run 'whoami'

测试令牌
$ salt -T -a pam web\* test.ping
[a1@zabbix_server ~]$ salt -T -a pam * test.ping
username: a1
password:
zabbixnode1.example.com:
    True

API

yum install salt-api -y
cd  /etc/pki/tls/certs
# 生成自签名证书, 过程中需要输入key密码及RDNs
make testcert
cd /etc/pki/tls/private/
# 解密key文件,生成无密码的key文件, 过程中需要输入key密码,该密码为之前生成证书时设置的密码
openssl rsa -in localhost.key -out localhost_nopass.key
或者
yum install gcc make python-devel libffi-develpip-python install PyOpenSSL
salt-call tls.create_self_signed_cert  #生成证书,需要有salt-minionsalt-callsalt-minion包里
添加saltapi.conf 文件
vim /etc/salt/master.d/saltapi.conf
rest_cherrypy:
   port: 8000
   host: 127.0.0.1
   #disable_ssl: true 开关https
   ssl_crt: /etc/pki/tls/certs/localhost.crt #使用前面生成的证书
   ssl_key: /etc/pki/tls/certs/localhost.key
external_auth:
    pam:
       saltapi:
          - .*
          - '@runner'
          - '@wheel'    
#添加用户    useradd -M -s /sbin/nologin saltapi    echo "spassword" | passwd saltapi --stdin

重启master
启动salt-api 
----------------------------------------
import json
import urllib
import urllib2
class SaltAPI(object):
    __token_id = ''
    def __init__(self):
        self.__url = url
        self.__user = user
        self.__password = pass
        params = {'eauth': 'pam', 'username': self.__user, 'password': self.__password}
        content = self.postRequest(params, prefix='/login')
        print content['return'][0]['token']
        self.__token_id = content['return'][0]['token']
    def postRequest(self, obj, prefix='/'):
        url = self.__url + prefix
        headers = {'X-Auth-Token': self.__token_id}
        data = urllib.urlencode(obj)
        req = urllib2.Request(url, data, headers)
        opener = urllib2.urlopen(req)
        content = json.loads(opener.read())
        return content
    def postRequest1(self, obj, prefix='/'):
        url = self.__url + prefix
        headers = {'X-Auth-Token': self.__token_id}
        req = urllib2.Request(url, obj, headers)
        opener = urllib2.urlopen(req)
        content = opener.info()
        return content
    def list_all_key(self):
        params = {'client': 'wheel', 'fun': 'key.list_all'}
        content = self.postRequest(params)
        # minions = content['return'][0]['data']['return']['minions']
        # minions_pre = content['return'][0]['data']['return']['minions_pre']
        # return minions,minions_pre
        minions = content['return'][0]['data']['return']
        return minions
    def delete_key(self, node_name):
        params = {'client': 'wheel', 'fun': 'key.delete', 'match': node_name}
        content = self.postRequest(params)
        ret = content['return'][0]['data']['success']
        return ret
    def accept_key(self, node_name):
        params = {'client': 'wheel', 'fun': 'key.accept', 'match': node_name}
        content = self.postRequest(params)
        ret = content['return'][0]['data']['success']
        return ret
    def reject_key(self, node_name):
        params = {'client': 'wheel', 'fun': 'key.reject', 'match': node_name}
        content = self.postRequest(params)
        ret = content['return'][0]['data']['success']
        return ret
    def remote_noarg_execution(self, tgt, fun):
        ''' Execute commands without parameters '''
        params = {'client': 'local', 'tgt': tgt, 'fun': fun}
        content = self.postRequest(params)
        ret = content['return'][0][tgt]
        return ret
    def remote_execution(self, tgt, fun, arg):
        ''' Command execution with parameters '''
        params = {'client': 'local', 'tgt': tgt, 'fun': fun, 'arg': arg}
        content = self.postRequest(params)
        ret = content['return'][0][tgt]
        return ret
    def shell_remote_execution(self, tgt, arg):
        ''' Shell command execution with parameters '''
        params = {'client': 'local', 'tgt': tgt, 'fun': 'cmd.run', 'arg': arg, 'expr_form': 'list'}
        content = self.postRequest(params)
        ret = content['return'][0]
        return ret
    def grains(self, tgt, arg):
        ''' Grains.item '''
        params = {'client': 'local', 'tgt': tgt, 'fun': 'grains.item', 'arg': arg}
        content = self.postRequest(params)
        ret = content['return'][0]
        return ret
    def target_remote_execution(self, tgt, fun, arg):
        ''' Use targeting for remote execution '''
        params = {'client': 'local', 'tgt': tgt, 'fun': fun, 'arg': arg, 'expr_form': 'nodegroup'}
        content = self.postRequest(params)
        jid = content['return'][0]['jid']
        return jid
    def deploy(self, tgt, arg):
        ''' Module deployment '''
        params = {'client': 'local', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg}
        content = self.postRequest(params)
        return content
    def async_deploy(self, tgt, arg):
        ''' Asynchronously send a command to connected minions '''
        params = {'client': 'local_async', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg}
        content = self.postRequest(params)
        jid = content['return'][0]['jid']
        return jid
    def target_deploy(self, tgt, arg):
        ''' Based on the list forms deployment '''
        params = {'client': 'local_async', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg, 'expr_form': 'list'}
        content = self.postRequest(params)
        jid = content['return'][0]['jid']
        return jid
    def jobs_list(self):
        ''' Get Cache Jobs Defaut 24h '''
        url = self.__url + '/jobs/'
        headers = {'X-Auth-Token': self.__token_id}
        req = urllib2.Request(url, headers=headers)
        opener = urllib2.urlopen(req)
        content = json.loads(opener.read())
        jid = content['return'][0]
        return jid
    def runner_status(self, arg):
        ''' Return minion status '''
        params = {'client': 'runner', 'fun': 'manage.' + arg}
        content = self.postRequest(params)
        jid = content['return'][0]
        return jid
    def runner(self, arg):
        ''' Return minion status '''
        params = {'client': 'runner', 'fun': arg}
        content = self.postRequest(params)
        jid = content['return'][0]
        return jid
cl = SaltAPI()
print cl.shell_remote_execution('aws-ms1','date')
print cl.grains('aws-ms1','ipv4')


WEB(halite)很不好用  

这个还行 https://github.com/yueyongyue/saltshaker/
yum install salt-api git
cd /var/www/
git clone https://github.com/saltstack/halite
cd halite/halite
./genindex.py -C

vim /etc/salt/master
rest_cherrypy:
host: 0.0.0.0
port: 8080
debug: true
disable_ssl: True
static: /var/www/halite/halite
app: /var/www/halite/halite/index.html

external_auth:
   pam:
     salt:
     - .*
     - '@runner'
     - '@wheel'
/etc/init.d/salt-master restart
useradd salt
echo salt | passwdstdin salt
salt -a pam \*  test.ping 
输入用户和密码 如看到minion返回信息 则表示登陆验证成功

启动 salt-api
salt-api -dcd /var/www/halite/halite
python server_bottle.py -d -C -l debug -s cherrypy
打开http://ip:8080/app

beacons

https://docs.saltstack.com/en/latest/topics/beacons/index.html
https://docs.saltstack.com/en/latest/ref/beacons/all/index.html#all-salt-beacons

1minion上定义beacon
beacons:
  service:
    httpd:
      onchangeonly: True
      uncleanshutdown: /var/run/httpd/httpd.pid
只要httpd的状态放生改变就会给master发送事件
2 你要知道发送的event长什么样
salt/beacon/centos2/service/    {
    "_stamp": "2015-12-06T07:24:25.095014",
    "data": {
        "httpd": {
            "running": false,
            "shutdown": "clean"
        },
        "id": "centos2"
    },
    "tag": "salt/beacon/centos2/service/"
}

3 编写master配置文件,监听相对应的tag
vim /etc/salt/master
reactor:
  - 'salt/beacon/znode2/service/':
    - '/srv/reactor/backup.sls'

4 编写触发文件
vim /srv/reactor/backup.sls
{%if data['data']['httpd']['running'] == False %}
backup file:
  local.cmd.run:
    - tgt: {{data['data']['id']}}
    - arg:
      - "/etc/init.d/httpd start"
{%endif%}

#如果调用的是state.sls 还要编写相对应的sls文件


自定义beacon插件
1 mkdir -pv /srv/salt/_beacons
2 在这个目录下编写插件
3 同步
salt '*' saltutil.sync_all

4 vim t.py
#你在minion配置文件中的字典,会赋值给config
beacons:
  t:
    - rm: /tmp/kk
    - interval: 2

函数的返回值必须是[{'t1':1},{'t2':2}]

导入你需要的模块
import 模块名字
def beacon(config):
    ret = []
    r_dict = {}
    拆分config然后去做你想做的判断
    然后把返回值追加到ret
    return ret

做一个最简单的审计系统
# -*- coding: utf-8 -*-

import logging
import re
import pexpect
log = logging.getLogger(__name__)

__virtualname__ = 't'

"""
def __virtual__():
    if salt.utils.is_windows():
        return False
    else:
        return __virtualname__
"""

def beacon(config):
    '''
    code_block:: yaml

        beacons:
          t:
            - rm : /
            - interval: 100
    '''
    ret = []
    user = __salt__['cmd.run']('whoami')
    logging.debug(user)
    r_dict = {}
    for t in config:
        for k,v in t.items():
            cmd = "grep '^.*rm.* /tmp/kk[[:space:]]$' /tmp/.shellog"
            output, status = pexpect.run(cmd, withexitstatus=1)
            if status == 0 :
                logging.debug(output)
                r_dict = { user : output}
                logging.debug(r_dict)
                ret.append(r_dict)

    return ret

安装zabbix_agent

ls zabbix_linux_install/
agent_install.sls  init.sls  zabbix_agentd.conf  zabbix.repo

cat init.sls
include:
  - zabbix_linux_install.agent_install

cat zabbix.repo
[zabbix]
baseurl=http://mirrors.aliyun.com/zabbix/zabbix/2.4/rhel/6/x86_64/
gpgcheck=0

cat agent_install.sls
agent_install.sls
/etc/yum.repos.d/zabbix.repo:
  file.managed:
    - source: salt://zabbix_linux_install/zabbix.repo

zabbix-agent:
  cmd.run:
    - name: yum install zabbix-agent-2.4.5 -y
    - require:
      - file: /etc/yum.repos.d/zabbix.repo
  service.running:
    - require:
      - cmd: zabbix-agent
    - watch:
      - file: /etc/zabbix/zabbix_agentd.conf

/etc/zabbix/zabbix_agentd.conf:
  file.managed:
    - source: salt://zabbix_linux_install/zabbix_agentd.conf    ( 主要内容:Hostname={{ grains['ip_interfaces']
                                                                                       ['eth1'][0] }})
    - template: jinja
    - require:
      - cmd: zabbix-agent



老师的
{% set NODENAME = grains['nodename'] %}
{% set BINDIR = '/usr/local/zabbix/sbin' %}
{% set LOGDIR = '/var/log/zabbix' %}
{% set Serverip = '192.168.40.11' %}
# source pacekages
zabbix_source:
  file.managed:
    - name: /tmp/zabbix-2.4.1.tar.gz
    - unless: test -e /tmp/zabbix-2.4.1.tar.gz
    - source: salt://files/common/zabbix/zabbix-2.4.1.tar.gz

#trace zabbix
extract_zabbix:
  cmd.run:
    - cwd: /tmp
    - names:
      - tar -zxf zabbix-2.4.1.tar.gz >/dev/null 2>&1
    - unless: test -d /tmp/zabbix-2.4.1/
    - require:
      - file: zabbix_source


#Add user
zabbix_user:
  user.present:
    - name: zabbix
    - uid: 1000
    - createhome: False
    - gid_from_name: True
    - shell: /sbin/nologin
/var/log/zabbix:
  file.directory:
    - user: zabbix
    - group: zabbix
    - dir_mode: 755
    - file_mode: 655
    - recurse:
      - user
      - group
"init.sls" 87L, 2508C
agent_start_init:
  file.managed:
    - name: /etc/init.d/zabbix_agentd
    - user: root
    - mode: 0755
    - source: salt://files/common/zabbix/zabbix_agentd
    - source_hash: salt://files/common/zabbix_agent/zabbix_agentd
    - template: jinja
    - defaults:
      ZABBIX_BIN: {{ BINDIR }}
    - unless: if [[ ${cat /etc/init.d/zabbix_agentd  | grep "{{ BINDIR }}" | awk -F'=' 'NR==1{print $2}'} = 
                                                                                 '"{{ BINDIR }}"' ]];then exit 0;fi
  cmd.run:
    - names:
      - /sbin/chkconfig --add zabbix_agentd
      - /sbin/chkconfig zabbix_agentd on
    - unless: /sbin/chkconfig --list zabbix_agentd
  service.running:
    - name: zabbix_agentd
    - enable: True
    - restart: True
zabbix_config_set:
  file.managed:
    - name: /usr/local/zabbix/etc/zabbix_agentd.conf
    - user: root
    - mode: 744
    - source: salt://files/common/zabbix/zabbix_agentd.conf
    - template: jinja
    - defaults:
      ServerADD: {{ Serverip }}
      AgentNAME: {{ NODENAME }}
    - unless: if grep '{{ NODENAME }}' /usr/local/zabbix/etc/zabbix_agentd.conf >/dev/null 2>&1;then exit 0;fi
/usr/bin/zabbix_get:
  file.symlink:
    - target: /usr/local/zabbix/bin/zabbix_get
    - unless: test -L /usr/bin/zabbix_get
/usr/bin/zabbix_sender:
  file.symlink:
    - target: /usr/local/zabbix/bin/zabbix_sender
    - unless: test -L /usr/bin/zabbix_sender

显示salt进程具体名称

安装setproctitle(Master/Minion端均进行)

yum -y install python-setproctitle
重启salt

service salt-master restart
service salt-minion restart
查看Master端进程

ps ax |grep salt |grep -v salt
Master端显示如下(同时个人在行尾追加上进程的具体用途):

2943 ?        S      0:00 /usr/bin/python /usr/bin/salt-master -d ProcessManager       # 中心进程管理器
2944 ?        S      0:00 /usr/bin/python /usr/bin/salt-master -d _clear_old_jobs      # 清除旧的Jobs文件及更新fileserver
2945 ?        Sl     0:00 /usr/bin/python /usr/bin/salt-master -d Publisher            # 将任务PUBMinion
2946 ?        Sl     0:00 /usr/bin/python /usr/bin/salt-master -d EventPublisher       # Event Publisher进程
2951 ?        S      0:00 /usr/bin/python /usr/bin/salt-master -d ReqServer_ProcessManager    # ReqServer进程管理器
2952 ?        Sl     0:01 /usr/bin/python /usr/bin/salt-master -d MWorker              # 劳苦大众, 奋斗在一线的Worker进程
2953 ?        Sl     0:01 /usr/bin/python /usr/bin/salt-master -d MWorker              # 同楼上
2954 ?        Sl     0:01 /usr/bin/python /usr/bin/salt-master -d MWorker
2955 ?        Sl     0:01 /usr/bin/python /usr/bin/salt-master -d MWorker
2956 ?        Sl     0:01 /usr/bin/python /usr/bin/salt-master -d MWorker
2957 ?        Sl     0:00 /usr/bin/python /usr/bin/salt-master -d MWorkerQueue         
# Ret接口(ROUTER)数据转发到Worker(DEALER)
执行个任务, 看看Minion端怎么显示(同时个人在行尾追加上进程的具体用途):

2003 ?        Sl     0:01 /usr/bin/python /usr/bin/salt-minion -d        # Minion进程, 接收来自Master端的任务
2069 ?        S      0:00 /usr/bin/python /usr/bin/salt-minion -d 20150108034936245247   # 接收到任务后, 会启动名为
对应jid的进程进行任务处理及结果反馈
这样, 就可以非常清晰的知道Salt的每个进程是做什么用途的, 如果Master/Minion进程异常, 也可以迅速的定位

salt安装

并发 == 效率
差异性

4505  4506 master        队列  zeromqraet

salt-call --master=10.21.40.23 --id="host1" state.highstate

yum安装
https://repo.saltstack.com/#rhel  salt官方源,版本更新
http://repo.saltstack.com/yum/redhat
[saltstack-repo]
name=SaltStack repo for RHEL/CentOS $releasever
baseurl=http://repo.saltstack.com/yum/redhat/$releasever/$basearch/latest
enabled=1
gpgcheck=1
gpgkey=http://repo.saltstack.com/yum/redhat/$releasever/$basearch/latest/SALTSTACK-GPG-KEY.pub

yum install epel-release 
yum install salt-master
mastersalt-2014.1.10-4.el6.noarch
salt-master-2014.1.10-4.el6.noarch
salt-minion-2014.1.10-4.el6.noarchoptionalminion端(客户端)
yum install salt-minion
salt-2014.1.10-4.el6.noarch
salt-minion-2014.1.10-4.el6.noarch

防火墙添加
iptables -I INPUT -m state --state new -m tcp -p tcp --dport 4505 -j ACCEPT
iptables -I INPUT -m state --state new -m tcp -p tcp --dport 4506 -j ACCEPT

python安装
yum install swig
yum install gcc gcc-c++
yum install openssl-devel
yum install libyaml-devel -y

pip install M2Crypto
pip install pyzmq
pip uninstall PyCrypto
pip uninstall salt Jinja2 msgpack-python PyYAML MarkupSafe
pip install PyCrypto
cd /usr/lib/python2.6/site-packages/
rm -fr salt salt-2014.1.4-py2.6.egg-info/
pip install salt

http://docs.saltstack.com/en/latest/



http://msgpack.org/
zeromq
使用了消息队列
信息使用msgpack进行封装

选择salt的理由

1 使用消息队列(异步) zeromq
2 消息封装的是2机制 msgpack封装
3 安全使用aes算法
4 服务器开两个端口4505 4506
5 理念是block(适合拼积木,实现业务流)

配置
在master上编辑vim /etc/hosts
master_ip  master_name
minion_ip  minion_name
然后把这份文件同步到所有的主机上


修改文件描述符
修改sk_buffer
echo 16777216 > /proc/sys/net/core/rmem_max
echo 16777216 > /proc/sys/net/core/wmem_max
echo "4096 87380 16777216" > /proc/sys/net/ipv4/tcp_rmem
echo "4096 87380 16777216" > /proc/sys/net/ipv4/tcp_wmem

 minion  vim /etc/salt/minion
 id: node2 (minion的名字)            冒号后有空格
master: 10.255.254.221masterip/etc/init.d/salt-minion start
/etc/init.d/salt-master start

查看你的minion是否发送了加入请求
[root@node1 salt]# salt-key -L
                salt-keyA  接受所有minion   -a minion名字   接受指定的minion
测试
salt '*' test.ping
   目标 模块.函数 函数的参数

如何定义目标
shell下的通配符
*
"*.example.com"
"web?.example.com"
"web[1-5].example.com"
regex  正则表达式
salt -E 'web1-(prod|devel).example.com'
list    列表的方式
salt -L 'web1,web2,web3'
grains(数据存放在minion上,默认就有)
[root@node1 salt]# salt 'node1' grains.ls
salt -G 'os:CentOS' test.ping
定义主机组
[root@node1 salt]# vim /etc/salt/master
salt -N 组名 test.ping
批次执行-b (依次执行)

salt '*' sys.doc
如何使用相对应的模块和函数
/usr/lib/python2.6/site-packages/salt/modules
test.ping (是在minion上执行)

http://docs.saltstack.com/en/latest/ref/modules/all/index.html
[root@node1 modules]# salt 'node3' cmd.run 'uptime'
salt中文件的根目录
file_roots:
  base:
    - /srv/salt
salt 'node2' cmd.script salt://test.sh
脚本缓存的路径
/var/cache/salt/minion/files/base

修改minion id

service salt-minion stop
rm -f /etc/salt/pki/minion/minion.pub
rm -f /etc/salt/pki/minion/minion.pem
echo $(hostname) > /etc/salt/minion_id
service salt-minion start

event和反射

1event实际就是一个字典
这个字典有两个固定的key
"tag" 可以用来区分哪一个mininon
"data" 包含更详细的数据,比如执行是否成功,执行的是哪一个函数,以及id是谁
2、把你的minionmaster,以及其他的minion可以联动起来

master执行  salt-run state.event pretty=True   捕捉event
minion执行 salt-call event.send  'tag'  'data'  data可以没有,tag必须有
master再开一个终端执行salt 'zabbix_server' state.sls apache_install
捕捉到的event:
salt/job/20151018191951447100/ret/zabbix_server    {   tagdata ,字典为data
    "_stamp": "2015-10-18T11:20:19.543008",
    "cmd": "_return",
    "fun": "state.sls",
    "fun_args": [
        "apache_install"
    ],
    "id": "zabbix_server",
    "jid": "20151018191951447100",
    "out": "highstate",
    "retcode": 0,
    "return": {
        "pkg_|-httpd_|-httpd_|-installed": {
            "__run_num__": 0,
            "changes": {},
            "comment": "Package httpd is already installed.",
            "duration": 4001.181,
            "name": "httpd",
            "result": true,
            "start_time": "11:20:14.264181"
        }
    },
"success": true
}
}配置
修改master配置文件
reactor:
  - "salt/job/*":        捕捉到的tagjid会变化用了*代替
    - /srv/reactor/my_custom_module.sls     捕捉到tag要做的事
mkdir -pv /srv/reactor/  &&  vim /srv/reactor/my_custom_module.sls
{% if data["fun_args"][0] == "apache_install" and data["success"] == true %}  进行一些具体的判断
touch_new_file:
  local.state.sls:   (老版本这里是cmd.state.sls- tgt: 'zabbix_server'   执行的目标
    - arg:
      - newfile       执行/srv/salt/newfile.sls文件
    - kwarg:          临时定义pillar
        pillar:          注意这里是四个空格
          filename: {{ data["fun_args"][0] }}
{% endif %} ##这个{{data是模板里面的变量}}
相当于 salt ' zabbix_server ' state.sls newfile pillar='{"filename": " apache_install "}'

cat /srv/salt/newfile.sls
/tmp/{{pillar['filename']}}:
  file.managed:
- source: salt://passwd

到此,执行salt 'zabbix_server' state.sls apache_install(成功),就会联动执行newfile.sls

帮助文档

包含所有模块,state等,记不起来可以在这找
salt 'centos2' sys.list_functions
salt 'centos2' sys.list_functions sys
    - sys.argspec
    - sys.doc          文档, salt '*' sys.doc cmdcmd的文档,其他类似
    - sys.list_functions   module的所有function的命令, salt  'Minion' sys.list_functions cmd
    - sys.list_modules   Minion支持的所有module列表,salt 'Minion' sys.list_modules
    - sys.list_renderers  查看所有Return列表
    - sys.list_returner_functions
    - sys.list_returners
    - sys.list_runner_functions
    - sys.list_runners
    - sys.list_state_functions  指定states的所有functionsalt  'Minion' sys.list_state_functions file
    - sys.list_state_modules   Minion支持的所有states列表
    - sys.state_doc                 state的详细用法与例子
    - sys.reload_modules
    - sys.renderer_doc
    - sys.returner_argspec
    - sys.returner_doc
    - sys.runner_argspec
    - sys.runner_doc
    - sys.state_argspec

grains

grainsmasterminion上拿数据
salt '*' grains.ls
salt 'node1' grains.items
salt 'node1' grains.item os
salt -G 'os:CentOS' test.ping

1、用grains命令添加
salt 'Minion' grains.append  saltbook  'verycool' 
salt '*' grains.setval key "{'sub-key': 'val', 'sub-key2': 'val2'}"  设置多个
salt '*' grains.remove key val  删除

2、在minion端定义grains
vim /etc/salt/minion.d/hostinfo.conf       #自定义grains,相当于打上一个标签,在根据标签进行操作
grains:
  roles:
    - webserver
    - memcache
  deployment: datacenter4
  cabinet: 13
 重启minion   /etc/init.d/salt-minion restart

3、在master端自己编写grains,然后推送给minion
mkdir /srv/salt/_grains
vim /srv/salt/_grains/system.py
import platform
def get_system():
    grains = {}                 # 初始化一个grains字典
    grains['system'] = platform.platform()
    return grains              #返回结果

salt "zabbix_agent" saltutil.sync_grains        #推送给minion(被控端的/var/cache/salt/minion/extmods/grains/目录下)
salt "zabbix_agent" grains.item system        #查看这个grains

使用
grains是静态的
1、通过目标使用grains
salt -G 'os:CentOS' test.ping
2、通过top.sls来使用
vim top.sls
base:
'location:shanghai':
    - match: grain
    - webserver (具体的sls文件名)
salt '*' state.highstate来调用,只有location:shanghai的主机才执行user.sls
3、通过在定义的statesls文件使用
{% set the_node_type = salt['grains.get']('grains的key', '默认值')  %}    or  grains['os']
{% if the_node_type %}
  'node_type:{{ the_node_type }}':
    - match: grain
    - {{ the_node_type }}
{% endif %}
4、通过编写模块来调用grains__grains__['key']
vim g1.py
def t1():
return __grains__['location']
salt '*' saltutil.sync_modules 推送
salt * g1.t1    调用

modules

常用模块  http://docs.saltstack.com/ref/modules/all/index.html#all-salt-modules

import salt.client
client = salt.client.LocalClient()
ret = client.cmd('*', 'test.ping')
print ret
Archive压缩包

salt 'centos1' archive.gunzip /tmp/sourcefile.txt.gz   #采用gzunzip解压/tmp/sourcefile.txt.gz包
salt 'centos1' archive.gzip /tmp/sourcefile.txt     #采用gzip压缩/tmp/sourcefile.txt文件
---   API调用:client.cmd('centos1', ' archive.gunzip'  ['/tmp/sourcefile.txt.gz '])
cmd命令

salt 'centos1' cmd.run "free -m"   #获取所有被控主机的内存使用情况
salt 'centos1' cmd.script salt://script/test.sh   #在centos1主机运行test.sh脚本,其中script/test.sh存放在
file_roots指定的目录,首先同步test.shminioncache目录(如同步到/var/cache/salt/minion/files/base/script/test.sh);
其次运行该脚本
---  API调用:client.cmd('SN2013-08-021', 'cmd.run',['free -m'])
cp文件目录管理

salt 'centos1' cp.cache_local_file /etc/hosts  #将指定被控主机的/etc/hosts文件复制到被控主机本地的salt cache目录
/var/cache/salt/minion/localfiles/
salt 'centos1' cp.get_dir salt://path/to/dir/ /minion/dest   #将主服务器file_roots指定位置下的目录复制到被控主机
salt 'centos1' cp.get_file salt://path/to/file /minion/dest  #将主服务器file_roots指定位置下的文件复制到被控主机
salt 'centos1' cp.get_url http://www.slashdot.org /tmp/index.html  #下载URL内容到被控主机指定位置
-----  API调用:client.cmd('centos1', 'cp.get_file',[' salt://path/to/file ',' /minion/dest'])
crontab定时任务

salt 'centos1' cron.raw_cron root    or  salt 'centos1' cron.list_tab root  #查看指定被控主机、root用户的crontab清单
salt 'centos1' cron.set_job root '*' '*' '*' '*' 1 /usr/local/weekly   #为指定的被控主机、root用户添加/usr/local/weekly任务
salt 'centos1' cron.rm_job root /usr/local/weekly  #删除指定的被控主机、root用户crontab的/usr/local/weekly任务
-----  API调用:client.cmd('centos1', 'cron.set_job',['root','*','*','*','*','*','/usr/echo'])
dnsutil DNS相关

salt 'centos1' dnsutil.hosts_append /etc/hosts 192.168.8.12 salt-master #添加指定被控主机hosts的主机配置项
salt 'centos1' dnsutil.hosts_remove /etc/hosts salt-master  #删除指定被控主机hosts的主机配置项
-----  API调用:client.cmd('*', 'dnsutil.hosts_append',['/etc/hosts','127.0.0.1','ad1.yuk.co'])
file文件

salt 'centos2' file.check_hash /etc/fstab md5:e4b13a1b984950ade18cbc8bc33be658  #校验被控主机/etc/fstab文件的md5
一致则返回True
salt '*' file.get_sum /etc/passwd md5   #校验所有被控主机文件的加密信息、支持md5、sha1、sha224、sha256、sha384、sha512
加密算法
salt '*' file.chown /etc/passwd root root  #修改所有被控主机/etc/passwd文件的属组、用户权限,
等价于chown root:root /etc/passwd
salt '*' file.copy /path/to/src /path/to/dst   #复制所有被控主机本地/path/to/src文件到本地的/path/to/dst文件
salt '*' file.directory_exists /etc   #检查所有被控主机/etc目录是否存在,存在则返回True,检查文件是否存在使用
file.file_exists方法
salt '*' file.stats /etc/passwd    #获取所有被控主机/etc/passwd的stats信息
salt '*' file.get_mode /etc/passwd  #获取所有被控主机/etc/passwd的权限mode,如755、644
salt '*' file.set_mode /etc/passwd 0644   #修改所有被控主机/etc/passwd的权限mode为0644
salt '*' file.mkdir /opt/test   #在所有被控主机创建/opt/test目录
salt '*' file.sed /etc/httpd/httpd.conf 'LogLevel warn' 'LogLevel info'  #将所有被控主机/etc/httpd/httpd.conf
文件的LogLevel参数的warn值修改成info
salt '*' file.append  /tmp/test/test.conf "maxclient 100"  #给所有被控主机的/tmp/test/test.conf文件追加内容"maxclient 100"
salt '*' file.remove /tmp/foo3   #删除所有被控主机的/tmp/foo文件
------  API调用:client.cmd('*', ' file.remove ',['/tmp/foo'])
iptables防火墙

salt '*' iptables.append filter INPUT rule='-m state --state RELATED,ESTABLISHED -j ACCEPT'
salt '*' iptables.insert filter INPUT position=3 rule='-m state --state RELATED,ESTABLISHED -j ACCEPT'
#在所有被控端主机追加(append)、插入(insert)iptables规则,其中INPUT为输入链
salt '*' iptables.delete filter INPUT position=3
salt '*' iptables.delete filter INPUT rule='-m state --state RELATED,ESTABLISHED-j ACCEPT'
#在所有被控端主机删除指定链编号为3(position=3)或指定存在的规则
salt '*' iptables.save /etc/sysconfig/iptables   #保存所有被控端主机规则到本地硬盘(/etc/sysconfig/iptables)
----  API调用: client.cmd('SN2013-08-022', 'iptables.append',['filter','INPUT','rule=\'-p tcp --sport 80 -j ACCEPT\''])
netwrok网络信息

salt 'centos2' network.dig www.qq.com
salt 'centos2' network.ping www.qq.com
salt 'centos2' network.traceroute www.qq.com
#在指定被控主机'centos2'获取dig、ping、traceroute目录域名信息
salt 'centos2' network.hwaddr eth0  #获取指定被控主机的MAC地址
salt 'centos2' network.in_subnet 10.0.0.0/16 #检测指定被控主机是否属于10.0.0.0/16子网范围,属于则返回True
salt 'centos2' network.interfaces #获取指定被控主机的网卡配置信息
salt 'centos2' network.ip_addrs  #获取指定被控主机的IP地址配置信息
salt 'centos2' network.subnets  #获取指定被控主机的子网信息
----  API调用:client.cmd('SN2013-08-022', 'network.ip_addrs')
pkg包管理

salt '*' pkg.install php  #为所有被控主机安装PHP环境,根据不同系统发行版调用不同安装工具进行部署,如redhat平台的yum,
等价于yum -y install php
salt '*' pkg.remove php  #卸载所有被控主机的PHP环境
salt '*' pkg.upgrade   #升级所有被控主机的软件包
----   API调用:client.cmd('SN2013-08-022', 'pkg.remove',['php'])
Service服务

salt '*' service.enable nginx
salt '*' service.disable nginx 
#开启(enable)、禁用(disable)nginx开机自启动服务
salt '*' service.reload nginx
salt '*' service.restart nginx
salt '*' service.start nginx
salt '*' service.stop nginx
salt '*' service.status nginx
#针对nginx服务的reload、restart、start、stop、status操作
-----  API调用: client.cmd('SN2013-08-022', 'service.stop',['nginx'])
other

Saltstack还提供了user(系统用户模块)、group(系统组模块)、partition(系统分区模块)、puppetpuppet管理模块)、
system(系统重启、关机模块)、timezone(时区管理模块)、nginxNginx管理模块)、mount(文件系统挂载模块),等等

自定义模块

分发机制
/usr/lib/python2.6/site-packages/salt/modules
minion执行,可以在master写分发到minion
根目录 fileroot:  /etc/salt/master
/srv/salt
1    module是在minion执行
2    master创建2次开发出来的modules,发布到所有的minion
3mkdir pv /srv/salt/_modules && cd /srv/salt/_modules
4、创建module文件并且定义函数
cat file.py             模块名File
def t1(num)            函数名
‘’‘salt \* file.t1 ’‘’     描述
return num*num    信息在函数的return
5、推送给所有的minion  salt '*' saltutil.sync_modules
会推送给minion的路径: /var/cache/salt/minion/extmods/modules
推送所有 : salt '*' saltutil.sync_all  包括以下所有的

zmq

saltstack
master 启动


REQSERVER
1 接收来自于client的请求,然后派发给空闲的Mworker
2 接收来自于minion的返回信息
4506 bind ROUTER
workers.ipc  bind  DEALER


Publisher
发布来自于client的请求给所有的minion
4505 bind  PUB
publish_pull.ipc bind PULL


EVENTPUBLISHER
主要是通知client,任务执行的状态,结果!
master_event_pull.ipc bind pull
master_event_pub.ipc bind PUB



MWORKER
多进程,实际处理工作
wokers.ipc connect  REP 


minion
4505 connect SUB (接收执行任务,数据以字典格式发送)
4605 connect REQ (返回执行结果)


执行具体任务的时候
job flow
1 salt '*' test.ping
创建了一个client对象
2 client 4506 REQ connect
发送执行项目
3 REQSERVER把请求交给可用的MWOKER,通过wokers.ipc

4 首先,确定你有权利这么做。然后通过publishclient交予的命令发送给所有的minionClearFuncs.publish()
MWokers 产生新的配对
publish_pull.ipc connect PUSH

5 你的woker宣告任务开始执行利用evnetpublish
master_event_pull.ipc  connect push
然后eventpublish通过pub方式把消息传送给所有的client订阅者
(连接到master_event_pub.ipc6 消息加密以后通过Publisher发送给所有的mininon

7 所有的minion通过4505的订阅接收消息

8 minion先解密消息,再在本地执行操作,但是事先会判断对象是不是自己

9 minion把响应数据加密后发送给master4506master把数据交给可用Mwoker

10 Mwoker接收数据后解密,然后通过eventpublish 发送相对应的状态通知

11 此时你的LocalClient就接收到返回信息了。或者等待超时。

12 当所有的minion都返回了结果,或者超时,则此次任务结束。
salt-zmq.png