Pages

Wednesday, 25 July 2018

Linux 自动化运维之SaltStack安装

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监听45054506端口,4505对应的是ZMQ的PUB system,用来发送消息,4506对应的是REP system是来接受消息的。

具体步骤如下:

  1. Salt stack的Master与Minion之间通过ZeroMq进行消息传递,使用了ZeroMq的发布-订阅模式,连接方式包括tcpipc
  2. salt命令,将cmd.run ls命令从salt.client.LocalClient.cmd_cli发布到master,获取一个Jodid,根据jobid获取命令执行结果。
  3. master接收到命令后,将要执行的命令发送给客户端minion。
  4. minion从消息总线上接收到要处理的命令,交给minion._handle_aes处理
  5. minion._handle_aes发起一个本地线程调用cmdmod执行ls命令。线程执行完ls后,调用minion._return_pub方法,将执行结果通过消息总线返回给master
  6. master接收到客户端返回的结果,调用master._handle_aes方法,将结果写的文件中
  7. 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-ssh
sudo yum install salt-syndic
sudo yum install salt-cloud
sudo yum install salt-api
服务端

sudo yum install salt-master
客户端

sudo yum install salt-minion

配置 :

服务端 vim /etc/salt/master

#master消息发布端口 Default: 4505
publish_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: test
id 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
master端执行

salt-key -L
salt-key -A
查看到minion端ip表示成功
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.sh
sudo sh bootstrap-salt.sh -M
 
# 启动 master 节点
sudo service salt-master start
 
# 启动 minion 节点
sudo service salt-minion start
 
# 查看启动日志
sudo tail -f /var/log/salt/minion
salt 会在本地监听两个端口: 4505 和 4506。
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
以上三个步骤哪个返回不是 localhost,就会被用做 id 值 ,否则就会使用外网的 ip 地址做为 id。最终获取的 ID 将记录在 /etc/salt/minion_id 文件中,该文件可以手动更改,重启服务后不会被重新覆盖。 但如果 minion 的配置文件 /etc/salt/minion 中设置了 id: xxxx,那么这个 id 值将取代 /etc/salt/minion_id 中记录的 ID 数值。
minion 会主动跟 master 通讯,通过配置文件 /etc/salt/minion 中的配置项来指定 master 的地址:
1
2
# 后面可填 hostname, ip, domain 等
master: saltmaster.example.com
在 master 节点上,可以通过 salt-key 命令管理所有 minion 节点的连接情况,包括接受、拒绝、删除连接。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 查看所有 keys
salt-key -L
# 查看单一 类型的key,后面参数可以是  unaccepted / accepted / rejected / denied
salt-key -l unaccepted
 
# 接受一个key的连接
salt-key -a PUBLIC_KEY
# 接受所有key
salt-key -A
 
# 拒绝一个key的连接
salt-key -r PUBLIC_KEY
# 拒绝所有key
salt-key -R
 
# 打印出指定key节点的指纹串
salt-key -f FINGER
# 获取 master 节点的finger
salt-key -f master
# 打印所有 finger
salt-key -F
默认情况下,minion 跟 master 连接的时候,会使用自己的 id 做为 key,这样在 master 上查看所有 public keys 时,列出的是id,除了基本的id之外,可以使用 finger print 的值来精准校验。
在 salt-key -F 命令输出结果的 local keys 结点,找到 master.pub 值
1
2
3
4
$ salt-key -F
Local 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:09
master.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
然后把这个值复制下来,并在 minion 节点中设置配置文件 /etc/salt/minion :
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'
这样 minion 启动后,通过判断设置的 master_finger 值,跟连接上的 master 信息比对,就可以校验出 master 是否正确。
在 master 节点也需要查看连接上来的 minion 节点的 finger 值,通过 finger 比对而不是 key,才能更加准确。
1
2
3
4
5
6
7
8
# 在minion中查看自己的finger
salt-call key.finger --local
 
# 在 master 上查看某个id的minionr的finger
sudo salt-key -f ubuntu
 
# 比对过finger正确无误后,就要可以接受它的连接了
sudo salt-key -a ubuntu
minion与master所有key finger信息,保存在目录 /etc/salt/pki/ 里面,如果是master节点,那么 /etc/salt/pki/master/ 下面的 minions、minions_pre、minions_rejected分别保存着接受、待接受和已拒绝的finger key。
现在已经配置了 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                          |
所有 salt 可用的模块见官方文档:http://docs.saltstack.com/en/latest/ref/modules/all/
管理正在执行的任务:
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.running
master.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
salt-call用于在minion上执行命令,如果想要执行的结果不发送到master那里去,则可以添加 –local 参数。
1
2
3
4
5
# 这个命令执行结果还是会通过4506端口发送给master
sudo salt-call sys.state_doc user.present
 
