4.3 配置管理对比——Puppet / SaltStack vs Ansible

预计阅读时间:13 分钟

📖 目录

在 Linux 运维领域,配置管理工具是自动化基础设施的基石。本文深入对比三大主流工具:PuppetSaltStackAnsible,从架构设计、语法风格、性能表现到社区生态,帮助你做出最合适的技术选型。

学习目标

学完本章,你将能够:

  • 掌握 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
}
Puppet 最佳实践:每个模块应包含 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
注意事项:Salt Master 与 Minion 之间通过证书认证通信,需确保时间同步(NTP)。生产环境中建议将 Master 配置为高可用(Multi-Master 或 Salt Syndic),避免单点故障。

三、三大工具全面对比

对比维度 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 天作为回滚备份

三种工具的执行模型对比

维度PuppetSaltStackAnsible
配置推送频率每 30 分钟拉取即时推送 + 拉取按需推送
配置漂移检测内置(持续收敛)需额外配置--check 模式
离线节点处理下次轮询时应用命令丢失(需重试)执行失败
大规模并发优秀(长连接)优秀(ZeroMQ)受限(SSH 并发)
幂等性保证声明式(自动收敛)声明式(State 幂等)模块级幂等
执行模型选择 需要「持续收敛」(配置漂移自动修复)选 Puppet;需要「即时推送」(事件驱动运维)选 SaltStack;需要「零侵入」(目标节点无 Agent)选 Ansible。没有绝对优劣,只有场景匹配。

配置管理与 IaC 的分工

维度配置管理(Ansible/Puppet/Salt)IaC(Terraform/Pulumi)
关注点「怎么配」——安装软件、部署配置、管理服务「建什么」——创建网络、计算、存储资源
目标对象操作系统内部(包、文件、服务)基础设施层(VPC、EC2、RDS)
典型操作apt install nginx、部署配置文件、启动服务创建 VPC、配置子网、部署 EC2 实例
互补关系Terraform 建好服务器后,Ansible 负责配置Ansible 创建好环境后,Terraform 可以管理其生命周期
最佳实践 Terraform 负责「创建基础设施」(VPC、EC2、RDS),Ansible 负责「配置操作系统」(安装软件、部署配置)。两者互补而非竞争,组合使用是企业级自动化标配。

配置管理工具测试框架

工具测试框架用途
Puppetpdk test / rspec-puppet单元测试 + 模块验证
SaltStacksalt-lint / pytest语法检查 + State 测试
Ansiblemolecule / 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   # 清理环境
测试先行 配置管理代码也应该有测试。在 CI 流水线中集成语法检查和单元测试,确保配置变更在合并前通过验证。molecule 是 Ansible 的事实标准测试框架,pdk test 用于 Puppet 模块。

常见错误

错误现象可能原因排查方法
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 报 UnreachableAuthentication failure SSH 密钥未配置或 sudo 权限不足 测试 ssh user@host 连通性;确认 ansible_become 配置正确

最佳实践

  1. 使用版本控制管理所有配置代码:Puppet Manifest、Salt State、Ansible Playbook 均应纳入 Git,配合 PR 审查和 CI 测试后再合并
  2. Puppet/Salt 采用角色分离:按功能(web、db、cache)划分模块/State,避免单个文件承载过多逻辑,便于复用和测试
  3. Ansible 使用 --check 模式预演:在生产执行前先跑 dry-run 模式,确认变更内容符合预期再正式执行
  4. Salt Pillar 数据加密敏感信息:数据库密码、API Key 等使用 pillar.get 配合 grains 做最小权限分发,避免明文写入 State 文件
  5. 三者均可引入测试框架: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
安全提醒 Salt 的远程执行能力非常强大,但也意味着 Master 被入侵后攻击面极大。生产环境中建议:① 关闭 auto_accept,手动签发 Minion 证书;② 使用 Pillar 做数据隔离,Minion 只能看到自己的配置;③ 关键操作走审计日志,配合 SIEM 监控异常命令。

练习题

  1. 分别用 Puppet DSL、Salt State YAML 和 Ansible Playbook 编写一段相同功能的配置:安装 nginx、部署自定义配置文件、启动服务并设置开机自启。对比三种写法的差异。
  2. 搭建一个 Salt Master + 2 个 Minion 的测试环境,使用 salt -G 按 Grains 分组执行远程命令,观察命令推送和结果回收的过程。
  3. 将一个现有的 Puppet 模块(含 Hiera 数据)翻译为等效的 Ansible Playbook,验证两者在目标节点上产生的最终配置文件内容一致。

本章总结

Puppet 与 SaltStack 以常驻代理换取集中管理与批量推送,Ansible 以 SSH 无代理换取低门槛与幂等性。选型取决于团队规模与既有习惯,没有绝对优劣;迁移时应遵循盘点、并行验证、分批切换、保留回滚的渐进路径。

延伸阅读

↑ 回到顶部