<服务>saltstack更多用法

时间:May 3, 2017 分类:

目录:

对象管理

代号 含义 示例
G Grains glob匹配 G@os:CentOS
E PCRE minion id匹配(正则匹配) E@salt[1-3]
P Grains PCRE匹配 `P@os:(RedHat CentOS)`
L minios列表 L@why-1.whysdomain.com,why-2.whysdomain.com
I Pillor glob匹配 I@createdata:20170426
S IP或者网段匹配 S@192.168.0.0/24 or 192.168.1.133
R Range cluster匹配 R@%foo.bar
D Minion Data匹配 D@key:value
N 通过group来进行匹配 N@whysdomain.com
C 通过条件来进行匹配 C 'G@os:CentOS or L@salt02'

关于通过条件进行匹配的示例代码

[root@salt-master-00 ~]# salt -C "E@tengine-ads-A-* or E@ads-ads-api-0(2|5)" cmd.run "grep '30/Aug/2017:06:46'  /home/ec2-user/logs/nginx/api.chuchujie.com.access.log | awk -F '\x01' '{if(\$6>400){print \$0}}'"

首先要讲一下这个匹配规整的生效,命令在master端接收后通过zeroMQ发送到所有的minion主机,而在minion端进行匹配本机的id是否在此次命令的执行范围内,如果在执行范围内就进行执行并返回结果,如果没有在执行范围内,就不进行任何操作,也不会返回任何结果。

默认匹配规则

最简单的就是*来匹配所有的主机,例如salt0[2-3]或salt0[2,3]来匹配salt02和salt03,但是与正则的规则不同,就类似grep和egrep的区别

正则匹配

正则匹配通过匹配minion id的方式进行管理

salt -E 'sal(02|03)' test.ping

如果是写入到sls文件中

base:
  'sal(02|03)'
    - match: pcre
    - packages等等自定义的模块

列表匹配

通过列表方式指定minion id

salt -L salt02,salt03 test.ping

Grains匹配

salt -G 'os:RedHat' test.ping

自带的Grains

Grains官方文档

查看grains列表

[root@salt01 ~]# salt '*' grains.ls
salt03:
    - SSDs
    - biosreleasedate
    - biosversion
    - cpu_flags
    - cpu_model
    - cpuarch
    - domain
    - fqdn
    - fqdn_ip4
    - fqdn_ip6
    - gpus
    - host
    - hwaddr_interfaces
    - id
    - init
    - ip4_interfaces
    - ip6_interfaces
    - ip_interfaces
    - ipv4
    - ipv6
    - kernel
    - kernelrelease
    - locale_info
    - localhost
    - machine_id
    - manufacturer
    - master
    - mdadm
    - mem_total
    - nodename
    - num_cpus
    - num_gpus
    - os
    - os_family
    - osarch
    - oscodename
    - osfinger
    - osfullname
    - osmajorrelease
    - osrelease
    - osrelease_info
    - path
    - productname
    - ps
    - pythonexecutable
    - pythonpath
    - pythonversion
    - saltpath
    - saltversion
    - saltversioninfo
    - selinux
    - serialnumber
    - server_id
    - shell
    - virtual
    - zmqversion

查看每一项的详细信息

[root@salt01 ~]# salt '*' grains.items

查看os项的详细信息

[root@salt01 ~]# salt '*' grains.item os
salt02:
    ----------
    os:
        RedHat
salt03:
    ----------
    os:
        RedHat
[root@salt01 ~]# salt -G 'os:RedHat' grains.item cpuarch 
salt02:
    ----------
    cpuarch:
        x86_64
salt03:
    ----------
    cpuarch:
        x86_64

自定义grains

[root@salt02 ~]# vi /etc/salt/minion
#grains:
#  roles:   #属性
#    - webserver    #值(列表形式)
#    - memcache
#  deployment: datacenter4  #属性: 值
#  cabinet: 13
#  cab_u: 14-15

简单添加

grains:
  test: why

重启服务生效

[root@salt02 ~]# /etc/init.d/salt-minion restart
Stopping salt-minion daemon:                               [  OK  ]
Starting salt-minion daemon:                               [  OK  ]

[root@salt01 ~]# salt -G 'os:RedHat' grains.item test
salt02:
    ----------
    test:
        why
salt03:
    ----------
    test:

如果是想获取字典的就需要

[root@salt01 ~]# salt '*' grains.item hwaddr_interfaces
salt02:
    ----------
    hwaddr_interfaces:
        ----------
        eth0:
            00:0c:29:ac:a6:7c
        lo:
            00:00:00:00:00:00
salt03:
    ----------
    hwaddr_interfaces:
        ----------
        eth0:
            00:0c:29:07:d4:cd
        lo:
            00:00:00:00:00:00
[root@salt01 ~]# salt '*' grains.item hwaddr_interfaces:eth0
salt02:
    ----------
    hwaddr_interfaces:eth0:
        00:0c:29:ac:a6:7c
salt03:
    ----------
    hwaddr_interfaces:eth0:
        00:0c:29:07:d4:cd

组匹配

salt -N test test.ping 

组需要在master端定义,组内也可以通过其他匹配方式构成组,当然也可以把两个甚至更多的组组成一个组

[root@salt01 ~]# vi /etc/salt/master 
#nodegroups:
#  group1: 'L@foo.domain.com,bar.domain.com,baz.domain.com and bl*.domain.com'
#  group2: 'G@os:Debian and foo.domain.com'
nodegroups:
  test: 'E@salt*'
[root@salt01 ~]# salt -N test test.ping 
salt03:
    True
salt02:
    True

更多的组配置方式,未完待续

复合匹配

salt -C 'G@os:CentOS and L@salt02,salt03'

如果是写入sls文件

base:
  'G@os:CentOS and L@salt02,salt03'
    - match: compound
    - webserver

Pillar值匹配

通过master端定义minion的数据,pillar在解析完成后,是一个嵌套的dict结构,最上层的key是minion ID,其value是该minion所拥有的所有数据,每一个value也是以key/value的形式存在,piller数据与特定的minion关联,每一个minion只能看到自己的数据,可以用来传递敏感数据

可以用于敏感数据,例如ssh key,加密证书等,由于pillar使用独立加密的session,可以确保敏感数据不被其他minion看到 变量,可以在pillar中处理平台的差异性,比如针对不同的操作系统设置不同的软件包的名字,在state中引用 任何数据,可以定义用户和UID的对应关系,minion的角色等

未完待续

CIDR匹配


模块

常用模块

  • test
  • service
  • cmd
  • saltutil
  • file
  • pkg
  • salt '*' pkg.install httpd 安装软件包
  • salt '*' cp.getfile salt://httpd.conf /etc/httpd/conf/httpd.conf 分发文件 当然,如果是拷贝master所在文件系统的文件,可以使用 salt-cp '*' /usr/local/apache/conf/httpd.conf /etc/httpd/conf/httpd.conf
  • salt '*' service.start httpd 启动服务

文件系统

[root@salt01 ~]# vi /etc/salt/master 
# Example:
# file_roots:
#   base:                       #基础
#     - /srv/salt/
#   dev:                        #开发
#     - /srv/salt/dev/services
#     - /srv/salt/dev/states
#   prod:                       #生产
#     - /srv/salt/prod/services
#     - /srv/salt/prod/states

可以看到给我们提供的例子,我们在操作的过程中会在不同的仓库来获取一些文件,例如压缩包,配置文件等,可根据需要来设置多个。

取消注释

file_roots:
  base:
    - /srv/salt

生成目录

[root@salt01 ~]# mkdir /srv/salt
[root@salt01 ~]# cd !$
cd /srv/salt
[root@salt01 salt]# mkdir etc
[root@salt01 salt]# mkdir script
[root@salt01 salt]# cd script/

远程执行

创建测试脚本

[root@salt01 salt]# vi /srv/salt/script/test.sh
#!/bin/bash
while true
do
    sleep 1
    echo 1 >> /tmp/log
done

一个死循环脚本,这样可以看到这个脚本的情况

执行脚本

[root@salt01 script]# salt -N test cmd.script salt://script/test.sh

可以看都master端是启动一个python执行刚才输入的命令

[root@salt01 ~]# ps -ef | grep cmd.script
root      7920  6321  2 01:08 pts/3    00:00:06 /usr/bin/python2.6 /usr/bin/salt -N test cmd.script salt://script/test.sh
root      8058  6818  0 01:12 pts/4    00:00:00 grep --color cmd.script

而minion端会在tmp目录下生成一个随机名字的脚本

[root@salt02 ~]# ps -ef | grep /bin/bash
root      4513  4510  0 01:09 ?        00:00:00 /bin/bash /tmp/tmpuDpb5k.sh
root      4749  3926  0 01:11 pts/1    00:00:00 grep --color /bin/bash

配置管理

[root@salt01 salt]# vi hosts.sls
[root@salt01 salt]# cat hosts.sls
/tmp/hosts:
  file.managed:
    - source: salt://etc/hosts
    - user: root
    - group: root
    - mode: 600

第一行,为name,可以随便定义,就是这个文件需要同步的绝对路径,如果在第一行没有定义name,需要在下边file.manager中添加- name: /tmp/hosts,这个只是做个示范 第二行,file.managed:代表使用file模块的managed方法来管理这个文件 第三行,- source: salt://etc/hosts代表源文件的地址,后面分别是用户,组和权限

[root@salt01 salt]# vi top.sls
[root@salt01 salt]# cat top.sls 
base:
  '*':
    - hosts

第一行,base:对应使用file_roots配置的仓库,base对应的即为/srv/salt 第二行,'*'匹配的minion 第三行,- hosts对应使用的sls文件,此处代指hosts.sls

拷贝需要同步的配置文件到仓库中

[root@salt01 salt]# cp /etc/hosts /srv/salt/etc/

执行命令

[root@salt01 salt]# salt '*' state.highstate
salt02:
----------
          ID: /tmp/hosts
    Function: file.managed
      Result: True
     Comment: File /tmp/hosts updated
     Started: 23:35:41.932641
    Duration: 342.232 ms
     Changes:   
              ----------
              diff:
                  New file

Summary
------------
Succeeded: 1 (changed=1)
Failed:    0
------------
Total states run:     1
salt03:
----------
          ID: /tmp/hosts            #name名字
    Function: file.managed          #使用的函数
      Result: True
     Comment: File /tmp/hosts updated
     Started: 23:35:41.939952
    Duration: 370.099 ms
     Changes:   
              ----------
              diff:                 #会做文件比对,文件修改了什么,但是此次是一个新文件
                  New file

