SaltStack 入门实践下
(2016-06-03 14:12:30)
标签:
saltstack |
分类: 自动化 |
转载自: 来自:http://tech.mainwise.cn/?p=438
十、关于渲染器render system
我们上面也提过salt默认的渲染器是yaml_jinja,salt处理我们的sls文件时,会先把文件用jinja2处理,然后传给ymal处理器在处理,然后生成的是salt需要的python数据类型。除了yaml_jinja还有yaml_mako,yaml_wempy,py,pydsl,我比较感兴趣的就是yaml_jinja,还有py,yaml_jinja是默认的,而py是用纯python来写的。下面来看个样例吧,
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
这个样例很简单,就是加了个判断,如果Minion的grains的os是RedHat那么apache的包名是httpd,默认是apache,我们知道在别的Linux发行版上,如ubuntu,suse他们的apache的包名就是叫apache,而在redhat系上则叫httpd,所以才有了这个判断写法,下面的service也是如此。我们着重说语法,jinja中判断,循环等标签是放在{% %}中的,通常也会有结束标签{% end** %},而变量是放在 {{ }}中的,salt,grains,pillar是salt中jinja里面的三个特殊字典,salt是包含所有salt函数对象的字典,grains是包含minion上grains的字典,pillar是包含minion上pillar的字典。
示例:for user/init.sls文件内容
{% set users = ['jerry','tom','gaga'] %}
{% for user in users %}
{{ user }}:
user .present:
- shell: /bin/bash
- home: /home/{{ user }}
{% endfor %}
示例;salt字典 user/init.sls文件内容
{% if salt['cmd.run']('uname -i') == 'x86_64' %}
hadoop:
user .present:
- shell: /bin/bash
- home: /home/hadoop
{% elif salt['cmd.run']('uname -i') == 'i386' %}
openstack:
user .present:
- shell: /bin/bash
- home: /home/openstack
{% else %}
django:
user .present:
- shell: /sbin/nologin
{% endif %}
py渲染器说明:py渲染器是用纯python写的sls文件,它返回的数据与yaml_jinja经过jinja处理经过yaml处理后的数据类似,用其他渲染器需要在sls文件头行声明用的渲染器类型,#!py就是声明用的py渲染器,py中可用的变量有salt,grains,pillar,opts,env,sls,前三个分别对应jinja里的salt,grains,pillar,opts是minion的配置文件的字典,env对应的是环境如base,sls对应的是sls的文件名
示例: user/init.sls
#!py
import os
def run():
'''add user hadoop'''
platform = os.popen('uname -a').read().strip()
if platform == 'x86_64':
return {'hadoop': {'user': ['present',{'shell': '/bin/bash'}, {'home': '/home/hadoop'}]}}
elif platform == 'i386':
return {'openstack': {'user': ['present', {'shell': '/bin/bash'}, {'home': '/home/openstack'}]}}
else:
return {'django': {'user': ['present', {'shell': '/sbin/nologin'}]}}
说明: 首行声明了使用py作为渲染器,导入了os模块,声明run函数,py渲染sls文件是从run函数开始的,其它的就是python的语法了,注意的是return的数据结构{ID: {module: [func, arg1,arg2,...,]}} 或 {ID: {module.func: [arg1,arg2,..,]}} 。表示的内容与“示例;salt字典”表达的相同
九、state的执行顺序
以前我们说过,state的执行时无序,那个无序是指执行我们写的那个sls是无序的,正是因为那个无序,salt保证每次执行的顺序是一样的,就加入了state order,在说它之前看看High Data(高级数据?)和Low Data(低级数据?),高级数据我理解的就是我们编写sls文件的数据,低级数据就是经过render和parser编译过的数据。
查看highdata
salt '*' state.show_highstate
查看lowdata
salt '*' state.show_lowstate
通过查看lowdata我们发现里面有一个字段order,因为salt默认会自动设置order,从10000开始。可以通过设置master配置文件参数state_auto_order: False来关闭
Order的设定:
1.include 被include的文件Order靠前,先执行
2.手动定义order字段,如
apache :
pkg :
- installed
- order: 1
order 的数字越小越先执行从1开始,-1是最后执行
3.依赖关系系统
十一、依赖关系系统requisite system
前面我们已经用过了依赖关系系统,就是定义状态与状态之间的依赖关系的,经常遇到的依赖系统的函数有’require’和’watch’和它们的变种’require_in’,’watch_in’,require和watch有什么区别吗?
1.不是所有的state都支持watch,比较常用的是service
2.watch定义的依赖条件发生变化时会执行一些动作,如当配置文件改变时,service会重启
示例: apache/init.sls文件内容
/etc/httpd/httpd.conf:
file :
- managed
- source: salt://httpd/httpd.conf
httpd:
pkg :
- installed
service :
- running
- require:
- pkg: httpd
- watch:
- file: /etc/httpd/httpd.conf ##当httpd.conf改变时,重启httpd服务
require与require_in, watch与watch_in
require,watch是指依赖,require_in,watch_in是指被依赖
a reuire b 那么就是b require_in a
a watch b 那么就是b watch_in a
示例: apache/init.sls文件内容
/etc/httpd/httpd.conf:
file :
- managed
- source: salt://httpd/httpd.conf
- watch_in:
- service: httpd
httpd:
pkg :
- installed
- require_in:
- service: httpd
service :
- running
十二、salt state环境
针对不用环境,应用不同的state的file,salt支持多环境,比如开发,测试,生产等环境,我们通过修改Master配置文件对不同的环境应用不同的目录!
file_roots:
base:
- /srv/salt/prod ##生产环境
qa :
- /srv/salt/qa ##测试环境,如果没发现去prod里面找
- /srv/salt/prod
dev :
- /srv/salt/dev ##开发环境,如果找不到,先去qa里找,如果找不到再去prod里面找
- /srv/salt/qa
- /srv/salt/prod
/srv/salt/prod/top.sls文件内容
base:
'web*prod*':
- webserver.foobarcom
qa:
'web*qa*':
- webserver.foobarcom
dev:
'web*dev':
- webserver.foobarcom
-
pillar的目录与file_roots无关,所以Pillar的目录默认还是/srv/salt,pillar只是Minion的一些信息,不会对系统有什么改变,所以不需要区分环境,通常base即可。
/srv/pillar/top.sls文件内容
base:
'web*prod*':
- webserver.prod
'web*qa*':
- webserver.qa
'web*dev*':
- webserver.dev
/srv/pillar/webserver/prod.sls文件内容
webserver_role: prod
/srv/pillar/webserver/qa.sls文件内容
webserver_role: qa
/srv/pillar/webserver/dev文件内容
webserver_root: dev
最后sls文件/srv/salt/prod/webserver/foobarcom.sls(该文件会被所有环境访问到)的内容:
{% if pillar.get('webserver_role', '') %}
/var/www/foobarcom:
file .recurse:
- source: salt://webserver/src/foobarcom
- env: {{ pillar['webserver_role'] }}
- user: www
- group: www
- dir_mode: 755
- file_mode: 644
{% endif %}
开发完成后,应用sls文件
1.现在开发环境
salt -I ‘webserver_role:dev’ state.sls webserver.foobarcom
十三、salt schedule
schedule是salt中的crontab,就是周期性执行一些函数,需要注意的是在minion上执行的函数是salt的可执行模块里的函数,在master上执行的是runner模块的函数,下面看看如何设置: master是修改master配置文件/etc/salt/master:
schedule:
overstate : ##这个是ID,可以随意起,全文件唯一
function: state.over ##对于master,function就是runner
seconds : 35 ##间隔秒数
minutes : 30 ##间隔分数
hours : 3 ##间隔小时数
这时每隔3小时30分35秒,master就会运行一个state.over这个runner
minion的schedule定义有两种方式
1.通过修改minion的配置文件,这种方式需要修改所有Minion的配置文件略麻烦
schedule:
highstate :
function: state.highstate
seconds: 30
minutes: 5
hours: 1
2.为Minion指定pillar
/srv/pillar/schedule.sls文件内容
schedule:
highstate :
function: state.highstate
seconds: 30
minutes: 5
hours: 1
通过top file指定到Minion
/srv/pillar/top.sls文件内容
base:
*:
- schedule
十四、YAML语法风格
1.空格和Tabs
在YAML中不要使用Tab
2.缩进
YAML推荐缩进为2个空格,’:’,’-’后面缩进1个空格再写
3.数字会解析成数字
如mode: 0644会解析成mode: 644,可用’括住防止mode: ’0644′此情况
4.YAML不允许双简写
vim:
pkg.installed ##第一个简写,单一个简写没问题
user.present ##第二个简写,加上它是不支持的
不要偷懒写成下面这样吧。
vim: pkg:
- installed user: – present – 5.YAML只支持ASCII
其它字符集最好不要使用,如果非要使用用以下格式:
- micro: ‘\u00b5′
6.下划线_将会被删除
date: 2013_05_13 --> date: 20130513
通过’括住防止出现该问题
date: '2013_05_13'
十五、salt事件系统与反应系统 event and reacter system
我们知道Master与Minion是基于ZMQ通信的,他们通信我们看来是消息队列,对它们来说这些消息就是一些事件,什么事件应该做什么,是salt基本已经预设好了。我们学习它的事件系统来完成一些自定义的行为,后面的反应系统就是基于事件系统的。一条消息其实就是一个事件,事件通常是一个字典数据,这个字典数据通常包含tag,这个tag是用来区分用途过滤消息的,详见绿大-https://groups.google.com/forum/#!topic/saltstack-users-cn/wXVE4ydnnzc ,让我们来看看这些事件。
捕捉事件(listen event)
1.下载官方给的事件捕捉程序eventlisten
https://github.com/saltstack/salt/blob/develop/tests/eventlisten.py 打开网址,复制下载,不要直接wget
2.运行该程序
Master: python2.6 eventlisten.py ##捕捉master端的event直接运行即可
Minion: python2.6 eventlisten.py -n minion <</span>minion-id> ##捕捉minion端的需要额外参数,minion-id是该Minion的id
发送事件(fire event)
Master发给minion
salt '*' event.fire "{'data': 'some message'}" "tag" ##前面必须是字符串包住的字典,后面是tag,如果你的minion在监听event,你会看到这条event的
Minion发给minion
salt-call event.fire_master 'some message' 'tag' ##前面数据类型没有要求,后面是tag,去master那看看收到了没有
Minion发给自己
salt-call event.fire "{'data': 'some message'}" 'tag' ##前面必须是字符串包住的字典,后面是tag
用code来捕捉,并发送event
捕捉事件 Master:
# python2.6
>>> import salt.utils.event
>>> event = salt.utils.event.SaltEvent('master', '/var/run/salt/master')
##master表明是在master端监听,/var/run/salt/master是你master的sock_dir
>>> data = event.get_event()
>>> print(data) ##查看内容
>>> data = event.get_event(wait=10, tag='auth') ##wait是指timeout时间,默认5s,用tag来过滤事件,可省略
>>> print(data)
>>> for data in event.iter_events(tag='auth'): ##用迭代器一直查看事件
>>> print(data)
Minion:
#python2.6
>>> import salt.utils.event
>>> event = salt.utils.event.SaltEvent('minion', '/var/run/salt/minion',id='minion_id')
##minion代表是在minion端监听,/var/run/salt/minion是minion端的sock_dir,minion_id是该Minion的id
>>> data = event.get_event()
>>> print(data)
>>> for data in event.iter_events(tag='auth'): ##用迭代器一直查看事件
>>> print(data)
—————–先这样吧 发送事件:
Master:
>>> import salt.utils.event
>>> event = salt.utils.event.SaltEvent('master', '/var/run/salt/minion')
>>> event.fire_event({'hello': 'world'}, 'hello')
—————–先这样
反应系统(reacter system)
反应系统是基于事件系统的,它的作用是当master收到来自minion的特殊事件后就触发某些动作,比如minion上线后发送一个init事件,master收到后,对其应用init的状态文件,minion没有反应系统,事情就是这样的。
配置reactor
1.修改master配置文件或者在/etc/salt/master.d/中建立reactor.conf,内容
reactor:
- 'testtag': ##接收到的tag
- /srv/reactor/start.sls
- /srv/reactor/monitor.sls
- 'test1*tag': ##接收到的tag,支持通配符
- /srv/reactor/other.sls
- 2.建立reactor响应sls文件
/srv/reacter/start.sls文件内容
{% if data['id'] == 'mysql1' %}
delete_file:
cmd .cmd.run:
- tgt: 'G@os:CentOS'
- expr_form: compound
- arg:
- rm -rf /tmp/*
{% endif %}
/srv/reactor/other.sls文件内容
{% if data['data']['state'] == 'refresh' %}
overstate_run:
runner .state.over
{% endif %}
下面来解释一下这两个文件,reacter的sls文件是支持jinja的,所以第一行是通过jinja来判断,reacter的sls支持两个变量data和tag,
data是接受事件的那个字典,tag就是事件的tag,所以第一行的判断就很好理解了,第二行是id,可以随意起,第三行是要运行的执行模块或者runner,如果是执行模块,以cmd.开始,如果是runner则以runner.开始,可执行模块执行需要target,所以-
tat:后面跟的就是可执行模块的target,- expr_form指的target的匹配方式,-
arg是只执行模块函数的参数,runner一般不需要这些。所以第一个示例相当于执行了salt
-C 'G@mysql1' cmd.run 'rm -rf
/tmp/*' salt-run
state.over
十六、salt Mine
salt的用的词都太高明,像Grains,Pillar,Mine真心没一个合适的词去翻译,Mine是做什么的呢?Mine的作用是在静态数据和动态数据建立起一座桥梁(官方文档如是说),Mine从minon收集数据然后发送给Master,并缓存在Master端,所有Minion都可以轻易的共享到,Master通常会维护比较新的数据,如果需要维护长期数据,就要考虑retruner或外部的工作缓存了。 mine的出现是用来解决一定问题的. 在Salt的网络体系中,各个minion是毫无关系,相互独立的. 但是在实际应用中,minion之间其实是有一定关联的,比如一台机器需要获取到另一台机器的一些信息或者执行一些命令. 后来Salt加入了peer系统(http://docs.saltstack.com/ref/peer.html)使其成为可能. 但是peer系统每次使用的时候都会重新执行一遍, 显然很多不常变化的信息重复执行效率较低,性能开销较大. 所以就有了后来的mine(peer和mine的关系是我杜撰出来的,如有雷同,纯属巧合). mine系统存储在master端, minion想获取的时候, 通过mine.get获取下就可以了,简单方便
修改minion配置文件,配置Mine
mine_functions:
network .interfaces: []
test .ping: []
mine_interval : 1
重启Minion,在master端测试
salt '*' mine_get '*' network.interfaces
salt '*' mine_get '*' test.ping
salt 'test1' mine_get '*' test.ping ##查看test1上能得到mine数据
十七、salt ssh
从0.17.0开始salt加入salt ssh,salt ssh不需要在客户端安装salt-minion包了,是通过ssh协议来完成运城命令执行,状态管理等任务的。它是作为master-minion形式的补充出现的,原理是有一个花名册的文件,里面记录了各个minion的信息,ip,账号,密码,等,需要远程执行命令时,直接通过ssh来执行,速度与master-minion形式慢很多。
使用: 1.配置/etc/salt/roster格式如下
test1:
host : 192.168.1.133
user : salt
passwd : redhat
sudo : True
port : 22
timeout : 5
test2:
host : 192.168.1.134
user : root
passwd : redhat
test3:
host : 192.168.1.135
user : sa
sudo : True
说明: test1我们定义了所有常见的选项,test2我们用了超级用户,使用账号密码,test3我们使用普通用户,没有写密码,就是通过key来认证了,并且以sudo方式执行的,需要注意的是1.key认证用的是/etc/salt/pki/master/ssh/目录下的密钥。2.如果普通用户的话,需要有sudo权限,因为一些功能,包括test.ping都需要sudo权限。
测试:
salt-ssh '*' test.ping
salt -ssh '*' -r 'ls /' ##执行shell命令
salt -ssh '*' cmd.run 'ls /' ##用模块来执行也是可以的
salt -ssh '*' state.sls ##执行状态,state.sls在0.71.0中还存在bug,0.72.0中已解决
十八、Returners
默认所有minion返回的值都会发送到master端,我们可以看到,returner就是让Minion把返回的值发给其它地方,如redis,MySQL,或者一个文本下面我们来自定义一个returner:
1.建立自定义returner
mkdir -p /srv/salt/_returners;
vim mysql.py ##就用官方给的例子吧,修改其中mysql的Host,user和pass
内容见https://github.com/saltstack/salt/blob/develop/salt/returners/mysql.py
2.建立需要的数据库
见https://github.com/saltstack/salt/blob/develop/salt/returners/mysql.py注释里的见表语句
3.授权其他主机用户可写该表
>grant all on salt.* to 'user_in_returner'@'%' identified by 'passwd_in_returner';
4.同步
salt '*' saltutil.sync_all ##同步到minion上去
5.测试
salt '*' test.ping --return mysql ##数据返回到mysql上去,打开mysql查看
十九、扩展salt
通过自定义各个模块来扩展salt,常见自定义模块有:
1.可执行模块 Execution Modules
如我们常用的cmd.run , test.ping这样的可执行模块
2.Grains
扩展grains,grains是一些静态信息,可能好多我们需要的没有,我们可以通过编写grains模块自定义grains
3.状态模块 State Module
如我们常用的pkg.install,file.managed
4.Returners
我们可以自定义returner,将返回的数据发送到其他存储,只要符合固定的格式就行了
5.Runner
Runner是在master端快速执行的模块,自定义很方便
二十、自定义可执行模块
所有可执行module见https://github.com/saltstack/salt/tree/develop/salt/modules,或http://docs.saltstack.com/ref/modules/all/index.html?highlight=full list builtin
1.建立自定义模块目录,通常所有自定义模块放在该目录下
mkdir /srv/salt/_modules
2.编写模块
vim test.py
-*- coding: utf-8 -*-
'''
support for yum of RedHat family!
'''
def __virtual__():
'''
Only RedHat family os can use it.
'''
if __grains__.get('os_family', 'unkown') == 'RedHat':
return 'yum'
else:
return False
def install(rpm):
cmd = 'yum -y install {0}'.format(rpm)
ret = __salt__['cmd.run'](cmd)
return ret
说明:__virtual__函数通常用来匹配是否满足该模块的环境,如果满足return出来的字符串作为该模块的名字而不是文件名,如果return的是False代表的此模块无效,不能使用。在自定义模块中可以中__grains__是一个包含了minion
所有grains的字典,__pillar__是包含了所有Pillar的grains字典,__salt__是所有可执行函数对象的字典,通常最常使用的就是这三个变量了。再往下面是定义了函数install,在salt中一般不用’%s’
% var这种格式化形式,而是使用字符串的format方法,具体使用见百度。下面就是通过__salt__执行了cmd.run这个函数来运行yum命令,很简单吧,最后把结果返回回去。
3.测试
salt '*' yum.install ftp ##查看返回值
二十一、自定义grains
自定义的grains也是由Python写成的,通常放在/srv/salt/_grains下,grains需要返回一个字典,__salt__,__grains__,__pillar__也是可以在grains中使用的。前面已经介绍过写简单自定义grains了,复杂就就参照https://github.com/saltstack/salt/blob/develop/salt/grains/core.py官方这个吧
二十二、自定义returner
前面已经看过官方的mysql的returner了,今天来说说自定义returner需要注意的,来个例子吧。 /srv/salt/_returners/file.py内容
def __virtual__():
return 'file'
def returner(ret):
'''
Return information to /tmp/returns.txt.
'''
# open a file
result_file = '/tmp/returns.txt'
f = open(result_file, 'a+')
f .write(str(ret))
f .close()
salt '*' saltutil.sync_all ##同步模块
salt '*' test.ping --return file ##测试
cat /tmp/returns.txt ##在minion上查看
{'jid': '20131227153001246117', 'return': True, 'retcode': 0, 'success': True, 'fun': 'test.ping', 'id': 'test1'}
说明:通过这个简单的例子我们了解返回的值是个字典,字典包括的项就是上面我们看到的,以后写其它returner时,也就是把这个字典的值写到不同的地方而已。这个returner的意思就是把返回的值写到各个minion的/tmp/returns.txt中。
二十三、file state backup
来例子看看吧。 /srv/salt/test.sls文件内容
/tmp/test.txt:
file .managed:
- source: salt://test.txt
- backup: minion
其中多了一个参数backup,后面跟的值minion,意思是说这个文件在minion中备份一份,文件名带着时间戳,备份位置在/var/cache/salt/minion/file_backup
执行并测试:
salt '*' state.sls test ##注,多修改几次test.txt,多运行几次该state
salt '*' file.list_backups /tmp/test.txt ##这是会返回备份序号,时间,位置,大小
回退 当文件改错后,我们可以用备份文件回退
salt '*' file.restore_backup /tmp/test.txt 2 ##回退的文件,与想要回退的序列号
删除 删除不需要的备份文件
salt '*' file.delete_backup /tmp/test.txt 3
二十四、应用实例
saltstack 远程触发文件备份、回滚
1、创建模块方法文件
mkdir /srv/salt/_modules
默认没有此文件,自己生成一个
下面的py文件自己定义,下面是我写的两个方法:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys,string,shutil
import os,tarfile
import datetime,time
tn=datetime.datetime.today()
time_now=tn.strftime("%Y-%m-%d")
data_bak='/data/databak'
data_tmp='/data/databak/tmp/%s' % time_now
com_f="%s/%s" % (data_bak,time_now)
if not os.path.exists(com_f):
os .makedirs(com_f)
def CpFile():
id = sys.argv[1]
dir = sys.argv[2] #传入两个变量,任务自动生成的id与要替换的文件
filename = '%s/%s.tar' % (com_f, id)
mode = 'w:tar'
os .chdir(data_bak)
w_file =open("/tmp/tmp.list",'w')
w_file .write(id+" "+dir) #记录每次备份的id与相对应的备份目录或文件的路径
w_file .close()
file = tarfile.open( filename, mode )
file .add( '/tmp/tmp.list' )
file .add( dir )
file .close()
return 'ok' #测试过程,就先让返回ok吧,之后再做判断
def RollBack():
id = sys.argv[1] #想要回滚到的版本id
if not os.path.exists(data_tmp):
os .makedirs(data_tmp)
filename = '%s/%s.tar' % (com_f, id)
tar = tarfile.open("%s" % filename)
for b in tar:
tar .extract(b,path="%s" % data_tmp)
tar .close()
for line in open('%s/tmp/tmp.list' % data_tmp):
id = line.split(" ")[:1][0]
dir = line.split(" ")[1:][0] #读取备份时的路径
backup_dir ='%s/%s' % (data_tmp,dir)
os .system('\cp -rp %s %s' % (backup_dir,dir))
return 'ok'
2、测试:
master上同步方法脚本到节点
salt '*' saltutil.sync_all
然后先测试备份 方法
saltstack
上节点服务器上查看,存在
把/tmp/test下内容删除,测试回滚操作
salt '*' cp_bakfile.RollBack 1234
使用gitfs做fileserver
用gitfs后,master会从git服务器取回文件缓存,minion不会直接联系git服务器修改master配置文件/etc/salt/master(注:以后说master配置文件就是指该文件)
fileserver_backend:
- git
gitfs_remotes:
- git://github.com/saltstack/saltstack.git
- git://github.com/example/test1.git ##可以多个git
- file:///root/td ##可以使用本地git
需要:python的模块GitPython >= 0.3.0
saltstack的目录结构
.
|-- bfile
|-- edit
| `-- vim.sls
`-- top.sls
vim.sls
/tmp/a.txt:
file .managed:
- source: salt://bfile
ftp:
pkg :
- installed
也可以使用git下的子目录作为文件服务器根目录
gitfs_root: somefolder/otherfolder
也可以混合使用git和本地磁盘作为文件服务器
fileserver_backend:
- roots
- git
-
使用ssh协议的GitFS,私钥为~/.ssh/id_rsa
gitfs_remotes:
- git+ssh://git@github.com/example/salt-states.git

加载中…