Saltstack
======

[toc]

简介
------
线上所有的统一配置管理工具均有saltstack完成
完成的线上任务:

1. 用于系统的初始化

2. 用于开源软件的配置管理，包括
	* nginx
	* phpfpm
	* redis
	* mongodb
	* memcache
	* ...

3. 用于管理c++开发的所有组件配置已经更新
	* roommaster
	* chatroom
	* gateserver
	* secgateserver
	* rediswriter
	* ...

## SaltStack介绍
SaltStack,一种全新的基础设施管理方式，部署轻松，在几分钟内可运行起来，扩展性好，很容易管理上万台服务器，速度够快，服务器之间秒级通讯。

### 特性
1. 具备配置管理
2. 可以远程执行
3. 基于python实现
4. 部署简单方便
5. 支持API以及自定义模块，通过python可以进行轻松的扩展
6. 主控端与被控端基于证书认证，安全可靠
7. 基于ZeroMQ进行消息传输

### 基本原理
SaltStack采用C/S模式，server端就是salt的salt-master，client端为salt-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自带的所有东西。

具体步骤如下

1. Salt stack的Master与Minion之间通过ZeroMq进行消息传递，使用了ZeroMq的发布-订阅模式，连接方式包括tcp，ipc

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执行结果，将结果输出到终端。

### 安装
saltstack的包已经加入EPEL源，可以直接通过yum安装

1. salt-master 安装
	```bash
    yum install salt-master
    ```

2. salt-minion 安装
	```bash
    wget -O install_salt.sh https://bootstrap.saltstack.com --no-check-certificate && sh install_salt.sh
    ```

### 提供的功能

#### Target
指定执行命令或者模块应用在哪个Minion上

1. globbing匹配
	```bash
    salt 'Dy-JXQ-101' test.ping
    ```
2. RE正则
	```bash
    salt -E 'Dy-JXQ-9(1-2)$' test.ping
    ```
3. grains
	```bash
    salt -G "os:CentOS" test.ping

    # 查看所有grains的键值
    salt 'test' grains.items
    # 查看所有grains项
    salt 'test' grains.ls
    # 查看某个grains的值
    salt 'test' grains.item num_cpus
    ```

    在top file中匹配grains
    ```bash
    'node_type:webserver':
    - match: grain
    - nginx
    - phpfpm
    - ```

4. nodegroups 是对minion分组
	```bash
    # /etc/salt/master.d/nodegroup.conf
    nodegroups:
      test1: 'L@test1, test2 or test3'
      test2: 'G@os:CentOS or test2'

    salt -N test1 test.ping
    ```

#### 批量执行
同Target类似例子
使用命令可以执行各种命令
```bash
	salt -E 'Dy-JXQ-10[1-9]$' cmd.run "uptime"
```

#### 多个Master
多个Master，在我们的生产环境中还未正式使用
多Master需要注意的地方：

1. 2个master之间并不会共享minion key，互相独立
2. 不会自动同步file\_roots，需要手动维护，可以使用git
3. 不会自动同步Pillar\_roots,需要手动维护，可以使用git
4. master的配置文件是独立

#### Pillar
Pillar在salt中是非常重要的组成部分，利用它可以完成动态的数据调整，可以使用sls文件编写
适用场景

1. 比较敏感的数据，如密码，key等
2. 特殊数据到指定的minion上
3. 动态内容

##### 查看Minion的pillar信息
```bash
	salt '*' pillar.items
	salt '*' pillar.item <key>
	salt '*' pillar.get <key>:<key>
```

##### 编写pillar数据

1. 指定pillar\_roots，默认是/srv/pillar

2. 在top file中使用pillar
```bash
'*':
    - {{ environment }}.epel
    - {{ environment }}.ntp
    - {{ environment }}.systembase
    - {{ environment }}.openssh
    - {{ environment }}.zabbix_agent
    - {{ environment }}.douyu_sudo
```

3. 在state中通过jinja使用pillar数据
```python
{% set zabbix_conf = salt['pillar.get']('zabbix_agent:config_settings', config_map, merge=True) %}
```

#### Grains
Grains主要提供一种对服务器静态信息的描述，可以对Grains增加自己对服务器节点的描述
grains的解析使用yaml模块进行解析，所以对齐是必须的，否则会出现不可预料的错误

通常Grains所提供的键值对有以下三种：

