故障复盘:PHP 和 MariaDB Docker 问题

多年前,我和我的孩子们(现在他们都长大了)一起看了一部名为《拜访罗宾逊一家》的电影。电影中贯穿始终的主题是,犯错误是可以的,因为我们可以从错误中学习,并“继续前进”。
不幸的是,几个问题凑巧同时发生,导致 2024 年 2 月 21 日,mariadb:latest
Docker 镜像无法与 PHP 和 NodeJS 客户端一起工作。现在,我在 MariaDB 基金会帮忙引入的一件事就是进行故障复盘的概念,不仅仅是在事情出错时,也在事情顺利时进行。这些复盘不是为了找出谁的错,也不是为了推卸责任。而是为了找出流程中的缺陷,正是这些缺陷导致了问题的发生,这样我们才能学习不再重蹈覆辙。为了制定措施防止再次发生。我们实际上会在每个发布周期都进行复盘,记录发生的事情,并看看是否有可以自动化和改进的地方。
在这种情况下,它影响了许多使用 Docker 获取 MariaDB 服务器的 PHP 应用程序用户。所以,我认为我们应该尽可能透明地说明发生了什么,我们如何解决的,以及我们打算如何阻止它在未来再次发生。
话不多说,让我们深入探讨。
前言
mariadb:latest
Docker 更新到 11.3 版本,其中包含 Debian 软件包的默认排序规则配置文件更改。该排序规则触发了 PHP mysqlnd 和 NodeJS 连接器中的一个 bug,导致应用程序无法登录。
在 MySQL 4.0 中,服务器将默认排序规则作为 1-255 范围内的单字节 ID 发送到客户端。在 MySQL 4.1(2003 年发布)中,这种通信方向发生了改变,现在是客户端到服务器,但为了兼容性,服务器到客户端的通信仍然受支持,并且一直延续至今。MariaDB 的新排序规则使用的 ID 高于 255,因此兼容性排序规则字节以零开头,并在其他地方使用扩展字节。
Debian 软件包中的配置文件在 PR 2775 中更新,使用了更现代的默认服务器排序规则,该 PR 已在 11.3 版本中合并。PHP 的 mysqlnd 和 NodeJS 的连接器仍然验证服务器排序规则,当它们看到零时,就会生成一个错误。这个错误会中止身份验证过程。
事件序列
为了阐明发生了什么,以下是对事件的详细记述。如果您对全局感兴趣,请随时跳到“原因”标题。
提供的时间均为 UTC 时间。
2023-10-19
一个合并请求被合并,该请求更改了 Debian 软件包配置文件中的默认服务器排序规则。注意:MariaDB Docker 镜像使用 Debian 打包。
2023-12-08
Jira MDEV-32975 已开启,用户发现上述提交在使用 MariaDB 11.3.1 的 Debian 软件包时破坏了 PHP 应用程序。
2024-01-26
Alexander Barkov 创建了一个兼容性补丁作为 MDEV-32975 的解决方案,并提交进行代码审查。
2024-02-20 23:45
MariaDB Docker 镜像已更新,使 mariadb:latest
指向 11.3.2 而不是 11.2.3,因为这是 MariaDB 新的最新稳定版本。
2024-02-21 07:34
社区用户在 MariaDB Docker 上开启了一个 issue,该用户在使用 Docker 镜像时遇到了身份验证问题。
2024-02-21 09:44
社区用户出于同样原因在 PHP 项目 GitHub 上开启了一个 issue。
2024-02-21 10:21
Andrew Hutchings 通过 Twitter 收到了问题警报并开始调查。
2024-02-21 10:34
Vicențiu Ciorbaru(通过 Docker GitHub issue 注意到问题)和 Andrew 同时内部通知了基金会其他员工。
2024-02-21 11:32
Andrew 已经确定问题原因即MDEV-32975。他请求将其升级为 Blocker(阻止)状态(立即得到批准)。向 MariaDB 核心开发人员沟通了严重性变更的原因。
2024-02-21 11:52
经过一些内部讨论后,Andrew 在 MariaDB Docker GitHub 议题上发布了一个临时解决方案。半小时内,社区进行了测试,似乎是成功的。
2024-02-21 16:59
经过大量研究和讨论,Andrew 弄清了根本原因的细节,他更新了 PHP 议题,提供了详细信息,导致该议题被重新开启。
2024-02-21 17:45
在他一天结束时,Andrew 内部将目前为止的所有信息传递给了团队的其他成员,但他继续协助社区成员解决这个问题直到深夜。他后来向团队沟通发现 NodeJS 也受到了影响。
2024-02-21 21:56
Daniel Black 负责 MariaDB Docker 镜像的管理,他上线并开始着手解决问题。
2024-02-21 22:50
Daniel 打开了 CONJS-281 来处理 NodeJS 的问题。
2024-02-22 00:52
Daniel 在 MariaDB 的 Docker 仓库中开启了一个合并请求来临时解决这个问题。
2024-02-22 01:06
Daniel 为 Docker 创建了一个合并请求,该请求指向带有临时解决方案的 MariaDB Docker 更新。4 分钟后,该请求被合并。
2024-02-22 04:43
更新后的 Docker 镜像已发布到 Docker Hub。
2024-02-22 05:04
MariaDB Docker GitHub 议题被标记为关闭。之后进行了外部沟通。
原因
不幸的是,几个问题凑巧同时发生,导致了这个问题。如果其中任何一个问题没有发生,我们都不会遇到这些麻烦。
- PHP mysqlnd 和 NodeJS 连接器试图验证一个已经 20 年未使用的字节上的数据。这对我们来说是出乎意料的,但可以理解这个检查从未被移除。
- 这个 bug 实际上通过 MDEV-32975(默认字符集不兼容 PHP MySQLi 扩展)得到了修复,但是这个补丁在 11.3 GA 之前没有经过审查,所以它不在代码库中。
- 上述两个因素的结合导致 PR 2775 (MDEV-32336 deb 默认配置 – 使用 collation-server = utf8mb4_uca1400_ai_ci) 中的配置变更导致了故障。
- 我们有可以及早发现此问题的 CI 测试,但 MDBF-637(由于 reprepro 缺少 debuginfo 软件包)意味着相关的 CI 构建器目前处于损坏状态。
见解
- WordPress 和 Nextcloud 社区在遇到问题时反应和沟通非常及时。
- 11.3 的 Debian 软件包仍然受到影响,根据当前的发布模型,将不再有 11.3 的后续版本发布,因此此问题将在 11.4 中修复。
- 我们确实有流程来避免和检测类似的问题,但当事态变得糟糕时,流程确实会失效。
建议
以下是我们正在实施的建议,以正确解决此问题并阻止未来发生类似情况。
- MDEV-32975 的补丁需要被审查和合并,如果需要更多工作,则需要纠正后合并。
- 11.3 的发布说明需要更新,以通知可能受此影响的 Debian 用户,并说明如何实施临时解决方案。
- 我们需要修复 MDBF-637,确保可以捕获此问题的测试能够运行,并使 amd64-rhel8-wordpress 成为合并请求的必需测试项。
- 我们应该通过博客文章(即此博客文章)将本次故障复盘公开。
结论
希望这篇文章能够让您了解 MariaDB Server 版本发布时出现问题后会发生什么。我们要感谢社区的快速报告、响应和反馈。没有你们,我们不可能这么快解决问题。
特色图片来自 Digits.co.uk,在 CC 许可下使用。
这处理得很差劲。MariaDB 团队本可以这么做:
* 回滚提交 https://github.com/MariaDB/server/pull/2775/files
* 发布 11.3.3(含修复),并宣布此版本为例外,且由于新的发布模型,未来将不再提供更新,详情见 https://mariadb.com/kb/en/mariadb-release-model/
现在,从今天到 11.4 稳定版发布期间,每个尝试使用 MariaDB 的新用户,要么在尝试连接其应用程序到服务器后放弃,要么像原始人一样手动修改默认配置(临时解决方案)。因此,你们将失去许多从官方网站下载最新稳定版(11.3)的潜在新客户。
嗨 Jon!感谢您的反馈。
事实上,我们首先想到的就是考虑紧急发布新版本。无论采用何种发布模型,当我们发布的服务器“无法使用”时,我们总是会这样做。对于这次事件,我们退后一步,分析了受影响的用户。
1. 大多数受影响的用户是使用 latest 标签运行 Docker 镜像的用户。对于这些用户来说,目前问题已 100% 解决。
2. RPM 和二进制 tarball 用户完全不受影响。
3. 大多数 Debian 和 Ubuntu 用户仍然主要使用各自发行版的官方仓库,这些仓库通常基于长期支持 (LTS) 分支,因此不受影响。
4. 在此事件中受影响的特定用户是使用 mariadb.org 上的 DEB 仓库的用户。对于这些用户,是的,他们需要更改配置才能使服务器正常工作。我们将在下载页面为该版本添加一条特别说明,说明需要进行配置临时解决方案。
5. 发布一个新版本至少需要一到两天时间,从初始提交到软件包可用。这还涉及到大量的镜像同步工作。总而言之,通过正常渠道将修复程序送达用户手中需要一些时间,而我们当时的重点是尽快为实际报告服务中断的用户解决问题,这次主要是容器用户。
6. 如果我们收到社区的额外投诉,我们可能仍然会决定发布 11.3.3 版本。
我喜欢这个时间线、事件序列、完全公开以及等等。
但我忍不住想问。你们能否对 MySQL Connector/NET 持续 1.5 年的故障进行事后复盘(https://stackoverflow.com/questions/74060289/mysqlconnection-open-system-invalidcastexception-object-cannot-be-cast-from-d/78025438#78025438 ,这是在 LTS 版本中发生的,影响了依赖此连接器的应用程序,如 PowerBI 和 Toad),这似乎并没有让你们太困扰?Debian 带着这个 LTS 版本发布了,而这个连接器无法工作。没有可以更改的配置文件来修复它。唯一的临时解决方案是使用另一个连接器重新编译(这对于 PowerBI 或 Toad 当然不在考虑范围内)。没有紧急性,没有火线修复,没有讨论重新发布,为什么?
虽然显然存在一些失误和错误,但这次应对似乎有很多成功之处。清晰的沟通,在制定长期解决方案的同时专注于即时临时解决方案和稳定性,与受影响社区协作实施他们可以执行的长期修复……有很多值得称赞的地方。
对我来说,一个突出点是 :latest 通常被认为是一个不安全的、会移动的标签。我只在我的 CI 测试中使用它来留意这类可能出现的问题。这可能会影响我对此事件的解读,而且它被切换到 11.3.2 让我感到相当困惑,因为我原本以为会切换到 11.3.0,所以我查找的方向不对,并且难以理解一些沟通内容。直到这篇文章发布后,我才真正弄明白。
另一个突出点是,主要的语言连接和标准查询功能是否没有针对每个版本进行测试?这似乎是一个可以触发阻止性问题(blocker)或至少向语言维护者告知潜在破坏性变更的步骤。
顺便说一句,Drupal 社区也在默默关注并随时准备在需要时做出响应 😉
感谢这篇出色的文章!
嗨 James,
非常感谢您的评论。
我相信按照 Docker 的惯例,`:latest` 是项目最新的稳定版本。11.3.2 是 11.3 系列新发布的 GA(通用版本),这就是为什么它当时从 11.2 系列切换到该版本(11.3.1 是一个 RC)。但是,我们应该考虑添加一个某种意义上的前沿(bleeding-edge)标签,正是为了您提到的用例。我会和团队讨论这个问题。
我们通常会针对许多语言连接和功能进行测试。但是,显然我们需要在这方面进行改进,目前正在采取措施来暴露这些问题。
很高兴 Drupal 项目在关注,希望能在 CloudFest 黑客松见到你们的一些成员 🙂