apt purge 说软件包已经没了,可每次开机隧道还是会起来。journal 继续变大,路由回来了,机器表现得像什么都没被删掉。厂商的安装程序会写入软件包管理器从未记录的文件,所以卸载软件包和卸载产品是两件不同的工作。下面的 runbook 把这两份记录分开,并有意识地把删除做完。

两个安装程序,两份不同的记录

Debian 软件包维护一份文件清单,purge 只删除清单里列出的内容以及软件包配置。shell 安装脚本或 .run 包不维护这样的清单;它想写哪里就写哪里,通常是 /opt、/usr/local 和 /etc/systemd/system。这些路径不会出现在任何软件包的文件清单里,所以 dpkg 既不知道也不会报告它们。最后你在同一个文件系统上有两个安装负责人,而只有其中一个记账。

删除之前,先就那条具体路径问 dpkg。它否认的路径就归你删除,而同一个回答也会告诉你,以后升级会不会把它放回来。

dpkg -S /etc/systemd/system/acmevpn.service
systemctl list-unit-files | grep -i acmevpn

unit 和它的 wants 链接是两个文件

启用是独立于安装的动作。unit 在 [Install] 段里声明目标关系,而 systemctl enable 把这些声明变成每个目标 .wants/ 目录里的符号链接。于是有了两条路径:unit 文件本身,以及把它拉进某个启动目标的链接。只删文件,链接就指向空处,开机会报错;只删链接,unit 仍可按名字启动。两样都得删。

按顺序删掉,然后再重新加载,好让 systemd 不再把旧定义留在内存里。

systemctl disable --now acmevpn.service
systemctl show -p FragmentPath -p ExecStart acmevpn.service
sudo rm /etc/systemd/system/acmevpn.service
sudo rm /etc/systemd/system/multi-user.target.wants/acmevpn.service
systemctl daemon-reload

要读 FragmentPath,而不是假设位置,因为厂商可能写的是 /usr/lib/systemd/system 或 /lib/systemd/system,而不是 /etc。删掉你以为的那份,真正生效的那份还在跑,修复看起来成功,直到下一次重新加载。重新加载结束后再列一遍 unit 文件,确认这个名字不再出现。

找出让这套安排活下来的那个可执行文件

只要 unit 处于启用状态,不管软件包数据库怎么想,它都会继续启动守护进程。那个进程可以把隧道重新拉起来、重新加回路由,或者恢复一条 kill-switch 规则,在你检查布局之前,这种行为简直像超自然现象。沿着 ExecStart 的路径看那个可执行文件还在不在,因为只要 /opt 下还留着一个可执行文件,这套安排就够了。删掉 unit 只是停掉计划中的启动,并不会删掉程序。

状态目录也要同样对待。账户令牌、生成的配置文件、缓存的服务器列表都放在 /var/lib 或某个家目录下,这些是凭据,不是可以随手丢掉的缓存。服务停下之后再删,而且要有意识地删,并记下你拿走了什么。

sudo ls -l /opt/acmevpn /usr/local/bin/acmevpn
sudo find /var/lib -maxdepth 2 -iname "*acme*"
systemctl is-enabled acmevpn.service

只选一个开机启动的负责人,然后验证

有三个候选者可以抢开机启动,三个都装得到的不是双保险,而是一场竞争。桌面自启动文件要等人登录之后才会跑,systemd unit 跑得更早,但需要一个不依赖屏幕的客户端,cron 的 @reboot 条目按时间表触发,而这个时间表对网络客户端可能是错的。厂商的 CLI 还可能要求一个服务器参数,因为开机时没有上一次会话可以续。选那个和客户端认证方式匹配的候选者,其余的都删掉。

验证的方法是重启机器,然后看三件互相独立的事实:unit 的状态、进程列表,以及一条到某个你能核实的东西的新连接。需要钥匙串或交互式登录的客户端,即使 unit 文件写得对,开机也会失败;这一点 journal 里写着,前提是你读它,而不是只看服务状态。

要么给客户端一份不需要提问就能用的凭据,要么改成在登录时启动。两种都站得住脚,而把它们叠在一起,正是一条隧道多出两个负责人、彼此争抢的原因。

写下来,让下一次 purge 知道该期待什么

每次手工删除之后记下两件事:这套安排是哪个安装程序建的,现在由谁负责启动。这条笔记把下一次 purge 从意外变成清单上的一项,而且它比干活的人活得更久。

把它放在服务配置旁边,而不是个人目录里,并且写上日期,因为厂商更新可能把你删掉的 unit 又装回来。每隔几个月拿 systemctl list-unit-files 和这条笔记对一遍,就能在修复还便宜的时候发现它被恢复了。

相关文章