1. 针对常用的键值对，比如系统版本号，网卡地址，mac地址，salt有一套原生的grains定义，该定义均由salt官方提供的python脚本提供，这些python脚本均内置到salt中

2. 通过编写minion端的/etc/salt/grains 加入额外的grains定义
```bash
	environment: live
	node_type:
	  - webserver
	  - memcache
	  - room_service
	  - redis
	  - nginx_lua
```

3. 通过编写master端的/etc/salt/\_grains目录下的python脚本，自定义需要的grains值
```python
def hello():
   	agrain = {}
   	agrain['hello'] = 'saltstack'
	return agrain
```
符合salt自己自定义的要求返回一个字典即可
在三种情况下，会执行_grains目录下的py脚本
	* 重启salt-minion
	* 在master端执行 salt 'test' saltutil.sync_all
	* master端执行 salt 'test' state.highstate
备注：在room\_services的state模块中，使用了dy\_extends.py脚本对对应的服务器进行了定制化配置


#### State
它的核心是SLS(salt state file)，文件默认是YAML格式，并默认使用jinja模板
该State是salt的配置管理核心，所有的配置定义均由state文件通过salt解析完成

##### 示例文件
选取自ntp中的init.sls
```python
# Include :download:`map file <map.jinja>` of OS-specific package names and
# file paths. Values can be overridden using Pillar.
{% from "ntp/map.jinja" import ntp with context %}
{% set service = {True: 'running', False: 'dead'} %}

ntp:
  pkg.installed:
    - name: {{ ntp.lookup.package }}

{% if 'ntp_conf' in ntp.lookup %}
ntpd_conf:
  file.managed:
    - name: {{ ntp.lookup.ntp_conf }}
    - source: salt://ntp/files/ntp.conf
    - template: jinja
    - context:
      config: {{ ntp.settings.ntp_conf }}
    - require:
      - pkg: ntp
{% endif %}

{% if salt['service.status']('ntpd') != True %}
correct_time:
  cmd.run:
    - name: ntpdate 2.asia.pool.ntp.org
{% endif %}

{% if 'ntpd' in ntp.settings %}
ntpd:
  service.{{ service.get(ntp.settings.ntpd) }}:
    - name: {{ ntp.lookup.service }}
    - enable: {{ ntp.settings.ntpd }}
    - require:
      - pkg: ntp
      {% if salt['service.status']('ntpd') != True %}
      - cmd: correct_time
      {% endif %}
    - watch:
      - file: ntpd_conf
{% endif %}
```

通过适当的配置，配置管理的功能和便利性会远超puppet

##### 关于salt的render\_system
salt默认的渲染器是yaml\_jinja,salt处理我们的sls文件时，会先把文件用jinja2处理，然后传给ymal处理器在处理，然后生成的是salt需要的python数据类型。除了yaml\_jinja还有yaml\_mako,yaml\_wempy,py,pydsl
yaml\_jinja是默认的，而py是用纯python来写的。


## 线上saltstack的使用

### 约定
针对我们的业务需求和基于一定的安全考虑
线上的saltstack使用上会区分为
	* 北京机房，一个独立的salt-master
	* 外网机房，一个独立的salt-master

两个salt-master的代码基本一样，基于room\_service和nginx的配置有些微不同
nginx的模块主要是外网代理vhost的不同
room\_service外网主要使用MsgRepeater的配置

