数据库迁移期间 PostgreSQL 共享内存不足
问题描述
由外部 PostgreSQL 支持的新创建 GitLab 实例始终无法就绪。迁移 Job 失败并进入 CrashLoopBackOff,而 sidekiq 和 webservice 一直停留在 Init,因为它们在等待该 Job:
迁移日志显示,架构加载在中途失败:
HINT 行指出了需要更改的参数。如果日志在此之前就被截断,则 ERROR: out of shared memory 行中的 structure.sql 也是同一个问题。
请注意,数据库连接本身正常——同一日志中更早的行确认 GitLab 已连接到 PostgreSQL 和 Redis:
根本原因
这里的 out of shared memory 不是 shared_buffers 或主机内存耗尽的问题,而是 PostgreSQL 的锁表已被占满。
GitLab 使用 psql --single-transaction 加载架构,因此整个 db/structure.sql——数千个表、索引、约束和序列——都会在一个事务中创建,并且它涉及的每个对象都会占用一个锁。PostgreSQL 在启动时根据 max_locks_per_transaction 确定共享锁表的大小,所需锁数量超过该值的事务会因 out of shared memory 而失败。
默认值为 64,远低于 GitLab 架构加载所需的值。任何仍使用默认值的 PostgreSQL 都会以这种方式失败,因此该现象会出现在全新实例上,而不是在负载下才出现。
故障排除
读取迁移 Job 日志,并确认失败原因为 out of shared memory,且发生在 structure.sql 行,而不是连接或身份验证错误:
然后在 PostgreSQL 端检查该设置:
如果值为 64(内置默认值),并且同时出现上述错误,即可确认原因。
解决方案
在 PostgreSQL 实例上增大 max_locks_per_transaction 并重启实例,然后删除失败的 GitLab 实例的迁移 Job,使其重新运行(或重新创建该实例)。
256 是一个安全的起始值。该参数不是动态参数:PostgreSQL 会在启动时分配锁表,因此需要重启。
具体设置方式取决于 PostgreSQL 的管理方式。
对于平台的数据服务 PostgreSQL,该参数通过实例的参数配置进行设置——请参阅 Creating a PostgreSQL Instance。若要从命令行在现有实例上更改该参数,请修补 CR 并让 operator 推出更改:
spec.postgresql.parameters 会替换整个映射,因此请包含所有已设置的参数——先读取当前值并手动合并,否则这些参数会在不知不觉中丢失。
等待推出完成,然后在数据库中确认新值——CR 再次报告 Running 仅表示 operator 已完成操作,并不表示参数已生效:
在多副本实例上,请将任何写入操作发送到主节点。副本为只读,并会因 cannot execute ... in a read-only transaction 拒绝 DDL,这很容易被误认为是权限问题。(SHOW 为只读操作,可在任意节点上运行。)通过其标签查找主节点:
GitLab 是 GitLab Inc. 的商标。
Alauda 是独立供应商。本产品与 GitLab Inc. 无关联,也未获得其认可或赞助。所有商标均归其各自所有者所有,此处仅用于标识目的。