自动化运维工具——Salt(saltstack)

cuixiaogang

Salt(原名saltstack:2020 年更名为 Salt,强调开放生态)是一款开源的基础设施自动化与配置管理工具,由 Salt, Inc.(现为 VMware 旗下产品)开发,主要用于大规模服务器、网络设备、云基础设施的配置管理、任务自动化、远程执行和监控。它通过统一的平台实现对基础设施的集中控制,帮助企业和开发者高效管理复杂环境,降低运维成本,提升部署效率。

核心组件与架构

SaltStack 采用 C/S 架构(Master/Minion 模式),核心组件包括:

  • Salt Master
    • 中央控制节点,负责协调和分发任务,存配置数据、状态文件和认证信息。
    • 支持多 Master 部署(高可用性),适用于大规模集群。
  • Salt Minion
    • 安装在目标节点(服务器、设备)上的代理程序,负责接收 Master 的指令并执行操作,反馈结果。
    • 支持主流操作系统(Linux、Windows、macOS、Unix 等)及容器(Docker、Kubernetes)。
  • 通信层
    • 基于ZeroMQTCP长连接实现高吞吐量、低延迟的消息传递,支持异步并行执行任务。
    • 支持加密通信(AES 加密)和认证(证书签名),确保数据安全。
  • 配置语言与模块
    • 使用YAML定义状态(State)文件,描述目标系统的期望状态(如软件安装、服务启停、文件配置等)。
    • 支持通过Python脚本扩展自定义模块,灵活实现复杂逻辑。

主要功能

  • 配置管理(Configuration Management)
    • 通过 “状态声明” 定义基础设施的期望状态(如包版本、文件内容、服务状态等),自动检测并修复偏差。
    • 支持模板引擎(Jinja2),动态生成配置文件,适配不同环境。
  • 远程执行(Remote Execution)
    • 实时在多台设备上并行执行命令或脚本,支持批量操作(如查看日志、重启服务、更新系统)。
    • 支持灵活的目标选择(按主机名、IP、标签、分组等)。
  • 自动化编排(Orchestration)
    • 通过State SLS文件Pillar数据(分层变量管理)定义复杂部署流程,支持依赖关系和执行顺序控制。
    • 集成 CI/CD 管道,实现应用的持续部署和环境快速复制。
  • 云容器管理
    • 支持主流云平台(AWS、Azure、Google Cloud)和容器技术(Docker、Kubernetes),实现跨平台资源编排。
    • 自动扩展、配置云实例,同步基础设施状态。
  • 监控与报告
    • 实时收集节点状态数据,生成健康报告,支持异常预警(如磁盘空间不足、服务崩溃)。
    • 与 Prometheus、Grafana 等工具集成,实现可视化监控。

主要运行方式

Master/Minion模式(默认代理模式)

  • 原理
    • 必备组件:Master(控制中心)+ Minion(节点代理),Minion 需预先安装并与 Master 认证(证书签名)。
    • 通信:通过 ZeroMQ(默认)或 TCP 长连接,支持异步并行指令,实时双向通信。
    • 适用场景:长期管理的服务器、容器、云实例等可安装代理的环境。
  • 典型流程
    • Minion 启动后向 Master 发送认证请求,Master 签署证书(salt-key -a)。
    • 执行命令:salt '*' cmd.run 'ls -l'(Master 向所有 Minion 并行发送指令)。
    • Minion 执行后返回结果至 Master,支持实时日志或异步回调。
  • 优势:高性能(万级节点秒级响应)、全功能支持(状态管理、Pillar 变量、持久化配置)。
  • 局限:需预安装 Minion,不适合第三方设备(如网络交换机)。

Salt SSH模式(无代理模式)

  • 原理
    • 无需安装 Minion,通过 SSH(或 WinRM 对 Windows)直接连接目标节点,临时加载 Salt 模块。
    • 依赖系统 SSH 密钥或密码,支持 sudo 权限。
    • 适用场景:临时任务、无法安装代理的设备(如客户服务器)、一次性批量操作。
  • 典型用法
1
2
3
4
5
# 无代理执行命令(等同于 ansible 的 ad-hoc 模式)
salt-ssh '*' cmd.run 'df -h'