# 加了--local后,结果不会发给master
 sudo salt-call --local test.ping
salt-run命令只在master运行
1
2
3
4
5
6
7
8
9
10
11
# 查看哪些minion处理上线状态
sudo salt-run manage.up (manage.down / manage.status)
 
# 查看历史运行过的jobs
sudo 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
然后在 /srv/salt/file/base 目录下面新建 sls 配置文件:
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
 
# 应用state
sudo 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
sls文件可以互相include,目录与文件名用.连接,如有这样的文件结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
tree /srv/salt/file/base
 
roles
    webserver
        - init.sls
        - packages.sls
        - start.sls
users
    - barney.sls
    - fred.sls
    - wilma.sls
    - betty.sls
    - dba.sls
    - all.sls
sites
    - init.sls
    src
        - first.html
- top.sls
其中 users/dba.sls 可以include其它:
1
2
include:
- users.wilma
目录 users 与 文件名wilma通过.连接,也可以使用相对路径,比如 users/all.sls
1
2
3
4
5
include:
- .wilma
- .betty
- .barney
- .fred
如果目录下面有个 init.sls文件,那么直接引用目录名,就相当于引用了这个init.sls:
1
2
include:
- sites
在根目录下面,可以有一个 top.sls 文件,叫做 highstate,这个文件中可以定义哪些目标minion,执行哪些state,通过命令 salt \* state.highstate 可以执行这个 top.sls
例如:
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
如果一个sls文件需要依赖其它state先执行,可以用 require 语法实现,如:
1
2
3
4
5
roles_webserver_start:
 service.running:
 - name: nginx
 - require:
   - pkg: nginx
还可以监控其它对象的修改,如果修改了再次再执行时就重新运行,比如监控一个html文件,当文件有修改时,就重新启动nginx:
1
2
3
4
5
6
7
8
9
10
11
$ cat /srv/salt/file/base/sites/init.sls
sites_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
sls文件中还可以定义 order 值,以指定该sate执行的顺序,例如可以设置成 1 或 last
1
2
3
4
5
$ cat /srv/salt/file/base/run_first.sls
run_first:
 cmd.run:
 - name: 'echo "I am run first."'
 - order: 1
还有另外一个非常有用的属性:failhard,如果在state中定义了 failhard=true ,那么这个state一旦失败了,整个highstate或其后的state会终止执行。
如果只是想测试一下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]'
在minion机器上,通过 grains.setval 命令设置的grains会保存在配置文件 /etc/salt/grains里
1
2
3
4
5
[vagrant@minion1 ~]$ sudo cat /etc/salt/grains
myenv: prod
roles:
- webserver
- appserver
grains是保存在minion上面的一些静态属性数据,而pillar则相反,是保存在master上面的动态可以修改的数据,并且可以控制哪些minion可以看到哪些数据。默认情况下,master配置文件中的所有数据都添加到Pillar中,且对所有minion可用。master上配置文件中定义pillar_roots,用来指定pillar的数据存储在哪个目录
1
2
3
pillar_roots:
   base:
    - /srv/salt/pillar/base
和state一样,pillar目录下面也有一个top.sls,作为入口,里面包含其它sls文件:
1
2
3
4
$ cat /srv/salt/pillar/base/top.sls
base:
 '*':
 - default
引用的default.sls示例:
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
在State文件中将可以引用Pillar数据,比如引用ngx_home_dir:
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-8
import logging
logger = 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)
自定义的可执行模块在master上编写好后,需要同步到minion上去,才能在minion上执行,同步完成后,需要有点延迟才能被salt-minion加载进去。
1
2
3
$ sudo salt \* saltutil.sync_modules
 
$ sudo salt \* hello.id
自定义states相应的py文件放到 _states目录下面,其中实现的函数需要返回一个包含 name, changes, result, comment这个四键值的字典,并且实现的时候需要考虑到 test=true时的情况,并做相应处理
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 os
import logging
 
def 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
接着在sls文件中用到我们上面编写的自定义state函数
1
2
3
4
5
6
$ cat custom.sls
 
custom_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
与自定义module和states不同,自定义grains不需要显式地用salt命令来运行,而是一旦同步到minion,相应自定义的函数会自动执行,并上报grains值给master。
在自定义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 platform
import logging
logger = logging.getLogger(__name__)
 
def _get_hostname():
    hostname = platform.node()
    logger.debug('Using hostname: {0}'.format(hostname))
    return hostname
 
def 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 grains
 
def 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
同步后,好面自定义的grains方法 set_myenv和set_roles 会自动执行,函数的返回值会保存到 minion 的配置文件 /etc/salt/grains 中,并上报给master。
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