ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

原地报废:不要在生产环境用Docker跑PostgreSQL!

原地报废:不要在生产环境用Docker跑PostgreSQL! 早在 2019 年老冯就在《把数据库放入 Docker 中是一个好主意吗》提到过 ——不要在生产环境用容器运行 PostgreSQL 数据库因为你有极大概率会遇上一堆在物理机/虚拟机上根本不存在的麻烦与问题。 这不最近用 Docker “官方” 的 Postgres 镜像的用户在升级的时候就踩雷了。 昨天 PostgreSQL 社区的老法师 Gwen Shapira 在 X 发了个帖子吐槽了这个事。⚠️重要提醒不要在生产环境用 Docker 官方的 Postgres 镜像。 如果非要用请务必显式指定 Debian 基础镜像版本。 PostgreSQL 的小版本升级比如 17.6 → 17.7通常是安全、简单、推荐的理论上绝不会破坏任何东西。但是如果你使用的是 Docker 官方镜像并在最近8 月以来做过小版本升级你可能见过这样的警告 “数据库创建时使用的排序规则版本为 2.36但当前操作系统提供的版本是 2.41。请重建所有使用默认排序规则的对象并执行ALTER DATABASE mydb REFRESH COLLATION VERSION或使用正确版本的库重新构建 PostgreSQL。”为什么会这样原因其实很简单、也很离谱Docker 官方 PG 镜像只支持两个 Debian 版本当Debian发布新版本时只要你没明确指定debian版本标签它会自动变成新的默认基础镜像新的 Debian 版本用了新版本的 glibcglibc 更新后locale排序规则文件发生变化于是你现在的状态变成运行的 PostgreSQL链接的是一套 locale 文件而数据库里的数据与索引是基于另一套旧的 locale 文件生成的PostgreSQL 很清楚这种混用会导致查询结果错误排序错误更严重时甚至会触发数据损坏因此它才会要求你重建所有受影响的对象再执行ALTER DATABASE ... REFRESH COLLATION VERSION而这一套操作本来只有在大版本升级才需要做谁都不会想到一个小版本升级居然要你重建整个数据库。 结果是Docker 官方镜像强行把这东西甩到用户脸上小版本升级也可能触发 glibc/locale 变化。小心官方镜像并不意味着 “负责任的生产环境表现”想象一下你用着 Docker 提供的 “官方” postgres 镜像然后赶上这周的 PostgreSQL 最新小版本发布 —— 于是准备升级一个小版本。 PG 小版本升级难道不是很安全很简单的吗只要重新 pull 一下 latest 镜像相当一部分人真是这么干的 稍微讲究一点的用户大概会使用 17.6 - 17.7这样的方式来拉取最新镜像感觉自己写死小版本没问题了吧。 如果是这样那就完犊子了除非你使用的镜像 Tag 严格包含了 Debian 版本号也就是 17.6-bookworm 这样的版本号否则在最近的小版本更新中实际隐含着一次 Linux 操作系统大版本升级。 你以为自己是从 17.6 升级到 17.7 但实际上还一起把底下的操作系统从 Debian 12 升级到了 13而这种计划外的原地升级会导致你的数据库索引原地报废或者更多到底是怎么回事Docker 官方提供的 PostgreSQL 镜像基于 Debian 系统镜像也有 Alpine 版本只不过阉割版基本都用 debian。 维护者指出这些镜像同时只支持两个 Debian 发行版当新的 Debian 稳定版发布时就会升级基础镜像到新版本并停止对最旧版本的支持。最近不是 Debian 13 trixie 刚刚发布了嘛于是 Docker 官方把postgres这个镜像升级了一下底层的 debian 系统镜像从 12 bookworm 升级到了 13 trixie。 结果底层 C 函数库 (glibc) 版本的出现跃迁 —— Debian 13的 glibc 版本从 12 的 2.36 升级到了 2.41而在这两个 glibc 版本中排序规则发生了变化这就坏事了。打开图片预览因为数据库索引的核心 —— 排序是由排序规则定义的而排序规则并非是一成不变的。 每当排序规则出现变化时使用旧版本排序规则的数据库集群就需要重建 —— 至少是重建索引否则的话就有可能出现数据损坏。 生产环境的严肃数据库哪有不用索引的结果就是至少在全库重建索引之前 —— “原地索引报废”数据库性能雪崩。 最坏的情况下还可能影响数据库约束数据一致性分区表的行为等等。这个失误的影响范围会很大在 DockerHub 上 postgres 镜像是下载量最大的镜像之一 —— 下载量已经超过十亿次最近一周 pull 大约一千七百万次。 很多用户都是docker pull postgres一把梭的就算指定了 17.6 这样的 PG 版本号 只要没指定 Debian 版本号也照样会翻车。紧急应对措施对于在生产环境中使用所谓 Docker 官方 “postgres” 容器的朋友老冯的建议是尽早把你的容器版本切换为锁定 PG Debian 版本号的镜像比如 17.6-bookworm。 这件事至少要在下次小版本升级 / 或者是重新 Pull 之前完成。在进行升级的时候也务必使用诸如17.7-bookworm这样的版本号。另外也不要想原地从17.7-bookworm直接飞升到17.7-trixie这种美事。 任何涉及到 glibc 版本的变动 (通常是 Linux 发行版大版本升级) 标准 SOP 都是要逻辑迁移的 —— 要么通过逻辑复制蓝绿部署在线迁移要么 pg_dump 逻辑转储。 除非你已经是聪明的 PG 老司机 —— 在初始化集群的时候就聪明的显式指定并选择了 PG built-in locale provider with C/C-UTF8。当然从长期来看最好还是迁移到物理机/虚拟机上的数据库部署方案更稳妥。 这一点老冯在《数据库应该放入K8S里吗》以及 《把数据库放入Docker是一个好主意吗》 就已经展开过了 ——越复杂的架构杂耍翻车的时候摔的就越痛如果你非要用容器不可的话老冯的建议也是找一个好点儿的三方 Docker Postgres 镜像起码比 “官方” 的这个土法镜像要好得多。为什么排序规则很重要那么为什么会出现这个问题呢老冯在《PG中的本地化排序规则》就深入聊过这个问题。 简单的结论就是你应该始终使用C.UTF-8作为全局排序规则同时在 PostgreSQL 17 之后的版本 则应该强制使用 PG 内置的 locale provider而不是使用操作系统 glibc 的排序规则。 真的要用到特定 Locale 规则的时候什么汉语拼音排序之类的直接在 DDL / SQL 里面显式声明就可以不影响使用的 —— 而且尽量用 ICU 排序规则不要使用操作系统的这里的原因是至少在 PG 17 之前PostgreSQL强依赖操作系统的本地化库来执行字符串比较排序 这是 glibc 提供的一个核心功能而 glibc 中排序规则是会变化的而 glibc 的版本都会在每次 Linux 发行版大版本升级的时候更新。 这就意味着对于生产环境来说你通常不能把 A 系统上的 PG 物理文件直接拷贝到 B 系统上去运行 —— 除非你使用了 PG17 后的内置排序规则而这并非默认设置。在initdb的时候使用--locale-providerbuiltin以及--builtin-localC.UTF-8这两个参数在 2024 年的 PGConf.Dev 上Jeremy Schneider 的 Collations from A-Z 主题演讲就深入解释过这个问题。 PostgreSQL 开发组也意识到这确实是一个问题所以在去年 PG 17 发布的时候引入了一个新的特性内置排序规则。也就是不再用操作系统 glibc 提供的排序规则了不过只支持 C 和 C.UTF-8 这两种规则。 如果你想更深入的进一步了解这个主题老冯非常建议你阅读这份材料。或者收看 PGConf.Dev 2024 现场视频。打开图片预览排序规则的23个常见误区以下全错让字符排好序是一件简单的事情。人和电脑用的排序规则是不变的。改变排序规则是一件很罕见的事。改变排序规则总是有意进行的。排序规则只会搞烂索引搞烂的东西可以重建我的数据库没有用到奇怪语言中的字符所以跟排序规则无关我的数据库能理解所有放在里面的字符PG 的 “错误排序库版本” 警告总是能被某人看到PG 总是能知道宿主系统使用的C标准库版本你可以把老的 glibc 代码里面的排序规则部分单拉出来单独构建然后装到新系统上来解决问题ICU 可以解决一切排序规则问题ICU 没有 glibc 2.28 fiasco 那样重大的排序规则变化假设 Devrim 和 Christoph 乐意替你构建老版本的 ICUglibc 小版本/补丁版本不会修改排序规则库版本号不变排序规则就不变PG 还没有提供内置的Collation Provider来解决上面所有的数据损坏危机PG 的 C 和 C.UTF-8 排序规则是一回事儿C.UTF-8 排序规则是不变的。Collation Provider 只解决排序规则的问题。C.UTF-8 里面的 CTYPE 是不变的用户想要DB级别的语言排序PG不太可能有一个内置的排序规则来解决上面这些问题令人欣慰的是PostgreSQL 去年的 17 版本中引入了内置排序规则解决了上面这些问题。 老冯的 PG 发行版 Pigsty 也相应地在 v3.4.0 正式引入应用了这个特性。—— 所有 PG 17 以上的集群都统一使用 built-in locale-provider固定使用 C.UTF-8 排序规则。 对于 17 以下的版本则使用操作系统的 C.UTF-8 排序规则如果操作系统实在是搓到不支持 C.UTF-8 真的有那就保底用 C 排序规则。这样做的好处是只要用这个内置排序规则操作系统再怎么瞎搞也不影响 PostgreSQL 的排序了。你即使升级了底层操作系统也不用折腾什么索引重建担心数据损坏了。官方不等于“靠谱”虽然这个特性很好不过显然对于 PostgreSQL 专家属于 “常识” 的最佳实践知识并没有普及到 Docker “官方”。 至少在 Docker 的官方 postgres 镜像上这个重要配置就缺位了本来可以绕过这个问题的还是踩雷翻车了。 正如 Gwen 所说有个 “官方” 俩字并不代表 “负责任的生产表现”。DockerHub 上的 postgres 镜像被广泛使用据说是下载量最多的镜像然而它的质量在 PostgreSQL 专家看来确实是相当令人堪忧的。 这个 “官方” 指的是 Docker 的 “官方”而不是 PostgreSQL 社区。所以里面充斥的大量的反模式使用起来非常难受。打开图片预览说到底这个所谓官方镜像就是一个极其简单的封装用 apt 给你从 PGDG APT 仓库里安装一下然后跑一个 init 脚本。 这个镜像对于 POC开发测试学习来说是基本够用了但离生产环境的距离可谓差着十万八千里。生产数据库不宜使用容器但老冯要说的是这不仅仅是 Docker 官方 PostgreSQL 镜像的问题而是容器本身就不适合运行生产数据库。 说到底Docker 和 K8S 从骨子里就不是为有状态服务而设计的硬凑上去很难有好结果。如果你用的是 Docker Postgres 容器即使没有在这次的小版本升级上翻车也很有可能会在其他问题上翻车。 比如默认的 64 MB Shmem 共享内存段直接写 Overlay FS安装的扩展在从节点上消失在一个卷上跑两个PG实例把数据烤糊奇葩的从库搭建流程。 诸如此类在物理机/虚拟机上根本不存在的容器特有问题老冯在《把数据库放入Docker是一个好主意吗》讨论过很多 但显然社区还会不断出现新惊喜吓容器上运行数据库的状态仍然没有达到裸 Linux 上运行的长期博弈均衡态。像 Locale 配置这样的工程细节有许许多多绝对不是 docker pull 一个所谓 “官方镜像” 能解决的。 例如 Pigsty 为了解决用好 PostgreSQL 的问题光本身的纯代码就有近十万行 这也显然不是 “官方镜像” 一个几百行 Shell/Dockerfile 脚本能覆盖的了的。实际上有一些第三方的 PostgreSQL over Kubernetes 供应商他们提供的 PG 容器会比这个 “官方版” 要好得多。 但老实说他们也依然会受到容器本身的掣肘 —— 一堆 K8S / Docker 大师吭哧吭哧优化半天也很难赶上直接在 Linux 上裸奔的 PG。 对数据库老司机来说确实有一种隔靴搔痒的感觉。Docker 确实很方便老冯也拿他跑无状态的服务批量运行编译任务有时候当廉价虚拟机测试或者是简单测试一下数据库功能。但唯独在生产环境使用的时候老冯会对用容器跑数据库坚定说 “不”—— 唯一的例外可能是 Redis。应该如何安装 PostgreSQL那么如果不用容器又应该如何安装部署 PostgreSQL 呢PostgreSQL 这样的数据库是与操作系统紧密联系的特殊软件。 最好的状态就是直接不带套运行在裸 Linux 上简单直接稳定可靠没有额外的性能折损与管理负担。有很多人觉得这是一件很复杂的事情好像又要折腾什么 YUM/APT 仓库官方镜像太慢又要翻墙 国内镜像站也全面断更《从PG“断供”看软件供应链中的信任问题》然后安装好了之后怎么配置调参优化也一筹莫展。 实际上这都已经是老黄历了。老冯的 开源 PG 发行版 Pigsty 就是为了直接在 Linux 上运行企业级 PostgreSQL 服务而设计的。目前我在 Debian 12/13Ubuntu 22/24EL 8/9/10 ARM / x86 也就是 14 个主流 Linux 发行版上提供了原生的 PostgreSQL 内核PG 13-18 六个大版本8 款不同风味的 PG 内核分支近百个生态工具与 430 个生态扩展。 并将其打造成一键部署安装拉起自带监控高可用PITR 的生产级方案。还提供了 PGDG 官方仓库的中国镜像应该是目前 国内唯一和 PGDG 保持同步的镜像站。老实说这可真是个苦力活儿。光打出来的 RPM/DEB 包就有大几万个。各种测试组合上游变动都需要照顾到。 老冯也想过 —— 做个 Docker 镜像呗偷懒又省事丢给用户你爱在什么操作系统上跑就在什么系统上跑。 但作为一个也要给自己用的大规模生产方案我还是决定要去做 “正确而艰难” 的事情 —— 提供在 14 个主流 Linux 发行版上直接运行整个 PostgreSQL 生态的能力。毕竟“官方镜像” 也是用 APT 从仓库里安装的……而且最棒的是Pigsty 的扩展仓库和镜像仓库是独立的如果你不喜欢用一个大而全的发行版 你也可以直接免费使用我们的 APT/YUM 仓库安装原生 PGDG 内核与上面所有这些工具与扩展。当然 广告就到这里。这一篇老冯聊了为什么不要在生产环境中用容器跑 PostgreSQL。 下一篇老冯会详细的介绍一下 PostgreSQL 安装实操 ——如果不用容器我应该怎么装 PG打开图片预览FAQ有不少读者提出了一些问题与质疑老冯在这里统一回复一下问我用xx容器镜像已经运行了 xx 年什么问题都没有呀意外 —— 它是有趣的东西在你遇到它们之前你永远不会遇到它们。可靠性看的是 RTO/RPO 指标而不是几个九的可用性时间。 因为用户可以单纯凭运气几年不出问题这种例子非常多。 而真正重要的始终是出问题后的处理能力因此只有反例有参考意义。问难道不是因为用户太菜了用 latest 镜像吗这跟 Docker 有什么关系世界是个巨大的草台班子docker pull postgres或者以为 postgres:17.6 就定死了 PG 版本号的的人 会远比想象中要多得多 —— 考虑到他们已经干出用容器跑生产数据库的事了。问所谓“物理机/虚拟机上不会有”这个问题是因为你的操作系统不升级用物理机/虚拟机部署不代表不升级 OS而是不会以为 docker pull 一下就 “升级” 了。 正常升级操作系统显式进行的计划迁移本例是在升级一个PG小版本镜像时导致了意外升级。问既然 PG 17 已经引入了内置排序规则为什么17以上的镜像小版本还会有这个问题很显然 Docker “官方” 并不知道这些最佳实践知识依然使用了系统默认的 locale provider。问生产环境就应该锁死在固定版本永远不升级。对菜鸟来说这种苟且策略有一定的合理性。 但对严肃生产数据库而言及时升级小版本是基本的安全素养。 每年零停机逻辑复制蓝绿部署迁移至新的系统/PG是卓越团队的标配。问原帖说的是不要用 Docker 官方镜像不是说在生产环境不要用容器跑数据库老冯有自己的观点不是原帖的翻译机。我的观点就是严肃生产数据库不要用容器跑。 Docker 官方 PG 镜像拉垮是一个原因但还有更多原因。细节请看参考阅读文章。问什么时候用容器跑数据库是有意义的在以下几种情况中老冯认为使用容器跑生产数据库勉强是合理的 —— 云厂商偷工减料批量超卖跑在人嫌狗厌的信创魔改国产操作系统上 或者是公司给的待遇只值得你糊弄一下。参考阅读把数据库放入 Kubernetes 是一个好主意吗把数据库放入Docker是一个好主意吗《Kubernetes创始人发声K8s在被反噬》《Docker 的诅咒曾以为它是终极解法最后却是“罪大恶极”》《从滴滴的故障我们能学到什么》《PostgreSQLK8s 性能优化记》《Running Database on Kubernetes》
返回列表