Saltstack相關使用

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章