SaltStack 使用总结

SaltStack是一个开源的、新的基础平台管理工具,使用Python语言开发,同时提供Rest API方便二次开发以及和其他运维管理系统进行集成。相对于出道比较早的Puppet,SaltStack先天的优势就是简单、易用,可以非常快速的在团队中推广和使用,而且运行多平台。

SaltStack目前拥有四大主要功能

  • 远程执行:就是在管理节点上实现在上百台、上千台机器上同时执行一个命令。
  • 配置管理:也可以称之为状态管理,你可以描述一个状态。例如,某台机器要安装Nginx软件包、Nginx必须是启动的状态、Nginx有一个配置文件(内容和某个地方的一样)。你用一种描述语法描述出来后交给SaltStack,SaltStack就可以帮你实现,而不用你手动进行Nginx软件包的安装、配置文件的修改、启动等。
  • 云管理:SaltStack有一个组件叫作salt-cloud,它可以帮你自动化的进行云主机的创建和管理,支持很多公有云或私有云,例如AWS、阿里云、HP云、OpenStack、CloudStack等。
  • 事件驱动:事件驱动基础设施是SaltStack最强大也是最神秘的功能,前面的远程执行和配置管理是最基础的的,事件驱动是指SaltStack在日常运行中可以产生和捕捉事件,并根据捕捉到的事件触发对应的操作。

SaltStack的四种运行方式

  • Local:在本地运行或者说单台使用SaltStack
  • Minion/Master: 传统的客户端/服务器端(C/S)架构
  • Syndic: 使用代理实现架构扩展,用于管理更多的节点
  • Salt SSH: 无须安装客户端,直接通过SSH通信

目标机器匹配方法

SaltStack有以下几种方式来选择目标机器,灵活而又强大。大的来讲分为两大类:

1. 基于Minion ID

Minion ID是客户端(minion)的唯一标识符。可以在minion配置文件里面使用ID选项进行配置,如果不指定,其默认是主机的FQDN名。Minion ID是不能变动的,因为在进行key认证的时候,生成的文件名是以Minion ID命名的。如果Minion发生变动,就需要使用salt-key -d删除老的Minion ID,然后重新加入新的Minion ID。

  • Globbing(通配符)
  • regex(正则表达式)
  • list(列表)

2. 不基于Minion ID

  • 子网/IP地址
  • Grains
  • Grains PCRE
  • Pillar
  • Compound matchers(复合匹配)
  • Node groups(节点组)
  • Batching execution(批处理执行)

演示

指定Minion ID是最直接的选择目标的方法

查看所有minion节点

[root@salt ~]# salt-key  -L
Accepted Keys:
Denied Keys:
Unaccepted Keys:
128.docker.itnotebooks.com
Rejected Keys:

授权minion节点

可以修改/etc/salt/master的配置auto_accept:True自动认证

[root@salt ~]# salt-key -a 128.docker.itnotebooks.com
The following keys are going to be accepted:
Unaccepted Keys:
128.docker.itnotebooks.com
Proceed? [n/Y] y
Key for minion 128.docker.itnotebooks.com accepted.
  • salt-key -L: 显示已经或未认证的minion节点,Accepted Keys为已认证清单
  • salt-key -D: 删除所有Minion节点的证书
  • salt-key -d id: 删除单个minion节点的证书
  • salt-key -A: 接受所有minion节点的请求
  • salt-key -a id: 接受单个minion节点的请求

Globbing是指在Minion ID的基础上,通过通配符来定位Minion。SaltStack默认使用Shell风格通配符(如“” “?” “[]”)来匹配Minion ID。不过需要注意的是,使用salt命令时必须将’’放在单引号中,或是用’’转义,用来避免shell解析。

匹配所有itnotebooks.com域的所有minion

[root@salt ~]# salt '*.itnotebooks.com' test.ping

匹配docker后面单个任意字符的Minion

[root@salt ~]# salt 'docker?.itnotebooks.com' test.ping

匹配docker节点1到节点3的Minion

[root@salt ~]# salt 'docker[1-3].itnotebooks.com' test.ping

匹配docker不是节点1和节点3的Minion

[root@salt ~]# salt 'docker[!13].itnotebooks.com' test.ping

list和直接Minion ID都是最基本的模式,可以列出每一个Minion ID来指定多个目标机器,使用选项’-L’

[root@salt ~]# salt -L 'docker1.itnotebooks.com,docker2.itnotebooks.com' test.ping

Salt可以使用Perl风格的正则表达式来匹配Minion ID,使用选项-E

[root@salt ~]# salt -E 'linux-(node1|node2)*' test.ping

规范的Minion ID可以很好的反映出该服务器运行的相关服务及所在位置

redis-node1-redis03-idc04-soa.itnotebooks.com
  • redis-node1:运行的服务是Redis,这是第一个节点
  • redis03:说明这个redis是Redis集群编号03里面的节点
  • idc04:这台服务器运行在编号04的IDC机房中
  • soa:这台服务器是给SOA服务使用的
  • itnotebooks.com:运行的服务器是itnotebooks.com业务

也可以使用IP地址或CIDR子网来指定目标,目前仅支持IPv4的地址

[root@salt ~]# salt -S '192.168.18.33' test.ping
[root@salt ~]# salt -S '192.168.18.0/24' test.ping

也可以使用Grains对Trageting进行匹配,使用选项’-G’

匹配所有CentOS系统的Minion

[root@salt ~]# salt -G 'os:CentOS' test.ping

通过Grain匹配非常灵活,如果你想进行更复杂的基于Grains的匹配,SaltStack提供了Grain PCRE,可以在Grains的基础上使用正则表达式

[root@salt ~]# salt --grain-pcre 'os_family:Red(Hat|Flag)' test.ping

Pillar的数据可以用来定位Minion,为定位Minions提供了灵活性和终极控制

[root@salt ~]# salt -I 'apache:httpd' test.ping

Compound matchers(混合匹配)可以使用布尔操作符连接多个目标条件。混合匹配可以用前面讨论的多种方式实现精确的匹配。混合配匹配默认使用Globbing,如果要使用其它匹配方式,需要加上类型前缀字母如下表所示。

未分类

复合匹配中也可以使用and、or、not操作符,例如要匹配主机名以及webserv开始且运行Debain系统的Minion,还能匹配主机名满足正则表达式web-dc1-srv.*的Minion

[root@salt ~]# salt -C 'webserv* and G@os:Debian or E@web-dc1-srv.*' test.ping
  • G表示用shell通配符匹配Grains
  • E表示用正则表达式匹配Minion ID

需要注意的是not不能用于第一个条件,需要用时可以像下面这样写:

[root@salt ~]# salt -C '* and not G@kernel:Darwin' test.ping

Node group是在Master中nodegroup用复合条件字义的一组Minion

[root@salt ~]# vim /etc/salt/master
nodegroups:
  group1: '[email protected],linux-node2.itnotebooks.com'
[root@salt ~]# systemctl restart salt-master
[root@salt ~]# salt -N group1 test.ping
linux-node2.itnotebooks.com
    True
linux-node1.itnotebooks.com
    True

远程执行命令-查看内存使用

[root@salt ~]# salt 'linux-node1.itnotebooks.com' cmd.run 'free -m'
              total        used        free      shared  buff/cache   available
Mem:           1838         749         119           0         969         887
Swap:             0           0           0

远程执行命令-查看操作系统类型

[root@salt ~]# salt 'linux-node1.itnotebooks.com' grains.item osfullname
linux-node1.itnotebooks.com:
  osfullname: CentOS

远程执行命令-查看指定发行版本号为6.4的主机的python版本

[root@salt ~]# salt -G 'osrelease:6.4' cmd.run 'python -V'
linux-node1.itnotebooks.com:
  Python 2.6.6

远程执行命令-解压文件

[root@salt ~]# salt 'linux-node1.itnotebooks.com' archive.gunzip /tmp/jdk-8.1.0.gz

远程执行命令-压缩文件

[root@salt ~]# salt 'linux-node1.itnotebooks.com' archive.gzip /tmp/test.txt

除了上面所提到的外,其它类似的模块还有很多很多
如cp、cron、file、iptables、network、dnsuti、service、pkg等等,详细的自己用过就知道了,当然你也可以在cmd.run里面去完成这一切

saltstack的jinja模

[root@master ~]# cd /srv/salt/base/
[root@master base]# ll
总用量 12
-rw-r--r-- 1 root root 172 11月 14 21:26 apache.sls
-rw-r--r-- 1 root root 128 11月 16 00:12 dns.sls
drwxr-xr-x 2 root root  25 11月 16 00:23 files
-rw-r--r-- 1 root root  28 11月 16 00:22 top.sls
[root@master base]# vim dns.sls 
[root@master base]# cat dns.sls 
/etc/resolv.conf:
  file.managed:
     - source: salt://files/resolv.conf
     - user: root
     - group: root
     - mode: 777
     - template: jinja
     - defaults:    #定义变量
       DNS_SERVER: 192.168.43.118

[root@master base]# vim files/resolv.conf 
[root@master base]# cat files/resolv.conf
#jjjjjjjjjjjjjjjjjj
nameserver {{ DNS_SERVER }}   #2个大括号表示变量

