Ansible vs SaltStack:3000台服务器选型实战
发布日期: 2026/07/23 阅读总量: 0
Ansible vs SaltStack:3000台服务器选型实战

Ansible vs SaltStack:3000台服务器选型实战

一、真实场景:凌晨3点的报警

2023年双11前夜,我负责的3000台Nginx服务器需要紧急更新一个安全补丁。当时用的是Ansible 2.15.3,执行一个简单的copy模块+service模块重启,结果跑了47分钟还没完。更崩溃的是,有120台服务器因为SSH连接超时导致状态不一致,部分机器更新了配置没重启,部分根本没更新。凌晨4点,老板电话打过来:“服务器怎么还在报错?”

这次事故直接让我决定:必须换方案。于是花了2周时间,在测试环境用500台服务器对比了Ansible 2.15.3和SaltStack 3006.2,最后搞了个混合方案。下面直接上干货。

二、问题:批量运维的三大痛点

  • 性能瓶颈:Ansible基于SSH,3000台机器串行或分批次执行,耗时随机器数线性增长。实测100台机器执行一个yum install需要3分20秒,3000台理论需要100分钟。
  • 状态一致性:SSH连接不稳定,网络抖动导致部分机器执行失败,需要手动重试。Ansible的retry机制在批量场景下经常漏掉。
  • 配置管理复杂度:3000台机器有不同的角色(Web、DB、Cache),Ansible的inventory和role管理在规模上去后变得混乱,变量覆盖问题频发。

三、方案对比:Ansible vs SaltStack

维度Ansible 2.15.3SaltStack 3006.2
通信方式SSH(无代理)ZeroMQ(有代理minion)
性能(500台并发)平均耗时:23秒/任务平均耗时:4.2秒/任务
配置语言YAML(Playbook)YAML(SLS)+ Jinja
学习曲线低,运维友好中,需要理解minion/master架构
状态管理幂等性依赖模块实现原生状态系统,更严格
大规模支持官方建议≤1000台官方支持10000+台
回滚能力无原生回滚支持state.sls回滚

数据来源:测试环境500台虚拟机(4核8G,CentOS 7.9),执行相同任务:更新nginx配置文件并重启。Ansible使用默认forks=5,SaltStack使用默认并发。

四、我的方案:混合架构

最终方案:日常配置管理用SaltStack,临时紧急任务用Ansible。原因:

  • SaltStack的ZeroMQ通信在批量场景下性能碾压SSH,适合3000台规模的日常巡检、配置下发。
  • Ansible无代理特性适合临时任务(比如某台机器需要单独调试),不需要提前部署minion。
  • 混合方案降低了单点故障风险:如果Salt master挂了,还能用Ansible SSH进去救急。

五、完整代码实现

5.1 SaltStack部署(Master + Minion)

环境:Salt master 1台(16核32G),minion 3000台(4核8G),CentOS 7.9,SaltStack 3006.2。

# Master安装
yum install -y https://repo.saltproject.io/py3/redhat/7/x86_64/3006/salt-repo-3006.el7.noarch.rpm
yum clean expire-cache
yum install -y salt-master salt-minion

# 配置Master(/etc/salt/master)
cat >> /etc/salt/master <<EOF
interface: 0.0.0.0
publish_port: 4505
ret_port: 4506
worker_threads: 20
max_open_files: 100000
timeout: 30
EOF

# 启动Master
systemctl enable salt-master
systemctl start salt-master

# Minion安装(批量脚本)
for ip in $(cat server_list.txt); do
    ssh root@$ip "yum install -y https://repo.saltproject.io/py3/redhat/7/x86_64/3006/salt-repo-3006.el7.noarch.rpm && yum install -y salt-minion && echo 'master: 192.168.1.100' > /etc/salt/minion.d/master.conf && systemctl enable salt-minion && systemctl start salt-minion"
done

# 接受所有minion密钥
salt-key -A -y

5.2 SaltStack SLS配置:Nginx统一管理

# /srv/salt/nginx/init.sls
nginx-install:
  pkg.installed:
    - name: nginx
    - version: 1.24.0

nginx-config:
  file.managed:
    - name: /etc/nginx/nginx.conf
    - source: salt://nginx/files/nginx.conf
    - user: root
    - group: root
    - mode: 644
    - template: jinja
    - defaults:
        worker_processes: {{ grains['num_cpus'] }}
        worker_connections: 4096

nginx-service:
  service.running:
    - name: nginx
    - enable: True
    - watch:
      - file: nginx-config

# 执行
salt '*' state.apply nginx

5.3 Ansible Playbook:紧急补丁下发

# emergency_patch.yml
---
- name: Emergency nginx patch
  hosts: all
  gather_facts: no
  serial: 50  # 每批50台,避免SSH风暴
  tasks:
    - name: Copy patch file
      copy:
        src: /tmp/nginx_security_patch.so
        dest: /usr/lib64/nginx/modules/
        owner: root
        group: root
        mode: 0644
      register: copy_result

    - name: Restart nginx if patch deployed
      service:
        name: nginx
        state: restarted
      when: copy_result.changed
      async: 30
      poll: 5

    - name: Verify nginx status
      command: nginx -t
      register: verify_result
      failed_when: "'successful' not in verify_result.stdout"

