我也只是SaltStack的初学者,若是文中有错误的地方,请不吝赐教。html
在学习的过程,我也作了很多实验,犯了很多错,积累了一些经验,对SaltStack的运行也有必定了解,若是有什么问题,或是不太理解的地方,很是欢迎留言交流!python
参考连接:官方文档web
简洁,简洁,简洁shell
众多强大而有力的设计都创建在简单的原则之上。Salt SLS系统也努力向K.I.S.S看齐。(Keep It Stupidly Simple)apache
SLS(表明SaLt State文件)是Salt State系统的核心。SLS描述了系统的目标状态,由格式简单的数据构成。这常常被称做配置管理。django
Note 这篇文章从总体上介绍了Salt States,之后还会增长对各组件的深刻介绍。
深刻学习以前,明白SLS文件只是结构化的数据而已经是颇有用的。看懂和编写SLS文件不须要理解这一点,但会让你体会到SLS系统的强大。编程
SLS文件本质上只是一些dictionaries
,lists
,strings
和numbers
。这种设计让SLS文件很是灵活,能够知足开发者的各类需求,并且可读性很高。写得越多,就越清楚到底写得是什么。服务器
Salt默认使用能找到的最简单的序列化数据格式——YAML,来表达SLS数据。典型的SLS文件以下:数据结构
apache: pkg: - installed service: - running - require: - pkg: apache
这些数据确保名为apache
的软件包处于已安装状态(若是不是,那么就安装apache
),服务进程apache
处于运行状态。这些数据简洁,易于理解。下面简单解释一下:
第1行是这段数据的ID,被称做ID声明。这个ID是将要执行的这些命令的名字。
第2行和第4行表示State声明开始,使用了pkg和service这两个states。pkg使用系统本地的软件包管理器管理将要安装的软件,service管理系统守护进程。
第3行和第5行是要执行的function。这些function定义了名字为ID的软件包和服务的目标状态。此例中,软件包应当处于已安装状态,服务必须运行。
最后,第6行是关键字require。这被称为必要语句(Requisite),它确保了apache服务只有在成功安装软件包后才会启动。ssh
部署像apache这样的web服务器时,还须要添加其余的内容。须要管理apache的配置文件,须要添加运行apache服务的用户和组。
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大大扩展了上面的例子,增长了配置、用户、组,还有一个新的必要语句:watch。
user和group这两个state添加在apache这个ID下,因此增长的user和group名字都是apache。require语句确保了只有在apache这个group存在时才创建user,只有在apache这个package成功安装后才会创建group。
service中的require语句换成了watch,从须要1个软件包改成监视3个state(分别是pkg、file和user)。watch语句和require很类似,都能保证被监视或须要的state在本身以前执行,可是watch还有其余做用。在被监视的state发生变化时,定义watch语句的state会执行本身的watcher函数。也就是说,更新软件包,修改配置文件,修改apache用户的uid都会触发service state的watcher函数。在这个例子中,service state的watcher会重启apache服务。
Note Salt的watcher概念很是有意思。Puppet中功能相似的是notify,也能够触发服务重启。Salt的watcher很是灵活,watcher本质上是在state的代码中定义的名为mod_watch()的函数,在这个函数中想作什么事情彻底就看你的需求了。我没有仔细看Puppet的notify如何实现,不知道是否有这么灵活。
在更有扩展性的部署Salt State时,须要用到不仅一个SLS。上面的例子中只使用1个SLS文件,2个或多个SLS文件能够结合造成State Tree。上面的例子还使用了一个奇怪的文件来源 —salt://apache/httpd.conf
,这个文件究竟在什么位置呢?
SLS文件以必定的目录结构分布在master上;SLS和要下发到minion上的文件都只是普通文件。
上面的例子中的文件在Salt的根目录(见《SaltStack中的文件服务器》)分布以下:
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:
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
Note 在ssh/server.sls中,用了两种不一样的方式来表示用Salt管理一个文件。在ID为/etc/ssh/sshd_config段中,直接使用file.managed做为state声明,而在ID为/etc/ssh/banner段中,使用file做为state声明,附加一个managed属性。两种表示方法的含义与结果彻底同样,只是写法不一样。
如今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添加到当前文件中,因此能够require或watch被引用的SLS中定义的内容,还能够extend其内容(立刻讲到)。include语句使得state能够跨文件引用。使用include至关于把被引用的内容文件添加到自身。
扩展是什么意思呢?好比在ssh/server.sls中定义了一个apache通用的服务器,如今要增长一个带mod_python模块的apache,不须要重头写新的SLS,直接include原来的server.sls,而后增长安装mode_python的state,再在apache service的watch列表中增长mod_python便可。python/mod_python.sls内容以下:
include: - apache extend: apache: service: - watch: - pkg: mod_python mod_python: pkg.installed
这个例子中,先将apache目录下的init.sls文件包含进来(在include一个目录时,Salt会自动查找init.sls文件),而后扩展了ID为apache下的service state中的watch列表。
也能够在Extending中修改文件的下载位置。ssh/custom-server.sls:
include: - ssh.server extend: /etc/ssh/banner: file: - source: salt://ssh/custom-banner
Extend使得Salt的SLS更加灵活。为何SLS可以作Extend呢?文章一开始最强调了,SLS中的文件仅仅是结构化的data而已,在处理SLS时,会将其中的内容解析成Python中的dict(固然这个dict中会嵌套dict和list)。修改apache watch的内容,至关于往list里面添加一个元素;修改banner文件的下载路径至关于修改dict中的某个key对应的值。在extending时,会附加加require/watch的内容,而不是覆盖。
由于SLS仅仅是data,因此不是非得用YAML来表达。Salt默认使用YAML,只是由于易学易用。只要有对应的renderer,SLS文件能够用任何方式表达(Salt关心的是最终解析出来的数据结构,只要你的renderer可以按要求返回这个数据结构,Salt干吗关心你如何书写源文件呢?)。
Salt默认使用yaml_jinja渲染器。yaml_jinjia先用jinja2模板引擎处理SLS,而后再调用YAML解析器。这种设计的好处是,能够在SLS文件使用全部的编程结构(jinja2能怎么用,这里就能怎么用。条件,循环,Python代码,什么均可以)。
其余可用的渲染器还包括:yaml_mako,使用Mako模板引擎;yaml_wempy,使用Wempy模板引擎;py,直接使用Python写SLS文件;pydsl,创建在Python语法基础上的描述语言。
关于jinja模板引擎的使用请参考其官方文档
在基于模板引擎的渲染器里,能够从3个组件中获取须要的数据:salt,grains和pilla。在模板文件中,能够用salt对象执行任意的Salt function,使用grains访问Grains数据。示例以下:
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中的条件结构,若是grains中的os代表minion的操做系统是Red Hat,那么Apache的软件包名和服务名应当是httpd。
再来一个更NB的例子,用到了jinja的循环结构,在设置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的强大,多个for循环用来动态地检测并挂载磁盘,屡次使用salt对象(这里使用了cmd.run这个执行模块)执行shell命令来收集数据。
在任务逻辑很是复杂时,默认的yaml_jinja渲染器不必定知足要求,这时可使用Python渲染器。如何在State tree中添加使用py渲染器的SLS文件呢?简单。 一个很是简单的基本Python SLS文件:
python/django.sls:
#!py def run(): ''' Install the django package ''' return {'include': ['python'], 'django': {'pkg': ['installed']}}
这个例子也很好理解,第1行告诉Salt不使用默认的渲染器,而是用py。接着定义了函数run,这个函数的返回值必须符合Salt的要求,即HighState数据结构(我接下来就写关于HighState的文章,如今没必要关心其细节,反正就是一个dict,key和value都有规定好的含义)。 若是换用pydsl渲染器,上面的例子会更简洁:
python/django.sls:
#!pydsl include('python', delayed=True) state('django').pkg.installed()
若是用YAML,会是下面这个样子:
include: - python django: pkg.installed
这也能够看出,正常状况下使用YAML是很是合适的,但若是有须要时,使用纯粹的Python SLS能够很是NB。
写好的SLS如何才能应用到minion呢?在SaltStack中,远程执行是一切的基础。执行命令salt '*' state.highstate
会让全部的minion到master上来取走本身的SLS定义,而后在本地调用对应的state module(user,pkg,service等)来达到SLS描述的状态。若是这条命令只返回minion的主机名加一个':',多半是哪个SLS文件有错。若是minion是以服务进程启动,执行命令salt-call state.highstate -l debug
能够看到错误信息,便于调试。minion还能够直接在前台以debug模式运行:salt-minion -l debug
。