saltstack实战 - 自动化运维管理

实验环境:
物理机执行: iptables -t nat -I POSTROUTING -s 172.25.254.0/24 -j MASQUERADE
让server1和server2可以上网。

server1 :172.25.254.1 master端
server2 : 172.25.254.2 minion端
server3 : 172.25.254.3 minion端

一、saltstack 简介

saltstack 简介

  • saltstack是一个配置管理系统,能够维护预定义状态的远程节点。
  • saltstack是一个分布式远程执行系统,用来在远程节点上执行命令和查询数据。
  • saltstack是运维人员提高工作效率、规范业务配置与操作的利器。
  • Salt的核心功能
    • 使命令发送到远程系统是并行的而不是串行的
    • 使用安全加密的协议
    • 使用最小最快的网络载荷
    • 提供简单的编程接口

Salt同样引入了更加细致化的领域控制系统来远程执行,使得系统成为目标不止可以通过主机名,还可以通过系统属性。

Salt 和ansible 最大的区别在于salt是具有客户端的,而 ansible 没有,salt 直接获取root权限进行访问,

saltstack 通讯机制

  • SaltStack 采用 C/S模式,minion与master之间通过ZeroMQ(轻量级)消息队列通信,默认监听4505端口。saltstack的优势就在于这个ZMQ,它运行起来的速度比ansible快很多

saltstack实战 - 自动化运维管理_第1张图片

  • Salt Master运行的第二个网络服务就是ZeroMQ REP系统,默认监听4506端口,就是接受客户端的回执的。

saltstack实战 - 自动化运维管理_第2张图片

saltstack 的安装与配置

我们可以去saltstack官网下载,也可以去阿里云中下载:
mirrors.aliyun.com

yum install https://mirrors.aliyun.com/saltstack/yum/redhat/salt-repo-latest-2.el7.noarch.rpm

执行这条命令会生成yum源:

[root@server1 images]# cd /etc/yum.repos.d/
[root@server1 yum.repos.d]# ls
redhat.repo  salt-latest.repo  westos.repo
			# 生成这个仓库
[root@server1 yum.repos.d]# cat salt-latest.repo 
[salt-latest]
name=SaltStack Latest Release Channel for RHEL/Centos $releasever
baseurl=https://repo.saltstack.com/yum/redhat/7/$basearch/latest
failovermethod=priority
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/saltstack-signing-key
## 我们把里面的路径改为指向阿里云,这样会更快一些。

[root@server1 yum.repos.d]# sed -i "s/repo.saltstack.com/mirrors.aliyun.com\/saltstack/g" /etc/yum.repos.d/salt-latest.repo
[root@server1 yum.repos.d]# cat salt-latest.repo 

baseurl=https://mirrors.aliyun.com/saltstack/yum/redhat/7/$basearch/latest
				# 这个 	
[root@server1 yum.repos.d]# yum repolist 
Loaded plugins: langpacks, product-id, search-disabled-repos, subscription-
              : manager
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
repo id            repo name                                              status
HighAvailability   westos.repoHighAvailability                               51
salt-latest/x86_64 SaltStack Latest Release Channel for RHEL/Centos 7Serv    83
##可以列出,说明没问题 	

然后我们在server2中也做同样的yum源的配置
master端设置:(server1)
• # yum install -y salt-master	#安装master端
• # systemctl enable salt-master #设置master自启动
• # systemctl start salt-master	#启动master服务
[root@server1 ~]# yum install -y lsof		# 可以用来查看打开的端口
[root@server1 ~]# lsof -i :4505		# 消息队列
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
salt-mast 3562 root   15u  IPv4  33242      0t0  TCP *:4505 (LISTEN)
[root@server1 ~]# lsof -i :4506			# 接收请求
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
salt-mast 3569 root   23u  IPv4  33287      0t0  TCP *:4506 (LISTEN)
# 此时server2还没有连接
minion端配置:
• # yum install -y salt-minion		#安装minion端
• # vim /etc/salt/minion
master: 172.25.0.1		#设置master主机的ip,注意冒号后有空格
• # systemctl enable salt-minion
• # systemctl start salt-minion

在这里插入图片描述
此时我们在server1中查看;

[root@server1 ~]# lsof -i :4505
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
salt-mast 3562 root   15u  IPv4  33242      0t0  TCP *:4505 (LISTEN)
## 4505端口没有变
[root@server1 ~]# lsof -i :4506
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
salt-mast 3569 root   23u  IPv4  33287      0t0  TCP *:4506 (LISTEN)
salt-mast 3569 root   30u  IPv4  37746      0t0  TCP server1:4506->server2:57622 (ESTABLISHED)
## server2 向 server1 发起了一个请求,这时我我们的master端需要通过key去允许,才可以建立连接,
相当于证书的颁发

saltstack实战 - 自动化运维管理_第3张图片
-A 允许所有,可以看出server2就到了接受的keys 里面去了。
这里我们要注意

[root@server2 salt]# cat minion_id 
server2		这个为文件里面记录了主机名,salt会始终会去文件中的名字,当我们更改了主机名时,要及时的去这个文件中更改,或者删除文件,重启minion端

[root@server1 ~]# lsof -i :4505
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
salt-mast 3562 root   15u  IPv4  33242      0t0  TCP *:4505 (LISTEN)
salt-mast 3562 root   17u  IPv4  38484      0t0  TCP server1:4505->server2:36732 (ESTABLISHED)
## 这是我们的主服务就和server2 建立了长连接了。这样当server1发布新东西时,这时所有minion端就可以接收到

