Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1662993
  • 博文数量: 631
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 3920
  • 用 户 组: 普通用户
  • 注册时间: 2014-08-06 21:58
个人简介

博客是我工作的好帮手,遇到困难就来博客找资料

文章分类

全部博文(631)

文章存档

2022年(2)

2021年(4)

2020年(40)

2019年(4)

2018年(78)

2017年(213)

2016年(41)

2015年(183)

2014年(66)

我的朋友

分类: 系统运维

2017-04-24 17:11:52

Salt State树
跟系统文件树一样,salt state文件也有自己的树系统,这通过配置文件中的file_roots配置


#在/etc/salt/master 中配置下面选项,指定salt state的根目录在/srv/salt
file_roots:
  base:
    - /srv/salt


修改master配置文件后需要重启,使得配置文件生效


service salt-master restart
或是
pkill salt-master
salt-master -d

Salt State 的top文件

top.sls 文件是SaltState的默认sls文件,再未作特别特别指定时候,salt 会默认调用top.sls来执行

例入 salt * state.highstate。

top.sls 文件需要手工创建,位置则是上面Salt State 树定义的目录/srv/salt下


#创建一个top.sls
touch /srv/salt/top.sls
 
# 添加如下内容,注意sls文件是用2个空格来作为缩进

# base 匹配状态树的base目录配置

# '*'  表示匹配所有的minions

# - webserver 表示执行base目录下webserver.sls状态文件     或是      webserver/init.sls文件

base:
  '*':
    - webserver 

创建一个简单的Salt State文件

上面在top.sls文件中定义了一个webserber文件下面来添加一个webserver文件

在文件/srv/salt/webserver.sls文件中添加下面内容


# apache :ID 声明,跟yum install apache 指定的名称一样
#   pkg :  状态声明,说明下面的操作是什么状态操作
#     - installed : 函数声明,说明该sls文件要指定的执行操作函数


apache:                  
  pkg:                  
    - installed

执行我们定义的Salt State 

# 更新所有的minion的状态

salt '*' state.highstate

备注:根据上面top.sls以及webserver.sls的内容,所有的minions都会给安装apache服务,如果在top.sls中修改minion的匹配,则仅会对匹配到的minion进行操作


当然也可以特别指定sls文件来执行  salt-call '192.168.0.100' state.sls webserver

调试Salt

#开启debug日志     salt-minion -l debug
 
#设置默认超时    salt '*' state.highstate -t 60

1. sls文件本质上是python嵌套字典(键值对),由salt-master以广播的形式传递给salt-minion,通过sls文件告知使用哪个模块的哪个函数,参数有哪些,在salt-minion一侧进行函数调用


2. 冒号':' 用来分隔键和值, 冒号:与后面的单词如果在一行,一定要有一个空格, 一个单词后面是否有冒号:取决于这个单词是否是key, 后面是否有值或者是嵌套的内容
   
3. 短横杠 - 表示这项是个列表项, 短横杠与后面的单词有一个空格

4. 缩进: 本层与下一层要有缩进, 缩进不能用tab, 一般是两个空格. 相同一缩进表示相同的层级


一个sls文件示例:httpd.sls


httpd:                  -------> httpd: sls文件的id,在此sls文件中不能重复,同时也是yum安装的包名和服务名,作为参数name传递给pkg.installed函数和service.running函数
  pkg:                  -------> pkg是包管理模块,对应/usr/lib/python2.6/site-packages/salt/states下的模块pkg.py
    - installed         -------> installed是pkg模块下的函数,id(httpd)作为installed的参数进行调用
  service:              ------->service是服务模块,对应/usr/lib/python2.6/site-packages/salt/states下的模块service.py, 由于service是一个key,其下的running, require, watch是列表形式的值,因此service之后有冒号
    - running           -------> running是service.py模块下的函数,id(httpd)作为running的参数进行调用
    - require:          ------->require关键字在 /usr/lib/python2.6/site-packages/salt/state.py里定义的,由于require是key,其下还有子层,因此require后面要有冒号: 且与后面的单词有一个空格
      - pkg: httpd
    - watch:            ------->require关键字在 /usr/lib/python2.6/site-packages/salt/state.py里定义的,由于watch是key,其下还有子层,因此require后面要有冒号:且与后面的单词有一个空格
      - file: /etc/httpd/conf/httpd.conf


/etc/httpd/conf/httpd.conf:
  file:                            ------->file是salt的文件管理模块,对应/usr/lib/python2.6/site-packages/salt/states下的模块file.py
    - managed                      -------> managed是file.py模块里的一个函数 
    - source: salt://httpd.conf    -------> source是managed函数的参数之一
    - require:
      - pkg: httpd


sls文件的执行: salt '*' state.sls httpd   (注意:是httpd,不是httpd.sls)


另一个sls文件示例:mysql.sls


  mysql-server:                    -------> 这是要安装的包名,也是sls文件的id,不能重复
  pkg:
    - installed                    -------> installed的下一行应该是,- name: mysql-server指明要安装的包名,但是由于包名与id(mysql-server)相同,因此, 无须再单独指明installed函数的参数,-name: mysql-server省略
  service.running:
    - name: mysqld                 -------> - name 这是service.running函数的参数,如果包名与服务名相同,则- name这项可以省略,但本案例中包名是mysql-server,服务名是mysqld,因此必须有-name 指明running函数的参数是mysqld 
    - require:                     -------> require是依赖的含义:要运行服务mysqld, 必须安装mysql-server的包    
      - pkg: mysql-server
    - watch:                       -------> watch: 表示对文件/etc/my.cnf的监控,当master 向minion传递my.cnf时,新的my.cnf与minion上原有文件不一致时,会重启mysqld服务    
      - file: /etc/my.cnf


/etc/my.cnf:                       -------> 这一行同样是id不能重复:表示传递到minion时所处的位置,同时也作为file.managed函数的参数,对应name形参
  file.managed:                    -------> 同上一个示例,file.py模块的managed函数,下面的source,user, group, mode都是managed函数的参数
    - source: salt://my.cnf        -------> source 是managed函数的参数,指定要传递到minion端的源文件. salt://my.cnf 表示my.cnf在/srv/salt之下,/srv/salt是saltstack的根目录
    - user: root                   -------> 表示文件的属主    
    - group: root                  -------> 表示文件的属组    
    - mode: 644                    -------> 表示文件的权限
    - require:                     -------> 文件从master传递到minion, 需要依赖mysql-server包的安装
      - pkg: mysql-server

sls文件的执行: salt '*' state.sls mysql   (注意:是mysql,不是mysql.sls)


[root@salt-master salt]# vim httpd.sls

httpd:
  pkg:
    - installed
  service.running:                #注意: 这种写法时running后面有冒号, 与上面的写法略有不同,但效果相同
    - require:
      - pkg: httpd
    - watch:
      - file: /etc/httpd/conf/httpd.conf


/etc/httpd/conf/httpd.conf:
  file.managed:                    #注意: 这种写法时managed后面有冒号, 与上面的写法略有不同,但效果相同
  - source: salt://httpd.conf
  - require:
      - pkg: httpd




[root@salt-master salt]# vim mysql.sls 


mysql-server:
  pkg:
    - installed
  service.running:
    - name: mysqld
    - require:
      - pkg: mysql-server
    - watch:
      - file: /etc/my.cnf


/etc/my.cnf:
  file.managed:
    - source: salt://my.cnf
    - user: root
    - group: root
    - mode: 644
    - require:
      - pkg: mysql-server
                                      


安装zabbix-agent的示例
[root@salt-master salt]# vim zabbix-agent.sls 

zabbix-agent:
  pkg:
    - installed
  service.running:
    - require:
      - pkg: zabbix-agent
    - watch:
      - file: /etc/zabbix/zabbix_agentd.conf


/etc/zabbix/zabbix_agentd.conf:
  file.managed:
    - source: salt://zabbix_agentd.conf
    - user: root
    - group: root
    - mode: 644



top.sls

top.sls 是配置管理的入口文件,一切都是从这里开始,在master 主机上,默认存放在/srv/salt/目录. top.sls 默认从 base 标签开始解析执行,下一级是操作的目标,可以通过正则,grain模块,或分组名,来进行匹配,再下一级是要执行的state文件,不包括扩展名。

创建 /srv/salt/top.sls

通过正则进行匹配的示例,


base:
  '*':
    - webserver

通过分组名进行匹配的示例,必须要有 - match: nodegroup
base:
  group1:
    - match: nodegroup   
    - webserver


通过grain模块匹配的示例,必须要有- match: grain
base:
  'os:Fedora':
    - match: grain
    - webserver

准备好top.sls文件后,编写一个state文件

/srv/salt/webserver.sls


apache:                 # 标签定义
  pkg:                  # state declaration
    - installed         # function declaration
·        第一行被称为(ID declaration)标签定义,在这里被定义为安装包的名。注意:在不同发行版软件包命名不同,比如 fedora 中叫httpd的包 Debian/Ubuntu中叫apache2
·        第二行被称为(state declaration)状态定义,在这里定义使用(pkg state module)
·        第三行被称为(function declaration)函数定义,在这里定义使用(pkg state module)调用 installed 函数


最后可以在终端中执行命令来查看结果:


salt'*' state.highstate      或附件 test=True参数测试执行

salt'*' state.highstate -v test=True

主控端对目标主机(targeted minions)发出指令运行state.highstatem模块,目标主机首先会对top.sls下载,解析,然后按照top.sls内匹配规则内的定义的模块将被下载,解析,执行,然后结果反馈给 master. 

SLS文件命名空间

注意在以上的例子中,SLS文件 webserver.sls 被简称为webserver. SLS文件命名空间有如下几条基本的规则:


1.    SLS文件的扩展名 .sls 被省略。 (例如. webserver.sls 变成 webserver)


2.    子目录可以更好的组织,每个子目录都由一个点来表示.(例如 webserver/dev.sls 可以简称为 webserver.dev)


3.    如果子目录创建一个init.sls的文件,引用的时候仅指定该目录即可. (例如 webserver/init.sls 可以简称为 webserver)


4.    如果一个目录下同时存在webserver.sls 和 webserver/init.sls,那么 webserver/init.sls 将被忽略,SLS文件引用的webserver将只引用webserver.sls


state多文件示例

下面是一个state多文件示例,

apache/init.sls
apache/httpd.conf


ssh/init.sls
ssh/server.sls
ssh/banner
ssh/ssh_config
ssh/sshd_config


创建一个引用这些目录的 server.sls

server:
  - apache
  - ssh


state的层级关系

include 示例:
include 包含某个state文件

/srv/salt/apache.sls


apache:
  pkg:
    - installed
  service:
    - running
    - require:
      - pkg: apache


使用 include 可以包含有state文件而不必重新写

/srv/salt/apache-custom.sls


include:
  apache

extend 示例:

extend 与include配合使用,作用是修改,或扩展引用的state文件的某个字段

/srv/salt/apache.sls

apache:
  pkg:
    - installed
  service:
    - running
    - require:
      - pkg: apache
extend默认是替换引用文件的某个字段的属性,如例


/srv/salt/apache-change.sls


include:
  - apache
extend:
  apache 
    pkg:
      - name: vim
      - installed
当extend与watch,或require结合使用的时候,则是扩展某个字段的属性,如例


/srv/salt/apache-custom.sls


include:
  - apache
extend:
  apache 
    service:
      - watch:
        - file: /etc/redis.conf

state的逻辑关系列表

match: 配模某个模块,比如 match: grain match: nodegroup

require:依赖某个state,在运行此state前,先运行依赖的state,依赖可以有多个

watch:在某个state变化时运行此模块

order:优先级比require和watch低,有order指定的state比没有order指定的优先级高

state的逻辑关系实例


require:依赖某个state,在运行此state前,先运行依赖的state,依赖可以有多个




httpd:                                  # maps to"name"
  pkg:   
    - installed 
  file:                                # maps to Statemodule filename
    - managed:                          # maps to the managedfunction in the file State module
    - name: /etc/httpd/conf/httpd.conf  # one of many options passed to the managefunction
    - source: salt://httpd/httpd.conf
    - require: 
      - pkg: httpd
·        watch:在某个state变化时运行此模块,watch除具备require功能外,还增了关注状态的功能。
redis:
  pkg:
    - latest
  file.managed:
    - source: salt://redis/redis.conf
    - name: /etc/redis.conf
    - require:
      - pkg: redis
  service.running:
    - enable: True
    - watch:
      - file: /etc/redis.conf
      - pkg: redis

order:优先级比require和watch低,有order指定的state比没有order指定的优先级高

vim:
  pkg.installed:
    - order: 1
想让某个state最后一个运行,可以用last


进阶主题:模板

使用模板来精简SLS,使SLS可以使用python的循环,分支,判断等逻辑
 
{%for item in ['tmp','test'] %}
/opt/{{item }}:
  file.directory:
    - user: root
    - group: root
    - mode: 755
    - makedirs: True
{%endfor %}
httpd:
  pkg.managed:
{%if grains['os'] == 'Ubuntu' %}
    - name: apache2
{%elif grains['os'] == 'CentOS' %}
    - name: httpd
{%endif %}
    - installed
通过加载jinja模板引擎,可以模板配置文件按照预订条件来生成最终的配置文件


/opt/test.conf


{%if grains['os'] == 'Ubuntu' %}
host:{{ grains['host'] }}
{%elif grains['os'] == 'CentOS' %}
host:{{ grains['fqdn'] }}
{%endif %}
/opt/test.conf:
  file.managed:
    - source: salt://test.conf
    -user: root
    - group: root
    - mode: 644
    - template: jinja






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

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服务。

上面的例子还使用了一个奇怪的文件来源 —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

在ssh/server.sls中,用了两种不同的方式来表示用Salt管理一个文件。在ID为/etc/ssh/sshd_config段中,直接使用file.managed作为state声明,而在ID为/etc/ssh/banner段中,使用file作为state声明,附加一个managed属性。

两种表示方法的含义与结果完全一样,只是写法不同。



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相当于把被引用的内容文件添加到自身。


扩展被引用的SLS数据 Extend

扩展是什么意思呢?比如在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的内容,而不是覆盖。

在基于模板引擎的渲染器里,可以从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命令来收集数据。


简单介绍Python和PyDSL渲染器


在任务逻辑非常复杂时,默认的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。


运行和调试Salt States


写好的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。


























阅读(2865) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~