Translated message

A translation of this page exists in English.

如何在 RHEL 7 或 RHEL 8 中区分崩溃和正常重启

已更新 -

如何在 RHEL 7 或 RHEL 8 中区分系统崩溃和正常重启或关闭?本文概述了 4 种方法:

  1. 使用 last -x 检查 wtmp
  2. 使用 ausearch 检查 auditd 日志
  3. 需要配置:创建一个自定义服务单元
  4. 需要配置:使用 journalctl 检查之前在持久 systemd 日志中的引导

(1)使用 last -x 检查 wtmp

使用简单的 last -Fxn2 shutdown reboot 命令,系统 wtmp 文件会报告两个最新的关闭或重启。reboot 表示系统正在启动,shutdown 表示系统正在关闭。

正常关闭 会显示为 reboot 行,后跟 shutdown 行,如下例中所示:

~]# last -Fxn2 shutdown reboot
reboot   system boot  4.18.0-80.el8.x8 Mon Aug 31 06:33:11 2020   still running
shutdown system down  4.18.0-80.el8.x8 Mon Aug 31 06:33:01 2020 - Mon Aug 31 06:33:11 2020  (00:00)

注:来自 last 的事件按时间降序打印,最近的在顶部。

非正常关闭 可以通过忽略 shutdown 来推断。相反,将有一个 reboot 行(如果 wtmp 文件在崩溃前被已被截断/轮转),或连续有 2 个 reboot 行,如下例所示:

~]# last -Fxn2 shutdown reboot
reboot   system boot  4.18.0-147.5.1.e Tue Sep  1 07:16:25 2020   still running
reboot   system boot  4.18.0-147.5.1.e Mon Aug  3 07:10:56 2020   still running

(2)使用 ausearch 检查 auditd 日志

auditd 非常棒,可以通过检查 ausearch -m 查看它记录的所有不同的事件。针对当前的问题,它记录了系统关闭和系统启动,如上所示。ausearch -i -m system_boot,system_shutdown | tail -4 命令将报告 2 个最近的关闭或启动。如果这报告了一个 SYSTEM_SHUTDOWN,后跟一个 SYSTEM_BOOT,则一切良好;但是,如果它连续报告 2 个 SYSTEM_BOOT 行,或只有一个 SYSTEM_BOOT 行,则系统不会正常关闭。

正常关闭 :

~]# ausearch -i -m system_boot,system_shutdown | tail -4
type=SYSTEM_SHUTDOWN msg=audit(08/31/2020 06:33:01.571:595) : pid=27156 uid=root auid=unset ses=unset subj=system_u:system_r:init_t:s0 msg=' comm=systemd-update-utmp exe=/usr/lib/systemd/systemd-update-utmp hostname=? addr=? terminal=? res=success' 
type=SYSTEM_BOOT msg=audit(08/31/2020 06:33:12.838:9) : pid=828 uid=root auid=unset ses=unset subj=system_u:system_r:init_t:s0 msg=' comm=systemd-update-utmp exe=/usr/lib/systemd/systemd-update-utmp hostname=? addr=? terminal=? res=success' 

注:正如时间戳表明的那样,来自 ausearch 的事件按时间升序打印,且最早的在顶部。

非正常关闭:

~]# ausearch -i -m system_boot,system_shutdown | tail -4
type=SYSTEM_BOOT msg=audit(09/20/2016 01:10:32.392:7) : pid=657 uid=root auid=unset ses=unset subj=system_u:system_r:init_t:s0 msg=' comm=systemd-update-utmp exe=/usr/lib/systemd/systemd-update-utmp hostname=? addr=? terminal=? res=success' 
type=SYSTEM_BOOT msg=audit(09/20/2016 01:11:41.134:7) : pid=656 uid=root auid=unset ses=unset subj=system_u:system_r:init_t:s0 msg=' comm=systemd-update-utmp exe=/usr/lib/systemd/systemd-update-utmp hostname=? addr=? terminal=? res=success' 

另一个非正常关闭:
只存在一个 SYSTEM_BOOT 记录可以解释为系统在崩溃前已运行了很长时间,之前重启的审计日志已被轮转了 ...因此唯一的结果是来自系统刚刚启动时的情况。

~]# ausearch -i -m system_boot,system_shutdown | tail -4
type=SYSTEM_BOOT msg=audit(09/01/2020 07:16:27.069:10) : pid=1057 uid=root auid=unset ses=unset subj=system_u:system_r:init_t:s0 msg=' comm=systemd-update-utmp exe=/usr/lib/systemd/systemd-update-utmp hostname=? addr=? terminal=? res=success'

(3)创建一个自定义服务单元

备注:如果您现在正在尝试诊断潜在的崩溃,这不会有帮助。您首先需要设置它。

这个方法非常棒,因为它允许完全控制。以下是如何做的一个示例:

  1. 创建一个仅在关闭时运行的服务
    (可选)自定义服务名称和 graceful_shutdown 文件。)

    ~]# cat /etc/systemd/system/set_gracefulshutdown.service
    [Unit]
    Description=Set flag for graceful shutdown
    DefaultDependencies=no
    RefuseManualStart=true
    Before=shutdown.target
    
    [Service]
    Type=oneshot
    ExecStart=/bin/touch /root/graceful_shutdown
    
    [Install]
    WantedBy=shutdown.target
    
    ~]# systemctl daemon-reload
    ~]# systemctl enable set_gracefulshutdown
    
  2. 创建一个服务,它仅在启动时运行,并且仅在上述服务创建的 graceful_shutdown 文件存在时运行
    (可选)自定义服务名称,并确保 graceful_shutdown 文件与上述服务匹配。)

    ~]# cat /etc/systemd/system/check_graceful.service
    [Unit]
    Description=Check if previous system shutdown was graceful
    ConditionPathExists=/root/graceful_shutdown
    RefuseManualStart=true
    RefuseManualStop=true
    
    [Service]
    Type=oneshot
    RemainAfterExit=true
    ExecStart=/bin/rm /root/graceful_shutdown
    
    [Install]
    WantedBy=multi-user.target
    
    ~]# systemctl daemon-reload
    ~]# systemctl enable check_graceful
    
  3. 每当正常重启后,systemctl is-active check_graceful 都可以确认之前的重启是否正常。
    输出示例:

    ~]# systemctl is-active check_graceful && echo GOOD || echo BAD
    active
    GOOD
    
    ~]# systemctl status check_graceful
     check_graceful.service - Check if system booted after a graceful shutdown
       Loaded: loaded (/etc/systemd/system/check_graceful.service; enabled; vendor preset: disabled)
       Active: active (exited) since Tue 2016-09-20 01:10:32 EDT; 20s ago
      Process: 669 ExecStart=/bin/rm /root/graceful_shutdown (code=exited, status=0/SUCCESS)
     Main PID: 669 (code=exited, status=0/SUCCESS)
       CGroup: /system.slice/check_graceful.service
    
    Sep 20 01:10:32 a72.example.com systemd[1]: Starting Check if system booted after a graceful shutdown...
    Sep 20 01:10:32 a72.example.com systemd[1]: Started Check if system booted after a graceful shutdown.
    
  4. 崩溃或非正常关闭后,将看到以下内容:

    ~]# systemctl is-active check_graceful && echo GOOD || echo BAD
    inactive
    BAD
    
    ~]# systemctl status check_graceful
    ● check_graceful.service - Check if system booted after a graceful shutdown
       Loaded: loaded (/etc/systemd/system/check_graceful.service; enabled; vendor preset: disabled)
       Active: inactive (dead)
    Condition: start condition failed at Tue 2016-09-20 01:11:41 EDT; 16s ago
               ConditionPathExists=/root/graceful_shutdown was not met
    
    Sep 20 01:11:41 a72.example.com systemd[1]: Started Check if system booted after a graceful shutdown.
    

(4)使用 journalctl 检查之前在持久 systemd 日志中的引导

备注:如果您现在尝试诊断潜在的崩溃,除非之前已将 systemd 配置为将日志持久保留到磁盘,否则不会有帮助。

  1. 配置 systemd-journald 来将日志持久保留在磁盘上
    更新 /etc/systemd/journald.conf 或按照如下所示自己创建目录

    # Create standard log dir and fix ownership/perms
    ~]# mkdir /var/log/journal; systemd-tmpfiles --create --prefix /var/log/journal 2>/dev/null
    
    # Next: tell systemd to flush the current journal to disk
    ~]# systemctl -s SIGUSR1 kill systemd-journald
    
    # OPTIONAL: reboot not required other than to give the following commands more than one boot to inspect
    ~]# reboot
    
  2. (可选)使用 journalctl --list-boots 得到按时间升序的启动的列表
    0 表示自系统启动以来的当前运行时日志 ; -1 表示涵盖之前启动的日志 ; -2 表示在此之前的启动,等等
    例如:

    ~]# journalctl --list-boots
    -2 e1dbd8f133f643d1a816605d96f3ca07 Fri 2020-03-27 22:31:25 UTC—Thu 2020-05-14 01:02:51 UTC
    -1 1969253689e842deaea06ca32f4650c7 Thu 2020-05-14 01:10:00 UTC—Thu 2020-06-04 08:29:42 UTC
     0 26a4a2ff48594778850d917a7e2ad195 Tue 2020-09-01 07:16:20 UTC—Tue 2020-09-01 19:11:20 UTC
    
  3. 使用 journalctl -b -1 -n 查看上一次启动的最后 10 行
    以下示例输出显示上一个系统重启是正常的

    ~]# journalctl -b -1 -n
    Sep 20 01:21:19 a72.example.com systemd[1]: Stopped Create Static Device Nodes in /dev.
    Sep 20 01:21:19 a72.example.com systemd[1]: Stopping Create Static Device Nodes in /dev...
    Sep 20 01:21:19 a72.example.com systemd[1]: Reached target Shutdown.
    Sep 20 01:21:19 a72.example.com systemd[1]: Starting Shutdown.
    Sep 20 01:21:19 a72.example.com systemd[1]: Reached target Final Step.
    Sep 20 01:21:19 a72.example.com systemd[1]: Starting Final Step.
    Sep 20 01:21:19 a72.example.com systemd[1]: Starting Reboot...
    Sep 20 01:21:19 a72.example.com systemd[1]: Shutting down.
    Sep 20 01:21:19 a72.example.com systemd-shutdown[1]: Sending SIGTERM to remaining processes...
    Sep 20 01:21:19 a72.example.com systemd-journal[483]: Journal stopped
    

来自作者的备注:根据我在红帽支持过程中为客户解决 RHEL 7 问题的经验(在我写这篇文章的 2016 年之前的几年里),这种方法比其他方法更不可靠。 当不好的事情发生时,journald 中的索引肯定会变得非常糟糕,以至于 journalctl -b -1 命令只能给出一个错误。我不确定这是否已在之后的 RHEL 7 和 RHEL 8 版本中得到了改进。

Comments