1. SSH 连接与客户端配置
一句话总结: SSH 是远程自动化的传输底座,客户端配置写在
~/.ssh/config,主机别名、端口、密钥都能按主机定制。
ssh user@host 是最基本用法,但每次都敲全参数很累,~/.ssh/config 让你用别名一键连接。
# ~/.ssh/config 示例
Host web
HostName 203.0.113.10
User deploy
Port 2222
IdentityFile ~/.ssh/deploy_key
# 之后直接
ssh web uptime
1.1 首次连接与 known_hosts
# 手动首次连接(会提示确认指纹)
ssh web
# 脚本场景跳过指纹交互(有安全代价,慎用)
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null web uptime
一句话总结: 自动化脚本里首次连接会卡在指纹确认,
-o StrictHostKeyChecking=no可跳过,但请配合固定 known_hosts 或提前预置指纹。
1.2 超时与连接保活
# 连接超时与命令超时
ssh -o ConnectTimeout=5 -o ServerAliveInterval=30 web uptime
# 批量连接时避免卡死
timeout 10 ssh web 'top -b -n 1' || echo "连接超时"
2. 免密登录与 ssh-agent
一句话总结: 免密登录靠公钥落盘到远端
authorized_keys,ssh-keygen生成、ssh-copy-id安装、ssh-agent托管私钥密码。
# 生成密钥(-t ed25519 更现代,-N 空口令)
ssh-keygen -t ed25519 -N '' -f ~/.ssh/id_ed25519
# 安装公钥到远端
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@host
# 手动安装等价命令
cat ~/.ssh/id_ed25519.pub | ssh user@host \
'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
2.1 ssh-agent 托管密钥
私钥带口令时,每次 SSH 都要输入;ssh-agent 在会话内缓存解密后的密钥。
# 启动 agent 并添加密钥
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
# 查看托管的指纹
ssh-add -l
# 转发 agent 到远端(慎用,仅受信主机)
ssh -A web
一句话总结: ssh-agent 免去反复输私钥口令;
-A的 agent 转发有中间人风险,非受信环境别开。
2.2 脚本里的非交互密钥
CI/CD 里通常把私钥作为变量注入,写入临时位置并设权限。
#!/usr/bin/env bash
set -euo pipefail
echo "$DEPLOY_KEY" > /tmp/deploy_key
chmod 600 /tmp/deploy_key
ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no deploy@host \
'systemctl restart web'
rm -f /tmp/deploy_key
3. 远程执行命令
一句话总结:
ssh host 'cmd'执行单条远程命令,复杂逻辑写成本地脚本再ssh host 'bash -s'通过 stdin 传过去执行。
# 单条命令
ssh web 'uname -a && df -h /'
# 多条命令用引号包成一段
ssh web 'cd /app && git pull && make deploy'
# 远程执行本地脚本:stdin 传输
ssh web 'bash -s' < ./remote_task.sh
3.1 环境变量与登录 shell
ssh 默认非交互 shell,$PATH 可能不含家目录下的 bin——远程命令里尽量用绝对路径。
# 显式加载环境
ssh web 'export PATH=$HOME/.local/bin:$PATH && mytool run'
# 强制登录 shell
ssh web 'bash -lc "mytool run"'
一句话总结: 远程命令的 PATH 与非交互环境与本地不同,依赖自定义 bin 时用
bash -lc加载登录环境,或写绝对路径。
3.2 远程返回值与引号层级
# 获取远程退出码
ssh web 'grep -q error /var/log/app.log' && echo "日志有 error" || echo "日志干净"
# 引号层级:本地引号剥掉后,远程再剥一层
ssh web "echo \"主机: \$(hostname)\""
# 若想远程展开 $(), 单引号包裹本地
ssh web 'echo "主机: $(hostname)"'
4. 文件同步与 rsync
一句话总结: rsync 按块差异传输,
-a归档、-z压缩、-P断点续传,是跨机同步的事实标准。
# 基本同步:本地 → 远端
rsync -avz ./dist/ web:/var/www/
# 远端 → 本地
rsync -avz web:/var/log/app.log /backup/
# 删除远端多余文件(--delete 谨慎使用)
rsync -avz --delete ./dist/ web:/var/www/
4.1 排除与限速
# 排除缓存目录、限速 1MB/s
rsync -avz --exclude='node_modules' --exclude='.git' \
--bwlimit=1000 ./ web:/app/
# 排除文件列表文件
rsync -avz --exclude-from=exclude.txt ./ web:/app/
一句话总结:
--exclude跳过无用的目录、--bwlimit限速防占满带宽,生产同步这两项几乎必配。
4.2 增量备份与校验
# 增量到带日期的目录(--link-dest 硬链接去重)
rsync -avz --link-dest=/backup/latest /data/ /backup/$(date +%F)/
# 只校验不传输
rsync -avzn --itemize-changes ./ web:/app/
5. SSH 隧道与端口转发
一句话总结:
-L本地转发、-R远程转发、-D动态 SOCKS,隧道把不安全的明文连接装进加密的 SSH 通道。
# 本地转发:本地 8080 → 远端内网 5432(数据库)
ssh -L 8080:db-internal:5432 jump
# 之后本地应用连 localhost:8080 即到达内网数据库
# 动态 SOCKS 代理
ssh -D 1080 jump
# 浏览器配 socks5://localhost:1080
# 远程转发:把本地服务暴露给远端
ssh -R 9000:localhost:80 web
5.1 隧道后台化
# 后台常驻隧道
ssh -fN -L 8080:db-internal:5432 jump
# 配置里声明常驻隧道
# Host jump
# LocalForward 8080 db-internal:5432
ssh -fN jump
一句话总结:
-fN后台常驻不执行命令;隧道端口被占用时会连接失败,用-o ExitOnForwardFailure=yes提前暴露错误。
5.2 跳板机串联
# 经跳板机到达内网:ProxyJump
ssh -J jump db-host uptime
# 配置文件串联
# Host db
# HostName 10.0.0.5
# ProxyJump jump
ssh db uptime
6. 安全加固
一句话总结: 安全加固三件套:禁用口令登录、禁用 root 直连、限制来源 IP;密钥权限与 known_hosts 指纹校验是自动化安全的前提。
# /etc/ssh/sshd_config 关键项
# PermitRootLogin no
# PasswordAuthentication no
# PubkeyAuthentication yes
# AllowUsers deploy ops
# 修改后重载
sudo systemctl reload sshd
6.1 密钥与 known_hosts 管理
# 私钥权限必须收紧
chmod 600 ~/.ssh/id_ed25519
chmod 700 ~/.ssh
# 预置 known_hosts,避免首次连接交互
ssh-keyscan -H web >> ~/.ssh/known_hosts
# 校验指纹后连接
ssh-keygen -F web -l
一句话总结: 自动化安全三要点:私钥 600、预置 known_hosts 防中间人、禁用口令与 root 直连防暴力破解。
6.2 fail2ban 与白名单
# fail2ban 拦截暴力破解(示意)
sudo apt install fail2ban
sudo systemctl enable --now fail2ban
# 防火墙只放行白名单 IP
sudo ufw allow from 203.0.113.0/24 to any port 22
7. 批量主机管理
一句话总结: 批量 SSH 的核心是「主机清单 + 并发 + 失败隔离」,把结果收敛成一份汇总报告,而不是被一条失败卡死。
7.1 主机清单与并发执行
#!/usr/bin/env bash
set -euo pipefail
hosts=(web1 web2 db1)
# 串行巡检
for h in "${hosts[@]}"; do
echo "== $h"
ssh "$h" uptime || echo " $h 失败"
done
# 并发巡检:& + wait
for h in "${hosts[@]}"; do
ssh "$h" 'uptime; df -h /' > "report_$h.txt" &
done
wait
cat report_web*.txt report_db*.txt
7.2 结果汇总与失败重试
# 失败主机单独收集
fail=0
for h in "${hosts[@]}"; do
if ! ssh -o ConnectTimeout=5 "$h" 'true' 2>/dev/null; then
echo "$h 不可达" >&2
((fail++))
fi
done
echo "不可达主机数: $fail"
# 超时主机跳过,不拖垮整批
for h in "${hosts[@]}"; do
timeout 8 ssh "$h" 'uptime' 2>/dev/null || echo "$h: 超时"
done
一句话总结: 批量巡检三原则:每台主机超时隔离、失败不中断整批、结果落盘合并;并行用
&+wait或 parallel 都行。
7.3 远程批量部署脚本
#!/usr/bin/env bash
set -euo pipefail
deploy_one() {
local host="$1"
echo "==> 部署 $host"
rsync -az --delete ./app/ "$host:/opt/app/" || return 1
ssh "$host" 'sudo systemctl restart app && sudo systemctl is-active app' || return 1
echo " $host OK"
}
for h in web1 web2 web3; do
deploy_one "$h" || echo " $h 失败"
done
8. 总结
| 环节 | 要点 |
|---|---|
| 客户端配置 | ~/.ssh/config 别名化,端口/密钥/跳板按主机定制 |
| 免密登录 | ssh-keygen 生成、ssh-copy-id 安装、agent 托管口令 |
| 远程执行 | ssh host 'cmd'、stdin 传本地脚本、bash -lc 加载环境 |
| 文件同步 | rsync -avz 归档压缩、--delete 对齐、--bwlimit 限速 |
| 隧道 | -L/-R/-D 三类转发、-fN 后台常驻、ProxyJump 跳板 |
| 安全 | 禁口令/禁 root/白名单,私钥 600、known_hosts 预置 |
| 批量 | 清单 + 并发 & + 超时隔离 + 结果落盘汇总 |
SSH 自动化把"单机脚本"升级成"多机编排":配置别名化、免密密钥化、批量化并发三步走,配合 rsync 同步与隧道转发,一台机器就能管好一个集群。定时任务与并发结合的下一步,是 cron 与 systemd timer。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。