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(keyconfig,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同步,请勿在站点侧修改)