跳转至

INCIDENT-20260917-01 — GitLab 部署期排障记录

  • 主题目录:gitlab-deploy-v2/
  • 记录对象:2026-09-17 首装 GitLab 期间发生的 4 个部署阻塞问题 + 1 起凭据处理失误
  • 等级:P3(部署期故障)——新建部署,无既有业务受影响,无数据损坏
  • 当前状态:已恢复。GitLab 全栈运行正常(现位于 server 命名空间,见 关联变更)
  • 关联文档:SOP.md、RUNBOOK.md

说明:本文件是台账与交接用,只记录"发生了什么、根因是什么、怎么修的、还欠什么"。日常排障手册见 RUNBOOK 第 8 章;单条问题的操作细节已回写进 SOP 第 5 章。


1. 概要

一次"按方案执行"的部署过程中,连续触发 4 个阻塞点,每个都会让部署停在中间态。四个问题的共同特点是:chart 的默认值与本项目环境的假设不一致,且报错信息都不直接指向真正的原因(例如"secret not found"真实原因是命名空间归属,out of shared memory 真实原因是 PG 参数而非磁盘或内存)。

序号 问题 从发现到恢复耗时
1 全部 Pod FailedMount:secret "gitlab-pg-app" not found 约 5 分钟(补建 Secret 后自愈)
2 gitlab-toolbox CrashLoop:cp: cannot stat '/etc/gitlab/.s3cfg' 约 15 分钟(含 values 变更与 upgrade)
3 gitlab-migrations 反复失败:ERROR: out of shared memory 约 15 分钟(改 PG 参数 + PG 滚动重启)
4 验证阶段误选控制平面 Service,port-forward 连接失败 约 3 分钟(改选数据面 Service)
— 安全侧:MinIO root 口令的 base64 值因命令输出不当一度写入终端记录 立即轮换(约 3 分钟)

2. 影响范围

  • 业务:无既有业务。本次为集群内首次部署 GitLab,故障期间只是"部署未完成",没有用户与数据受影响。
  • 数据:无丢失、无损坏。PG 在 schema 加载阶段失败并重试,最终一次成功;MinIO 桶与 Gitaly 目录未被破坏。
  • 集群其他组件:未受影响(Traefik 与既有工作负载全程正常)。
  • 凭据:MinIO root 口令的 base64 形式出现在终端输出中(本地终端与本次会话记录),已通过轮换消除影响;轮换后同步更新了 3 处引用(Rails 对象存储、registry 存储、备份 s3cfg)。

3. 时间线

时间(2026-09-17,宿主机本地时间) 事件
03:19 开始执行 SOP 阶段 0:宿主机创建 60G qcow2 并热挂给 k8s-node-01;节点内格式化、挂载 /mnt/ssd、建 PV 目录
03:22–03:24 阶段 1/2/3:安装 Gateway API v1.6.1 CRDs;创建 local-ssd SC 与 3 个 local PV;创建命名空间与随机凭据 Secret(PG / Redis / MinIO)→ 此处埋下问题 1:三个凭据 Secret 分别建在 gitlab-pg / gitlab-redis / gitlab-minio
03:24–03:28 阶段 4/5/6:部署 MinIO(含 6 个 bucket 与 3 个存储 Secret)、CNPG operator、Cluster gitlab-pg(healthy,PG 17.11)、Redis
03:29:16 阶段 7:helm upgrade --install gitlab 返回 STATUS: deployed(revision 1)
03:29–03:30 观察 Pod 全部卡在 Init/ContainerCreating,事件反复报 MountVolume.SetUp failed ... secret "gitlab-pg-app" not found、secret "gitlab-redis" not found
约 03:31 问题 1 处置:在 release 所在命名空间补建同名 Secret gitlab-pg-app / gitlab-redis(值从原 Secret 读取)
约 03:33 复查:Pod 开始推进(Envoy 数据面 2/2 Running,其余进入 Init)
约 03:41 观察到 gitlab-toolbox 进入 Error 并 Back-off;日志结尾 cp: cannot stat '/etc/gitlab/.s3cfg' → 问题 2
约 03:42 问题 2 处置:取 MinIO 现有凭据,建 gitlab-backups bucket,生成 gitlab-backup-s3cfg(s3cmd 格式),并在 values 中补 gitlab.toolbox.backups.objectStorage.config.secret
03:43:39 helm upgrade 应用(revision 2);toolbox 随后 1/1 Running
约 03:44 观察到 gitlab-migrations-* 容器 Back-off,exitCode=1;查 --previous 日志见 psql:...: ERROR: out of shared memory / HINT: increase "max_locks_per_transaction" → 问题 3;同时确认数据库中表数为 0(schema 未加载成功)
约 03:46 问题 3 处置:kubectl patch cluster gitlab-pg 增加 postgresql.parameters.max_locks_per_transaction: "256";CNPG 滚动重启 PG(期间 webservice/sidekiq 的 dependencies init 容器继续等待重试)
约 03:48–03:52 migrations 重新执行:数据库中表数从 0 增至 1105,日志进入"注册 OAuth 应用"等种子阶段
03:55:59 执行幂等复跑:helm upgrade --no-hooks(revision 3)→ 所有 Pod AGE 未变、无重建,确认重复执行零变更
约 04:00 gitlab-migrations-* Completed 1/1;webservice 2/2 Running,其余组件就绪
约 04:02 验证 Gateway PROGRAMMED=True;问题 4:首次 port-forward 误选控制平面 Service envoy-gateway(18000/19001 管理端口),连接失败;改选数据面 envoy-gitlab-gitlab-gw-80260869(80/22)后正常
约 04:05 完成验证:/users/sign_in 200、/ 302 → 登录页、未知域名 404、registry /v2/ 401;部署收尾

