上一篇文章歸納性的介紹了Salt的用途和它的基本組成和實現原理,也深刻的的介紹了Salt的命令編排和批量執行,可是對於狀態管理只是簡單的介紹了一下,由於狀態管理是一個比較重要且經常使用的功能,單獨的介紹狀態管理會比較適合。本文將會首先介紹Salt狀態管理的一些概念,而後會經過實例來演示Salt狀態管理的使用,實例的演示基於Vagrant和Vagrant的Salt插件。html
在Salt中,全部的狀態都是經過狀態描述文件來定義的,而它們都存儲在master節點(masterless狀況除外)。Salt經過狀態樹定義了不一樣'環境'下狀態描述文件的層次結構。以下圖:前端
如上圖所示,狀態樹由的根節點是master的配置文件/etc/salt/master,它經過'file_roots'配置項定義了不一樣環境下配置文件所存在的目錄。‘環境’這個概念的主要是用於分門別類的存放不一樣用途的狀態描述文件。例如,一個公司的服務器集羣一般有不一樣的用途,大部分機器是用於線上環境,可是也還有一部分機器用於開發和測試。由於機器的用途不一樣,因此他們除了一些基礎配置相同外,大部分配置是截然不同的。Salt考慮到了這一點,他經過’環境‘將不一樣用途的狀態描述文件隔離在不一樣的目錄,而後經過base環境下的'top.sls'文件描述該環境下哪些minion應該處於哪一種狀態。base環境是默認的基礎環境,它能夠用於存放一些基礎的狀態描述文件,如每一個機器都須要的ldap、ntp、監控等。其它環境的定義是能夠按本身的須要自定義的,如上圖,一般能夠定義dev,qa和prod環境分別表明開發、測試和生產環境。c++
salt-master配置文件中的file_roots定義了環境,以下:git
# Master file_roots configuration: file_roots: base: - /srv/salt/base dev: - /srv/salt/dev qa: - /srv/salt/qa prod: - /srv/salt/prod
base環境下的'top.sls'文件描述該環境下哪些minion應該處於哪一種狀態,以下:github
base: '*': - global dev: 'webserver*dev*': - webserver 'db*dev*': - db qa: 'webserver*qa*': - webserver 'db*qa*': - db prod: 'webserver*prod*': - webserver 'db*prod*': - db
如上,集羣中全部的minions都會使用/srv/salt/base/global.sls定義的狀態;fqdn匹配'webserver*dev*'的minions的會使用/srv/salt/dev/webserver.sls所定義的狀態,其它相似。Salt仍然是使用Targeting的功能來選取節點,因此選取的方式有不少種。web
Salt中狀態樹拓撲結構的定義由salt-master配置文件中的'file_roots'和base環境下的top.sls文件組成。狀態的具體定義是由存儲在這些目錄下的sls文件描述。apache
關於狀態樹的更多信息,請閱讀:http://salt.readthedocs.org/en/latest/ref/states/top.html編程
<Include Declaration>: - <Module Reference> - <Module Reference> <Extend Declaration>: <ID Declaration>: [<overrides>] # standard declaration <ID Declaration>: <State Declaration>: - <Function> - <Function Arg> - <Function Arg> - <Function Arg> - <Name>: <name> - <Requisite Declaration>: - <Requisite Reference> - <Requisite Reference> # inline function and names <ID Declaration>: <State Declaration>.<Function>: - <Function Arg> - <Function Arg> - <Function Arg> - <Names>: - <name> - <name> - <name> - <Requisite Declaration>: - <Requisite Reference> - <Requisite Reference> # multiple states for single id <ID Declaration>: <State Declaration>: - <Function> - <Function Arg> - <Name>: <name> - <Requisite Declaration>: - <Requisite Reference> <State Declaration>: - <Function> - <Function Arg> - <Names>: - <name> - <name> - <Requisite Declaration>: - <Requisite Reference>
上表給出了一個比較完整的狀態描述文件的結構,這是用yaml格式來描述的。Yaml格式和jinja2模板是Salt默認提供的狀態文件描述格式,同時Salt也支持不一樣類型的描述文件,他們經過Render模塊支持,例如xml等。在此,咱們以默認的yaml格式進行介紹。ubuntu
咱們先看一個實際的例子,example.sls:vim
vim: <ID Declaration> pkg: <State Declaration> - installed <Function> salt: <ID Declaration> pkg: <State Declaration> - latest <Function> service.running: <State Declaration>.<Function> - require: <Requisite Declaration> - file: /etc/salt/minion <Requisite Reference> - pkg: salt <Requisite Reference> - names: <Names> - salt-master <Name> - salt-minion <Name> - watch: <Requisite Declaration> - file: /etc/salt/minion <Requisite Reference> /etc/salt/minion: <ID Declaration> file.managed: <State Declaration>.<Function> - source: salt://salt/minion <Function Arg> - user: root <Function Arg> - group: root <Function Arg> - mode: 644 <Function Arg> - require: <Requisite Declaration> - pkg: salt <Requisite Reference>
<ID Declaration>ID聲明
在這個例子中首先經過<ID Declaration>定義了三個狀態描述模塊,他們分別是vim,salt和/etc/salt/minion。在<ID Declaration>下包含了<State Declaration><Function>等定義,這些定義具體描述了vim, salt和/etc/salt/minion這三個模塊具體是由哪些狀態組件組成,使用了狀態組件的哪些功能和具體的參數,它們之間的依賴關係是什麼。同時,若是<State Declaration>下沒有定義<Name Declaration>或<Names Declaration>那麼<ID Declaration>將會默認成爲<State Declaration>下的Name參數。就如同下面兩個狀態描述是等價的,他們都定義了使用pkg這個狀態組件將vim這個包處於安裝狀態。
vim: pkg: -installed
和
editor: pkg: - installed - name: vim
<ID Declaration>在整個狀態樹中必須是單一的,它是其它狀態描述模塊引用它的Key。若是在狀態樹中出現兩個同名的<ID Declaration>,Salt只會識別第一個被加載的狀態定義模塊。
<Name Declaration>和<Names Declaration>聲明
<Name Declaration>和<Names Declaration>都定義在<State Declaration>下,能夠把它們看做是State下某個Function的參數,其中<Names Declaration>就是一個參數數組。如在salt中的service狀態模塊的描述中,就使用salt-mastre和salt-minion做爲<Names Declaration>,定義了這兩個服務處於安裝狀態。
這兩個聲明的使用能夠解決一些實際中的問題,如避免ID衝突,縮短ID聲明等,可參考:
http://salt.readthedocs.org/en/latest/ref/states/highstate.html#name-declaration
http://salt.readthedocs.org/en/latest/ref/states/highstate.html#names-declaration
<State Declaration>狀態聲明
狀態聲明下包含了功能<Function>、功能參數<Function Arg>、Name、Names和表示狀態之間的關係的聲明<Requisite Declaration>(狀態之間的關係在後面一節介紹)。
其實從狀態聲明的數據結構,並結合上一篇文章說講到的命令編排來看,咱們能夠隱約的察覺出salt的狀態管理其實也是使用了由minions所提供的不一樣狀態組件,就如同命令編排中不一樣的module。在狀態描述文件中,經過使用<State Declaration>和<Function>指定了使用狀態組件的某個函數,並將Function Arg, Name和Names傳遞到該函數執行。因此這也驗證了Salt本質上是一個可批量執行的遠程命令編排系統,它的其它擴展功能,包括狀態管理也是基於這樣一個系統構建。
Salt提供了這豐富的狀態組件用於實現狀態管理,如常見的包管理、服務管理、文件管理等,參考:http://docs.saltstack.com/ref/states/all/index.html
如本例中salt和vim模塊都使用了pkg組件,並分別使用了latest和installed函數,這些咱們均可以在該組件的文檔中找到:
http://docs.saltstack.com/ref/states/all/salt.states.pkg.html#salt.states.pkg.installed
http://docs.saltstack.com/ref/states/all/salt.states.pkg.html#salt.states.pkg.latest
從文檔的描述,咱們能夠知道installed函數保證了包處於安裝狀態,latest函數確保了包處於安裝狀態而且是最新的。
在知曉工做原理的前提下,咱們能夠很輕鬆的經過文檔學習狀態管理的使用,而且能自定義的擴展狀態組件。
在Salt中,<Include Declaration>能夠用於應用引用位於其它.sls文件下的<ID Declaration>,就如同c語言的inlcude語句同樣。例如位於base環境中的一個sls文件:
include: - apache # include /srv/salt/base/apache.sls extend: apache: # extend the descrpition of apache service: - watch: - file: mywebsite mywebsite: file: - managed
這個文件首先用<Include Declaration>引用了apache這個狀態文件,由於是位於base環境,因此實際引用的是/srv/salt/base/apache.sls文件。若是apache.sls文件位於/sav/salt/base/web/apache.sls,那麼在include時應該指明是web.apache.
extend語句對apache的定義進行了擴展(apache.sls中已經對apache進行了定義),這個功能至關於c++中子類對父類進行擴展。
在這隻要明確若是須要引用位於其它sls文件中的<ID Declaration>就必須先用include文件引用該sls文件。
除此以外,Salt還提供了7種<Requisite Declaration>,用於實現狀態之間的依賴,它們分別是require, require_in, watch, watch_in, prereq, prereq_in, use。
require, require_in
require聲明瞭本狀態依賴於指定的狀態,require_in聲明瞭本狀態被指定狀態依賴。A require B <=> B require_in A。經過require指令,咱們就能夠指定一個狀態收斂的順序,如先安裝vim再配置vim的配置文件。
vim: pkg.installed /etc/vimrc: file.managed: - source: salt://edit/vimrc # get from master's file server:/srv/salt/[env]/edit/vimrc - require: - pkg: vim
等價於
vim: pkg.installed: - require_in: - file: /etc/vimrc /etc/vimrc: file.managed: - source: salt://edit/vimrc
watch, watch_in
watch和watch_in是require和require_in的擴展,惟一的區別是watch和watch_in會額外的調用狀態組件中的mod_watch函數,若是狀態組件沒有提供該函數,那麼它和require, require_in的行爲徹底同樣。
如本節的第一個例子,經過include apache並擴展了對apache的定義,將service.runing的watch設置成了mywebsite。那麼,mywebsite狀態的改變將觸發調用service的mod_watch函數,重啓apache服務。
prereq, prereq_in
prereq, prereq_in一樣是指明瞭本狀態的執行依賴於指定的狀態。可是與require不一樣的地方是,當A require B,那麼狀態的收斂順序是,先B後A,若是B失敗,A不會執行;當A prereq B時,系統先會用(test=True)去測試B狀態是否會改變(B過程並未實際執行),若是B狀態會改變,那麼先執行A狀態,再執行B狀態,若是A執行失敗,那麼B就不執行了。prereq就是pre request的意思。
這兩個聲明一般用於分佈式服務中,部署升級時先將服務從負載均衡中摘除,在進行代碼升級。例如:
graceful-down: cmd.run: - name: service apache graceful - prereq: - file: site-code site-code: file.recurse: - name: /opt/site_code - source: salt://site/code
當經過salt master代用更新了/opt/site_code下的代碼文件時,salt-minion上的file組件會先對比本地的代碼文件是否與master上的不一致,若是不一致說明site-code這個狀態會變化,那麼先執行graceful-down這個狀態,apache在服務完當前的請求後會shutdown,若是前端的負載均衡器有心跳包檢查機制,會自動將請求分發到其它的節點。這時在實際執行更新代碼的操做,從master上的file server下載最新的site-code文件。
use
use聲明能夠簡化配置,複用指定狀態的配置。例如:
/etc/foo.conf: file.managed: - source: salt://foo.conf - template: jinja - mkdirs: True - user: apache - group: apache - mode: 755 /etc/bar.conf file.managed: - source: salt://bar.conf - use: - file: /etc/foo.conf
等價於
/etc/foo.conf: file.managed: - source: salt://foo.conf - template: jinja - mkdirs: True - user: apache - group: apache - mode: 755 /etc/bar.conf file.managed: - source: salt://bar.conf - template: jinja - mkdirs: True - user: apache - group: apache - mode: 755
Salt除了能夠靜態地描述狀態文件,同時還支持動態生成的狀態文件,使用者能夠經過Jinja2模板並結合Grains或Pillar等功能,對狀態文件進行編程,動態生成狀態文件。
apache: pkg.installed: {% if grains['os'] == 'RedHat' %} - name: httpd {% elif grains['os'] == 'Ubuntu' %} - name: apache2 {% endif %}
經過Grains提供的操做系統信息動態的指定pkg組件使用apache2或httpd安裝包。
更詳細內容請參考:
http://salt.readthedocs.org/en/latest/topics/tutorials/states_pt3.html
http://salt.readthedocs.org/en/latest/topics/tutorials/states_pt4.html
Salt的狀態管理由state模塊完成,一個命令'salt '*' state.highstate'就會觸發全部的minions進行狀態收斂。整個過程大體以下: