☰
SaltStack入门实战:Master/Minion架构与State配置管理指南
2026/10/27 13:35:04 网站建设 项目流程

看到 Salt 这个词,不同背景的人会联想到完全不同的东西。在视频标题里,它可能是一段航海生存冒险;在运维和开发同学的终端里,Salt 更多时候指 SaltStack,一个基于 Python 的自动化配置管理与远程执行工具。这篇内容只讨论后者,聊清楚 SaltStack 怎么搭起来、怎么用 State 管理服务器配置、怎么排查一台 Minion 为什么没执行到期望的结果。适合刚接触自动化运维的开发者,也适合已经会用 Ansible 但想对比和理解 Salt 工作方式的人。读完你会得到一套最小可复现的实验环境,以及一份可以直接搬进项目里的排错清单。

1. 先搞清楚 SaltStack 是什么,以及它和“Salt”这个关键词的关系

1.1 同一个名字,两种语境

“Salt”在中文里最常见的意思是盐,在游戏圈里可能指向某个开放世界生存游戏,在技术圈里则大概率指 SaltStack。很多初学者会因为这个名字产生困惑,搜索时也容易混进大量不相干内容。

实际项目里的 SaltStack 是一套自动化运维工具,核心能力有四个:

  • 远程执行:在一台管理机上批量给上千台服务器下发命令。
  • 配置管理:用声明式文件描述服务器应该处于什么状态,例如“安装 Nginx”“配置文件内容是……”
  • 事件驱动:Salt 内部有事件总线,可以监听服务器上报的事件并触发自动操作。
  • 编排与 API:通过 Salt API、reactor 等机制,把“发现配置漂移”和“自动修复”串成流程。

本文约定:后面提到的 Salt,默认都指 SaltStack,不再解释游戏语境。如果你是因为其他语境点进来的,也可以把这篇文章当成一次“自动化配置管理”的技术冒险:从两台虚拟机开始,搭建出一个能管理多个节点的最小系统。

1.2 SaltStack 的核心组件:Master、Minion、Syndic 与 Salt SSH

SaltStack 最常见的部署形态是 Master/Minion 架构。Master 是中心节点,负责下发命令、保存返回结果;Minion 是安装在每台被管理服务器上的轻量代理,主动连接到 Master,并执行 Master 下发的任务。

这种“Minion 主动连接 Master”的设计,和普通主从架构不同。它带来的好处是:即使被管理节点在内网或 NAT 后面,只要它能访问 Master 的端口,就能完成注册和管理,不需要在防火墙上给 Master 开返回通道。

核心组件可以用一张表概括:

组件角色说明
Salt Master中心管控节点调度任务、保存 job 结果、维护 state 和 pillar 数据
Salt Minion被管理节点代理接收任务并执行,把结果返回给 Master
Salt Syndic级联代理支持多层级联,把下层 Master 的 Minion 汇总到上层 Master
Salt SSH无代理模式不安装 Minion,通过 SSH 批量执行命令和 state
Salt API对外接口把 Salt 的能力封装成 HTTP 接口,便于集成到运维平台

实际使用中,最常见的还是 Master/Minion。下面所有实验都围绕这个模式展开。

1.3 适用场景与选型边界

在选型时,很多人会拿 Salt 和 Ansible 对比。两者都能做配置管理和远程执行,但设计取向不同。

Ansible 默认无代理,依赖 SSH,适合临时批量操作和不喜欢在机器上装额外进程的环境。Salt 默认需要装 Minion Agent,但换来的是更快的通信速度、更精细的事件机制、更灵活的状态收敛能力。

对比维度SaltStackAnsible
默认模式Master/Minion 代理SSH 无代理
通信方式ZeroMQ 或 TCPSSH
大规模并发较强,适合千级节点受 SSH 并发限制,需要调整 forks
配置语言YAML + JinjaYAML + Jinja
学习曲线较陡,概念多相对平缓
事件驱动内置 event bus较弱,依赖回调插件

需要注意,Salt 不适合做过于复杂的 workflow 编排。如果你要写“先发布 A 服务,再等健康检查,再发布 B 服务”这种强依赖流程,建议把编排逻辑放到 GitLab CI、Jenkins Pipeline 或专门的流程引擎里,Salt 负责执行单点状态和命令即可。