[root@master base]# salt '*'  state.highstate
192.168.43.118:
----------
          ID: /etc/resolv.conf
    Function: file.managed
      Result: True
     Comment: File /etc/resolv.conf updated
     Started: 00:43:27.222821
    Duration: 21.809 ms
     Changes:   
              ----------
              diff:
                  --- 
                  +++ 
                  @@ -1,2 +1,3 @@
                   #jjjjjjjjjjjjjjjjjj
                  -nameserver 192.168.43.1
                  +nameserver 192.168.43.118
                  +

Summary
------------
Succeeded: 1 (changed=1)
Failed:    0

[root@master base]# vim files/resolv.conf 
[root@master base]# cat files/resolv.conf
#jjjjjjjjjjjjjjjjjj
#  {{ grains['fqdn_ip4'] }}
nameserver {{ DNS_SERVER }}
[root@master base]# salt '*'  state.highstate
192.168.43.118:
----------
          ID: /etc/resolv.conf
    Function: file.managed
      Result: True
     Comment: File /etc/resolv.conf updated
     Started: 00:47:03.799795
    Duration: 32.514 ms
     Changes:   
              ----------
              diff:
                  --- 
                  +++ 
                  @@ -1,3 +1,3 @@
                   #jjjjjjjjjjjjjjjjjj
                  +#  ['192.168.43.118']
                   nameserver 192.168.43.118
                  -

Summary
------------
Succeeded: 1 (changed=1)
Failed:    0

系统初始化配置:

[root@master base]# pwd
/srv/salt/base
[root@master base]# mkdir init
[root@master base]# mv apache.sls dns.sls files/ /tmp/
[root@master base]# tree
.
├── init
└── top.sls

1 directory, 1 file

[root@master base]# cp /tmp/dns.sls init/
[root@master base]# ll
总用量 4
drwxr-xr-x 2 root root 21 11月 16 00:55 init
-rw-r--r-- 1 root root 28 11月 16 00:22 top.sls
[root@master base]# cd init/
[root@master init]# ls
dns.sls

[root@master init]# vim dns.sls 
[root@master init]# cat dns.sls
/etc/resolv.conf:
  file.managed:
     - source: salt://init/files/resolv.conf
     - user: root
     - group: root
     - mode: 777

[root@master init]# mkdir files
[root@master init]# cp /etc/resolv.conf files/
[root@master init]# cp /etc/resolv.conf files/
[root@master init]# vim history.sls
[root@master init]# cat history.sls
/etc/profile:
  file.append:  #file模块的追加方法
    - text:
      - export HISTTIMEFORMAT="%F %T 'whoami'"

[root@master init]# export  PROMPT_COMMAND=' { msg=$(history 1 | { read x y; echo $y; });logger "[euid=$(whoami)]":$(who am i):['pwd']"$msg";} '
[root@master init]# who
root     pts/0        2018-11-16 00:29 (desktop-4a0ohej)
root     pts/1        2018-11-16 00:38 (desktop-4a0ohej)
[root@master init]# tail -f /var/log/messages
Nov 16 01:01:01 master systemd: Starting Session 34 of user root.
Nov 16 01:10:01 master systemd: Started Session 35 of user root.
Nov 16 01:10:01 master systemd: Starting Session 35 of user root.
Nov 16 01:14:00 master root: [euid=root]:root pts/0 2018-11-16 00:29 (desktop-4a0ohej):[pwd]export PROMPT_COMMAND=' { msg=$(history 1 | { read x y; echo $y; });logger "[euid=$(whoami)]":$(who am i):['pwd']"$msg";} '
Nov 16 01:14:04 master root: [euid=root]:root pts/0 2018-11-16 00:29 (desktop-4a0ohej):[pwd]uptime
Nov 16 01:14:39 master root: [euid=root]:root pts/0 2018-11-16 00:29 (desktop-4a0ohej):[pwd]tail -f /var/log/messages
Nov 16 01:14:42 master root: [euid=root]:root pts/0 2018-11-16 00:29 (desktop-4a0ohej):[pwd]uptime
Nov 16 01:15:06 master root: [euid=root]:root pts/0 2018-11-16 00:29 (desktop-4a0ohej):[pwd]tail -f /var/log/messages
Nov 16 01:15:12 master root: [euid=root]:root pts/0 2018-11-16 00:29 (desktop-4a0ohej):[pwd]tail -f /var/log/messages
Nov 16 01:15:17 master root: [euid=root]:root pts/0 2018-11-16 00:29 (desktop-4a0ohej):[pwd]who

[root@master init]# ll
总用量 12
-rw-r--r-- 1 root root 172 11月 16 01:17 audit.sls
-rw-r--r-- 1 root root 137 11月 16 00:57 dns.sls
drwxr-xr-x 2 root root  25 11月 16 00:58 files
-rw-r--r-- 1 root root  88 11月 16 01:03 history.sls
[root@master init]# cat audit.sls 
/etc/bashrc:
  file.append:
    - text:
      - export  PROMPT_COMMAND=' { msg=$(history 1 | { read x y; echo $y; });logger "[euid=$(whoami)]":$(who am i):['pwd']"$msg";} '

[root@master init]# cat sysctl.sls
vm.swappinese:   #尽量不适用swap分区
  sysctl.present:
    - value: 0

net.ipv4.ip_local_port_range:
  sysctl.present:
    - value: 10000 65000

fs.file-max:    #最大打开文件数
  sysctl.present:
    - value: 100000

[root@master init]# vim env_init.sls
[root@master init]# cat env_init.sls
include:
  - init.dns
  - init.history
  - init.audit
  - init.sysctl

[root@master init]# cd ..
[root@master base]# ls
init  top.sls
[root@master base]# vim top.sls 
[root@master base]# cat top.sls
base:
  '*':
    - init.env_init  #在base路径下去init目录找env_init文件并执行

[root@master base]# salt '*' state.highstate test=True  测试不执行
[root@master base]# salt '*' state.highstate

saltstack配置管理

状态模块:

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

[root@master ~]# grep -v '^$' /etc/salt/master |grep -v '#'
client_acl:
  luo:
    - test.ping
    - network.*
file_roots:     #top.sls路径,可以写多个
  base:         #但是只读取base路径
    - /srv/salt/base
  test:
    - /srv/salt/test
  prod:
    - /srv/salt/prod
pillar_roots:
  base:
    - /srv/pillar
mysql.host: '192.168.43.118'
mysql.user: 'salt'
mysql.pass: 'Myq1231!'
mysql.db: 'salt'
mysql.port: 3306

[root@master ~]# systemctl restart salt-master

[root@master ~]# mkdir /srv/salt/{base,test,prod}

[root@master salt]# ls
apache.sls  base  prod  test  top.sls
[root@master salt]# mv apache.sls top.sls base/

编写基本文件管理:

[root@master ~]# mkdir /srv/salt/base/files
[root@master base]# pwd
/srv/salt/base
[root@master base]# vim dns.sls
[root@master base]# cat dns.sls
/etc/resolv.conf:
  file.managed:     #file模块的managed方法
     - source: salt://files/resolv.conf  #路径,salt://=/srv/salt/base ,files新建的文件。
     - user: root   #用户
     - group: root  #用户组
     - mode: 777    #权限

[root@master base]# cp /etc/resolv.conf ./files/
[root@master base]# vim files/resolv.conf 
[root@master base]# cat files/resolv.conf
nameserver 192.168.43.1

[root@master base]# salt '*' state.sls dns   state状态模块的sls方法,dns为状态名,可以任意写。
192.168.43.118:
----------
          ID: /etc/resolv.conf
    Function: file.managed
      Result: True
     Comment: File /etc/resolv.conf updated
     Started: 00:18:45.928344
    Duration: 18.082 ms
     Changes:   
              ----------
              diff:
                  --- 
                  +++ 
                  @@ -1,3 +1 @@
                  -# Generated by NetworkManager
                  -search com
                   nameserver 192.168.43.1
              mode:
                  0777

Summary
------------
Succeeded: 1 (changed=1)

[root@master base]# pwd
/srv/salt/base
[root@master base]# vim top.sls 
[root@master base]# cat top.sls
base:
  '*':
    - dns

[root@master files]# pwd
/srv/salt/base/files
[root@master files]# vim resolv.conf 
[root@master files]# cat resolv.conf 
#jjjjjjjjjjjjjjjjjj
nameserver 192.168.43.1

[root@master base]# salt '*' state.sls dns  state状态模块的sls方法,dns为状态名,可以任意写。
192.168.43.118:
----------
          ID: /etc/resolv.conf
    Function: file.managed
      Result: True                
     Comment: File /etc/resolv.conf updated
     Started: 00:26:07.017848
    Duration: 24.144 ms
     Changes:   
              ----------
              diff:      
                  --- 
                  +++ 
                  @@ -1 +1,2 @@
                  +#jjjjjjjjjjjjjjjjjj
                   nameserver 192.168.43.1

Summary
------------
Succeeded: 1 (changed=1)
Failed:    0

[root@master base]# cat /etc/resolv.conf 
#jjjjjjjjjjjjjjjjjj
nameserver 192.168.43.1

自动化运维工具—SaltStack安装部署及简单案例

SaltStack原理

  • SaltStack由Master(服务端)和Minion(客户端)组成,Master和Minion之间通过ZeroMQ(消息队列)进行通讯,Master和Minion分别监听4505与4506端口,4505为master与minion认证通信端口,4506为master用来发送或者接受minion的命令执行返回信息。
  • 当客户端启动后,会主动链接master端注册,然后一直保持该TCP连接,而master通过这条TCP连接对客户端进行控制,如果连接断开,master将对客户端不能进行控制,但是,当客户端断开连接后,会定期向master端请求注册。

