【编者按】新婚现场给服务器扩容,下班路上修Bug……对于程序员来说,这样的日常并不陌生。在新年第一天,国外一名叫Ali Josie 的软件工程师、信息安全爱好者就经历了找Bug、复现、修复这样的事情,并且还发表了一篇《This Is Why Our 3000 Apache Servers Went Down On The First Day of 2022》,具体是怎么一回事呢?

作者 |Ali Josie 译者 | 弯月
出品 | CSDN(ID:CSDNnews)

新年第一天,又恰逢周六,早上醒来却看到一堆整个基础设施挂掉的警报!我的一位同事就遭遇了这样的真实,他当时的心情可想而知。

大清早

首先,最重要的是恢复服务,把服务宕机的影响降到最低,我们重启了所有Apache服务器,还好没有任何问题。接下来就要找出宕机的原因了。为什么所有服务器都在新年第一天宕机?这肯定不是偶然吧?

我们看到每台服务器上都记录了如下日志:

AH00171: Graceful restart requested, doing restart
libgomp: could not create thread pool destructor.

libgomp是什么?我们先上网查了一下这个错误。ServerFault上有人问过这个问题,但没人回答,至少没有我们能用的东西。不过这个问题有点奇怪,因为提问者说他的服务器每隔24~36小时就会发生一次。

思考

回到错误本身。我们每天早上都会做一次日志轮转,这样每天都用新的日志。因此要重启服务器。似乎Apache已经成功重启,但由于libgomp错误又宕机了。

在网上搜索到的大量结果中寻找答案无异于大海捞针,于是我们开始阅读libgomp的源代码,看看究竟发生了什么。首先,libgomp是什么?根据其主页的描述:

  • “GOMP项目是C、C++和Fortran编译器OpenMP的一个实现……GOMP能简化所有GNU系统上的并行编程。”

所以它是OpenMP的实现。它怎么会出问题?

搜索了一下源代码, 我们发现错误消息的唯一出处是这里:

所以显然,它在试图创建一个线程键,但出错了。检查pthread_key_create的手册:

  • “pthread_key_create会创建用于线程专有的数据键,可在进程的所有线程中使用。pthread_key_create()提供的键值是不透明的对象,用于定位线程专有的数据。虽然不同线程可以使用相同的键名称,通过pthread_setspecific()绑定到键的值是按照线程维护的,在线程的整个生命周期都有效。”

有意思!那返回值是什么?

“pthread_key_create()函数会在下述情况失败:

  • 系统资源不足,无法创建另一个线程特定数据键,或每个进程的键总数达到了 PTHREAD_KEYS_MAX 上限。

  • 内存不足,无法创建键。”

然后检查了代码,看看发生了什么,以及PTHREAD_KEYS_MAX最大值是多少:

所以说,key只是一个0~1024之间(不含1024)的数字,赋给pthread_key_create的调用者。这些键由一个简单的CAS负责赋值,因此肯定有某个地方释放这些键。似乎我们找到了问题。我们只需要增大PTHREAD_KEYS_MAX。但是,这个值是常量。我们甚至找到了一个帖子,要求增加PTHREAD_KEYS_MAX:

  • “pthread_key_create()会拒绝超过 PTHREAD_KEYS_MAX pthread_key_t的创建请求。我遇到的问题是在NetBSD上Apache无法与多种模块一起工作,因为这个值太低了。时间长了,服务器就会陷入无法提供服务的状态。”

这篇帖子描述的问题域我们相似,因此我们的假设可能是正确的。但是我们依然没办法增大这个值。

我们开始调查为何重新加载Apache会进入libgomp的这段代码。所以显然,重载Apache会导致mod_php调用一个名为Imagick的模块。Imagick是什么?它是一个使用ImageMagick库来创建和修改图片的PHP扩展。

怀疑

似乎关闭Imagick就可以避免使用libgomp,这样就不会遇到最大线程数的问题了。而且只需要设置一个环境变量即可。似乎这个方案非常安全,但我们依然有一个最大的疑问:

  • 为什么会在1月1日发生?而且这么大的范围,真的是偶然吗?

  • 为什么用了这么多年都没事儿?会不会因为是某个更新的原因?

