saltstack
status
$ID: 定义state的名称,通常采用与描述的对象保持一致的方法,如apache、nginx等 $State: 须管理对象的类型,详见http://docs.saltstack.com/ref/states/all/index.html - $state: states 定制对象的状态 salt \* state.sls dhcp 使用 salt \* state.sls dhcp/aaa 使用dhcp目录下的aaa.sls salt \* state.sls dhcp test=True 测试,不会修改minion salt \* state.highstate /usr/lib/python2.6/site-packages/salt/states 映射到modules /usr/lib/python2.6/site-packages/salt/modules 如果使用states,需要在/srv/salt 下编写sls文件 state 确定你的文件路径 file_roots: base: - /srv/salt 保证你有入口文件 state文件包 vim /srv/salt/dhcp/init.sls include: - dhcp.python_devel 包含dhcp目录下的python_devel.sls cat /srv/salt/dhcp/python_devel.sls dhcp: pkg.installed salt \* state.sls dhcp [root@node1 salt]# cat top.sls base: '*': - test 找test.sls test是一个sls文件的名字 或者是一个目录的名字(该目录下必须有init.sls这个文件) [root@node1 salt]# cat test.sls httpd: #id(默认这个位置对应的是name的值) pkg: #state - installed #state的函数 state目录 /usr/lib/python2.6/site-packages/salt/states 使用该state salt 'node2' state.highstate sls文件的中的任务执行顺序可以使用关键字控制(require) httpd: ID pkg: states模块 - installed 模块的函数 service.running: - require: 需要,依赖 - pkg: httpd 指定执行某一个sls文件(文件的扩展名.sls不需要写出来) [root@node1 salt]# salt 'node2' state.sls test 针对配置文件设置监听,实现自动重启 ############################################### httpd: pkg: - installed service: - running - require: - pkg: httpd - watch: 观察,文件有变化就启动 - file: /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf: file: - managed - source: salt://httpd.conf - require: - pkg: httpd ################################################ apache: pkg: - name: httpd - installed service.running: - name: httpd - require: - pkg: httpd - watch: - file: apache_config apache_config: file.managed: - name: /etc/httpd/conf/httpd.conf - source: salt://httpd.conf - template: jinja 在apache_config控制的文件用jinja渲染 - require: - pkg: httpd httpd.conf 1、 安装软件包 2、 修改配置文件 3、 启动服务 https://docs.saltstack.com/en/latest/ref/states/all/index.html vim user.sls {%for user in ['test1','test1','test3','test4']%} 渲染 {{user}}: 变量 user.present {%endfor%} 结束(如果是if 为endif) 一次调用多个sls文件 vim /srv/salt/top.sls base: ‘*’ - user - apache_start salt \* state.highstate 源码安装nginx nginx_source: file.recurse: # file.managed:用于单文件,file.recurse用于目录 - name: /tmp/ - source: salt://nginx/ extract_nginx: cmd.run: - cwd: /tmp - names: # 多命令用names,单命令用name - tar xf nginx-1.6.2.tar.gz - tar xf pcre-8.35.tar.gz - require: - file: nginx_source nginx_compile: cmd.run: - cwd: /tmp/nginx-1.6.2 - names: - ./configure --prefix=/usr/local/nginx --with-pcre=/tmp/pcre-8.35 && make - make install - require: - cmd: extract_nginx 自定义 state就是用来控制sls文件与module的映射 state默认路径 /usr/lib/python2.6/site-packages/salt/states mkdir -pv /srv/salt/_states [root@node1 _states]# cat wjj.py def bs(name, x): 定义函数 ret = { 定义一个字典 'name': name, 'changes': {}, 'result': False, 'comment': 'test_state' 描述的字段 } try: result = x*x 做一些事情 ret['changes']['new'] = result ret['result'] = True except: 出现异常怎么办 ret['changes']['new'] = 'jisuan cuwu' return ret 注意你的函数的第一个参数应该永远是name 同步一下你的修改 salt '*' saltutil.sync_all 使用你自定义的state 定义sls文件 [root@node1 _states]# cat /srv/salt/jisuan.sls test: wjj: - bs - x: 3 salt 'node2' state.sls jisuan tips:在state里面引用salt命令 __salt__['模块.函数'](参数) #encoding=utf-8 import logging import os log = logging.getLogger(__name__) def touch(name, path, file): ret = { 'name': name, 'changes': {}, 'result': True, 'comment': 'test_state' } #log.error(name) #log.error(path) try: with open(path+'/'+str(file), 'w') as f: f.write('my test!') except Exception, e: log.error(str(e)) return ret def delete(name, path, file): ret = { 'name': name, 'changes': {}, 'result': True, 'comment': 'test_state' } cmd='rm -fr ' + path + '/' + file os.system(cmd) ret['result']=True return ret
pillar
https://docs.saltstack.com/en/latest/topics/pillar/index.html 修改/etc/salt/master配置中的pillar_opts:Ture或False来定义是否开启或禁用这项功能 目录 /srv/pillar vim /srv/pillar/top.sls base: '*': - vimrc - data - pkg vim /srv/pillar/data.sls bind: port: 53 listen-on: any vim /srv/pillar/vimrc.sls {% if grains['id'].endswith('server') %} id标识以server结尾的 类似的startswith vimrc: salt://edit/vimrc1 {% else %} vimrc: salt://edit/vimrc2 {% endif %} vim /srv/pillar/pkg.sls {% if grains['os'] == 'RedHat' %} 根据不同的操作系统,定义不同的包名 apache: httpd git: git {% elif grains['os'] == 'Debian' %} apache: apache2 git: git-core {% endif %} 刷新生效 salt '*' saltutil.refresh_pillar 查看 salt \* pillar.items 使用pillar 可以在state、模板文件中引用,模板格式为“{{pillar变量}}”,例如: {{ pillar['appname'] }}(一级字典) {{ pillar['flow']['maxconn'] }}(二级字典)或 {{ salt['pillar.get']('flow: 'maxconn', {}) }} Python API格式如下:pillar['flow']['maxconn'] pillar.get(' flow:appname', {}) 1、在state的sls文件 vim /srv/salt/down_vimrc.sls /root/.vimrc: file.managed: - source: {{ pillar['vimrc'] }} 调用之前定义的pillar 或者- source: {{ salt['pillar.get']('vimrc','salt://edit/vimrc2') }} pillar的key,默认值 salt \* state.show_sls down_vimrc 查看详细的变量 2、salt -I "nginx:80" cmd.run "ls" -I 指定pillar 3、pillar的临时定义 vim /srv/salt/pillar_temp.sls {%set u = pillar['u_name']%} 之前没有这个pillar {{u}}: user.present salt 'node2' state.sls pillar_temp pillar='{"u_name": "v1"}' 临时指定一个pillar
job
[root@salt-master salt]# salt -I "nginx:80" cmd.run "sleep 100" -v Executing job with jid 20151011154835704857 salt-run 管理job salt-run -d |grep jobs salt-run jobs.active 查看当前正在运行的jobs salt-run jobs.list_job 20130916125524463507 指定jid查看jobs详细信息 salt-run jobs.list_jobs #查看所有jobs信息 salt-run jobs.lookup_jid 20130916125524463507 #指定jid查询jobs结果 salt-run jobs.print_job #指定jid查询jobs详细信息 ------------------------------------------------- 通过saltutil模块管理job salt \* sys.doc saltutil |grep job salt '*' saltutil.find_cached_job <job id> #查询job cache信息 salt 'node2' saltutil.find_job 20150810003021849521 通过jid查找 salt 'node2' saltutil.is_running cmd.run 通过函数名查找 salt 'node2' saltutil.kill_job 20150810003232388165 杀死一个job saltutil.running #查看minion当前正在运⾏的jobs saltutil.find_job<jid> #查看指定jid的job(minion正在运⾏的jobs) saltutil.signal_job<jid> <single> #给指定的jid进程发送信号 saltutil.term_job <jid> #终⽌指定的jid进程(信号为15) saltutil.kill_job <jid> #终⽌指定的jid进程(信号为9)
return存储结果
每台minion跟存储服务器连接后发送返回数据。在大规模的Minion环境下并不适合企业级应用。也有网友通过event事件实现Master端直接Return到存储服务器。参考地址为:http://pengyao.org/salt-stack_master_retuner_over_event_system.html 使用redis 存储 修改minion的/etc/salt/minion配置文件 redis.db: '0' #redis数据库 redis.host: 'vps.shencan.net' #redis主机(ip地址和域名都行) redis.port: 6379 #redis端口 minion按照redis客户端 pip install redis python -c 'import redis; print redis.VERSION' salt 'Minion' cmd.run 'hostname' --return redis mysql存储 /usr/lib/python2.6/site-packages/salt/returners/ 具体的配置 cat mysql.py vim /etc/salt/minion(冒号后有空格) mysql.host: '10.255.254.221' mysql.user: 'salt' mysql.pass: '123' mysql.db: 'salt' mysql.port: 3306 在10.255.254.221这台mysql服务器上 启动mysql服务 设置mysql用户,salt,密码,123 GRANT ALL PRIVILEGES ON *.* TO 'salt'@'%' IDENTIFIED BY ‘123’; 把以下sql语句保存到/tmp/salt.sql文件中,执行mysql -u root < /tmp/salt.sql (创建一个salt库,然后在salt库里面创建两个表jids,salt_returns) GRANT ALL PRIVILEGES ON *.* TO 'salt'@'%' IDENTIFIED BY ‘123’; CREATE DATABASE `salt` DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci; 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; 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; DROP TABLE IF EXISTS `salt_events`; CREATE TABLE `salt_events` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `tag` varchar(255) NOT NULL, `data` varchar(1024) 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; 保证你的python能够操作mysql数据库 yum install MySQL-python -y 使用mysql作为returner,将结果放入mysql [root@node1 ~]# salt 'node2' test.ping --return mysql node2: True ---------------------------------------------------------------- 可以通过salt的schedule去收集minion上的信息。 schedule: uptime: function: status.uptime seconds: 60 returner: mysql meminfo: function: status.meminfo minutes: 5 returner: mysql
api web
用户 # chmod 755 /etc/salt/master /var/run/salt /var/cache/salt 修改master配置文件 external_auth: pam: a1: - '*': - test.* - cmd.* http://docs.saltstack.com/en/latest/topics/eauth/index.html#acl-eauth 切换a1用户进行测试 salt -a pam * test.ping salt -a pam * cmd.run 'whoami' 测试令牌 $ salt -T -a pam web\* test.ping [a1@zabbix_server ~]$ salt -T -a pam * test.ping username: a1 password: zabbixnode1.example.com: True API yum install salt-api -y cd /etc/pki/tls/certs # 生成自签名证书, 过程中需要输入key密码及RDNs make testcert cd /etc/pki/tls/private/ # 解密key文件,生成无密码的key文件, 过程中需要输入key密码,该密码为之前生成证书时设置的密码 openssl rsa -in localhost.key -out localhost_nopass.key 或者 yum install gcc make python-devel libffi-develpip-python install PyOpenSSL salt-call tls.create_self_signed_cert #生成证书,需要有salt-minion,salt-call在salt-minion包里 添加saltapi.conf 文件 vim /etc/salt/master.d/saltapi.conf rest_cherrypy: port: 8000 host: 127.0.0.1 #disable_ssl: true 开关https ssl_crt: /etc/pki/tls/certs/localhost.crt #使用前面生成的证书 ssl_key: /etc/pki/tls/certs/localhost.key external_auth: pam: saltapi: - .* - '@runner' - '@wheel' #添加用户 useradd -M -s /sbin/nologin saltapi echo "spassword" | passwd saltapi --stdin 重启master 启动salt-api ---------------------------------------- import json import urllib import urllib2 class SaltAPI(object): __token_id = '' def __init__(self): self.__url = url self.__user = user self.__password = pass params = {'eauth': 'pam', 'username': self.__user, 'password': self.__password} content = self.postRequest(params, prefix='/login') print content['return'][0]['token'] self.__token_id = content['return'][0]['token'] def postRequest(self, obj, prefix='/'): url = self.__url + prefix headers = {'X-Auth-Token': self.__token_id} data = urllib.urlencode(obj) req = urllib2.Request(url, data, headers) opener = urllib2.urlopen(req) content = json.loads(opener.read()) return content def postRequest1(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 = opener.info() return content def list_all_key(self): params = {'client': 'wheel', 'fun': 'key.list_all'} content = self.postRequest(params) # minions = content['return'][0]['data']['return']['minions'] # minions_pre = content['return'][0]['data']['return']['minions_pre'] # return minions,minions_pre minions = content['return'][0]['data']['return'] return minions def delete_key(self, node_name): params = {'client': 'wheel', 'fun': 'key.delete', 'match': node_name} content = self.postRequest(params) ret = content['return'][0]['data']['success'] return ret def accept_key(self, node_name): params = {'client': 'wheel', 'fun': 'key.accept', 'match': node_name} content = self.postRequest(params) ret = content['return'][0]['data']['success'] return ret def reject_key(self, node_name): params = {'client': 'wheel', 'fun': 'key.reject', 'match': node_name} content = self.postRequest(params) 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} content = self.postRequest(params) 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} content = self.postRequest(params) ret = content['return'][0][tgt] return ret def shell_remote_execution(self, tgt, arg): ''' Shell command execution with parameters ''' params = {'client': 'local', 'tgt': tgt, 'fun': 'cmd.run', 'arg': arg, 'expr_form': 'list'} content = self.postRequest(params) ret = content['return'][0] return ret def grains(self, tgt, arg): ''' Grains.item ''' params = {'client': 'local', 'tgt': tgt, 'fun': 'grains.item', 'arg': arg} content = self.postRequest(params) ret = content['return'][0] 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'} content = self.postRequest(params) jid = content['return'][0]['jid'] return jid def deploy(self, tgt, arg): ''' Module deployment ''' params = {'client': 'local', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg} content = self.postRequest(params) 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} content = self.postRequest(params) jid = content['return'][0]['jid'] return jid def target_deploy(self, tgt, arg): ''' Based on the list forms deployment ''' params = {'client': 'local_async', 'tgt': tgt, 'fun': 'state.sls', 'arg': arg, 'expr_form': 'list'} content = self.postRequest(params) jid = content['return'][0]['jid'] return jid def jobs_list(self): ''' Get Cache Jobs Defaut 24h ''' url = self.__url + '/jobs/' headers = {'X-Auth-Token': self.__token_id} req = urllib2.Request(url, headers=headers) opener = urllib2.urlopen(req) content = json.loads(opener.read()) jid = content['return'][0] return jid def runner_status(self, arg): ''' Return minion status ''' params = {'client': 'runner', 'fun': 'manage.' + arg} content = self.postRequest(params) jid = content['return'][0] return jid def runner(self, arg): ''' Return minion status ''' params = {'client': 'runner', 'fun': arg} content = self.postRequest(params) jid = content['return'][0] return jid cl = SaltAPI() print cl.shell_remote_execution('aws-ms1','date') print cl.grains('aws-ms1','ipv4') WEB(halite)很不好用 这个还行 https://github.com/yueyongyue/saltshaker/ yum install salt-api git cd /var/www/ git clone https://github.com/saltstack/halite cd halite/halite ./genindex.py -C vim /etc/salt/master rest_cherrypy: host: 0.0.0.0 port: 8080 debug: true disable_ssl: True static: /var/www/halite/halite app: /var/www/halite/halite/index.html external_auth: pam: salt: - .* - '@runner' - '@wheel' /etc/init.d/salt-master restart useradd salt echo salt | passwd –stdin salt salt -a pam \* test.ping 输入用户和密码 如看到minion返回信息 则表示登陆验证成功 启动 salt-api salt-api -d 或 cd /var/www/halite/halite python server_bottle.py -d -C -l debug -s cherrypy 打开http://ip:8080/app
beacons
https://docs.saltstack.com/en/latest/topics/beacons/index.html https://docs.saltstack.com/en/latest/ref/beacons/all/index.html#all-salt-beacons 1 在minion上定义beacon beacons: service: httpd: onchangeonly: True uncleanshutdown: /var/run/httpd/httpd.pid 只要httpd的状态放生改变就会给master发送事件 2 你要知道发送的event长什么样 salt/beacon/centos2/service/ { "_stamp": "2015-12-06T07:24:25.095014", "data": { "httpd": { "running": false, "shutdown": "clean" }, "id": "centos2" }, "tag": "salt/beacon/centos2/service/" } 3 编写master配置文件,监听相对应的tag vim /etc/salt/master reactor: - 'salt/beacon/znode2/service/': - '/srv/reactor/backup.sls' 4 编写触发文件 vim /srv/reactor/backup.sls {%if data['data']['httpd']['running'] == False %} backup file: local.cmd.run: - tgt: {{data['data']['id']}} - arg: - "/etc/init.d/httpd start" {%endif%} #如果调用的是state.sls 还要编写相对应的sls文件 自定义beacon插件 1 mkdir -pv /srv/salt/_beacons 2 在这个目录下编写插件 3 同步 salt '*' saltutil.sync_all 4 vim t.py #你在minion配置文件中的字典,会赋值给config beacons: t: - rm: /tmp/kk - interval: 2 函数的返回值必须是[{'t1':1},{'t2':2}] 导入你需要的模块 import 模块名字 def beacon(config): ret = [] r_dict = {} 拆分config然后去做你想做的判断 然后把返回值追加到ret return ret 做一个最简单的审计系统 # -*- coding: utf-8 -*- import logging import re import pexpect log = logging.getLogger(__name__) __virtualname__ = 't' """ def __virtual__(): if salt.utils.is_windows(): return False else: return __virtualname__ """ def beacon(config): ''' code_block:: yaml beacons: t: - rm : / - interval: 100 ''' ret = [] user = __salt__['cmd.run']('whoami') logging.debug(user) r_dict = {} for t in config: for k,v in t.items(): cmd = "grep '^.*rm.* /tmp/kk[[:space:]]$' /tmp/.shellog" output, status = pexpect.run(cmd, withexitstatus=1) if status == 0 : logging.debug(output) r_dict = { user : output} logging.debug(r_dict) ret.append(r_dict) return ret
安装zabbix_agent
ls zabbix_linux_install/ agent_install.sls init.sls zabbix_agentd.conf zabbix.repo cat init.sls include: - zabbix_linux_install.agent_install cat zabbix.repo [zabbix] baseurl=http://mirrors.aliyun.com/zabbix/zabbix/2.4/rhel/6/x86_64/ gpgcheck=0 cat agent_install.sls agent_install.sls /etc/yum.repos.d/zabbix.repo: file.managed: - source: salt://zabbix_linux_install/zabbix.repo zabbix-agent: cmd.run: - name: yum install zabbix-agent-2.4.5 -y - require: - file: /etc/yum.repos.d/zabbix.repo service.running: - require: - cmd: zabbix-agent - watch: - file: /etc/zabbix/zabbix_agentd.conf /etc/zabbix/zabbix_agentd.conf: file.managed: - source: salt://zabbix_linux_install/zabbix_agentd.conf ( 主要内容:Hostname={{ grains['ip_interfaces'] ['eth1'][0] }}) - template: jinja - require: - cmd: zabbix-agent 老师的 {% set NODENAME = grains['nodename'] %} {% set BINDIR = '/usr/local/zabbix/sbin' %} {% set LOGDIR = '/var/log/zabbix' %} {% set Serverip = '192.168.40.11' %} # source pacekages zabbix_source: file.managed: - name: /tmp/zabbix-2.4.1.tar.gz - unless: test -e /tmp/zabbix-2.4.1.tar.gz - source: salt://files/common/zabbix/zabbix-2.4.1.tar.gz #trace zabbix extract_zabbix: cmd.run: - cwd: /tmp - names: - tar -zxf zabbix-2.4.1.tar.gz >/dev/null 2>&1 - unless: test -d /tmp/zabbix-2.4.1/ - require: - file: zabbix_source #Add user zabbix_user: user.present: - name: zabbix - uid: 1000 - createhome: False - gid_from_name: True - shell: /sbin/nologin /var/log/zabbix: file.directory: - user: zabbix - group: zabbix - dir_mode: 755 - file_mode: 655 - recurse: - user - group "init.sls" 87L, 2508C agent_start_init: file.managed: - name: /etc/init.d/zabbix_agentd - user: root - mode: 0755 - source: salt://files/common/zabbix/zabbix_agentd - source_hash: salt://files/common/zabbix_agent/zabbix_agentd - template: jinja - defaults: ZABBIX_BIN: {{ BINDIR }} - unless: if [[ ${cat /etc/init.d/zabbix_agentd | grep "{{ BINDIR }}" | awk -F'=' 'NR==1{print $2}'} = '"{{ BINDIR }}"' ]];then exit 0;fi cmd.run: - names: - /sbin/chkconfig --add zabbix_agentd - /sbin/chkconfig zabbix_agentd on - unless: /sbin/chkconfig --list zabbix_agentd service.running: - name: zabbix_agentd - enable: True - restart: True zabbix_config_set: file.managed: - name: /usr/local/zabbix/etc/zabbix_agentd.conf - user: root - mode: 744 - source: salt://files/common/zabbix/zabbix_agentd.conf - template: jinja - defaults: ServerADD: {{ Serverip }} AgentNAME: {{ NODENAME }} - unless: if grep '{{ NODENAME }}' /usr/local/zabbix/etc/zabbix_agentd.conf >/dev/null 2>&1;then exit 0;fi /usr/bin/zabbix_get: file.symlink: - target: /usr/local/zabbix/bin/zabbix_get - unless: test -L /usr/bin/zabbix_get /usr/bin/zabbix_sender: file.symlink: - target: /usr/local/zabbix/bin/zabbix_sender - unless: test -L /usr/bin/zabbix_sender
显示salt进程具体名称
安装setproctitle(Master/Minion端均进行) yum -y install python-setproctitle 重启salt service salt-master restart service salt-minion restart 查看Master端进程 ps ax |grep salt |grep -v salt Master端显示如下(同时个人在行尾追加上进程的具体用途): 2943 ? S 0:00 /usr/bin/python /usr/bin/salt-master -d ProcessManager # 中心进程管理器 2944 ? S 0:00 /usr/bin/python /usr/bin/salt-master -d _clear_old_jobs # 清除旧的Jobs文件及更新fileserver 2945 ? Sl 0:00 /usr/bin/python /usr/bin/salt-master -d Publisher # 将任务PUB到Minion端 2946 ? Sl 0:00 /usr/bin/python /usr/bin/salt-master -d EventPublisher # Event Publisher进程 2951 ? S 0:00 /usr/bin/python /usr/bin/salt-master -d ReqServer_ProcessManager # ReqServer进程管理器 2952 ? Sl 0:01 /usr/bin/python /usr/bin/salt-master -d MWorker # 劳苦大众, 奋斗在一线的Worker进程 2953 ? Sl 0:01 /usr/bin/python /usr/bin/salt-master -d MWorker # 同楼上 2954 ? Sl 0:01 /usr/bin/python /usr/bin/salt-master -d MWorker 2955 ? Sl 0:01 /usr/bin/python /usr/bin/salt-master -d MWorker 2956 ? Sl 0:01 /usr/bin/python /usr/bin/salt-master -d MWorker 2957 ? Sl 0:00 /usr/bin/python /usr/bin/salt-master -d MWorkerQueue # 将Ret接口(ROUTER)数据转发到Worker(DEALER) 执行个任务, 看看Minion端怎么显示(同时个人在行尾追加上进程的具体用途): 2003 ? Sl 0:01 /usr/bin/python /usr/bin/salt-minion -d # Minion进程, 接收来自Master端的任务 2069 ? S 0:00 /usr/bin/python /usr/bin/salt-minion -d 20150108034936245247 # 接收到任务后, 会启动名为 对应jid的进程进行任务处理及结果反馈 这样, 就可以非常清晰的知道Salt的每个进程是做什么用途的, 如果Master/Minion进程异常, 也可以迅速的定位
salt安装
并发 == 效率 差异性 4505 4506 master 队列 zeromq,raet salt-call --master=10.21.40.23 --id="host1" state.highstate yum安装 https://repo.saltstack.com/#rhel salt官方源,版本更新 http://repo.saltstack.com/yum/redhat [saltstack-repo] name=SaltStack repo for RHEL/CentOS $releasever baseurl=http://repo.saltstack.com/yum/redhat/$releasever/$basearch/latest enabled=1 gpgcheck=1 gpgkey=http://repo.saltstack.com/yum/redhat/$releasever/$basearch/latest/SALTSTACK-GPG-KEY.pub yum install epel-release yum install salt-master master端 salt-2014.1.10-4.el6.noarch salt-master-2014.1.10-4.el6.noarch salt-minion-2014.1.10-4.el6.noarch(optional) minion端(客户端) yum install salt-minion salt-2014.1.10-4.el6.noarch salt-minion-2014.1.10-4.el6.noarch 防火墙添加 iptables -I INPUT -m state --state new -m tcp -p tcp --dport 4505 -j ACCEPT iptables -I INPUT -m state --state new -m tcp -p tcp --dport 4506 -j ACCEPT python安装 yum install swig yum install gcc gcc-c++ yum install openssl-devel yum install libyaml-devel -y pip install M2Crypto pip install pyzmq pip uninstall PyCrypto pip uninstall salt Jinja2 msgpack-python PyYAML MarkupSafe pip install PyCrypto cd /usr/lib/python2.6/site-packages/ rm -fr salt salt-2014.1.4-py2.6.egg-info/ pip install salt http://docs.saltstack.com/en/latest/ http://msgpack.org/ zeromq 使用了消息队列 信息使用msgpack进行封装 选择salt的理由 1 使用消息队列(异步) zeromq 2 消息封装的是2机制 msgpack封装 3 安全使用aes算法 4 服务器开两个端口4505 4506 5 理念是block(适合拼积木,实现业务流) 配置 在master上编辑vim /etc/hosts master_ip master_name minion_ip minion_name 然后把这份文件同步到所有的主机上 修改文件描述符 修改sk_buffer echo 16777216 > /proc/sys/net/core/rmem_max echo 16777216 > /proc/sys/net/core/wmem_max echo "4096 87380 16777216" > /proc/sys/net/ipv4/tcp_rmem echo "4096 87380 16777216" > /proc/sys/net/ipv4/tcp_wmem minion vim /etc/salt/minion id: node2 (minion的名字) 冒号后有空格 master: 10.255.254.221(master的ip) /etc/init.d/salt-minion start /etc/init.d/salt-master start 查看你的minion是否发送了加入请求 [root@node1 salt]# salt-key -L salt-key –A 接受所有minion -a minion名字 接受指定的minion 测试 salt '*' test.ping 目标 模块.函数 函数的参数 如何定义目标 shell下的通配符 * "*.example.com" "web?.example.com" "web[1-5].example.com" regex 正则表达式 salt -E 'web1-(prod|devel).example.com' list 列表的方式 salt -L 'web1,web2,web3' grains(数据存放在minion上,默认就有) [root@node1 salt]# salt 'node1' grains.ls salt -G 'os:CentOS' test.ping 定义主机组 [root@node1 salt]# vim /etc/salt/master salt -N 组名 test.ping 批次执行-b (依次执行) salt '*' sys.doc 如何使用相对应的模块和函数 /usr/lib/python2.6/site-packages/salt/modules test.ping (是在minion上执行) http://docs.saltstack.com/en/latest/ref/modules/all/index.html [root@node1 modules]# salt 'node3' cmd.run 'uptime' salt中文件的根目录 file_roots: base: - /srv/salt salt 'node2' cmd.script salt://test.sh 脚本缓存的路径 /var/cache/salt/minion/files/base
修改minion id
service salt-minion stop rm -f /etc/salt/pki/minion/minion.pub rm -f /etc/salt/pki/minion/minion.pem echo $(hostname) > /etc/salt/minion_id service salt-minion start
event和反射
1、event实际就是一个字典 这个字典有两个固定的key "tag" 可以用来区分哪一个mininon "data" 包含更详细的数据,比如执行是否成功,执行的是哪一个函数,以及id是谁 2、把你的minion和master,以及其他的minion可以联动起来 master执行 salt-run state.event pretty=True 捕捉event minion执行 salt-call event.send 'tag' 'data' data可以没有,tag必须有 master再开一个终端执行salt 'zabbix_server' state.sls apache_install 捕捉到的event: salt/job/20151018191951447100/ret/zabbix_server { tag和data ,字典为data "_stamp": "2015-10-18T11:20:19.543008", "cmd": "_return", "fun": "state.sls", "fun_args": [ "apache_install" ], "id": "zabbix_server", "jid": "20151018191951447100", "out": "highstate", "retcode": 0, "return": { "pkg_|-httpd_|-httpd_|-installed": { "__run_num__": 0, "changes": {}, "comment": "Package httpd is already installed.", "duration": 4001.181, "name": "httpd", "result": true, "start_time": "11:20:14.264181" } }, "success": true } }配置 修改master配置文件 reactor: - "salt/job/*": 捕捉到的tag,jid会变化用了*代替 - /srv/reactor/my_custom_module.sls 捕捉到tag要做的事 mkdir -pv /srv/reactor/ && vim /srv/reactor/my_custom_module.sls {% if data["fun_args"][0] == "apache_install" and data["success"] == true %} 进行一些具体的判断 touch_new_file: local.state.sls: (老版本这里是cmd.state.sls) - tgt: 'zabbix_server' 执行的目标 - arg: - newfile 执行/srv/salt/newfile.sls文件 - kwarg: 临时定义pillar pillar: 注意这里是四个空格 filename: {{ data["fun_args"][0] }} {% endif %} ##这个{{data是模板里面的变量}} 相当于 salt ' zabbix_server ' state.sls newfile pillar='{"filename": " apache_install "}' cat /srv/salt/newfile.sls /tmp/{{pillar['filename']}}: file.managed: - source: salt://passwd 到此,执行salt 'zabbix_server' state.sls apache_install后(成功),就会联动执行newfile.sls
帮助文档
包含所有模块,state等,记不起来可以在这找 salt 'centos2' sys.list_functions salt 'centos2' sys.list_functions sys - sys.argspec - sys.doc 文档, salt '*' sys.doc cmd 为cmd的文档,其他类似 - sys.list_functions module的所有function的命令, salt 'Minion' sys.list_functions cmd - sys.list_modules Minion支持的所有module列表,salt 'Minion' sys.list_modules - sys.list_renderers 查看所有Return列表 - sys.list_returner_functions - sys.list_returners - sys.list_runner_functions - sys.list_runners - sys.list_state_functions 指定states的所有function,salt 'Minion' sys.list_state_functions file - sys.list_state_modules Minion支持的所有states列表 - sys.state_doc state的详细用法与例子 - sys.reload_modules - sys.renderer_doc - sys.returner_argspec - sys.returner_doc - sys.runner_argspec - sys.runner_doc - sys.state_argspec
grains
grains是master从minion上拿数据 salt '*' grains.ls salt 'node1' grains.items salt 'node1' grains.item os salt -G 'os:CentOS' test.ping 1、用grains命令添加 salt 'Minion' grains.append saltbook 'verycool' salt '*' grains.setval key "{'sub-key': 'val', 'sub-key2': 'val2'}" 设置多个 salt '*' grains.remove key val 删除 2、在minion端定义grains vim /etc/salt/minion.d/hostinfo.conf #自定义grains,相当于打上一个标签,在根据标签进行操作 grains: roles: - webserver - memcache deployment: datacenter4 cabinet: 13 重启minion /etc/init.d/salt-minion restart 3、在master端自己编写grains,然后推送给minion mkdir /srv/salt/_grains vim /srv/salt/_grains/system.py import platform def get_system(): grains = {} # 初始化一个grains字典 grains['system'] = platform.platform() return grains #返回结果 salt "zabbix_agent" saltutil.sync_grains #推送给minion(被控端的/var/cache/salt/minion/extmods/grains/目录下) salt "zabbix_agent" grains.item system #查看这个grains 使用 grains是静态的 1、通过目标使用grains salt -G 'os:CentOS' test.ping 2、通过top.sls来使用 vim top.sls base: 'location:shanghai': - match: grain - webserver (具体的sls文件名) salt '*' state.highstate来调用,只有location:shanghai的主机才执行user.sls 3、通过在定义的state,sls文件使用 {% set the_node_type = salt['grains.get']('grains的key', '默认值') %} or grains['os'] {% if the_node_type %} 'node_type:{{ the_node_type }}': - match: grain - {{ the_node_type }} {% endif %} 4、通过编写模块来调用grains,__grains__['key'] vim g1.py def t1(): return __grains__['location'] salt '*' saltutil.sync_modules 推送 salt ‘*’ g1.t1 调用
modules
常用模块 http://docs.saltstack.com/ref/modules/all/index.html#all-salt-modules import salt.client client = salt.client.LocalClient() ret = client.cmd('*', 'test.ping') print ret Archive压缩包 salt 'centos1' archive.gunzip /tmp/sourcefile.txt.gz #采用gzunzip解压/tmp/sourcefile.txt.gz包 salt 'centos1' archive.gzip /tmp/sourcefile.txt #采用gzip压缩/tmp/sourcefile.txt文件 --- API调用:client.cmd('centos1', ' archive.gunzip' , ['/tmp/sourcefile.txt.gz ']) cmd命令 salt 'centos1' cmd.run "free -m" #获取所有被控主机的内存使用情况 salt 'centos1' cmd.script salt://script/test.sh #在centos1主机运行test.sh脚本,其中script/test.sh存放在 file_roots指定的目录,首先同步test.sh到minion的cache目录(如同步到/var/cache/salt/minion/files/base/script/test.sh); 其次运行该脚本 --- API调用:client.cmd('SN2013-08-021', 'cmd.run',['free -m']) cp文件目录管理 salt 'centos1' cp.cache_local_file /etc/hosts #将指定被控主机的/etc/hosts文件复制到被控主机本地的salt cache目录 (/var/cache/salt/minion/localfiles/) salt 'centos1' cp.get_dir salt://path/to/dir/ /minion/dest #将主服务器file_roots指定位置下的目录复制到被控主机 salt 'centos1' cp.get_file salt://path/to/file /minion/dest #将主服务器file_roots指定位置下的文件复制到被控主机 salt 'centos1' cp.get_url http://www.slashdot.org /tmp/index.html #下载URL内容到被控主机指定位置 ----- API调用:client.cmd('centos1', 'cp.get_file',[' salt://path/to/file ',' /minion/dest']) crontab定时任务 salt 'centos1' cron.raw_cron root or salt 'centos1' cron.list_tab root #查看指定被控主机、root用户的crontab清单 salt 'centos1' cron.set_job root '*' '*' '*' '*' 1 /usr/local/weekly #为指定的被控主机、root用户添加/usr/local/weekly任务 salt 'centos1' cron.rm_job root /usr/local/weekly #删除指定的被控主机、root用户crontab的/usr/local/weekly任务 ----- API调用:client.cmd('centos1', 'cron.set_job',['root','*','*','*','*','*','/usr/echo']) dnsutil DNS相关 salt 'centos1' dnsutil.hosts_append /etc/hosts 192.168.8.12 salt-master #添加指定被控主机hosts的主机配置项 salt 'centos1' dnsutil.hosts_remove /etc/hosts salt-master #删除指定被控主机hosts的主机配置项 ----- API调用:client.cmd('*', 'dnsutil.hosts_append',['/etc/hosts','127.0.0.1','ad1.yuk.co']) file文件 salt 'centos2' file.check_hash /etc/fstab md5:e4b13a1b984950ade18cbc8bc33be658 #校验被控主机/etc/fstab文件的md5 一致则返回True salt '*' file.get_sum /etc/passwd md5 #校验所有被控主机文件的加密信息、支持md5、sha1、sha224、sha256、sha384、sha512 加密算法 salt '*' file.chown /etc/passwd root root #修改所有被控主机/etc/passwd文件的属组、用户权限, 等价于chown root:root /etc/passwd salt '*' file.copy /path/to/src /path/to/dst #复制所有被控主机本地/path/to/src文件到本地的/path/to/dst文件 salt '*' file.directory_exists /etc #检查所有被控主机/etc目录是否存在,存在则返回True,检查文件是否存在使用 file.file_exists方法 salt '*' file.stats /etc/passwd #获取所有被控主机/etc/passwd的stats信息 salt '*' file.get_mode /etc/passwd #获取所有被控主机/etc/passwd的权限mode,如755、644 salt '*' file.set_mode /etc/passwd 0644 #修改所有被控主机/etc/passwd的权限mode为0644 salt '*' file.mkdir /opt/test #在所有被控主机创建/opt/test目录 salt '*' file.sed /etc/httpd/httpd.conf 'LogLevel warn' 'LogLevel info' #将所有被控主机/etc/httpd/httpd.conf 文件的LogLevel参数的warn值修改成info salt '*' file.append /tmp/test/test.conf "maxclient 100" #给所有被控主机的/tmp/test/test.conf文件追加内容"maxclient 100" salt '*' file.remove /tmp/foo3 #删除所有被控主机的/tmp/foo文件 ------ API调用:client.cmd('*', ' file.remove ',['/tmp/foo']) iptables防火墙 salt '*' iptables.append filter INPUT rule='-m state --state RELATED,ESTABLISHED -j ACCEPT' salt '*' iptables.insert filter INPUT position=3 rule='-m state --state RELATED,ESTABLISHED -j ACCEPT' #在所有被控端主机追加(append)、插入(insert)iptables规则,其中INPUT为输入链 salt '*' iptables.delete filter INPUT position=3 salt '*' iptables.delete filter INPUT rule='-m state --state RELATED,ESTABLISHED-j ACCEPT' #在所有被控端主机删除指定链编号为3(position=3)或指定存在的规则 salt '*' iptables.save /etc/sysconfig/iptables #保存所有被控端主机规则到本地硬盘(/etc/sysconfig/iptables) ---- API调用: client.cmd('SN2013-08-022', 'iptables.append',['filter','INPUT','rule=\'-p tcp --sport 80 -j ACCEPT\'']) netwrok网络信息 salt 'centos2' network.dig www.qq.com salt 'centos2' network.ping www.qq.com salt 'centos2' network.traceroute www.qq.com #在指定被控主机'centos2'获取dig、ping、traceroute目录域名信息 salt 'centos2' network.hwaddr eth0 #获取指定被控主机的MAC地址 salt 'centos2' network.in_subnet 10.0.0.0/16 #检测指定被控主机是否属于10.0.0.0/16子网范围,属于则返回True salt 'centos2' network.interfaces #获取指定被控主机的网卡配置信息 salt 'centos2' network.ip_addrs #获取指定被控主机的IP地址配置信息 salt 'centos2' network.subnets #获取指定被控主机的子网信息 ---- API调用:client.cmd('SN2013-08-022', 'network.ip_addrs') pkg包管理 salt '*' pkg.install php #为所有被控主机安装PHP环境,根据不同系统发行版调用不同安装工具进行部署,如redhat平台的yum, 等价于yum -y install php salt '*' pkg.remove php #卸载所有被控主机的PHP环境 salt '*' pkg.upgrade #升级所有被控主机的软件包 ---- API调用:client.cmd('SN2013-08-022', 'pkg.remove',['php']) Service服务 salt '*' service.enable nginx salt '*' service.disable nginx #开启(enable)、禁用(disable)nginx开机自启动服务 salt '*' service.reload nginx salt '*' service.restart nginx salt '*' service.start nginx salt '*' service.stop nginx salt '*' service.status nginx #针对nginx服务的reload、restart、start、stop、status操作 ----- API调用: client.cmd('SN2013-08-022', 'service.stop',['nginx']) other Saltstack还提供了user(系统用户模块)、group(系统组模块)、partition(系统分区模块)、puppet(puppet管理模块)、 system(系统重启、关机模块)、timezone(时区管理模块)、nginx(Nginx管理模块)、mount(文件系统挂载模块),等等 自定义模块 分发机制 /usr/lib/python2.6/site-packages/salt/modules 在minion执行,可以在master写分发到minion 根目录 fileroot: /etc/salt/master /srv/salt 1、 module是在minion执行 2、 master创建2次开发出来的modules,发布到所有的minion 3、mkdir –pv /srv/salt/_modules && cd /srv/salt/_modules 4、创建module文件并且定义函数 cat file.py 模块名File def t1(num) 函数名 ‘’‘salt \* file.t1 ’‘’ 描述 return num*num 信息在函数的return里 5、推送给所有的minion salt '*' saltutil.sync_modules 会推送给minion的路径: /var/cache/salt/minion/extmods/modules 推送所有 : salt '*' saltutil.sync_all 包括以下所有的
zmq
saltstack master 启动 REQSERVER 1 接收来自于client的请求,然后派发给空闲的Mworker 2 接收来自于minion的返回信息 4506 bind ROUTER workers.ipc bind DEALER Publisher 发布来自于client的请求给所有的minion 4505 bind PUB publish_pull.ipc bind PULL EVENTPUBLISHER 主要是通知client,任务执行的状态,结果! master_event_pull.ipc bind pull master_event_pub.ipc bind PUB MWORKER 多进程,实际处理工作 wokers.ipc connect REP minion 4505 connect SUB (接收执行任务,数据以字典格式发送) 4605 connect REQ (返回执行结果) 执行具体任务的时候 job flow 1 salt '*' test.ping 创建了一个client对象 2 client 4506 REQ connect 发送执行项目 3 REQSERVER把请求交给可用的MWOKER,通过wokers.ipc 4 首先,确定你有权利这么做。然后通过publish 把client交予的命令发送给所有的minion。ClearFuncs.publish() MWokers 产生新的配对 publish_pull.ipc connect PUSH 5 你的woker宣告任务开始执行利用evnetpublish master_event_pull.ipc connect push 然后eventpublish通过pub方式把消息传送给所有的client订阅者 (连接到master_event_pub.ipc) 6 消息加密以后通过Publisher发送给所有的mininon 7 所有的minion通过4505的订阅接收消息 8 minion先解密消息,再在本地执行操作,但是事先会判断对象是不是自己 9 minion把响应数据加密后发送给master的4506,master把数据交给可用Mwoker 10 Mwoker接收数据后解密,然后通过eventpublish 发送相对应的状态通知 11 此时你的LocalClient就接收到返回信息了。或者等待超时。 12 当所有的minion都返回了结果,或者超时,则此次任务结束。 salt-zmq.png
