0%

SaltStack学习笔记-概念及入门

saltstack学习笔记

1 入门

1.1 saltstack架构

SaltStack是一种新型的基础设施管理软件,简单易部署,可伸缩的足以管理成千上万的服务器,和足够快的速度控制,与他们交流,以毫秒为单位。SaltStack提供了一个动态基础设施通信总线用于编排,远程执行、配置管理等等。SaltStack项目于2011年启动,年增长速度较快,五年期固定基础设施编制和配置管理的开源项目。SaltStack社区致力于保持slat项目集中、友好、健康、开放。

简单来说它的两大基础功能就是:配置管理、远程命令执行。剩下就是根据你的需求自由组合,实现更复杂的功能和系统管理。
(待补充)

1.2 安装saltstack

salt官方推荐使用salt bootstrap进行安装,它会根据运行的系统不同自动选择安装方式,解决依赖。

由于salt源在国外且速度较慢,我们使用切换到国内阿里源的方式安装:

配置方法

Saltstack 的官方文档对其支持的每种操作系统上 salt 的安装已经提供了具体的说明,在您可以访问本站的镜像页面:

https://mirrors.aliyun.com/saltstack/
来获取这些方法,另外,为了能使用本镜像站快速的安装软件包,您需要做2点修改:

a.脚本中的域名修改

把对应OS初始化脚本中的域名

repo.saltstack.com
替换成在阿里镜像站对应的路径

mirrors.aliyun.com/saltstack
比如对于 Centos 7 系统,在 saltstack 的官网提供的配置初始化手册是:

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

这时,你需要执行:

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

来安装这个初始化软件包。

b.配置中的域名修改

仍以 Centos 7 为例,初始化rpm包生成的配置文件为:

/etc/yum.repos.d/salt-latest.repo

文件中的访问地址需要替换成镜像站的路径,执行命令:

sudo sed -i "s/repo.saltstack.com/mirrors.aliyun.com\/saltstack/g" /etc/yum.repos.d/salt-latest.repo

1.3 配置salt

安装完成后可以开始配置salt,Salt Master启动后会默认监听两个端口:

4505(publish_port)为Salt Master pub接口,提供远程执行命令发送功能;

4506(ret_port)为Salt Master ret端口,支持认证,文件服务,结果收集等功能。

要确保防火墙对这两个端口开放。

Salt Master的配置文件在/etc/salt/master

Salt Minion的配置文件在/etc/salt/minion

1.3.1 Salt minion配置

minion和master之间的通信最好使用域名进行连接,实验中可修改对应的/etc/hosts文件,加入如下配置:

192.168.58.128 master
192.168.58.129 minion-one

用vi打开minion主机的/etc/salt/minion,修改如下:
首先找出配置选项的master命令行,去掉注释并改为

master:master

然后找到ID行,去掉注释并改为:

id: minion-one # 这个名字不一定需要和主机名一样,为了方便可以设置成一样

如果未找到id行可以自己添加

注意:如果未手动指定minion ID,minion将在启动时尝试智能化判断其minion ID,对于大多数系统,minion ID将设置为全局域名(FQDN)。

之后重启salt master和minion对应的进程生效。

1.3.2 在master上接受minion密钥

minion在启动之后会连接master,请求master为其签发证书,签发完成后代表master可以信任该minion。

salt-key命令可以帮助我们管理minion的密钥,在master上执行:

[root@localhost scripts]# salt-key -L
Accepted Keys:
Denied Keys:
Unaccepted Keys:
minion-one
minion-two
Rejected Keys:

这是可以发现minion-one主机已经出现在了unaccepted keys中,这表示该minion已经和master联系,并且master已经获取了minion的公钥,正在等待接受该minion的指令。

这时我们可以通过查看密钥指纹来确保其与minion的密钥想匹配,在master上查询的命令:

[root@localhost scripts]# salt-key -f minion-one
Unaccepted Keys:
minion-one: 30:96:64:30:3a:a0:77:9f:2e:22:a1:5c:15:aa:9b:ad:36:35:06:73:9e:5f:ce:bd:ab:d2:26:6e:5d:0b:cc:02

同样地,在minion上可以通过salt-call命令来获取:

[root@localhost scripts]# salt-call --local key.finger
local:
30:96:64:30:3a:a0:77:9f:2e:22:a1:5c:15:aa:9b:ad:36:35:06:73:9e:5f:ce:bd:ab:d2:26:6e:5d:0b:cc:02

可以看到两者相互匹配,所以我们可以在master上接受该密钥,使用salt-key -a参数:

[root@localhost scripts]# salt-key -a minion-one
The following keys are going to be accepted:
Unaccepted Keys:
minion-one
Proceed? [n/Y] Y
Key for minion minion-one accepted.

此时再次查看:

[root@localhost scripts]# salt-key -L
Accepted Keys:
minion-one
Denied Keys:
Unaccepted Keys:
minion-two
Rejected Keys:

可以看到minion密钥已经被接受。

注: 对于有很多minion的情况,可以在/etc/salt/master配置文件中查找到如下行:#auto_accept: True,去掉注释,可以让master以后自动签发密钥;也可以使用salt-key -A命令统一接受密钥。

1.3.3 运行第一条salt命令

尝试在master端运行并返回结果:

[root@localhost scripts]# salt '*' test.ping
minion-one:
True

这是一个简单的探测主机是否存活的命令,在该示例中我们发送一条消息给若干minion,并告诉它们运行salt内置的一个模块中的一条命令,这个命令能很好地查询我们有哪些minion是存活的。

实际上,test模块还包含有许多其他有用的函数,我们可以使用sys.list_functions模块功能返回这些函数:

[root@localhost scripts]# salt 'minion-one' sys.list_functions test
minion-one:
- test.arg
- test.arg_clean
- test.arg_repr
- test.arg_type
- test.assertion
.......以下省略

我们来测试一下列表中的其他函数,比如test.echo,想了解函数的用法可以执行:

[root@localhost scripts]# salt minion-one sys.doc test.echo 
test.echo:

Return a string - used for testing the connection

CLI Example:

salt '*' test.echo 'foo bar baz quo qux'

下一章我们将进一步学习远程执行命令。

2 通过salt远程执行管理minion

2.1 远程命令的组成结构

想了解salt命令的最简单方法就是用--help选项:

[root@localhost scripts]# salt --help
Usage: salt [options] '<target>' <function> [arguments]

...... 以下省略

可以看到,salt远程执行命令由五部分组成:

  • 第一部分–salt命令本身
  • 第二部分–命令行选项
  • 第三部分–目标定位字符串
  • 第四部分–salt模块函数
  • 第五部分–远程执行函数的参数

下面是一个包含所有五个部分的例子:

[root@localhost scripts]# salt --summary '*' cmd.run "uptime"
minion-one:
16:24:02 up 1:52, 2 users, load average: 0.00, 0.01, 0.05
minion-two:
16:24:02 up 1:49, 1 user, load average: 0.24, 0.09, 0.10


-------------------------------------------
Summary
-------------------------------------------
# of minions targeted: 2
# of minions returned: 2
# of minions that did not return: 0
# of minions with errors: 0
-------------------------------------------

接下来我们详细了解这五个部分。

2.1.1 命令行选项

salt的命令行选项的作用就是通过不同的选项来改变程序本身的行为。
下面介绍几个主要的命令行参数:

  • -v / –verbose 开启命令的详细描述–详细说明命令运行后会发生什么
  • –summary 显示一条salt命令的概要
  • –out 控制salt执行后输出结果的格式:
    a. –out=json 使用json格式输出执行结果; b.–out=yaml 使用yaml格式输出执行结果

还有很多选项可以改变salt命令的行为,可以通过salt --help命令来查看选项的用法。

2.1.2 目标定位字符串

目标定位字符串用于在有大量minion服务器的情况下,灵活地定位到所需的服务器并执行远程命令。

全局匹配

到现在的所有命令,我们的定位都采用了“*”进行匹配,salt匹配字符串的写法和shell基本相同

  • *代表0个或任意个字符
  • ? 代表一个字符
  • [] 代表字符的集合,如[a~z]代表所有小写字母,[0-9]代表数字

正则表达式匹配

如果我们需要更复杂的匹配,可以使用正则表达式。salt使用python的re正则模块,可以解析正则表达式(PCRE),正则表达式匹配需要配合命令行选项-E或者-pcre,具体正则规则不是本文的重点,有需要者请自行查询相关资料。

列表匹配

有时候我们需要指定一个列表里面的机器进行操作,这时候可以使用-L 参数进行列表匹配:

[root@localhost scripts]# salt -L "minion-one,minion-two" test.ping
minion-one:
True
minion-two:
True

通常列表匹配的主机都写在master的配置文件中,在/etc/salt/master中以nodegroups形式出现,包括正则匹配全局匹配等方式都可以写在配置文件中,然后通过分组匹配选项-N加上nodegroup名称进行匹配,后面会详细介绍。

Grain和Pillar匹配

Grain和Pillar是salt独有的两个概念,它们都是以key value形式存储的数据库,Grains是由minion返回给master的数据,而Pillar数据则是存储在master上的。每个minion只能看到自己的Pillar,Grain可以看做是Host的元数据(metadata), 例如CPU的数量;而Pillars则是主机所需的数据,例如数据库密码。一个minion可以告诉master它的Grain数据,而minion则需要从master索要Pillar数据。后面会详细讲解。

Grains匹配

Grains可以认为是描述minion本身固有属性的静态数据,列入minion服务器的操作系统、内存的大小、网卡的mac地址等,可以用如下命令列出主机所有的Grains数据。

