![闰秒期间还有其他人遇到过 Linux 服务器大量崩溃的情况吗?](https://linux22.com/image/588854/%E9%97%B0%E7%A7%92%E6%9C%9F%E9%97%B4%E8%BF%98%E6%9C%89%E5%85%B6%E4%BB%96%E4%BA%BA%E9%81%87%E5%88%B0%E8%BF%87%20Linux%20%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%A4%A7%E9%87%8F%E5%B4%A9%E6%BA%83%E7%9A%84%E6%83%85%E5%86%B5%E5%90%97%EF%BC%9F.png)
*注意:如果您的服务器仍然因内核混乱而出现问题,并且您无法重新启动 - 在您的系统上安装 gnu date 后,最简单的解决方案是:date -s now。这将重置内核的内部“time_was_set”变量并修复 java 和其他用户空间工具中占用 CPU 的 futex 循环。我已经在自己的系统上跟踪了此命令,并确认它正在执行它所说的操作*
事后分析
令人失望的是:唯一断掉的是我的 VPN(openvpn)与集群的链接,因此在它重新建立的几秒钟里,我们兴奋不已。其他一切都很好,闰秒过去后,ntp 启动也很顺利。
我已经把我当天的经历写在http://blog.fastmail.fm/2012/07/03/a-story-of-leaping-seconds/
如果你看看 Marco 的博客http://my.opera.com/marcomarongiu/blog/2012/06/01/an-humble-attempt-to-work-around-the-leap-second- 他有一个解决方案,使用 ntpd -x 在 24 小时内逐步更改时间,以避免 1 秒的跳过。这是运行您自己的 ntp 基础设施的替代涂抹方法。
就在今天,2012 年 6 月 30 日星期六 - 格林威治标准时间刚过一天。我们位于不同数据中心的几台服务器(由不同的团队管理)全部瘫痪 - 不响应 ping,屏幕空白。
它们都运行 Debian Squeeze - 包括从库存内核到自定义 3.2.21 版本的一切。大多数是 Dell M610 刀片,但我刚刚丢失了一台 Dell R510,其他部门也丢失了其他供应商的机器。还有一台较旧的 IBM x3550 崩溃了,我以为这可能无关紧要,但现在我很怀疑。
有一次崩溃我确实得到了屏幕转储,其中显示:
[3161000.864001] BUG: spinlock lockup on CPU#1, ntpd/3358
[3161000.864001] lock: ffff88083fc0d740, .magic: dead4ead, .owner: imapd/24737, .owner_cpu: 0
不幸的是,所有刀片服务器都配置了 kdump,但它们死机太厉害,kdump 无法触发 - 而且它们还打开了控制台消隐功能。我现在已经禁用了控制台消隐功能,所以希望下次崩溃后我能得到更多信息。
只是想知道这是普遍现象还是“只有我们”。它们位于不同数据中心的不同单元,购买时间不同,由不同的管理员运行(我运行的是 FastMail.FM 的单元),这真的很奇怪……现在甚至还有不同的供应商硬件。大多数崩溃的机器已经运行了数周/数月,并且运行的是 3.1 或 3.2 系列内核。
最近的一次崩溃是一台运行 3.2.21 的机器,它只运行了大约 6 个小时。
解决方法
好的,各位,下面是我解决这个问题的方法。
- 禁用ntp:
/etc/init.d/ntp stop
- 创建http://linux.brong.fastmail.fm/2012-06-30/fixtime.pl(代码取自 Marco,请参阅评论中的博客文章)
- 毫无争议地跑去
fixtime.pl
看看有没有闰秒 - 提出
fixtime.pl
要取消闰秒
注意:取决于。我把 squeeze二进制文件adjtimex
的副本放在adjtimex
http://linux.brong.fastmail.fm/2012-06-30/adjtimex— 它可以在不依赖 squeeze 64 位系统的情况下运行。如果您将它放在与 相同的目录中fixtime.pl
,则当系统版本不存在时将使用它。显然,如果您没有 squeeze 64 位……请自行寻找。
我ntp
明天又要重新开始。
正如一位匿名用户所建议的那样 - 运行的替代方法adjtimex
是自己设置时间,这大概也会清除闰秒计数器。
答案1
这是由 ntpd 调用 adjtimex(2) 来告诉内核插入闰秒时发生的活锁引起的。请参阅 lkml 帖子http://lkml.indiana.edu/hypermail/linux/kernel/1203.1/04598.html
Red Hat 也应该更新他们的 KB 文章。https://access.redhat.com/knowledge/articles/15145
更新:Red Hat 有第二篇专门针对此问题的知识库文章:https://access.redhat.com/knowledge/solutions/154713- 上一篇文章是针对一个早期的、不相关的问题
解决方法是关闭 ntpd。如果 ntpd 已经发出 adjtimex(2) 调用,您可能需要禁用 ntpd 并重新启动才能 100% 安全。
这会影响 RHEL 6 和其他运行较新内核(约比 2.6.26 更新)的发行版,但不会影响 RHEL 5。
发生这种情况的原因前闰秒实际安排发生的原因是 ntpd 让内核在午夜处理闰秒,但需要在午夜之前提醒内核插入闰秒。因此,ntpd 会在闰秒当天的某个时间调用 adjtimex(2),此时会触发此错误。
如果您安装了 adjtimex(8),则可以使用此脚本来确定是否设置了标志 16。标志 16 是“插入闰秒”:
adjtimex -p | perl -p -e 'undef $_, next unless m/status: (\d+)/; (16 & $1) && print "leap second flag is set:\n"'
更新:
Red Hat 已更新其 KB 文章,内容如下:“RHEL 6 客户可能会受到一个已知问题的影响,该问题会导致 NMI Watchdog 在收到 NTP 闰秒公告时检测到挂起。此问题正在及时解决。如果您的系统收到了闰秒公告,但没有遇到此问题,则它们不再受到影响。”
更新:上述语言已从 Red Hat 文章中删除;并添加了第二个 KB 解决方案,详细说明了 adjtimex(2) 崩溃问题:https://access.redhat.com/knowledge/solutions/154713
然而,IBM 工程师 John Stultz 在 LKML 帖子中更改的代码指出,在实际应用闰秒时也可能出现死锁,因此您可能需要在禁用 ntpd 后通过重新启动或使用 adjtimex(8) 来禁用闰秒。
最后更新:
好吧,我不是内核开发人员,但我在这里再次查看了 John Stultz 的补丁:https://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=6b43ae8a619d17c4935c3320d2ef9e92bdeed05d
如果这次我没看错的话,我之前关于应用闰秒时会出现另一个死锁的说法是错误的。根据他们的 KB 条目,这似乎也是 Red Hat 的观点。但是,如果您已禁用 ntpd,请将其再禁用 10 分钟,这样当 ntpd 调用 adjtimex(2) 时就不会遇到死锁。
我们很快就会发现是否还有更多错误:)
跃迁后第二次更新:
我花了几个小时阅读 ntpd 和修补前的(有缺陷的)内核代码,虽然我在这里可能错了,但我会尝试解释我认为发生了什么:
首先,ntpd 始终调用 adjtimex(2)。这是其“时钟环路过滤器”的一部分,在 ntp_loopfilter.c 中的 local_clock 中定义。您可以在此处看到该代码:http://www.opensource.apple.com/source/ntp/ntp-70/ntpd/ntp_loopfilter.c(来自 ntp 版本 4.2.6)。
时钟循环过滤器运行得相当频繁 —— 每次 ntpd 轮询其上游服务器时它都会运行,默认情况下每 17 分钟或更长时间运行一次。时钟循环过滤器的相关位是:
if (sys_leap == LEAP_ADDSECOND)
ntv.status |= STA_INS;
进而:
ntp_adjtime(&ntv)
换句话说,在有闰秒的日子里,ntpd 会设置“STA_INS”标志并调用 adjtimex(2)(通过其可移植性包装器)。
该系统调用进入内核。以下是相关的内核代码:https://github.com/mirrors/linux/blob/a078c6d0e6288fad6d83fb6d5edd91ddb7b6ab33/kernel/time/ntp.c
内核代码路径大致如下:
- 第 663 行——do_adjtimex 例程开始。
- 第 691 行-取消任何现有的闰秒计时器。
- 第 709 行 - 抓住 ntp_lock 自旋锁(此锁与可能的活锁崩溃有关)
- 第 724 行——调用 process_adjtimex_modes。
- 第 616 行——调用 process_adj_status。
- 第 590 行 - 根据 adjtimex(2) 调用中设置的标志,设置 time_status 全局变量
- 第 592 行 - 检查 time_state 全局变量。在大多数情况下,调用 ntp_start_leap_timer。
- 第 554 行 - 检查 time_status 全局变量。STA_INS 将被设置,因此将 time_state 设置为 TIME_INS 并调用 hrtimer_start(另一个内核函数)来启动闰秒计时器。在创建计时器的过程中,此代码会获取 xtime_lock。如果发生这种情况,而另一个 CPU 已经获取了 xtime_lock和ntp_lock,然后是内核活锁。这就是 John Stultz 编写补丁以避免使用 hrtimers 的原因。这就是今天给大家带来麻烦的原因。
- 第 598 行 - 如果 ntp_start_leap_timer 实际上没有启动跳跃计时器,则将 time_state 设置为 TIME_OK
- 第 751 行 - 假设内核没有活锁,则堆栈被解开并且 ntp_lock 自旋锁被释放。
这里有一些有趣的事情。
首先,每次调用 adjtimex(2) 时,第 691 行都会取消现有计时器。然后,第 554 行会重新创建该计时器。这意味着每次 ntpd 运行其时钟循环过滤器时,都会调用有缺陷的代码。
因此,我认为 Red Hat 的说法是错误的,他们说一旦 ntpd 设置了闰秒标志,系统就不会崩溃。我相信每个运行 ntpd 的系统都有可能在闰秒之前的 24 小时内每 17 分钟(或更长时间)发生一次活锁。我相信这也可以解释为什么这么多系统崩溃;与每小时 3 次相比,一次崩溃的可能性要小得多。
更新:在 Red Hat 的知识库解决方案中https://access.redhat.com/knowledge/solutions/154713,Red Hat 工程师确实得出了同样的结论(运行 ntpd 会不断遇到错误代码)。而且他们确实比我早几个小时就得出了结论。此解决方案未链接到主要文章https://access.redhat.com/knowledge/articles/15145,所以直到现在我才注意到。
其次,这也解释了为什么加载的系统更容易崩溃。加载的系统将处理更多中断,从而导致“do_tick”内核函数被更频繁地调用,从而为该代码在创建计时器时运行和获取 ntp_lock 提供了更多机会。
第三,当闰秒真正发生时,系统是否有可能崩溃?我不确定,但有可能,因为触发并实际执行闰秒调整的计时器(ntp_leap_second,第 388 行)也会获取 ntp_lock 自旋锁,并调用 hrtimer_add_expires_ns。我不知道该调用是否也可能导致活锁,但这似乎并非不可能。
最后,是什么原因导致闰秒标志在闰秒运行后被禁用?答案是 ntpd 在午夜后调用 adjtimex(2) 时停止设置闰秒标志。由于未设置标志,第 554 行上的检查将不为真,并且不会创建任何计时器,第 598 行将把 time_state 全局变量重置为 TIME_OK。这解释了为什么如果您在闰秒后立即使用 adjtimex(8) 检查标志,您仍然会看到闰秒标志被设置。
简而言之,今天最好的建议似乎毕竟是我给出的第一个建议:禁用 ntpd,并禁用闰秒标志。
最后还有一些想法:
- 没有一家 Linux 供应商注意到 John Stultz 的补丁并将其应用到他们的内核中 :(
- 为什么 John Stultz 没有提醒一些供应商这是必要的?也许活锁的可能性看起来很低,发出噪音是没有必要的。
- 我听说过,在应用闰秒时,Java 进程会锁定或旋转。也许我们应该效仿 Google,重新思考如何将闰秒应用于我们的系统:http://googleblog.blogspot.com/2011/09/time-technology-and-leaping-seconds.html
06/02 John Stultz 的更新:
https://lkml.org/lkml/2012/7/1/203
该帖子逐步介绍了闰秒为何会导致 futex 计时器过早并持续到期,从而导致 CPU 负载激增。
答案2
这给我们带来了沉重的打击。在重启了许多主机后,下面的操作非常简单,而且完全有效,无需重启主机:
/etc/init.d/ntp stop
ntpdate 0.us.pool.ntp.org
/etc/init.d/ntp start
只需重置系统时钟即可。天哪。我六个小时前就知道了。
答案3
一个简单的 C 程序,用于清除内核时间状态字段中的闰秒位:
#include <sys/timex.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char **argv) {
struct timex txc;
int ret;
(void) argc;
(void) argv;
bzero(&txc, sizeof(txc));
txc.modes = 0; /* fetch */
ret = adjtimex(&txc);
if (ret < 0) {
perror("adjtimex (get)");
return 1;
}
txc.modes = ADJ_STATUS;
txc.status &= ~16;
ret = adjtimex(&txc);
if (ret < 0) {
perror("adjtimex (set)");
return 1;
}
return 0;
}
另存为,用root 身份lsec.c
编译并运行。gcc -Wall -Wextra -o lsec lsec.c
您可能希望在运行 ntpd 之前先将其停止,然后在闰秒之后重新启动 ntpd。
答案4
http://my.opera.com/marcomarongiu/blog/2012/03/12/no-step-back似乎表明 Debian squeeze 内核无法处理闰秒。
关于 comp.protocols.tim.ntp 的这个线程也很有趣:https://groups.google.com/forum/?fromgroups#!topic/comp.protocols.time.ntp/KSflIgjUdPE
也就是说,闰秒还没有发生:23:59:60 UTC
最后,https://access.redhat.com/knowledge/articles/15145有以下内容:“当闰秒发生时,内核会将一条消息打印到系统日志中。打印此消息有可能会导致 Red Hat Enterprise Linux 中的内核崩溃。”