saltstack實戰2--遠程執行之返回(returner)


saltstack有3大功能:遠程執行,配置管理,雲管理

其中遠程執行又可分解為:目標,模塊,返回  這3個部分。

比如下面語句

[root@master ~]# salt '*'  test.ping
minion01:
    True
minion02:
    True
[root@master ~]# salt '*'  cmd.run 'df -h'
minion02:
    Filesystem      Size  Used Avail Use% Mounted on
    /dev/sda3        35G  4.2G   30G  13% /
    tmpfs           2.0G   12K  2.0G   1% /dev/shm
    /dev/sda1       380M   33M  327M  10% /boot
minion01:
    Filesystem      Size  Used Avail Use% Mounted on
    /dev/sda3        35G  4.2G   30G  13% /
    tmpfs           2.0G   12K  2.0G   1% /dev/shm
    /dev/sda1       380M   33M  327M  10% /boot
[root@master ~]# 

其中'*' 是目標,匹配所有已經認證的minion,test.ping 和cmd.run  是模塊中的方法,而下面的結果屬於返回

今天先講一下“返回”

返回,英文名稱,returner,默認情況下,發送給salt-minion的命令執行結果返回給salt master。其實saltstack returner的接口允許將結果發送給任意系統,比如:mysql,redis,syslog。

--return  syslog  表示minion將結果返回給自己的syslog

[root@master ~]# salt '*'  cmd.run 'df -h' --return syslog
minion01:
    Filesystem      Size  Used Avail Use% Mounted on
    /dev/sda3        35G  4.2G   30G  13% /
    tmpfs           2.0G   12K  2.0G   1% /dev/shm
    /dev/sda1       380M   33M  327M  10% /boot
minion02:
    Filesystem      Size  Used Avail Use% Mounted on
    /dev/sda3        35G  4.2G   30G  13% /
    tmpfs           2.0G   12K  2.0G   1% /dev/shm
    /dev/sda1       380M   33M  327M  10% /boot
[root@master ~]# 

 

下面查看minion的系統日志,可以看到上面返回的結果 