Summary
------------
Succeeded: 1 (changed=1)            #因为命令是直接执行top.sls中的所有,会返回成功了多少
Failed:    0
------------
Total states run:     1


[root@salt01 salt]# cat /srv/salt/etc/hosts 
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

[root@salt02 ~]# cat /tmp/hosts 
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

如果是调试可以加test=True

salt '*' state.highstate test=True

修改此文件

[root@salt01 salt]# echo '192.168.0.191 salt01' >> /srv/salt/etc/hosts
[root@salt01 salt]# salt '*' state.sls hosts        #state.sls是在salt文件系统的根目录进行查找,hosts会自动补全为hosts.sls
salt03:
----------
          ID: /tmp/hosts
    Function: file.managed
      Result: True
     Comment: File /tmp/hosts updated
     Started: 23:42:57.820794
    Duration: 942.963 ms
     Changes:   
              ----------
              diff:
                  ---  
                  +++  
                  @@ -1,2 +1,3 @@
                   127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
                   ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
                  +192.168.0.191 salt01

Summary
------------
Succeeded: 1 (changed=1)
Failed:    0
------------
Total states run:     1
salt02:
----------
          ID: /tmp/hosts
    Function: file.managed
      Result: True
     Comment: File /tmp/hosts updated
     Started: 23:42:58.065344
    Duration: 1171.583 ms
     Changes:   
              ----------
              diff:
                  ---  
                  +++  
                  @@ -1,2 +1,3 @@
                   127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
                   ::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
                  +192.168.0.191 salt01

Summary
------------
Succeeded: 1 (changed=1)
Failed:    0
------------
Total states run:     1

如果在目录结构中,可以通过以下方式执行

[root@salt01 salt]# echo '192.168.0.192 salt02' >> /srv/salt/etc/hosts
[root@salt01 salt]# mkdir /srv/salt/hosts
[root@salt01 salt]# mv /srv/salt/hosts.sls /srv/salt/hosts
[root@salt01 salt]# salt '*' state.sls hosts.hosts

还有这个目录下的sls文件会有一个入口文件的,为init.sls

[root@salt01 salt]# mv /srv/salt/hosts/hosts.sls /srv/salt/hosts/init.sls
[root@salt01 salt]# salt '*' state.sls hosts

默认是先查找hosts.init.sls,如果没有,就会查找hosts.sls,如果两个都不存在就会报错

Batch Size

salt支持指定执行命令的主机数量,可以按照百分比或者直接指定固定数量,通过-b(--batch-size)参数实现,原理是batch size会维护一个运行的minions数量的窗口,第一次指定数量的minion运行,一旦有一个minion执行完成,会立刻新增一个minion进行执行。

salt '*' -b 10 test.ping 或者 salt '*' -b 20% service.restart apache

salt语法

YAML

state状态的核心为sls,语法为YAML

YAML语法使用固定的缩进风格表示数据层结构关系,salt需要的每个缩进级别由两个空格组成,字典的话以冒号为结尾的字符串,每个value在冒号后用空格隔开,列表项则为一个短横杠进和一个空格,多个项用相同的缩进级别作为同一列表的一个部分。更多的内容可以参考http://docs.saltstack.cn/topics/yaml/index.html

模块用法 https://docs.saltstack.com/en/latest/ref/states/all/index.html

https://docs.saltstack.com/en/latest/ref/states/all/salt.states.file.html#module-salt.states.file

通过python进行演示

[root@salt01 ~]# vi test.yaml
[root@salt01 ~]# cat test.yaml 
why:
  - name: why
  - age: 24
mb:
  - name: mabiao
  - age: 26
[root@salt01 ~]# vi testyaml.py
[root@salt01 ~]# cat testyaml.py 
#!/usr/bin/env python
import yaml
import sys

fd = open(sys.argv[1])

print yaml.load(fd)
[root@salt01 ~]# chmod +x testyaml.py 
[root@salt01 ~]# ./testyaml.py test.yaml 
{'why': [{'name': 'why'}, {'age': 24}], 'mb': [{'name': 'mabiao'}, {'age': 26}]}

这样yaml就被解析为python的字典了

官方示例

/etc/http/conf/http.conf:
  file.managed:
    - source: salt://apache/http.conf
    - user: root
    - group: root
    - mode: 644
    - template: jinja
    - defaults:
        custom_var: "default value"
        other_var: 123
{% if grains['os'] == 'Ubuntu' %}
    - context:
        custom_var: "override"
{% endif %}

根据系统进行匹配

jinja2

