5月28日 00:52

如何使用 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

连接验证:

bash
ansible 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 函数定义任务,适合需要条件判断、异常处理的自动化场景。

安装与基本用法

bash
pip 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 deployfab 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
bash
mkdir -p ~/.ssh/sockets

首次连接建立 socket,后续连接复用同一 socket,速度提升明显。

Pexpect:处理交互式 SSH 会话

有些 SSH 操作需要交互(输入密码、确认 yes/no),Pexpect 可以自动化这些交互。

python
import 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 连接的场景。

python
import 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 脚本FabricAnsible
学习成本中(需会 Python)中(需学 YAML + 模块)
适用规模<10 台10-100 台100+ 台
幂等性需手动保证需手动保证内置支持
错误处理set -e 粗粒度Python try/except模块级失败检测
配置管理不支持不支持完整支持
依赖Python + pipPython + 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,复杂度递增但能力也递增。掌握工具选型、安全加固和常见陷阱,才能在生产环境用得稳、用得安全。

标签:SSH