首页 > 代码库 > Saltstack中Pillar那点事

Saltstack中Pillar那点事

Salt中Pillar那点事


基本简介


在 SaltStack 中, Pillar作为定义minion全局数据的接口. 默认存储在master端, Minion启动时会连接master获取最新的pillar数据. Pillar使用类似于State Tree的结构, 默认使用 YAML 作为其描述格式, 在Minion内部最终转换成 Python字典 .


那么在Salt内部, Pillar是如何工作的? 在哪些情况下,需要先执行刷新Pillar操作? 而哪些又不需要?


本文基于 Salt 2014.1.4


配置文件中的Pillar


pillar_roots

存在于master/minion配置文件中. 指定Pillar roots对应环境的目录, 其布局类似于State Tree. 在minion配置文件中配置该选项, 只有当 file_client 为 local 时才生效.

state_top

存在于master/minion配置文件中, 默认值为top.sls. 官方描述为用于state system, 用于告诉minion使用哪个环境并且需要执行哪些模块. 其实该选项也应用在pillar system中, 作用和state system类似. 所以如果更改了本选项, pillar system对应的top.sls也需要变更. 在minion配置文件中配置该选项, 只有当 file_client 为 local 时才生效.

file_client

存在于minion配置文件中, 默认值为remote. 用于指定去哪里查找文件. 有效值是 remote 和 local. remote 表示使用master, local 用于 Masterless 的情况.

pillar_opts

存在于master配置文件中, 默认值为True. 指定是否将master配置选项作为pillar. 如果该选项为True, 修改了master配置选项时, 需要重启master, 才能在pillar中得到最新的值.

Minion中的Pillar实现


Minion中pillar为Python字典, Minion启动时, 默认会连接master获取最新的pillar数据, 存储在 self.opts[‘pillar‘] 中. 对应代码 如下:


class Minion(MinionBase):

    ‘‘‘

    This class instantiates a minion, runs connections for a minion,

    and loads all of the functions into the minion

    ‘‘‘

    def __init__(self, opts, timeout=60, safe=True):

        ‘‘‘

        Pass in the options dict

        ‘‘‘

        ......

        self.opts[‘pillar‘] = salt.pillar.get_pillar(

            opts,

            opts[‘grains‘],

            opts[‘id‘],

            opts[‘environment‘],

        ).compile_pillar()

        ......

那么 salt.pillar.get_pillar 是如何工作的? 对应代码 如下:


def get_pillar(opts, grains, id_, saltenv=None, ext=None, env=None):

    ‘‘‘

    Return the correct pillar driver based on the file_client option

    ‘‘‘

    if env is not None:

        salt.utils.warn_until(

            ‘Boron‘,

            ‘Passing a salt environment should be done using \‘saltenv\‘ ‘

            ‘not \‘env\‘. This functionality will be removed in Salt Boron.‘

        )

        # Backwards compatibility

        saltenv = env


    return {

            ‘remote‘: RemotePillar,

            ‘local‘: Pillar

            }.get(opts[‘file_client‘], Pillar)(opts, grains, id_, saltenv, ext)

也可以从代码中获知, 会从opts中获取 file_client 值, 如果是remote, 则对应的对象为RemotePillar, 如果是local, 则为Pillar, 进行后续处理


如果Minion在运行过程中, 接受到的指令以 refresh_pillar 字符串开头, 则执行 pillar_refresh 操作. 对应代码 如下:


if package.startswith(‘module_refresh‘):

    self.module_refresh()

elif package.startswith(‘pillar_refresh‘):

    self.pillar_refresh()

那么 pillar_refresh() 都进行了哪些工作? 对应代码 如下:


def pillar_refresh(self):

    ‘‘‘

    Refresh the pillar

    ‘‘‘

    self.opts[‘pillar‘] = salt.pillar.get_pillar(

        self.opts,

        self.opts[‘grains‘],

        self.opts[‘id‘],

        self.opts[‘environment‘],

    ).compile_pillar()

    self.module_refresh()

从代码中得知, pillar_refresh操作, 除了从Master端/Minion本地获取最新的pillar信息外, 也会执行模块刷新(module_refresh)工作. 可以将minion本地的日志级别调整为 trac, 然后执行 saltutil.refresh_pillar 操作, 然后观察minion日志, 是否会刷新模块进行验证.


Target中的Pillar


Salt指令发送底层网络, 采用ZeroMQ PUB/SUB结构. Minion会监听SUB接口, Master会将指令发送到本地的PUB接口, 然后所有Minion均会收到该指令, 然后在Minion本地判断自己是否需要执行该指令(即Target). 当前版本中, 已经支持pillar作为Target(通过"-I"选项指定). 对应代码 如下:


def pillar_match(self, tgt, delim=‘:‘):

    ‘‘‘

    Reads in the pillar glob match

    ‘‘‘

    log.debug(‘pillar target: {0}‘.format(tgt))

    if delim not in tgt:

        log.error(‘Got insufficient arguments for pillar match ‘

                  ‘statement from master‘)

        return False

    return salt.utils.subdict_match(self.opts[‘pillar‘], tgt, delim=delim)