salt默认使用jinja2模板系统生成yaml

  • 变量: {{foo}},变量的属性可以通过{{foo.bar}}或者{{foo['bar']}}
  • 注释: {# comment #}
  • for:
{% for eachitem in items %}
{{eachitem}}
{% endfor %}
  • if:
{% if GFW %}
Welcome to China!
{% elif not Internet %}
Welcome to North Korea!
{% else %}
Freedon!
{% endif %}

requisites

  • require:本state执行时需要先执行哪些state,用于前置条件,例如同步配置文件前需要安装软件包
  • require_in:与require位置相反
  • watch:除了require之外,也会对监控依赖的state的状态,如果状态发生改变,做出反应,例如配置文件变更,重启服务或者reload
  • watch_in:与watch位置相反
  • prereq:通过test=True接口检查依赖状态
  • prereq_in:与prereq位置相反

可用的模块

注意,以下模块与远程执行模块同名,但不一样

软件包状态模块

https://docs.saltstack.com/en/latest/ref/states/all/salt.states.pkg.html

  • pkg.installed 确保软件包安装,如果没有安装则进行安装
  • pkg.latest 确保软件包是最新版本,如果不是最新版本,进行升级
  • pkg.remove 确保软件包已卸载,如果之前已安装则进行卸载
  • pkg.purge 除了remove还会删除配置文件

文件状态模块

https://docs.saltstack.com/en/latest/ref/states/all/salt.states.file.html

  • file.managed 确保文件存在且为对应状态
  • file.recurse 确保目录存在且为对应状态
  • file.absent 确保文件不存在,如果存在则删除

服务状态模块

https://docs.saltstack.com/en/latest/ref/states/all/salt.states.service.html

  • service.running 确保服务处于运行状态,如果没有运行则进行启动
  • service.enabled 确保服务会开机启动
  • service.disabled 确保服务不会开机启动
  • service.dead 确保服务当前没有运行

saltstack安装nginx

[root@salt01 ~]# cd /srv/salt/
[root@salt01 salt]# mkdir nginx
[root@salt01 salt]# cd nginx
[root@salt01 nginx]# vi nginx.sls
nginx:
  pkg:                          #pkg模块
    - installed                 #installed方法
  service:                      #service模块
    - running                   #启动状态
    - enable: True              #是否允许进行相应的操作,例如restart,reload
    - reload: True              #允许进行reload
    - watch:                    #reload的触发条件,触发条件执行reload
      - pkg: nginx              #监控的服务
      - file: /etc/nginx/nginx.conf         #监控的变动的文件,对应下方的/etc/nginx/nginx.conf
      - file: /etc/nginx/conf.d/default.conf    #监控的变动的文件,对应下边的/etc/nginx/conf.d/default.conf,也可以进行自定义,就是需要配置的过程中添加- name
/etc/nginx/nginx.conf:
  file.managed:
    - source: salt://etc/nginx/nginx.conf
    - user: root
    - group: root
    - mode: 644
    - request:
      - pkg: nginx 
/etc/nginx/conf.d/default.conf:
  file.managed:
    - source: salt://etc/nginx/conf.d/default.conf
    - user: root
    - group: root
    - mode: 644
    - request:
      - pkg: nginx 

当然也可以有另一种写法,例如

  serivce.running:
    - enable: True

而不写

  service:        
    - running     
    - enable: True

还有就是request和request_in的用法,如果这个文件需要用require_in来写

nginx:
  pkg:                          
    - installed 
    - require_in:
      - file: /etc/nginx/nginx.conf
      - file: /etc/nginx/conf.d/default.conf
  service:                      
    - running                   
    - enable: True              
    - reload: True              
    - watch:                    
      - pkg: nginx              
      - file: /etc/nginx/nginx.conf         
      - file: /etc/nginx/conf.d/default.conf    
/etc/nginx/nginx.conf:
  file.managed:
    - source: salt://etc/nginx/nginx.conf
    - user: root
    - group: root
    - mode: 644
/etc/nginx/conf.d/default.conf:
  file.managed:
    - source: salt://etc/nginx/conf.d/default.conf
    - user: root
    - group: root
    - mode: 644

获取nginx配置文件

[root@salt01 nginx]# yum install -y nginx
[root@salt01 nginx]# cp /etc/nginx/nginx.conf /srv/salt/etc/nginx/
[root@salt01 nginx]# cp /etc/nginx/conf.d/default.conf /srv/salt/etc/nginx/conf.d/

远程执行

[root@salt01 nginx]# salt '*' state.sls nginx.nginx

可以看到两台minion的80端口都已经打开

[root@salt01 nginx]# salt '*' cmd.run 'ss -nlt|grep 80'
salt02:
    LISTEN     0      128                      :::80                      :::*     
    LISTEN     0      128                       *:80                       *:*
salt03:
    LISTEN     0      128                      :::80                      :::*     
    LISTEN     0      128                       *:80                       *:*

修改配置文件

listen       80 default_server;
改为
listen       8080 default_server;

通过minion端执行命令

[root@salt02 ~]# salt-call state.sls nginx.nginx 
[INFO    ] Loading fresh modules for state activity
[INFO    ] Fetching file from saltenv 'base', ** done ** 'nginx/nginx.sls'
[ERROR   ] You should upgrade pyOpenSSL to at least 0.14.1 to enable the use of X509 extensions
[INFO    ] Running state [nginx] at time 08:05:17.334971
[INFO    ] Executing state pkg.installed for nginx
[INFO    ] Executing command ['rpm', '-qa', '--queryformat', '%{NAME}_|-%{EPOCH}_|-%{VERSION}_|-%{RELEASE}_|-%{ARCH}_|-(none)\n'] in directory '/root'
[INFO    ] Package nginx is already installed.
[INFO    ] Completed state [nginx] at time 08:05:18.590533
[INFO    ] Running state [/etc/nginx/nginx.conf] at time 08:05:18.610151
[INFO    ] Executing state file.managed for /etc/nginx/nginx.conf
[INFO    ] File /etc/nginx/nginx.conf is in the correct state
[INFO    ] Completed state [/etc/nginx/nginx.conf] at time 08:05:18.633830
[INFO    ] Running state [/etc/nginx/conf.d/default.conf] at time 08:05:18.636151
[INFO    ] Executing state file.managed for /etc/nginx/conf.d/default.conf
[INFO    ] Fetching file from saltenv 'base', ** done ** 'etc/nginx/conf.d/default.conf'
[INFO    ] File changed:
---  
+++  
@@ -3,7 +3,7 @@
 #

 server {
-    listen       80 default_server;
+    listen       8080 default_server;
     listen       [::]:80 default_server;
     server_name  _;
     root         /usr/share/nginx/html;

[INFO    ] Completed state [/etc/nginx/conf.d/default.conf] at time 08:05:18.924077
[INFO    ] Running state [nginx] at time 08:05:18.925517
[INFO    ] Executing state service.running for nginx
[INFO    ] Executing command '/sbin/service nginx status' in directory '/root'
[INFO    ] Executing command '/sbin/chkconfig --list nginx' in directory '/root'
[INFO    ] Executing command '/sbin/runlevel' in directory '/root'
[INFO    ] Executing command '/sbin/runlevel' in directory '/root'
[INFO    ] Executing command '/sbin/chkconfig --list nginx' in directory '/root'
[INFO    ] Executing command '/sbin/chkconfig nginx on' in directory '/root'
[INFO    ] Executing command '/sbin/chkconfig --list nginx' in directory '/root'
[INFO    ] Executing command '/sbin/runlevel' in directory '/root'
[INFO    ] {'nginx': True}
[INFO    ] Completed state [nginx] at time 08:05:19.150004

local:
----------
          ID: nginx
    Function: pkg.installed
      Result: True
     Comment: Package nginx is already installed.
     Started: 08:05:17.334971
    Duration: 1255.562 ms
     Changes:   
----------
          ID: /etc/nginx/nginx.conf
    Function: file.managed
      Result: True
     Comment: File /etc/nginx/nginx.conf is in the correct state
     Started: 08:05:18.610151
    Duration: 28.544 ms
     Changes:   
----------
          ID: /etc/nginx/conf.d/default.conf
    Function: file.managed
      Result: True
     Comment: File /etc/nginx/conf.d/default.conf updated
     Started: 08:05:18.636151
    Duration: 261.897 ms
     Changes:   
              ----------
              diff:
                  ---  
                  +++  
                  @@ -3,7 +3,7 @@
                   #

                   server {
                  -    listen       80 default_server;
                  +    listen       8080 default_server;
                       listen       [::]:80 default_server;
                       server_name  _;
                       root         /usr/share/nginx/html;
----------
          ID: nginx
    Function: service.running
      Result: True
     Comment: Service reloaded
     Started: 08:05:18.925517
    Duration: 204.327 ms
     Changes:   
              ----------
              nginx:
                  True

Summary
------------
Succeeded: 4 (changed=2)
Failed:    0
------------
Total states run:     4

在salt02节点上也可以看到端口变为8080

[root@salt02 ~]# ss -nlpt | grep 8080
LISTEN     0      128                       *:8080                     *:*      users:(("nginx",23545,11),("nginx",25037,11))

Schedule

Schedule可以定时执行任务,可以在master,minion和pillar配置

schedule:                   #固定语法
  job1:                     #名字            
    function: state.sls     #模块.方法
    seconds: 3600           #时间
    args:                   
      - httpd               #传参
    kwargs:                 
      test: True            #也可以传参以字典的形式

还有更多的splay,when等方式,都可以参考http://docs.saltstack.cn/topics/jobs/#scheduling-jobs

定义目录

pillar属于一个单独的模块,需要定义目录

[root@salt01 ~]# vi /etc/salt/master
#pillar_roots:
#  base:
#    - /srv/pillar

取消默认注释的即可

配置定时任务

[root@salt01 ~]# mkdir -p /srv/pillar
[root@salt01 ~]# cd /srv/pillar
[root@salt01 pillar]# vi top.sls
base:
  '*':
    - nginx

[root@salt01 pillar]# vi nginx.sls
schedule:
  nginx:
    function: state.sls
    minutes: 1
    args:
      - 'nginx.nginx'

查看minion的pillar信息

[root@salt01 pillar]# salt '*' pillar.data
salt03:
    ----------
    schedule:
        ----------
        nginx:
            ----------
            args:
                - nginx.nginx
            function:
                state.sls
            minutes:
                1
salt02:
    ----------
    schedule:
        ----------
        nginx:
            ----------
            args:
                - nginx.nginx
            function:
                state.sls
            minutes:
                1

查看minion端是否生效

因为在上边修改salt base库中的配置文件中的端口为8080,salt02是通过salt-call进行配置同步的,这里salt03还有没有变更,看一下配置pillar后salt03的nginx的端口。

[root@salt03 ~]# ss -nlput | grep 8080
[root@salt03 ~]#

可以看到配置完pillar只是在master端生效。

刷新配置

[root@salt01 pillar]# salt '*' saltutil.refresh_pillar
salt02:
    True
salt03:
    True

再次验证

[root@salt03 ~]# ss -nlput | grep 8080
[root@salt03 ~]# ss -nlput | grep 8080
[root@salt03 ~]# ss -nlput | grep 8080
tcp    LISTEN     0      128                    *:8080                  *:*      users:(("nginx",23568,11),("nginx",37170,11))

如果出现不生效,可以修改一下salt仓库的配置文件

jinja模板管理配置文件

通过直接修改配置文件过于麻烦,可以通过jinja模板来实现通过在sls文件定义配置文件内容

修改被管理文件

[root@salt01 ~]# vi /srv/salt/etc/nginx/conf.d/default.conf
server {
    listen       8080 default_server;
改为
server {
    listen       {{ port }} default_server;

模板添加映射

[root@salt01 ~]# vi /srv/salt/nginx.sls
nginx:
  pkg:
    - installed
  service:
    - running
    - enable: True
    - reload: True
    - watch:
      - pkg: nginx
      - file: /etc/nginx/nginx.conf
      - file: /etc/nginx/conf.d/default.conf
/etc/nginx/nginx.conf:
  file.managed:
    - source: salt://etc/nginx/nginx.conf
    - user: root
    - group: root
    - mode: 644
/etc/nginx/conf.d/default.conf:
  file.managed:
    - source: salt://etc/nginx/conf.d/default.conf
    - user: root
    - group: root
    - mode: 644
    - template: jinja
    - context:
        port: 9090

指定了jinja的模板,然后给予了port对应的值9090

执行命令

[root@salt01 ~]# salt '*' state.sls nginx
          ID: /etc/nginx/conf.d/default.conf
    Function: file.managed
      Result: True
     Comment: File /etc/nginx/conf.d/default.conf updated
     Started: 01:34:05.127787
    Duration: 463.375 ms
     Changes:   
              ----------
              diff:
                  ---  
                  +++  
                  @@ -3,7 +3,7 @@
                   #

                   server {
                  -    listen       8080 default_server;
                  +    listen       9090 default_server;
                       listen       [::]:80 default_server;
                       server_name  _;
                       root         /usr/share/nginx/html;

可以在执行结果中看到配置文件的变更,minion在产生配置文件的时候已经进行了渲染,替换port为9090

minion端查询

[root@salt02 ~]# ss -nlpt | grep 9090
LISTEN     0      128                       *:9090                     *:*      users:(("nginx",23545,10),("nginx",44932,10))

根据minion进行匹配

如果是需要根据minion进行匹配

更改模板映射(添加判断)

[root@salt01 ~]# vi /srv/salt/nginx.sls
template和context可以配置为
    - template: jinja
    - context:
        {% if grains['id'] == 'salt02' %}
        port: 9092
        {% elif grains['id'] == 'salt03' %}
        port: 9093
        {% else %}
        port: 9091
        {% endif %}

执行命令

[root@salt01 ~]# salt '*' state.sls nginx

检查执行结果

[root@salt02 ~]# ss -nlpt | grep '\b90'
LISTEN     0      128                       *:9092                     *:*      users:(("nginx",23545,11),("nginx",49584,11))
[root@salt03 ~]# ss -nlpt | grep '\b90'
LISTEN     0      128                       *:9093                     *:*      users:(("nginx",23568,11),("nginx",49223,11))

通过pillar

[root@salt01 ~]# cd /srv/pillar/
[root@salt01 pillar]# mkdir nginx
[root@salt01 pillar]# cd nginx
[root@salt01 nginx]# vi init.sls
nginx:
  {% if grains.id == 'salt02' %}
  port: 9192
  {% elif grains.id == 'salt03' %}
  port: 9193
  {% else %}
  port: 9190
  {% endif %}
[root@salt01 nginx]# cd ..
[root@salt01 pillar]# vi top.sls
base:
  '*':
    - nginx

刷新pillar并检验

[root@salt01 ~]# salt '*' saltutil.refresh_pillar
salt02:
    True
salt03:
    True
[root@salt04 ~]# salt '*' pillar.get nginx:port
salt02:
    9192
salt03:
    9193

修改sls文件

[root@salt04 ~]# vi /srv/salt/nginx.sls
    - template: jinja
    - context:
        port: {{ salt['pillar.get']('nginx:port',80) }}

当然这样是一个负责的写法,即使在minion中没有pillar被定义nginx的key,也会使用默认的端口80

不负责任的写法就是

        port: pillar['nginx']['port']

这个问题在于,如果minion中没有pillar被定义nginx的key,就会报错

检验

[root@salt04 ~]# salt '*' state.sls nginx

可以在minion上检查

[root@salt02 ~]# ss -nlpt | grep '\b91'
LISTEN     0      128                       *:9192                     *:*      users:(("nginx",23545,11),("nginx",49584,11))
[root@salt03 ~]# ss -nlpt | grep '\b91'
LISTEN     0      128                       *:9193                     *:*      users:(("nginx",23568,11),("nginx",49223,11))

如果是salt '' state.highstate nginx执行,可以不执行salt '' saltutil.refresh_pillar,因为在执行的过程会刷新pillar信息

官方还有更多的实例提供 https://github.com/saltstack-formulas

salt proxy

proxy是为了解决salt master处理时间过长的问题,采取通过在master端和minion端添加proxy代理层的方式实现,原理是master端发起命令后,将命令原封不动发送给proxy端,proyx接收命令后,对选定主机进行匹配,发送对应命令给minion端,minion端执行后将结果返回给proxy端,proxy端再返回给master端。

proxy上也有一个master,通过syndic接收由master进行执行

syndic接口允许建立salt命令拓扑,通过master连接到master上指挥minion。

IP 主机名 作用
192.168.0.191 salt01 salt-master
192.168.0.192 salt02 salt-minion
192.168.0.193 salt03 salt-minion
192.168.0.194 salt04 salt-syndic
192.168.0.195 salt05 salt-syndic

清除缓存的key

[root@salt01 ~]# rm -rf /etc/salt/pki/
[root@salt01 ~]# salt-key -D -y
Deleting the following keys:
Accepted Keys:
salt02
salt03
Key for minion salt02 deleted.
Key for minion salt03 deleted.

删除pki目录

生产环境下建议改名

[root@salt02 ~]# rm -rf /etc/salt/pki/
[root@salt02 ~]# /etc/init.d/salt-minion restart
Stopping salt-minion daemon:                               [  OK  ]
Starting salt-minion daemon:                               [  OK  ]
[root@salt03 ~]# rm -rf /etc/salt/pki/
[root@salt03 ~]# /etc/init.d/salt-minion restart
Stopping salt-minion daemon:                               [  OK  ]
Starting salt-minion daemon:                               [  OK  ]

准备环境

[root@salt04 ~]# rpm -ivh http://mirror.centos.org/centos/6.8/extras/x86_64/Packages/epel-release-6-8.noarch.rpm
Retrieving http://mirror.centos.org/centos/6.8/extras/x86_64/Packages/epel-release-6-8.noarch.rpm
warning: /var/tmp/rpm-tmp.G3MsQu: Header V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY
Preparing...                ########################################### [100%]
   1:epel-release           ########################################### [100%]
[root@salt04 ~]# /usr/sbin/ntpdate pool.ntp.org
29 Apr 02:45:41 ntpdate[3046]: step time server 85.199.214.100 offset -28803.232360 sec
[root@salt04 ~]# echo '*/5 * * * * /usr/sbin/ntpdate pool.ntp.org >/dev/null 2>&1' >> /var/spool/cron/root
[root@salt04 ~]# service iptables stop
iptables: Setting chains to policy ACCEPT: filter          [  OK  ]
iptables: Flushing firewall rules:                         [  OK  ]
iptables: Unloading modules:                               [  OK  ]
[root@salt04 ~]# yum install -y salt-master salt-syndic

修改配置文件

[root@salt04 ~]# vi /etc/salt/master
#syndic_master: masterofmaster
改为
syndic_master: salt01

#order_masters: False
改为
order_masters: True

启动服务

[root@salt04 ~]# /etc/init.d/salt-master start
Starting salt-master daemon:                               [  OK  ]
[root@salt04 ~]# /etc/init.d/salt-syndic start
Starting salt-syndic daemon:                               [  OK  ]

认证key

[root@salt01 ~]# salt-key -A -y
The following keys are going to be accepted:
Unaccepted Keys:
salt04
salt05
Key for minion salt04 accepted.
Key for minion salt05 accepted.

minion的master修改为proxy

[root@salt02 ~]# vi /etc/salt/minion
master: salt01
改为
master: salt04
[root@salt02 ~]# /etc/init.d/salt-minion restart
Stopping salt-minion daemon:                               [  OK  ]
Starting salt-minion daemon:                               [  OK  ]
[root@salt03 ~]# vi /etc/salt/minion
master: salt01
改为
master: salt05
[root@salt03 ~]# /etc/init.d/salt-minion restart
Stopping salt-minion daemon:                               [  OK  ]
Starting salt-minion daemon:                               [  OK  ]

认证key

[root@salt04 ~]# salt-key -L
Accepted Keys:
Denied Keys:
Unaccepted Keys:
salt02
Rejected Keys:
[root@salt04 ~]# salt-key -A -y
The following keys are going to be accepted:
Unaccepted Keys:
salt02
Key for minion salt02 accepted.
[root@salt04 ~]# salt '*' test.ping
salt02:
    True
[root@salt05 ~]# salt-key -L
Accepted Keys:
Denied Keys:
Unaccepted Keys:
salt03
Rejected Keys:
[root@salt05 ~]# salt-key -A -y
The following keys are going to be accepted:
Unaccepted Keys:
salt03
Key for minion salt03 accepted.
[root@salt05 ~]# salt '*' test.ping
salt03:
    True

测试

[root@salt01 ~]# salt '*' test.ping
salt02:
    True
salt03:
    True

另外需要注意的是,虽然此时我们执行test.ping可能正常获取返回,但是自定义模块无法正常获取返回

[root@salt01 ~]# salt '*' state.sls nginx
salt03:
    Data failed to compile:
----------
    No matching sls found for 'nginx' in env 'base'
salt02:
    Data failed to compile:
----------
    No matching sls found for 'nginx' in env 'base'

这是因为minion是从proxy获取这些sls文件,也就是需要在proxy上配置base和pillar并同步sls文件才行

job管理

jid为job id,格式为%Y%m%d%H%M%S%f

显示JID

[root@salt01 ~]# salt '*' test.ping -v
Executing job with jid 20170502230853712401
-------------------------------------------

salt02:
    True
salt03:
    True

查看job执行过程

[root@salt01 ~]# salt '*' cmd.run 'sleep 200' -v

可以在minion下缓存目录看到jid的文件,而master会通过判断minion端的jid文件是否存在确定命令是否还在执行

[root@salt02 ~]# ll /var/cache/salt/minion/proc/
total 4
-rw-r--r--. 1 root root 99 May  2 23:22 20170502232214623279

master端也在缓存目录下记录返回结果,不过是以hash的方式,默认缓存24小时

[root@salt01 ~]# ls /var/cache/salt/master/jobs/
04  06  0a  15  17  23  27  2e  35  3b  3d  41  46  4b  51  53  56  5a  62  64  68  6f  75  7c  80  88  8f  92  94  98  a0  a9  af  b8  ba  c0  c2  c6  c9  cd  d6  d8  e6  eb  f3  f6  fe
05  08  12  16  1e  24  2a  32  3a  3c  3e  43  49  4f  52  54  57  5c  63  65  6e  72  7b  7d  83  8c  91  93  96  9c  a7  aa  b7  b9  bb  c1  c4  c7  cc  d0  d7  d9  ea  ee  f5  fb  ff

查看所有job命令

[root@salt01 ~]# salt-run jobs.list_jobs

查看jid返回结果

[root@salt01 ~]# salt-run jobs.lookup_jid  20170502230853712401
salt02:
    True
salt03:
    True

查看当前所有minion正在运行的jobs

[root@salt01 ~]# salt-run jobs.active

可以通过刚才执行sleep 200的命令后,新开启终端执行

[root@salt01 ~]# salt-run jobs.active
20170503000138921306:
    ----------
    Arguments:
        - sleep 200
    Function:
        cmd.run
    Returned:
    Running:
        |_
          ----------
          salt02:
              18692
        |_
          ----------
          salt03:
              17965
    Target:
        *
    Target-type:
        glob
    User:
        root

当然也有另一种查看方式

[root@salt01 ~]# salt '*' saltutil.running
salt02:
    |_
      ----------
      arg:
          - sleep 200
      fun:
          cmd.run
      jid:
          20170503000138921306
      pid:
          18692
      ret:
      tgt:
          *
      tgt_type:
          glob
      to:
          4
      user:
          root
salt03:
    |_
      ----------
      arg:
          - sleep 200
      fun:
          cmd.run
      jid:
          20170503000138921306
      pid:
          17965
      ret:
      tgt:
          *
      tgt_type:
          glob
      to:
          4
      user:
          root

不过区别是,后者在查看以前的job的时候是无法查看的

[root@salt01 ~]# salt '*' saltutil.find_job 20170502230853712401
salt02:
    ----------
salt03:
    ----------
[root@salt01 ~]# salt '*' saltutil.find_job 20170503000847093876
salt02:
    ----------
    arg:
        - sleep 200
    fun:
        cmd.run
    jid:
        20170503000847093876
    pid:
        19328
    ret:
    tgt:
        *
    tgt_type:
        glob
    to:
        4
    user:
        root
salt03:
    ----------
    arg:
        - sleep 200
    fun:
        cmd.run
    jid:
        20170503000847093876
    pid:
        18601
    ret:
    tgt:
        *
    tgt_type:
        glob
    to:
        4
    user:
        root

发送信号

发送指定信号

[root@salt01 ~]# salt '*' saltutil.signal_job 201705030008470938769 9
salt02:
salt03:

发送终止任务信号

[root@salt01 ~]# salt '*' saltutil.term_job 20170503001108876741
salt02:
    Signal 15 sent to job 20170503001108876741 at pid 19582
salt03:
    Signal 15 sent to job 20170503001108876741 at pid 18855

发送

[root@salt01 ~]# salt '*' saltutil.kill_job  20170503001230542501 
salt02:
    Signal 9 sent to job 20170503001230542501 at pid 19704
salt03:
    Signal 9 sent to job 20170503001230542501 at pid 18977

salt-ssh

saltssh基于ssh,解决了salt依赖root用户执行命令,而saltssh则可以通过sudo进行执行命令,另外salt只支持glob和正则匹配target

安装服务

[root@salt01 ~]# yum install -y salt-ssh

配置salt-ssh

[root@salt01 ~]# vi /etc/salt/roster
[root@salt01 ~]# cat /etc/salt/roster 
salt02:
  host: 192.168.0.192
  user: root
  passwd: 123456

检验

注意要先同意连接。

[root@salt01 ~]# salt-ssh '*' test.ping
salt02:
    ----------
    retcode:
        254
    stderr:
    stdout:
        The host key needs to be accepted, to auto accept run salt-ssh with the -i flag:
        The authenticity of host '192.168.0.192 (192.168.0.192)' can't be established.
        RSA key fingerprint is ad:99:6b:06:34:4a:20:1b:83:d6:41:5d:6d:de:3e:8c.
        Are you sure you want to continue connecting (yes/no)? 
[root@salt01 ~]# ssh 192.168.0.192
The authenticity of host '192.168.0.192 (192.168.0.192)' can't be established.
RSA key fingerprint is ad:99:6b:06:34:4a:20:1b:83:d6:41:5d:6d:de:3e:8c.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.0.192' (RSA) to the list of known hosts.
root@192.168.0.192's password: 
Last login: Tue May  2 20:31:09 2017 from 192.168.0.101
[root@salt02 ~]# exit
logout
Connection to 192.168.0.192 closed.
[root@salt01 ~]# salt-ssh '*' test.ping
salt02:
    True
[root@salt01 ~]# salt-ssh '*' -r 'hostname'
salt02:
    ----------
    retcode:
        0
    stderr:
    stdout:
        root@192.168.0.192's password: 
        salt02