[root@minion02 ~]# tail -f /var/log/messages
Dec 25 03:36:01 minion02 rsyslogd: [origin software="rsyslogd" swVersion="5.8.10" x-pid="1802" x-info="http://www.rsyslog.com"] rsyslogd was HUPed
Dec 25 13:49:23 minion02 python2.6: {"fun_args": ["df -h"], "jid": "20161225134925942528", "return": "Filesystem      Size  Used Avail Use% Mounted on\n/dev/sda3        35G  4.2G   30G  13% /\ntmpfs   
2.0G 12K 2.0G 1% /dev/shm\n/dev/sda1 380M 33M 327M 10% /boot", "retcode": 0, "success": true, "fun": "cmd.run", "id": "minion02"}

其中

jid,你可以把它簡單理解為一個任務編號

return:Filesystem     # 表示返回到文件系統里,也就是系統日志里

retcode:0              #表示返回結果狀態碼

success:true       #成功與否

fun:cmd.run      #執行的函數模塊

id:minion02        #minion的id

這個返回的日志存儲在了minion的系統日志上,而不是master的系統日志上

  

假如,我們想讓minion把返回的結果直接存到mysql里面,然后進行二次開發,從mysql庫里找出哪些minion執行成功,哪些執行失敗

工作中很少讓minion直接存到mysql了,因為如果minion機器比較多,並發連接過多很可能導致mysql宕機,這里我們這樣做。出於測試目的,而對於少量的機器也可以采用這種方式
由於minion要連接mysql,需要安裝python-mysqldb模塊

 操作步驟如下

1、准備一台mysql機器

2、mysql服務器建庫建表

3、所有minion安裝mysql-python模塊

4、minion更改配置文件,連接mysql

5、master發送命令測試 

 

 這里使用第4台機器,IP地址為10.0.1.174作為mysql服務器

[root@mysql ~]# rpm -qa|grep mysql
mysql-libs-5.1.73-3.el6_5.x86_64
[root@mysql ~]# yum install mysql mysql-server -y

  

 啟動mysql服務,更改root密碼

[root@mysql ~]# /etc/init.d/mysqld start
Initializing MySQL database:  WARNING: The host 'mysql' could not be looked up with resolveip.
This probably means that your libc libraries are not 100 % compatible
with this binary MySQL version. The MySQL daemon, mysqld, should work
normally with the exception that host name resolving will not work.
This means that you should use IP addresses instead of hostnames
when specifying MySQL privileges !
Installing MySQL system tables...
OK
Filling help tables...
OK

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
To do so, start the server, then issue the following commands:

/usr/bin/mysqladmin -u root password 'new-password'
/usr/bin/mysqladmin -u root -h mysql password 'new-password'

Alternatively you can run:
/usr/bin/mysql_secure_installation

which will also give you the option of removing the test
databases and anonymous user created by default.  This is
strongly recommended for production servers.

See the manual for more instructions.

You can start the MySQL daemon with:
cd /usr ; /usr/bin/mysqld_safe &

You can test the MySQL daemon with mysql-test-run.pl
cd /usr/mysql-test ; perl mysql-test-run.pl

Please report any problems with the /usr/bin/mysqlbug script!

                                                           [  OK  ]
Starting mysqld:                                           [  OK  ]
[root@mysql ~]#  /usr/bin/mysqladmin -u  root  password   '123456'
[root@mysql ~]# 

  

 開始給saltstack建庫建表

[root@mysql ~]# mysql -uroot -p123456
1.創建庫名
CREATE DATABASE  `salt`
  DEFAULT CHARACTER SET utf8
  DEFAULT COLLATE utf8_general_ci;

  

2.創建jid表
USE `salt`;


DROP TABLE IF EXISTS `jids`;
CREATE TABLE `jids` (
  `jid` varchar(255) NOT NULL,
  `load` mediumtext NOT NULL,
  UNIQUE KEY `jid` (`jid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  

3.創建return表 存放返回的數據
DROP TABLE IF EXISTS `salt_returns`;
CREATE TABLE `salt_returns` (
  `fun` varchar(50) NOT NULL,
  `jid` varchar(255) NOT NULL,
  `return` mediumtext NOT NULL,
  `id` varchar(255) NOT NULL,
  `success` varchar(10) NOT NULL,
  `full_ret` mediumtext NOT NULL,
  `alter_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  KEY `id` (`id`),
  KEY `jid` (`jid`),
  KEY `fun` (`fun`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
4.創建事件表
DROP TABLE IF EXISTS `salt_events`;
CREATE TABLE `salt_events` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`tag` varchar(255) NOT NULL,
`data` mediumtext NOT NULL,
`alter_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`master_id` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `tag` (`tag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
5.授權訪問
grant all on salt.* to salt@'%' identified by 'salt123';
flush  privileges;

  

minion端安裝MySQL-python模塊
yum install MySQL-python
原理是minion把數據直接推到mysql的數據庫中
在master上給操作所有minion安裝此模塊,執行如下命令
salt '*'  cmd.run  'yum install MySQL-python -y'

  

2、minion端修改配置文件(master上不需要修改)
# vi /etc/salt/minion 在配置文件后面追加以下內容
mysql.host: '10.0.1.174'
mysql.user: 'salt'
mysql.pass: 'salt123'
mysql.db: 'salt'
mysql.port: 3306

先備份原先的配置

[root@master ~]# salt '*'  cmd.run  '/bin/cp /etc/salt/minion  /etc/salt/minion.01'
minion01:
minion02:
[root@master ~]# 

  

然后登錄到minion機器上修改文件,執行如下命令

cat >> /etc/salt/minion <<EOF
mysql.host: '10.0.1.174'
mysql.user: 'salt'
mysql.pass: 'salt123'
mysql.db: 'salt'
mysql.port: 3306
EOF

檢查確認下

[root@minion01 ~]# tail -6 /etc/salt/minion
#return: mysql
mysql.host: '10.0.1.174'
mysql.user: 'salt'
mysql.pass: 'salt123'
mysql.db: 'salt'
mysql.port: 3306
[root@minion01 ~]# 

對其余minion也同樣操作  

修改完畢后,重啟minion服務

重啟方法有兩種
1、使用命令重啟
salt   '*' cmd.run  '/etc/init.d/salt-minion restart
2、使用模塊重啟
salt   '*' service.restart  salt-minion

我們采用第二種重啟

[root@master ~]# salt   '*' service.restart  salt-minion
minion01:
    True
minion02:
    True
[root@master ~]# 

  

接下來測試是否返回給了mysql

先在master上執行命令,加入--return  mysql參數

[root@master ~]# salt   '*'  test.ping --return mysql
minion02:
    True
minion01:
    True
[root@master ~]# 

然后在mysql上查看結果

可以看到執行了test.ping 這個模塊

jid編號是以當前時間格式命名的

return:true 表示返回成功

等一些列信息

mysql> select * from salt_returns\G;
*************************** 1. row ***************************
       fun: test.ping
       jid: 20161225144720565109
    return: true
        id: minion02
   success: 1
  full_ret: {"fun_args": [], "jid": "20161225144720565109", "return": true, "retcode": 0, "success": true, "fun": "test.ping", "id": "minion02"}
alter_time: 2016-12-25 14:47:18
*************************** 2. row ***************************
       fun: test.ping
       jid: 20161225144720565109
    return: true
        id: minion01
   success: 1
  full_ret: {"fun_args": [], "jid": "20161225144720565109", "return": true, "retcode": 0, "success": true, "fun": "test.ping", "id": "minion01"}
alter_time: 2016-12-25 14:47:18
2 rows in set (0.00 sec)

ERROR: 
No query specified

mysql> 

  

以下寫法是等價的

[root@master ~]# salt   '*'  test.ping --return mysql
minion02:
    True
minion01:
    True
[root@master ~]# salt   '*'  test.ping --return=mysql
minion01:
    True
minion02:
    True
[root@master ~]# 

如果你不想每次加參數--return mysql   讓它自動返回到mysql,可以修改master的配置文件,取消return: mysql這一行的注釋,然后重啟master服務

minion端的配置文件也有return: mysql這一行,經過我的測試,取消它的注釋是沒用的

[root@master ~]# tail -2 /etc/salt/master 
# Which returner(s) will be used for minion's result:
return: mysql
[root@master ~]# 

  

 

第二種結構,master把結果返回給mysql ,如下顯示

這里我們使用到了salt自帶的event,下面一串是搜索到的,看了也是似懂非懂。

event是一個本地的ZeroMQ PUB Interface,event是一個開放的系統,用於發送信息通知salt或其他的操作系統。每個event都有一個標簽。事件標簽允許快速制定過濾事件。除了標簽之外,每個事件都有一個數據結構。這個數據結構是一個dict類型,其中包含關於事件的信息。

本來,minion發結果發給master上的event,然后event再通知salt,然后salt在把結果顯示在終端
今天我們要做的就是直接從event里取數據,然后把結果寫到mysql里

 

 

event接口測試

先測試下能否從event里取出數據,簡單些一個取數據的python腳本,內容如下

[root@master master]# mkdir /scripts
[root@master master]# cd /scripts/
[root@master scripts]# vim salt-event.py
[root@master scripts]# cat salt-event.py 
import salt.utils.event
event = salt.utils.event.MasterEvent('/var/run/salt/master')
for data in event.iter_events(full=True):
	print data
	print '------'
[root@master scripts]# 

  

執行這個腳本

[root@master scripts]# python salt-event.py 

新打開一個窗口,使用salt簡單執行一個命令

[root@master salt]# salt   '*'  test.ping 
minion01:
    True
minion02:
    True
[root@master salt]# 

剛才窗口可以看到返回的結果,它是一個python字典形式的數據。

[root@master scripts]# python salt-event.py 
{'tag': 'salt/event/new_client', 'data': {'_stamp': '2016-12-25T07:35:17.261403'}}
------
{'tag': '20161225153517270106', 'data': {'_stamp': '2016-12-25T07:35:17.270256', 'minions': ['minion01', 'minion02']}}
------
{'tag': 'salt/job/20161225153517270106/new', 'data': {'tgt_type': 'glob', 'jid': '20161225153517270106', 'tgt': '*', '_stamp': '2016-12-25T07:35:17.270458', 'user': 'root', 
'arg': [], 'fun': 'test.ping', 'minions': ['minion01', 'minion02']}} ------ {'tag': 'salt/job/20161225153517270106/ret/minion01', 'data': {'fun_args': [], 'jid': '20161225153517270106', 'return': True, 'retcode': 0, 'success': True, 'cmd': '_return',
'_stamp': '2016-12-25T07:35:17.412322', 'fun': 'test.ping', 'id': 'minion01'}} ------ {'tag': 'salt/job/20161225153517270106/ret/minion02', 'data': {'fun_args': [], 'jid': '20161225153517270106', 'return': True, 'retcode': 0, 'success': True, 'cmd': '_return',
'_stamp': '2016-12-25T07:35:17.412489', 'fun': 'test.ping', 'id': 'minion02'}} ------

上面能從event取到數據,那么我們就能把數據取到之后存入mysql里

  

 

以上測試沒問題后,需要如下操作

為了避免minion直接返回到mysql的干擾,我們把minion端配置文件里連接mysql的信息注釋掉,同時也要把master端口return: mysql注釋掉。分別重啟服務

1、mysql需要建庫建表,由於上面的演示已經建庫建表完成了,這里就不需要了,但是可以把之前表里的數據清空了

2、因為master要連接數據庫了,因此需要安裝MySQL-python模塊

3、master端寫一個從event把數據存入mysql的腳本

4、后台執行此腳本

5、測試

 

master端安裝配置文件

[root@master salt]# yum -y install MySQL-python 
Loaded plugins: fastestmirror, refresh-packagekit, security
Setting up Install Process
Loading mirror speeds from cached hostfile
 * base: centos.ustc.edu.cn
 * epel: mirrors.tuna.tsinghua.edu.cn
 * extras: centos.ustc.edu.cn
 * updates: centos.ustc.edu.cn
Package MySQL-python-1.2.3-0.3.c1.1.el6.x86_64 already installed and latest version
Nothing to do
[root@master salt]# 

master端編寫腳本

[root@master salt]# cd /scripts/
[root@master scripts]# ls
salt-event.py
[root@master scripts]# vim salt_event_to_mysql.py
[root@master scripts]# cat salt_event_to_mysql.py 
#!/bin/env python
#coding=utf8
# Import python libs
import json
# Import salt modules
import salt.config
import salt.utils.event
# Import third part libs
import MySQLdb
__opts__ = salt.config.client_config('/etc/salt/master')
#create MySQL connect
#conn = MySQLdb.connect(host=__opts__['mysql.host'],user=__opts__['mysql.user'],passwd=__opts__['mysql.pass'],db=__opts__['mysql.db'],port=__opts__['mysql.port'])

conn = MySQLdb.connect(host='10.0.1.174',user='salt',passwd='salt123',db='salt',port=3306)
cursor = conn.cursor()
# Listen Salt Master Event System
event = salt.utils.event.MasterEvent(__opts__['sock_dir'])
for eachevent in event.iter_events(full=True):
    ret = eachevent['data']
    if "salt/job/" in eachevent['tag']:
        #Return Event
        if ret.has_key('id') and ret.has_key('return'):
            #Ignore saltutil.find_job event
            if ret['fun'] == "saltutil.find_job":
                continue
            sql = '''INSERT INTO `salt_returns`
                (`fun`,`jid`,`return`,`id`,`success`,`full_ret` )
                VALUES (%s,%s,%s,%s,%s,%s)'''
            cursor.execute(sql,(ret['fun'],ret['jid'],
                                json.dumps(ret['return']),ret['id'],
                                ret['success'],json.dumps(ret)))
            cursor.execute("COMMIT")
    # Other Event
    else:
        pass
[root@master scripts]# 

  

master上后端執行此腳本

[root@master scripts]# python salt_event_to_mysql.py &
[1] 30810
[root@master scripts]# 

master上執行測試操作

[root@master scripts]# salt   '*'  test.ping 
minion02:
    True
minion01:
    True
[root@master scripts]# 

mysql上查看有新的數據,原先表還是空表。測試之后有了數據

mysql> select * from salt_returns\G;
Empty set (0.00 sec)

ERROR: 
No query specified

mysql> select * from salt_returns\G;
*************************** 1. row ***************************
       fun: test.ping
       jid: 20161225160554520788
    return: true
        id: minion01
   success: 1
  full_ret: {"fun_args": [], "jid": "20161225160554520788", "return": true, "retcode": 0, "success": true, "cmd": "_return", "_stamp": "2016-12-25T08:05:54.705395", "fun": "test.ping", "id": "minion01"}
alter_time: 2016-12-25 16:05:52
*************************** 2. row ***************************
       fun: test.ping
       jid: 20161225160554520788
    return: true
        id: minion02
   success: 1
  full_ret: {"fun_args": [], "jid": "20161225160554520788", "return": true, "retcode": 0, "success": true, "cmd": "_return", "_stamp": "2016-12-25T08:05:54.708870", "fun": "test.ping", "id": "minion02"}
alter_time: 2016-12-25 16:05:52
2 rows in set (0.00 sec)

ERROR: 
No query specified

mysql> 

 

 注意點:

1、看到一些文章提到還要在master的配置文件里添加下面幾行,但是上面我沒有添加,mysql也通過python腳本獲取數據了

master配置文件不需要添加以下

mysql.host: '10.0.1.174'
mysql.user: 'salt'
mysql.pass: 'salt123'
mysql.db: 'salt'
mysql.port: 3306

 

2、下面報錯就是沒安裝MySQL-python模塊導致的

yum -y install MySQL-python

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM