SSH远程安装deb包遇Broken pipe错误?从管道原理到磁盘排查的完整手册
还真被我遇上了。手头一台麒麟系统的服务器远程通过 SSH 安装一个软件升级包本来敲完dpkg -i等着进度条走结果终端啪地弹出来一行dpkg-deb: error: paste subprocess was killed by signal (Broken pipe)后面还跟着一句 SSH 的packet write wait: connection to 192.168.1.11 port 22: broken pipe。那台机器不在手边只能靠命令行操作一时之间还真有点慌。这类报错看着简单但“Broken pipe”这个词其实横跨了好几层可能是 dpkg 的问题可能是磁盘问题也可能是 SSH 连接问题。当时折腾了半个多小时才彻底排除后来把这类错误的排查思路和手法整理了一遍顺手也把容易踩的坑标了出来。无论是麒麟系统还是其他基于 Debian 的发行版只要用 dpkg 装 .deb 包大概率都用得上。1. 错误现场与故障现象还原1.1 错误信息的完整特征先还原一下我当时看到的完整终端输出免得你遇到类似情况时对不上号$ sudo dpkg -i upgrade-network-xxx.amd64.deb (Reading database ... 32867 files and directories currently installed.) Preparing to unpack upgrade-network-xxx.amd64.deb ... Unpacking upgrade-network-xxx (1.0.2) over (1.0.1) ... dpkg-deb: error: paste subprocess was killed by signal (Broken pipe) dpkg: error processing archive upgrade-network-xxx.amd64.deb (--install): dpkg-deb --control subprocess returned error exit status 2 Errors were encountered while processing: upgrade-network-xxx.amd64.deb注意这里的错误有两段第一段是dpkg-deb: error: paste subprocess was killed by signal (Broken pipe)第二段是 dpkg 把错误抛给你说--control subprocess returned error exit status 2。如果是在本地没有 SSH 的场景基本上只会有前半段后面跟的则是 SSH 客户端的 broken pipe 报错。报错的关键词是paste subprocess。这里的paste不是让你粘帖东西而是 dpkg-deb 内部调用的一个 Linux 文本处理命令。当 dpkg-deb 需要从 deb 包中提取控制信息或数据内容时会先生成 tar 格式的数据流再通过管道交给paste这类命令做行处理。一旦后面的步骤没有正常接收数据前面的进程就会收到系统发来的 SIGPIPE 信号然后被终止。最终表现就是“Broken pipe”。1.2 发生场景的共性分析我个人总结下来出现这个错误主要有三类场景本地终端安装 .deb 包在图形界面或本地 console 上执行dpkg -i突然报错多半和系统资源、软件包完整性有关。通过 SSH 远程安装这是我最常遇到的场景。远程终端会话本身不稳定一旦连接断开正在跑的 dpkg 进程也容易跟着遭殃。自动化脚本或 CI/CD 里执行 dpkg 操作脚本中封装了dpkg-deb -x或dpkg -i如果脚本提前退出、管道被关闭同样会触发。无论是麒麟系统还是 Debian/Ubuntu只要底层用的是 dpkg 体系这个报错的排查逻辑都是相通的。麒麟系统作为基于 Debian 定制的发行版日常安装本地软件包时用得尤其多。我们就围绕这个错误从原理到底层再到实操一层层拆开来看。2. 根因剖析为什么会出现 Broken pipe2.1 管道机制与 dpkg-deb 的关系“Broken pipe”字面意思是“管道破裂”。Linux 下管道是一种进程间通信方式前一个命令的输出直接作为后一个命令的输入。管道本身是个缓冲区数据从上游流向缓冲区再由下游读取。如果下游进程已经退出上游进程仍然往管道里写数据系统就会向上游发送 SIGPIPE 信号默认行为是终止这个进程。dpkg-deb 在处理 .deb 包时内部并不是一步到位解压文件的而是有多级处理流程。它先把 deb 包里的control.tar.gz和data.tar.*解出来再通过管道交给paste、tar之类的命令去进一步拆包。如果中间的某个下游进程因故退出上游就会因为管道没有读取端而收到 SIGPIPE然后被杀掉。杀掉的信号会在错误信息里体现为(Broken pipe)。所以看到 dpkg-deb 报这个错第一反应不应该仅仅是“软件包坏了”而应该想到管道对端为什么提前退出了这才是问题的核心。2.2 常见的几类触发原因根据我的实践经验导致“管道对端提前退出”的原因可以分为这么几类分类具体原因表现特征系统资源不足磁盘空间满、inode 耗尽解压时无法写入新文件下游命令失败退出软件包损坏下载不完整、文件被截断dpkg-deb 读出非法数据后续处理直接失败权限/路径问题目标目录不存在或没有写权限解压中途失败进程退出dpkg 状态损坏dpkg 数据库锁冲突、半安装状态执行 dpkg 操作时自身状态冲突父进程被终止SSH 断连、终端关闭导致 SIGHUP正在运行的 dpkg 连带被杀其中我自己遇到概率最高的是“磁盘满”和“SSH 断连”。磁盘满导致数据写不进去paste处理到一半发现没有空间直接退出上游 dpkg-deb 收到 SIGPIPE就报出了我们看到的错误。SSH 断连的情况更隐蔽因为断连可能发生在任何时刻dpkg 进程本身没有被显式 kill但 SSH 会话关闭时终端进程组会被发送 SIGHUP 信号如果 dpkg 没有忽略这个信号就同样会终止。2.3 为什么在 SSH 远程安装时更容易遇到这里要把两层概念拆清楚。dpkg-deb 报错是发生在远端服务器上的而 SSH 客户端的packet write wait: connection to 192.168.1.11 port 22: broken pipe是你本机 SSH 进程发现的。很多时候真实的顺序是这样的你通过 SSH 连上服务器执行dpkg -i。安装过程比较久中途网络闪断或者 SSH 服务器因为空闲超时把连接关了。本机 SSH 客户端发现连接断开尝试往 socket 里写数据结果得到 broken pipe 错误。远端的 shell 收到 SIGHUP把正在跑的 dpkg 进程也给带走了。等网络恢复后你再看到终端一堆错误输出里既有 ssh 的 broken pipe也有 dpkg-deb 的 broken pipe。所以packet write wait和dpkg-deb的 Broken pipe 经常会同时出现但它们是两码事。前者是网络层的后者是进程层的。排查时要先判断哪个在前哪个在后否则容易白折腾。3. 分步排查与解决方案3.1 第一步检查磁盘空间与 inode接到错误后我建议第一个命令就是看磁盘因为磁盘满是最常见也最致命的原因。执行df -h df -idf -h看的是文件系统空间df -i看的是 inode 数量。inode 是文件系统用来记录文件元数据的数据结构即使空间没满inode 满了也同样写不了文件。如果是 / 分区或者 /var 分区 100%先清理再重试。常见的清理目标有# 清理 apt 缓存 sudo apt clean # 查看 /tmp 下有什么大户 sudo du -sh /tmp/* 2/dev/null | sort -rh | head # 清理旧的日志 sudo journalctl --vacuum-time3d清理后重新df -h确认。如果磁盘和 inode 都没问题再往下走。3.2 修复 dpkg/apt 状态磁盘没问题的情况下dpkg 自身状态也可能会卡住。比如之前某次安装被杀dpkg 的数据库里留下了半安装状态后续操作就会异常。先跑一遍自动修复sudo dpkg --configure -a这条命令会让 dpkg 尝试配置所有未配置完的包。如果它自己也报错再翻日志sudo tail -100 /var/log/dpkg.log sudo tail -100 /var/log/apt/term.log日志里经常能看到具体是哪个包、哪个文件在捣鬼。找到具体包名后根据情况选择# 移除损坏但不需要的包 sudo dpkg --remove --force-remove-reinstreq 包名 # 重新配置安装一半的包 sudo dpkg --configure -a这里要提醒一句--force-remove-reinstreq属于强制手段确认包没有价值后再用别一上来就删否则可能连带删掉依赖它的业务程序。3.3 清理锁与临时目录dpkg 本身有锁机制防止多个进程同时操作。如果之前某个 dpkg 进程被杀锁文件可能还留在原地下面的命令卡在Waiting for cache lock。这时候不能直接乱删锁文件先看谁在用sudo fuser -v /var/lib/dpkg/lock sudo fuser -v /var/lib/dpkg/lock-frontend如果有进程输出说明确实有 dpkg 在跑等它结束。如果没有输出说明锁已经被清理或残留了这时可以安全删掉锁文件sudo rm -f /var/lib/dpkg/lock sudo rm -f /var/lib/dpkg/lock-frontend sudo rm -f /var/cache/apt/archives/lock临时的 dpkg 解压目录/tmp也可能堆积了僵尸文件顺手清理一下。但同样的别无脑rm -rf /tmp/*如果有其他程序在用反而添乱。3.4 校验并重新安装软件包如果系统本身没问题那大概率是软件包不完整。.deb 文件本质上是一个 ar 归档里面包裹了debian-binary、control.tar.gz和data.tar.*。如果下载过程中文件被截断dpkg-deb 自然没法正常解压。先校验下载的包md5sum upgrade-network-xxx.amd64.deb拿到官方提供的校验值去比对如果对不上重新下载。也可以不校验直接检查包内部结构dpkg-deb -I upgrade-network-xxx.amd64.deb-I显示包的元信息如果它报错基本可以确认包损坏。如果正常可以继续看文件列表dpkg-deb -c upgrade-network-xxx.amd64.deb确认包没问题后再安装。这里有个小经验dpkg -i只安装不处理依赖所以推荐用apt来装本地包让 apt 自动处理依赖关系sudo apt install ./upgrade-network-xxx.amd64.debapt install ./这种写法在 Debian/Ubuntu/麒麟上都能用会先做依赖分析还能自动从源里补齐依赖比裸dpkg -i稳得多。3.5 解决远程连接中断问题如果前面的排查都没有解决你发现每次都是 SSH 断了之后才报错那就要从远程会话本身下手。最常见也最推荐的做法是把长任务放进tmux或screen里这样即使本地断开远端任务还能继续跑。tmux 的基本操作# 安装 tmux sudo apt install tmux -y # 创建新会话 tmux new -s install # 在会话里执行安装 sudo apt install ./xxx.deb # 临时离开会话不影响任务 Ctrlb d # 重新进入会话 tmux attach -t install在tmux里执行命令SSH 断连不会影响安装进程因为 tmux 会话是独立于当前终端的守护进程。你重连后tmux attach回去就能看到结果。如果不想用 tmux也可以用nohup加日志sudo nohup dpkg -i upgrade-network-xxx.amd64.deb /tmp/dpkg_install.log 21 但 nohup 对进程组的保护不如 tmux 彻底子进程可能还是会受影响所以我还是推荐 tmux。另外也可以调整 SSH 客户端和服务端的存活检测参数减少断连概率。在客户端~/.ssh/config里加Host 192.168.1.11 ServerAliveInterval 30 ServerAliveCountMax 60服务端在/etc/ssh/sshd_config里可以设置ClientAliveInterval 30 ClientAliveCountMax 60这样 SSH 会定期互相发心跳避免长时间无数据导致网络设备掐断连接。4. 案例实操麒麟系统升级包安装完整过程4.1 环境说明与升级包信息我这里的实际操作场景是一台麒麟高级服务器操作系统 V10 的机器内核版本 4.19.x系统刚从旧版本升级到新版本需要安装一个内部发布的 “网络优化组件升级包”。包名我打个码就叫net-optimize-2.0_amd64.deb。安装前我先确认了系统版本和 dpkg 版本$ cat /etc/kylin-release Kylin Linux Advanced Server release V10 (Tercel) $ dpkg --version dpkg 1.19.7 (amd64)这类内部升级包通常发布在文件服务器上我通过 SSH 用scp拉到服务器然后直接dpkg -i结果就遇到了开头的错误。4.2 按顺序排查并最终定位原因看到dpkg-deb: error: paste subprocess was killed by signal (Broken pipe)后我本来以为是包的问题但重传了一次还是同样报错所以决定按流程排查。先看磁盘$ df -h / Filesystem Size Used Avail Use% Mounted on /dev/mapper/kl-root 97G 97G 0G 100% /好家伙根分区 100% 占满。df -i虽然没满但空间一点不剩任何新文件都写不进去。dpkg 解压 deb 包时要在根目录和/tmp写入临时文件磁盘满了paste 子进程自然撑不住。接下来清理空间sudo apt clean sudo rm -rf /var/log/journal/* sudo du -sh /tmp/* 2/dev/null | sort -rh | head -5清理完之后磁盘占用降到 60%。这时再重新安装sudo apt install ./net-optimize-2.0_amd64.deb这次没有任何报错依赖也自动补齐了。我总结当时最大的坑是升级前没有留意磁盘告警导致一个简单的安装动作因为空间不足而失败。磁盘满导致的 dpkg 错误特别容易迷惑人因为报错信息指向的是“信号”和“管道”而不是“磁盘空间不足”。4.3 用 tmux 保住长任务这个案例还给了另一个教训。其实在第一次安装时我是直接在 SSH 终端里敲的命令安装过程还比较慢。第二次用 tmux 包裹以后我才发现真正耗时的不是解压而是依赖解析和配置脚本执行。之后我再做升级都会先执行tmux new -s upgrade然后在 tmux 会话里执行安装命令这样就算本地网络抖动也不会把远端安装进程带走。如果 SSH 断开重连后用tmux attach -t upgrade就能回到现场看到安装日志。这个习惯后来帮我避免了好几次“远程升级装到一半被断连搞成半包状态”的麻烦。4.4 验证安装结果安装完成后不能只看终端没报错就喊完事还是要验证一下包状态$ dpkg -l net-optimize DesiredUnknown/Install/Remove/Purge/Hold | StatusNot/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend |/ Err?(none)/Reinst-required (Status,Err: uppercasebad) ||/ Name Version Architecture Description ---- ii net-optimize 2.0 amd64 Network Optimization Component第一列ii表示安装成功且配置完成。再运行一下组件自带的版本命令$ net-optimize --version net-optimize version 2.0确认没有其他异常。整个安装过程结束。5. 常见问题速查与避坑清单5.1 问题、原因、对策速查表为了后面遇到同类问题能快速定位我把常见组合整理成一个速查表。你可以收藏起来或者打印出来贴在工位上。现象可能原因解决动作dpkg-deb: paste subprocess was killed by signal (Broken pipe)磁盘满 / inode 耗尽df -h、df -i清理空间后用apt install ./xxx.deb重装同样的错误且包是新下载的软件包下载损坏用md5sum校验dpkg-deb -I验证包结构重新下载错误后 dpkg 卡住不动锁文件残留 / dpkg 状态损坏dpkg --configure -a检查lock必要时清理锁文件伴随packet write wait: ... broken pipeSSH 连接中断用 tmux 或 nohup 执行长任务配置 SSH 心跳保活安装过程中报权限不足需要 root 权限使用sudo或用root用户执行反复重试依然报同样错误底层文件系统异常执行fsck前先备份检查 RAID 状态或虚拟化宿主机磁盘5.2 日常维护建议与个人心得先说一条最实在的心得不要在裸 SSH 会话里直接跑长时的软件安装或升级任务。互联网公司的服务器还好办公网里的机器和异地机房设备经常会有网络抖动一条 timeout 就能让你“摸不着头脑”。我现在只要是在远程操作安装超过两分钟的任务都会主动加tmux。哪怕最后任务只跑了 30 秒这个习惯也不能省。再说磁盘监控。这个错误我最近几次遇到的根源都是磁盘满而且不是 / 分区满就是/var分区满。日志文件、容器镜像、apt 缓存都是吃磁盘的大户。建议至少给服务器配一个简单的磁盘告警常用做法是cron定时跑一个脚本超过 80% 就往群里发通知或者直接部署 node_exporter Alertmanager 那套监控。能早发现就不要等安装报错才意识到。关于 dpkg 的强制选项我想多说一句。网上很多资料会让你dpkg --force-all -i xxx.deb这种“大力出奇迹”的方式在业务服务器上非常危险。它可能绕过依赖检查装出半残状态后续再装别的包时又冒出一堆诡异问题。能用apt install ./包名.deb解决的问题就不要用--force硬上。还有个小技巧如果 dpkg 数据库已经处于半损坏状态可以先备份一下sudo cp -a /var/lib/dpkg /var/lib/dpkg.bak修复过程中如果操作失误还能回滚。备份这块老运维可能觉得多余但在紧急时刻能救命的往往就是这一手。另外升级包安装前可以先查看包里的文件列表确认不会覆盖掉正在使用的配置文件dpkg-deb -c net-optimize-2.0_amd64.deb看看有没有/etc/下的文件提前做好备份比装完再后悔强。最后再说回错误本身。Broken pipe 对开发者来说是个常见的信号但对运维排查来说它更像一个“症状”而不是“病因”。遇到它不要急着重装包先检查磁盘、再检查 dpkg 状态、检查网络会话按顺序来大概率能找到真正的原因。这之后我在麒麟系统上已经很少被这个报错吓到了。说实话系统提示虽然看着吓人但只要把上面几步走一遍绝大多数场景都能在十分钟内解决。希望这篇总结也能让你别再为这个错误熬夜。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →