上一篇文章归纳性的介绍了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进行状态收敛。整个过程大体以下: