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 = 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. .


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章