saltstack基本原理
SaltStack采用 C/S模式,server端就是salt的master,client端就是minion,minion与master之间通过ZeroMQ消息队列通信,是一个同时对一组服务器进行远程执行命令和状态管理的工具。
minion上线后先与master端联系,把自己的pub key发过去,这时master端通过salt-key -L命令就会看到minion的key,接受该minion-key后,也就是master与minion已经互信
master可以发送任何指令让minion执行了,salt有很多可执行模块,比如说cmd模块,在安装minion的时候已经自带了,它们通常位于你的python库中,locate salt | grep /usr/可以看到salt自带的所有东西。
这些模块是python写成的文件,里面会有好多函数,如cmd.run,当我们执行salt '*' cmd.run 'uptime'的时候,master下发任务匹配到的minion上去,minion执行模块函数,并返回结果。master监听4505和4506端口,4505对应的是ZMQ的PUB system,用来发送消息,4506对应的是REP system是来接受消息的。
具体步骤如下:
- Salt stack的Master与Minion之间通过ZeroMq进行消息传递,使用了ZeroMq的发布-订阅模式,连接方式包括
tcp,ipc
 - salt命令,将
cmd.run ls命令从salt.client.LocalClient.cmd_cli发布到master,获取一个Jodid,根据jobid获取命令执行结果。
 - master接收到命令后,将要执行的命令发送给客户端minion。
 - minion从消息总线上接收到要处理的命令,交给
minion._handle_aes处理
 minion._handle_aes发起一个本地线程调用cmdmod执行ls命令。线程执行完ls后,调用minion._return_pub方法,将执行结果通过消息总线返回给master
- master接收到客户端返回的结果,调用
master._handle_aes方法,将结果写的文件中
 salt.client.LocalClient.cmd_cli通过轮询获取Job执行结果,将结果输出到终端。
安装
- CentOS6/Redhat6
 
sudo yum install https://repo.saltstack.com/yum/redhat/salt-repo-latest-1.el6.noarch.rpm
yum clean all
- 安装salt-minion, salt-master, 或者其它组件
 
sudo yum install salt-master #server端sudo yum install salt-minion #client端sudo yum install salt-sshsudo yum install salt-syndicsudo yum install salt-cloudsudo yum install salt-api

sudo yum install salt-master

sudo yum install salt-minion
配置 :
服务端 vim /etc/salt/master
#master消息发布端口 Default: 4505publish_port: 4505 #工作线程数,应答和接受minion Default: 5
worker_threads: 100#客户端与服务端通信的端口 Default: 4506
ret_port: 4506 # 自动接受所有客户端
auto_accept: True # 自动认证配置
autosign_file: /etc/salt/autosign.conf
客户端 vim /etc/salt/minion
# master IP或域名master: 10.0.0.1# 客户端与服务端通信的端口。 Default: 4506
syndic_master_port: 4506# 建议线上用ip显示或业务编号
id: testid minion的唯一标示。Default: hostname
minion id:minion的唯一标示,默认为minion的hostname,如果id修改了,master 需要重新认证。
(通过tcpdump做了个实验,修改minion id后,master上会新增一个id,但老id也还在,执行salt ‘‘ test.ping的时候,执行时间变长了,延迟时间约为14s,而且master会在发送命令后延迟10s再给每个已经执行成功的minion发送一个包并有minion有返回,如果没有老id存在不会发送,可以理解为mater在向每个minion寻求未连接的id的信息,minion的salt服务关闭也是这种情况,修改timeout值无效。)
测试命令 :
测试环境中关闭 iptbles!!service iptables stop
salt-key -Lsalt-key -A
Accepted Keys:
192.168.1.3
Denied Keys:
Unaccepted Keys:
Rejected Keys:
salt '*' test.ping
192.168.1.3:
True
------------------------------
运维工具Salt简明入门指南
安装和配置
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
 | curl -L https://bootstrap.saltstack.com -o bootstrap-salt.shsudo sh bootstrap-salt.sh -M# 启动 master 节点sudo service salt-master start# 启动 minion 节点sudo service salt-minion start# 查看启动日志sudo tail -f /var/log/salt/minion | 
master通过 4505 端口向所有 minion 发送消息,minion执行完后再通过4506端口把结果发回给master,通讯采用ZeroMQ,数据使用msgpack压缩。
salt-minion 会在本机生成一个用于跟 master 通讯的 id,保存在 /etc/salt/minion_id 文件中,生成 id 的方法如下:
- 调用 Python 函数 socket.getfqdn()
 - 读取文件 /etc/hostname
 - 读取 hosts /etc/hosts
 
minion 会主动跟 master 通讯,通过配置文件 /etc/salt/minion 中的配置项来指定 master 的地址:
1 
2 
 | # 后面可填 hostname, ip, domain 等master: saltmaster.example.com | 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
 | # 查看所有 keyssalt-key -L# 查看单一 类型的key,后面参数可以是  unaccepted / accepted / rejected / deniedsalt-key -l unaccepted# 接受一个key的连接salt-key -a PUBLIC_KEY# 接受所有keysalt-key -A# 拒绝一个key的连接salt-key -r PUBLIC_KEY# 拒绝所有keysalt-key -R# 打印出指定key节点的指纹串salt-key -f FINGER# 获取 master 节点的fingersalt-key -f master# 打印所有 fingersalt-key -F | 
在 salt-key -F 命令输出结果的 local keys 结点,找到 master.pub 值
1 
2 
3 
4 
 | $ salt-key -FLocal Keys:master.pem:  2d:f0:d4:d4:ba:0f:a6:e6:3d:e1:6a:e2:90:53:50:cb:38:73:1d:b0:f6:09:e8:56:14:ea:57:d8:18:22:46:09master.pub:  5b:9d:77:12:d8:79:f9:23:12:87:9c:63:aa:47:c9:ff:31:36:b3:c5:00:ef:83:c0:fe:be:f7:29:de:15:d9:0a | 
1 
 | master_finger: '5b:9d:77:12:d8:79:f9:23:12:87:9c:63:aa:47:c9:ff:31:36:b3:c5:00:ef:83:c0:fe:be:f7:29:de:15:d9:0a' | 
在 master 节点也需要查看连接上来的 minion 节点的 finger 值,通过 finger 比对而不是 key,才能更加准确。
1 
2 
3 
4 
5 
6 
7 
8 
 | # 在minion中查看自己的fingersalt-call key.finger --local# 在 master 上查看某个id的minionr的fingersudo salt-key -f ubuntu# 比对过finger正确无误后,就要可以接受它的连接了sudo salt-key -a ubuntu | 
现在已经配置了 minion 与 master 的连接,并且授权了,下面就可以在 master 上面通过发送一些命令,来批量操作 minion 机器了。
常用命令
salt 命令的格式如下:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
 | salt '<target>' <function> [arguments]# 如salt '*' pkg.install vim# 普通 (按 minion-id 过滤)salt '*.example.org' test.ping# 正则表达 (regular expression)salt -E 'virtmach[0-9]' test.ping# 列表 (list)salt -L 'foo,bar,baz,quo' test.ping# 混合型 (combined)salt -C 'G@os:Ubuntu and webser* or E@database.*' test.ping| Letter |  Match Type        | Example                             || ------ | ------------------ | ---------------------------------- || G      | Grains glob        | G@os:Ubuntu                         || E      | 正则匹配 Minion ID  | E@web\d+\.(dev\|qa\|prod)\.loc      || P      | Grains 正则匹配     | P@os:(RedHat\|Fedora\|CentOS)       || L      | List of minions    | L@minion1,minion3 or bl*.domain.com || I      | Pillar glob        | I@pdata:foobar                      || S      | Subnet/IP address  | S@192.168.1.0/24 or S@192.168.1.100 || R      | Range cluster      | R@%foo.bar                          | | 
管理正在执行的任务:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
 | sudo salt master.example cmd.run 'sleep 100'# 这时打开另一个shell查,看运行中的任务sudo salt master.example saltutil.runningmaster.example:    |_      ----------      arg:          - sleep 100      fun:          cmd.run      jid:          20180507025356063697      pid:          4818      ret:      tgt:          master.example      tgt_type:          glob      user:          sudo_vagrant# 终止掉这个任务sudo salt master.example saltutil.kill_job 20180507025356063697 | 
1 
2 
3 
4 
5 
 | # 这个命令执行结果还是会通过4506端口发送给mastersudo salt-call sys.state_doc user.present# 加了--local后,结果不会发给master sudo salt-call --local test.ping | 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
 | # 查看哪些minion处理上线状态sudo salt-run manage.up (manage.down / manage.status)# 查看历史运行过的jobssudo salt-run jobs.list_jobs# 查看指定job执行情况 $ sudo salt-run jobs.lookup_jid 20180507113234037412# 查看所有runner功能模块文档$ sudo salt-run doc.runner | 
自己编写state
在配置文件 /etc/salt/master.d/下面新建配置文件,指定salt系统配置文件根目录:
1 
2 
3 
4 
 | $ cat /etc/salt/master.d/file-roots.conf file_roots:    base:    - /srv/salt/file/base | 
1 
2 
3 
4 
5 
6 
7 
 | $ cat user-wilma.sls user_wilma:    user.present:      - name: wilma      - fullname: Wilma Flintstone      - uid: 2001      - home: /home/wilma | 
