saltstack中job缓存、salt-ssh、salt-syndic与salt-api
前言
server1是saltstack的master端,server2和server3是minion端
1、job
master在下发指令任务时,会附带上产生的jid。minion在接收到指令开始执行时,会在本地的/var/cache/salt/minion/proc目录下产生该jid命名的文件,用于在执行过程中master查看当前任务的执行情况。指令执行完毕将结果传送给master后,删除该临时文件。Job缓存默认保存24小时,有时为了留底,需要把job存到数据库中,分为两种方式
(1)、minion端存入数据库
工作机制如下图,minion端在返回数据给master时,也会给数据库一份
首先在server1中安装并开启mariadb-service,安全初始化mysql_secure_installation,进入数据库,需要执行创建库和三个表,为了看得清楚,写在了下面的jobs.sql文件中,编写完成后导入数据库即可。
CREATE DATABASE `salt` #创建库
DEFAULT CHARACTER SET utf8
DEFAULT COLLATE utf8_general_ci;
USE `salt`;
--
-- Table structure for table `jids`
--
DROP TABLE IF EXISTS `jids`;
CREATE TABLE `jids` ( #创建jid表
`jid` varchar(255) NOT NULL,
`load` mediumtext NOT NULL,
UNIQUE KEY `jid` (`jid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#CREATE INDEX jid ON jids(jid) USING BTREE;
--
-- Table structure for table `salt_returns`
--
DROP TABLE IF EXISTS `salt_returns`;
CREATE TABLE `salt_returns` ( #创建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;
--
-- Table structure for table `salt_events`
--
DROP TABLE IF EXISTS `salt_events`;
CREATE TABLE `salt_events` ( #创建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;
mysql -pwestos < jobs.sql
将测试文件导入mysql,进入数据库,授权salt用户远程操作salt.*表的所有操作
修改server2(minion端)的配置文件/etc/slat/minion,重启minion端
安装MySQL-python.x86_64 0:1.2.5-1.el7。注意:写入的语句是python语言,谁写入数据库谁就需要安装MySQL-python.x86_64 0:1.2.5-1.el7
salt server2 test.ping --return mysql
和salt server2 my_disk.df--return mysql
,进行测试
返回server1看数据库中是否存在刚才的测试信息,如下,配置成功
(2)、master端存入数据库
工作机制如下图,minion端正常返回数据给master端,master端发给数据库。好处是无需到每个minion端修改策略,只需要在master端设置即可。
修改配置文件/etc/slat/master,如下
进入数据库,授权本地用户执行salt.*表的所有操作,重启salt-master,安装MySQL-python
salt ‘*’ test.ping
,进行测试
成功存入数据库
2、salt-ssh
假如有些主机无法安装minion端,那该怎么办呢?比如server3关掉minion端。
可以使用salt-ssh的方式,salt-ssh可以独立运行的,不需要minion端。salt-ssh 用的是sshpass进行密码交互的。Ssh方式是串行模式,性能下降,所以只是补充,不是主流,主要还是minion。
在master端yum install -y salt-ssh.noarch,修改配置文件/etc/salt/roster
master端连接,就可以找到配置文件中写的server3,成功
3、salt-syndic
syndic其实就是个代理,隔离master与minion。Syndic必须要运行在master上,再连接到另一个topmaster上,Syndic是可以理解为秘书,替top master管理master。Topmaster 下发的状态需要通过syndic来传递给下级master,反过来minion传递给master的数据也是由syndic传递给topmaster,意味着topmaster并不知道有多少个minion。示意图如下
server1还是master,现在新建一个server4来做顶级master。和前面一样需要给server4配置yum源,安装salt-master。
server4修改配置文件/etc/salt/master,重启salt-master,现在server4就是topmaster了
Server1安装yum install -y salt-syndic.noarch,vim /etc/salt/master修改配置文件,告诉server1,server4是它的topmaster,重启systemctl restart salt-master.service
,开启systemctl start salt-syndic.service
Server4,salt-key -L查看,salt-key -A添加,topmaster成功捕捉到master,看不到minion
salt '*' test.ping
,测试成功(注意:上个ssh实验关掉了server3的minion,现在要开启,否则server3不成功)
4、salt-api
SaltStack 官方提供有REST API格式的 salt-api 项目,将使Salt与第三方系统集成变得尤为简单。可以使用python编写
Server1 安装yum install salt-api.noarch,cd /etc/pki/tls/private ,创建密钥openssl genrsa 1024 > localhost.key
cd /etc/pki/tls/certs,创建证书make testcert
Vim /etc/salt/master.d/api.conf创建api配置文件
创建/etc/salt/master.d/auth.conf认证文件,使用pam策略,对saltapi用户的权限全开
[root@server1 master.d]# useradd saltapi #创建用户
[root@server1 master.d]# echo westos | passwd --stdin saltapi #设密码
[root@server1 master.d]# systemctl restart salt-master.service
[root@server1 master.d]# systemctl start salt-api.service
[root@server1 master.d]# netstat -antlp | grep 8000 #成功看到api的8000端口
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 30474/salt-api
注册产生token,然后test测试,连接成功
之后就可以自己用python开发特殊需要的函数。比如编写saltapi.py文件,内容如下,
# -*- coding: utf-8 -*-
import urllib2,urllib
import time
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
try:
import json
except ImportError:
import simplejson as json
class SaltAPI(object):
__token_id = ''
def __init__(self,url,username,password):
self.__url = url.rstrip('/')
self.__user = username
self.__password = password
def token_id(self):
''' user login and get token id '''
params = {'eauth': 'pam', 'username': self.__user, 'password': self.__password}
encode = urllib.urlencode(params)
obj = urllib.unquote(encode)
content = self.postRequest(obj,prefix='/login')
try:
self.__token_id = content['return'][0]['token']
except KeyError:
raise KeyError
def postRequest(self,obj,prefix='/'):
url = self.__url + prefix
headers = {'X-Auth-Token' : self.__token_id}
req = urllib2.Request(url, obj, headers)
opener = urllib2.urlopen(req)
content = json.loads(opener.read())
return content
def list_all_key(self):
params = {'client': 'wheel', 'fun': 'key.list_all'}
obj = urllib.urlencode(params)
self.token_id()
content = self.postRequest(obj)
minions = content['return'][0]['data']['return']['minions']
minions_pre = content['return'][0]['data']['return']['minions_pre']
return minions,minions_pre
def delete_key(self,node_name):
params = {'client': 'wheel', 'fun': 'key.delete', 'match': node_name}
obj = urllib.urlencode(params)
self.token_id()
content = self.postRequest(obj)
ret = content['return'][0]['data']['success']
return ret
def accept_key(self,node_name):
params = {'client': 'wheel', 'fun': 'key.accept', 'match': node_name}
obj = urllib.urlencode(params)
self.token_id()
content = self.postRequest(obj)
ret = content['return'][0]['data']['success']
return ret
def remote_noarg_execution(self,tgt,fun):
''' Execute commands without parameters '''
params = {'client': 'local', 'tgt': tgt, 'fun': fun}
obj = urllib.urlencode(params)
self.token_id()
content = self.postRequest(obj)
ret = content['return'][0][tgt]
return ret
def remote_execution(self,tgt,fun,arg):
''' Command execution with parameters '''
params = {'client': 'local', 'tgt': tgt, 'fun': fun, 'arg': arg}
obj = urllib.urlencode(params)
self.token_id()
content = self.postRequest(obj)
ret = content['return'][0][tgt]
return ret
def target_remote_execution(self,tgt,fun,arg):
''' Use targeting for remote execution '''
params = {'client': 'local', 'tgt': tgt, 'fun': fun, 'arg': arg, 'expr_form': 'nodegroup'}
obj = urllib.urlencode(params)
self.token_id()
content = self.postRequest(obj)
jid = content['return'][0]['jid']
return jid
def deploy(self,tgt,arg):
''' Module deployment '''
params = {'client': 'local', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg}
obj = urllib.urlencode(params)
self.token_id()
content = self.postRequest(obj)
return content
def async_deploy(self,tgt,arg):
''' Asynchronously send a command to connected minions '''
params = {'client': 'local_async', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg}
obj = urllib.urlencode(params)
self.token_id()
content = self.postRequest(obj)
jid = content['return'][0]['jid']
return jid
def target_deploy(self,tgt,arg):
''' Based on the node group forms deployment '''
params = {'client': 'local_async', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg, 'expr_form': 'nodegroup'}
obj = urllib.urlencode(params)
self.token_id()
content = self.postRequest(obj)
jid = content['return'][0]['jid']
return jid
def main():
sapi = SaltAPI(url='https://172.25.11.1:8000',username='saltapi',password='westos')
#sapi.token_id()
print sapi.list_all_key() #输出所有minion主机名
#sapi.delete_key('test-01')
#sapi.accept_key('test-01')
sapi.deploy('server2','apache') #给server2部署apache服务
#print sapi.remote_noarg_execution('test-01','grains.items')
if __name__ == '__main__':
main()