關(guān)于,SaltStack 這個牛逼的配置管理神器,上周我寫了篇入門級的 《SaltStack 一日游》。
今天,深入點研究 SaltStack 中的精華部分:“Salt States”。
「Salt States」 翻譯成什么好呢?我想了半天,乳頭都快想破了。老外整的這些玩意翻譯成中文就不倫不類了,索性就按照字面意思 one by one 地譯成「鹽態(tài)」好了,蛤蛤~
注:“乳”字,在漢語中除了指“分泌奶水的器官、乳汁”等含義,還有“初生的、幼小的、小”的意思,除了“乳頭”,類似的例子還有:
- 我的乳名(小名)叫小明。
- 我家門前有條乳溝(小溝)。
今天,我就來講講「鹽態(tài)」到底是怎么回事。以下譯自: HOW DO I USE SALT STATES?
KISS
簡潔,簡潔,簡潔
眾多強(qiáng)大而有力的設(shè)計都建立在簡單的原則之上。Salt State 系統(tǒng)也努力向 K.I.S.S(Keep It Stupidly Simple) 看齊。
SLS(代表 SaLt State文件)是 Salt State 系統(tǒng)的核心。SLS描述了系統(tǒng)的目標(biāo)狀態(tài),由格式簡單的數(shù)據(jù)構(gòu)成,經(jīng)常被稱作配置管理。
只是數(shù)據(jù)而已
深入學(xué)習(xí)之前,明白 SLS文件只是結(jié)構(gòu)化的數(shù)據(jù)而已 是很有用的??炊途帉慡LS文件不需要理解這一點,但會讓你體會到SLS系統(tǒng)的強(qiáng)大。
SLS 文件本質(zhì)上只是一些 dictionaries,lists,strings和numbers。這種設(shè)計讓SLS文件非常靈活,可以滿足開發(fā)者的各種需求,而且可讀性很高。寫得越多,就越清楚到底寫得是什么。最終的結(jié)果是一個簡單易懂的系統(tǒng),它可以隨著開發(fā)者或管理者的需求而變化。
top 文件
下面的 sls 示例文件可以通過一個叫 top.sls 的文件來分派給主機(jī)執(zhí)行。這個文件的詳細(xì)信息可參考 here
默認(rèn)的數(shù)據(jù):YAML
Salt 默認(rèn)使用YAML 這種最簡單的序列化數(shù)據(jù)格式來表達(dá)SLS數(shù)據(jù)。
典型的SLS文件如下:
apache/init.sls:
apache:
pkg.installed:[]
service.running
- require:
- pkg: apache
這些數(shù)據(jù)確保名為apache的軟件包處于已安裝狀態(tài),服務(wù)進(jìn)程apache處于運行狀態(tài)。
這些數(shù)據(jù)簡潔,易于理解。下面簡單解釋一下:
- 第 1 行是這段數(shù)據(jù)的
ID,被稱作ID聲明。這個ID是將要執(zhí)行的這些命令的名字。 - 第 2、3 行包含了要執(zhí)行的
State模塊方法,它的格式為<模塊名>.<方法名>。pkg.installed使用系統(tǒng)本地的軟件包管理器管理將要安裝的軟件;service.running確保指定的服務(wù)必須運行。 - 最后,是關(guān)鍵字
require,它是必要語句Requisite,確保了apache服務(wù)只有在成功安裝軟件包后才會啟動。
添加配置文件和用戶
部署像apache這樣的web服務(wù)器時,還需要添加其他的內(nèi)容。需要管理apache的配置文件,需要添加運行apache服務(wù)的用戶和組。
apache:
pkg.installed: []
service.running:
- watch:
- pkg: apache
- file: /etc/httpd/conf/httpd.conf
- user: apache
user.present:
- uid: 87
- gid: 87
- home: /var/www/html
- shell: /bin/nologin
- require:
- group: apache
group.present:
- gid: 87
- require:
- pkg: apache
/etc/httpd/conf/httpd.conf:
file.managed:
- source: salt://apache/httpd.conf
- user: root
- group: root
- mode: 644
這個SLS大大擴(kuò)展了上面的例子,增加了配置、用戶、組,還有一個新的必要語句:watch。
添加 state 非常簡單:user和group這兩個state添加在apache這個ID下,所以增加的user和group名字都是apache。require語句確保了只有在apache這個group存在時才建立user,只有在apache這個package成功安裝后才會建立group。
接下來,service中的require語句換成了watch,從需要 1 個軟件包改為監(jiān)視 3 個state(分別是pkg、file和user)。watch語句和require很相似,都能保證被監(jiān)視或需要的state在自己之前執(zhí)行,但是watch還有其他作用。在被監(jiān)視的state發(fā)生變化時,定義watch語句的state會執(zhí)行自己的watcher函數(shù)。也就是說,更新軟件包、修改配置文件、修改apache用戶的uid都會觸發(fā)service state的watcher函數(shù)。在這個例子中,service state的watcher會重啟apache服務(wù)。
多個SLS文件
要想可擴(kuò)展性地部署 Salt State 系統(tǒng),將會用到不止一個 SLS 文件。上面的例子中只使用 1 個SLS文件,2 個或多個SLS文件可以結(jié)合形成State Tree。上面的例子還使用了一個奇怪的文件來源 salt://apache/httpd.conf,這個文件也必須要找的到。
SLS文件以一定的目錄結(jié)構(gòu)分布在master上;SLS和要下發(fā)到minion上的文件都只是普通文件。
上面例子中的文件在 Salt根目錄(/etc/salt/)下:
apache/init.sls
apache/httpd.conf
httpd.conf 只是apache目錄下的一個普通文件,可以直接引用。
使用多個SLS文件可以更加靈活方便,以SSH為例:
ssh/init.sls:
openssh-client:
pkg.installed
/etc/ssh/ssh_config:
file.managed:
- user: root
- group: root
- mode: 644
- source: salt://ssh/ssh_config
- require:
- pkg: openssh-client
ssh/server.sls:
openssh-client:
pkg.installed
/etc/ssh/ssh_config:
file.managed:
- user: root
- group: root
- mode: 644
- source: salt://ssh/ssh_config
- require:
- pkg: openssh-client
ssh/server.sls:
include:
- ssh
openssh-server:
pkg.installed
sshd:
service.running:
- require:
- pkg: openssh-client
- pkg: openssh-server
- file: /etc/ssh/banner
- file: /etc/ssh/sshd_config
/etc/ssh/sshd_config:
file.managed:
- user: root
- group: root
- mode: 644
- source: salt://ssh/sshd_config
- require:
- pkg: openssh-server
/etc/ssh/banner:
file:
- managed
- user: root
- group: root
- mode: 644
- source: salt://ssh/banner
- require:
- pkg: openssh-server
注:在
ssh/server.sls中,用了兩種不同的方式來表示用Salt管理一個文件。在ID為/etc/ssh/sshd_config段中,直接使用file.managed作為state聲明,而在ID為/etc/ssh/banner段中,使用file作為state聲明,附加一個managed屬性。兩種表示方法的含義與結(jié)果完全一樣,只是寫法不同。
現(xiàn)在 State Tree 如下:
apache/init.sls
apache/httpd.conf
ssh/init.sls
ssh/server.sls
ssh/banner
ssh/ssh_config
ssh/sshd_config
ssh/server.sls 中使用了include語句。include將別的SLS添加到當(dāng)前文件中,所以可以require或watch或 extend(憋著急,下面馬上會講到)被引用的SLS中定義的內(nèi)容。
include語句使得state可以跨文件引用,使用include相當(dāng)于把被引用的內(nèi)容文件添加到自身。
注:你可能注意到有些
SLS文件叫init.sls,有些又不是,關(guān)于它的約定規(guī)則可以參考 States Tutorial
Extend:擴(kuò)展被引用的SLS數(shù)據(jù)
有的時候,SLS 文件需要擴(kuò)展,也許是 apache 服務(wù)需要監(jiān)聽另外一個文件,或者在某種特殊條件下,某個文件需要添加進(jìn)來。
在下面的例子中,第一個將添加一個自定義的 banner 文件到 ssh,第二個多添加一個watcher到 apache 以便引入mod_python。
ssh/custom-server.sls:
include:
- ssh.server
extend:
/etc/ssh/banner:
file:
- source: salt://ssh/custom-banner
python/mod_python.sls:
include:
- apache
extend:
apache:
service:
- watch:
- pkg: mod_python
mod_python:
pkg.installed
custom-server.sls 文件使用 extend 語法來覆蓋banner 的下載路徑文件,相當(dāng)于替換了banner的配置文件。
在mod_python.sls 文件中, 添加了 mod_python,但是更關(guān)鍵的是 apache 服務(wù)擴(kuò)展成為它還要額外監(jiān)聽這個 mod_python 包。
Extend 使得 SLS更加靈活,在處理SLS時,會將其中的內(nèi)容解析成Python中的dict(當(dāng)然這個dict中會嵌套dict和list)。
- 擴(kuò)展
apache的watch,相當(dāng)于往list里面 添加 一個元素。 - 修改
banner文件的下載路徑相當(dāng)于 修改dict中的某個key對應(yīng)的值。
注:在使用
extend時,會添加require/watch的內(nèi)容,而不是覆蓋。
Render System:理解渲染系統(tǒng)
由于SLS僅僅是數(shù)據(jù),所以它不是一定得用YAML來表達(dá)。Salt默認(rèn)使用YAML,只是因為易學(xué)易用。只要有對應(yīng)的渲染器,SLS文件可以用任何方式表達(dá)。
注:
Salt關(guān)心的是最終解析出來的數(shù)據(jù)結(jié)構(gòu),只要你的渲染器能夠按要求返回這個數(shù)據(jù)結(jié)構(gòu),它不關(guān)心你是如何編寫的。
Salt默認(rèn)使用yaml_jinja渲染器,yaml_jinja渲染器先用jinja2模板引擎處理SLS源文件,然后再調(diào)用YAML解析器。這種設(shè)計的好處是: 可以在SLS文件中使用所有的編程結(jié)構(gòu)。
jinja2能怎么用,這里就能怎么用。條件,循環(huán),Python代碼……神馬都可以
其他可用的渲染器還包括:yaml_mako,使用 Mako模板引擎;yaml_wempy,使用Wempy模板引擎;py,直接使用Python寫SLS文件;pydsl,建立在Python語法基礎(chǔ)上的描述語言。
yaml_jinja: 默認(rèn)的渲染器
關(guān)于jinja模板引擎的使用請參考其 官方文檔
Salt在和渲染器工作時,已經(jīng)往里面?zhèn)鬟M(jìn)去了一些十分有用的數(shù)據(jù)。在基于模板引擎的渲染器里,可以從3個組件中獲取需要的數(shù)據(jù):salt,grains和pilla。在模板文件中,可以用salt對象執(zhí)行任意的Salt function,使用grains訪問Grains數(shù)據(jù)。示例如下:
apache/init.sls:
apache:
pkg.installed:
{% if grains['os'] == 'RedHat'%}
- name: httpd
{% endif %}
service.running:
{% if grains['os'] == 'RedHat'%}
- name: httpd
{% endif %}
- watch:
- pkg: apache
- file: /etc/httpd/conf/httpd.conf
- user: apache
user.present:
- uid: 87
- gid: 87
- home: /var/www/html
- shell: /bin/nologin
- require:
- group: apache
group.present:
- gid: 87
- require:
- pkg: apache
/etc/httpd/conf/httpd.conf:
file.managed:
- source: salt://apache/httpd.conf
- user: root
- group: root
- mode: 644
這個例子很容易理解,用到了jinja中的條件結(jié)構(gòu),如果grains中的os表明minion的操作系統(tǒng)是Red Hat,那么Apache的軟件包名和服務(wù)名應(yīng)當(dāng)是httpd。
再來一個更niubility的例子,用到了jinja的循環(huán)結(jié)構(gòu),在設(shè)置 MooseFs分布式chunkserver的模塊中:
moosefs/chunk.sls:
include:
- moosefs
{% for mnt in salt['cmd.run']('ls /dev/data/moose*').split() %}
/mnt/moose{{ mnt[-1] }}:
mount.mounted:
- device: {{ mnt }}
- fstype: xfs
- mkmnt: True
file.directory:
- user: mfs
- group: mfs
- require:
- user: mfs
- group: mfs
{% endfor %}
/etc/mfshdd.cfg:
file.managed:
- source: salt://moosefs/mfshdd.cfg
- user: root
- group: root
- mode: 644
- template: jinja
- require:
- pkg: mfs-chunkserver
/etc/mfschunkserver.cfg:
file.managed:
- source: salt://moosefs/mfschunkserver.cfg
- user: root
- group: root
- mode: 644
- template: jinja
- require:
- pkg: mfs-chunkserver
mfs-chunkserver:
pkg.installed: []
mfschunkserver:
service.running:
- require:
{% for mnt in salt['cmd.run']('ls /dev/data/moose*') %}
- mount: /mnt/moose{{ mnt[-1] }}
- file: /mnt/moose{{ mnt[-1] }}
{% endfor %}
- file: /etc/mfschunkserver.cfg
- file: /etc/mfshdd.cfg
- file: /var/lib/mfs
這個例子展示了jinja的強(qiáng)大,多個for循環(huán)用來動態(tài)地檢測并掛載磁盤,多次使用salt對象(這里使用了cmd.run這個執(zhí)行模塊)執(zhí)行shell命令來收集數(shù)據(jù)。
簡單介紹Python和PyDSL渲染器
在任務(wù)邏輯非常復(fù)雜時,默認(rèn)的yaml_jinja渲染器不一定滿足要求,這時可以使用Python渲染器。
Normally a YAML renderer should be used for the majority of SLS files, but an SLS file set to use another renderer can be easily added to the tree.
正常情況下,YAML的渲染器應(yīng)該可以適用于絕大部分 SLS 文件,但是使用其他的渲染器的 SLS 文件同樣可以輕易地適配到 sls tree 中。
下面是一個非常簡單的基本Python SLS文件:
python/django.sls:
#!py
def run():
'''
Install the django package
'''
return {'include': ['python'],
'django': {'pkg': ['installed']}}
這個例子也很好理解,第 1 行告訴Salt不使用默認(rèn)的渲染器,而是用py。接著定義了函數(shù)run,這個函數(shù)的返回值必須符合Salt的要求,即HighState數(shù)據(jù)結(jié)構(gòu)。
如果換用pydsl渲染器,上面的例子會更簡潔:
python/django.sls:
#!pydsl
include('python', delayed=True)
state('django').pkg.installed()
如果用YAML,會是下面這個樣子:
include:
- python
django:
pkg.installed
這也可以看出,正常情況下使用YAML是非常合適的,但如果有需要時,使用純粹的Python SLS可以非常犀利地裝逼哦。
運行和調(diào)試 Salt States
寫好的SLS如何才能應(yīng)用到minion呢?
在SaltStack中,遠(yuǎn)程執(zhí)行是一切的基礎(chǔ)。執(zhí)行命令salt '*' state.apply 會讓所有的minion到master上來取走自己的SLS文件,然后在本地調(diào)用對應(yīng)的state模塊(user,pkg,service等,內(nèi)置的 state 模塊列表在 這里)來達(dá)到SLS描述的狀態(tài)。
如果這條命令只返回minion的主機(jī)名加一個':',多半是哪一個SLS文件有錯。如果minion是以服務(wù)進(jìn)程啟動,執(zhí)行命令:
salt-call state.aply -l debug
可以看到錯誤信息,便于調(diào)試。minion還可以直接在前臺以debug模式運行:
salt-minion -l debug
What's next?
這篇文章只是對
Salt States的初步介紹,下一步請繼續(xù)閱讀Pillar部分,官方文檔 在此。