Salt狀態管理

前言

上一篇文章歸納性的介紹了Salt的用途和它的基本組成和實現原理,也深刻的的介紹了Salt的命令編排和批量執行,可是對於狀態管理只是簡單的介紹了一下,由於狀態管理是一個比較重要且經常使用的功能,單獨的介紹狀態管理會比較適合。本文將會首先介紹Salt狀態管理的一些概念,而後會經過實例來演示Salt狀態管理的使用,實例的演示基於Vagrant和Vagrant的Salt插件。html

Salt狀態管理的關鍵概念

狀態樹

在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進行狀態收斂。整個過程大體以下:

  1. master經過命令'salt [Targeting] state.highstate'調用指定的minions的state模塊的highstate函數;
  2. minion的state模塊訪問master的file server,經過top.sls的定義,file server會將minion所屬環境下的狀態文件和靜態文件傳輸給minion;
  3. minion對狀態文件進行編譯,從highdata到lowdata,生成了具體的狀態收斂順序;
  4. minion中的state模塊根據狀態收斂的順序執行該狀態說指定的狀態組件,如pkg, service, file等;
  5. minion中的state將執行結果返回給master;

實例:使用Vagrant和Salt配置開發環境

需求

  • 單節點
  • 安裝vim並從git倉庫中拉取配置文件
  • 使用ubuntu 12.04 64位操做系統
  • 整個過程所有自動化

實現步驟

  1. 安裝virtualbox和vagrant
  2. 安裝vagrant-salt插件,'$vagrant plugin install vagrant-salt';
  3. 參考salty-vagrant的手冊,編寫Vagrant配置文件;
  4. 參考本文內容編寫Salt的配置文件;
  5. 運行'$vagrant up'構建開發環境;

所有文件:https://github.com/AlexYangYu/example-vagrant-salt

相關文章
相關標籤/搜索