# 执行
ansible-playbook -i inventory.ini emergency_patch.yml --forks=20

5.4 性能压测脚本

#!/bin/bash
# 对比Ansible和SaltStack执行时间
# 测试环境:500台虚拟机

echo "=== Ansible 测试 ==="
for i in 1 2 3; do
    start=$(date +%s%N)
    ansible all -m shell -a "uptime" --forks=50 > /dev/null 2>&1
    end=$(date +%s%N)
    echo "Run $i: $(( (end-start)/1000000 )) ms"
done

echo "=== SaltStack 测试 ==="
for i in 1 2 3; do
    start=$(date +%s%N)
    salt '*' cmd.run "uptime" --timeout=10 > /dev/null 2>&1
    end=$(date +%s%N)
    echo "Run $i: $(( (end-start)/1000000 )) ms"
done

5.5 混合方案:Ansible调用SaltStack

#!/usr/bin/env python3
# 当Salt master挂了,用Ansible SSH执行紧急任务
# 需要提前配置SSH免密

import subprocess
import json

def emergency_execute(hosts, command):
    """用Ansible执行紧急命令"""
    inventory = {
        "all": {
            "hosts": hosts
        }
    }
    with open('/tmp/emergency_inventory.json', 'w') as f:
        json.dump(inventory, f)
    
    cmd = [
        "ansible", "all",
        "-i", "/tmp/emergency_inventory.json",
        "-m", "shell",
        "-a", command,
        "--forks", "20",
        "--ssh-common-args", "-o StrictHostKeyChecking=no"
    ]
    result = subprocess.run(cmd, capture_output=True, text=True)
    return result.stdout

# 使用示例
if __name__ == "__main__":
    hosts = ["192.168.1.1", "192.168.1.2"]
    output = emergency_execute(hosts, "systemctl restart nginx")
    print(output)

六、效果数据

场景Ansible(forks=50)SaltStack(默认)提升
500台执行uptime23秒4.2秒5.5倍
500台更新nginx配置+重启47秒8.1秒5.8倍
3000台执行yum update(模拟)约4分30秒(分批)约35秒7.7倍
配置错误率(网络抖动模拟)3.2%0.1%32倍

数据说明:测试环境为500台虚拟机(4核8G,CentOS 7.9),网络延迟模拟平均5ms抖动。Ansible使用serial=50分批,SaltStack使用默认并发。3000台数据为按比例推算。

七、避坑指南(5个真实踩过的坑)

坑1:SaltStack minion密钥认证失败

现象:部署完minion后,salt-key -L看不到任何minion。排查发现是minion配置文件中的master地址写错了,但更坑的是:minion启动时不会报错,只是默默重试。解决方案:在minion启动脚本里加一行验证:salt-call test.ping,如果失败就打印错误日志。

# 在minion启动后验证
salt-call test.ping --local 2>&1 || echo "Minion连接Master失败,请检查/etc/salt/minion.d/master.conf"

坑2:Ansible SSH连接数过多导致控制机崩溃

现象:forks设置成100,结果Ansible控制机(4核8G)直接OOM。原因是每个SSH连接会fork一个进程,100个并发进程内存爆炸。解决方案:控制forks≤50,同时使用pipelining=True减少SSH连接开销。

# ansible.cfg
[ssh_connection]
pipelining = True
forks = 50
control_path = /tmp/ansible-%%h-%%p-%%r

坑3:SaltStack state.apply执行顺序依赖

现象:先安装nginx再配置,但SLS文件里写反了顺序,导致配置模块先执行,nginx还没安装,报错。解决方案:使用require和watch明确依赖关系,不要依赖文件顺序。

nginx-config:
  file.managed:
    - name: /etc/nginx/nginx.conf
    - source: salt://nginx/files/nginx.conf
    - require:
      - pkg: nginx-install

坑4:Ansible变量覆盖导致配置错误

现象:group_vars和host_vars里定义了同一个变量,结果host_vars没生效。排查发现是inventory文件里hosts分组写错了,导致变量作用域混乱。解决方案:用ansible-inventory --graph检查变量继承关系,确保host_vars优先级高于group_vars。

# 检查变量
ansible-inventory -i inventory.ini --graph
ansible all -m debug -a "var=nginx_port"

坑5:SaltStack大并发时master内存暴涨

现象:3000台minion同时返回结果,master内存从2G飙升到12G,导致OOM。原因是默认的worker_threads=20不够,结果队列堆积。解决方案:调大worker_threads到50,同时启用returner把结果存到MySQL,减少内存占用。

# /etc/salt/master
worker_threads: 50
returner: mysql
mysql.host: '192.168.1.200'
mysql.user: 'salt'
mysql.pass: 'password'
mysql.db: 'salt_returns'

八、总结

3000台服务器,别迷信单一工具。Ansible适合小规模(≤500台)和临时任务,SaltStack适合大规模(≥1000台)的日常管理。混合方案能让你在踩坑时有退路。记住:任何自动化工具都替代不了完善的监控和回滚机制。