### 配置目录的组织
salt的state配置采用归一化的编排，而针对不同环境的不同变量配置采用pillar的动态特性进行区分
具体实施上采用以下的目录结构
```bash
├── pillar
│   ├── bj-test
│   │   ├── douyu_sudo.sls
│   │   ├── epel.sls
│   │   ├── init.sls
│   │   ├── memcache.sls
│   │   ├── mongodb.sls
│   │   ├── nginx_lua.sls
│   │   ├── nginx.sls
│   │   ├── ntp.sls
│   │   ├── openssh.sls
│   │   ├── phpfpm.sls
│   │   ├── redis.sls
│   │   ├── room_service.sls
│   │   ├── systembase.sls
│   │   └── zabbix_agent.sls
│   ├── dev
│   │   ├── douyu_sudo.sls
│   │   ├── epel.sls
│   │   ├── init.sls
│   │   ├── memcache.sls
│   │   ├── mongodb.sls
│   │   ├── nginx.sls
│   │   ├── ntp.sls
│   │   ├── openssh.sls
│   │   ├── phpfpm.sls
│   │   ├── redis.sls
│   │   ├── room_service.sls
│   │   ├── systembase.sls
│   │   └── zabbix_agent.sls
│   ├── live
│   │   ├── douyu_sudo.sls
│   │   ├── epel.sls
│   │   ├── init.sls
│   │   ├── memcache.sls
│   │   ├── mongodb.sls
│   │   ├── nginx_lua.sls
│   │   ├── nginx.sls
│   │   ├── ntp.sls
│   │   ├── openssh.sls
│   │   ├── phpfpm.sls
│   │   ├── redis.sls
│   │   ├── room_service.sls
│   │   ├── systembase.sls
│   │   └── zabbix_agent.sls
│   ├── pressure
│   │   ├── douyu_sudo.sls
│   │   ├── epel.sls
│   │   ├── init.sls
│   │   ├── memcache.sls
│   │   ├── mongodb.sls
│   │   ├── nginx.sls
│   │   ├── ntp.sls
│   │   ├── openssh.sls
│   │   ├── phpfpm.sls
│   │   ├── redis.sls
│   │   ├── room_service.sls
│   │   ├── systembase.sls
│   │   └── zabbix_agent.sls
│   ├── prod
│   │   ├── douyu_sudo.sls
│   │   ├── epel.sls
│   │   ├── init.sls
│   │   ├── memcache.sls
│   │   ├── mongodb.sls
│   │   ├── nginx.sls
│   │   ├── ntp.sls
│   │   ├── openssh.sls
│   │   ├── phpfpm.sls
│   │   ├── redis.sls
│   │   ├── room_service.sls
│   │   ├── systembase.sls
│   │   └── zabbix_agent.sls
│   └── top.sls
└── salt
    ├── douyu_sudo
    ├── epel
    ├── _grains
    ├── memcache
    ├── mongodb
    ├── mysql
    ├── nginx
    ├── nginx_lua
    ├── ntp
    ├── openssh
    ├── phpfpm
    ├── redis
    ├── room_service
    ├── systembase
    ├── zabbix_agent
    └── top.sls
```

#### 区分环境
利用pillar的top.sls入口文件，对符合的主机进行环境区分

1. 所有主机都必须在/etc/salt/grains中指定 'environment:prod/live/pressure/dev/bj-test‘
	用于标示主机是否属于生产环境或者测试环境

2. top.sls文件示例
```python
# top.sls
{% set environment = salt['grains.get']('environment', '') %}
include:
  - {{ environment }}
```
```python
# init.sls
{% set environment = salt['grains.get']('environment', '') %}
{{ saltenv if saltenv != None else env }}:
  '*':
    - {{ environment }}.epel
    - {{ environment }}.ntp
    - {{ environment }}.systembase
    - {{ environment }}.openssh
    - {{ environment }}.zabbix_agent
    - {{ environment }}.douyu_sudo

  'node_type:webserver':
    - match: grain
    - {{ environment }}.nginx
    - {{ environment }}.phpfpm

  'node_type:nginx':
    - match: grain
    - {{ environment }}.nginx

  'node_type:nginx_lua':
    - match: grain
    - {{ environment }}.nginx_lua

  'node_type:phpfpm':
    - match: grain
    - {{ environment }}.phpfpm

  'node_type:memcache':
    - match: grain
    - {{ environment }}.memcache

  'node_type:redis':
    - match: grain
    - {{ environment }}.redis

  'node_type:mongodb':
    - match: grain
    - {{ environment }}.mongodb

  'node_type:room_service':
    - match: grain
    - {{ environment }}.room_service
```

其中的node_type为自定义grains，用来区分主机需要安装的服务类型

