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.3 | SaltStack 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台执行uptime | 23秒 | 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台)的日常管理。混合方案能让你在踩坑时有退路。记住:任何自动化工具都替代不了完善的监控和回滚机制。