4.3 配置管理对比——Puppet / SaltStack vs Ansible
预计阅读时间:13 分钟
📖 目录
在 Linux 运维领域,配置管理工具是自动化基础设施的基石。本文深入对比三大主流工具:Puppet、SaltStack 与 Ansible,从架构设计、语法风格、性能表现到社区生态,帮助你做出最合适的技术选型。
学习目标
学完本章,你将能够:
- 掌握 Puppet Master-Agent 架构、Catalog 编译流程与 Facter / Hiera 的角色
- 掌握 Puppet Manifest 资源声明语法与模块标准目录结构
- 理解 SaltStack Master-Minion 推拉模式与 State / Pillar / Grains 体系
- 掌握 Ansible 无代理架构、Playbook 语法与幂等性设计
- 理解三种工具在架构、性能、学习曲线上差异并做出选型判断
- 掌握从 Puppet / SaltStack 迁移到 Ansible 的路径与检查清单
前置知识
- 4.1:Ansible 自动化 Ansible 自动化——Playbook、Role、Jinja2 模板与幂等性
- ansible01 Ansible 生产实战——Role 设计、执行策略与大规模管理
- 2.1:Shell 脚本入门 Shell 脚本入门——命令执行与脚本化基础
一、Puppet:Master-Agent 架构详解
1.1 架构模型
Puppet 采用经典的 Master-Agent(主从)模式,是最早一批将"基础设施即代码"理念产品化的工具。
┌──────────────┐ HTTPS (8140) ┌──────────────┐
│ Puppet Master│ ◄─────────────────────── │ Puppet Agent │
│ (编译 Catalog)│ │ (执行 Catalog)│
└──────┬───────┘ └──────┬───────┘
│ │
▼ ▼
模块 / manifests 本地状态报告
(代码仓库) (上报给 Master)
- Puppet Master:中央服务器,负责编译 Manifest 为 Catalog(JSON 格式),并通过证书认证分发给 Agent
- Puppet Agent:安装在目标节点上的守护进程,每 30 分钟默认拉取一次 Catalog 并执行
- Facter:系统信息采集工具,收集 OS、IP、内存等 facts 传递给 Master
- Hiera:分层数据系统,实现数据与代码分离
1.2 Manifest 语法
Puppet 使用自研的声明式 DSL(Domain Specific Language),核心单元是 资源(Resource)、类(Class) 和 模块(Module)。
# 基础资源声明
package { 'nginx':
ensure => installed,
}
file { '/etc/nginx/nginx.conf':
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/nginx/nginx.conf',
require => Package['nginx'],
}
service { 'nginx':
ensure => running,
enable => true,
subscribe => File['/etc/nginx/nginx.conf'],
}
# 定义一个类
class nginx (
String $worker_processes = 'auto',
) {
package { 'nginx': ensure => installed }
file { '/etc/nginx/nginx.conf':
content => template('nginx/nginx.conf.erb'),
require => Package['nginx'],
}
service { 'nginx':
ensure => running,
subscribe => File['/etc/nginx/nginx.conf'],
}
}
# 按节点分类
node 'web01.example.com' {
include nginx
}
node 'db01.example.com' {
include mysql::server
}
manifests/、templates/、files/、metadata.json 四个标准目录。使用 pdk(Puppet Development Kit)初始化模块骨架,避免手动搭建目录结构。
二、SaltStack:Salt Master/Minion 架构
2.1 架构模型
SaltStack 同样采用 Master-Minion 模式,但使用 ZeroMQ 消息队列实现高性能通信,并支持即时命令推送(类似 Ansible 的 ad-hoc)。
┌──────────────┐ ZeroMQ (4505/4506) ┌──────────────┐
│ Salt Master │ ◄─────────────────────── │ Salt Minion │
│ (事件驱动) │ │ (执行引擎) │
└──────┬───────┘ └──────┬───────┘
│ │
▼ ▼
Pillar 数据 / States Grains (本地 facts)
(分层加密存储) (系统属性缓存)
- 4505 端口:发布/订阅通道(Master 推送命令)
- 4506 端口:请求/响应通道(Minion 上报结果)
- Pillar:按 Minion 匹配规则分发的加密数据存储,类似 Hiera
- Grains:Minion 本地收集的静态系统信息,可用于 Targeting
2.2 State 文件语法
Salt 的 State 文件使用 YAML 格式,以 SLS(Salt State)为基本单元,配合 Jinja2 模板引擎实现动态逻辑。
# /srv/salt/nginx/init.sls
nginx_installed:
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'
- require:
- pkg: nginx_installed
- watch_in:
- service: nginx_service
nginx_service:
service.running:
- name: nginx
- enable: True
# 使用 Jinja2 模板的高级 State
{% set sites = pillar.get('nginx_sites', {}) %}
{% for site, config in sites.items() %}
nginx_site_{{ site }}:
file.managed:
- name: /etc/nginx/conf.d/{{ site }}.conf
- source: salt://nginx/templates/vhost.conf.jinja
- template: jinja
- context:
server_name: {{ config.server_name }}
port: {{ config.get('port', 80) }}
{% endfor %}
2.3 远程执行
Salt 独有的亮点是其 远程执行(Remote Execution)能力,无需编写 State 即可直接在 Minion 上执行命令:
# ad-hoc 命令:所有 web 角色的 Minion
salt -G 'roles:web' cmd.run 'uptime'
# 批量执行,控制并发
salt -G 'roles:web' state.apply nginx batch=5
# 通过 Pillar 匹配
salt -I 'environment:production' pkg.install security-patch-202401
三、三大工具全面对比
| 对比维度 | Puppet | SaltStack | Ansible |
|---|---|---|---|
| 架构模式 | Master-Agent(拉取) | Master-Minion(推送/拉取混合) | 无 Agent(SSH 推送) |
| 语言/格式 | 自研 DSL(.pp) | YAML + Jinja2 | YAML + Jinja2 |
| 通信协议 | HTTPS (SSL/TLS) | ZeroMQ | SSH / Paramiko |
| 默认通信端口 | 8140 | 4505 / 4506 | 22 (SSH) |
| 部署复杂度 | 高(需安装 Master + Agent) | 中(需安装 Master + Minion) | 低(仅需控制节点 SSH) |
| 学习曲线 | 陡峭(自研语法) | 中等(YAML + 专有概念) | 平缓(YAML 为主) |
| 执行模型 | 拉取(Agent 定时轮询) | 推送 + 拉取混合 | 推送(SSH 连接执行) |
| 即时执行 | 需额外配置(Bolt) | 内置(salt 命令) | 内置(ansible 命令) |
| 大规模节点性能 | 优秀(长连接 + 缓存) | 优秀(ZeroMQ 高吞吐) | 受限(SSH 并发开销) |
| 伸缩上限(参考) | 5000+ 节点 | 10000+ 节点 | 2000-3000 节点(需优化) |
| Windows 支持 | 良好 | 良好 | 有限(需 WinRM) |
| 社区活跃度 | 中等(企业用户为主) | 中等 | 最高(GitHub Star 最多) |
| 开源/商业 | 开源 + Puppet Enterprise | 开源 + SaltStack Commercial | 开源 + Ansible Automation Platform |
| 典型用户 | 金融、政府、传统企业 | 云厂商、大规模基础设施 | 互联网公司、DevOps 团队 |
3.1 架构差异深入分析
Puppet 的拉取模型意味着 Agent 主动向 Master 请求配置,适合需要强一致性的合规场景。每 30 分钟的强制收敛确保了状态的持续正确性,但也意味着配置变更无法即时生效。
SaltStack 的混合模型兼具两种优势:Master 可以通过事件总线即时推送命令(类似 Ansible 的 ad-hoc),Minion 也可以独立拉取并执行 State。这让 Salt 既能做配置管理,也能做事件驱动的运维操作。
Ansible 的无 Agent 模式最大的优势是零侵入——目标节点只需安装 SSH 和 Python,无需额外守护进程。这极大降低了部署门槛,但也意味着每次执行都有 SSH 连接建立的开销。
3.2 语言与生态
Puppet 的 DSL 虽然陡峭,但其声明式语法在表达资源依赖关系时非常精确。Puppet Forge 上有超过 7000 个模块,覆盖了绝大多数常见软件。
Salt 和 Ansible 都基于 YAML,语法亲和力更强。Ansible 的 Galaxy 生态拥有超过 25000 个 Role,社区贡献最为活跃。
四、选型建议
4.1 按场景选择
- 大规模企业(5000+ 节点):Puppet 或 SaltStack。两者都有成熟的集群方案,Puppet 有 Puppet Enterprise 提供商业支持
- 快速上手 / 小中型团队:Ansible。零 Agent 部署,YAML 易学,Ansible Galaxy 生态丰富
- 需要即时命令执行:SaltStack 或 Ansible。两者都内置 ad-hoc 能力,Puppet 需额外配置 Bolt
- 合规与审计要求高:Puppet。其持续收敛模型和详尽的报告系统天然适合合规场景
- 混合云 / 多云环境:Ansible。对 AWS、Azure、GCP 等云平台的模块支持最为全面
- 已有 Python 技术栈:SaltStack 或 Ansible。Salt 的执行模块和 Ansible 的模块都用 Python 编写,扩展门槛低
4.2 迁移路径
从 Puppet 或 SaltStack 迁移到 Ansible 是当前最常见的路径。推荐采用渐进式迁移:
# 阶段一:用 Ansible 管理新服务,Puppet/Salt 继续管旧服务
# 在 inventory 中分组
[legacy]
web01 ansible_host=10.0.1.1
web02 ansible_host=10.0.1.2
[new]
web03 ansible_host=10.0.1.3
web04 ansible_host=10.0.1.4
# 阶段二:将 Puppet Manifest / Salt State 翻译为 Ansible Playbook
# Puppet: package { 'nginx': ensure => installed }
# 对应 Ansible:
- name: Install nginx
apt:
name: nginx
state: present
# 阶段三:全面切换后移除 Agent
# 在目标节点上卸载 puppet-agent / salt-minion
ansible all -m shell -a "systemctl stop puppet && apt remove puppet-agent -y"
ansible all -m shell -a "systemctl stop minion && apt remove salt-minion -y"
1. 盘点现有模块/State 的资源覆盖范围
2. 在 Ansible Galaxy 中搜索对应 Role 或编写自定义 Module
3. 搭建并行环境验证 Playbook 执行结果与原有配置一致
4. 分批切换节点,灰度验证
5. 切换完成后保留旧工具的代码仓库 30 天作为回滚备份
三种工具的执行模型对比
| 维度 | Puppet | SaltStack | Ansible |
|---|---|---|---|
| 配置推送频率 | 每 30 分钟拉取 | 即时推送 + 拉取 | 按需推送 |
| 配置漂移检测 | 内置(持续收敛) | 需额外配置 | 需 --check 模式 |
| 离线节点处理 | 下次轮询时应用 | 命令丢失(需重试) | 执行失败 |
| 大规模并发 | 优秀(长连接) | 优秀(ZeroMQ) | 受限(SSH 并发) |
| 幂等性保证 | 声明式(自动收敛) | 声明式(State 幂等) | 模块级幂等 |
配置管理与 IaC 的分工
| 维度 | 配置管理(Ansible/Puppet/Salt) | IaC(Terraform/Pulumi) |
|---|---|---|
| 关注点 | 「怎么配」——安装软件、部署配置、管理服务 | 「建什么」——创建网络、计算、存储资源 |
| 目标对象 | 操作系统内部(包、文件、服务) | 基础设施层(VPC、EC2、RDS) |
| 典型操作 | apt install nginx、部署配置文件、启动服务 | 创建 VPC、配置子网、部署 EC2 实例 |
| 互补关系 | Terraform 建好服务器后,Ansible 负责配置 | Ansible 创建好环境后,Terraform 可以管理其生命周期 |
配置管理工具测试框架
| 工具 | 测试框架 | 用途 |
|---|---|---|
| Puppet | pdk test / rspec-puppet | 单元测试 + 模块验证 |
| SaltStack | salt-lint / pytest | 语法检查 + State 测试 |
| Ansible | molecule / ansible-lint | 集成测试 + 语法检查 |
# Ansible molecule 测试示例
# molecule/default/molecule.yml
dependency:
name: galaxy
driver:
name: docker
platforms:
- name: instance
image: geerlingguy/docker-ubuntu2204-ansible:latest
privileged: true
pre_build_image: true
provisioner:
name: ansible
verifier:
name: ansible
# molecule create # 创建测试容器
# molecule converge # 执行 Playbook
# molecule verify # 验证断言
# molecule destroy # 清理环境
常见错误
| 错误现象 | 可能原因 | 排查方法 |
|---|---|---|
Puppet Agent 报 Could not retrieve catalog |
Agent 证书未签名或已过期 | 执行 puppetserver ca list 查看待签证书,puppetserver ca sign 签发 |
| Salt Minion 无法连接 Master | 4505/4506 端口未开放,或 Minion ID 与 Master 预授权不匹配 | 检查防火墙规则;执行 salt-key -L 查看未接受的 Minion |
Puppet 语法报 Illegal variable name |
变量名包含非法字符或使用了保留关键字 | 变量名仅允许字母、数字和下划线;查看 Puppet 保留字列表 |
Salt State 执行后 Minion 报 SLS not found |
State 文件路径不正确或文件未同步到 FileRoot | 确认 /srv/salt/ 下文件存在;检查 file_roots 配置 |
Ansible Playbook 报 Unreachable 或 Authentication failure |
SSH 密钥未配置或 sudo 权限不足 | 测试 ssh user@host 连通性;确认 ansible_become 配置正确 |
最佳实践
- 使用版本控制管理所有配置代码:Puppet Manifest、Salt State、Ansible Playbook 均应纳入 Git,配合 PR 审查和 CI 测试后再合并
- Puppet/Salt 采用角色分离:按功能(web、db、cache)划分模块/State,避免单个文件承载过多逻辑,便于复用和测试
- Ansible 使用
--check模式预演:在生产执行前先跑 dry-run 模式,确认变更内容符合预期再正式执行 - Salt Pillar 数据加密敏感信息:数据库密码、API Key 等使用
pillar.get配合grains做最小权限分发,避免明文写入 State 文件 - 三者均可引入测试框架:Puppet 有
pdk test,Salt 有salt-lint,Ansible 有molecule,上线前务必做语法和集成验证
故障排查案例:Puppet Agent 配置漂移后无法自动修复
现象:某节点的 nginx 配置被手动修改后,Puppet Agent 每 30 分钟轮询一次,但配置文件始终未被还原为 Manifest 定义的状态。
排查:执行 puppet agent --test --debug 2>&1 | grep -i "catalog",发现 Catalog 编译成功但未应用到文件;检查 /etc/puppetlabs/puppet/fileserver.conf,发现 puppet:///modules/nginx/nginx.conf 的源文件路径配置错误。
根因:Puppet Master 的模块目录中缺少 nginx 模块的 files/nginx.conf 文件,Catalog 引用了不存在的文件资源,Agent 执行时报 Could not retrieve file metadata 但未中断整个运行。
修复:① 在 Puppet Master 上创建模块目录结构:pdk new module nginx;② 将 nginx.conf 放入 nginx/files/nginx.conf;③ 执行 puppet module list 确认模块已加载;④ 在 Agent 上执行 puppet agent --test 验证配置还原。
# Puppet 排障常用命令
# 在 Master 上检查 Catalog 编译
puppet catalog compile web01.example.com --trace
# 在 Agent 上强制执行一次并输出详细日志
puppet agent --test --debug --verbose
# 查看 Agent 的 Last Run Summary
cat /opt/puppetlabs/puppet/cache/state/last_run_summary.yaml | grep -A5 "resources"
# 检查 Hiera 数据查找
hiera -m "nginx::worker_processes" node=web01.example.com
SaltStack 远程执行安全注意事项
| 风险 | 场景 | 防护措施 |
|---|---|---|
| 命令注入 | Minion 上执行 cmd.run 时拼接用户输入 | 使用 cmd.run_quiet + 白名单命令,避免拼接 |
| 未授权 Minion | 新 Minion 自动接受(auto_accept: true) | 关闭 auto_accept,手动 salt-key -a 签发 |
| Pillar 数据泄露 | Minion 可查询所有 Pillar 数据 | 配置 pillar_opts: false,仅允许 Minion 查看自己的 Pillar |
| Master 被入侵 | 攻击者通过 Master 推送恶意 State | 启用 Salt SSH(无 Minion 模式)做关键操作审计 |
# Salt 安全加固配置(Master 端)
# /etc/salt/master.d/security.conf
auto_accept: false # 禁止自动接受 Minion
pillar_opts: false # Minion 无法读取 Master 的 pillar_opts
file_roots: # 限制 State 文件的根目录
base:
- /srv/salt
# 使用 target 匹配限制命令执行范围
# 仅允许在 production 环境执行
salt -I 'environment:production' cmd.run 'yum update -y' --state-output=quiet
练习题
- 分别用 Puppet DSL、Salt State YAML 和 Ansible Playbook 编写一段相同功能的配置:安装 nginx、部署自定义配置文件、启动服务并设置开机自启。对比三种写法的差异。
- 搭建一个 Salt Master + 2 个 Minion 的测试环境,使用
salt -G按 Grains 分组执行远程命令,观察命令推送和结果回收的过程。 - 将一个现有的 Puppet 模块(含 Hiera 数据)翻译为等效的 Ansible Playbook,验证两者在目标节点上产生的最终配置文件内容一致。
本章总结
Puppet 与 SaltStack 以常驻代理换取集中管理与批量推送,Ansible 以 SSH 无代理换取低门槛与幂等性。选型取决于团队规模与既有习惯,没有绝对优劣;迁移时应遵循盘点、并行验证、分批切换、保留回滚的渐进路径。