[root@localhost scripts]# salt "minion-one" grains.items
minion-one:
----------
SSDs:
biosreleasedate:
04/13/2018
biosversion:
6.00
cpu_flags:
- fpu
- vme
- de
......以下省略

检索某一Grains数据使用如下命令:

[root@localhost scripts]# salt 'minion-one' grains.item os
minion-one:
----------
os:
CentOS

了解了操作系统信息后,我们可以利用Grains定位主机–利用-G--grain:,如对使用CentOS的机器进行定位:

[root@localhost scripts]# salt -G 'os:CentOs' test.ping
minion-one:
True
minion-two:
True

定位系统版本是7.6.*的主机:

[root@localhost scripts]# salt -G 'osrelease:7.6.*' test.ping
minion-one:
True
minion-two:
True

定位ens33网卡的特定的MAC地址的主机:

[root@localhost scripts]# salt -G 'hwaddr_interfaces:ens33:00:0c:29:0f:c1:a3' test.ping
minion-one:
True

Grains的匹配强大之处不止于此,除了自带的Grains之外,我们还可以自定义Grains来满足不同的需求。后面会详细讨论,这里仅仅给出一个简单的例子:

[root@localhost scripts]# salt -G 'hwaddr_interfaces:ens33:00:0c:29:0f:c1:a3' test.ping
minion-one:
True
[root@localhost scripts]#
[root@localhost scripts]#
[root@localhost scripts]# salt 'minion-one' grains.setval install_date 20190311
minion-one:
----------
install_date:
20190311
[root@localhost scripts]# salt 'minion-one' grains.item install_date
minion-one:
----------
install_date:
20190311

Pillars匹配

Pillars数据同Grains相似,不同之处是Pillars数据可以定义为更加动态的形式,并且是一个安全的数据存储库。它也是一个key-value形式存储的数据库,也可以和Grains一样进行匹配。不同的是需要使用-I--pillar选项:

  • 列出主机所有的Pillar数据: #salt "minion-one" pillar.items
  • 查看单个Pillar数据:#salt "minion-one" pillar.item role
  • 匹配role值是Web的主机并执行远程命令:salt -I "role:Web" test.ping

复合匹配

复合匹配就是将前面几种匹配方式混合使用,通过复合匹配我们可以定义更精细和复杂的minion匹配方式。例如:

# salt -C 'minion-* and G@os:CentOs not E@.*-two$' test.ping

这段匹配的含义是匹配所有‘minion-’开头的并且操作系统是CentOS且不能以-two结尾的机器。

可以使用的匹配方法如下表所示:

字母 匹配类型 示例
G Grains G@os:Ubuntu
E 正则 E@web\d+(dev
P Grains正则 P@os:(RedHot
L 列表 L@minion1.example.com
I Pillar I@pdata:foobar
S Subnet/IP address S@192.168.0.1/24 or S@192.168.1.100
R Range cluster R@%foo.bar

2.2 远程执行模块和函数

远程执行的最后一部分是我们需要运行的模块以及相关函数和对应的执行参数。模块是函数的逻辑分组,一系列的函数组合在一起构成一个模块。

所有远程执行命令的格式都是.格式,使用sys.list_modules函数可以列举出对应minion主机上的所有模块;使用sys模块的list_functions函数可以列举出模块内可以应用的函数。

下面列举一些常用的模块和函数:

1. 远程命令执行模块

cmd模块可以在多台主机上同时执行一条相同命令

[root@saltmaster ~]# salt '*' cmd.run "ps aux| wc -l"
minion-one:
103
minion-two:
118
[root@saltmaster ~]# salt '*' cmd.run_all "ps aux| wc -l" # 显示更详细参数
minion-two:
----------
pid:
9579
retcode:
0
stderr:
stdout:
118
minion-one:
----------
pid:
8023
retcode:
0
stderr:
stdout:
103

2. 安装包管理

pkg模块用于管理程序包,我们使用pkg.install模块来安装程序包

[root@saltmaster ~]# salt 'minion-one' pkg.install 'httpd'
minion-one:
----------
httpd:
----------
new:
2.4.6-88.el7.centos
old:
httpd-tools:
----------
new:
2.4.6-88.el7.centos
old:
mailcap:
----------
new:
2.1.41-2.el7
old:

3. 管理服务模块

salt通过service管理服务模块管理服务,该模块包括了service.start,service.status,service.stop等,如果service.status远程执行模块输出True结果,则表示该服务正在运行,如果输出False则表示该服务停止。

[root@saltmaster ~]# salt 'minion-one' service.status httpd
minion-one:
False
[root@saltmaster ~]# salt 'minion-one' service.start httpd
minion-one:
True
[root@saltmaster ~]# salt 'minion-one' service.status httpd
minion-one:
True
[root@saltmaster ~]# salt 'minion-one' service.stop httpd
minion-one:
True
[root@saltmaster ~]# salt 'minion-one' service.status httpd
minion-one:
False

4. 文件管理模块

对于文件管理,salt提供了file模块,file.status可以获取文件属性,
file.chown可以修改文件属主

[root@saltmaster ~]# salt 'minion-one' file.stats /etc/yum.conf
minion-one:
----------
atime:
1552284497.869986
ctime:
1552283157.8879962
gid:
0
group:
root
inode:
16988181
mode:
0644
mtime:
1541382837.0
size:
970
target:
/etc/yum.conf
type:
file
uid:
0
user:
root

5. 用户管理模块

user模块可以用来管理用户,添加用户的salt命令格式如下:

salt '*' user.add name <uid> <gid> <groups> <home> <shell>

可以不指定其他信息,只做最简单的用户添加:

salt '*' user.add 'mysql'

删除用户:

salt 'minion-one' user.delete mysql

查看用户信息:

salt 'minion-one' user.info root

3 编写自己的模块代码

3.1 理解salt底层执行的原理

salt底层通信是通过ZeroMQ完成的,采用了ZeroMQ的订阅发布模式(Pub和Sub)
image

简单来说Pub/Sub模式类似于广播电台,在订阅发布模式中Pub将消息发送到总线,所有的Sub收到来自总线的消息后,根据自己的订阅条件来接收特定的消息。对应到Salt中就是master将事件发布到消息总线后,minion订阅并监听事件,然后minion会查看事件是否和自己匹配以确定是否需要执行。

Salt minion启动时从配置能文件中获取master的地址,如果为域名则进行解析,解析完成后会连接master的4506(ret接口)进行key认证。认证通过会获取到master的publish_port(默认4505),然后连接publish_port订阅来自master pub接口的任务。当master下发操作指令时,所有的minion都能接收都,然后minion会检查本机是否匹配,如果匹配则执行。执行完毕后把结果发送到master的4506由master进行处理。

命令的发送完全是异步的,并且命令包很小,此外这些命令包通过maqpack进行序列化后数据会进一步压缩,所以salt的网络负载会非常低。

3.2 执行模块的构成结构和编写自己的模块

(暂时忽略)

4 通过state模块定义主机状态

4.1 状态的概念以及如何撰写第一条状态

对于minion的环境控制,使用状态进行管理更为合适。状态是对minion的一种描述和定义,管理人员可以不关心具体部署的任务是如何完成的,只需要描述minion要到达什么状态,底层由salt的状态模块来完成功能。

之前我们使用pkg模块的install函数安装了httpd,这个函数的功能相当于在每台minion上执行yum install httpd命令,重复执行上面的命令相当于在所有minion上重新执行yum install httpd命令。

下面我们使用state模块完成部署:

  • 建立目录

    [root@saltmaster srv]# mkdir -p /srv/salt
    [root@saltmaster srv]# cd /srv/salt
  • 建立apache.sls文件

install_httpd:
pkg.installed:
- name: httpd
  • 使用state模块执行apache.sls
[root@saltmaster salt]# salt 'minion-one' state.sls apache
minion-one:
----------
ID: install_httpd
Function: pkg.installed
Name: httpd
Result: True
Comment: The following packages were installed/updated: httpd
Started: 11:44:28.533490
Duration: 8349.668 ms
Changes:
----------
httpd:
----------
new:
2.4.6-88.el7.centos
old:

Summary for minion-one
------------
Succeeded: 1 (changed=1)
Failed: 0
------------
Total states run: 1
Total run time: 8.350 s

可以看到用state模块部署Apache软件时我们用了一个描述性的配置文件,命令行调用了state模块的SLS函数,从返回的结果上看也是成功部署了httpd。

为了看出区别我们再执行一次:

[root@saltmaster salt]# salt 'minion-one' state.sls apache
minion-one:
----------
ID: install_httpd
Function: pkg.installed
Name: httpd
Result: True
Comment: All specified packages are already installed
Started: 11:49:56.301971
Duration: 1233.84 ms
Changes:

Summary for minion-one
------------
Succeeded: 1
Failed: 0
------------
Total states run: 1
Total run time: 1.234 s

从返回的结果来看httpd软件已经安装过了,多次运行也会得到和上面相同的结果。这就表明了执行模块和状态模块之间的主要区别–执行模块是过程式的,状态模块是描述性的。这意味着当你连续几次调用同一个执行模块时将执行相同的逻辑和指令,而状态模块则相反,当我们执行时state模块会首先做判断,判断httpd是否已经安装了,如果没有则进行安装,如果有则什么都不做,这种性质叫做幂等性,只有当真实状态和所需状态不同的情况下才执行功能

4.2 状态配置文件的各个要素

下面我们来看一下状态文件的构成,以刚才的Apache.sls文件为例:

install_httpd:
pkg.installed:
- name: httpd

sls配置文件使用YAML语言描述,默认的sls文件的renderer是YAML renderer,YAML renderer的工作是将YAML数据格式的结构编译成为Python的数据结构给salt使用。

主要记住三个规则就可以使用YAML语法写SLS文件了:

  1. 规则一: 缩进
    YAML使用了一个固定的缩进风格来标书数据层结构关系,salt需要每个缩进级别由两个空格组成,不要使用tabs。

  2. 规则二: 冒号
    Python字典的keys在YAML中的表现形式是一个以冒号结尾的字符串。values的表现形式是冒号下面的每一行用一个空格隔开:

    my_key: my_value

    另一种选择,一个value可以通过缩进与key连接

    mykey:
    my_value
  3. 规则三:短横杠
    用一个短横杠加一个空格来表示列表项,多个项使用同样的缩进级别来作为同一列表的一部分。

    my_dictionary:
    - list_value_one
    - list_value_two
    - list_value_three

    在Python中将被映射为:
    {"my_dictionary": ["list_value_one","list_value_two", “list_value_three”]}

下面详细说一下配置文件的格式:

<ID Declaration>:
<State Module>.<Function>:
- name: <name>
- <Function Arg>
- <Function Arg>
- <Function Arg>
- <Requisite Declaration>
- <Requisite Reference>

每个状态的字符串必须独一无二。可以包含字母、数字、空格和下划线。ID尽量保持简洁明了。.即上文提到的远程执行命令采用的相同格式。

最后是函数参数,收个参数通常是name,然后是状态所需的其他参数。这里不同点是所有使用的模块都是状态模块而不是普通的远程执行模块。

更多请参见官方文档

4.3 常用的状态模块用法

file模块

file模块包含许多函数,比较常用的有:

file.managed下发文件,确保文件存在

/etc/foo.conf:
file.manager:
- source:
- salt://foo.conf
- user: qtj
- group: users
- mode: 644

file.directory建立目录:

/srv/stuff/substuff:
file.directory:
- user: qtj
- group: users
- mode: 755
- makedirs: True

file.symlink建立软连接:

/etc/grub.conf:
file.symlink:
- target: /boot/grub/grub.conf

file.recurse下发整个目录:

/opt/code/flask:
file.recurse:
- source: salt://code/flask
- include_empty: True

pkg模块

pkg.installed软件安装:

mypkgs:
pkg.installed:
- pkgs:
- foo
- bar: 1.2.3-4
- baz

后面可以指定软件rpm来源,如果什么都不带则默认安装最新版本

service模块

启动redis服务:

redis:
service.running:
- enable: True
- reload: True
- watch:
- pkg: redis

cron模块

每五分钟执行一次:

date > /tmp/crontest:
cron.present:
- user: root
- minute"*/5"

user模块

user.present:

user.present:
- fullname: lym
- shell: /bin/bash
- home: /home/lym
- uid: 5200
- gid: 5200
- groups:
- users

sysctl模块

调整内核参数:

vm.swappiness:
sysctl.present:
- value: 20

pip模块

django:
pip.installed:
- name: django >=1.11.6, <= 1.11.12
- require:
- pkg: python-pip

4.4 使用requisites对状态进行排序控制

如果一个主机涉及多个状态,并且状态之间相互关联,需要在执行顺序上有先后之分,那就必须引入requisites来进行控制。

下面以安装Apache为例说明:

[root@saltmaster salt]# cat apache.sls 
install_httpd:
pkg.installed:
- name: httpd
httpd_running:
service.running:
- name: httpd
- enable: True
- require:
- pkg: install_httpd

这段示例中定义了两个状态,分别是安装httpd和启动httpd,很明显必须先安装完成再启动httpd。这里我们通过require指定httpd running操作必须在install_httpd安装完成以后才可以执行。

进一步地

install_httpd:
pkg.installed:
- name: httpd
httpd_running:
service.running:
- name: httpd
- enable: True
- require:
- pkg: install_httpd
- watch:
- file: httdp_conf
httdp_conf:
file.managed:
- name: /etc/httpd/conf/httdp.conf
- source: salt://httdp.conf
- user: root
- group: root
- mode: 600

加入了一个httpd.conf文件下发的配置,首先安装httpd,然后确定httpd进程启动,最后对比需要下发的httpd.conf文件是否和minion上相同,如果不同就下发,之后通过watch指定触发重新加载httpd进程生效。

5 通过Jinja2 模板以及Grain和Pillar扩展主机状态

5.1 Jinja2 模板语言的基础

Jinja2变量

Jinja2模板包含变量和表达式:变量用{{}}包围,表达式用{% %}包围。
从一个例子开始:

[root@saltmaster salt]# cat var.sls 
{% set var='Hello World!' %}
test_var:
cmd.run:
- name: echo "var is {{ var }}"

运行并查看Jinja2变量的输出:

[root@saltmaster salt]# salt "minion-one" state.sls var
minion-one:
----------
ID: test_var
Function: cmd.run
Name: echo "var is Hello World!"
Result: True
Comment: Command "echo "var is Hello World!"" run
Started: 18:08:14.434038
Duration: 16.576 ms
Changes:
----------
pid:
17044
retcode:
0
stderr:
stdout:
var is Hello World!

Summary for minion-one
------------
Succeeded: 1 (changed=1)
Failed: 0
------------
Total states run: 1
Total run time: 16.576 ms

流程控制语句

  • For

遍历序列中的每一项:

{% for user in users %}
{{user}}
{% endfor %}

变量是一个字典:

{% for key, value in my_dict.items() %}
{{ key }}
{{ value }}
{% endfor %}

与Python不同是是模板循环内不能有break或continue语句。

  • if
    {% if users %}
    {% for user in users %}
    {{ user.username }}
    {% endfor %}
    {% endif %}

5.2 Grain和Pillar的概念及设置

Grain的概念

Grains是存储在minion上的一种静态数据,minion启动后就进行Grains的计算,包括操作系统类型、版本、CPU核数、内存大小等等,这些数据不经常变化。Grain让salt变得更加灵活。

Grain相关的基本命令

列出所有minion上的Grains项

[root@saltmaster ~]# salt "minion-one" grains.ls
minion-one:
- SSDs
- biosreleasedate
- biosversion
- cpu_flags
- cpu_model
- cpuarch
- disks
- dns
- domain
- fqdn
- fqdn_ip4
......以下省略

查询minion上某一具体的Grain值:

[root@saltmaster ~]# salt "minion-one" grains.item cpu_model
minion-one:
----------
cpu_model:
Intel(R) Core(TM) i5-4210U CPU @ 1.70GHz

列出对应minion上所有Grain的详细信息:

[root@saltmaster ~]# salt "minion-one" grains.items
minion-one:
----------
SSDs:
biosreleasedate:
04/13/2018
biosversion:
6.00
cpu_flags:
- fpu
- vme
- de
......以下省略

设置Grains数据

salt为我们提供了很多默认的Grains数据,但有一些场景下需要用户自定义Grains的数据,下面讲解常用的设置方法:

  • 命令行方式
    单个值设置:
    [root@saltmaster ~]# salt "minion-one" grains.setval my_grain bar
    minion-one:
    ----------
    my_grain:
    bar

多个值设置:

[root@saltmaster ~]#  salt '*' grains.setval key "{'key1': 'val1', 'key2': 'val2'}"
minion-one:
----------
key:
----------
key1:
val1
key2:
val2
minion-two:
----------
key:
----------
key1:
val1
key2:
val2

列表结构设置:

[root@saltmaster ~]#  salt '*' grains.setval my_grain_dict '["one", "two", "three"]'
minion-one:
----------
my_grain_dict:
- one
- two
- three
minion-two:
----------
my_grain_dict:
- one
- two
- three

设置成功后通过下面的命令查询:

[root@saltmaster ~]# salt '*' grains.item my_grain_dict
minion-one:
----------
my_grain_dict:
- one
- two
- three
minion-two:
----------
my_grain_dict:
- one
- two
- three

以上变量已经写入了minion的/etc/salt/grains文件中,通过查看对应文件可以获得

  • grains_module的方式设置

a. 在master上建立模块对应的目录:

#mkdir -pv /src/salt/_grains

b. 写入一个模块:

vim my_grain_mod.py
import time

def now():
grains = {}
grains['now'] = time.time()
return grains

c. 同步模块到minion

[root@saltmaster _grains]# salt "minion-one" saltutil.sync_all
minion-one:
----------
beacons:
clouds:
engines:
grains:
- grains.my_grain_mod
log_handlers:
matchers:
modules:
output:
proxymodules:
renderers:
returners:
sdb:
serializers:
states:
thorium:
utils:

d. 重载一次模块

[root@saltmaster _grains]# salt "minion-one" sys.reload_modules
minion-one:
True

e. 查看新设置的Grains

[root@saltmaster _grains]# salt "minion-one" grains.item now
minion-one:
----------
now:
1552442351.143724
  • 在minion端设置
    通过修改minion的配置文件同样可以自定义Grains,登录到一台minion上:

    [root@salt-minion ~]# vim /etc/salt/minion.d/grain.conf
    grains:
    new_grain: bar
    new_grain_dict:
    - one
    - two
    - three

    然后重启salt-minion 加载配置文件,之后在master上可以查看的新变量:

    [root@saltmaster _grains]# salt "minion-one" grains.item new_grain
    minion-one:
    ----------
    new_grain:
    bar
    [root@saltmaster _grains]# salt "minion-one" grains.item new_grain_dict
    minion-one:
    ----------
    new_grain_dict:
    - one
    - two
    - three
  • 删除自定义Grains

    # salt "minion-one" grains.delval my_grain

Pillar的概念

Grains是静态数据,如果是动态数据则需要使用Pillar,Pillar数据存储在master上,指定的minion只能看到自己的Pillar数据,其他的minion看不到任何Pillar数据,这一点和状态文件正好相反–所有通过认证的minion都可以获取状态文件,但是每个minion却自己有一套Pillar数据,并且这些数据都是加密的。

Pillar相关的基本概念

列出对应minion上所有的Pillar的详细信息:

# salt "minion-one" pillar.items

查询minion上某一具体Grain的值:

# salt 'minion-one' pillar.item foo

设置Pillar数据

设计这样一个场景:现在有三台minion,每台需要从master获取自己需要的私钥,私钥在传输过程中必须加密,且私钥不能够让任何其他minion获取到。下面用三个不同的字符串来代表私钥:

首先建立目录,然后为每个minion编写对应的SLS文件:

# mkdir -pv /srv/pillar  # 目录是固定的,写在其他地方不识别!
# vim minion_one_key.sls
private_key: minion_one_key
# vim minion_two_key.sls
private_key: minion_twp_key

之后建立入口文件:

base:
'minion-one':
- minion_one_key
'minion-two':
- minion_two_key

top.sls文件中可以使用第二节讲解过的各种匹配模式,这里使用了通用匹配,只匹配对应的minion名字,设置完毕后执行下面命令刷新Pillar数据:

# salt '*' saltutil.refresh_pillar

然后查看所有Pillar数据:

[root@saltmaster pillar]# salt '*' pillar.items
minion-one:
----------
private_key:
minion_one_key
minion-two:
----------
private_key:
minion_two_key

可以看到不同的minion获取了自己私有的字符串,这就完成了Pillar下发敏感数据的过程,通信加密,且minion之间不可见。

5.3 用Jinjia2配合Grain和Pillar扩展SLS配置文件

扩展Apache.sls配置文件

之前我们学习了如何用Apache.sls状态文件部署Apache,sls文件内容如下:

[root@saltmaster salt]# cat apache.sls
install_httpd:
pkg.installed:
- name: httpd

这个文件仅仅适用于Redhat系的Linux系统,如果是Ubuntu系统,它对应的Apache安装包名叫做apache2,这个安装脚本就不适用了。

上面这种情况抽象出来,就是根据minion的不同属性,SLS状态文件需要有逻辑判断来执行不同的操作。而Grains是描述minion固有属性的数据,Jinjia2可以做流程和逻辑控制,那么两者结合起来就可以满足上面的需求。

修改后的sls状态文件如下:

install_httpd:
pkg.installed:
{% if grains['os_family'] == 'Debian' %}
- name: apache2
{% elif grains['os_family'] == 'RedHat' %}
- name: httpd
{% endif %}

多系统vim安装实例

根据不同操作系统安装下不同的vim包下发不同的配置文件:

[root@saltmaster salt]# cat vim.sls
vim:
pkg:
- installed
{% if grains['os_family'] == 'RedHat' %}
- name: vim-enhanced
{% elif grains['os_family'] == 'Debian' %}
- name: vim-nox
{% endif %}

{% if grains['os'] == 'Arch' %}
file:
- managed
- source: salt://vim/vimrc
- user: root
- group: root
- mode: 644
- template: jinja
- makedirs: True
- require:
- pkg: vim
{% endif %}

iptables根据系统不同下发

iptables:
pkg:
- installed
service:
- running
- watch:
- pkg: iptables
- file: iptables
file:
- managed
- source: salt://iptables/iptables
{% if grains['os'] == 'CentOS' or grains['os'] == 'Fedora' %}
- name: /etc/sysconfig/iptables
{% elif grains['os'] == 'Arch' %}
- name: /etc/conf.d/iptables
{% endif %}

通过Pillar扩展SLS配置

Jinjia2模板语言和Pillar的结合在使用上和Grains没有任何区别。下面举一个例子,有三台minion分别设置了它们的Pillar的user值如下:

[root@saltmaster pillar]# salt '*' pillar.items
minion-one:
----------
user:
- mysql1
- mysql2
- mysql3
minion-two:
----------
user:
- web1
- web2
- web3

要在这两台机器上根据它们的Pillar值分别建立相关的用户,sls脚本代码如下:

[root@saltmaster salt]# cat adduser.sls 
{% for i in pillar['user'] %}
add_{{ i }}:
user.present:
- name: {{ i }}
{% endfor %}

运行结果如下:

[root@saltmaster salt]# salt '*' state.sls adduser
minion-one:
----------
ID: add_mysql1
Function: user.present
Name: mysql1
Result: True
Comment: New user mysql1 created
Started: 13:45:04.686551
Duration: 92.536 ms
Changes:
----------
fullname:
gid:
1001
groups:
- mysql1
home:
/home/mysql1
homephone:
name:
mysql1
other:
passwd:
x
roomnumber:
shell:
/bin/bash
uid:
1001
workphone:
......中间部分省略

Summary for minion-one
------------
Succeeded: 3 (changed=3)
Failed: 0
------------
Total states run: 3
Total run time: 164.397 ms
......以下省略

5.4 用Jianjia2配合Grains和Pilliar动态下发配置文件

第四节我们学习过file模块的一个状态函数managed,这个模块可以从master下发配置文件到匹配的minion上,这种下发方式使所有minion得到一份同样的配置文件。

但是现实情况是不同的minion有不同的CPU核数,不同的内存大小,很多软件需要根据这些配置进行不同的相应调整。Jinjia2配合Grain和Pillar可以很好地决绝此类问题,下面我们学校如何通过模板文件动态地生成配置文件。

一个简单模板下发实例

首先编辑状态文件:

[root@saltmaster salt]# cat template.sls 
template_test:
file.managed:
- source: salt://test.j2
- name: /tmp/test.conf
- user: root
- group: root
- mode: 644
- template: jinja

编辑模板文件:

[root@saltmaster salt]# cat test.j2 
cpu_num = {{ grains['num_cpus'] }}
mem_total = {{ grains['mem_total'] }}
hostname = {{ grains['host'] }}
user = {{ pillar['user'][0] }}

测试模板文件下发:

[root@saltmaster salt]# salt "minion-one" state.sls template
minion-one:
----------
ID: template_test
Function: file.managed
Name: /tmp/test.conf
Result: True
Comment: File /tmp/test.conf updated
Started: 14:02:28.037462
Duration: 122.729 ms
Changes:
----------
diff:
New file
mode:
0644

Summary for minion-one
------------
Succeeded: 1 (changed=1)
Failed: 0
------------
Total states run: 1
Total run time: 122.729 ms

登录到minion-one查看下发的配置文件:

[root@salt-minion ~]# cat /tmp/test.conf
cpu_num = 1
mem_total = 972
hostname = salt-minion
user = mysql1

进一步地,如果在这个例子中加入部分Jinja2的逻辑控制功能:

[root@saltmaster salt]# vim test.j2 

{% if grains['num_cpus'] >= 8 %}
cpu_num = 8
{% else %}
cpu_num = {{ grains['num_cpus'] }}
{% endif %}
{% if grains['mem_total'] <= 512 %}
mem_total <= 512
{% elif grains['mem_total'] <= 1024 %}
mem_total = {{ grains['mem_total'] }}
{% else %}
mem_total = 1024
{% endif %}
hostname = {{ grains['host'] }}
{% for i in pillar['user'] %}
{{ i }}
{% endfor %}

测试模板下发:

[root@saltmaster salt]# salt "minion-one" state.sls template
minion-one:
----------
ID: template_test
Function: file.managed
Name: /tmp/test.conf
Result: True
Comment: File /tmp/test.conf updated
Started: 14:34:54.882206
Duration: 89.375 ms
Changes:
----------
diff:
---
+++
@@ -1,4 +1,8 @@

+cpu_num = 1
+
+
+mem_total = 972
+
+ mysql1
+
+ mysql2
+
+ mysql3
+

hostname = salt-minion


Summary for minion-one
------------
Succeeded: 1 (changed=1)
Failed: 0
------------
Total states run: 1
Total run time: 89.375 ms

登录到minion-one, 查看下发的配置文件:

[root@salt-minion ~]# cat /tmp/test.conf
cpu_num = 1
mem_total = 972
hostname = salt-minion
mysql1
mysql2
mysql3

配置文件下发完成。

5.5 Jinja与Grains和Pillar的补充

前面我们已经知道,如果有一个叫做user的Grain/Pillar属性,可以通过如下方法访问:

The user {{ grains['user'] }} is referred to here.
The user {{ pillar['user'] }} is referred to here.

但如果Pillar或者Grains没有设置user,模板将无法正确渲染,比较安全的方法是使用内置的salt交叉执行模块:

The user {{ salt['grains.get']('user', 'larry') }} is referred to here.
The user {{ salt['grains.get']('user', 'larry') }} is referred to here.

如果user没有设置,则会默认使用Larry这个值。

我们还可以通过搜索Grains和Pillar来让模板变得更加动态。使用config.get方法,salt会首先搜索minion配置文件中的值,如果没有找到则会检查Grain,如果还没有,则会检查Pillar,如果还没有,则会搜索Master配置,如果全部没有,则会使用默认提供的值:

The user {{ salt['config.get']('user', 'larry') }} is referred to here.

欢迎关注我的其它发布渠道