1 
2 
3 
4 
5 
6 
7 
8 
9 
 | # 查看并检验一下sls文件sudo salt master.example state.show_sls user-wilma# 应用statesudo salt minion2.example state.sls user-wilma# 上面命令的效果和这条语句一致sudo salt minion2.example state.single user.present \    name=wilma fullname='Wilma Flintstone' uid=2001 home=/home/wilma | 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
 | tree /srv/salt/file/baseroles    webserver        - init.sls        - packages.sls        - start.slsusers    - barney.sls    - fred.sls    - wilma.sls    - betty.sls    - dba.sls    - all.slssites    - init.sls    src        - first.html- top.sls | 
1 
2 
 | include:- users.wilma | 
1 
2 
3 
4 
5 
 | include:- .wilma- .betty- .barney- .fred | 
1 
2 
 | include:- sites | 
例如:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
 | $ cat top.sls base:    'os:Ubuntu':       - match: grain       - default.vim    'os:CentOS':       - match: grain       - default.vim-enhanced    'minion1.example':       - roles.webserver       - sites    'minion2.example':       - users.dba    'minion3.example':       - users.dba       - users.qa    'minion4.example':       - users.all$ sudo salt minion1\* state.highstate | 
1 
2 
3 
4 
5 
 | roles_webserver_start: service.running: - name: nginx - require:   - pkg: nginx | 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
 | $ cat /srv/salt/file/base/sites/init.slssites_first: file.managed: - name: /usr/share/nginx/html/first.html - source: salt://sites/src/first.html - user: www - mode: 0644 service.running: - name: nginx - watch:   - file: /usr/share/nginx/html/first.html | 
1 
2 
3 
4 
5 
 | $ cat /srv/salt/file/base/run_first.slsrun_first: cmd.run: - name: 'echo "I am run first."' - order: 1 | 
如果只是想测试一下state,不想在minion真正的运行,则可以添加 test=true参数
1 
2 
3 
4 
5 
 | $ sudo salt minion1.example state.show_sls sites$ sudo salt minion1.example state.sls sites test=true$ sudo salt minion1.example state.show_highstate$ sudo salt minion\* state.highstate test=true | 
Grains和Pillar
grains就是所有关于minion属性的数据,一般是minion在启动的时候主动上报给master的。
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
 | # 查看主机有哪些grains属性$ sudo salt master.example grains.ls# 列出minion的os和ip属性$ sudo salt \* grains.item os ipv4 --out=text# 给minion设置一个myevn=prod的grains$ sudo salt -E 'minion(1|2).*' grains.setval myenv prod# 设置的属性值也可以是数组$ sudo salt 'minion1.*' grains.setval roles '[webserver, appserver]' | 
1 
2 
3 
4 
5 
 | [vagrant@minion1 ~]$ sudo cat /etc/salt/grainsmyenv: prodroles:- webserver- appserver | 
1 
2 
3 
 | pillar_roots:   base:    - /srv/salt/pillar/base | 
1 
2 
3 
4 
 | $ cat /srv/salt/pillar/base/top.slsbase: '*': - default | 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
 | $ cat /srv/salt/pillar/base/default.sls############IDC################{% if grains['ip_interfaces'].get('eth0')[0].startswith('10.10') %}nameservers: ['10.10.9.31','10.10.9.135']zabbixserver: ['10.10.9.234']{% else %}nameservers: ['10.20.9.75']zabbixserver: ['10.20.9.234']{% endif %}######## nginx ########ngx_home_dir: /var/cache/nginx | 
1 
2 
3 
4 
5 
 | nginx:  pkg:    - installed  user.present:    - home: {{ pillar['ngx_home_dir'] }} | 
自定义模块
自己可编写py文件,放到 _modules 目录下面,就可以扩展salt能够运行的模块了,例如:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
 | $ cat _modules/hello.py #coding=utf-8import logginglogger = logging.getLogger(__name__)def id():    '''    这是docstring,可以用命令查看:    sudo salt-call sys.doc hello.id    '''    id = __grains__['id']    logger.debug('Found grain id: {0}'.format(id))    return 'Hello, {0}.'.format(id) | 
1 
2 
3 
 | $ sudo salt \* saltutil.sync_modules$ sudo salt \* hello.id | 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
 | $ cat _states/custom.py import osimport loggingdef enforce_tmp(name, contents=None):    return_dict = {        'name': name,        'changes': {},        'result': False,        'comment': ''    }    # do your own stuff ...     # Check if this is a test run, if so do not change anything.    if __opts__['test'] == True:        logging.info('this is a test, skip changes...')    return_dict['result'] = True    return return_dict | 
1 
2 
3 
4 
5 
6 
 | $ cat custom.slscustom_state: custom.enforce_tmp: - name: foo - contents: bar | 
