远程执行命令
该接口直接调用的 Salt 执行模块。 salt 远程执行命令需要三个内容:
目标 target
功能 function
参数 args
命令类似于
salt '' [arguments]
target
target 允许我们过滤出需要执行任务的 minions 进行操作。 默认过滤器是 minion id 上的 glob 通配符匹配。 例如:
salt '*' test.version
salt '*.sublimetext.com' test.version
我这里的 minion 主机 id 为 www.sublimetext.com,所以 *.sublimetext.com 同样可以匹配到主机。
目标可以基于使用 Grains 系统的 minion 系统信息进行过滤:
salt -G 'os:Ubuntu' test.version
因为我们当前的系统是 ubuntu,所以只有 -G 'os:Ubuntu' 可以匹配到主机。
-G 参数表示使用 Grains 系统,具体 Grains 我们以后专门讲到。
可以通过正则表达式过滤目标:
salt -E 'www.(\w*).com' test.version
-C 指定使用多个过滤接口方案,每个方案中首字母表示方案类型,@ 后接具体方案,然后用 and or 进行组合。实例中表示匹配 grain 系统为 ubuntu 并且 minion id 以 www 开头或者任何以 db 开头的 minion。
列表匹配
salt -L 'www.sublimetext.com,' test.version
-L 参数表示 minion 列表,用逗号分隔。
配置 node group 匹配 minion
在 /etc/salt/master 添加 node groups 配置
nodegroups:
group1: "G@os:Ubuntu"
group2: "[email protected]"
如上,我们定义了 group1、group2,并且指定了特定的 minions。
修改 master 配置文件后需要重启 salt-master
# 杀死 salt-master 进程
pkill salt-master
# 重新启动
salt-master -d -l info
salt -N 'group1' test.version
-N 参数表示使用 node group 配置 minion
Target 还有一些更复杂的写法,但越复杂的匹配规则,匹配出现遗漏或者多余的几率越多,在生产环境中出现这样的问题还是非常危险的,所以我并不是非常支持写复杂的规则的。最好的方案还是使用 grains 或者 pillar 做匹配。后续我们也会具体讲到。
执行多个 salt 任务
执行多个 salt 任务,可以发送函数和参数的列表,而不是发送单个函数和参数。 这些函数按照它们在命令行中定义的顺序在 minion 上依次执行,然后所有命令中的数据都在一个字典中返回。 这意味着以可预测的方式调用命令集,并且可以容易地解释返回的数据。
如通过传递以逗号分隔的函数列表,后跟逗号分隔的参数列表来执行复合命令:
语法 salt
salt '*' cmd.run,test.ping,test.echo 'cat /proc/cpuinfo|head -2',,foo
注意,如果函数没有传递任何参数,那么需要有一个占位符来替代缺少的参数。 这就是为什么在上面的例子中,有两个逗号紧挨着。 test.ping 不带参数,所以我们需要添加另一个逗号,否则 Salt 会尝试将 foo 传递给 test.ping。
不同模块直接 , 分割,不能有空格;参数之间 , 分割,与模块一一对应。
如果需要传递包含逗号的参数,请确保在分隔参数的逗号周围添加空格。 例如:
salt '*' cmd.run,test.ping,test.echo 'echo "1,2,3"' , , foo
如果参数之间有空格,需要用引号。
salt '*' cmd.exec_code python 'import sys; print sys.version'
开发执行模块
如果 salt 自带的模块无法满足环境需求,我们可以自己开发对应的模块。
Salt 执行模块是放置在 Salt 文件服务器根目录下名为 _modules/ 的目录中的 Python 或 Cython 模块。 使用默认的后端文件服务器(即 roots)时,除非在 file_roots 配置选项中另外定义了环境参数,否则 _modules/ 目录将位于大多数系统上的 /srv/salt/_modules中。
当调用以下任何 Salt 函数时,放置在 _modules/ 中的模块将会同步到 minions:
state.apply
saltutil.sync_modules
saltutil.sync_all
注意,模块的默认名称是其文件名(即 foo.py 成为模块 foo ),但可以使用 virtual 函数为其自定义名称。
如果 Salt 模块有错误且无法导入,则 Salt minion 将继续加载而不会出现问题,并且将简单地省略带有错误的模块。
如果添加一个 Cython 模块,该文件必须命名为
自定义执行模块
我们现在写一个最简单的执行模块,因为我们修改了 file_roots 路径,所以在我们的环境里自定义执行模块的路径为 /etc/salt/srv/salt/base/_modules/。
创建对应目录 mkdir /etc/salt/srv/salt/base/_modules/
注意,自定义模块的文件名要避免和已存在的模块重复,否则会覆盖原模块。
新建文件 /etc/salt/srv/salt/base/_modules/custom.py
def my_module():
return 'This is a custom module'
这个文件代码,我们没有做任何操作,只是返回了一个字符串。
注意,自定义的执行模块必须有 return 返回数据,用以传递给 master。
现在我们需要把模块同步给 minion。
salt '*' saltutil.sync_modules
现在执行我们的模块。
salt '*' custom.my_module
Zip 归档模块
如果我们自定义的模块非常复杂,我们可以用类似打包 python 模块包的方式,将整个模块的多个文件打包为 .zip 文件。
通过在 minion 配置中将 enable_zip_modules 设置为 True,Salt 加载器将能够以这种方式导入 .zip 文件。 这允许我们将依赖关系与其模块打包在一起,以便于部署、隔离等。
修改 /etc/salt/minion,添加如下内容:
enable_zip_modules: True
重启 salt-minion:
pkill salt-minion
salt-minion -d -l info
我们新建一个模块 lumberjack, 新建对应目录。
mkdir -p /etc/salt/tmp/lumberjack/{work,sleep}
创建模块方法,文件 /etc/salt/tmp/lumberjack/init.py。
from lumberjack import sleep, work
def is_ok(person):
''' 自定义模块的方法调用其他文件函数 '''
return sleep.all_night(person) and work.all_day(person)
创建模块依赖的方法。
文件 /etc/salt/tmp/lumberjack/sleep/init.py。
def all_night(msg):
return True
文件 /etc/salt/tmp/lumberjack/work/init.py。
def all_day(msg):
return True
生成 zip 包
cd /etc/salt/tmp
zip -r ../srv/salt/base/_modules/lumberjack lumberjack
推送新的模块到 minion
salt '*' saltutil.sync_modules
使用 zip 执行模块
salt '*' lumberjack.is_ok 'User Admin'
注意,模块目录中 init.py 文件是必须存在的,salt 执行时默认调用该文件,其他目录文件遵循 python 语法即可。
Salt 执行模块的交叉调用
所有 Salt 执行模块都可以互相调用,模块可以调用其他执行模块中可用的功能函数。
变量 salt 在加载到 Salt minion 后被打包到模块中。
salt 变量是一个包含所有 Salt 函数的 Python 字典。 字典键是表示模块名称的字符串,值是函数本身。
可以通过访问 salt dict 中的值来交叉调用 Salt 模块。
实例如下:
文件 /etc/salt/srv/salt/base/_modules/custom.py 添加以下内容
def cross(cmd):
return __salt__['cmd.run'](cmd)
在这个自定义模块中,我们添加了 cross 方法,调用了 salt 自己的 cmd.run 执行命令。
现在同步模块,并测试执行
salt '*' saltutil.sync_modules
salt '*' custom.cross 'date'
装饰器处理依赖
在编写执行模块时,很多时候某些模块将在所有主机上运行,但某些功能具有外部依赖性,例如需要安装的服务或需要在系统上存在的二进制文件。
可以使用装饰器,而不是尝试将大部分代码包装在大型 try/except 块中。
如果传递给装饰器的依赖项不存在,那么 salt minion 将从该主机上的模块中删除这些函数。
如果定义了 fallback_function,它将替换该函数而不是删除它。
同样在 /etc/salt/srv/salt/base/_modules/custom.py 添加以下内容
import logging
from salt.utils.decorators import depends
log = logging.getLogger(__name__)
try:
# 导入一个不存在的模块
import dependency_that_sometimes_exists
except ImportError as e:
log.trace('Failed to import dependency_that_sometimes_exists: {0}'.format(e))
# 定义一个用于处理异常的私有方法
def _fallback():
'''
Fallback function for the depends decorator to replace a function with
'''
return '"dependency_that_sometimes_exists" 这个是测试装饰器依赖的错误处理模块'
# depends 装饰器指定依赖方法以及异常处理方法
@depends('dependency_that_sometimes_exists', fallback_function=_fallback)
def foo():
'''
Function with a dependency on the "dependency_that_sometimes_exists" module.
If the "dependency_that_sometimes_exists" is missing this function will be
replaced with "_fallback"
'''
return True
以上代码中,我们先导入一个不存在的模块,定义 foo 自定义模块时使用 @depends 装饰器指定了依赖, fallback_function 作为不满足依赖关系时错误处理方法。
_fallback() 作为异常处理方法,前置下划线为私有方法,无法在外部程序调用。
现在同样同步模块并执行
salt '*' saltutil.sync_modules
salt '*' custom.foo