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