# 无代理应用状态(SLS 文件)
salt-ssh '*' state.apply webserver.sls
  • 优势:零部署成本,兼容老旧系统或第三方设备。
  • 局限:性能低于代理模式(逐台串行,默认),功能子集(不支持持久化状态、Pillar 动态变量)。

状态管理(State System)

  • 原理
    • 通过SLS(Salt State File)声明节点的期望状态(YAML格式),Salt 自动检测并修复偏差。
    • 支持Jinja2 模板,动态注入变量(如{{ pillar['db_password'] }})。
    • 执行模式:
      • state.apply:强制收敛至目标状态。
      • state.sls –test:模拟运行,预览变更(dry-run)。
  • 示例 SLS 文件(webserver.sls)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apache-installed:
pkg.installed:
- name: httpd

apache-running:
service.running:
- name: httpd
- enable: True
- require:
- pkg: apache-installed

index-file:
file.managed:
- name: /var/www/html/index.html
- source: salt://web/index.html.j2
- template: jinja
- user: apache
  • 适用场景:配置标准化(如统一Nginx版本、防火墙规则)、应用部署流水线。
  • 核心特性:幂等性(重复执行无副作用)、依赖管理(require/watch 关键字)。

远程执行(Remote Execution)

  • 原理:
    • 直接调用 Salt 模块(如 cmd.run, pkg.install, file.manage),实时控制节点。
    • 目标选择:通过 Grains(节点固有属性,如 os_family: RedHat)、Pillar(用户变量)、正则表达式精准筛选。
  • 典型命令
1
2
3
4
5
6
7
8
# 按 Grains 选择:所有 CentOS 节点重启服务
salt -G 'os_family:RedHat' service.restart httpd

# 按 Pillar 选择:数据库节点安装 MySQL
salt -I 'roles:db' pkg.install mysql-server

# 正则匹配:IP 以 192.168.1 开头的节点执行命令
salt '192.168.1.*' cmd.run 'uptime'
  • 优势:实时性强,支持复杂业务逻辑(如批量回滚、应急修复)。
  • 扩展:通过 salt-run 在 Master 端执行全局操作(如 salt-run manage.status 查看 Minion 状态)。

Orchestration 编排模式

  • 原理:
    • 定义跨节点的复杂工作流,支持顺序、并行、条件分支,通过 Orchestration SLS 或 salt-run 触发。
    • 典型工具:state.sls(带 order 参数)、orchestrate 模块、salt-run 脚本。
  • 适用场景:CI/CD 流水线、灾备切换、多组件协同部署。
  • 核心能力:全局依赖管理、跨 Master 协作(通过 salt-ssh 或多云 API)。

示例:部署微服务集群

  1. 先部署数据库,再部署应用服务器
1
2
3
4
5
6
7
8
9
10
11
12
# orchestrate/deploy.sls
deploy-db:
state.sls:
- name: db.server
- order: 10

deploy-app:
state.sls:
- name: app.server
- order: 20
- require:
- state: deploy-db
  1. 多个应用实例同时启动
1
salt-run state.orchestrate app.scale count=5

扩展模式:Proxy Minion(无代理设备管理)

  • 原理
    • 针对无法安装Minion的设备(如网络交换机、IoT 终端),通过Proxy Minion模拟 Minion 行为。
    • Proxy Minion 运行在中间服务器(或 Master 自身),通过 API/CLI 转发指令(如 SSH 登录交换机配置端口)。
  • 适用场景:异构基础设施(混合服务器、网络设备、嵌入式系统)。

配置示例:

  1. Master 配置 proxy配置文件(/etc/salt/proxy):
1
2
3
4
5
6
proxy:
proxytype: netmiko # 网络设备专用代理类型
device_type: cisco_ios
ip: 192.168.1.1
username: admin
password: secret
  1. 执行命令:
1
salt 'switch1' netmiko.send_command 'show ip interface brief'

常用命令

基础远程操作

命令格式 说明 示例与输出
salt '<target>' <module>.<func> [args] 向目标节点发送指令,支持所有 Salt 模块(如cmd.run,pkg.install salt '*' cmd.run 'echo "Hello Salt"' (所有节点执行 shell 命令)
salt '<target>' sys.list_modules 查看目标节点支持的模块(如 pkg, service) 输出:{'pkg': ['install', 'remove'], 'service': ['start', 'stop']}
salt '<target>' sys.doc <module> 查看模块函数的详细文档(如 pkg.install 的参数) salt '*' sys.doc pkg.install(显示安装包的可选参数:name, version
salt -G '<grain=value>' <module> 按 Grains(节点固有属性)筛选目标(如系统类型) salt -G 'os_family:Debian' pkg.upgrade(所有 Debian 系节点升级软件包)
salt -I '<pillar=value>' <module> 按 Pillar(用户变量)筛选目标(如角色标签) salt -I 'roles:webserver' service.restart nginx(所有 web 节点重启 Nginx)
salt -E '<regex>' <module> 正则匹配目标(如 IP 段) salt -E 'web-.*-0[1-3]' cmd.run 'uptime'(匹配以 web- 开头,结尾 01-03 的节点)

常用参数:

  • –out=json:以 JSON 格式输出结果(适合脚本解析)。
  • -t 30:设置超时时间(默认 5 秒,复杂任务延长)。
  • –static:禁用彩色输出(适合日志记录)。

状态管理(配置收敛)

命令格式 说明 示例与输出
salt '<target>' state.apply [sls_file] 应用状态文件(SLS),强制收敛至期望状态 salt '*' state.apply webserver(部署 webserver.sls 定义的配置)
salt '<target>' state.highstate 自动加载默认状态(/srv/salt/top.sls 匹配的 SLS) 初始化节点时常用,按top.sls分组部署(如base 环境的所有节点)
salt '<target>' state.test [sls] 模拟运行状态,预览变更(dry-run) salt web01 state.test webserver(显示哪些包将被安装、文件将被修改)
salt '<target>' state.show_sls <sls> 查看 SLS 文件的渲染结果(含 Jinja2 模板变量) salt '*' state.show_sls webserver.index(调试模板生成的最终配置)
salt '<target>' state.sls <sls> pillar='{"key":"value"}' 临时注入 Pillar 变量(覆盖默认值) salt web01 state.sls webserver pillar='{"port": 8080}'(动态设置端口)

关键文件:

  • top.sls:状态匹配规则(如 ‘web*’: base: webserver)。
  • sls 文件:/srv/salt/webserver/init.sls(建议按角色分目录)。

密钥与认证管理(Master 专属)

命令格式 说明 示例与操作流程
salt-key -L 列出所有 Minion 的待认证、已接受、拒绝的密钥 输出:Accepted Keys: web01<br>Denied Keys: rogue-minion
salt-key -a <minion-id> 接受指定 Minion 的认证请求 salt-key -a web01(需 Minion 首次启动时自动生成证书)
`salt-key -A 接受所有 Minion 的认证请求 -
salt-key -d <minion-id> --yes 删除已接受的 Minion 密钥(断开连接) salt-key -d web01 --yes(节点重装或密钥泄露时使用)
salt-key -p <minion-id> 打印 Minion 公钥(用于手动复制证书) 输出 PEM 格式公钥,适合离线环境部署

minion加入Master方式:

  • 修改配置文件:/etc/salt/minion
  • #master: salt修改为master: <masterIP地址>
  • #id:可以修改为IP地址,默认使用的是hostname
  • 使用salt-minion -d启动并发送认证请求。

Pillar 与 Grains 数据查看

命令格式 说明 示例与输出
salt '<target>' grains.items 查看节点所有 Grains 信息(如操作系统、IP、内核) 输出:{'os': 'CentOS', 'ip4_interfaces': {'eth0': ['192.168.1.10']}}
salt '<target>' grains.get <key> 提取特定 Grains 值(如 os_family) salt web01 grains.get os_family(输出:RedHat)
salt '<target>' pillar.items 查看节点所有 Pillar 变量(用户自定义配置) 输出:{'db_password': 'secure123', 'roles': ['web', 'app']}
salt '<target>' pillar.get <key> 提取特定 Pillar 值(如 db.port) salt db01 pillar.get db.port (输出:3306)

用途:目标选择(如 -G 'os_family:Debian')、模板动态赋值({{ grains['os'] }})。

Orchestration 编排(复杂流程)

命令格式 说明 示例与场景
salt-run state.orchestrate <sls> 执行编排 SLS 文件(跨节点工作流) salt-run state.orchestrate deploy.web_cluster(先部署数据库,再部署 Web 节点)
salt '<target>' state.sls <sls> order=10 定义执行顺序(配合 order 参数) 在 SLS 中:deploy-db: state.sls: order: 10 deploy-web: order: 20 require: deploy-db
salt-run manage.status 查看所有 Minion 连接状态 输出:web01: True<br>db01: False(检查节点是否在线)

Salt SSH 无代理模式

命令格式 说明 示例与场景
salt-ssh '<target>' <module> 无代理执行命令(SSH 直连,无需 Minion) salt-ssh '192.168.1.*' cmd.run 'free -m'(临时查看远程服务器内存)
salt-ssh '<target>' state.apply 无代理应用状态(适合一次性配置) salt-ssh 'new-server' state.apply base(初始化新服务器,无需安装代理)
salt-ssh --private-key /path key.pem 指定 SSH 密钥(替代密码认证) 适合自动化脚本,避免手动输入密码

注意:需提前配置 SSH 免密登录,或通过 –password 临时认证。

常用模块表

模块 常用函数 说明
cmd run, shell, script 执行shell命令/脚本
pkg install, upgrade, remove 软件包管理(Yum/Apt/Chocolatey)
service start, stop, restart, status 服务管理(支持 Systemd/Initd)
file managed, touch, directory 文件/目录管理(支持模板渲染)
user add, delete, mod 用户账户管理
network interface_ip_set, firewall 网络配置(IP、防火墙规则)
state apply, test, highstate 状态管理核心

常用组合命令

快速排查节点问题:

1
2
salt '*' --out=yaml grains.items  # 查看所有节点基础信息
salt -G 'mem_total<4096' cmd.run 'free -h' # 筛选内存小于 4GB 的节点

批量维护操作:

1
2
salt -I 'env:prod' service.stop httpd  # 停止生产环境所有 Apache
salt -E 'db-.*' pkg.install mysql=8.0.33 # 安装指定版本 MySQL

状态调试与回滚:

1
2
salt web01 state.show_sls webserver  # 查看渲染后的 SLS 配置
salt web01 state.highstate --refresh-pillar # 强制刷新 Pillar 并重新应用状态

SLS配置

SLS(Salt State File) 是 SaltStack 实现声明式配置管理的核心文件,通过 YAML 格式定义基础设施的期望状态(如 “Apache 服务应运行”“/etc/nginx.conf 应包含特定配置”)。Salt 会自动检测当前状态与 SLS 的偏差,并执行最小化变更以收敛至目标状态。

SLS的核心结构

核心结构:

  • ID:同一 SLS 中唯一,建议描述操作(如mysql-configured)。
  • 状态模块:Salt 内置或自定义的状态函数(如pkg.installed, file.managed)。
  • 参数:模块的输入(如包名、文件路径),支持动态变量({{ pillar['db_port'] }})。
  • 依赖:
    • require:强依赖(先执行被依赖的 ID)。
    • watch:监听变更(当被依赖的 ID 变化时,触发当前操作)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 示例:安装并启动 Apache(webserver/init.sls)
apache-installed: # ID(唯一标识,描述操作)
pkg.installed: # 状态模块(pkg 管理包,installed 表示“应已安装”)
- name: httpd # 参数:包名(不同系统可能不同,需结合 Grains 动态化)
- version: 2.4.57 # 可选:指定版本

apache-running:
service.running: # 状态模块(service 管理服务,running 表示“应运行”)
- name: httpd
- enable: True # 可选:开机自启
- require: # 依赖:先完成 apache-installed 再执行此操作
- pkg: apache-installed

index-file:
file.managed: # 状态模块(file 管理文件,managed 表示“应存在且内容正确”)
- name: /var/www/html/index.html
- source: salt://webserver/index.html.j2 # 引用 Master 端文件(salt:// 指向 /srv/salt)
- template: jinja # 启用 Jinja2 模板渲染
- user: apache # 文件属主
- group: apache
- mode: '0644'
- watch: # 监听:当文件变更时,触发依赖的服务重启
- service: apache-running

SLS的核心组件

状态匹配规则

top.sls定义环境(Environment)目标节点的映射关系,是 Salt 加载 SLS 的入口。

1
2
3
4
5
6
7
8
9
10
11
# /srv/salt/top.sls
base: # 环境名(默认 base,可扩展为 dev/prod)
'web*': # 目标匹配(通配符、Grains、Pillar 或正则)
- webserver.apache # 加载的 SLS 文件(路径:/srv/salt/webserver/apache.sls)
'db*':
- match: grain # 按 Grains 匹配(如 os_family)
- grain: os_family
- grain_val: RedHat
- db.mysql
'roles:lb': # 按 Pillar 中的角色标签匹配
- lb.nginx

匹配优先级:match: pcre(正则) > match: grain > 通配符 > 标签(Pillar)。

文件结构规范

建议按 角色 / 功能 组织 SLS 文件,提升复用性:

1
2
3
4
5
6
7
8
9
10
11
12
/srv/salt/
├── top.sls # 全局匹配规则
├── webserver/
│ ├── init.sls # 主状态(安装+配置 Apache)
│ ├── firewall.sls # 子状态(配置防火墙端口)
│ └── index.html.j2 # Jinja2 模板文件
├── db/
│ ├── mysql.sls # MySQL 安装
│ └── config.sls # 数据库配置
└── pillar/
├── top.sls # Pillar 数据匹配(类似 top.sls)
└── webserver.sls # 环境变量(如端口、密码)

Jinja2 模板:动态化配置

在 SLS 或文件中嵌入 Jinja2 语法,根据节点属性(Grains)或用户变量(Pillar)生成差异化配置。

1
2
3
4
5
6
7
8
9
10
11
12
# webserver/init.sls(动态端口)
apache-port:
file.managed:
- name: /etc/httpd/conf/httpd.conf
- source: salt://webserver/httpd.conf.j2
- template: jinja
- context: # 传递额外变量到模板
port: {{ pillar.get('apache_port', 80) }} # 默认 80,Pillar 中可覆盖

# httpd.conf.j2(模板内容)
Listen {{ port }}
ServerName {{ grains['fqdn'] }} # 节点主机名

高级特性

条件判断

1
2
3
4
5
6
7
8
9
10
# 根据操作系统选择包名
{% if grains['os_family'] == 'Debian' %}
apache-package:
pkg.installed:
- name: apache2
{% else %}
apache-package:
pkg.installed:
- name: httpd
{% endif %}

循环

1
2
3
4
5
6
7
8
# 批量创建用户(从 Pillar 获取列表)
{% for user in pillar.get('users', []) %}
user-{{ user.name }}:
user.present:
- name: {{ user.name }}
- uid: {{ user.uid }}
- home: /home/{{ user.name }}
{% endfor %}

自定义状态模块

1
2
3
4
5
6
7
8
# /srv/salt/_states/custom_service.py
def running(name, port):
return {
'name': name,
'changes': {'status': 'started'},
'result': True,
'comment': f'Service {name} on port {port} is running'
}
1
2
3
4
5
# 在 SLS 中使用自定义状态
custom-service:
custom_service.running:
- name: myapp
- port: 8080

状态测试(Dry-Run)

1
2
3
4
5
6
7
8
9
salt web01 state.apply webserver --test
# 输出:
# Summary for web01
# ------------
# Succeeded: 2 (unchanged=1, test=1)
# Failed: 0
# ------------
# Total states run: 2
# Total run time: 0.000 ms

Jinja2的主要语法

语法 说明 示例(在 SLS 或 .j2 文件中)
{{ 变量 }} 输出变量值(Grains、Pillar、自定义变量) {{ grains['os'] }} :输出节点操作系统(如 CentOS)
{% if 条件 %}...{% endif %} 条件判断(支持 Grains/Pillar 逻辑) {% if pillar['env'] == 'prod' %}80{% else %}8080{% endif %}
{% for 项 in 列表 %}...{% endfor %} 循环(批量生成配置) 批量创建用户:{% for user in pillar['users'] %}user_{{ user.name }}{% endfor %}
{{ 函数() }} 调用 Salt 模块或自定义函数 {{ salt['cmd.run']('hostname') }}:执行命令并输出结果
{{ 变量[竖线(MD语法冲突)]过滤器 }} 过滤数据(如默认值、格式化) {{ pillar.get('db_port', 3306) }}:无变量时默认 3306