可以看出, 其匹配使用的是 self.opts[‘pillar‘] 即当前Minion内存中的Pillar的数据. 因此如果在Master/Minion(当 file_client 为 local 时)修改了Pillar数据后, 想要使用最新的Pillar来做Target操作, 需要在执行前先手动执行 saltutil.refresh_pillar 操作, 以刷新Minion内存中的Pillar数据.


远程执行模块中的Pillar


pillar.items


对应代码 如下:


pillar = salt.pillar.get_pillar(

    __opts__,

    __grains__,

    __opts__[‘id‘],

    __opts__[‘environment‘])


return pillar.compile_pillar()

会连接Master/Minion(当 file_client 为 local 时)获取最新的pillar数据并返回. 但并不会刷新Minion本地的缓存. 也就是说, 在master端修改了Pillar Tree, 在刷新pillar(saltutil.refresh_pillar)前, 可以先使用 pillar.items 来验证其数据是否达到预期.


pillar.data


对应代码 如下:


data = http://www.mamicode.com/items

只是创建了一个赋值引用, 指定data和执行items一样


pillar.item


对应代码 如下:


ret = {}

pillar = items()

for arg in args:

    try:

        ret[arg] = pillar[arg]

    except KeyError:

        pass

return ret

先使用pillar.items来获取最新的Master端最新的pillar数据. 然后一个for循环, 从items获取所需要的keys对应的值. 所以item可以查询多个key.


pillar.raw


对应代码 如下:


if key:

    ret = __pillar__.get(key, {})

else:

    ret = __pillar__


return ret

从当前Minion本地获取 __pillar__ (self.opts[pillar])的值. 也就是说使用 pillar.raw 与 pillar.items 不同, 获取到的是Minion内存中的pillar的值, 并非是master端定义的值. 如果指定了key, 则返回对应key的值. 如果没有, 则返回整个 __pillar__


pillar.get


对应代码 如下:


return salt.utils.traverse_dict(__pillar__, key, default)

和 pillar.raw 工作方式类似, 是从 __pillar__ 中进行的取值, 用于获取pillar中对应的key值. 与 pillar.raw执行key不同的是, get递归获取内嵌字典的值(默认以":"做分隔). 从最新develop分支中看, 下一个版本(Helium)中将增加merge功能.


pillar.ext


与pillar.items工作方式类似, 用于获取ext pillar的值


saltutil.refresh_pillar


对应代码 如下:


__salt__[‘event.fire‘]({}, ‘pillar_refresh‘)

在Minion本地Event接口上产生一个 pillar_refresh event. 之前在Minion中的Pillar中, Minion本地会监听本地Event接口, 如果捕捉到以 pillar_refresh 开始的指令, 会刷新本地pillar.


配置管理中的Pillar


在SLS中使用Pillar


在SLS中, 可以直接使用pillar. 如pillar[‘pkg‘], 其直接使用的是Minion当前内存中pillar的值(self.opts[‘pillar‘]).


state.sls & state.highstate


将这两个远程执行模块方法放到配置管理中, 因为其功能是用于向Minions发送配置管理指令.


state.sls及state.highstate在代码中, 均为 salt.state.HighState 对象. 在执行时为 State 对象. State类在实例化时,则会刷新pillar, 对应代码 如下:


class State(object):

    ‘‘‘

    Class used to execute salt states

    ‘‘‘

    def __init__(self, opts, pillar=None, jid=None):

        if ‘grains‘ not in opts:

            opts[‘grains‘] = salt.loader.grains(opts)

        self.opts = opts

        self._pillar_override = pillar

        self.opts[‘pillar‘] = self._gather_pillar()

而_gather_pillar 对应代码 如下:


def _gather_pillar(self):

    ‘‘‘

    Whenever a state run starts, gather the pillar data fresh

    ‘‘‘

    pillar = salt.pillar.get_pillar(

            self.opts,

            self.opts[‘grains‘],

            self.opts[‘id‘],

            self.opts[‘environment‘],

            )

    ret = pillar.compile_pillar()

    if self._pillar_override and isinstance(self._pillar_override, dict):

        ret.update(self._pillar_override)

    return ret

_gather_pillar从Master上获取Minion对应的最新pillar数据, __init__方法中的 self.opts[‘pillar‘] = self._gather_pillar() 将该数据赋值给self.opts[‘pillar‘]以完成Minion本地内存中Pillar数据的刷新操作. 这就是为什么修改了Master上的Pillar的值, 而无需执行刷新操作(saltutil.refresh_pillar), 因为在执行state.highstate及state.sls时会自动应该最新的值.


ext_pillar


Salt支持从第三方系统中获取Pillar信息,使Salt易于与现有的CMDB系统进行数据整合. 对应的配置是master配置文件中的ext_pillar选项. 官方当前已经提供了 若干驱动 .


如果已经提供的驱动并不满足需求, 自定义ext_pillar驱动也非常简单. 只需要驱动文件放到master端salt代码中pillar目录下即可, 驱动为python代码, 其中包含ext_pillar函数, 且该函数第一个参数是minion_id, 第二个参数为pillar, 其返回值是一个标准的 Python字典 即可. 可以参照 cobbler的ext_pillar 进行编写.


Posted on: 2014-06-08


Category: SaltStack – Tags: saltstack, pillar



pengyao. Built using Pelican. Theme by Giulio Fidente on github. .


本文出自 “DavideyLee” 博客,谢绝转载!

Saltstack中Pillar那点事