SaltStack常用模块

  • 与ansible类似,SaltStack提供了很多功能模块,便于对操作系统的基础功能和常用工具操作。
1:pkg模块 :是包管理,包括增删更新。
2:file模块 :管理文件操作,包括同步文件、设置文件权限和所属用户组、删除文件等操作。
3:cmd模块 :是在Minion上执行命令或者脚本。
4:user模块 :管理系统账户操作。
5:service模块 :管理系统服务操作
6:cron模块 :管理cron服务操作

实验环境

未分类

SaltStack安装

1、更改主机名关闭防火墙

  • 需要注意的是master与minion端都需关闭SELinux和防火墙,且一定要设置完整的FQDN,域名的形式也要一样,不然在主控端执行远程执行命令或者配置的时候,等待的时间会非常长,甚至还会出现其他不可控的情况。
systemctl stop firewalld.service
setenforce 0

vim /etc/hostname

master.saltstack.com    //(管理)
web01.saltstack.com     //(被管理)
  • 修改每台hosts文件

vim /etc/hosts

192.168.144.112 master.saltstack.com     //所有机器保持一致,保证解析得到的域名可控
192.168.144.111 web01.saltstack.com
  • 重启服务器

2、安装master与minion端

  • 安装master端
yum install epel-release -y 
yum install salt-master
  • 安装被控制端
yum install epel-release -y
yum install -y salt-minion

3、master端配置

vim /etc/salt/master

interface: 192.168.144.111    //15行 监听地址改为本地IP
............
auto_accept: True                //215  证书认证
............
file_roots:                          //416行 站点目录开启,注意打开后目录文件是否存在,若不存在需要手动创建
    base:
         - /srv/salt
............
nodegroups:                          //710行 组分类
    group1: 'web01.saltstack.com' 
    group2: 'web02.saltstack.com'
............
pillar_opts: True              //552行  pillar开启

pillar_roots:                     //529行
     base:
        - /srv/pillar
  • 过滤空行与#开头行,查看所配置项。
cat /etc/salt/master | grep -v ^$ | grep -v ^#
  • 创建master的站点目录/srv/salt与pillar目录/srv/pillar
mkdir /srv/salt
mkdir /srv/pillar
systemctl start salt-master.service //开启服务
netstat -natp | egrep '4505|4506' //查看4505、4506端口

4、被管理端minnion配置

vim /etc/salt/minion

master: 192.168.144.111     //16行 指定管理端IP
id: web01.saltstack.com      //78行 指定被控的主机名
  • 启动minion
systemctl start salt-minion.service

5、C/S构建完成,验证简单操作

salt '' test.ping //查看通信状态
salt '' cmd.run 'df -h' //查看所有被管理端的挂载情况
salt 'web01.saltstack.com' cmd.run 'df -h' //查看指定主机的挂载情况
salt '' grains.items //查看grains值
salt '' pillar.items //查看pillar(动态信息)
salt-key //查看已经被接受过的客户端

Saltstack批量部署安装Apache

1、创建sls入口文件表示执行Apache模块

vim /srv/salt/top.sls

base:
 '*':        //*号表示对所有主机
     - apache   //对应下面执行文件的名称

2、创建模块执行文件

vim /srv/salt/apache.sls

apache-service:
    pkg.installed:
        - names:
            - httpd
            - httpd-devel
    service.running:
        - name: httpd
        - enable: True
systemctl restart salt-master
  • 在主控端执行刷新state配置命令,让被控制端执行安装apache
salt '*' state.highstate
  • 也可去minion被控制端检测是否安装成功。

利用saltstack的event实现自己的功能

saltstack的master上minion连接较多,下面这个程序可以分析哪些minion任务执行成功,哪些执行失败以及哪些没有返回。

脚本说明:

一、最先打印出本次任务的job id、command name以及其它相关信息,然后是本次任务的执行流程和结果,这和我们单独执行这个命令是一致的。最后程序会打印出所有未成功的任务和未返回的任务,并且重新执行一遍。 这里要说明的是,因为没有查看对应的情景,对于失败任务的排判断做的不好,另外minion未连接我也归为任务未返回,并且会再执行一遍,实际上如果是minion未连接,则不应该执行。

二、 程序我们先派生子进程去执行salt命令,再salt命令执行完毕后,我们的程序会对其中失败的和未返回的minion任务二次执行

三、编写脚本

import salt.utils.event
import re
import signal, time
import sys
import os
def single_handler(target):
    os.execl('/usr/bin/salt', 'salt', target, 'state.sls', 'os')

def handler(num1, num2):
    #signal.signal(signal.SIGCLD,signal.SIG_IGN)
    print 'We are in signal handler'
    print 'Job Not Ret: '+str(record[jid])
    print ' Job Failed: '+str(failedrecord[jid])
    print 'all done...'
    for item in failedrecord[jid]:
        #print item
        try:
           pid  = os.fork()
           if pid == 0:
              single_handler(item)
        except OSError:
           print 'we exec. '+ item +' error!'
    for item in record[jid]:
        #print item
        try:
           print 'fork ok ' + item
           pid = os.fork()
           if pid == 0 :
              single_handler(item)
        except OSError:
           print 'we exec. '+item + ' error!' 
    sys.stdout.flush()
    os._exit(0)



fd = open('/tmp/record', 'w+')
#sys.stdout = fd
#sys.stderr = fd

signal.signal(signal.SIGCLD, handler)

#fd = open('/var/log/record', 'w+')
os.dup2(fd.fileno(), sys.stdout.fileno())
os.dup2(fd.fileno(), sys.stderr.fileno())

#sys.stdout = fd
#sys.stderr = fd


try:
   pid = os.fork()
   if pid == 0:
      time.sleep(2)
      try:
         os.execl('/usr/bin/salt', 'salt', '*', 'state.sls', 'os')
      except OSError:
         print 'exec error!'
         os._exit(1)
except OSError:
   print 'first fork error!'
   os._exit(1)
event = salt.utils.event.MasterEvent('/var/run/salt/master')
flag=False
reg=re.compile('salt/job/([0-9]+)/new')
reg1=reg
#a process to exec. command, but will sleep some time
#another process listen the event
#if we use this method, we can filter the event through func. name
record={}
failedrecord={}
jid = 0


#try:
for eachevent in event.iter_events(tag='salt/job',full=True):
    eachevent=dict(eachevent)
    result = reg.findall(eachevent['tag'])
    if not flag and result:
       flag = True
       jid = result[0]
       print "   job_id: " + jid
       print "  Command: " + dict(eachevent['data'])['fun'] + ' ' + str(dict(eachevent['data'])['arg'])
       print "    RunAs: " + dict(eachevent['data'])['user'] 
       print "exec_time: " + dict(eachevent['data'])['_stamp'] 
       print "host_list: " + str(dict(eachevent['data'])['minions'])
       sys.stdout.flush()
       record[jid]=eachevent['data']['minions']
       failedrecord[jid]=[]
       reg1 = re.compile('salt/job/'+jid+'/ret/([0-9.]+)')
    else:
       result = reg1.findall(eachevent['tag'])
       if result:
          record[jid].remove(result[0])
          if not dict(eachevent['data'])['success']:
             failedrecord[jid].append(result[0])
#except:
#   print 'we in except'
"""
   print 'Job Not Ret: '+str(record[jid])
   print ' Job Failed: '+str(failedrecord[jid])
   for item in failedrecord[jid]:
       os.system('salt '+ str(item) + ' state.sls os')
   for item in record[jid]:
       os.system('salt '+ str(item) + ' state.sls os')
   os._exit(0)
"""

执行结果:

   job_id: 20151208025319005896
  Command: state.sls ['os']
    RunAs: root
exec_time: 2015-12-08T02:53:19.006284
host_list: ['172.18.1.212', '172.18.1.214', '172.18.1.213', '172.18.1.211']
172.18.1.213:
----------
          ID: configfilecopy
    Function: file.managed
        Name: /root/node3
      Result: True
     Comment: File /root/node3 is in the correct state
     Started: 02:53:19.314015
    Duration: 13.033 ms
     Changes:   
----------
          ID: commonfile
    Function: file.managed
        Name: /root/commonfile
      Result: True
     Comment: File /root/commonfile is in the correct state
     Started: 02:53:19.327173
    Duration: 1.993 ms
     Changes:   

Summary
------------
Succeeded: 2
Failed:    0
------------
Total states run:     2
172.18.1.212:
----------
          ID: configfilecopy
    Function: file.managed
        Name: /root/node2
      Result: True
     Comment: File /root/node2 is in the correct state
     Started: 02:53:19.337325
    Duration: 8.327 ms
     Changes:   
----------
          ID: commonfile
    Function: file.managed
        Name: /root/commonfile
      Result: True
     Comment: File /root/commonfile is in the correct state
     Started: 02:53:19.345787
    Duration: 1.996 ms
     Changes:   

Summary
------------
Succeeded: 2
Failed:    0
------------
Total states run:     2
172.18.1.211:
----------
          ID: configfilecopy
    Function: file.managed
        Name: /root/node1
      Result: True
     Comment: File /root/node1 is in the correct state
     Started: 02:53:19.345017
    Duration: 12.741 ms
     Changes:   
----------
          ID: commonfile
    Function: file.managed
        Name: /root/commonfile
      Result: True
     Comment: File /root/commonfile is in the correct state
     Started: 02:53:19.357873
    Duration: 1.948 ms
     Changes:   

Summary
------------
Succeeded: 2
Failed:    0
------------
Total states run:     2
172.18.1.214:
    Minion did not return. [Not connected]
We are in signal handler
Job Not Ret: ['172.18.1.214']
 Job Failed: []
all done...
fork ok 172.18.1.214
172.18.1.214:
    Minion did not return. [Not connected]

saltstack之salt event事件用法

event是一个本地的ZeroMQ PUB Interface,event是一个开放的系统,用于发送信息通知salt或其他的操作系统。每个event都有一个标签。事件标签允许快速制定过滤事件。除了标签之外,每个事件都有一个数据结构。这个数据结构是一个dict类型,其中包含关于事件的信息。

作用:用于监控salt-master执行结果。

一、监听salt event事件脚本

1.环境准备

节点 IP

  • salt-master 192.168.56.41
  • salt-minion 192.168.56.42

2.配置好salt-key连接,再在salt-master节点上面开两个窗口测试。

#salt-master窗口01

[root@salt ~]# salt '*' test.ping
salt-minion:
    True
[root@salt ~]# salt '*' test.ping
salt-minion:
    True

#salt-master窗口02

#编写脚本
[root@salt-minion ~]# cat salt_monitor_event.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import salt.utils.event
event = salt.utils.event.MasterEvent('/var/run/salt/master')
for eachevent in event.iter_events(full=True):
    print eachevent
    print "---------"

#授权
[root@salt-minion ~]# chmod +x salt_monitor_event.py

#执行结果
[root@salt ~]# python salt_monitor_event.py
{u'tag': '20180624070339744384', u'data': {u'_stamp': u'2018-06-23T23:03:39.745401', u'minions': [u'salt-minion']}}
---------
{u'tag': 'salt/job/20180624070339744384/new', u'data': {u'tgt_type': u'glob', u'jid': u'20180624070339744384', u'tgt': u'*', u'missing': [], u'_stamp': u'2018-06-23T23:03:39.745714', u'user': u'root', u'arg': [], u'fun': u'test.ping', u'minions': [u'salt-minion']}}
---------
{u'tag': 'salt/job/20180624070339744384/ret/salt-minion', u'data': {u'fun_args': [], u'jid': u'20180624070339744384', u'return': True, u'retcode': 0, u'success': True, u'cmd': u'_return', u'_stamp': u'2018-06-23T23:03:39.783037', u'fun': u'test.ping', u'id': u'salt-minion'}}
---------
{u'tag': '20180624070341195901', u'data': {u'_stamp': u'2018-06-23T23:03:41.196632', u'minions': [u'salt-minion']}}
---------
{u'tag': 'salt/job/20180624070341195901/new', u'data': {u'tgt_type': u'glob', u'jid': u'20180624070341195901', u'tgt': u'*', u'missing': [], u'_stamp': u'2018-06-23T23:03:41.196926', u'user': u'root', u'arg': [], u'fun': u'test.ping', u'minions': [u'salt-minion']}}
---------
{u'tag': 'salt/job/20180624070341195901/ret/salt-minion', u'data': {u'fun_args': [], u'jid': u'20180624070341195901', u'return': True, u'retcode': 0, u'success': True, u'cmd': u'_return', u'_stamp': u'2018-06-23T23:03:41.234596', u'fun': u'test.ping', u'id': u'salt-minion'}}
---------
{u'tag': '20180624070347154023', u'data': {u'_stamp': u'2018-06-23T23:03:47.154591', u'minions': [u'salt-minion']}}
---------
{u'tag': 'salt/job/20180624070347154023/new', u'data': {u'tgt_type': u'glob', u'jid': u'20180624070347154023', u'tgt': u'*', u'missing': [], u'_stamp': u'2018-06-23T23:03:47.154990', u'user': u'root', u'arg': [], u'fun': u'test.ping', u'minions': [u'salt-minion']}}
---------
{u'tag': 'salt/job/20180624070347154023/ret/salt-minion', u'data': {u'fun_args': [], u'jid': u'20180624070347154023', u'return': True, u'retcode': 0, u'success': True, u'cmd': u'_return', u'_stamp': u'2018-06-23T23:03:47.191617', u'fun': u'test.ping', u'id': u'salt-minion'}} 

二、saltStack的event接口通过mysql数据库接收SaltStack批量管理日志

作用:在master上直接将返回结果写入mysql

1.编写自定义return脚本

vim salt_event_to_mysql.py


#!/bin/env python
#coding=utf8
# Import python libs
import json
# Import salt modules
import salt.config
import salt.utils.event
# Import third part libs
import MySQLdb
__opts__ = salt.config.client_config('/etc/salt/master')
#create MySQL connect
#conn = MySQLdb.connect(host=__opts__['mysql.host'],user=__opts__['mysql.user'],passwd=__opts__['mysql.pass'],db=__opts__['mysql.db'],port=__opts__['mysql.port'])


conn = MySQLdb.connect(host='192.168.3.87',user='salt',passwd='salt',db='salt',port=3306)
cursor = conn.cursor()
# Listen Salt Master Event System
event = salt.utils.event.MasterEvent(__opts__['sock_dir'])
for eachevent in event.iter_events(full=True):
    ret = eachevent['data']
    if "salt/job/" in eachevent['tag']:
        #Return Event
        if ret.has_key('id') and ret.has_key('return'):
            #Ignore saltutil.find_job event
            if ret['fun'] == "saltutil.find_job":
                continue
            sql = '''INSERT INTO `salt_returns`
                (`fun`,`jid`,`return`,`id`,`success`,`full_ret` )
                VALUES (%s,%s,%s,%s,%s,%s)'''
            cursor.execute(sql,(ret['fun'],ret['jid'],
                                json.dumps(ret['return']),ret['id'],
                                ret['success'],json.dumps(ret)))
            cursor.execute("COMMIT")
    # Other Event
    else:
        pass

保存退出

注意:

MySQLdb.connect(host=__opts__['mysql.host'],user=__opts__['mysql.user'],passwd=__opts__['mysql.pass'],db=__opts__['mysql.db'],port=__opts__['mysql.port'])

要换成自己的实际数据库地址、数据库用户、密码,如:

conn = MySQLdb.connect(host='192.168.3.87',user='salt',passwd='salt',db='salt',port=3306)

2.修改master的配置文件

vim /etc/salt/master


mysql.host: '192.168.3.87' # mysql服务器的IP地址
mysql.user: 'salt' # mysql数据库的用户名,需要跟后面授权的用户名一致
mysql.pass: 'salt' # mysql数据库的密码,需要跟后面授权的密码一致
mysql.db: 'salt' # mysql数据库的名称
mysql.port: 3306 # 使用端口为3306


mysql.host: '192.168.3.87' # mysql服务器的IP地址
mysql.user: 'salt' # mysql数据库的用户名,需要跟后面授权的用户名一致
mysql.pass: 'salt' # mysql数据库的密码,需要跟后面授权的密码一致
mysql.db: 'salt' # mysql数据库的名称
mysql.port: 3306 # 使用端口为3306

保存退出

3.在master上安装MySQL-python

yum -y install MySQL-python

创建数据库

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,KEY `id` (`id`),KEY `jid` (`jid`),KEY `fun` (`fun`) ) 
ENGINE=InnoDB DEFAULT CHARSET=utf8;

授权

GRANT ALL PRIVILEGES ON salt.* to 'salt'@'%' identified by 'salt';
flush privileges;

4.在master的后台执行自定义return脚本

python salt_event_to_mysql.py &

5.开一个新的master终端进行测试

salt '*' test.ping

6.在mysql上看是否已经将数据写入数据库

mysql -uroot -p

输入密码之后进入mysql数据库

use salt
show tables;
select * from salt_returns G
如果出现如下结果表示插入成功:
mysql> select * from salt_returns G
*************************** 1. row ***************************
     fun: test.ping
     jid: 20160807111832766142
  return: true
      id: 192.168.3.108
 success: 1
full_ret: {"fun_args": [], "jid": "20160807111832766142", "return": true, "retcode": 0, "success": true, "cmd": "_return", "_stamp": "2016-08-07T03:18:32.950841", "fun": "test.ping", "id": "192.168.3.108"}
*************************** 2. row ***************************
     fun: test.ping
     jid: 20160807111832766142
  return: true
      id: minion_client01.DHCP
 success: 1
full_ret: {"fun_args": [], "jid": "20160807111832766142", "return": true, "retcode": 0, "success": true, "cmd": "_return", "_stamp": "2016-08-07T03:18:32.953034", "fun": "test.ping", "id": "minion_client01.DHCP"}

Saltstack 安装配置详解

下面这篇文章主要介绍另外一个运维自动化工具 Saltstack 。

一、简介

Saltstack 比 Puppet 出来晚几年,是基于Python 开发的,也是基于 C/S 架构,服务端 master 和客户端 minions ;Saltstack 和 Puppet 很像,可以说 Saltstatck 整合了 Puppet 和 Chef 的功能,更加强大,更适合大规模批量管理服务器,并且它比 Puppet 更容易配置。
三大功能: 远程命令执行,配置管理(服务,文件,cron,用户,组),云管理。
支持系统:大多数都支持,windows 上不支持安装 master。

二、安装配置

1、准备工作

准备两台机器,这两台机器都关闭 selinux,清空 iptables 规则并保存。

master:192.168.0.109
slaver:192.168.0.110