1 
2 
3 
4 
 | # 先同步$ sudo salt \* saltutil.sync_states$ sudo salt minion1.example state.sls custom test=true | 
在自定义grains文件中,定义的函数不是以 _ 开头的都被认为是需要执行的,函数返回的字典值会被合并到全局grains变量中
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
 | $ cat _grains/my_grains.py """Custom grains for the example hosts."""import platformimport logginglogger = logging.getLogger(__name__)def _get_hostname():    hostname = platform.node()    logger.debug('Using hostname: {0}'.format(hostname))    return hostnamedef set_myenv():    """    Set the 'myenv' grain based on the host name.    """    grains = {}    hostname = _get_hostname()    if hostname.startswith('minion1'):        grains['myenv'] = 'prod'    elif hostname.startswith('minion2'):        grains['myenv'] = 'prod'    elif hostname.startswith('minion3'):        grains['myenv'] = 'stage'    elif hostname.startswith('minion4'):        grains['myenv'] = 'dev'    return grainsdef set_roles():    """    Set the 'roles" grain based on the host name.    """    grains = {}    hostname = _get_hostname()    if hostname.startswith('minion1'):        grains['roles'] = ['webserver', 'appserver']    elif hostname.startswith('minion2'):        grains['roles'] = ['database']    elif hostname.startswith('minion3'):        grains['roles'] = ['webserver', 'appserver', 'database']    elif hostname.startswith('minion4'):        grains['roles'] = ['webserver', 'appserver', 'database']    return grains | 
1 
2 
3 
4 
5 
6 
 | # 先删除掉老的grains$ sudo salt \* cmd.run 'rm /etc/salt/grains'$ sudo salt \* service.restart salt-minion$ sudo salt \* saltutil.sync_grains | 
参考:《Salt Essentials》
CentOS8 vps上,安装SaltStack
SaltStack 是一款开源可以远程执行命令的配置管理工具。通过远程执行命令管理系统,可以在 master 主机上对 minions 主机进行命令执行并回显命令执行结果。这对于管理多台主机就显得非常方便了,我们可以只在一台 master 主机上操作多台 minions 主机,而不必频繁登录到主机。
系统环境:
- 控制主机(Salt-Master):
 
IP:172.16.200.10
- 被控节点(Minion):
 
IP:172.16.200.1
hostname: minion1
1] 在控制主机安装Salt-Master软件包
首先使用”yum” 命令就可以安装最新版的 salt-mater 软件包:
# yum install https://repo.saltstack.com/yum/redhat/salt-repo-latest.el7.noarch.rpm # yum install salt-master -y
安装完成后,修改配置文件”/etc/salt/master”如下:
interface: 10.0.0.209 hash_type: sha256
之后,重新启动 salt-master 并设置为开机启动:
# systemctl start salt-master.service # systemctl enable salt-master.service
2] 设置防火墙,开启saltstack侦听端口
saltstack 默认使用4505-4506进行通信,使用如下命令在 Firewalld 中放行端口:
# firewall-cmd --permanent --zone=public --add-port=4505-4506/tcp # firewall-cmd –reload
3] 在被控节点安装salt-minion
被控节点的 salt-minion 软件包安装成配置同 master类似,首先使用 yum 命令安装 salt-minion 软件:
# yum install https://repo.saltstack.com/yum/redhat/salt-repo-latest.el7.noarch.rpm # yum install salt-minion
安装完成后,修改配置文件”/etc/salt/minion”如下:
master: 10.0.0.209 hash_type: sha256
最后,重新启动 salt-minion 并设置为开机启动:
# systemctl start salt-minion.service # systemctl enable salt-minion.service
4] saltstack  控制主机和受控主机互联
现在,的控制主机上执行如下命令,查看已经连接的受控主机:
# salt-key -L
如果配置全部正确,就会显示如下信息。
图.1 salt-master 主机显示 key 信息
可以看以,现在有一台受控主机”minion”还处于”Unaccepted Keys”状态,使用如下命令将其加入接受:
# salt-key --accept=minion
接受受控主机后,再查看 keys 状态的话,就会发现该主机已经被接受,可以远程执行命令了:
图.2 salt-master 接受受控主机
5] saltstack系统功能测试
saltstack 系统互联成功后,就可以执行命令进行功能测试了。在控制主机执行命令的格式为:
# salt Name_of_Accept_key command
其中:
- Name_of_Accept_key 表示已经接受的受控主机名
 - command 指想要执行的命令,常用的有test.ping以及cmd.run等,其中cmd.run 后需要指定受控机器上执行的命令
 
图.3 salt-master远程执行命令并显示相应结果
以上就是安装和配置 saltstack 系统全部过程,如需了解更多细节,可以参考官方文档。
No comments:
Post a Comment