#### 典型的salt模块目录组成
salt的所有使用的模块基本是自有，线上所提供的各种开源模块无法符合自身业务的需求
以nginx目录结构为例
```nginx
nginx
├── files
│   ├── conf
│   │   ├── mime.types
│   │   └── nginx.conf
│   ├── initd
│   │   └── nginxd
│   ├── logrotate
│   │   ├── nginxd
│   │   └── nginx_logfile_logrotate.py
│   ├── monitor
│   │   ├── nginx.conf
│   │   ├── nginx_monitor.conf
│   │   └── nginx_status.sh
│   ├── nginx_install.sh
│   ├── ssl_conf
│   │   ├── server.key
│   │   ├── server.pem
│   │   ├── ybadmin.crt
│   │   └── ybadminv1.key
│   ├── tar_package
│   │   ├── nginx-1.8.0.tar.gz
│   │   ├── Nginx-accesskey-2.0.3.tar.gz
│   │   └── ngx_cache_purge-master.zip
│   ├── vhost
│   │   ├── cooperate
│   │   │   ├── backend
│   │   │   │   ├── cooperate.backend.036yx.com.conf
│   │   │   │   ├── cooperate.backend.2144.com.conf
│   │   │   │   └── cooperate.backend.ilongyuan.com.cn.conf
│   │   │   └── edge
│   │   │       ├── cooperate.proxy.036yx.com.conf
│   │   │       ├── cooperate.proxy.2144.com.conf
│   │   │       └── cooperate.proxy.ilongyuan.com.cn.conf
│   │   └── core
│   │       ├── backend
│   │       │   ├── adsys.douyutv.com.conf
│   │       │   ├── api.douyutv.com.conf
│   │       │   ├── file.douyutv.com.conf
│   │       │   ├── ssl.douyutv.com.conf
│   │       │   ├── static2.douyutv.com.conf
│   │       │   ├── staticdn.douyutv.com.conf
│   │       │   ├── staticlive.douyutv.com.conf
│   │       │   ├── uc.douyutv.com.conf
│   │       │   ├── upload.douyutv.com.conf
│   │       │   └── www.douyutv.com.conf
│   │       └── edge
│   │           ├── proxy_adsys.douyutv.com.conf
│   │           ├── proxy_api.douyutv.com.conf
│   │           ├── proxy_ssl.douyutv.com.conf
│   │           ├── proxy_static2.douyutv.com.conf
│   │           ├── proxy_staticlive.douyutv.com.conf
│   │           ├── proxy_uc.douyutv.com.conf
│   │           ├── proxy_www.douyutv.com.conf
│   │           └── proxy_ybadmin.douyutv.com.conf
│   └── web_rsync
│       ├── exclude.txt
│       ├── rsync.pwd
│       └── webrsync.sh
├── init.sls
├── map.jinja
└── states
    ├── config.sls
    ├── install.sls
    ├── logrotate.sls
    ├── monitor.sls
    ├── service.sls
    ├── user.sls
    └── vhost_conf
        ├── cooperate_backend_vhost_group.sls
        ├── cooperate_proxy_vhost_group.sls
        ├── core_backend_vhost_adsys.sls
        ├── core_backend_vhost_fileupload.sls
        ├── core_backend_vhost_group.sls
        ├── core_backend_vhost_ssl.sls
        ├── core_backend_vhost_staticdn.sls
        ├── core_backend_vhost_uc.sls
        ├── core_proxy_vhost_group.sls
        ├── core_proxy_vhost_ssl.sls
        ├── douyu.sls
        └── yuba_proxy_vhost_ybadmin.sls
```

1. init.sls 为整个模块的应用入口，用于囊括states目录下面的state定义
2. map.jinja 为整个模块配置的默认变量设置，可以使用pillar覆盖map里面设置的变量，该件也是其他sls定义文件的变量的源
3. files目录存放所有需要引入的文件，包括tar包，各种配置文件
4. states目录存放所有的配置sls文件




### 使用的模块
现在使用的模块分为
```bash
salt
    ├── douyu_sudo		# 控制服务器的sudo权限
    ├── epel			# 控制所有CentOS主机的epel源
    ├── _grains			# 扩展grains
    ├── memcache		# memcache的配置管理
    ├── mongodb			# mongodb的配置管理，暂时未上线
    ├── mysql			# mysql的配置管理，暂时未上线
    ├── nginx			# nginx的配置管理，包括所有在线使用的webserver
    ├── nginx_lua		# nginx_lua环境模块，应php要求新加入的配置管理
    ├── ntp				# ntp配置管理，控制所有服务器的ntp client同步时间
    ├── openssh			# 服务器的ssh服务管理
    ├── phpfpm			# phpfpm进程配置管理，线上使用的配置统一一致，个别phpfpm特殊需求
    ├── redis			# redis的配置管理，负责控制所有redis主从关系
    ├── room_service	# c++组件的配置管理，部分功能未启用
    ├── systembase		# 系统初始化的配置管理，包括安装包，kernel参数优化
    ├── zabbix_agent	# zabbix_agent的配置管理，监控客户端
    └── top.sls			# 入口文件
```