关于凭据轮换的时点:问题 1 处置过程中,一条用于"校验 Secret 键名"的命令把 Secret 的 base64 值打印到了终端(见第 5 章)。发现后立即轮换 MinIO root 口令并重建 3 处派生 Secret,时间在 03:33 前后完成。

4. 根因

4.1 问题 1:凭据 Secret 与 release 不在同一命名空间

  • 根因:GitLab chart 只在 release 所在命名空间查找 global.psql.password.secret / global.redis.auth.secret 引用的 Secret。方案按 chart 文档的"依赖各自独立命名空间"设计,把 gitlab-pg-app 建在 gitlab-pg、gitlab-redis 建在 gitlab-redis,而 release 在 gitlab → init 容器挂载失败。
  • 证据:
Warning FailedMount  MountVolume.SetUp failed for volume "init-migrations-secrets" :
  [secret "gitlab-redis" not found, secret "gitlab-pg-app" not found]
  • 判定依据:把同名 Secret 补建到 gitlab 命名空间后,所有 FailedMount 立即停止、Pod 继续初始化——直接证明是命名空间归属问题,而非 Secret 内容或 RBAC。

4.2 问题 2:toolbox 的 S3 备份配置缺失导致启动即退出

  • 根因:chart 的 backups.objectStorage.backend 默认为 s3,toolbox 容器启动命令因此进入 s3 分支:
cp -v -r -L /etc/gitlab/.s3cfg $HOME/.s3cfg && while sleep 3600; do :; done

而 /etc/gitlab/.s3cfg 只在你提供了 backups.objectStorage.config.secret 时才由 Secret 挂载进去。未提供 → cp 失败 → 容器 exit 1 → CrashLoopBackOff。 - 证据:

Attempting to run '/bin/bash -c cp -v -r -L /etc/gitlab/.s3cfg $HOME/.s3cfg && while sleep 3600; do :; done' as a main process
cp: cannot stat '/etc/gitlab/.s3cfg': No such file or directory
(容器状态:terminated, exitCode=1, reason=Error;Back-off restarting failed container toolbox)
  • 判定依据:补齐 gitlab-backup-s3cfg(key config,s3cmd 格式)并重启后,toolbox 立刻 1/1 Running。

4.3 问题 3:PostgreSQL 默认 max_locks_per_transaction 不足以加载 GitLab schema

  • 根因:GitLab 的 db/structure.sql 在一个事务内创建上千个表/索引/约束等对象,PG 每个事务能持有的锁对象数上限由 max_locks_per_transaction(默认 64)决定。超出即报 out of shared memory,schema load 中途回滚 → 表数为 0 → migrations 重试 → 再失败。
  • 证据:
psql:/srv/gitlab/db/structure.sql:62068: ERROR:  out of shared memory
HINT:  You might need to increase "max_locks_per_transaction".
rake aborted!
failed to execute: psql --set ON_ERROR_STOP=1 --quiet --no-psqlrc --output /dev/null --file /srv/gitlab/db/structure.sql --single-transaction gitlabhq_production

(同时刻查库:select count(*) from information_schema.tables = 0,证明没有任何对象落库)

  • 判定依据:patch 为 256 后,show max_locks_per_transaction 返回 256,migrations 一次通过(表数 0 → 1105 → 完成),未再做其他改动。

4.4 问题 4:验证入口选错 Service

  • 根因:Envoy Gateway 生成两类 Service——控制平面 envoy-gateway(18000/18001/18002/19001/9443,管理端口)与数据面 envoy-<release>-<gateway>-<hash>(80/22,业务流量)。用 grep envoy | head -1 取值时恰好命中控制平面。
  • 证据:kubectl -n gitlab port-forward svc/envoy-gateway 8080:80 建立的连接立即被关闭(HTTP 000);改选数据面后 /users/sign_in 返回 200。
  • 判定依据:换 Service 即可,无需任何配置变更——排除配置问题。

4.5 附:凭据处理失误

  • 根因:为"确认 Secret 键名正确"执行了 kubectl get secret ... -o jsonpath='{.data}',该写法会把 base64 明文值整体输出。虽然 base64 不是加密,等价于口令泄露到终端与会话记录。
  • 判定依据:该命令输出中可直接解码出 MinIO 的 AK/SK。

5. 处置过程

