常用自动化工具的基本概念和使用场景总结
一、概述
本文解析 Shell 脚本、Ansible、Fabric、SaltStack 四款主流自动化运维工具的技术原理、核心概念与适用场景,并基于 Ubuntu 24.04 环境(控制机:10.0.0.13,目标机:10.0.0.14/10.0.0.16),完整实现「批量拷贝脚本+执行」的全流程实操。针对 Ubuntu 24.04 下 SaltStack/Ansible 安装兼容性问题,提供可直接落地的生产级解决方案,覆盖从基础原理到实战落地的全维度内容。
二、自动化运维工具核心原理与生态梳理
2.1 核心技术维度对比
| 维度 | Shell 脚本 | Ansible | Fabric | SaltStack |
|---|---|---|---|---|
| 架构模式 | 无架构(命令行串联) | 无代理架构(Agentless) | 无代理架构(Python 封装 SSH) | 有代理架构(C/S,Master/Minion) |
| 通信协议 | SSH | SSH | SSH | ZeroMQ + SSH(备用) |
| 配置方式 | 命令行/Shell 语法 | YAML(Playbook/Inventory) | Python 代码 | YAML(SLS 状态文件) |
| 执行引擎 | 系统 Shell 解释器(bash/sh) | Ansible Core(Python 编写) | Fabric 库(Python 编写) | Salt Master/Minion 守护进程 |
| 依赖基础 | 系统原生组件(无额外依赖) | Python + SSH | Python + SSH + Fabric 库 | Python + ZeroMQ + 系统服务 |
| 并发能力 | 串行执行(需手动实现并发) | 轻量级并发(默认5线程) | 自定义并发(Python 线程/进程) | 高并发(支持千级节点) |
| 状态管理 | 无(仅命令执行) | 支持(幂等性操作) | 无(需手动实现) | 强支持(SLS 状态文件,幂等性) |
| 学习成本 | 低(基础Shell语法) | 中(YAML+Ansible模块) | 中(Python基础+Fabric API) | 高(C/S架构+SLS语法+配置项多) |
2.2 核心概念
(1)Shell 脚本
- 核心定义:基于操作系统 Shell 解释器,将一系列运维命令按逻辑串联成可执行文件,实现简单自动化的工具。
- 核心概念:
- 循环语句(for/while):实现多主机批量操作;
- SSH 免密登录:核心依赖,避免手动输入密码;
- 退出码($?):判断命令执行结果,实现错误处理;
- SCP:基于 SSH 的文件传输工具,实现脚本分发。
- 技术原理:通过 Shell 解释器逐行执行命令,利用 SSH/SCP 实现跨主机操作,无中间层,直接对接系统内核。
(2)Ansible
- 核心定义:基于 Python 开发的开源配置管理和自动化部署工具,采用无代理架构,通过 SSH 实现跨主机自动化。
- 核心概念:
- Inventory(主机清单):定义被管理主机的分组、变量、连接信息;
- Playbook(剧本):YAML 格式的配置文件,包含多个 Play,每个 Play 定义对一组主机的操作;
- Module(模块):Ansible 核心执行单元(如 copy/command/service),幂等性设计;
- Task(任务):单个操作单元(如执行一个模块);
- Role(角色):Playbook 的模块化封装,实现配置复用。
- 技术原理:控制机通过 SSH 连接目标机,将模块化的 Python 脚本(模块)推送到目标机执行,执行完成后删除临时文件,无残留。
(3)Fabric
- 核心定义:基于 Python 的 SSH 封装库,将复杂的 SSH 操作抽象为简洁的 Python API,实现代码化的运维自动化。
- 核心概念:
- Connection(连接):封装单个主机的 SSH 连接信息;
- Task(任务):自定义 Python 函数,实现具体运维操作;
- put/get:文件上传/下载 API;
- run/sudo:远程执行命令/特权命令 API。
- 技术原理:通过 Python 的 paramiko 库(SSH 协议实现)封装 SSH 连接、命令执行、文件传输等操作,以代码方式替代手动 SSH 操作,灵活性更高。
(4)SaltStack
- 核心定义:基于 Python 开发的企业级自动化运维平台,采用 C/S 架构,支持批量执行、配置管理、远程执行等功能。
- 核心概念:
- Master(主控端):集中管理 Minion,下发指令、接收返回结果;
- Minion(被控端):部署在目标主机的代理进程,接收 Master 指令并执行;
- Grains(静态数据):Minion 端的静态属性(如系统版本、IP、硬件信息);
- Pillar(动态数据):Master 下发给 Minion 的动态配置(如密码、密钥);
- SLS(状态文件):YAML 格式的配置文件,定义 Minion 的最终状态(幂等性);
- Salt Key:Master 与 Minion 之间的密钥认证,确保通信安全。
- 技术原理:Master 与 Minion 建立长连接(ZeroMQ),Master 下发指令后,Minion 异步执行并返回结果,支持实时性、高并发的大规模集群管理。
2.3 适用场景精准匹配
| 工具 | 最佳适用场景 | 不适用场景 | 典型用户群体 |
|---|---|---|---|
| Shell 脚本 | 1. 临时运维操作(如批量重启服务); 2. 小型集群(≤5台)的简单批量操作; 3. 无额外依赖的极简场景 | 1. 大规模集群(≥10台); 2. 需幂等性的配置管理 | 运维新手、小型企业运维 |
| Ansible | 1. 中小规模集群(≤50台)的配置管理; 2. 批量部署/软件安装; 3. 无代理架构的合规场景 | 1. 超大规模集群(≥100台); 2. 高实时性需求 | 中小企业运维、云原生运维 |
| Fabric | 1. 定制化运维场景(如复杂的跨主机逻辑); 2. Python 生态集成的自动化; 3. 灵活的批量执行 | 1. 需状态管理的场景; 2. 零基础运维人员使用 | 开发型运维、Python 开发者 |
| SaltStack | 1. 大规模集群(≥50台)的批量管理; 2. 高并发/实时性运维需求; 3. 企业级状态管理 | 1. 小型集群(性价比低); 2. 无公网/内网隔离场景 | 大型企业运维、云服务商运维 |
三、前置统一准备
3.1 控制机 SSH 免密登录配置
SSH 免密是所有无代理架构工具的核心依赖,需优先配置:
# 1. 控制机(10.0.0.13)生成 SSH 密钥(全程无密码回车)
ssh-keygen -t rsa -b 2048 -N "" -f ~/.ssh/id_rsa
# 2. 推送公钥到所有目标机(首次执行需手动输入目标机root密码)
for host in 10.0.0.14 10.0.0.16; do
ssh-copy-id -i ~/.ssh/id_rsa.pub root@$host
done
# 3. 验证免密登录(无密码提示且输出"免密登录成功"即为合格)
for host in 10.0.0.14 10.0.0.16; do
ssh root@$host "echo '✅ $host 免密登录成功'"
done
3.2 待分发执行的测试脚本准备
控制机创建 /root/test_script.sh,用于验证批量执行效果:
#!/bin/bash
# 测试脚本:输出主机基础信息(幂等性设计,可重复执行)
set -e # 遇到错误立即退出
# 输出主机标识信息
echo -e "\n===== 脚本执行成功 - 主机信息 ====="
echo "主机IP: $(hostname -I | awk '{print $1}')"
echo "主机名: $(hostname)"
echo "系统版本: $(lsb_release -d | awk -F: '{print $2}' | sed 's/^ //')"
echo "执行时间: $(date +'%Y-%m-%d %H:%M:%S')"
echo "====================================="
# 可选:添加自定义业务逻辑(如创建测试文件)
touch /tmp/script_executed_$(date +%Y%m%d).log
echo "脚本执行日志已生成" >> /tmp/script_executed_$(date +%Y%m%d).log
赋予执行权限:
chmod +x /root/test_script.sh
四、方案1:原生 Shell 脚本实现
4.1 脚本编写(生产级优化版)
控制机创建 /root/batch_execute.sh,包含错误处理、日志输出:
#!/bin/bash
# 批量分发并执行脚本的 Shell 脚本(生产级)
set -euo pipefail
# 配置区
TARGET_HOSTS=("10.0.0.14" "10.0.0.16") # 目标主机列表
LOCAL_SCRIPT="/root/test_script.sh" # 本地脚本路径
REMOTE_SCRIPT="/tmp/test_script.sh" # 远程脚本路径
LOG_FILE="/root/batch_exec.log" # 执行日志文件
# 日志函数
log() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" >> $LOG_FILE
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}
# 前置检查
if [ ! -f "$LOCAL_SCRIPT" ]; then
log "❌ 错误:本地脚本 $LOCAL_SCRIPT 不存在"
exit 1
fi
if [ ! -x "$LOCAL_SCRIPT" ]; then
log "⚠️ 警告:本地脚本无执行权限,自动添加"
chmod +x "$LOCAL_SCRIPT"
fi
# 批量处理主机
log "===== 开始批量执行脚本 ====="
for host in "${TARGET_HOSTS[@]}"; do
log "===== 处理主机 $host ====="
# 1. 拷贝脚本到目标机
if scp -o ConnectTimeout=10 "$LOCAL_SCRIPT" root@$host:"$REMOTE_SCRIPT"; then
log "✅ $host:脚本拷贝成功"
# 2. 执行脚本
if ssh -o ConnectTimeout=10 root@$host "chmod +x $REMOTE_SCRIPT && $REMOTE_SCRIPT"; then
log "✅ $host:脚本执行成功"
else
log "❌ $host:脚本执行失败"
fi
else
log "❌ $host:脚本拷贝失败(SSH连接超时/权限错误)"
fi
done
log "===== 批量执行完成 ====="
echo -e "\n📊 执行日志已保存至:$LOG_FILE"
4.2 执行与验证
# 赋予执行权限
chmod +x /root/batch_execute.sh
# 执行脚本
/root/batch_execute.sh
# 验证结果(查看目标机执行日志)
ssh root@10.0.0.14 "cat /tmp/script_executed_$(date +%Y%m%d).log"
五、方案2:Ansible 实现(无代理架构)
5.1 Ansible 安装(Ubuntu 24.04 适配版)
Ubuntu 24.04 官方源 Ansible 版本较低,采用 pip 安装最新版:
# 安装依赖
apt update && apt install -y python3-pip python3-dev libffi-dev libssl-dev sshpass
# 安装 Ansible(避开系统源限制)
pip3 install ansible --break-system-packages
# 验证安装
ansible --version # 输出版本信息即为成功
5.2 配置与 Playbook 编写
(1)主机清单配置
创建 /etc/ansible/hosts(无则新建):
[target_hosts]
10.0.0.14 ansible_ssh_user=root ansible_ssh_timeout=10
10.0.0.16 ansible_ssh_user=root ansible_ssh_timeout=10
[target_hosts:vars]
ansible_ssh_private_key_file=/root/.ssh/id_rsa # SSH私钥路径
ansible_become=yes # 开启提权
ansible_become_method=sudo # 提权方式
ansible_become_user=root # 提权用户
(2)Playbook 编写(幂等性设计)
创建 /root/batch_script.yml:
- hosts: target_hosts
gather_facts: true # 收集主机基础信息
become: yes # 提权执行
vars:
local_script: "/root/test_script.sh"
remote_script: "/tmp/test_script.sh"
tasks:
# 1. 拷贝脚本(幂等性:仅当文件不同时更新)
- name: Copy test script to remote hosts
copy:
src: "{{ local_script }}"
dest: "{{ remote_script }}"
mode: '0755'
force: yes # 覆盖已存在的脚本
register: copy_result
tags: [copy, script]
# 2. 执行脚本(仅当拷贝成功时执行)
- name: Execute test script on remote hosts
command: "{{ remote_script }}"
register: exec_result
when: copy_result is succeeded
tags: [execute, script]
# 3. 输出执行结果(格式化)
- name: Show execution result
debug:
msg: |
🎯 主机 {{ inventory_hostname }} 执行结果:
{{ exec_result.stdout }}
when: exec_result is defined
tags: [show, script]
# 4. 验证执行结果(检查日志文件)
- name: Verify script execution
stat:
path: "/tmp/script_executed_{{ ansible_date_time.date }}.log"
register: log_file
tags: [verify, script]
- name: Show verify result
debug:
msg: "✅ {{ inventory_hostname }}:日志文件已生成"
when: log_file.stat.exists
tags: [verify, script]
5.3 执行与验证
# 执行 Playbook(指定标签,灵活执行)
ansible-playbook /root/batch_script.yml --tags copy,execute,show
# 验证结果(批量检查目标机)
ansible target_hosts -m command -a "cat /tmp/script_executed_$(date +%Y%m%d).log"
六、方案3:Fabric 实现(Python 定制化)
6.1 Fabric 环境搭建
# 创建虚拟环境(隔离依赖,避免污染系统Python)
apt install -y python3-venv
python3 -m venv /root/fabric-venv
# 激活虚拟环境
source /root/fabric-venv/bin/activate
# 安装 Fabric(最新稳定版)
pip install fabric==2.7.1 # 指定版本,避免兼容性问题
# 验证安装
fab --version # 输出版本信息即为成功
6.2 编写 Fabric 脚本(生产级)
控制机创建 /root/fabfile.py,支持并发、错误重试、日志:
from fabric import Connection
from fabric.exceptions import NetworkError, CommandTimeout
from invoke import task
import os
import logging
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed
# ===================== 全局配置区(按需修改)=====================
CONFIG = {
"user": "root",
"hosts": ["10.0.0.14", "10.0.0.16"],
"local_script": "/root/test_script.sh",
"remote_script": "/tmp/test_script.sh",
"ssh_key": "/root/.ssh/id_rsa",
"timeout": 15,
"max_workers": 2, # 并发数
"log_file": "/root/fabric_deploy.log"
}
# ===================== 日志初始化 =====================
logging.basicConfig(
filename=CONFIG["log_file"],
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
encoding="utf-8"
)
logger = logging.getLogger(__name__)
# ===================== 工具函数 =====================
def check_local_script():
"""检查本地脚本是否存在且有执行权限"""
script_path = CONFIG["local_script"]
if not os.path.exists(script_path):
err_msg = f"本地脚本 {script_path} 不存在!"
print(f"❌ {err_msg}")
logger.error(err_msg)
return False
if not os.access(script_path, os.X_OK):
warn_msg = f"本地脚本 {script_path} 无执行权限,自动添加..."
print(f"⚠️ {warn_msg}")
logger.warning(warn_msg)
os.chmod(script_path, 0o755)
print(f"✅ 本地脚本 {script_path} 检查通过")
logger.info("本地脚本检查通过")
return True
def handle_host(host):
"""处理单台主机:拷贝+执行脚本"""
print(f"\n===== 开始处理主机 {host} [{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] =====")
logger.info(f"开始处理主机 {host}")
try:
# 建立SSH连接
conn = Connection(
host=host,
user=CONFIG["user"],
connect_kwargs={
"key_filename": CONFIG["ssh_key"],
"timeout": CONFIG["timeout"],
"look_for_keys": True,
"allow_agent": False
}
)
# 1. 拷贝脚本
print(f"📤 上传脚本到 {host}:{CONFIG['remote_script']}")
conn.put(
CONFIG["local_script"],
CONFIG["remote_script"],
preserve_mode=True # 保留文件权限
)
logger.info(f"{host}:脚本上传成功")
# 2. 执行脚本
print(f"▶️ 执行脚本 {CONFIG['remote_script']}")
result = conn.run(
f"chmod +x {CONFIG['remote_script']} && {CONFIG['remote_script']}",
hide=False, # 显示执行输出
timeout=CONFIG["timeout"]
)
# 3. 结果判断
if result.ok:
success_msg = f"{host} 脚本执行成功"
print(f"✅ {success_msg}")
logger.info(success_msg)
else:
fail_msg = f"{host} 脚本执行返回非0码"
print(f"⚠️ {fail_msg}")
logger.warning(fail_msg)
conn.close()
return {"host": host, "status": "success"}
except NetworkError as e:
err_msg = f"{host} 网络错误:{str(e)}"
print(f"❌ {err_msg}")
logger.error(err_msg)
return {"host": host, "status": "failed", "error": "network_error"}
except CommandTimeout as e:
err_msg = f"{host} 执行超时:{str(e)}"
print(f"❌ {err_msg}")
logger.error(err_msg)
return {"host": host, "status": "failed", "error": "timeout"}
except Exception as e:
err_msg = f"{host} 未知错误:{str(e)}"
print(f"❌ {err_msg}")
logger.error(err_msg)
return {"host": host, "status": "failed", "error": "unknown"}
# ===================== 核心任务 =====================
@task
def deploy(c):
"""批量部署脚本(入口函数)"""
print("===== Fabric 批量脚本部署工具 =====")
# 前置检查
if not check_local_script():
return
# 并发处理主机
results = []
with ThreadPoolExecutor(max_workers=CONFIG["max_workers"]) as executor:
# 提交任务
future_to_host = {executor.submit(handle_host, host): host for host in CONFIG["hosts"]}
# 获取结果
for future in as_completed(future_to_host):
results.append(future.result())
# 输出汇总
print("\n===== 执行结果汇总 =====")
success_hosts = [r["host"] for r in results if r["status"] == "success"]
fail_hosts = [r["host"] for r in results if r["status"] == "failed"]
print(f"✅ 成功主机:{', '.join(success_hosts) if success_hosts else '无'}")
print(f"❌ 失败主机:{', '.join(fail_hosts) if fail_hosts else '无'}")
print(f"\n📊 详细日志:{CONFIG['log_file']}")
logger.info(f"执行完成 - 成功:{len(success_hosts)} 台,失败:{len(fail_hosts)} 台")
6.3 执行与验证
# 激活虚拟环境(每次执行前需激活)
source /root/fabric-venv/bin/activate
# 执行批量部署任务
fab deploy
# 验证结果
ssh root@10.0.0.14 "cat /tmp/script_executed_$(date +%Y%m%d).log"
七、方案4:SaltStack 实现(C/S 架构,Ubuntu 24.04 适配)
7.1 离线安装(解决网络访问问题)
(1)控制机(Master)安装
# 1. 下载离线包(可在联网机器下载后传输至控制机/root目录)
# 适配 Ubuntu 24.04 的 SaltStack 离线包(版本:3006.6)
wget https://repo.saltproject.io/py3/ubuntu/24.04/amd64/salt-3006.6.tar.gz -O /root/salt-3006.6.tar.gz
# 2. 解压并安装
tar -zxvf /root/salt-3006.6.tar.gz -C /root
cd /root/salt-3006.6
pip3 install -r requirements.txt --break-system-packages
python3 setup.py install --break-system-packages
# 3. 配置 Master
mkdir -p /etc/salt/master.d
cat > /etc/salt/master << EOF
interface: 0.0.0.0
publish_port: 4505
ret_port: 4506
auto_accept: True # 自动接受Minion密钥(生产环境慎用)
EOF
# 4. 启动 Master
salt-master -d # 后台运行
(2)目标机(Minion)安装
# 1. 拷贝离线包到目标机
scp /root/salt-3006.6.tar.gz root@10.0.0.14:/root
scp /root/salt-3006.6.tar.gz root@10.0.0.16:/root
# 2. 目标机执行安装(以10.0.0.14为例)
ssh root@10.0.0.14 "
tar -zxvf /root/salt-3006.6.tar.gz -C /root &&
cd /root/salt-3006.6 &&
pip3 install -r requirements.txt --break-system-packages &&
python3 setup.py install --break-system-packages &&
mkdir -p /etc/salt/minion.d &&
echo 'master: 10.0.0.13' > /etc/salt/minion &&
salt-minion -d
"
7.2 编写 SLS 状态文件
控制机创建 /srv/salt/script_deploy.sls:
# SaltStack 状态文件:分发并执行脚本(幂等性)
test_script_file:
file.managed:
- name: /tmp/test_script.sh
- source: salt://test_script.sh
- mode: 0755
- user: root
- group: root
test_script_exec:
cmd.run:
- name: /tmp/test_script.sh
- require:
- file: test_script_file
- creates: /tmp/script_executed_{{ grains['date'] }}.log # 幂等性:仅当日志不存在时执行
7.3 执行状态
# 1. 拷贝测试脚本到 SaltStack 文件根目录
cp /root/test_script.sh /srv/salt/
# 2. 批量执行状态
salt '*' state.apply script_deploy
# 3. 验证结果
salt '*' cmd.run 'cat /tmp/script_executed_$(date +%Y%m%d).log'
八、工具选型建议与生产环境落地
8.1 选型决策树
- ≤5台主机 + 零基础运维 → Shell 脚本;
- 5-50台主机 + 配置管理需求 → Ansible;
- 5-50台主机 + 定制化逻辑 → Fabric;
- ≥50台主机 + 高并发/实时性 → SaltStack。
8.2 生产环境优化建议
- 安全性:
- SSH 免密登录限制 IP(修改
/root/.ssh/authorized_keys添加from="10.0.0.13"); - Ansible/SaltStack 禁用密码登录,仅用密钥认证;
- SSH 免密登录限制 IP(修改
- 可维护性:
- 所有配置文件集中管理(如
/etc/ansible//srv/salt纳入版本控制); - 脚本添加注释、配置区与逻辑区分离;
- 所有配置文件集中管理(如
- 监控告警:
- 批量执行脚本添加邮件/钉钉告警(失败时触发);
- 对 SaltStack/Ansible 执行结果做日志监控。
九、常见问题排查
9.1 SSH 免密登录失败
- 检查目标机
/etc/ssh/sshd_config:确保PubkeyAuthentication yes; - 检查权限:
~/.ssh权限为 700,~/.ssh/authorized_keys权限为 600; - 重启 SSH 服务:
systemctl restart sshd。
9.2 Ansible 执行超时
- 修改
ansible.cfg:添加timeout = 10; - 检查目标机防火墙:放行 SSH 端口(22)。
9.3 Fabric 并发报错
- 降低
max_workers数值(避免目标机 SSH 连接数限制); - 检查目标机
sshd_config:MaxSessions调大(如 10)。
9.4 SaltStack Minion 无法连接 Master
- 检查 Master 防火墙:放行 4505/4506 端口;
- 检查 Minion 配置:
master字段正确指向控制机 IP; - 重启 Minion:
salt-minion restart。
总结
- 技术原理:Shell 脚本是基础,Ansible/Fabric 是无代理架构的进阶,SaltStack 是有代理架构的企业级方案,核心均基于 SSH/跨主机通信;
- 落地优先级:Ubuntu 24.04 环境下,优先选择 Shell/Ansible/Fabric(无网络依赖、易落地),大规模集群再考虑 SaltStack 离线安装;
- 核心能力:四款工具均可实现「批量拷贝+执行脚本」,差异在于架构、并发、学习成本,需根据集群规模和运维团队技术栈选择。

302

被折叠的 条评论
为什么被折叠?



