ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SaltStack从零入门:Master/Minion架构与State配置管理实战

SaltStack从零入门:Master/Minion架构与State配置管理实战 如果把服务器数量从三五台涨到几十台、上百台运维方式一定会经历一次分水岭靠人 SSH 上去敲命令已经不够用了。你会发现软件包版本对不上、配置文件漏改、服务没有设置开机自启任何一个小偏差都会在批量操作中被成倍放大。这个阶段多数团队会开始引入自动化运维工具。本文要讲的 Salt也就是 SaltStack就是其中一个很能打的选择。这个系列已经写到第二篇了。上一篇做了整体铺垫这篇就直接“从零开始干”搭建环境、建立起 master 和 minion 的通信、跑通远程命令再写一个真正能管理 Nginx 状态的文件。这里提前说清楚Salt 不是调味料也不是一些生存游戏里挖的盐块而是自动化运维领域的老牌工具 SaltStack。因为它名字太普通刚开始搜索资料时很容易混进一堆厨房菜谱。SaltStack 和 Ansible 经常被放在一起比较但 Salt 有一个非常突出的特点它不只是把命令发给目标机器更希望你提前描述这台机器“应该长成什么样”然后由它持续收敛到这个状态。这种思路很好不过对刚接触的人有一个明显门槛——概念多要理解 master、minion、key、state、pillar 这些词。所以这篇文章不贪多只做一件事用一个最小闭环把整套链路跑通。读完你可以照着在测试环境里用 Salt 管理至少一台服务器。1. 为什么要从零开始搭建一套 Salt先把场景说清楚。假设你现在负责十台机器每次上线要做这些事装好 JDK、修改 Nginx 配置、创建系统用户、把某个服务设为开机自启、同步一份配置文件。用手工 SSH 操作十台机器至少半小时而且很容易在第 7 台和第 9 台之间产生差别。如果团队再加两个人操作风格不统一配置漂移只会更严重。这时候自动化工具的价值就出来了。Salt 能让你用一条命令批量执行操作也能把整台服务器要达到的“期望状态”写进一个文件。它解决了三个核心问题批量执行一条命令同时跑在匹配到的所有机器上不用逐台登录。状态收敛通过 SLS 文件描述“这台机器要装 Nginx、要监听 8080 端口、要开机自启”Salt 会检查当前状态并自动补齐差异。可复用与可追溯所有配置都是文本文件可以纳入 Git 管理变更可以 review出问题可以回滚。那是不是所有场景都应该用 Salt也不是。如果你只有一两台机器配置简单、很少变更手工脚本反而更轻量。Salt 适合的是“有一定规模、需要长期维护、希望配置保持一致”的环境。它默认的架构是 Master/Minion 模式需要装 agentminion这和 Ansible 的免代理模式不太一样。如果你的环境里有大量不可预装 agent 的临时机器Salt 并不是最优解。学习路径上建议走这样一条线先搞清楚 master/minion 怎么通信再做远程命令然后写 State 状态文件最后用 Pillar 和 Grains 做参数化和分组。不要一上来就研究事件驱动、Reactor、Runner容易劝退。2. 基础概念与核心原理2.1 什么是 SaltStackSaltStack 是一个基于 Python 编写的基础设施自动化工具。它管理节点的逻辑很像“控制端 被控制端”的模式。控制端叫 master被控制端叫 minion。master 负责下发命令和状态文件minion 在自己的机器上执行并把结果返回给 master。从官方定位看Salt 的能力范围很广远程命令执行、配置管理、软件部署、定时任务、事件触发的自动化响应等。但万变不离其宗所有能力都建立在 master 和 minion 能稳定通信这件事上。2.2 Master、Minion 与 Key 认证Master 是管理中枢默认监听两个端口4505publish 端口负责向 minion 发布命令。4506ret 端口负责接收 minion 返回的结果。Minion 启动后会生成一对密钥并把公钥发送给 master 请求认证。管理员在 master 上把 minion 的公钥加入“已接受”列表两者之间才能正常通信。这是 Salt 安全模型的一部分也意味着认证关系是双向确认过的。新手最容易误以为 minion 启动后就能直接用但实际上如果 minion 的 key 还处于未接受状态master 下发命令时不会收到任何结果。所以在“跑通 Salt”这条路上接受 key 是第一个必过的关卡。2.3 远程执行与 State 状态管理远程执行很好理解master 下发一个函数调用minion 执行后返回结果。例如salt * cmd.run uptime这条命令会向所有 minion 下发uptime命令并把每台机器返回的文本汇总到终端。State 状态管理是 Salt 更核心的能力。你可以把它理解成“声明式配置”不写“怎么装 Nginx”而写“目标机器上必须有一个叫 nginx 的软件包必须有一个运行中的 nginx 服务”。Salt 在 minion 上检查现状如果不符合就执行动作让它符合如果已经符合就跳过。在较新版本的 Salt 中执行状态管理的命令是state.apply。它等价于应用 highstate。老资料里常见的state.highstate也还能用但新项目建议直接用state.apply。SLS 文件是 Salt State 的编排文件本质上是一个 YAML 描述文件。文件名和目录名决定了这个状态叫什么例如webserver/init.sls对应状态名webserver。2.4 Grains 与 Pillar 的区别这两个概念很容易混淆但定位完全不同。Grains 是 minion 端的静态信息比如操作系统类型、CPU 架构、内存大小、IP 地址。它由 minion 自己收集master 可以查询也可以用来做目标匹配。可以粗暴理解为“机器自报的户口信息”。Pillar 是 master 向特定 minion 下发的变量数据。它由管理员在 master 上定义只有匹配到的 minion 才能拿到对应的数据。Pillar 特别适合存放环境差异参数比如不同环境的 Nginx 监听端口、数据库地址、应用版本号。举个例子同一套 SLS 文件通过 Pillar 给测试环境的 minion 传入 8080 端口给生产环境的 minion 传入 80 端口状态描述不需要改一行。这就是“配置与逻辑分离”的价值。2.5 和常见自动化工具对比工具架构模式是否需要 agent适用场景特点SaltStackMaster/Minion需要大规模服务器、配置管理、事件自动化速度快状态管理强大通信模型较重Ansible无中心/SSH不需要临时任务、批量命令、轻量配置管理上手简单依赖 SSH速度取决于连接数PuppetMaster/Agent 或单机需要长期配置管理模型成熟语法学习曲线较陡手工脚本无不需要少量机器、一次性任务简单直接难以复用和收敛差异这里要说明表里的对比是常规理解不涉及绝对优劣。选型时还要看团队熟悉度、网络环境、是否需要持续状态修正。3. 环境准备与安装3.1 最小环境学习阶段不需要很复杂的集群两台机器足够也可以在一台机器上同时安装 master 和 minion 用于体验。生产环境建议 master 独立部署不混装业务组件。本文以两台服务器为例master管理节点负责下发命令和状态文件。minion被管理节点可以理解成我们要自动化的那台业务服务器。操作系统可以是 Ubuntu/Debian 或 CentOS/RHEL 系安装命令略有差别。下面的命令不写死某一个版本具体版本以你实际操作环境为准思路是通用的。3.2 安装 salt-masterUbuntu/Debian 系sudo apt update sudo apt install -y salt-masterCentOS/RHEL 系sudo yum install -y salt-master安装后启动并设置开机自启sudo systemctl enable --now salt-master确认 master 服务状态sudo systemctl status salt-master看到active (running)说明启动成功。3.3 安装 salt-minion在 minion 机器上安装软件包sudo apt update sudo apt install -y salt-minion或者sudo yum install -y salt-minion注意测试环境里如果你只有一台机器也可以在同一台机器上装 salt-minion用 localhost 作为 master 地址体验完整流程。3.4 启动与防火墙放行启动 minion 并设置开机自启sudo systemctl enable --now salt-minion关键点来了。master 默认监听 4505 和 4506 端口minion 要能访问这两个端口才能完成通信。如果机器启用了防火墙需要放行sudo firewall-cmd --permanent --add-port4505/tcp sudo firewall-cmd --permanent --add-port4506/tcp sudo firewall-cmd --reload如果使用的是云服务器还要在安全组里放行这两个端口。这里有一个常见误区只放行 4506 而忘记 4505。实际上 4505 是命令下发端口不放行时 master 发不了命令minion 自然没有响应。4. 核心流程拆解从认证到第一条远程命令4.1 配置 master 和 minionmaster 的默认配置文件在/etc/salt/masterminion 的默认配置文件在/etc/salt/minion。为了保持主配置干净推荐在/etc/salt/master.d/和/etc/salt/minion.d/目录下创建自定义配置片段。在 minion 上创建/etc/salt/minion.d/master.confmaster: 192.168.1.10 id: web-test-01master写 master 机器的 IP 或主机名id是当前 minion 的唯一标识。id 默认会取主机名但显式指定更可控。修改后重启 minionsudo systemctl restart salt-minion如果配置无误minion 会主动向 master 发起注册请求。4.2 接受 minion 密钥回到 master 机器上查看当前密钥状态salt-key -L输出类似Accepted Keys: Unaccepted Keys: web-test-01 Rejected Keys:这时web-test-01还没有被接受。接受单个 minionsalt-key -a web-test-01如果是测试环境也可以直接接受所有未接受的 keysalt-key -A生产环境不建议直接-A因为可能把不该信任的 minion 也加进来。如果不小心接受了错误的 key可以用删除命令移除salt-key -d web-test-01通常删除后还需要在 minion 端删除/etc/salt/pki/minion下的密钥文件并重启 minion才会重新注册一个全新的 key。这个细节在真实排障中经常用到。4.3 连通性测试接受 key 后回到 master 执行最基本的测试命令salt * test.ping输出web-test-01: TrueTrue表示 master 和 minion 链路已经通了。所有后续操作都建立在这个基础上。如果返回Minion did not return就要回到前面的步骤排查后面第 7 章会详细讲。4.4 远程命令执行连通后批量执行命令就没问题了salt * cmd.run uptime输出web-test-01: 14:22:01 up 3 days, 12:33, 1 user, load average: 0.00, 0.01, 0.05你还可以用不同的目标匹配方式来选择要执行的机器而不只是*# 通配符匹配 salt web-* test.ping # 正则匹配 salt -E web-\d test.ping # 列表匹配 salt -L web-test-01,web-test-02 test.ping # 基于 grains 匹配 salt -G os:Ubuntu test.ping这些匹配方式在后续执行 State 时同样适用。需要特别提醒的是cmd.run能力太强在生产环境执行前一定要确认目标表达式是否准确不要随手一个*覆盖所有机器。5. 完整示例用 State 管理 Nginx 配置远程命令已经跑通接下来进入 Salt 的本体State 状态管理。我们会用一段真实可用的配置让 minion 上自动完成 Nginx 安装、配置和启动。5.1 规划目录结构默认情况下Salt 会从 master 的/srv/salt目录读取状态文件从/srv/pillar目录读取 Pillar 数据。如果你的发行版打包时自定义了路径可以先在 master 配置里显式指定。在 master 的/etc/salt/master.d/salt_paths.conf中设置file_roots: base: - /srv/salt pillar_roots: base: - /srv/pillar修改后重启 salt-mastersudo systemctl restart salt-master然后创建目录sudo mkdir -p /srv/salt/webserver/files sudo mkdir -p /srv/pillar5.2 编写顶层 top.slsSalt 使用 top file 来决定哪些 minion 应用哪些 State。先在/srv/salt/top.sls中写一个最简单的绑定# /srv/salt/top.sls base: web-test-01: - webserver这个文件的意思是在 base 环境中匹配到web-test-01这个 minion 时应用名为webserver的状态。webserver对应/srv/salt/webserver/init.sls。5.3 编写 Nginx 的 SLS 文件在/srv/salt/webserver/init.sls中定义三段状态安装 nginx 软件包。通过模板渲染并放置 Nginx 配置文件。确保 nginx 服务运行并开机自启。# /srv/salt/webserver/init.sls nginx: pkg.installed: - name: nginx nginx-config: file.managed: - name: /etc/nginx/conf.d/salt-demo.conf - source: salt://webserver/files/salt-demo.conf - template: jinja - defaults: listen_port: 8080 server_name: salt-demo.local - require: - pkg: nginx nginx-service: service.running: - name: nginx - enable: True - require: - pkg: nginx - watch: - file: nginx-config重点解释几个关键点file.managed会把 master 上的模板文件推送到 minion 指定路径。template: jinja表示该文件使用 Jinja 模板渲染。defaults可以给模板传递默认变量。watch的作用是监听配置文件变化。如果nginx-config这个状态在后续执行中发现文件内容变化就会触发 nginx-service 重启服务。5.4 添加配置模板在 master 上创建/srv/salt/webserver/files/salt-demo.conf# /srv/salt/webserver/files/salt-demo.conf server { listen {{ listen_port }}; server_name {{ server_name }}; root /usr/share/nginx/html; index index.html; }这是一个缩略版 Nginx 配置只为了演示模板渲染效果生产环境需要根据实际场景补全。当 Salt 渲染这个文件时{{ listen_port }}会被替换成defaults里传入的 8080。5.5 用 Pillar 传参数为了让不同环境可以复用同一套状态文件可以把可变参数放到 Pillar 里。创建/srv/pillar/nginx.sls# /srv/pillar/nginx.sls nginx: listen_port: 8080 server_name: salt-demo.local再创建/srv/pillar/top.sls# /srv/pillar/top.sls base: web-test-01: - nginxPillar 数据被修改后需要通知 minion 重新拉取salt web-test-01 saltutil.refresh_pillar查看 minion 是否拿到了 Pillarsalt web-test-01 pillar.data接下来把 SLS 文件里的静态defaults改成从 Pillar 读取这样更贴近真实用法# /srv/salt/webserver/init.sls {% set nginx_cfg pillar.get(nginx, {}) %} {% set listen_port nginx_cfg.get(listen_port, 8080) %} {% set server_name nginx_cfg.get(server_name, salt-demo.local) %} nginx: pkg.installed: - name: nginx nginx-config: file.managed: - name: /etc/nginx/conf.d/salt-demo.conf - source: salt://webserver/files/salt-demo.conf - template: jinja - defaults: listen_port: {{ listen_port }} server_name: {{ server_name }} - require: - pkg: nginx nginx-service: service.running: - name: nginx - enable: True - require: - pkg: nginx - watch: - file: nginx-config这里的pillar.get(nginx, {})是一种安全写法。如果某个 minion 没有配置 Nginx 的 Pillar会使用兜底默认值 8080 和salt-demo.local不会因为键不存在而渲染失败。5.6 执行 state.apply先在 master 上做一次“预演”确认 SLS 能被正确解析salt web-test-01 state.show_sls webserver如果输出能列出 nginx、nginx-config、nginx-service 三段状态说明语法没问题。真正执行时salt web-test-01 state.apply执行后Salt 会读取 top.sls找到web-test-01对应的状态组逐项检查并应用。6. 运行结果与效果验证6.1 预期输出state.apply执行成功时输出会给出每段状态的执行结果。简化后类似web-test-01: ---------- ID: nginx Function: pkg.installed Result: True Comment: Package nginx is already installed Changes: ID: nginx-config Function: file.managed Result: True Comment: File /etc/nginx/conf.d/salt-demo.conf is in the correct state Changes: ID: nginx-service Function: service.running Result: True Comment: Service nginx is already running Changes: Summary for web-test-01 ------------ Succeeded: 3 Failed: 0 ------------ Total states run: 3看到Succeeded且Failed为 0说明这一轮状态应用成功。6.2 验证 Web 服务到 minion 机器上检查端口监听情况ss -lntp | grep 8080再在 master 或本机发起 HTTP 请求curl http://192.168.1.20:8080如果 Nginx 返回 HTML 内容说明配置文件已经被正确渲染并加载。6.3 再次执行验证幂等性Salt 的状态管理设计目标之一是幂等。再次执行salt web-test-01 state.apply第二次执行后通常Succeeded的状态数量不变但每段状态的Comment会明确提示“已经处于正确状态”。这里的关键是观察Changes字段第一次执行时安装软件包可能有Changes第二次如果所有段都没有实际变更说明状态已经收敛没有反复摇摆。如果第二次执行仍然重复修改配置文件或反复重启服务就需要检查模板变量是否每次渲染结果不同或者是否需要调整watch和require的依赖关系。7. 常见问题与排查思路Salt 体系比较庞大刚上手时问题集中在通信、key 和 SLS 渲染三个环节。下面列出几个高频问题。问题现象可能原因排查方式解决方案salt * test.ping返回 Minion did not returnminion 服务未启动或 key 未接受或网络不通检查systemctl status salt-minion查看salt-key -L确认 4505
返回列表