问题 执行的动作 是否含回滚动作
1 从原 Secret 读取值,在 release 命名空间(当时的 gitlab)补建 gitlab-pg-app / gitlab-redis;Pod 自愈,无需重启 无
2 建 gitlab-backups bucket;生成 gitlab-backup-s3cfg;values 增加 gitlab.toolbox.backups.objectStorage.config.secret;helm upgrade(revision 2) 无(新增配置,可逆)
3 kubectl -n gitlab-pg patch cluster gitlab-pg 增加 max_locks_per_transaction: "256";等待 CNPG 滚动重启完成 无(参数增大,向后兼容;若需回退可 patch 删除该键并重启)
4 改用数据面 Service 做 port-forward 验证 无
凭据 生成新口令写回 minio-root-credentials;rollout restart sts/minio;按 SOP 阶段 4.2 重建 gitlab-object-storage / registry-storage / gitlab-backup-s3cfg 三个派生 Secret;用 mc ls 验证新凭据可用 无(旧口令立即失效即为目标)

6. 恢复验证

验证项 命令 结果
数据库迁移 kubectl -n gitlab get job \| grep migrations Complete 1/1
表对象落库 psql -tAc "select count(*) from information_schema.tables where table_schema=current_schema()" 1105(并持续完成种子数据)
工作负载 kubectl -n gitlab get pod 全部 Running/Completed,无 Back-off
工具容器 kubectl -n gitlab get pod -l app=toolbox 1/1 Running 且不再重启
页面 curl -H 'Host: gitlab.rainchen.top' .../users/sign_in 200
路由分流 未知域名请求 404(证明按域名分流生效)
镜像仓库 curl -H 'Host: registry.rainchen.top' .../v2/ 401 + WWW-Authenticate 正确
对象存储凭据 mc ls m(新凭据) 正常列出 6 个 bucket
幂等 相同参数二次 helm upgrade --no-hooks 全部 Pod AGE 未变、无重建

7. 复盘

有效的做法

  • 每个问题都用最小可判定实验定位:补 Secret 后是否自愈、换 Service 后是否正常、改 PG 参数后是否通过——避免在报错字面上纠缠(例如 out of shared memory 很容易被误判为内存不足或磁盘满)。
  • 关键步骤都保留了证据(describe 事件、--previous 日志、表数查询),使根因可复现、可交接。
  • 处置动作全部选择可逆或向后兼容的方式(补建 Secret、新增参数、换验证入口),没有出现"越修越乱"。
  • 幂等性单独复跑验证,确认重复执行零变更。

拖慢恢复的做法

  • 首装时按"依赖各自命名空间"的直觉建 Secret,没有先验证 chart 对 Secret 的查找范围——一个 helm template 阶段的检查(渲染产物中 init 容器挂载的 Secret 名)本可以提前发现。
  • 只做了"渲染复核",没有在渲染产物里检查 toolbox 的启动命令与 EnvoyProxy 的 Service 类型是否适配当前环境;这两个都在同一份渲染结果里可以直接看出。
  • 一条只读校验命令把 Secret 值打到了终端,属于操作规范问题(应始终用 -o jsonpath 取单个键或用 --dry-run 校验,不输出 .data 整体)。
  • 验证阶段用 grep envoy | head -1 取值,过于乐观。

8. 行动项

事项 责任人 期限 状态
把四个根因与对应修正回写进 SOP 第 5 章(含"踩坑"注记),保证下次重建不再踩 rain 2026-09-19 已完成
建立 GitLab 备份机制:新增每周全量备份 CronJob(toolbox backup-utility),保留 3 份(--maximum-backups 3) rain 2026-09-26 待办(当前完全无备份,见 RUNBOOK 第 6 章)
做一次恢复演练(从 gitlab-backups 恢复到测试命名空间),验证备份可用 rain 2026-10-03 待办
补齐 Secret 备份(kubectl get secret -o yaml 落到仅本人可读的目录),与对象数据备份配套 rain 2026-09-26 待办(backup-utility 不含 Secret)
将 GitLab 接入既有 Prometheus/Grafana,并配置首批告警(5xx、Pod 不可用、PV 水位、PG 健康、Redis 内存) rain 2026-10-03 待办(详见 RUNBOOK 第 7 章)
把 MinIO 数据卷从循环设备(118G)迁到 /data(1.7T)导出目录,降低容量与 IO 风险 rain 按需(当前用量 <1%,非紧急) 待办
约定凭据相关命令的输出纪律:只取单个键、避免 .data 整体输出;取用后不要粘进聊天/工单 rain 立即生效 已完成(本次事件后已按此执行)

附:关联变更

  • 2026-09-18 00:48–01:13:GitLab 全栈命名空间统一到 server(重建路径,非本文件范围)。方案与风险见 GitLab迁移到server-方案与风险.md,执行证据见 ns_migration/apply-*.txt 与 ns_migration/backup/。
  • 迁移后复核:本文件第 6 章的各项验证在 server 命名空间下重新执行并通过(详见 SOP 6.1)。

来源:/home/rain/Documents/k8s_ins/gitlab-deploy-v2/INCIDENT-20260917-01-gitlab-部署与迁移期排障.md | 最后更新:2026-09-19 16:51(由 build/sync.py 同步,请勿在站点侧修改)