Linux 故障恢复练习:用日志、端口与 HTTP 验证恢复

故障恢复练习应在隔离环境中先验证正常基线,再只改变一个条件,根据观察到的证据恢复它。判断恢复完成时,要同时检查服务状态和用户实际收到的响应。

开始前准备虚拟机快照与控制台恢复(英语),并按日志排障步骤比较变更前后的证据。

参考环境为常规安装的 Ubuntu 26.04 LTS 或 Rocky Linux 9 系列虚拟机。容器、Windows Subsystem for Linux(WSL)和其他版本的服务、软件包配置可能不同,先核对版本和恢复入口。

明确隔离范围并建立正常基线

使用具有 systemd 用户管理器、Python 3 和 curl 的实验虚拟机。练习只操作本次创建的用户服务和文件,不修改生产 Nginx、SSH、防火墙或磁盘配置。用 ss -lnt 确认回环地址 127.0.0.1 的 18080 端口可用;若已占用,应把本练习所有对应端口统一换成另一个空闲端口。

先用 command -v python3 确认实际可执行文件路径。示例使用 /usr/bin/python3;若本机不同,应同时修改 ExecStart 及后续 sed 要匹配的原路径。若已存在同名服务或 ~/linux-recovery-lab,不要覆盖,应统一更换实验名称和相关路径。

Python http.server 简易 HTTP 服务器仅供本地练习,绑定回环地址,不用作互联网生产服务器。以下命令应逐条执行并检查结果;预期的失败也要记录后再进入恢复步骤,不要直接当作遇错即停的批处理脚本运行。

mkdir -p "$HOME/linux-recovery-lab/site" "$HOME/.config/systemd/user"
printf "recovery baseline\n" > "$HOME/linux-recovery-lab/site/index.html"
cat > "$HOME/.config/systemd/user/linux-course-recovery.service" <<'EOF'
[Unit]
Description=Isolated Linux recovery lab

[Service]
Type=simple
WorkingDirectory=%h/linux-recovery-lab/site
ExecStart=/usr/bin/python3 -m http.server 18080 --bind 127.0.0.1
EOF
systemctl --user daemon-reload
systemctl --user start linux-course-recovery.service
systemctl --user is-active linux-course-recovery.service
curl --fail --max-time 5 http://127.0.0.1:18080/
cp "$HOME/.config/systemd/user/linux-course-recovery.service" "$HOME/linux-recovery-lab/service.good"

确认状态为 active,且页面正文为 recovery baseline,再保存和核对 service.good 正常副本,之后才引入故障。这里只 start,没有 enable,因此未增加开机自动启动。基线不成立时,先排查 Python 路径、单元配置和端口冲突。

Type=simple 的启动命令返回,不等于应用已准备好接收请求。若初次请求恰逢启动过程,先确认状态和日志,再短暂等待后重试同一 URL;不要把尚未就绪立即归为另一种故障。核对客户端代理设置,避免把代理响应当成本机服务响应。

实验一:只修改可执行文件路径

sed -i 's@ExecStart=/usr/bin/python3 @ExecStart=/nonexistent/course-python @' "$HOME/.config/systemd/user/linux-course-recovery.service"
systemctl --user daemon-reload
systemctl --user restart linux-course-recovery.service
systemctl --user status linux-course-recovery.service --no-pager
journalctl --user -u linux-course-recovery.service --since "5 minutes ago" --no-pager

单元现在指向不存在的程序,服务无法保持活动状态。先检查 sed 确实修改了 ExecStart;路径未匹配就不能认为故障已注入。受执行时序影响,可能在启动命令中立即看到失败,也可能稍后才由 status 显示。

在日志中找出执行失败及 203/EXEC 状态等线索,记录实际消息。203 表示进程执行失败,并非只代表“网络连接失败”;应结合路径和系统错误解释。五分钟窗口可能包含前几轮练习,须按本次修改时刻对照。

恢复正常配置并重新验证响应

cp "$HOME/linux-recovery-lab/service.good" "$HOME/.config/systemd/user/linux-course-recovery.service"
systemctl --user daemon-reload
systemctl --user reset-failed linux-course-recovery.service
systemctl --user start linux-course-recovery.service
systemctl --user is-active linux-course-recovery.service
curl --fail --max-time 5 http://127.0.0.1:18080/

恢复已验证的单元文件,让管理器重新读取,再启动服务。reset-failed 可重置失败状态及启动限制计数,但不会修复错误配置,也不是删除 journal 历史记录的命令。只有活动状态与预期页面都恢复,才算完成本轮恢复。

实验二:区分端口关闭和 HTTP 错误

curl -i --max-time 5 http://127.0.0.1:18080/missing-resource
systemctl --user stop linux-course-recovery.service
curl -i --connect-timeout 2 --max-time 5 http://127.0.0.1:18080/
ss -lnt
systemctl --user start linux-course-recovery.service
curl --fail --max-time 5 http://127.0.0.1:18080/

第一次请求由正在运行的服务器返回 HTTP 404,说明连接已建立,但该资源不存在。停止服务后的请求则在连接阶段失败,常见表现是连接被拒绝;这与收到正常格式的 HTTP 404 响应不同。

比较 curl 带与不带 --fail 的退出状态:普通 404 传输可能返回 0,使用 --fail 时,HTTP 400 及以上响应通常使 curl 返回 22。连接错误有自己的非零退出状态。通过这种对照,可以练习区分服务、传输和 HTTP 层,而无需关闭远程防火墙。

每次只改一个条件并保存记录

记录项 本练习应保留的证据
正常基线 active 状态、监听端口、预期页面
变更 只改 ExecStart 路径,或只停止服务
证据 时间、退出状态、journal、HTTP、套接字
恢复措施 恢复正常文件,或重新启动服务
复验 相同 URL、端口及预期正文
预防复发 部署前检查语法和路径,并进行状态检查

此阶段不要通过填满系统盘、阻断管理 SSH 或随机删除文件制造故障。更具破坏性的实验,应另用克隆虚拟机、容量受限的虚拟磁盘,以及已经验证的控制台和备份,并先界定范围。练习的目标是掌握从观察到恢复的判断过程。

停止实验服务并仅移除本次单元

systemctl --user stop linux-course-recovery.service
systemctl --user reset-failed linux-course-recovery.service
rm -- "$HOME/.config/systemd/user/linux-course-recovery.service"
systemctl --user daemon-reload
ss -lnt

确认回环地址上的 18080 监听已消失。工作目录中仍有正常配置副本和静态页面,检查内容后再单独清理。失败状态操作只针对本次实验单元,不清空其他用户服务的记录。

官方参考资料