2. 环境准备:从两台虚拟机开始搭建最小集群

2.1 实验环境规划

学习 Salt 不需要重型集群,两台 Linux 虚拟机就够。一台做 Master,一台做 Minion。

下面以 Rocky Linux 9 为例。如果你使用 Ubuntu/Debian,安装命令和软件源会略有区别,但整体流程相同。

主机名角色假设 IP建议配置
salt-masterMaster192.168.56.102C4G
salt-minion-01Minion192.168.56.111C2G

安装前先确认几点:

  • 两台机器都能互相访问 IP。
  • 主机名尽量不要带大写和下划线,Minion 默认以主机名作为 id。
  • 防火墙端口要提前规划:Master 的 4505 端口用于 Minion 拨号,4506 端口用于任务执行和结果返回。
  • 关闭 SELinux 会造成学习环境不验证,最好把 SELinux 调整到 permissive,并确认没有系统安全策略阻断。

注意:生产环境不要轻易关闭 SELinux。遇到连接问题时先看审计日志,确认是状态还是端口策略导致。

2.2 安装 SaltStack Master

SaltStack 官方提供了软件源,安装时建议使用发行版对应的 repo 包。

Rocky Linux 9 上可以这样安装:

# 安装 epel 和 salt 官方仓库 sudo dnf install -y epel-release sudo dnf install -y https://repo.saltproject.io/salt/py3/redhat/9/x86_64/latest/salt-repo-latest.el9.noarch.rpm sudo dnf clean expire-cache # 安装 master sudo dnf install -y salt-master

安装完成后,先在主配置里做最小化调整。Salt Master 默认配置文件在/etc/salt/master,常见的几个参数如下:

# /etc/salt/master interface: 0.0.0.0 publish_port: 4505 ret_port: 4506 # 提高 worker 数量,实验环境默认值即可 worker_threads: 5 # 日志级别 log_level: info

保存后启动服务:

sudo systemctl enable --now salt-master sudo systemctl status salt-master

检查 4505 和 4506 端口是否监听:

sudo ss -lntp | grep salt

看到4505和4506处于 LISTEN 状态,说明 Master 已经准备好。

这里要理解一个关键点:4505 端口负责接收 Minion 的长连接,4506 端口负责接收 Minion 返回的执行结果。两个端口职责不同,生产环境配置安全组时不要只放行其中一个。

2.3 安装并注册 Minion

在第二台机器上安装 salt-minion:

sudo dnf install -y epel-release sudo dnf install -y https://repo.saltproject.io/salt/py3/redhat/9/x86_64/latest/salt-repo-latest.el9.noarch.rpm sudo dnf clean expire-cache sudo dnf install -y salt-minion

修改 Minion 配置,指定 Master 地址,并给 Minion 设置固定 id。这个 id 一定要稳定,因为之后所有 target 匹配、证书授权都会以它为准。

# /etc/salt/minion master: 192.168.56.10 id: minion-node-01

在较新版本中,也可以把配置放在/etc/salt/minion.d/99-salt-minion.conf,这样主配置升级时不容易被覆盖。

# /etc/salt/minion.d/99-salt-minion.conf master: 192.168.56.10 id: minion-node-01

启动 Minion:

sudo systemctl enable --now salt-minion sudo systemctl status salt-minion

启动后,Minion 会生成密钥对,并把公钥发送给 Master,等待管理员授权。

2.4 确认 Master 与 Minion 通信状态

回到 Master 上查看待授权的 key:

sudo salt-key -L

预期输出里有Unaccepted Keys,下面能看到minion-node-01。

接受 key:

sudo salt-key -a minion-node-01

再次查看:

sudo salt-key -L

minion-node-01出现在Accepted Keys中,就说明注册完成。

用 test.ping 验证通信:

sudo salt 'minion-node-01' test.ping

返回true,说明 Master 与 Minion 之间的网络、密钥、端口全部正常。

这一步最常见的坑是:Minion 启动后没有出现在 Unaccepted Keys 里。原因通常是网络不通,或者 Minion 配置里 master 地址写错。可先在 Minion 上手动执行salt-minion -l debug观察日志,确认它是否在持续尝试连接4505端口。

3. 第一个最小闭环:用 Salt 远程执行命令

3.1 test.ping 为什么是第一个要跑的命令

test.ping 并不是真正去 ping 某个 IP,而是 Master 向目标 Minion 发送一个 UTF-8 字符,Minion 原样返回。它能通过,代表整条链路已经打通:

  • Minion 能连接 Master 的 4505 端口。
  • Master 的密钥认证通过。
  • Minion 能接收任务并返回结果。

所以它是排查一切问题时的“链路自检”。

执行方式:

sudo salt 'minion-node-01' test.ping

输出:

minion-node-01: True

如果 Minion 数量多,可以一次对所有节点执行:

sudo salt '*' test.ping

这个命令会返回所有在线 Minion 的结果。

3.2 远程执行命令的语法与 target 匹配

Salt 远程执行命令的通用格式是:

salt '<target>' <module.function> [参数]

target 用来选择要执行任务的 Minion 集合。最常用的匹配方式:

匹配方式示例说明
通配符salt 'minion*' test.ping按 Minion id 通配
列表salt -L 'minion-01,minion-02' test.ping指定多个 id
正则salt -E '^minion-node-\d+$' test.ping按正则匹配 id
Grainssalt -G 'os_family:RedHat' test.ping按系统属性匹配
Pillarsalt -I 'role:web' test.ping按 Pillar 数据匹配
CIDRsalt -S '192.168.56.0/24' test.ping按 IP 网段匹配

例如批量查看所有 Minion 系统信息:

sudo salt '*' grains.item os os_family oscodename

在真实环境中,target 匹配非常关键。如果目标写得太宽,可能会把生产环境所有机器都执行一遍;写得太窄,又可能漏掉该执行的节点。建议先在最小范围用--out=json测试,确认目标节点后再执行。

3.3 把结果看懂:返回结构、jid 与耗时字段

默认输出适合人看,但脚本化处理时最好使用 JSON 输出:

sudo salt '*' test.ping --out=json

输出:

{ "minion-node-01": true }

执行远程命令时,Master 会给每次任务生成一个 job id,简称 jid。哪怕执行结果返回超时,也可以从 job cache 里找到这次任务日志。

查看已执行的任务:

sudo salt-run jobs.list_jobs

这会列出所有历史任务,包括 jid、执行时间、target、执行的函数和参数。当 Minion 掉线导致结果没有返回时,这种查询方式非常有用。

远程执行 cmd.run 时要注意谨慎使用。比如:

sudo salt '*' cmd.run 'uptime'

输出:

minion-node-01: 10:00:32 up 1 day, 2:33, 2 users, load average: 0.00, 0.01, 0.05

不过,cmd.run 是强命令,生产环境不建议开放给所有用户。如果有可能,优先使用 Salt 提供的模块函数,例如pkg.install、service.start、file.replace,而不是写一段任意 shell。原因是模块函数会校验参数、返回结构化结果,出错信息也更明确。

注意:在批量执行 cmd.run 前,一定要确认目标范围,否则一条 rm 命令可能影响大量节点。

4. 用 State 管理配置:从“执行命令”走向“声明期望状态”

4.1 State 文件、Top File 和 highstate 的关系

只靠远程执行命令,自动化程度还不够。真正有价值的是“声明期望状态”:用文件描述每台机器应该装什么包、配置文件写成什么、服务应该启动。Salt 会负责把当前状态收敛到期望状态。

这里涉及三个概念:

  • State 文件(SLS):用 YAML 写的最小状态描述。
  • Top File:把不同的 Minion 节点和不同的 State 文件关联起来。
  • Highstate:让 Master 根据 Top File 对所有目标 Minion 应用所有状态的过程。

默认目录结构:

/srv/salt/ ├── top.sls └── nginx/ └── init.sls

top.sls 示例:

# /srv/salt/top.sls base: '*': - nginx

这个文件的意思是:在 base 环境中,匹配到所有节点时,应用nginx这个 State。

4.2 用 file.managed 管理 Nginx 配置

一个 State 文件可以描述多个资源。下面用 Nginx 配置管理作为示例。

先准备一个 Nginx 配置源文件。实际项目中,通常会把文件放在 Salt 根目录的nginx/files/下。

mkdir -p /srv/salt/nginx/files

创建一个最小 Nginx 配置:

# /srv/salt/nginx/files/nginx.conf user nginx; worker_processes auto; error_log /var/log/nginx/error.log; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; } } }

然后编写 State 文件:

# /srv/salt/nginx/init.sls nginx_config: file.managed: - name: /etc/nginx/nginx.conf - source: salt://nginx/files/nginx.conf - user: root - group: root - mode: '0644' - watch_in: - service: nginx_service

解释一下关键参数:

  • name:目标机器上的最终文件路径。
  • source:文件来源,salt://协议表示从 Master 的/srv/salt目录取文件。
  • mode:文件权限,注意用字符串'0644',避免 YAML 把 0644 解析成数字。
  • watch_in:当文件内容变化时,触发等待的服务。这里关联到nginx_service这个 service 状态。

4.3 用 pkg.installed 和 service.running 写一个完整 state

完整的 Nginx state 应该包含三件事:安装包、下发配置、启动服务。

把init.sls扩展成完整版本:

# /srv/salt/nginx/init.sls nginx_install: pkg.installed: - name: nginx nginx_config: file.managed: - name: /etc/nginx/nginx.conf - source: salt://nginx/files/nginx.conf - user: root - group: root - mode: '0644' - watch_in: - service: nginx_service nginx_service: service.running: - name: nginx - enable: True - require: - pkg: nginx_install

这里用到了两个关键顺序:

  • require:nginx_service依赖nginx_install,保证先安装再启动服务。
  • watch:nginx_config通过watch_in监听服务状态,文件变化后自动重启 Nginx。

在 Salt 里,watch和require区别很大。require只控制顺序,而watch会在被监听对象发生变化时触发行为的额外反应,例如重启服务。服务重启逻辑不放在 shell 脚本里,而是由 State 系统管理,这样更容易追踪变更。

4.4 执行 highstate 与按需应用指定 state

在 Master 上执行:

sudo salt 'minion-node-01' state.apply nginx

这条命令表示只对目标节点应用nginx这个 State,不依赖 top.sls。

如果希望按 top.sls 全量应用:

sudo salt 'minion-node-01' state.highstate

返回结果会包含三个部分:

  • pkg_|-nginx_install_|-nginx_|-installed
  • file_|-nginx_config_|-/etc/nginx/nginx.conf_|-managed
  • service_|-nginx_service_|-nginx_|-running

每部分都有result: True、changes: {}、comment字段。

第一次执行时changes里会明确写出安装了什么、修改了什么:

nginx_install: ---------- new: nginx old:

再次执行时,changes应该为空,说明系统已经处于期望状态,没有配置漂移。这就是状态收敛和幂等性的表现。

5. 深入理解 Pillar、Grains 与 Jinja 模板

5.1 Grains:Minion 的静态属性

Grains 是 Minion 启动时收集的静态信息,包括操作系统、CPU 核数、内存、IP 地址等。它不会频繁变化,适合做条件判断和节点分组。

查看所有 Grains:

sudo salt 'minion-node-01' grains.items

只看某几个字段:

sudo salt 'minion-node-01' grains.item os os_family ipv4

在 State 文件里,可以用 Grains 做分支判断。例如:

{% if grains['os_family'] == 'RedHat' %} nginx_package: nginx {% elif grains['os_family'] == 'Debian' %} nginx_package: nginx {% endif %}

这样同一个 State 就能兼容不同发行版。

5.2 Pillar:敏感的定向配置

Pillar 和 Grains 最大的区别是:Pillar 由 Master 维护,可以按 Minion 定向下发,并且适合存储密码、密钥、差异化配置等数据。Pillar 内容也通过 Salt 加密通道传输,比明文写在 minon 端的文件安全得多。

默认 Pillar 目录是/srv/pillar,需要创建一份 top.sls:

# /srv/pillar/top.sls base: 'minion-node-01': - nginx

创建对应的 Pillar 数据文件:

# /srv/pillar/nginx.sls nginx: server_name: www.example.com listen_port: 8080 max_workers: 4

查看 Minion 收到的 Pillar:

sudo salt 'minion-node-01' pillar.items

输出里会包含nginx下面的所有键值。

Pillar 里的数据可以直接被 Jinja 模板读取,从而让不同节点生成不同的配置文件。注意:不要用 Pillar 暴露不必要的信息到大范围节点上。写入密钥时建议结合 Salt 的外部 Pillar 或 sdb 模块,避免密钥出现在版本控制系统的纯文本文件里。

5.3 Jinja 模板:让 state 不再写死

前面 Nginx 配置是静态文件。在真实场景中,不同服务器往往需要不同的 server_name、端口和 worker 数量,可以把 Nginx 配置改成 Jinja 模板。

先修改 State 文件,把source指向模板文件,并告诉 Salt 这个文件需要模板渲染:

# /srv/salt/nginx/init.sls nginx_config: file.managed: - name: /etc/nginx/nginx.conf - source: salt://nginx/files/nginx.conf.j2 - template: jinja - user: root - group: root - mode: '0644' - watch_in: - service: nginx_service

模板文件内容:

# /srv/salt/nginx/files/nginx.conf.j2 user nginx; worker_processes {{ pillar.get('nginx', {}).get('max_workers', 'auto') }}; error_log /var/log/nginx/error.log; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; server { listen {{ pillar.get('nginx', {}).get('listen_port', 80) }}; server_name {{ pillar.get('nginx', {}).get('server_name', 'localhost') }}; location / { root /usr/share/nginx/html; index index.html; } } }

这样,修改 Pillar 数据后重新 apply State,配置文件就会按新值渲染出来。

使用 Jinja 模板时,一个常见坑是模板变量取不到值时直接报错。建议所有 Pillar 取值都带上默认值,例如pillar.get('nginx', {}).get('listen_port', 80)。这样即使某台 Minion 没有配置对应 Pillar,也不会导致渲染失败。

5.4 参数速查表

数据来源存放位置主要用途敏感信息更新方式
GrainsMinion 端收集系统属性、分组、条件判断不存放重启 Minion 或刷新
PillarMaster 端维护节点差异化配置、敏感数据适合存放修改后继续 apply 或刷新
StateMaster 端维护描述系统期望状态不存放修改 SLS 后 apply
GitFS外部 Git 仓库存放 State 和 Pillar 文件结合 git roles提交代码后 refresh

生产环境建议把 State 和 Pillar 统一放到 Git 仓库,再通过 GitFS 或 CI 流程同步到 Master。这样配置变更可以走 Code Review,比直接在 master 上改文件更可控。

6. 运行验证与结果分析

6.1 查看执行结果和返回码

Salt 命令本身返回 0 不代表所有 Minion 都成功。看结果要关注每个 Minion 返回体里的result字段。

以 state.apply 为例:

sudo salt 'minion-node-01' state.apply nginx --out=json

输出片段:

{ "minion-node-01": { "pkg_|-nginx_install_|-nginx_|-installed": { "comment": "Package nginx is already installed", "result": true, "changes": {} }, "file_|-nginx_config_|-/etc/nginx/nginx.conf_|-managed": { "comment": "File /etc/nginx/nginx.conf is up to date", "result": true, "changes": {} } } }

判断执行是否成功,不能只看命令是否返回,也不能只看result: True,还要确认:changes是否符合预期、comment是否合理、有没有某个资源被跳过。

可以写一个简单脚本批量检查所有 Minion 的结果:

sudo salt '*' state.highstate --out=json | jq 'to_entries[] | {name: .key, result: .value.result}'

如果result是 false,就用--out=highstate查看更详细的失败信息。

6.2 用 event bus 和 job cache 观察执行过程

Salt Master 内部有事件总线,所有 Minion 的连接、任务执行、返回结果都会以事件形式在总线上流动。

查看实时事件:

sudo salt-run state.event listen

执行一条命令,Event Bus 里会出现salt/job/20250301.../new、salt/job/.../ret/minion-node-01等类型的事件。

当任务执行得非常慢,或者有大量 Minion 没有返回时,可以用 job cache 查找:

sudo salt-run jobs.lookup_jid 20250301120000123456

把 jid 替换成目标任务的 ID,就能看到该任务在各 Minion 上的返回结果。如果某些 Minion 返回为空,可以判断是执行超时还是 Minion 掉线。

6.3 幂等性验证:再次执行 highstate

Salt 设计上要求 State 是幂等的:同一个状态反复执行,结果不应该产生副作用。

验证方法很简单:

sudo salt 'minion-node-01' state.apply nginx

然后再次执行:

sudo salt 'minion-node-01' state.apply nginx

第二次执行时,changes应该是空,comment应该是类似 “Package nginx is already installed” 或 “File is up to date” 的信息。

如果第二次执行仍然产生大量 changes,说明 State 写得有问题。常见原因包括:

  • 写的不是期望状态,而是每次执行都会追加内容的脚本。
  • 模板渲染结果不稳定,每次生成的文件内容都不同。
  • cmd.run里执行的命令本身每次都会返回变化。

幂等性不是 Salt 自动保证的,而是靠写 State 的人的约束。这是配置管理工具和普通跑脚本之间最大的区别。

7. 常见问题排查

7.1 Minion 一直处于离线状态

现象:salt-key -L里能看到 Minion,但执行salt '*' test.ping时返回Minion did not return. [No response],或者 Minion 显示为离线。

可能原因:

  • 4505 或 4506 端口被防火墙拦截。
  • Minion 上master地址配置错误。
  • Minion 进程没有启动或启动后退出。
  • Master 上的 worker 线程耗尽。

排查步骤:

  1. 在 Minion 上检查进程:ps aux | grep salt-minion。
  2. 查看 Minion 日志:journalctl -u salt-minion -f。
  3. 在 Minion 上测试连接:timeout 5 bash -c '</dev/tcp/192.168.56.10/4505' && echo ok。
  4. 在 Master 上检查端口:ss -lntp | grep 4505。
  5. 查看 Master 日志:journalctl -u salt-master -f。

处理建议:如果是防火墙问题,放行 4505/4506;如果是配置问题,修正/etc/salt/minion或/etc/salt/minion.d/下的配置;如果是 worker 线程问题,适当调大worker_threads。

7.2 Minion key 认证失败

现象:执行命令时看到类似Minion did not return. [No response],或者日志里出现Authentication attempt of minion-node-01 failed.。

可能原因:

  • Minion 被重新安装过,密钥对发生了变化。
  • Master 上保留了旧的 Unaccepted/Denied Keys。
  • Minion 的 id 被另一台机器抢用。

检查方式:

sudo salt-key -L

处理建议:先删除旧 key,再重启 Minion,然后重新接受新 key:

sudo salt-key -d minion-node-01 sudo systemctl restart salt-minion sudo salt-key -a minion-node-01

预防方法是:给每台 Minion 设置固定且唯一的 id,不要依赖可能重复的 hostname。

7.3 state 执行报错定位

现象:state.apply nginx后,返回结果里某个资源的result: false。

常见错误:

错误现象可能原因处理方式
Source file salt://nginx/files/nginx.conf not foundstate 中的 source 路径错误检查/srv/salt/nginx/files/下是否有文件
Data failed to compile: Rendering SLS failedYAML 缩进或 Jinja 模板语法错误使用python -c "import yaml; yaml.safe_load(open('...'))"或salt-run fileserver.file_list检查
Job 20250... failed with return code: 1模块执行失败先手动在 Minion 上执行对应的命令,确认系统情况
After rendering, state was shown as {'file': ...}SLS 结构不完整检查 state 是否包含名称、模块函数和参数

排查顺序:

  1. 先看报错信息里的comment,它通常已经写明原因。
  2. 在 Master 上确认文件是否存在:
sudo salt-run fileserver.file_list
  1. 在 Minion 上手动执行模块函数,比如salt 'minion-node-01' pkg.install nginx或salt 'minion-node-01' service.status nginx。
  2. 用--out=highstate输出更完整的结果,而不是只看默认样例。

7.4 配置变更不生效

现象:修改了 State 文件或 Pillar 数据,重新执行state.apply,但目标机器上的配置没有变。

可能原因:

  • 修改的是文件,但没有重新 apply。
  • top.sls 没有包含这个 State,导致state.highstate没有执行它。
  • 文件权限或 watch 条件没有触发服务重启。
  • Minion 和 Master 的 fileserver 缓存没有刷新。

检查方式:

sudo salt 'minion-node-01' file.get_managed /etc/nginx/nginx.conf sudo salt 'minion-node-01' pillar.items

处理建议:

  • 先执行salt-run fileserver.clear_cache清理缓存。
  • 确认salt 'minion-node-01' state.apply nginx中file_managed状态是否有 changes。
  • 如果changes为空,说明文件内容没有变化,需要检查模板是否用了正确的变量。
  • 检查watch_in是否挂到了 service 状态上。

从根本上预防,可以把 State 文件、模板文件、Pillar 数据全部纳入 Git 版本库。每次变更都走 Review,并在 CI 里做一次state.apply --test语法检查,能减少大量“改了没生效”的问题。

8. 生产环境最佳实践与扩展方向

8.1 从单 Master 到多 Master 高可用

学习环境用一个 Master 没有问题,生产环境则要考虑单点故障。常见做法有两种:

  • 运行多个 Master,Minion 配置里可以指定多个master地址,自动切换。
  • 使用 Salt Syndic 做层级架构,上层 Master 管理多个区域 Master,适合跨机房、跨网络边界的场景。

多 Master 部署的重点不在安装,而在配置同步。所有 Master 上的 state、pillar、配置文件必须保持一致。建议用 Git 作为唯一数据源,通过 CI/CD 推送到各个 Master,避免不同 Master 上配置不一致导致执行结果漂移。

8.2 Salt SSH 和 Salt API 的使用场景

如果目标机器不能安装 Agent,可以使用 Salt SSH。它的用法和 Master/Minion 模式基本一致,只是通过 SSH 执行。

sudo salt-ssh '192.168.56.12' test.ping

Salt SSH 适合做临时接管、短期批量命令和硬件原因不方便装 Agent 的场景。但因为每次执行都要走 SSH 握手,并发和速度会比代理模式差。

如果团队想自动化执行 Salt,而不是每个人都能登录 Master,可以提供 Salt API。启用前先配置认证:

# /etc/salt/master.d/api.conf rest_cherrypy: port: 8000 host: 0.0.0.0 debug: False ssl_crt: /etc/pki/tls/certs/localhost.crt ssl_key: /etc/pki/tls/private/localhost.key

生产环境使用 Salt API 时,要把端口放在内网,并通过统一认证网关代理,避免直接暴露。API 调用还需要做权限控制,区分哪些用户可以执行只读命令、哪些用户可以执行高权限命令。

8.3 发布前检查清单

在把新 State 发布到生产环境前,建议逐项检查:

  • [ ] 所有 State 文件已经过state.apply语法校验。
  • [ ] top.sls 中引用的 SLS 文件都存在。
  • [ ] 模板文件中所有变量都有默认值。
  • [ ] Pillar 中的敏感信息没有硬编码在普通 State 里。
  • [ ] 目标节点范围是否最小化,先灰度一批节点。
  • [ ] 执行前是否备份关键配置文件。
  • [ ] 是否提前确认 Nginx、MySQL 等服务的 reload 方式。
  • [ ] 是否监控执行日志和异常事件。
  • [ ] 是否有回滚方案:旧文件版本、旧服务版本、旧 Pillar 数据。
  • [ ] 是否记录变更前后差异,便于审计。

8.4 下一步可以扩展的方向

SaltStack 的入门不难,难在把很多基础能力组合成完整的自动化体系。学完 state、pillar、grains、jinja 之后,可以按下面几个方向继续深入:

  • 自定义 module 和 state:当内置模块不能满足需求时,用 Python 编写自己的模块。
  • event reactor:利用 Salt event bus 监听文件变更、服务异常、Minion 上线等事件,自动执行修复。
  • schedulers:让 Minion 或 Master 定时执行任务,例如每小时校验一次 Nginx 配置。
  • MySQL 和数据库模块:用它管理系统账号、数据库权限。
  • 与 CI/CD 集成:在 GitLab CI 或 Jenkins 中触发 Salt highstate,实现配置变更的自动化发布。

配置管理工具的价值,不在于能执行多少条命令,而在于把服务器最终状态固化成可审查、可回滚、可重复验证的代码。从两台测试机开始,把一条 test.ping、一个 Nginx state、一次 highstate 弄明白,后面再管理几百台机器时,思路会清晰很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询