2、编辑 hosts 文件

两台都设置,若机器太多,可以通过搭建 DNS,则不用在每台机器上设置这个

# vim /etc/hosts
192.168.0.109  master.test.com
192.168.0.110  slaver.test.com

3、设置 hostname

在 master 上

[iyunv@master ~]# vim /etc/sysconfig/network
HOSTNAME=master.test.com

在 slaver 上

[iyunv@slaver ~]# vim /etc/sysconfig/network
HOSTNAME=slaver.test.com

4、安装

1)服务端安装

[iyunv@master ~]# yum install -y epel-release
[iyunv@master ~]# yum install -y salt-master salt-minion

2)客户端安装

[iyunv@slaver ~]# yum install -y epel-release
[iyunv@slaver ~]# yum install -y salt-minion

5、配置

服务端和客户端都要配置 master

# vim /etc/salt/minion                   //在第16行添加,冒号后有一个空格
master: 192.168.0.109

6、启动服务

1)服务端

[iyunv@master ~]# /etc/init.d/salt-master start
Starting salt-master daemon:                               [确定]
[iyunv@master ~]# /etc/init.d/salt-minion start
Starting salt-minion daemon:                               [确定]

2)客户端

[iyunv@slaver ~]# /etc/init.d/salt-minion start
Starting salt-minion daemon:                               [确定]

三、配置认证

1)在服务端上操作

[iyunv@master ~]# salt-key -a  slaver.test.com
[iyunv@master ~]# salt-key -a  master.test.com
[iyunv@master ~]# salt-key

未分类

说明:-a :accept ,-A:accept-all,-d:delete,-D:delete-all。可以使用 salt-key 命令查看到已经签名的客户端。此时我们在客户端的 /etc/salt/pki/minion 目录下面会多出一个minion_master.pub 文件。

2)测试验证

示例1: salt ‘*’ test.ping //检测通讯是否正常,也可以指定其中一个 ‘slaver.test.com’

未分类

示例2: salt ‘*’ cmd.run ‘df -h’ //远程执行命令

未分类

说明: 这里的 * 必须是在 master 上已经被接受过的客户端,可以通过 salt-key 查到,通常是我们已经设定的 id 值。关于这部分内容,它支持通配、列表以及正则。 比如两台客户端 web10、web11, 那我们可以写成 salt ‘web*’ salt ‘web1[02]’ salt -L ‘web10,web11’ salt -E ‘web(10|11)’ 等形式,使用列表,即多个机器用逗号分隔,而且需要加-L,使用正则必须要带-E选项。 它还支持 grains 和 pillar,分别加 -G 和 -I 选项,下面会介绍到。

四、grains 和 pillar

下面来介绍 grains 和 pillar

1、grains

grains 是在 minion(客户端)启动时收集到的一些信息,比如操作系统类型、网卡ip等。
使用命令:

[iyunv@master ~]# salt 'slaver.test.com' grains.ls            //列出所有的 grains 项目名字
[iyunv@master ~]# salt 'slaver.test.com' grains.items      //列出所有的 grains 项目名以及值

grains的信息并不是动态的,并不会时时变更,它只是在 minion 启动时收集到的。grains 也可以做配置管理。
下面我们来自定义 grains

1)客户端上配置

[iyunv@slaver ~]# vim /etc/salt/grains               //添加如下,注意冒号后有空格
role: nginx
env: test
myname: tpp

或者

[iyunv@slaver ~]# vim /etc/salt/minion            //在最下面添加或更改
grains:
  role:
    - nginx
  env:
    - test
  myname:
    - tpp

重启minion服务

[iyunv@slaver ~]# /etc/init.d/salt-minion restart

2)服务端获取 grains

[iyunv@master ~]# salt 'slaver.test.com' grains.item role env myname       //列出多个

未分类

[iyunv@master ~]# salt 'slaver.test.com' grains.get myname                    //列出单个

未分类

注意:grains 在远程执行命令时很方便。我们可以按照 grains 的一些指标来操作。比如把所有的 web 服务器的 grains 的 role 设置为 nginx,那这样我们就可以批量对 nginx 的服务器进行操作了:

[iyunv@master ~]# salt -G role:nginx cmd.run 'hostname'
[iyunv@master ~]# salt -G os:CentOS cmd.run 'hostname'

2、pillar

pillar 和 grains 不一样,是在 master 上定义的,并且是针对 minion 定义的一些信息。像一些比较重要的数据(密码)可以存在 pillar 里,还可以定义变量等。
查看指定minion的 pillar 值:

[iyunv@master ~]# salt 'slaver.test.com' pillar.items

1)服务端自定义配置 pillar

[iyunv@master ~]# vim /etc/salt/master                    //找

到如下内容,去掉#号

pillar_roots:
  base:
    - /srv/pillar
[iyunv@master ~]# mkdir /srv/pillar
[iyunv@master ~]# vim /srv/pillar/test.sls                   //自定义配置文件,内容如下
conf: /etc/123.conf
myname: tpp
[iyunv@master ~]# vim /srv/pillar/top.sls                   //总入口文件,内容如下
base:
  'slaver.test.com':
    - test

重启master

[iyunv@master ~]# /etc/init.d/salt-master restart

注意:当更改完 pillar 配置文件后,我们可以通过刷新 pillar 配置来获取新的 pillar 状态:

[iyunv@master ~]# salt '*' saltutil.refresh_pillar

2)验证:

[iyunv@master ~]# salt 'slaver.test.com' pillar.items

未分类

[iyunv@master ~]# salt 'slaver.test.com' pillar.item conf
[iyunv@master ~]# salt 'slaver.test.com' pillar.item myname

未分类

pillar 同样可以用来作为 salt 的匹配对象。比如:

[iyunv@master ~]# salt -I 'conf:/etc/123.conf' test.ping
[iyunv@master ~]# salt -I 'conf:/etc/123.conf' cmd.run 'w'

未分类

五、配置管理安装Apache

下面进行的演示是远程通过 yum 方式安装 Apache。步骤如下:

1、配置

[iyunv@master ~]# vim /etc/salt/master        //打开如下内容的注释

file_roots:
  base:
    - /srv/salt

注意:环境: base、dev(开发环境)、test(测试环境)、prod(生产环境)。

[iyunv@master ~]# mkdir /srv/salt
[iyunv@master ~]# vim /srv/salt/top.sls
base:
  'slaver.test.com':
    - apache

注意:若换成 ‘*’,则表示在所有的客户端执行 apache 模块。

[iyunv@master ~]# vim /srv/salt/apache.sls
apache-service:
  pkg.installed:
    - names:                //如果只有一个服务,那么就可以写成 –name: httpd 不用再换一行
      - httpd
      - httpd-devel
  service.running:
    - name: httpd
    - enable: True

注意:apache-service 是自定义的 id 名。pkg.installed 为包安装函数,下面是要安装的包的名字。service.running 也是一个函数,来保证指定的服务启动,enable 表示开机启动。

2、重启服务

[iyunv@master ~]# /etc/init.d/salt-master restart

3、执行命令

[iyunv@master ~]# salt 'slaver.test.com' state.highstate         //执行时间比较长,因为要安装httpd

未分类

未分类

未分类

如上图所示,说明 Apache 远程安装已成功。

六、文件目录管理

1、文件管理

1)服务端配置

接着编辑之前的 top.sls 文件

[iyunv@master ~]# vim /srv/salt/top.sls              //修改为如下
base:
  'slaver.test.com':
    - filetest

新建 filetest.sls 文件

[iyunv@master ~]# vim /srv/salt/filetest.sls
file-test:
  file.managed:
    - name: /tmp/filetest.txt
    - source: salt://test/123/1.txt
    - user: root
    - group: root
    - mode: 644

注意:第一行的 file-test 为自定的名字,表示该配置段的名字,可以在别的配置段中引用它;source指定文件从哪里拷贝,这里的 test 目录相当于是 /srv/salt/test 目录;name指定远程客户端要生成的文件。

新建所要测试的源文件

[iyunv@master ~]# mkdir -p /srv/salt/test/123/
[iyunv@master ~]# vim /srv/salt/test/123/1.txt
msiyuetian.blog.iyunv.com

执行命令:

[iyunv@master ~]# salt 'slaver.test.com' state.highstate

未分类

2)客户端验证

未分类

2、目录管理

1)服务端配置

接着编辑之前的 top.sls 文件

[iyunv@master ~]# vim /srv/salt/top.sls              //修改为如下
base:
  'slaver.test.com':
    - filedir

新建 filedir.sls 文件

[iyunv@master ~]# vim /srv/salt/filedir.sls
file-dir:
  file.recurse:
    - name: /tmp/testdir
    - source: salt://test1/234
    - user: root
    - file_mode: 644
    - dir_mode: 755
    - mkdir: True
    - clean: True

注意:clean: True 源删除文件或目录,目标也会跟着删除,否则不会删除。可以默认设置为 False。

新建所要测试的源目录

[iyunv@master ~]# mkdir -p /srv/salt/test1/234
[iyunv@master ~]# vim /srv/salt/test1/234/2.txt
msiyuetian.blog.iyunv.com

执行命令:

[iyunv@master ~]# salt 'slaver.test.com' state.highstate

未分类

2)客户端验证

未分类

3)测试增删功能

在服务端新建 mydir 目录以及 testdir.add 文件,删除 2.txt 文件:

[iyunv@master ~]# mkdir /srv/salt/test1/234/mydir
[iyunv@master ~]# touch /srv/salt/test1/234/mydir/111.txt
[iyunv@master ~]# touch /srv/salt/test1/234/testdir.add
[iyunv@master ~]# rm -rf /srv/salt/test1/234/2.txt

执行命令:

[iyunv@master ~]# salt 'slaver.test.com' state.highstate

客户端验证

未分类

注意:由上图可知,成功在客户端 /tmp/testdir/ 目录下创建了 mydir 目录以及 testdir.add 文件,并删除 2.txt 文件。这里值得注意的是要成功创建 mydir 目录,前提是 mydir 目录下要有文件,如这里的111.txt 文件,如若没有,客户端是不会创建 mydir 目录的。

七、远程执行

前面提到远程执行命令 test.ping,cmd.run,点前面的是模块,点后面的是函数;这样总归是不太规范化,下面详细介绍怎么远程执行命令和脚本。

1、远程执行命令

1)服务端配置

接着编辑之前的 top.sls 文件

[iyunv@master ~]# vim /srv/salt/top.sls              //修改为如下
base:
  'slaver.test.com':
    - cmdtest

新建 cmdtest.sls 文件

[iyunv@master ~]# vim /srv/salt/cmdtest.sls
cmd-test:  
  cmd.run:
    - onlyif: test -f /tmp/123.txt
    - names:
      - touch /tmp/cmdtest.txt
      - mkdir /tmp/cmdtest
    - user: root

注意:条件 onlyif 表示若 /tmp/123.txt 文件存在,则执行后面的命令;可以使用 unless,两者正好相反。

执行命令:

[iyunv@master ~]# salt 'slaver.test.com' state.highstate

2)客户端验证

未分类

2、远程执行脚本

1)服务端配置

接着编辑之前的 top.sls 文件

[iyunv@master ~]# vim /srv/salt/top.sls              //修改为如下
base:
  'slaver.test.com':
    - shelltest

新建 shelltest.sls 文件

[iyunv@master ~]# vim /srv/salt/shelltest.sls
shell-test:
  cmd.script:
    - source: salt://test/1.sh
    - user: root

新建 1.sh 脚本文件

[iyunv@master ~]# vim /srv/salt/test/1.sh
#!/bin/bash
touch /tmp/shelltest.txt
if [ -d /tmp/shelltest ]
then
    rm -rf /tmp/shelltest
else
    mkdir /tmp/shelltest
fi

执行命令:

[iyunv@master ~]# salt 'slaver.test.com' state.highstate

2)客户端验证

未分类

注意:通过上面的例子,我们实现了远程执行脚本;如果我们想一键远程安装 LAMP 或者 LNMP,那么只需把本例中的 1.sh 脚本替换成 一键安装的脚本就行。

八、管理任务计划

1、建立 cron

1)服务端配置

编辑 top.sls 文件

[iyunv@master ~]# vim /srv/salt/top.sls              //修改为如下
base:
  'slaver.test.com':
    - crontest

编辑 crontest.sls 文件

[iyunv@master ~]# vim /srv/salt/crontest.sls
cron-test:
  cron.present:
    - name: /bin/touch /tmp/111.txt
    - user: root
    - minute: '*'
    - hour: 20
    - daymonth: 1-10
    - month: '3,5'
    - dayweek: '*'

注意,*需要用单引号引起来。当然我们还可以使用 file.managed 模块来管理 cron,因为系统的 cron都是以配置文件的形式存在的。

执行命令:

[iyunv@master ~]# salt 'slaver.test.com' state.highstate

2)客户端验证

未分类

2、删除 cron

1)服务端配置

我们只需修改 crontest.sls 文件

[iyunv@master ~]# vim /srv/salt/crontest.sls

把 cron.present: 改成 cron.absent:
注意:两者不能共存,要想删除一个 cron,那之前的 present 就得替换掉或者删除掉。
执行命令:

[iyunv@master ~]# salt 'slaver.test.com' state.highstate

未分类

2)客户端验证

[iyunv@slaver ~]# crontab -l           //可查看到该任务计划已删除

九、Saltstack 常用命令

1、拷贝文件到客户端

[iyunv@master ~]# salt 'slaver.test.com' cp.get_file salt://apache.sls /tmp/cp.txt
slaver.test.com:
    /tmp/cp.txt

2、拷贝目录到客户端

[iyunv@master ~]# salt 'slaver.test.com' cp.get_dir salt://test /tmp
slaver.test.com:
    - /tmp/test/1.sh
    - /tmp/test/123/1.txt

未分类

3、显示存活的客户端

[iyunv@master ~]# salt-run manage.up

未分类

4、命令下执行服务端的脚本

[iyunv@master ~]# vim /srv/salt/test/shell.sh

#! /bin/bash
echo "msiyuetian.blog.iyunv.com" > /tmp/shell.txt
[iyunv@master ~]# salt 'slaver.test.com' cmd.script salt://test/shell.sh

客户端查看

未分类

使用saltstack部署zabbix-agent

使用saltstack批量部署服务是工作中一种常见的内容,对于一个服务的部署可以分为:前期准备(系统资源的设定、参数调整、软件包的下载)、安装、配置、启动这几个步骤。
本文以zabbix客户端的设定为例,向大家展示如何用saltstack部署服务。

部署步骤

1.首先利用pillar定义配置文件中一些关键的数据,例如zabbix server的IP(如果有需求还可以设置开放端口、客户端名称)。在整个部署开始之前,我们需要了解pillar和file的相关目录信息,在master的配置文件中:

file_roots:
  base:
    - /srv/salt/prod
  dev:
    - /srv/salt/dev/services
    - /srv/salt/dev/states
  prod:
    - /srv/salt/prod/services
    - /srv/salt/prod/states
以及:
pillar_roots:
  base:
    - /srv/pillar

file_roots以及pillar_roots里的base、dev和prod会在后续的配置中经常使用,默认情况下我们在每个base地址下创建一个top.sls文件。

2.pillar文件的配置

pillar的根文件top.sls:

[root@server2 pillar]# cat top.sls 
base:
  '*':
    - zabbix

指定了任何minion都包含zabbix.sls的静态数据,在zabbix.sls文件中我们指定了zabbix_server的ip地址:

[root@server2 pillar]# cat zabbix.sls 
zabbix-agent:
  Zabbix_Server: 192.168.42.129
  port: 10050

查看下pillar中定义的Zabbix_Server和port的值:

[root@server2 pillar]#  salt '*' pillar.items
minion-192.168.42.130:
    ----------
    zabbix_agent:
        ----------
        Zabbix_Server:
            192.168.42.129
        port:
            10050
minion-192.168.42.128:
    ----------
    zabbix_agent:
        ----------
        Zabbix_Server:
            192.168.42.129
        port:
            10050

3.zabbix服务配置

此示例中使用源码进行安装,请提前准备好zabbix 源码包以及配置文件的模板。

[root@server2 files]# pwd
/srv/salt/prod/zabbix/files
[root@server2 files]# ls
zabbix-3.2.1.tar.gz  zabbix_agentd.conf

查看file_roots目录(/srv/salt/prod)中的内容:

[root@server2 prod]# ls
top.sls  zabbix

首先编写top.sls文件:

[root@server2 prod]# cat top.sls 

base:
  '*':
    - zabbix.zabbix_agent

这里的base指的是file_roots定义目录中的base:

file_roots:
  base:
    - /srv/salt/prod

在这个目录中有一个zabbix目录,该目录中有一个zabbix_agent.sls文件,主要的配置都在该文件中:

[root@server2 zabbix]# cat zabbix_agent.sls 
include:
  - zabbix.common_install

create_zabbix_user:
  user.present:
    - name: zabbix
    - shell: /sbin/nologin
  group.present:
    - name: zabbix

zabbix_tar:
  file.managed:
    - name: /tmp/zabbix-3.2.1.tar.gz
    - source: salt://zabbix/files/zabbix-3.2.1.tar.gz
    - user: zabbix
    - group: zabbix
    - mode: 0644

/opt/app:
  file.directory:
    - user: zabbix
    - group: zabbix

zabbix_decompression:
  cmd.run:
    - name: tar xvf /tmp/zabbix-3.2.1.tar.gz -C /opt/app 
    - unless: test -d /opt/app/zabbix-3.2.1
    - require:
      - file: /tmp/zabbix-3.2.1.tar.gz

zabbix_install:
  cmd.run:
    - name: cd /opt/app/zabbix-3.2.1 && ./configure --enable-agent && make && make install
    - require:
      - cmd: zabbix_decompression

/usr/local/etc/zabbix_agented.conf:
  file.managed:
    - name: /usr/local/etc/zabbix_agentd.conf
    - source: salt://zabbix/files/zabbix_agentd.conf
    - user: zabbix
    - group: zabbix
    - mode: 0644
    - template: jinja
    - defaults:
      Server: {{pillar['zabbix_agent']['Zabbix_Server']}}
    - require:
      - cmd: zabbix_install
run_zabbix:
  cmd.run:
    - name: /usr/local/sbin/zabbix_agentd -c /usr/local/etc/zabbix_agentd.conf
    - require:
      - cmd: zabbix_install
    - watch:
      - file: /usr/local/etc/zabbix_agentd.conf

在该文件的开始有一个include操作,安装zabbix之前系统可能需要先安装相关软件包和编译工具,所以把这个操作放在单独的zabbix.common_install文件中,该文件也在/srv/salt/prod/zabbix目录。内容如下:

[root@server2 zabbix]# cat common_install.sls 
pkg-init:
  pkg.installed:
    - names:
      - gcc
      - gcc-c++
      - glibc
      - make
      - autoconf
      - openssl
      - openssl-devel

接下来的过程包括:
(1)zabbix用户和组的创建;
(2)zabbix源码包的拷贝;
(3)源码包的解压缩和配置安装;
(4)zabbix客户端配置文件(zabbix_agentd.conf)的部署;
(5)zabbix服务的开启;

4.执行过程:

-------------

[root@server2 zabbix]# salt '*' state.highstate

Succeeded: 15 (changed=3)
Failed:     0
-------------
Total states run:     15
Total run time:   26.597 s

saltstack/salt的state.sls和pillar定义以及使用

SLS(代表SaLt State文件)是Salt State系统的核心。SLS描述了系统的目标状态,由格式简单的数据构成。这经常被称作配置管理 首先,在master上面定义salt的主目录,默认是在/srv/salt/下面,vim /etc/salt/master:

file_roots:
   base:
     - /srv/salt
   dev:
    - /srv/salt-dev

然后,在/srv/salt下面创建top.sls文件(如果有的话,就不用创建了,直接编辑好了) vim top.sls

base:
  '*':

top.sls 默认从 base 标签开始解析执行,下一级是操作的目标,可以通过正则,grain模块,或分组名,来进行匹配,再下一级是要执行的state文件

base:
  '*':               #通过正则去匹配所有minion
    - nginx          #这里都是我自己写的state.sls模块名 这里可以无视 后面会提到

  my_app:             #通过分组名去进行匹配 必须要定义match:nodegroup
    - match: nodegroup
    - nginx

  'os:Redhat':        #通过grains模块去匹配,必须要定义match:grain
    - match: grain
    - nginx

整个top.sls大概的格式就是这个样子,编写完top.sls后,编写state.sls文件;

cd /srv/salt 
vim nginx.sls

nginx.sls内容:

nginx:
  pkg:               #定义使用(pkg state module)
    - installed      #安装nginx(yum安装)
  service.running:   #保持服务是启动状态
    - enable: True
    - reload: True
    - require:
      - file: /etc/init.d/nginx
    - watch:                 #检测下面两个配置文件,有变动,立马执行上述/etc/init.d/nginx 命令reload操作
      - file: /etc/nginx/nginx.conf
      - file: /etc/nginx/fastcgi.conf
      - pkg: nginx
/etc/nginx/nginx.conf:       #绝对路径
  file.managed:
    - source: salt://files/nginx/nginx.conf  #nginx.conf配置文件在salt上面的位置
    - user: root
    - mode: 644
    - template: jinja   #salt使用jinja模块
    - require:
      - pkg: nginx

/etc/nginx/fastcgi.conf:
  file.managed:
    - source: salt://files/nginx/fastcgi.conf 
    - user: root
    - mode: 644
    - require:
      - pkg: nginx

在当前目录下面(salt的主目录)创建files/nginx/nginx.conf、files/nginx/fastcgi.conf文件,里面肯定是你自己项配置的nginx配置文件的内容啦;使用salt做自动化,一般nginx都是挺熟悉的,这里不做详细解释了

测试安装:

root@salt salt # salt 'sa10-003' state.sls nginx test=True
··········这里省略输出信息
Summary
------------
Succeeded: 8
Failed:    0
------------
Total:     8

往minion上面进行推送的时候,一般salt ‘sa10-003’ state.sls nginx 这种命令;当然,也可以执行 salt sa10-003 state.highstate 这种命令会默认匹配所有的state.sls模块。其中test=True 是指测试安装 ,也就是不进行实际操作,只是查看测试效果。

state的逻辑关系列表:
include: 包含某个文件 比如我新建的一个my_webserver.sls文件内,就可以继承nginx和php相关模块配置,而不必重新编写

root@salt salt # cat my_webserver.sls 
include:
  - nginx
  - php

match: 配模某个模块,比如 之前定义top.sls时候的 match: grain match: nodegroup require: 依赖某个state,在运行此state前,先运行依赖的state,依赖可以有多个 比如文中的nginx模块内,相关的配置必须要先依赖nginx的安装

- require:
  - pkg: nginx

watch: 在某个state变化时运行此模块,文中的配置,相关文件变化后,立即执行相应操作

- watch:
  - file: /etc/nginx/nginx.conf
  - file: /etc/nginx/fastcgi.conf
  - pkg: nginx

order: 优先级比require和watch低,有order指定的state比没有order指定的优先级高,假如一个state模块内安装多个服务,或者其他依赖关系,可以使用

nginx:
  pkg.installed:
    - order:1

想让某个state最后一个运行,可以用last

Pillar是Salt非常重要的一个组件,它用于给特定的minion定义任何你需要的数据,这些数据可以被Salt的其他组件使用。这里可以看出Pillar的一个特点,Pillar数据是与特定minion关联的,也就是说每一个minion都只能看到自己的数据,所以Pillar可以用来传递敏感数据(在Salt的设计中,Pillar使用独立的加密session,也是为了保证敏感数据的安全性)。 另外还可以在Pillar中处理平台差异性,比如针对不同的操作系统设置软件包的名字,然后在State中引用等。

定义pillar数据

默认情况下,master配置文件中的所有数据都添加到Pillar中,且对所有minion可用。默认如下:

#pillar_opts: True

master上配置文件中定义pillar_roots,用来指定pillar的数据存储在哪个目录

pillar_roots:
   base:
    - /srv/salt/pillar

首先,和state系统一样,pillar也是需要一个top.sls文件作为一个入口,用来指定对象。

base:
  '*':
    - pillar #这里指定了一个pillar模块

pillar.sls文件:

############IDC################
{% if grains['ip_interfaces'].get('eth0')[0].startswith('10.10') %}
nameservers: ['10.10.9.31','10.10.9.135']
zabbixserver: ['10.10.9.234']
{% else %}
nameservers: ['10.20.9.75']
zabbixserver: ['10.20.9.234']
{% endif %}

######## nginx ########
ngx_home_dir: /var/cache/nginx

上文的IDC这块是我自己整理的通过ip来划分不同的nameserver等,这里只是放出来参考,在State文件中将可以引用Pillar数据,比如引用
ngx_home_dir:

nginx:
  pkg:
    - installed
  user.present:
    - home: {{ pillar['ngx_home_dir'] }}
    - shell: /sbin/nologin
    - require:
      - group: nginx
  group.present:
    - require:
      - pkg: nginx
  service.running:
    - enable: True
    - reload: True
    - require:
      - file: /etc/init.d/nginx
      - file: /data1/logs/nginx
    - watch:
      - file: {{ pillar['ngx_conf_dir'] }}/nginx.conf
      - file: {{ pillar['ngx_conf_dir'] }}/fastcgi.conf
      - pkg: nginx

······ 后面关于配置就省略了

在pillar内可以提前将不同的部分根据在pillar内定义好,这样统一配置的时候就可以实现根据机器实际情况配置;比如根据机器的硬件情况配置nginx的worker_processes:

user nginx;
{% if grains['num_cpus'] < 8 %}
worker_processes {{ grains['num_cpus'] }};
{% else %}
worker_processes 8;
{% endif %}
worker_rlimit_nofile 65535;
``````````具体配置省略

很多定义的时候,都可以使用到pillar来进行自定义相关数据,具体情况可以自行摸索,这里只是个举例。

SaltStack配置管理–状态间的关系

1、include的引用

需求场景:用于含有多个SLS的状态,使用include可以进行多个状态的组合

[root@linux-node1 prod]# pwd
/srv/salt/prod
[root@linux-node1 prod]# vim lamp.sls
include:
  - apache.init
  - php.init
  - mysql.init
[root@linux-node1 prod]# vim ../base/top.sls 
prod:
  'linux-node1.example.com':
    - lamp
[root@linux-node1 prod]# salt -S "192.168.56.11" state.highstate
linux-node1.example.com:
----------
          ID: apache-install
    Function: pkg.installed
        Name: httpd
      Result: True
     Comment: All specified packages are already installed
     Started: 09:29:20.324067
    Duration: 984.864 ms
     Changes:   
----------
          ID: apache-config
    Function: file.managed
        Name: /etc/httpd/conf/httpd.conf
      Result: True
     Comment: File /etc/httpd/conf/httpd.conf is in the correct state
     Started: 09:29:21.311111
    Duration: 50.95 ms
     Changes:   
----------
          ID: apache-service
    Function: service.running
        Name: httpd
      Result: True
     Comment: The service httpd is already running
     Started: 09:29:21.362769
    Duration: 52.404 ms
     Changes:   
----------
          ID: php-install
    Function: pkg.installed
      Result: True
     Comment: All specified packages are already installed
     Started: 09:29:21.415555
    Duration: 0.693 ms
     Changes:   
----------
          ID: php-config
    Function: file.managed
        Name: /etc/php.ini
      Result: True
     Comment: File /etc/php.ini is in the correct state
     Started: 09:29:21.416438
    Duration: 15.578 ms
     Changes:   
----------
          ID: mysql-install
    Function: pkg.installed
      Result: True
     Comment: All specified packages are already installed
     Started: 09:29:21.432162
    Duration: 0.542 ms
     Changes:   
----------
          ID: mysql-config
    Function: file.managed
        Name: /etc/my.cnf
      Result: True
     Comment: File /etc/my.cnf is in the correct state
     Started: 09:29:21.432807
    Duration: 38.858 ms
     Changes:   