这样解决问题显然不能让我们满意。还有好多未解之谜。我们开始进一步阅读Apache HTTP的和libgomp的代码,但似乎一切都很正常,至少我们没发现任何问题。问题也无法重现,很快这个问题就会变成未解之谜。我们搜索了许多无关的关键字,甚至找到了一些关于“2038年问题”的帖子。

但这些都没有任何帮助。我们甚至怀疑过Apache的最大uptime。

最后我们检查了Imagick的更新日志,发现了这个:

“多个修改来减少GOMP段错误的发生,包括:

  • 在关闭过程中,如果可能,则调用omp_pause_resource_all

  • 增加了 imagick.shutdown_sleep_count (默认10)和imagick.set_single_thread(默认On)。两者都可以减少关闭时的段错误。”

这符合我们的猜测:将Imagick的最大线程数设置为1就能解决问题。但并没有解答有关时间的最大疑问。

灵光乍现

在搜索了更多奇怪的东西后,我们想看看一月份有没有人遇到这个问题。


第一篇文章正是解开这一切的钥匙!

突然想到……要是线程键从来没有被释放,会怎样?有可能吗?因为从部署依赖就从来没有发生过这个问题……所以我们重新计算了一下,1024个键,如果每天早上重新加载,就需要两年零10个月才会超过1024次重新加载。如果过去1024天内每天早上都分配一个线程键,而这个键从未被释放的话……

终于看到了一丝曙光。我们终于找到了重现该问题的方法。我们做了一个测试环境,用同样的服务器配置,然后简单地运行这个脚本。


for i in seq{1..1100}; do sudo systemctl reload apache2;done

重新加载apache2 1100次(多了76次作为冗余)。然后果然问题出现了!

Apache在重新加载了1024次以后,libgomp就报错了。现在所有问题都得到了解答。

来看看能否通过增加环境变量MAGICK_THREAD_LIMIT(新版Imagick是OMP_THREAD_LIMIT)。很不幸,问题依旧。所以下一步就是更新Imagick版本到一个修正了该问题的版本(v3.5.0+)。很幸运,更新之后重新加载数千次都不会出问题。

检查

还有个未解决的问题:新版Imagick有没有删除这个键?为了解答这个疑问,我们使用了一个工具:ltrace这个工具可以截获并记录程序运行的特定命令。我们首先在旧版本的Imagick(v.3.4.4)的服务器上运行ltrace:

ltrace -xpthread_key_*@libpthread.so.0 -L -c /usr/sbin/apache2 -k graceful
  • -x是特定库中的函数的搜索字符串,此处为 libpthrad.so.0中的 pthrad_key_create 和 pthread_key_delete。

  • -L告诉ltrace忽略默认的过滤器,以降低噪声。

  • -c会在末尾汇总所有结果。而 /usr/sbin/apache2 -k graceful相当于systemctlreload apache。

结果并没有出乎意料:

% time    seconds  usecs/call     calls     function
------ ----------- ----------- -----------------------------
100.00   0.000157         157         1 pthread_key_create
------ ----------- ----------- -----------------------------
100.00   0.000157                     1total

3.4.4版只调用了pthread_key_create而没有删除!

然后在新版(v3.6.0)上运行同样的命令:


% time    seconds  usecs/call     calls     function
------ ----------- ----------- ------------------------------
------ ----------- ----------- ------------------------------
100.00    0.000000                     0 total

看来,新版都没有使用多线程,因此完全没有创建键。

总结

终于解决了,但是为什么这么长时间都没有重启过?我们决定不再在这个问题上浪费时间了,因为“如果排除一切不可能的选项,那么剩下来的那个无论多么不可思议,都是真相。”

解决这个问题后感觉挺奇怪。虽然解决问题感觉挺自豪,但世界上还有很多长时间运行的服务器不知道在什么时候就会遇到这个问题。

原文链接:https://alijosie.medium.com/this-is-why-our-3000-apache-servers-went-down-on-the-first-day-of-2022-3cc5e9639587

本文为 CSDN 翻译,转载请注明来源出处。

新年第一天,3000台Apache服务器宕机相关推荐

  1. 服务器高并发处理/服务器宕机了怎么处理?

    服务器高并发处理/服务器宕机了怎么处理? 高并发问题是大部分服务器都经历过的,由于资源的有限性,其同时处理请求的能力自然也有限制.当高并发出现时,服务端的处理和响应速度会大幅降低,更严重的会使服务器崩 ...

  2. 从春运分析服务器宕机原因

    春运,这一号称人类历史上规模最大的周期性人类大迁徙,2011年如期而至,在从1月19日开始为期40天左右的时间内,铁道部预计客流量将达28.5亿人次,快要把我国大众运个来回了.如此浩大的客流,造就了一 ...

  3. 拼多多显示服务器,9.9元秒iPhoneXS致拼多多服务器宕机了:官方回应

    原标题:9.9元秒iPhoneXS致拼多多服务器宕机了:官方回应 [PConline资讯]12月31日跨年夜,电商平台拼多多提供了9.9秒杀1000台iPhone.瓜分2亿红包等活动.由于瞬时涌入的用 ...

  4. Nginx负载均衡,服务器宕机问题

    如果Nginx没有仅仅只能代理一台服务器的话,那它也不可能像今天这么火,Nginx可以配置代理多台服务器,当一台服务器宕机之后,仍能保持系统可用.具体配置过程如下: 1. 在http节点下,添加ups ...

  5. 如何应对缓存服务器宕机的情况

    假如所有缓存服务器都宕机,而且不能很快恢复,并且假设数据库服务器能够支撑,在代码中如何应对这样的情况? 之前的做法是在读缓存的地方捕获异常并写入日志,然后直接从数据库读取数据:在写缓存的地方捕获异常并 ...

  6. 如何应对java服务器宕机_代码中如何应对缓存服务器宕机的情况

    今天在演练这样一个场景--假如所有缓存服务器都宕机,而且不能很快恢复,并且假设数据库服务器能够支撑,在代码中如何应对这样的情况? 之前的做法是在读缓存的地方捕获异常并写入日志,然后直接从数据库读取数据 ...

  7. keep老是显示服务器开小差,nginx+keepalived高可用服务器宕机解决方案

    原标题:nginx+keepalived高可用服务器宕机解决方案 1.基本服务器宕机的主从切换配置 两台nginx服务器,分别安装keepalived,配置/etc/keepalived/keepal ...

  8. 网站服务器宕机怎么办,服务器宕机了该怎么办?

    原标题:服务器宕机了该怎么办? 导读 如果Web服务器已经虚拟化,试着ping物理服务器自己的真实IP.这样可以帮助你进一步隔离问题.如果完全无法ping同服务器,而且也已经确定完全检查了网络连接,那 ...

  9. 服务器宕机记录16.12.14

    阿里云主机 16.12.14日11:37分宕机,第一时间收到短信,ssh远程远法登录,阿里云控制台,无法登录.查看CPU和内存,没有鸟用,只能重启. 11:40分,正常登录主机,查看messages ...

最新文章

  1. VMware Workstation Pro下载
  2. HTML的五种经典布局方式(二)
  3. Linux学习之六-Yum命令的使用
  4. python三大特征六大原则_面向对象程序设计(Object Oriented Programming)的三大特性,六大原则...
  5. 部署php项目到linux
  6. 计算机组成原理三个相关,计算机组成原理试题3
  7. Jmeter测试计划无法保存或者另存为
  8. APP测试点(思维导图)
  9. 1114 Family Property (25 point(s)) PAT甲级
  10. 三层交换(VLAN间路由)
  11. 微信安装旧版本方法,版本过低,请升级最新版本
  12. Word-VBA:删除选择区域的项目符号
  13. wxpython使用多进程_最简单方法远程调试Python多进程子程序
  14. 市场导向还是技术导向
  15. PicPick软件免费版与正式版区别
  16. [XSY] 线图(树形DP、分类讨论)
  17. 理解Javascript的正则表达式
  18. python实现之初等函数三——三角函数
  19. Vue--keep-alive--详解
  20. 第五季完美童模 形象大使林子懿现场走秀回顾

热门文章

  1. Tensorflow结点打包和依赖控制
  2. 【转】Java 项目UML反向工程转化工具
  3. 栈的效率为什么比堆高?为什么栈的运行速度比堆快?
  4. c/c++教程 - 总集1. c基础语法
  5. JS小技巧 ----- 在onmousedown事件下避免文本选取
  6. 【100题】第三十六 比赛淘汰问题(谷歌笔试)
  7. 腾讯-004-两个排序数组的中位数
  8. Team Foundation Server
  9. 二叉树关于,前序遍历的输入是否规范问题、
  10. 基本飞行模式中英文对照