使用 systemctl 和 journalctl 排查 Linux 服务故障
一套实用的调试工作流,通过 systemctl 和 journalctl 诊断 Linux 服务失败或异常问题。
使用 systemctl 和 journalctl 排查 Linux 服务故障
当 Linux 服务出现故障时,最快的解决路径通常不是上网搜索,而是进行三项本地检查:systemd 认为发生了什么、服务记录了哪些日志、以及故障前发生了什么变化。systemctl 和 journalctl 能让你无需猜测就能获得这些答案。
本指南以常见的服务故障为例:无法启动的服务、正在运行但未执行有效工作的进程、以及看似正常却突然退出的服务。这些命令适用于大多数由 systemd 管理的服务,但具体的单元名称和日志位置可能因发行版和软件包而异。
理解 systemctl 和 journalctl
在深入排查之前,理解这两个核心工具的作用至关重要:
systemctl:该命令是控制和查询systemd系统及服务管理器的核心工具。它可以启动、停止、重启、检查状态以及启用/禁用服务。journalctl:该命令用于查询 systemd 日志(journal),这是一个集中式日志系统。它收集来自内核、系统服务和应用程序的日志,提供系统事件的统一视图。journalctl对于理解服务为何失败或出现异常行为至关重要。
常见故障场景及解决方案
让我们探讨典型问题及其处理方法:
1. 服务启动失败
这是最常见的问题。你尝试启动一个服务,但它立即失败。
步骤 1:检查服务状态
使用 systemctl status 快速了解服务的状态和最近的日志条目。
sudo systemctl status apache2.service
预期输出(示例 - 你的输出可能不同):
● apache2.service - The Apache HTTP Server
Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
Active: **failed** (result: exit-code) since Tue 2023-10-27 10:00:00 UTC; 1min ago
Docs: https://httpd.apache.org/docs/2.4/
Process: 12345 ExecStart=/usr/sbin/apachectl start (code=exited, status=1/FAILURE)
Main PID: 12345 (code=exited, status=1/FAILURE)
Oct 27 10:00:00 your-server systemd[1]: Starting The Apache HTTP Server...
Oct 27 10:00:00 your-server apachectl[12345]: AH00526: Syntax error on line 123 of /etc/apache2/apache2.conf:
Oct 27 10:00:00 your-server apachectl[12345]: Invalid Mutex directory in argument file: '/var/run/apache2/'
Oct 27 10:00:00 your-server systemd[1]: apache2.service: Main process exited, code=exited, status=1/FAILURE
Oct 27 10:00:00 your-server systemd[1]: **Failed** to start The Apache HTTP Server.
Oct 27 10:00:00 your-server systemd[1]: apache2.service: Unit entered failed state.
分析: systemctl status 输出清晰地显示了 Active: failed,并提供了错误消息片段:Invalid Mutex directory in argument file: '/var/run/apache2/'。这表明存在配置问题。
步骤 2:使用 journalctl 调查日志
如需更详细的信息,使用 journalctl 查看特定失败服务的日志。-u 标志指定单元(服务)。
sudo journalctl -u apache2.service -xe
-u apache2.service:过滤apache2.service单元的日志。-x:为某些日志消息添加解释。-e:跳转到日志末尾,显示最近的条目。
潜在发现: journalctl 输出可能揭示有关配置错误、权限问题或依赖问题的更多上下文。
步骤 3:检查配置文件
根据错误消息,检查相关的配置文件。在上面的示例中,它指向 /etc/apache2/apache2.conf 和目录 /var/run/apache2/。
sudo nano /etc/apache2/apache2.conf
解决方案: 此类问题通常源于缺少运行时目录、软件包变更或引用了不再存在的路径的配置文件。不要盲目地从互联网上的示例创建目录。首先确认你的发行版上应用程序期望什么,然后修复缺失的路径或配置。可能的修复方法如下:
sudo mkdir -p /var/run/apache2/
sudo chown www-data:www-data /var/run/apache2/
sudo systemctl start apache2.service
如果错误提到语法问题,在再次重启之前运行应用程序自身的配置测试:
sudo apachectl configtest
sudo nginx -t
sudo sshd -t
特定于应用程序的验证器可以捕获 systemd 无法理解的错误。Systemd 知道进程是否退出,但它不知道你的 Nginx server 块是否指向错误的证书文件,或者 Apache 指令是否属于不同的上下文。
2. 服务正在运行但无响应
有时,systemctl status 显示服务为 active (running),但它并未执行预期的功能(例如,Web 服务器未提供页面)。
步骤 1:验证服务状态和 PID
确认它确实在运行并具有进程 ID (PID)。
sudo systemctl status nginx.service
如果显示 active (running),记下 PID。
步骤 2:检查服务日志中的错误
即使正在运行,服务也可能遇到内部错误,导致其无法正常运行。
sudo journalctl -u nginx.service -f
-f:实时跟踪日志输出。如果你能在journalctl运行时触发问题(例如,尝试访问网页),这非常有用。
步骤 3:检查应用程序特定日志
许多服务除了 systemd 日志外还会写入自己的日志。对于 Nginx 或 Apache 等 Web 服务器,检查其典型的日志位置(例如 /var/log/nginx/error.log、/var/log/apache2/error.log)。
sudo tail -n 50 /var/log/nginx/error.log
步骤 4:检查资源利用率
系统过载可能导致服务无响应。
top
htop
free -h
检查服务进程的 CPU、内存或磁盘 I/O 是否过高。
同时检查服务是否在你期望的地址上监听:
sudo ss -ltnp
sudo ss -lunp
对于 Web 服务,在 systemctl 中看到 nginx 处于活动状态只是故事的一半。你还需要知道它是否绑定到 0.0.0.0:80、127.0.0.1:8080、IPv6 套接字,或者根本没有套接字。防火墙规则、反向代理不匹配或错误的绑定地址都可能使一个健康的进程从外部看起来像是坏了。
解决方案: 如果日志显示问题或资源紧张,你可能需要:
- 优化配置。
- 重启服务(
sudo systemctl restart <service_name>.service)。 - 调查底层系统资源问题。
- 必要时增加系统资源。
3. 服务意外停止
如果之前正在运行的服务突然停止,通常是由于未处理的异常或看门狗超时。
步骤 1:使用 journalctl 检查近期历史
使用 journalctl 查看服务停止前发生了什么。如果你知道大致时间,--since 和 --until 标志会很有帮助。
sudo journalctl -u <service_name>.service --since "1 hour ago"
或者,查看自上次启动以来与该服务相关的所有日志:
sudo journalctl -u <service_name>.service -b
步骤 2:查找核心转储或崩溃报告
如果服务崩溃,系统可能生成了核心转储或崩溃报告。
ls -l /var/crash/
步骤 3:检查 systemd 服务单元文件
检查服务的单元文件(通常位于 /etc/systemd/system/ 或 /lib/systemd/system/),查看 Restart= 指令和 WatchdogSec= 设置。不正确的 Restart= 配置或过短的 WatchdogSec= 可能导致意外重启或失败。
systemctl cat <service_name>.service
解决方案: 解决日志中确定的根本原因。这可能涉及修复代码错误、调整 systemd 单元文件参数或增加资源限制。
如果你看到重复重启,检查 systemd 是否对单元进行了速率限制:
systemctl status <service_name>.service
journalctl -u <service_name>.service --since "30 minutes ago"
关于 Start request repeated too quickly 的消息通常意味着服务在短时间内崩溃了多次。修复根本问题后,清除失败状态:
sudo systemctl reset-failed <service_name>.service
sudo systemctl start <service_name>.service
4. systemctl enable 或 systemctl disable 问题
虽然不是运行时故障,但启用或禁用服务时可能会出现问题。
问题: 服务已启用但未在启动时启动,反之亦然。
检查状态:
sudo systemctl is-enabled <service_name>.service
此命令将输出 enabled 或 disabled。
故障排除:
- 确保服务单元文件本身有效且放置正确(例如
/etc/systemd/system/)。 - 对单元文件进行更改后,始终运行
sudo systemctl daemon-reload。 - 检查服务的日志(
journalctl -u <service_name>.service),查找即使已启用也可能阻止其激活的启动错误。
有效故障排除的技巧
- 从
systemctl status开始:始终从这里开始。它提供快速快照,并通常为你指明正确的方向。 - 使用
journalctl -u <service>:这是你理解为什么会发生某事的主要工具。 journalctl的-f标志:在尝试重现问题时,对于实时监控非常有用。systemctl restart <service>:更改配置后,始终重启服务以应用更改。systemctl daemon-reload:在修改任何.service单元文件后至关重要。- 检查依赖关系:有时服务失败是因为它依赖的服务尚未启动或自身失败。
systemctl status通常会显示这一点。 - 权限:许多服务失败是由于文件或目录权限不正确。确保服务运行的用户具有必要的访问权限。
- 网络问题:如果服务依赖网络,请检查网络连接、防火墙规则和端口可用性。
一个可靠的故障排除顺序
当压力来临时,每次都使用相同的顺序:
systemctl status <service>.service
journalctl -u <service>.service -b --no-pager
systemctl cat <service>.service
systemctl list-dependencies <service>.service
从当前状态开始,然后读取当前启动的日志,接着检查 systemd 看到的单元,最后检查依赖关系。如果服务面向网络,添加 ss -ltnp 和本地的 curl 或客户端测试。如果它读取配置文件,运行服务自身的配置验证器。
关键是要避免随机重启。重启在配置更改或进程卡住后可能是一个有效的修复,但它也会销毁证据。先阅读足够的日志,以便你知道自己在更改什么以及为什么更改。
阅读日志输出而不迷失方向
journalctl 可能很嘈杂,尤其是在繁忙的服务器上。从窄范围开始,仅在需要时扩大范围。
对于当前启动中的一个服务:
journalctl -u <service>.service -b --no-pager
对于最近几分钟:
journalctl -u <service>.service --since "15 minutes ago" --no-pager
对于上一次启动:
journalctl -u <service>.service -b -1 --no-pager
当服务在启动期间失败然后恢复,或者整个机器在你检查之前重启时,查看上一次启动的视图非常有用。你可以使用以下命令列出启动:
journalctl --list-boots
如果服务记录结构化字段或长行,请使用简短的 ISO 时间戳:
journalctl -u <service>.service -o short-iso --no-pager
当你需要共享日志时,请删除机密、令牌、内部主机名和客户数据。服务日志通常包含环境派生的设置、URL、标头或连接字符串。一个干净的故障排除习惯包括在将输出粘贴到任何地方之前进行编辑。
当 systemctl 显示 "Active" 但用户仍然看到故障时
active (running) 状态仅意味着 systemd 有一个符合单元期望的进程。它并不证明应用程序是健康的。一个 Web 应用程序可以在运行时返回 HTTP 500。一个工作进程可以在活动时卡在错误的队列消息上。一个数据库代理可以在运行时所有后端连接都失败。
对于网络服务,从用户依赖的相同层进行测试:
curl -v http://127.0.0.1:8080/health
curl -v http://localhost/health
curl -v https://service.example.com/health
这三个检查回答了不同的问题。第一个检查本地应用程序端口。第二个可能包括本地反向代理。第三个检查 DNS、TLS、路由、防火墙规则和面向公众的路径。
对于工作进程服务,查看它们消费或生产的内容。队列工作进程可能需要检查队列深度。备份服务可能需要检查最近的输出文件。指标收集器可能需要查询指标后端。systemctl 告诉你监督是否有效;应用程序检查告诉你服务是否有用。
一次只修复一个变量
当单元在部署后失败时,很容易更改多个设置然后重启。这可能会隐藏真正的原因。最好一次只做一个更改:
systemctl cat my-app.service
journalctl -u my-app.service --since "30 minutes ago" --no-pager
sudo systemctl edit my-app.service
sudo systemctl daemon-reload
sudo systemctl restart my-app.service
然后在更改下一项之前检查结果。如果失败是缺少文件,修复文件路径。如果是权限错误,修复所有权或模式。如果是依赖关系,修复单元关系或应用程序重试行为。缓慢、乏味的故障排除通常比带有五次未跟踪编辑的重启循环更快。