[root@server1 ~]# lsof -i :4506
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
salt-mast 3569 root   23u  IPv4  33287      0t0  TCP *:4506 (LISTEN)

查看更详尽的进程信息:

[root@server1 ~]# yum install -y python-setproctitle.x86_64 		# 安装这个包
[root@server1 ~]# systemctl restart salt-master.service 			# 重启salt
[root@server1 ~]# ps ax 		# 查看
 5983 ?        Ss     0:00 /usr/bin/python /usr/bin/salt-master ProcessManager
 5990 ?        S      0:00 /usr/bin/python /usr/bin/salt-master MultiprocessingLoggingQueue
 5997 ?        Sl     0:00 /usr/bin/python /usr/bin/salt-master ZeroMQPubServerChannel		# 消息队列
 6000 ?        S      0:00 /usr/bin/python /usr/bin/salt-master EventPublisher
 6001 ?        S      0:00 /usr/bin/python /usr/bin/salt-master Maintenance
 6002 ?        S      0:00 /usr/bin/python /usr/bin/salt-master ReqServer_ProcessManager		# 请求响应
 6003 ?        Sl     0:00 /usr/bin/python /usr/bin/salt-master MWorkerQueue		# 维护队列
 6004 ?        R      0:00 /usr/bin/python /usr/bin/salt-master MWorker-0
 6005 ?        R      0:00 /usr/bin/python /usr/bin/salt-master MWorker-1
 6006 ?        R      0:00 /usr/bin/python /usr/bin/salt-master MWorker-2
 6007 ?        R      0:00 /usr/bin/python /usr/bin/salt-master MWorker-3
 6008 ?        Sl     0:00 /usr/bin/python /usr/bin/salt-master FileserverUpdate
 6019 ?        R      0:00 /usr/bin/python /usr/bin/salt-master MWorker-4

master 端测试与minion端的连接:
在这里插入图片描述
这里 * 代表当先目录下的所有文件,所以我们要加上 ‘’ 来让他代表slat里面的所有key(用salt-key -L 可以列出的)。
也可以直接指定key。
在这里插入图片描述
这时我们就可以使用远程命令了,而且我们是超户哦:

[root@server1 ~]# salt server2 cmd.run df
server2:
    Filesystem            1K-blocks    Used Available Use% Mounted on
    /dev/mapper/rhel-root  17811456 1249308  16562148   8% /
    devtmpfs                 495420       0    495420   0% /dev
    tmpfs                    507512      80    507432   1% /dev/shm
    tmpfs                    507512   13220    494292   3% /run
    tmpfs                    507512       0    507512   0% /sys/fs/cgroup
    /dev/sda1               1038336  135224    903112  14% /boot
    tmpfs                    101504       0    101504   0% /run/user/0
[root@server1 ~]# salt server2 cmd.run pwd
server2:
    /root
    
[root@server1 ~]# salt server2 cmd.run 'touch /tmp/file'
server2:

[root@server2 ~]# ls /tmp/file
/tmp/file

二、 saltstack远程执行

远程执行shell命令

Salt命令由三个主要部分构成:

 salt ''  [arguments]
 		 目标	 	函数			内容

Targect:

target: 指定哪些minion, 默认的规则是使用glob匹配minion id.
[root@server1 ~]# salt server? test.ping		# 使用通配符
server2:
    True
[root@server1 ~]# salt server[1-3] test.ping
server2:
    True
[root@server1 ~]# salt '*' test.ping
server2:
    True
[root@server1 ~]# salt 'server2' test.ping
server2:
    True
[root@server1 ~]# salt 'server2,server3' test.ping
No minions matched the target. No command was sent, no jid was assigned.
ERROR: No return received
[root@server1 ~]# salt -L 'server2,server3' test.ping		# 使用列表
server2:
    True
server3:
    Minion did not return. [No response]
ERROR: Minions returned with non-zero exit code
[root@server1 ~]# salt -E 'server[1-3]' test.ping			# 使用正则表达式
server2:
    True

Function:

funcation是module提供的功能,Salt内置了大量有效的functions.(其实就是python的各种模块)
## http://docs.saltstack.cn/	查看中文手册
[root@server1 ~]# salt '*' cmd.run 'uname -a'		# 远程执行
server2:
    Linux server2 3.10.0-957.el7.x86_64 #1 SMP Thu Oct 4 20:48:51 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

[root@server1 ~]# salt 'server2' sys.doc pkg			#查看模块文档
arguments通过空格来界定参数:
        salt '*' pkg.remove 
        salt '*' pkg.remove ,,
        salt '*' pkg.remove pkgs='["foo", "bar"]'

saltstack实战 - 自动化运维管理_第4张图片
这样就很简单的完成了远程安装。命令游4505端口推送,下面的带颜色的结果由4506端口接收的。
我们还可以去:
http://docs.saltstack.cn/ref/modules/all/index.html
查看salt内置的执行模块列表,这里面的使用方式是写文件时的用法,和命令行不太相同。

salt的数据都会保存在/var/cache/salt 目录中,(ansible保存在 /tmp 中)

[root@server2 salt]# pwd
/var/cache/salt
[root@server2 salt]# tree .
.
`-- minion
    |-- extmods
    `-- proc

3 directories, 0 files

编写远程执行模块

当系统自带的模块不够时,我们就可以去自己编写。
默认的文件目录为:

[root@server1 ~]# vim  /etc/salt/master
#
#file_roots:
#  base:
#    - /srv/salt		# 在这里
#
[root@server1 ~]# mkdir /srv/salt				# 这里就是salt的base目录,所有的数据都定位到这里,ansible指定的是当前目录。
[root@server1 ~]# mkdir /srv/salt/_modules	# 这里是模块固定存放目录,
[root@server1 _modules]# vim mydisk.py		# 编写文件
def df():
    return __salt__['cmd.run']('df -h')
[root@server1 _modules]# salt server2 saltutil.sync_modules		# 同步到server2
server2:
    - modules.mydisk


在server2中查看:
[root@server2 salt]# tree .
.
`-- minion
    |-- extmods
    |   `-- modules
    |       `-- mydisk.py
    |-- files
    |   `-- base			# 这个base就是/srv/salt
    |       `-- _modules
    |           `-- mydisk.py				# 文件就通不过来了
    |-- module_refresh
    `-- proc
##  可以看出文件缓存到了server2中的 /var/cache/salt  目录中

然后我们就可以调用了:
[root@server1 salt]# salt server2 mydisk.df			# 调用我们刚才写的mydisk模块的df方法。
server2:
    Filesystem             Size  Used Avail Use% Mounted on
    /dev/mapper/rhel-root   17G  1.2G   16G   8% /
    devtmpfs               484M     0  484M   0% /dev
    tmpfs                  496M  100K  496M   1% /dev/shm
    tmpfs                  496M   13M  483M   3% /run
    tmpfs                  496M     0  496M   0% /sys/fs/cgroup
    /dev/sda1             1014M  133M  882M  14% /boot
    tmpfs                  100M     0  100M   0% /run/user/0

了解 YAML 语法

编写模块时使用的是 YAML 语法,它的使用规则:
saltstack实战 - 自动化运维管理_第5张图片

配置管理

创建sls文件

  • SLS(代表SaLt State文件)是Salt State系统的核心。SLS描述了系统的目标状态,由格式简单的数据构成。这经常被称作配置管理。

sls文件的写法有两种方式;

			vim apache.sls
[root@server1 salt]# cat apache.sls 
httpd:
  pkg.installed			(这种是写到了一起)

[root@server1 salt]# cat apache.sls 
httpd:				# 声明(这里是软件包的声明)
  pkg:				# 状态声明(模块)
    - installed		# 函数声明


[root@server1 salt]# salt server2 state.sls apache		# 指定主机执行

这时在server2上它会先把sls文件推送过来,再去执行安装。
[root@server2 salt]# tree .
.
`-- minion
    |-- extmods
    |   `-- modules
    |       |-- mydisk.py
    |       `-- mydisk.pyc
    |-- files
    |   `-- base
    |       |-- apache.sls		# 推送过来了
    |       `-- _modules
    |           `-- mydisk.py
    |-- module_refresh
    `-- proc
        `-- 20200608051500237034

当我们需要安装多个包时:

web:
  pkg.installed:
    - pkgs:
      - httpd			# 这里指定包,可以接多个
	  - php

推送过去:
[root@server1 salt]# salt server2 state.sls apache
saltstack实战 - 自动化运维管理_第6张图片
在server2中查看日志:
saltstack实战 - 自动化运维管理_第7张图片
它再安装的时后连依赖性也安装了。这时候如果我们在删除的时后就不会卸载到那些依赖性,因为salt只会把状态调整到 installed ,中间的过程可能会多样,所以就造成了这种结果。这不是一种还原机制。


  • Salt 状态系统的核心是SLS,或者叫SaLt State 文件。
  • SLS表示系统将会是什么样的一种状态,而且是以一种很简单的格式来包含这些数据,
    常被叫做配置管理。
  • sls文件命名:
    • sls文件以”.sls”后缀结尾,但在调用是不用写此后缀。
    • 使用子目录来做组织是个很好的选择。
    • init.sls 在一个子目录里面表示引导文件,也就表示子目录本身, 所以apache/init.sls 就是
      表示apache.
    • 如果同时存在apache.sls 和 apache/init.sls,则 apache/init.sls 被忽略,apache.sls将被用来
      表示 apache.

当我们的sls文件越写越多时,为了方便,我们会在base目录创建目录,将一个属性 sls文件全部放到里面:

[root@server1 salt]# mkdir apache
[root@server1 salt]# mv apache.sls apache/installed.sls
[root@server1 salt]# ls apache/			# 就好比ansibe中的角色一样
installed.sls
[root@server1 salt]# salt server2 state.sls apache.installed			# 这时的推送是这样的,用 . 分隔

这样有时会出现弊端,那我们还有另一种方式:

[root@server1 apache]# mv installed.sls init.sls			# init.sls 入口文件,每次都会先访问这个文件
[root@server1 apache]# cd			# 进入根目录
[root@server1 ~]# salt server2 state.sls apache		# 也可以进行推送了,不必进入base的目录
server2:								#注意这里只写apache,init.sls引导文件就代表apache
----------
          ID: web
    Function: pkg.installed
      Result: True
     Comment: All specified packages are already installed
     Started: 14:12:36.650803
    Duration: 555.309 ms
     Changes:   

Summary for server2
------------
Succeeded: 1
Failed:    0
------------
Total states run:     1
Total run time: 555.309 ms

我们可以把其它的sls文件,用include 写入到 int.sls 文件中

我们现在用init.sls开启httpd:

[root@server1 salt]# cat apache/init.sls 
web:
  pkg.installed:
    - pkgs:
      - httpd
      - php
  service.running:
    - name: httpd
[root@server1 salt]# salt server2 state.sls apache

saltstack实战 - 自动化运维管理_第8张图片
server2中的apache就开起来了.

[root@server1 salt]# cat apache/init.sls 
web:
  pkg.installed:
    - pkgs:
      - httpd
      - php
  service.running:
    - name: httpd
    - enable: True		# 加上开机自启
[root@server1 salt]# salt server2 state.sls apache

[root@server2 salt]# systemctl is-enabled httpd
enabled
[root@server2 salt]# tree 
.
`-- minion
    |-- accumulator
    |-- extmods
    |   `-- modules
    |       |-- mydisk.py
    |       `-- mydisk.pyc
    |-- files
    |   `-- base
    |       |-- apache
    |       |   |-- init.sls	 # 这里
    |       |   `-- installed.sls
    |       |-- apache.sls
    |       `-- _modules
    |           `-- mydisk.py
    |-- highstate.cache.p
    |-- module_refresh
    |-- pkg_refresh
    |-- proc
    `-- sls.p

配置文件:

[root@server1 salt]# cat apache/init.sls 
web:
  pkg.installed:
    - pkgs:
      - httpd
      - php
  file.managed:
    - source: salt://apache/files/httpd.conf			#源地址,指/srv/salt/apache/files/httpd.conf
    - name: /etc/httpd/conf/httpd.conf			# 目的地地址

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


[root@server1 salt]# cd apache/
[root@server1 apache]# mkdir files			# 创建对应目录
[root@server1 apache]# scp server2:/etc/httpd/conf/httpd.conf ./files		# 拷贝一个文件过来
root@server2's password: 
httpd.conf                                                                           100%   11KB   5.6MB/s   00:00    
[root@server1 apache]# vim httpd.conf

在这里插入图片描述
我们将端口改为8080 端口.以示区别。

在这里插入图片描述
server2的配置文件已经更改为8080端口了。但是此时的netstat 端口还是80端口,所以我们

web:
  pkg.installed:
    - pkgs:
      - httpd
      - php
  file.managed:
    - source: salt://apache/files/httpd.conf
    - name: /etc/httpd/conf/httpd.conf

  service.running:
    - name: httpd
    - enable: True
    - watch:
      - file: web			

用 watch 监控web声明,这里应注意,每个声明底下的模块只能使用一次,不然就会混乱,比如web地下只能有一个file模块,如果想再加的话,就再写一个声明

[root@server1 salt]# salt server2 state.sls apache

[root@server2 salt]# netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      3055/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      3149/master         
tcp6       0      0 :::8080                 :::*                    LISTEN      14806/httpd         	#就变成8080端口了
tcp6       0      0 :::22                   :::*                    LISTEN      3055/sshd           
tcp6       0      0 ::1:25                  :::*                    LISTEN      3149/master 

我们还可以这么写:

web:
  pkg.installed:
    - pkgs:
      - httpd
      - php

  service.running:
    - name: httpd
    - enable: True
    - watch:
      - file: /etc/httpd/conf/httpd.conf

/etc/httpd/conf/httpd.conf:			# name替换为声明
  file.managed:			# 将file独立出来
    - source: salt://apache/files/httpd.conf

开启server3,做和server2相同的配置:

[root@server3 packages]# yum install -y salt-minion.noarch 	下载开启服务,enable。
[root@server3 packages]# vim /etc/salt/minion		# 设置master

[root@server1 salt]# salt-key -L
Accepted Keys:
server2
Denied Keys:
Unaccepted Keys:
server3
Rejected Keys:
[root@server1 salt]# salt-key -A
The following keys are going to be accepted:
Unaccepted Keys:
server3
Proceed? [n/Y] Y
Key for minion server3 accepted.
[root@server1 salt]# salt-key -L
Accepted Keys:
server2
server3
Denied Keys:
Unaccepted Keys:
Rejected Keys:
[root@server1 salt]# lsof -i :4505
COMMAND    PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
/usr/bin/ 5997 root   15u  IPv4  40208      0t0  TCP *:4505 (LISTEN)
/usr/bin/ 5997 root   17u  IPv4  42437      0t0  TCP server1:4505->server2:36740 (ESTABLISHED)
/usr/bin/ 5997 root   23u  IPv4  89914      0t0  TCP server1:4505->server3:37524 (ESTABLISHED)

nginx配置

[root@server1 salt]# mkdir nginx
[root@server1 salt]# cd nginx
[root@server1 nginx]# vim init.sls
nginx-install:				# 安装依赖性
  pkg.installed:
    - pkgs:
      - openssl-devel
      - pcre-devel
      - gcc

  file.managed:		# 源码包推送过去
    - name: /mnt/nginx-1.17.10.tar.gz
    - source: salt://nginx/files/nginx-1.17.10.tar.gz

  cmd.run:		# 编译安装
    - name: cd /mnt/ && tar zxf nginx-1.17.10.tar.gz && cd nginx-1.17.10 && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --prefix=/usr/local/nginx --with-http_ssl_module &> /dev/null && make &> /dev/null && make install &> /dev/null
    - creates: /usr/local/nginx			# cmd会重复执行编译安装,这个参数会检测路径是否存在,若存在,就不再编译安装了

nginx-service:
  file.managed:			#推送文件
    - name: /usr/lib/systemd/system/nginx.service
    - source: salt://nginx/files/nginx.service

  service.running:			# 启动服务
    - name: nginx


[root@server1 nginx]# mkdir files
[root@server1 nginx]# cd files/
[root@server1 files]# ls
nginx-1.17.10.tar.gz  nginx.service		# 脚本文件和源码包
[root@server1 files]# cat nginx.service 
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target                       
[root@server1 nginx]# salt server3 state.sls nginx

[root@server3 sbin]# ps ax				# 启动起来了
 6868 ?        Ss     0:00 nginx: master process ./nginx
 6869 ?        S      0:00 nginx: worker process

加入配置文件:

nginx-install:
  pkg.installed:
    - pkgs:
      - openssl-devel
      - pcre-devel
      - gcc

  file.managed:
    - name: /mnt/nginx-1.17.10.tar.gz
    - source: salt://nginx/files/nginx-1.17.10.tar.gz

  cmd.run:
    - name: cd /mnt/ && tar zxf nginx-1.17.10.tar.gz && cd nginx-1.17.10 && sed -i 's/CFLAGS="$CFLAGS -g"/#CFLAGS="$CFLAGS -g"/g' auto/cc/gcc && ./configure --prefix=/usr/local/nginx --with-http_ssl_module &> /dev/null && make &> /dev/null && make install &> /dev/null
    - creates: /usr/local/nginx

/usr/local/nginx/conf/nginx.conf:		# 拷贝配置文件过去
  file.managed:
    - source: salt://nginx/files/nginx.conf

nginx-service:
  file.managed:
    - name: /usr/lib/systemd/system/nginx.service
    - source: salt://nginx/files/nginx.service

  service.running:
    - name: nginx
    - enable: True		# 自启
    - reload: True		# 重新加载
    - watch:			# 监控
      - file: /usr/local/nginx/conf/nginx.conf
[root@server1 files]# vim nginx.conf 
worker_processes  2;		# 设置worker 两个
[root@server1 files]# salt server3 state.sls nginx
[root@server3 sbin]# ps ax
 6895 ?        Ss     0:00 nginx: master process /usr/local/nginx/sbin/nginx
 6898 ?        S      0:00 [kworker/u2:0]
 6913 ?        S      0:00 [kworker/0:2]
 6916 ?        R      0:00 [kworker/0:0]
 6978 ?        S      0:00 nginx: worker process
 6979 ?        S      0:00 nginx: worker process		# 就有两个worker进程了

在这里我们举例使用一下include:

[root@server1 nginx]# cat init.sls 
nginx-install:
  pkg.installed:
    - pkgs:
      - openssl-devel
      - pcre-devel
      - gcc

  file.managed:
    - name: /mnt/nginx-1.17.10.tar.gz
    - source: salt://nginx/files/nginx-1.17.10.tar.gz

  cmd.run:
    - name: cd /mnt/ ...
    - creates: /usr/local/nginx

include:				# 这就是include的使用方式,默认只读取接口文件,将另一个文件放入到这个接口文件中,就可以使用了
  - nginx.service				# nginx 目录下的 service 文件
 
[root@server1 nginx]# cat service.sls 		# 这个文件放的是从init.sls中截取的文件内容
/usr/local/nginx/conf/nginx.conf:
  file.managed:
...

[root@server1 nginx]# salt server3 state.sls nginx		# 推送
server3:
----------
          ID: /usr/local/nginx/conf/nginx.conf
    Function: file.managed
      Result: True
     Comment: File /usr/local/nginx/conf/nginx.conf is in the correct state
     Started: 11:04:36.407291
    Duration: 45.936 ms
     Changes:   
----------
          ID: nginx-service
    Function: file.managed
        Name: /usr/lib/systemd/system/nginx.service
我们发现会优先执行include的内容

top文件

  • 大多数技术系统的基础架构是由计算机组成,该架构中的每台计算机都扮演着与其他计算机相似的角色。 这些机器相互协作以创建应用程序的技术栈。
  • 为了有效地管理这些计算机设备,管理员需要能够为这些计算机或计算机分组创建角色。例如,一组为前端Web流量提供服务的计算机可能具有某些角色,这些角色指示这些计算机都应都安装了Apache Web服务器软件包,并且Apache服务应始终处于运行状态。
  • 在Salt中,包含网络上的计算机分组与应用于它们的配置角色之间的映射关系的文件称为top file文件。
  • 默认情况下,顶级文件的名称为top.sls,之所以如此命名是因为它们始终存在于包含状态文件的目录层次结构的“顶部”中。该目录层次结构称为state tree状态树。

我们进入base目录(/srv/salt),创建top.sls文件

[root@server1 salt]# vim top.sls 
[root@server1 salt]# cat top.sls 
base:
  'server2':
    - apache				# 在server2上执行apache的sls文件
  'server3':
    - nginx					 # 在server3上执行 nginx 的sls文件

说明了对于名为base的环境,server2将应用名为apache的状态文件。server3将应用名为nginx的状态文件。
Salt将在/srv/salt(base,在master的配置文件中设定)目录中查找apache.sls或init.sls。
在这里插入图片描述
这种高级状态文件的推送方式为:

[root@server1 salt]# salt '*' state.highstate
server2:
          ID: web
    Function: service.running
        Name: httpd
      Result: True
     Comment: The service httpd is already running
Summary for server2
------------
Succeeded: 3
Failed:    0
------------
server3:
----------
          ID: nginx-service
    Function: service.running
        Name: nginx
      Result: True
     Comment: The service nginx is already running  

Summary for server3
------------
Succeeded: 6
Failed:    0
------------

而且只会把状态不同的进行变更。

三、grains与pillar详解(数据采集系统)

grains和pillar的区别在于:

  • grains:静态,在minion生效
  • pillar:动态,在server端生效

像上面的操作,我们在推送的时后会把数据缓存到minion端的 /var/cache/salt/ 中,使用pillar方式就不会。

grains

  • Grains是SaltStack的一个组件,存放在SaltStack的minion端。
  • 当salt-minion启动时会把收集到的数据静态存放在Grains当中,只有当minion重启时才会进行数据的更新。
  • 由于grains是静态数据,因此不推荐经常去修改它。
  • 应用场景:
    • 信息查询,可用作CMDB。
    • 在target中使用,匹配minion。
    • 在state系统中使用,配置管理模块。

信息查询:
用于查询minion端的IP、FQDN等信息。

## 默认可用的grains:salt '*' grains.ls
[root@server1 salt]# salt server2 grains.ls
server2:
    - SSDs
    - biosreleasedate
    - biosversion
    - cpu_flags
    - cpu_model
    - cpuarch
    - cwd
    - disks
    - dns
    - domain
    - fqdn
    - fqdn_ip4

## 查看每一项的值:
[root@server1 salt]# salt server3 grains.items
        Red Hat
    master:
        172.25.254.1
    mdadm:
    mem_total:
        991
    nodename:
        server3
## 取单项的值:
[root@server1 salt]# salt server3 grains.item ipv4			# 不是 items 了
server3:
    ----------
    ipv4:
        - 127.0.0.1
        - 172.25.254.3

这些grains,我们可以在minion端的配置文件去静态指定

grains:
  roles: apache		##指定这个键值对
#    - webserver		列表的方式可以通过短杠指定
#    - memcache	
#  deployment: datacenter4
#  cabinet: 13
#  cab_u: 14-15
[root@server2 minion.d]# systemctl restart salt-minion.service 
#重启salt-minion,否则数据不会更新

[root@server1 salt]# salt server2 grains.item roles
server2:
    ----------
    roles:					# 然后就可以看到了。
        apache

我们还可以去etc/salt/grains中定义:

[root@server3 files]# vim /etc/salt/grains
[root@server3 files]# cat /etc/salt/grains 
roles: nginx

[root@server1 salt]# salt server3 saltutil.sync_grains			#同步数据
server3:
[root@server1 salt]# salt server3 grains.item roles			# 就可以查看了,这种方式不用重启服务
server3:
    ----------
    roles:
        nginx

还可以通过编写模块的方式;

[root@server1 salt]# mkdir /srv/salt/_grains		# 在salt-master端创建_grains目录
[root@server1 salt]# cd _grains/
[root@server1 _grains]# vim my_grain.py
[root@server1 _grains]# cat my_grain.py 
def my_grain():
    grains = {}
    grains['salt'] = 'stack'
    grains['hello'] = 'world'
    return grains
[root@server1 _grains]# salt '*' saltutil.sync_grains			# 同步到所有结点
server2:
    - grains.my_grain
server3:
    - grains.my_grain

[root@server1 _grains]# salt 'server2' grains.item hello
server2:
    ----------
    hello:
        world
[root@server1 _grains]# salt 'server3' grains.item salt
server3:
    ----------
    salt:
        stack


[root@server2 salt]# tree .
.
`-- minion
    |-- accumulator
    |-- extmods
    |   |-- grains
    |   |   |-- my_grain.py
    |   |   `-- my_grain.pyc
    |   `-- modules
    |       |-- mydisk.py
    |       `-- mydisk.pyc
    |-- files
    |   `-- base
    |       |-- apache
    |       |   |-- files
    |       |   |   `-- httpd.conf
    |       |   |-- init.sls
    |       |   `-- installed.sls
    |       |-- apache.sls
    |       |-- _grains
    |       |   `-- my_grain.py		# 同步过来了

grains匹配运用:

[root@server1 _grains]# salt -G 	roles:apache 	cmd.run hostname
server2:						#这里是server2中的角色
    server2
-G 通过grains来匹配目标,只要目标有roles是 apache 的就执行 hostname
[root@server1 _grains]# salt -G roles:apache cmd.run hostname
server2:
    server2
[root@server1 _grains]# salt -G roles:nginx cmd.run hostname
server3:
    server3
[root@server1 _grains]# salt -G hello:world cmd.run hostname
server2:
    server2
server3:
    server3

在top文件中匹配:

[root@server1 salt]# vim top.sls 
[root@server1 salt]# cat top.sls 
base:
  'roles:apache':
    - match: grain			# 通过grain匹配roles是apache的主机,相当于标签,方便了对很多台主机进行操作
    - apache
  'roles:nginx':
    - match: grain
    - nginx
[root@server1 salt]# salt '*' state.highstate		# 高级推送

pillar

  • pillar和grains一样也是一个数据系统,但是应用场景不同。
  • pillar是将信息动态的存放在master端,主要存放私密、敏感信息(如用户名密码等),而且可以指定某一个minion才可以看到对应的信息。
  • pillar更加适合在配置管理中运用。

数据不能存放到/srv/salt上,因为这个目录里的数据都推推送到minion端。

定义pillar基础目录:
[root@server1 salt]# vim /etc/salt/master
saltstack实战 - 自动化运维管理_第9张图片
我们可以在这里进行指定,也可以采用默认,我们采用默认。

[root@server1 salt]# mkdir /srv/pillar
[root@server1 salt]# vim packages.sls
{% if grains['fqdn'] == 'server3' %}			# 如果主机名是server3
package: nginx										# 是这个对印关系
{% elif grains['fqdn'] == 'server2' %}			# 如果
package: httpd
{% endif %}
[root@server1 pillar]# vim top.sls				# 编写top文件
[root@server1 pillar]# cat top.sls 
base:
  '*':
    - packages
这时我们就可以查看了,因为是动态的,所以不需要刷新或重启
[root@server1 pillar]# salt '*' pillar.items
server3:
    ----------
    package:
        nginx			# 获取到了
server2:
    ----------
    package:
        httpd

[root@server1 pillar]# salt '*' pillar.item package		#但是我们单独查看的时候
server3:
    ----------
    package:
server2:
    ----------
    package:			# 就访问不到了
[root@server1 pillar]# salt '*' saltutil.refresh_pillar			# 刷新
server2:	
    True
server3:
    True
[root@server1 pillar]# salt '*' pillar.item package
server2:
    ----------
    package:
        httpd
server3:
    ----------
    package:
        nginx			# 就能访问了

pillar的数据匹配:

[root@server1 pillar]# salt -I package:nginx cmd.run hostname
server3:
    server3
##  -I 表示用 pillar 的方式查询 package 对应 nginx 的主机执行 hostname

state系统中使用:

[root@server1 salt]# cd apache/
[root@server1 apache]# ls
files  init.sls
[root@server1 apache]# vim init.sls 
web:
  pkg.installed:
    - pkgs:
      - {{ pillar['package'] }}		这里本来是 name:httpd,现在在pillar中获取
      - php
。。。
[root@server1 apache]# salt '*' state.highstate			# 高级推送没问题
[root@server1 apache]# salt server2 state.sls apache		# 指定推送也没有问题
[root@server1 apache]# salt server3 state.sls apache		# 推送给server3时就会报错,因为3上装的nginx

# 匹配nginx:
[root@server1 nginx]# ls
files  init.sls
[root@server1 nginx]# vim init.sls
  service.running:
    - name: {{ pillar['package'] }}			# 用pillar去获取nginx
    - enable: True
    - reload: True
    - watch:
      - file: /usr/local/nginx/conf/nginx.conf
[root@server1 nginx]# salt server3 state.sls nginx			# 推送没有问题

四、Jinja模板

  • Jinja是一种基于python的模板引擎,在SLS文件里可以直接使用jinja7模板来做一些操作。
  • 通过jinja模板可以为不同服务器定义各自的变量。
  • 两种分隔符: {% … %} 和 {{ … }},前者用于执行诸如 for 循环 或赋值的语句,后者把表达式的结果打印到模板上。

jinja模板的使用方式

Jinja最基本的用法是使用控制结构包装条件:

[root@server1 salt]# ls
apache  _grains  _modules  nginx top.sls
[root@server1 salt]# vim test.sls 
/mnt/testfile:
  file.append:
    {% if grains['fqdn'] == 'server2' %}
    - text: server2
    {% elif grains['fqdn'] == 'server3' %}			# 通过grains 获取
    - text: server3
    {% endif %}
[root@server1 salt]# salt '*' state.sls test		# 推送

在server2个serve'r3中查看;
[root@server3 salt]# cat /mnt/testfile 
server3
[root@server2 salt]# cat /mnt/testfile 
server2

Jinja在普通文件的使用:

[root@server1 salt]# cd apache/
[root@server1 apache]# ls
files  init.sls
[root@server1 apache]# vim init.sls 
web:
  pkg.installed:
    - pkgs:
      - {{ pillar['package'] }}
      - php

  service.running:
    - name: httpd
    - enable: True
    - watch:
      - file: /etc/httpd/conf/httpd.conf
 
/etc/httpd/conf/httpd.conf:
  file.managed:
    - source: salt://apache/files/httpd.conf
    - template: jinja		# 使用jinja模板
    - context:					# 直接更改
      bind: 172.25.254.2

[root@server1 apache]# vim files/httpd.conf 		# 在配置文件中更改
Listen {{ bind }}:80		# 监听server2的80端口。

[root@server1 apache]# salt server2 state.sls apache		# 推送		
server2:

          ID: /etc/httpd/conf/httpd.conf
    Function: file.managed
      Result: True
     Comment: File /etc/httpd/conf/httpd.conf updated
     Started: 23:13:33.279187
    Duration: 32.654 ms
     Changes:   
              ----------
              diff:
                  --- 
                  +++ 
                  @@ -39,7 +39,7 @@
                   # prevent Apache from glomming onto all bound IP addresses.
                   #
                   #Listen 12.34.56.78:80
                  -Listen 8080
                  +Listen 172.25.254.2:80		# 更改了

我们这里还可以用pillar直接进行调用:

[root@server1 apache]# cd /srv/pillar/
[root@server1 pillar]# ls
packages.sls  top.sls
[root@server1 pillar]# vim packages.sls 
{% if grains['fqdn'] == 'server3' %}
package: nginx
{% elif grains['fqdn'] == 'server2' %}
package: httpd
port: 8080
{% endif %}
[root@server1 pillar]# cd ../salt/apache/files/
[root@server1 files]# vim httpd.conf 

Listen {{ bind }}:{{ pillar['port'] }}		# 更改如这行,使用jinja模板才可以调用pillar
[root@server1 files]# salt server2 state.sls apache
                   #Listen 12.34.56.78:80
                  -Listen 172.25.254.2:80
                  +Listen 172.25.254.2:8080		# 更改了

引用变量:

[root@server1 files]# vim httpd.conf

在这里插入图片描述

[root@server1 files]# salt server2 state.sls apache		# 推送也成功了

在state文件中引用:

[root@server1 files]# vim httpd.conf

在这里插入图片描述
直接使用变量,然后去sls文件中更改:

[root@server1 files]# vim ../init.sls 
[root@server1 files]# cat ../init.sls 
...
/etc/httpd/conf/httpd.conf:
  file.managed:
    - source: salt://apache/files/httpd.conf
    - template: jinja
    - context:
      bind: {{ grains['ipv4'][-1] }}		# 直接在这里引用
      port: {{ pillar['port'] }}

引用变量文件:

[root@server1 salt]# vim lib.sls		# 建立文件
[root@server1 salt]# cat lib.sls 
{%set port = 8000 %}
[root@server1 salt]# vim apache/files/httpd.conf 

在最顶写上:
在这里插入图片描述

root@server1 salt]# cat apache/init.sls 
...
/etc/httpd/conf/httpd.conf:
  file.managed:
    - source: salt://apache/files/httpd.conf
    - template: jinja
    - context:
      bind: {{ grains['ipv4'][-1] }}
      port: {{ pillar['port'] }}			# sls 文件也有port变量

要注意这两个port 是不同的,下面这个是pillar指定的,而上面时变量文件指定的

[root@server1 salt]# cat lib.sls 
{%set port = 8000 %}		# 变量文件指定8000端口
   
[root@server1 salt]# cat ../pillar/packages.sls 
{% if grains['fqdn'] == 'server3' %}
package: nginx
{% elif grains['fqdn'] == 'server2' %}
package: httpd
port: 8080			# pillar指定8080多口
{% endif %}
[root@server1 salt]# pwd
/srv/salt

那幺到底谁生效哪?这就是优先级的问题,因为lib.sls文件是最后导进去的,所以这个会生效
推送测试:

[root@server1 salt]# salt server2 state.sls apache

在这里插入图片描述
是8000端口吧!
而且这时文件在传送过去的时候删除了第一行,因为我们的apache不识别这种格式:

[root@server2 salt]# vim /etc/httpd/conf/httpd.conf

saltstack实战 - 自动化运维管理_第10张图片
但是我们在/var/cache 中查看:

[root@server2 salt]# vim /var/cache/salt/minion/files/base/apache/files/httpd.conf 

saltstack实战 - 自动化运维管理_第11张图片
这里是有的。这个是从master中缓存过来的。

用JInja 模板做高可用

我们让server2做master,server3做backup。

[root@server1 salt]# mkdir keepalived
[root@server1 salt]# mkdir files
[root@server1 salt]# cd keepalived/files/
[root@server1 files]# scp server2:/etc/keepalived/keepalived.conf .			#拷贝一份配置文件,可以先装上keepalived复制一份配置文件在删除。
[root@server1 files]# vim keepalived.conf 
global_defs {
   notification_email {
	root@localhost
   }
   notification_email_from keepalived@localhost
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id LVS_DEVEL
   vrrp_skip_check_adv_addr
#   vrrp_strict		# 注释掉这个
   vrrp_garp_interval 0
   vrrp_gna_interval 0
}

vrrp_instance VI_1 {
    state {{ STATE }}			# 我们改为引用变量
    interface eth0
    virtual_router_id 51		
    priority {{ PRI}}		# 变量
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
	172.25.254.100			#
    }
}

然后用pillar指定变量:
[root@server1 keepalived]# vim /srv/pillar/packages.sls 
{% if grains['fqdn'] == 'server3' %}
package: nginx
state: BACKUP			# server3 成为备用结点
pri: 50
{% elif grains['fqdn'] == 'server2' %}
package: httpd
port: 8080
state: MASTER			# serer2做主节点
pri: 100
{% endif %}


[root@server1 files]# cd ..
[root@server1 keepalived]# vim init.sls 		# 建立引导文件
[root@server1 keepalived]# cat init.sls 
kp-install:			# 安装(可以先注释掉下面,让先安装,复制一个配置文件过来,后面接上面的scp那条命令)
  pkg.installed:
    - pkgs:
      - keepalived

  file.managed:			# 配置文件(上面获取到的)
    - name: /etc/keepalived/keepalived.conf
    - source: salt://keepalived/files/keepalived.conf
    - template: jinja
    - context:
      STATE: {{ pillar['state'] }}			# 调用变量
      PRI: {{  pillar['pri'] }}

  service.running:		# 启动服务
    - name: keepalived
    - enable: True
    - reload: True
    - watch:
      - file: kp-install

测试推送:
[root@server1 keepalived]# salt '*' state.sls keepalived

# server2中查看
[root@server2 ~]# cat /etc/keepalived/keepalived.conf 
。。。
vrrp_instance VI_1 {
    state MASTER		# master结点
    interface eth0
    virtual_router_id 51
    priority 100		# 优先级100
[root@server2 ~]# ip a


2: ens3:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000

    inet 172.25.254.100/32 scope global ens3		# 虚拟IP

# server3中查看;
[root@server3 ~]# cat /etc/keepalived/keepalived.conf 
...
vrrp_instance VI_1 {
    state BACKUP		# 备用结点	
    interface ens3
    virtual_router_id 51
    priority 50			# 优先级50

现在我们去编写top文件,去高级推送:

[root@server1 salt]# vim top.sls 
[root@server1 salt]# cd /srv/salt/
[root@server1 salt]# vim top.sls 
base:
  'server2':		# server2开启apache和高可用
    - apache
    - keepalived
  'server3':
    - nginx		# 开启nginx和高可用
    - keepalived
然后去掉apache目录的的init.sls和配置文件的监听地址,只留下监听端口,这样才能通过访问VIP访问。

在这里插入图片描述
在这里插入图片描述
这里都去掉了bind哪一行,只剩下了port。
然后高级推送:

[root@server1 salt]# salt '*' state.highstate
[root@server1 salt]# curl 172.25.254.100



	
		Test Page for the Apache HTTP Server on Red Hat Enterprise Linux