nodegroup
複雜的sls
自定義動態garins
salt的拓展
salt的用戶認證管理
minion信息的集中獲取
自定義salt modules
nodegroup
salt的命令管理在對批量的機器進行操作(如果是單個的機器進行命令操作,ssh是最直接的方法)的時候才能更顯示出他的部分強大。有時候我們通過target進行各種匹配,雖然可以寫的很強大,強大到我們可以匹配出任何的滿足我們需求的節點,但是寫這個target的時候,如果過於複雜就要花費稍微長點的時間,所以在這個時候nodegroup可以很滿足我們的需求,但是呢,直接寫這個group分組也是很麻煩的,有沒有更好的方法呢?
前提:master支持部分配置的動態加載,比如nodegroup,實現的方式是動態的讀取/etc/salt/master.d/*.conf內容,我們只要去更新nodegroup中的內容就可以了
我這裏有多個用戶都會去操作(每個用戶管理的salt的機器不一樣)salt,而salt-master同一個配置只能加載一次,所以我只能去維護一個定義了nodegroup文件
實現方式:
每個salt用戶(在salt服務器上也是一個用戶:普通用戶)的~/groups文件夾中定義了一個個文件,每個文件有一堆的minion ID列表,然後寫個腳本去讀取(~/groups)文件夾中的所有文件,然後生產跟文件名對應的group名
如下:
# ls ~/groups/
test1.txt test2.txt test3.txt
然後執行如下命令: (這個命令爲自己實現)
[halfss@salt ~]# opstack update_groups
組更新完畢
組生成成功後:
[halfss@salt ~]$ salt -N test1 test.ping
minion1:
True
minion2:
True
minion3:
True
update_groups的代碼大概(我線上部分調整後直接粘貼過來,未測試)如下:
def update_groups():
file_dir = '%s/groups' % os.path.expandvars('$HOME')
groups_re = '#%s_start\n.*\n#%s_end\n' % (user,user)
groups = ''
for group_file in os.listdir(file_dir):
if group_file.split('.')[-1] != 'txt':
continue
group_file = '%s/%s' % (file_dir,group_file)
servers = [ server[:1] for server file(group_file).readlines()]
groups += " %s: L@%s\n" % (group_file.split('/')[-1].split('.')[0],','.join(servers))
groups_tmp = '#%s_start\n%s#%s_end\n' % (user,groups,user)
nodegroups = file('/etc/salt/master.d/nodegroups.conf','r').read()
nodegroups,re_count = re.subn(r'%s' % groups_re, groups_tmp,node groups
if re_count == 0:
nodegroups += groups_tmp
file('/etc/salt/master.d/nodegroups.conf','w').write(nodegroups)
print "組更新完畢")
複雜的sls
有些時候默認的提供的sls的語法並不能滿足實際需求,好在靈活強大的salt已經支持sls拓展(詳情可以訪問:http://docs.saltstack.com/topics/tutorials/starting_states.html )
可以直接寫python代碼,只要返回值類似yaml風格一樣東西就OK
比如我要對節點的hosts中的某個域名做管理,找最近的IP去解析
實例如下:
import os
import re
def run():
hosts = [ #這裏的IP是模擬IP
"192.168.1.2",
"10.0.0.1",
]
hosts_time = {}
for host in hosts:
cmd = "ping -c 4 %s" % host
content = os.popen(cmd).read()
use_time = re.findall(r'time=(.*)ms',content)
hosts_time[host] = sum([float(u) for u in use_time])
hosts_time = sorted(hosts_time.items(),key=lambda hosts_time:hosts_time[1])
ip = hosts_time[0][0]
dict = {
'download':{'host.present':[{'ip':ip,'names':['download.cn']}]}
}
return dict
salt會用yaml去解析返回的這個字典
自定義動態garins
salt中自定義的minion ID,一般遵守fqdn規範,以儘可能他提供更多的信息方便管理員進行管理,但是fqdn不是萬能的,不一定能包含需要的所有信息,這個時候自定義的grains就有用了
這裏自定義了個grain,會根據一個URL返回的值生產一個字典,返回給salt解析
/srv/salt/_grains/ops_user.py
import urllib2
def ops_user():
grains = {}
ops_user = urllib2.urlopen('https://test.com/api/opsuser').read() #這裏放回的是一個以逗號分割的字符串
ops_user = ops_user.split(',')
grains['ops_user'] = ops_user
return grains
if __name__ == '__main__':
print ops_user()
然後同步grains,之後所有的minion都會有和這個grain的屬性了 saltutil.sync_grains
不過這裏有一個小問題,這個granis是靜態值,除非指定節點去刷新,否則grains不會改變
salt的拓展
salt的master和minion的交互很大程度上都和網絡有關係,比如在管理多個國家的機器的時候(比如大中華局域網),這個時候,用一個master來管理,先不說體驗上的問題,本身就是不現實的,這個時候怎麼搞呢? 分佈式
一個master控制多個master,同時被控制的master又可以控制很多的minion
這個時候咱們的問題就好處理的多了,當然不能說完全沒有問題
中心master
指定開啓syndic模式,這樣消息才能發送到syndic節點上
# grep order_masters /etc/salt/master
order_masters: True
指定爲中心master節點,啓動syndic服務
被管理的master
# grep syndic_master /etc/salt/master
syndic_master: salt.lightcloud.cn
/etc/init.d/salt-syndic start
比如總的master爲master,syndic節點爲syndic1
將minion1的master制定爲syndic,啓動minion服務
然後在syndic1節點就可以看到未接受的key,接受後,syndic就可以管理minion1了,同時master也可以管理minion1了
問題:key的管理這塊,還是僅僅minoin直接連接的節點纔可以管理,也就是說剛纔minion1的接受key的那個操作,只有在syndic1纔可以完成,master是不行的
salt的用戶認證管理
在salt服務器上可以用root來管理所有的minions,使用所有的功能,但是實際生產環境中,機器有很多,不是所有的人都要管理這些機器,就需要把這些機器分給不同的用戶進行管理,這裏可以使用salt的external_auth模塊來做處理
官方文檔:http://docs.saltstack.com/topics/eauth/index.html
官方的例子中寫的很清晰,比如master配置文件中如下的配置
external_auth: #制定啓用認證模塊
pam: #指定所使用的認證模塊,還有其他的認證模塊可以使用比如ldap
thatch: #指定用戶名(master服務器的系統用戶名)
- 'web*': #指定匹配的minion 這裏有點操蛋的是,不能使用compund模式
- test.* #這裏指定了可以使用那些模塊,後面是並列的
- network.*
steve:
- .*
這裏的這個用戶thatch,可以對minion id中以web開頭的使用test和network模塊的所有功能,而steve這個用戶就NB了,可以管理所有的minion,而且可以使用說有的功能
如果在長期業務固化的系統中,這樣的設定本來沒什麼問題,但是在業務快速迭代的系統中,業務會老是變來變去業務的負責人也同樣會變來編曲,但是業務的主機名不會經常變化,這樣的設定就會有問題,個人認爲最好的解決方案應該是基於minion的某些屬性來設定權限(可以動態的去管理這些屬性);這樣在業務變化的時候讓這些屬性也動態的去變化,權限也就動態的變化了
可是默認的salt不支持這樣的功能(已經跟官方反饋,個人認爲這個功能在不久的將來會加上);自己也不能幹等着,於是我就個所有的minion加另一個ops_user的屬性(方法參考: 自定義動態garins),這裏定義完了,怎麼用呢?調整external的用戶認證如下:
external_auth:
pam:
halfss:
- '*':
- '*'
halfss1:
- '*':
- '*'
這裏我們看到了,我給了這2個用戶halfss halfss1所有機器的所有權限,如果這樣設置的是,基本上對minion的權限管理是廢了,但是還有一步,調整下salt的一段代碼,如下:
調整用戶權限:
/usr/lib/python2.6/site-packages/salt
diff client.py client.py_back
969,971d968
< if self.salt_user != 'root':
< tgt = '%s and G@ops_user:%s' % (tgt,self.salt_user)
< expr_form = 'compound'
979a977
>
這樣普通用戶即使在執行 salt '*' test.ping 的時候也會成功,而且僅僅是有他權限的機器執行,這樣我就完成了對minion動態的分配權限.而且還帶來一個好處是,普通用戶的體驗會更好一些,在官方的代碼中,如果普通用戶沒有所有機器的權限,那麼他直接這樣執行是會報錯的,官方代碼中(即使是普通用戶),"*" 理解爲salt-master中的所有minion,而不是改用戶的所有minon(這個跟他的廣播機制有關) 這個功能也已經跟官方反饋,他會在0.16中實現這個功能
minion信息的集中獲取
master默認會將minion是信息(pillar和grains)存儲在/var/cache/salt/master/minions/下(以minoin id創建一個目錄,該目錄下有個data.p的文件);這樣的方式並不便於minoin信息是採集與管理(如果有很多的機器,然後獲取所有機器的minion信息的時慢的要死,當然這個不能怪salt);我們可以把這些信息都放到一個文件中,便於信息的採集與管理,這裏提供對信息統一收集的基礎代碼,如下:
獲取minion的grain及pillar
/usr/lib/python2.6/site-packages/salt/master.py
cdir = os.path.join(self.opts['cachedir'], 'minions', load['id'])
if not os.path.isdir(cdir):
os.makedirs(cdir)
datap = os.path.join(cdir, 'data.p')
+ file('/var/log/salt/minions','w+').write(str({
+ 'minion_id':load['id'],
+ 'grains': load['grains'],
+ 'pillar': data})+'\n')
with salt.utils.fopen(datap, 'w+') as fp_:
fp_.write(
self.serial.dumps(
{'grains': load['grains'],
自定義salt modules
salt中自定義modules,實在是太簡單了,爲了讓你詳細,先來個最簡單的
# cat /srv/salt/_modules/custom.py
def test():
return 'i am test'
同步到所有minion
# salt '*' saltutil.sync_modules
直接就可以使用了
[root@localhost _modules]# salt '*' custom.test #調用方法,文件名.方法名
minion1:
i am test
這個是最簡單的;但是有時候,我們需要實現一些比較複雜的功能,而這些功能有的salt已經幫我們實現了,我們僅僅需要直接拿來用就好了;還有的我們需要使用minion的中grains或者pillar的信息;在有其他的功能,我們就需要自己是實現了,先看看剛纔的2個怎麼搞
1. 調用先有的module來顯現自定義module中需要的功能
salt salt內置的一個字典,包含了所有的salt的moudle
[root@localhost _modules]# cat /srv/salt/_modules/custom.py
def test(cmd):
return __salt__['cmd.run'](cmd)
[root@localhost _modules]# salt '*' custom.test ls
minion1:
'
anaconda-ks.cfg
install.log
install.log.syslog
match.py
salt
test.py
是不是有點想想不到的簡單?
2. 使用gains中信息
[root@localhost _modules]# cat /srv/salt/_modules/custom.py
def test():
return __grains__['id']
[root@localhost _modules]# salt '*' custom.test
minion1:
minion1
將自定義的modules文件放在配置文件中定義的file_roots(默認爲/srv/salt)下的 _modules目錄下,會在執行highstate的時候自動同步,或者按照如下方式,手工推送
salt '*' saltutil.sync_modules 或者 salt '*' saltutil.sync_all