----------
          ID: mysql-service
    Function: service.running
        Name: mariadb
      Result: True
     Comment: The service mariadb is already running
     Started: 09:29:21.471799
    Duration: 38.431 ms
     Changes:   

Summary for linux-node1.example.com
------------
Succeeded: 8
Failed:    0
------------
Total states run:     8
Total run time:   1.182 s

2、extend的使用

需求场景:软件包安装的时候,需求假设:只在node1上按装php-mbstring包,其他的机器不安装。

[root@linux-node1 prod]# pwd
/srv/salt/prod
[root@linux-node1 prod]# vim lamp.sls 
include:
  - apache.init
  - php.init
  - mysql.init

extend:
  php-install:
    pkg.installed:
      - name: php-mbstring
[root@linux-node1 prod]# salt -S "192.168.56.11" state.highstate

3、require和require_in的使用

require:我依赖谁
require_in:我被谁依赖
需求场景:如果安装不成功或者配置httpd不成功,不启动httpd

(1)require使用
[root@linux-node1 apache]# pwd
/srv/salt/prod/apache
[root@linux-node1 apache]# systemctl stop httpd
[root@linux-node1 apache]# vim init_require.sls 
apache-install:
  pkg.installed:
    - name: httpd

apache-config:
  file.managed:
    - name: /etc/httpd/conf/httpd.conf
    - source: salt://apache/files/httpd1.conf----->将此处的文件改错,模拟配置错误
    - user: root
    - group: root
    - mode: 644

apache-service:
  service.running:
    - name: httpd
    - enable: True
    - require:---------------------------->使用require,表示依赖
      - pkg: apache-install--------------->依赖的状态模块为pkg模块,id为apache-install
      - file: apache-config--------------->依赖的状态模块为file模块,id为apache-config
[root@linux-node1 apache]# salt -S "192.168.56.11" state.highstate   #执行模块提示会有报错,此时httpd不会正常启动
......
----------
          ID: apache-config
    Function: file.managed
        Name: /etc/httpd/conf/httpd.conf
      Result: False
     Comment: Source file salt://apache/files/httpd1.conf not found
     Started: 09:48:33.459243
    Duration: 40.414 ms
     Changes:   
----------
          ID: apache-service
    Function: service.running
        Name: httpd
      Result: False
     Comment: One or more requisite failed: apache.init.apache-config
     Changes:   
----------
......
Summary for linux-node1.example.com
------------
Succeeded: 6
Failed:    2
------------
Total states run:     8
Total run time:   1.110 s
[root@linux-node1 apache]# systemctl status httpd
● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
   Active: inactive (dead) since Sat 2018-01-20 09:44:04 CST; 4min 59s ago
     Docs: man:httpd(8)
           man:apachectl(8)
  Process: 65439 ExecStop=/bin/kill -WINCH ${MAINPID} (code=exited, status=0/SUCCESS)
 Main PID: 1025 (code=exited, status=0/SUCCESS)
   Status: "Total requests: 0; Current requests/sec: 0; Current traffic:   0 B/sec"

Jan 17 10:41:59 linux-node1 systemd[1]: Starting The Apache HTTP Server...
Jan 17 10:42:02 linux-node1 systemd[1]: Started The Apache HTTP Server.
Jan 18 03:49:02 linux-node1 systemd[1]: Reloaded The Apache HTTP Server.
Jan 20 09:43:53 linux-node1 systemd[1]: Stopping The Apache HTTP Server...
Jan 20 09:44:04 linux-node1 systemd[1]: Stopped The Apache HTTP Server.


(2)require_in使用
[root@linux-node1 apache]# vim init_require_in.sls 
apache-install:
  pkg.installed:
    - name: httpd
    - require_in:------------------>被依赖
      - service: apache-service---->被依赖的模块是service,id为apache-service

apache-config:
  file.managed:
    - name: /etc/httpd/conf/httpd.conf
    - source: salt://apache/files/httpd.conf
    - user: root
    - group: root
    - mode: 644
    - require_in:
      - service: apache-service

apache-service:
  service.running:
    - name: httpd
    - enable: True

解释说明:require和require_in都能实现依赖的功能,主动和被动的关系不同

4、watch和watch_in的使用

需求场景:监控配置文件变动,重启服务或重载服务

[root@linux-node1 apache]# pwd
/srv/salt/prod/apache
[root@linux-node1 apache]# vim init_watch.sls 
apache-install:
  pkg.installed:
    - name: httpd

apache-config:
  file.managed:
    - name: /etc/httpd/conf/httpd.conf
    - source: salt://apache/files/httpd.conf
    - user: root
    - group: root
    - mode: 644

apache-service:
  service.running:
    - name: httpd
    - enable: True
    - watch:---------------------->使用watch
      - file: apache-config------->监控的模块为file,id为apache-config
[root@linux-node1 apache]# vim files/httpd.conf   #随意修改配置文件
[root@linux-node1 apache]# salt -S "192.168.56.11" state.highstate
......
----------
          ID: apache-config
    Function: file.managed
        Name: /etc/httpd/conf/httpd.conf
      Result: True
     Comment: File /etc/httpd/conf/httpd.conf updated
     Started: 10:07:14.430189
    Duration: 55.133 ms
     Changes:   
              ----------
              diff:
                  --- 
                  +++ 
                  @@ -1,4 +1,5 @@
                   #
                  +#hahahaaha--------------->检测到配置文件增加的内容
                   #hahahaaha
                   # This is the main Apache HTTP server configuration file.  It contains the
                   # configuration directives that give the server its instructions.
----------
          ID: apache-service
    Function: service.running
        Name: httpd
      Result: True
     Comment: Service restarted---------------------->将服务重启
     Started: 10:07:14.533852
    Duration: 1219.798 ms
     Changes:   
              ----------
              httpd:
                  True
......

#增加reload参数,让服务重载
[root@linux-node1 apache]# vim init_watch.sls 
apache-install:
  pkg.installed:
    - name: httpd

apache-config:
  file.managed:
    - name: /etc/httpd/conf/httpd.conf
    - source: salt://apache/files/httpd.conf
    - user: root
    - group: root
    - mode: 644

apache-service:
  service.running:
    - name: httpd
    - enable: True
    - reload: True----------------------------------->增加参数重载
    - watch:
      - file: apache-config

[root@linux-node1 apache]# salt -S "192.168.56.11" state.highstate
----------
          ID: apache-config
    Function: file.managed
        Name: /etc/httpd/conf/httpd.conf
      Result: True
     Comment: File /etc/httpd/conf/httpd.conf updated------>检测文件有变化
     Started: 10:10:08.493557
    Duration: 53.016 ms
     Changes:   
              ----------
              diff:
                  --- 
                  +++ 
                  @@ -1,4 +1,5 @@
                   #
                  +#hahahaaha
                   #hahahaaha
                   #hahahaaha
                   # This is the main Apache HTTP server configuration file.  It contains the
----------
          ID: apache-service
    Function: service.running
        Name: httpd
      Result: True
     Comment: Service reloaded---------------->服务重载
     Started: 10:10:08.596434
    Duration: 158.753 ms
     Changes:   
              ----------
              httpd:
                  True
----------
#watch_in的使用和require_in是一样的

5、unless:状态间的条件判断

需求场景:给apache的admin目录进行加密登陆查看

(1)修改配置文件,添加认证功能
[root@linux-node1 apache]# vim files/httpd.conf 
<Directory "/var/www/html/admin">
        AllowOverride All
        Order allow,deny
        Allow from all
        AuthType Basic
        AuthName "haha"
        AuthUserFile /etc/httpd/conf/htpasswd_file
        Require user admin
</Directory>


(2)修改状态文件init.sls
[root@linux-node1 apache]# vim init.sls 
apache-install:
  pkg.installed:
    - name: httpd

apache-config:
  file.managed:
    - name: /etc/httpd/conf/httpd.conf
    - source: salt://apache/files/httpd.conf
    - user: root
    - group: root
    - mode: 644

apache-auth:
  pkg.installed:
    - name: httpd-tools
  cmd.run:------>使用cmd模块的run方法
    - name: htpasswd -bc /etc/httpd/conf/htpasswd_file admin admin---->生成密码文件
    - unless: test -f /etc/httpd/conf/htpasswd_file---->unless判断条件,test -f判断为假则执行。即htpasswd文件如果不存在就执行生成密码

apache-service:
  service.running:
    - name: httpd
    - enable: True
    - reload: True
    - watch:
      - file: apache-config

[root@linux-node1 apache]# salt -S "192.168.56.11" state.highstate
......
----------
          ID: apache-auth
    Function: cmd.run
        Name: htpasswd -bc /etc/httpd/conf/htpasswd_file admin admin
      Result: True
     Comment: Command "htpasswd -bc /etc/httpd/conf/htpasswd_file admin admin" run
     Started: 10:34:54.930867
    Duration: 48.152 ms
     Changes:   
              ----------
              pid:
                  4166
              retcode:
                  0
              stderr:
                  Adding password for user admin
              stdout:
----------
          ID: apache-service
    Function: service.running
        Name: httpd
      Result: True
     Comment: Service reloaded
     Started: 10:34:55.014468
    Duration: 162.844 ms
     Changes:   
              ----------
              httpd:
                  True
......

浏览器访问192.168.56.11/admin/index.html会出现密码验证