如何防止Cron作业脚本产生孤立的ssh-agent进程?
解决Cron脚本中ssh-agent进程残留的问题
问题原因
你的脚本每次通过Cron执行时,因为Cron的运行环境不继承SSH相关环境变量(SSH_AUTH_SOCK为空),所以每次都会启动新的ssh-agent进程,但脚本执行完毕后没有主动终止这个进程,导致大量孤立进程堆积。
解决方案
下面提供几种适合你的修改方案,按复杂度从低到高排序:
方案1:脚本结束时强制杀掉启动的ssh-agent
这是最简单的修改方式,只需要在脚本中添加“退出时清理进程”的逻辑,确保不管脚本成功还是失败,都会杀掉我们启动的ssh-agent。
修改后的run.sh脚本:
#!/bin/sh # 保存启动的ssh-agent PID,用于后续清理 agent_pid="" if [ -z "$SSH_AUTH_SOCK" ] ; then # 捕获ssh-agent的输出,避免直接eval导致无法提取PID agent_output=$(ssh-agent -s) eval "$agent_output" # 从输出中提取agent的PID agent_pid=$(echo "$agent_output" | grep -o "SSH_AGENT_PID=[0-9]*" | cut -d= -f2) ssh-add ~/.ssh/remote_backups fi # 设置陷阱:脚本退出时(无论正常/异常),杀掉我们启动的ssh-agent trap '[ -n "$agent_pid" ] && ssh-agent -k' EXIT # 以下为原有业务代码,无需修改 printf "\n======\n" date printf "|| Beginning backup replication...\n" cd /var/www/html/wp-content/uploads/backups echo "|| Transferring daily backups..." lftp sftp://remote_backups <<EOF cd /home/web/ mirror --exclude=.* --include=./*.zip --include=./*dat ./ backups --delete --reverse --only-newer -v bye EOF cd ../backups-weekly echo "|| Transferring weekly backups..." lftp sftp://remote_backups <<EOF cd /home/web/ mirror --exclude=.* --include=./*.zip ./ backups-weekly --delete --reverse --only-newer -v bye EOF now=$(date) echo "|| Backup replication complete!" echo "|| Completed at: ${now}"
原理:
- 用
trap命令注册一个脚本退出时的执行动作,确保ssh-agent进程被强制终止 - 提取
ssh-agent的PID,避免误杀其他用户的agent进程
方案2:复用已有的ssh-agent(减少进程创建)
如果希望尽量复用已有的ssh-agent进程,可以通过固定socket文件路径的方式,让脚本检查是否有存活的agent,没有再启动新的。
修改后的脚本片段:
#!/bin/sh # 固定ssh-agent的socket文件路径 export SSH_AUTH_SOCK="$HOME/.ssh/agent-socket" export SSH_AGENT_PID="$HOME/.ssh/agent-pid" # 检查socket是否存在且agent进程存活 if! [ -S "$SSH_AUTH_SOCK" ] ||! kill -0 "$(cat "$SSH_AGENT_PID" 2>/dev/null)" 2>/dev/null; then # 启动新的agent,将PID和socket路径写入文件 ssh-agent -s | grep -E "(SSH_AUTH_SOCK|SSH_AGENT_PID)" > "$HOME/.ssh/agent-info" . "$HOME/.ssh/agent-info" ssh-add ~/.ssh/remote_backups fi # 后面的业务代码不变...
注意:需要确保~/.ssh目录权限正确(chmod 700 ~/.ssh),避免其他用户访问socket文件。
方案3:彻底抛弃ssh-agent(最省心)
既然你用lftp做SFTP传输,可以直接让lftp指定密钥文件,完全不需要ssh-agent,从根源上解决进程残留问题。
修改脚本中的lftp调用部分,比如将原来的:
lftp sftp://remote_backups <<EOF cd /home/web/ mirror --exclude=.* --include=./*.zip --include=./*dat ./ backups --delete --reverse --only-newer -v bye EOF
替换为:
lftp -e "set sftp:connect-program 'ssh -i ~/.ssh/remote_backups'; cd /home/web/; mirror --exclude=.* --include=./*.zip --include=./*dat ./ backups --delete --reverse --only-newer -v; bye" sftp://remote_backups
然后删除脚本中所有和ssh-agent、ssh-add相关的代码即可。
原理:直接告诉ssh(lftp底层调用的是ssh)使用指定的密钥文件,不需要通过agent来管理密钥,流程更简单,也没有进程残留的风险。
推荐选择
如果你对Linux进程管理不太熟悉,方案3是最省心的,不需要处理agent的生命周期,修改量也小,适合快速解决问题。
内容的提问来源于stack exchange,提问作者Aaron Silber
相关产品推荐
相关产品推荐

