如何使用 SSH 进行自动化运维?有哪些常用的自动化工具和脚本?
SSH 自动化运维是后端工程师和运维人员的高频工作场景。无论是批量部署、定时巡检还是故障恢复,SSH 都是底层通道。下面从工具选型、实战脚本到生产注意事项,系统梳理 SSH 自动化运维的核心知识。
核心答案:SSH 自动化运维的常用工具
SSH 自动化运维工具按复杂度可分为三层:
- 轻量脚本层:Shell 脚本 + SSH 原生命令,适合简单批量操作
- 编程封装层:Fabric、Paramiko、Pexpect,适合需要逻辑判断的任务
- 配置管理层:Ansible、SaltStack(SSH 模式),适合大规模集群管理
选型依据:服务器规模 < 10 台用 Shell 脚本即可;10-100 台用 Fabric;100+ 台上 Ansible。三者都基于 SSH 协议,无需在目标机器装代理。
Ansible:最主流的 SSH 自动化工具
Ansible 是当前使用最广泛的 SSH 自动化配置管理工具,核心优势是无代理(Agentless),通过 SSH 连接目标机器执行任务。
安装与连接配置
bash# 安装 pip install ansible # 配置主机清单 /etc/ansible/hosts [webservers] web1.example.com web2.example.com [dbservers] db1.example.com [all:vars] ansible_user=admin ansible_ssh_private_key_file=~/.ssh/ansible_key ansible_ssh_common_args=-o StrictHostKeyChecking=no
连接验证:
bashansible all -m ping
Playbook 实战:Nginx 部署
yaml# deploy_nginx.yml --- - hosts: webservers become: yes tasks: - name: Install nginx apt: name: nginx state: present update_cache: yes - name: Deploy config template: src: templates/nginx.conf.j2 dest: /etc/nginx/nginx.conf notify: reload nginx - name: Ensure nginx is running service: name: nginx state: started enabled: yes handlers: - name: reload nginx service: name: nginx state: reloaded
执行:ansible-playbook deploy_nginx.yml
Ansible 的关键概念
- 幂等性:同一个 Playbook 执行多次,结果一致。
apt: state=present已安装则跳过,不会重复安装 - Handler:只在被 notify 触发时执行,且在所有 task 结束后统一执行,适合重启服务这类操作
- 变量与模板:Jinja2 模板渲染配置文件,不同环境用不同变量文件(
group_vars/、host_vars/)
Fabric:Python 族的 SSH 自动化利器
Fabric 是基于 Paramiko 的 Python 库,用 Python 函数定义任务,适合需要条件判断、异常处理的自动化场景。
安装与基本用法
bashpip install fabric
python# fabfile.py from fabric import Connection, task @task def deploy(c): """Deploy app to remote server""" conn = Connection('user@web1.example.com') conn.run('cd /var/www/myapp && git pull origin main') conn.run('pip install -r requirements.txt') conn.sudo('systemctl restart myapp') @task def check_disk(c, host): """Check remote disk usage""" conn = Connection(f'admin@{host}') result = conn.run('df -h / --output=pcent | tail -1', hide=True) usage = int(result.stdout.strip().replace('%', '')) if usage > 80: print(f"WARNING: {host} disk usage {usage}%") else: print(f"OK: {host} disk usage {usage}%")
运行:fab deploy 或 fab check_disk --host=web1.example.com
Fabric 的适用场景
Fabric 的优势在于你可以用 Python 写任意逻辑:根据返回值分支、调用 API、发送通知。这在 Shell 脚本里会很啰嗦。适合中小规模(几十台)的运维自动化,尤其是团队已经用 Python 的场景。
Shell 脚本:最直接的 SSH 批处理方式
不想引入额外依赖时,Shell 脚本 + SSH 是最简方案。
并行批量执行
bash#!/bin/bash # parallel_ssh.sh SERVERS=( "admin@web1.example.com" "admin@web2.example.com" "admin@web3.example.com" ) SSH_OPTS="-i ~/.ssh/batch_key -o ConnectTimeout=10 -o StrictHostKeyChecking=no" for server in "${SERVERS[@]}"; do ssh $SSH_OPTS "$server" "$1" & done wait echo "All done."
使用:./parallel_ssh.sh "uptime" 或 ./parallel_ssh.sh "systemctl restart nginx"
带错误处理和日志的脚本
bash#!/bin/bash # batch_deploy.sh set -euo pipefail LOG="/var/log/batch_deploy.log" SERVERS_FILE="servers.txt" BRANCH="${1:-main}" log() { echo "[$(date '+%F %T')] $1" | tee -a "$LOG"; } failed=0 while read -r server; do log "Deploying to $server (branch=$BRANCH)..." if ssh -o ConnectTimeout=10 "$server" bash -s <<DEPLOY set -e cd /var/www/app git fetch origin && git reset --hard "origin/$BRANCH" npm ci && npm run build pm2 restart app DEPLOY then log "SUCCESS: $server" else log "FAILED: $server" ((failed++)) fi done < "$SERVERS_FILE" log "Finished. Failed: $failed" [ $failed -eq 0 ] || exit 1
用 SSH ControlMaster 加速连接
每次 SSH 都要建 TCP 连接和密钥交换,批量操作时开销很大。开启 ControlMaster 复用连接:
bash# ~/.ssh/config Host * ControlMaster auto ControlPath ~/.ssh/sockets/%r@%h-%p ControlPersist 600
bashmkdir -p ~/.ssh/sockets
首次连接建立 socket,后续连接复用同一 socket,速度提升明显。
Pexpect:处理交互式 SSH 会话
有些 SSH 操作需要交互(输入密码、确认 yes/no),Pexpect 可以自动化这些交互。
pythonimport pexpect def ssh_with_password(host, user, password, command): """Auto-handle password prompt in SSH session""" child = pexpect.spawn(f'ssh {user}@{host}', timeout=30) i = child.expect(['password:', 'Are you sure you want to continue connecting']) if i == 1: child.sendline('yes') child.expect('password:') child.sendline(password) child.expect(r'\$') child.sendline(command) child.expect(r'\$') print(child.before.decode().strip()) child.sendline('exit') child.close()
注意:生产环境应优先使用密钥认证而非密码。Pexpect 更适合测试环境或遗留系统的临时自动化。
Paramiko:纯 Python SSH 库
Paramiko 是 Fabric 的底层依赖,也可以直接使用,适合需要精细控制 SSH 连接的场景。
pythonimport paramiko def batch_check(servers, command): """Batch execute command and collect results""" results = {} for server in servers: ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: ssh.connect(server['host'], username=server['user'], key_filename=server.get('key')) stdin, stdout, stderr = ssh.exec_command(command) results[server['host']] = stdout.read().decode().strip() except Exception as e: results[server['host']] = f"ERROR: {e}" finally: ssh.close() return results servers = [ {'host': 'web1.example.com', 'user': 'admin', 'key': '~/.ssh/id_ed25519'}, {'host': 'web2.example.com', 'user': 'admin', 'key': '~/.ssh/id_ed25519'}, ] print(batch_check(servers, 'uptime'))
生产环境的安全加固
SSH 自动化的安全是重中之重,以下是必须做的加固措施。
密钥认证 + 最小权限
bash# 生成专用密钥(不要用默认 id_rsa) ssh-keygen -t ed25519 -f ~/.ssh/automation_key -C "automation@deploy" # 在目标机器限制密钥权限 # ~/.ssh/authorized_keys command="/usr/local/bin/automation-wrapper.sh",no-port-forwarding,no-X11-forwarding,no-pty ssh-ed25519 AAAAC3...
command= 限制该密钥只能执行指定脚本,即使密钥泄露也无法执行任意命令。
SSH 服务端配置
bash# /etc/ssh/sshd_config 关键配置 PermitRootLogin no # 禁止 root 登录 PasswordAuthentication no # 禁用密码认证 MaxAuthTries 3 # 限制尝试次数 AllowUsers admin deploy # 白名单用户 ClientAliveInterval 300 # 空闲超时 ClientAliveCountMax 2 # 超时次数
跳板机/堡垒机架构
生产环境不应让自动化脚本直连服务器,而应通过跳板机:
bash# ~/.ssh/config 配置跳板机 Host bastion HostName jump.example.com User jump_user Host web-* ProxyJump bastion User admin
这样所有连接都经过审计和管控。
工具对比与选型
| 维度 | Shell 脚本 | Fabric | Ansible |
|---|---|---|---|
| 学习成本 | 低 | 中(需会 Python) | 中(需学 YAML + 模块) |
| 适用规模 | <10 台 | 10-100 台 | 100+ 台 |
| 幂等性 | 需手动保证 | 需手动保证 | 内置支持 |
| 错误处理 | set -e 粗粒度 | Python try/except | 模块级失败检测 |
| 配置管理 | 不支持 | 不支持 | 完整支持 |
| 依赖 | 无 | Python + pip | Python + pip |
| 生态 | 无 | 一般 | 丰富(Galaxy) |
选型建议:个人项目或临时任务用 Shell;Python 团队做部署自动化用 Fabric;企业级集群管理用 Ansible。三者可以混合使用,不是互斥关系。
自动化运维实战场景
批量部署
多台服务器同时部署应用是最常见的 SSH 自动化场景。用 Ansible 实现时,Playbook 天然支持批量执行和回滚:
yaml# app_deploy.yml --- - hosts: webservers become: yes serial: 2 # 每次部署2台,降低风险 tasks: - name: Create backup archive: path: /var/www/app dest: "/backup/app_{{ ansible_date_time.iso8601 }}.tar.gz" - name: Pull latest code git: repo: https://github.com/user/app.git dest: /var/www/app version: "{{ deploy_version | default('main') }}" - name: Install dependencies command: npm ci args: chdir: /var/www/app - name: Build and restart shell: npm run build && pm2 restart app args: chdir: /var/www/app
serial: 2 实现滚动部署,避免所有服务器同时不可用。如果前两台部署失败,后续不会继续。
定时巡检与告警
用 cron + Shell 脚本实现轻量巡检,异常时通过企业微信或钉钉推送告警:
bash#!/bin/bash # health_check.sh WEBHOOK_URL="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx" SERVERS=("web1" "web2" "db1") for server in "${SERVERS[@]}"; do # 检查 CPU 负载 load=$(ssh -o ConnectTimeout=5 "$server" "cat /proc/loadavg | awk '{print \$1}'") threshold=4.0 if (( $(echo "$load > $threshold" | bc -l) )); then curl -s "$WEBHOOK_URL" -H 'Content-Type: application/json' \ -d "{\"content\":\"ALERT: $server load=$load (> $threshold)\"}" fi # 检查磁盘 disk_pct=$(ssh -o ConnectTimeout=5 "$server" "df -h / | awk 'NR==2{print \$5}' | tr -d '%'") if [ "$disk_pct" -gt 85 ]; then curl -s "$WEBHOOK_URL" -H 'Content-Type: application/json' \ -d "{\"content\":\"ALERT: $server disk=${disk_pct}%\"}" fi done
配合 cron 每分钟执行:* * * * * /opt/scripts/health_check.sh
自动备份与清理
bash#!/bin/bash # auto_backup.sh set -euo pipefail BACKUP_DIR="/backup" DATE=$(date +%Y%m%d) RETENTION=7 for server in db1 db2; do ssh admin@$server "mysqldump -u root --all-databases --single-transaction | gzip" \ > "$BACKUP_DIR/${server}_${DATE}.sql.gz" done # 保留最近7天备份 find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION -delete echo "[$(date)] Backup completed" >> /var/log/backup.log
--single-transaction 保证 InnoDB 备份一致性,不锁表。
自动化运维的常见踩坑
连接超时导致脚本卡死:SSH 默认没有连接超时,批量操作时一台机器卡住整个脚本就挂了。解决:始终设置 ConnectTimeout,Shell 用 timeout 命令包裹,Python 用 paramiko.SSHClient().connect(timeout=10)。
known_hosts 拒绝连接:首次连接新机器时 SSH 会要求确认指纹,自动化脚本会卡住。解决:StrictHostKeyChecking=no(测试环境)或在跳板机上预置 known_hosts。
并发连接数过多被拒:目标机器的 MaxStartups 限制了并发 SSH 连接数。Ansible 默认 5 个 fork,可通过 -f 调整。大批量操作建议分批执行。
密码写死在脚本里:这是最常见的安全隐患。应该用密钥认证,密钥用 ssh-agent 管理,不写进代码。
非幂等操作导致重复执行:比如部署脚本每次都执行数据库迁移,可能导致重复操作。Ansible 的模块天生幂等,Shell/Fabric 需要自己判断状态再执行。
SSH 自动化运维从 Shell 脚本到 Ansible,复杂度递增但能力也递增。掌握工具选型、安全加固和常见陷阱,才能在生产环境用得稳、用得安全。