跳转至

GitLab webservice OOM 与启动期误杀修复 —— 变更 SOP

  • 交付物目录:gitlab-502-webservice-oom/
  • 主题:修复 GitLab Web 502(webservice 容器 cgroup OOM + liveness 启动期误杀)
  • 变更对象:server 命名空间 gitlab release(GitLab chart 10.3.2 / GitLab 19.3.2)
  • 变更时间:2026-09-20 22:20(helm revision 2);22:25 幂等复跑(revision 3)
  • 配套文档:INCIDENT-20260920-webservice-oom-502.md(故障报告)、backup/README.md(备份台账)
  • 实现体说明:本次为极简变更(单集群、动作 ≤3 条、无编排),按用户明确要求以 helm + values 文件承载,不使用 Python 工程;配置以实现体 gitlab-deploy-v2/values-gitlab.yaml 为准

目录


1. 背景

2026-09-20 22:10:55 – 22:13:38,访问 https://gitlab.rainchen.top 持续返回 GitLab 定制的 502 页面(HTTP 502: Waiting for GitLab to boot),Web UI 与 API 完全不可用约 3 分钟。

直接原因:webservice Pod 内 webservice 容器被内核 cgroup OOM 杀掉(CONSTRAINT_MEMCG,exitCode 137 / OOMKilled),Puma 重启并 preload Rails 期间(约 3 分钟)未监听 8080,同 Pod 内的 gitlab-workhorse 对全部入口请求返回 502。

两条隐患:

  1. limits.memory: 3Gi 低于实际常驻水位(重启后 6 分钟即回到 2892Mi/3072Mi),且 chart 默认 puma.disableWorkerKiller: true,worker 内存只增不减,必然周期性 OOM;
  2. chart 默认 liveness 探针(initialDelay 20s / period 60s / failureThreshold 3)短于 Puma preload 时长,容器在启动期即被判死杀掉(另一副本 v74jx 22:17:22 即因此重启),使服务难以稳定。

详细根因与证据见 INCIDENT-20260920-webservice-oom-502.md。

2. 目标

变更完成后应达到的可验证状态:

  1. gitlab-webservice-default 副本 2/2 Running 且 RESTARTS 不再增长;
  2. workhorse 日志近 15 分钟 "status":5xx 计数为 0;
  3. https://gitlab.rainchen.top/ 返回 302、/users/sign_in 返回 200;
  4. webservice 容器内存水位 < limit 的 70%(实测 2541Mi / 6Gi = 41%);
  5. 新副本带 startupProbe,preload 期间不再被 liveness 误杀;
  6. 相同参数再次 helm upgrade 零变更(Pod 不重建)。

3. 前置条件

项 要求 实测
k8s 权限 集群管理员 kubeconfig ~/.kube/config 可用(server 192.168.31.8:16443)
helm v3.16.3 可用
节点资源余量 webservice 新 limit 6Gi 可被调度 node-02 / node-03 各约 11G 空闲(16G 总量)
备份 变更前 values 等快照已落盘 gitlab-502-webservice-oom/backup/(见 backup/README.md)
chart 源码 离线 chart 在本地 gitlab-deploy-v2/gitlab/(10.3.2,无需联网拉取)

执行前检查:

kubectl -n server get pod -l app=webservice          # 记录变更前副本与重启次数
helm -n server history gitlab                        # 记录变更前 revision
helm -n server get values gitlab > /tmp/values-before.yaml   # 集群实际生效值快照

4. 影响范围

主机/组件 变更内容 是否停服
k8s 集群 server 命名空间 gitlab release 的 webservice Deployment 滚动更新(pod template 变化触发重建) 否,但 Web UI 有秒级抖动
webservice Pod 内存 requests 2Gi→3Gi、limits 3Gi→6Gi;新增 startupProbe 否(滚动更新,maxUnavailable: 1)
其余组件 无变更(Gitaly / Sidekiq / Registry / PG / Redis / MinIO 不受影响) 否
  • 窗口与影响面:滚动更新期间旧副本持续服务,Web UI 理论无中断;但变更前处于 OOM 循环、HPA 已缩至 1 副本,仍可能出现短时 502。本次实测滚动期间无新 502。
  • 不影响:Git over SSH、容器镜像仓库、CI(未部署 runner)。
  • 无需停服窗口,建议预留 10 分钟观察;Puma 新副本就绪实测约 4 分钟。
  • 变更前须确认集群上无正在跑的 GitLab 相关维护任务。

5. 分步操作

以下为实现等价的操作序列,以 gitlab-deploy-v2/values-gitlab.yaml 为实现体,以实现体为准。本变更无删除/格式化动作,唯一需留意的是第 4 步会触发 webservice 滚动重启。

# 0) 【高危/留档】变更前备份 values 与集群状态(已执行,见 backup/README.md)
mkdir -p /home/rain/Documents/k8s_ins/gitlab-502-webservice-oom/backup/gitlab-deploy-v2
cp -p /home/rain/Documents/k8s_ins/gitlab-deploy-v2/values-gitlab.yaml \
      /home/rain/Documents/k8s_ins/gitlab-502-webservice-oom/backup/gitlab-deploy-v2/values-gitlab.yaml.bak.$(date +%Y%m%d-%H%M%S)

# 1) 修改实现体:gitlab-deploy-v2/values-gitlab.yaml 的 gitlab.webservice 段
#    - resources.requests.memory: 2Gi -> 3Gi
#    - resources.limits.memory:   3Gi -> 6Gi
#    - 新增 deployment.startupProbe(httpGet /-/liveness:8080, initialDelay 10s, period 10s,
#      timeout 5s, failureThreshold 30)——启动完成前不执行 liveness,消除 preload 期误杀
cd /home/rain/Documents/k8s_ins/gitlab-deploy-v2

# 2) 本地渲染复核(不改集群):确认 startupProbe 与 6Gi 已渲染、未引入多余组件
helm template gitlab ./gitlab -n server -f values-gitlab.yaml > /tmp/render-check.yaml
grep -n -A10 'startupProbe:' /tmp/render-check.yaml | head -n 20   # 预期含 path: /-/liveness, port: 8080
grep -n 'memory: 6Gi' /tmp/render-check.yaml                        # 预期命中 webservice 容器 limits

# 3) 记录变更前 revision(本次为 1,仅 09-18 安装过一次)
helm -n server history gitlab

# 4) 【高危:触发 webservice 滚动重启】应用变更
helm -n server upgrade gitlab ./gitlab -f values-gitlab.yaml --timeout 900s

# 5) 观察滚动过程:新 RS 先起,旧 Pod 保持服务直到新副本 Ready
kubectl -n server get pod -l app=webservice -w
# 预期:新 Pod 以 Init:0/3 -> 0/2 -> 2/2 就绪(实测约 4 分钟),随后旧 Pod 退出

# 6) 幂等复跑:相同参数再执行一次,预期 Pod 名与 IP 不变(零变更)
helm -n server upgrade gitlab ./gitlab -f values-gitlab.yaml --timeout 900s
kubectl -n server get pod -l app=webservice

失败判断依据:第 4 步返回非 0;第 5 步新 Pod 超过 10 分钟未 Ready,或反复重启 —— 任一出现即按第 7 章回滚。

6. 验证

# 1) 副本就绪且零重启
kubectl -n server get pod -l app=webservice
# 预期:2/2 Running,RESTARTS 0

# 2) 配置确实生效(资源 + startupProbe)
kubectl -n server get deploy gitlab-webservice-default -o jsonpath='{range .spec.template.spec.containers[*]}{.name}{" req="}{.resources.requests.memory}{" lim="}{.resources.limits.memory}{" startupProbe="}{.startupProbe.httpGet.path}{"\n"}{end}'
# 预期:webservice req=3Gi lim=6Gi startupProbe=/-/liveness

# 3) 无新 5xx(POD 换成实际名)
POD=$(kubectl -n server get pod -l app=webservice -o name | head -1 | cut -d/ -f2)
kubectl -n server logs "$POD" -c gitlab-workhorse --since=15m | grep -c '"status":5'   # 预期 0

# 4) 端到端页面
curl -sk -o /dev/null -w '%{http_code}\n' https://gitlab.rainchen.top/            # 预期 302
curl -sk -o /dev/null -w '%{http_code}\n' https://gitlab.rainchen.top/users/sign_in  # 预期 200

# 5) 内存水位(应显著低于 limit)
kubectl -n server top pod -l app=webservice --containers | grep webservice
# 预期:webservice 容器 RSS < 4.2Gi(limit 的 70%)

# 6) 幂等:第 5 章第 6 步复跑后 Pod AGE 连续递增、名与 IP 未变即为零变更

6.1 本次实测结果(2026-09-20 22:24 – 22:26)

检查项 实测
副本 gitlab-webservice-default-59998b794d-8xttj 2/2 Running,RESTARTS 0(变更前为反复重启)
配置 webservice requests 3Gi / limits 6Gi,startupProbe /-/liveness:8080 已渲染生效
5xx 新 Pod workhorse 近 15 分钟 0 条(变更前 30 分钟内 58 条 502)
页面 / 302、/users/sign_in 200
内存 webservice 2541Mi / 6Gi(41%)(变更前 2892Mi / 3Gi,94%)
幂等 复跑 helm upgrade → revision 3,Pod 名与 IP 未变、AGE 由 4m39s 增至 7m19s,零变更

7. 回滚

备份与留档

  • 备份目录:gitlab-502-webservice-oom/backup/,逐条台账见 backup/README.md。
  • 关键备份:backup/gitlab-deploy-v2/values-gitlab.yaml.bak.20260920-221939(含变更前资源与探针配置)。

回滚触发判据(任一满足即回滚,不临场判断)

  • helm upgrade 返回非 0 或超时;
  • 新副本 10 分钟内未 2/2 Ready,或 RESTARTS 持续增长;
  • 验证阶段 /users/sign_in 非 200 或 15 分钟窗口内仍有 5xx;
  • 出现因资源不足导致的 Pending Pod(节点内存不够)。

回滚步骤

cd /home/rain/Documents/k8s_ins/gitlab-deploy-v2

# 方式 A(推荐):用变更前备份精确回滚 values,再重新部署
cp ../gitlab-502-webservice-oom/backup/gitlab-deploy-v2/values-gitlab.yaml.bak.20260920-221939 ./values-gitlab.yaml
helm -n server upgrade gitlab ./gitlab -f values-gitlab.yaml --timeout 900s

# 方式 B:回退到安装时版本(会退回 09-18 的初始配置,含本次变更之外的差异)
helm -n server rollback gitlab 1

回滚后验证:kubectl -n server get deploy gitlab-webservice-default -o jsonpath='{...resources}' 回到 2Gi/3Gi 且无 startupProbe;kubectl -n server get pod -l app=webservice 重启完成、/users/sign_in 返回 200。

回滚代价:内存退回 3Gi 后 OOM 与启动期误杀问题重现,Web UI 仍会周期性 502。

8. 风险说明

风险 等级 说明 缓解措施
滚动更新触发 webservice 重建 中 变更前仅 1 个副本且处 OOM 边缘,更新窗口内可能出现短时 502 旧副本保持服务直到新副本 Ready(maxUnavailable: 1);实测本次滚动期间 0 条 5xx;建议在低峰执行
内存 limit 提到 6Gi × 2 副本 中 峰值内存占用上升,可能挤占同节点其它 Pod 节点各约 11G 空闲,MemoryPressure False;HPA maxReplicas 仍为 2,不放大副本数
仅调大 limit 未启用 Puma 自愈 中 Rails 内存缓慢增长,长期仍可能逼近 6Gi(周期被拉长,非根除) 观察 24–48h 内存曲线;若持续增长则启用 puma.disableWorkerKiller: false + workerMaxMemory
startupProbe 容忍 310s 启动 低 若 Puma 真故障,容器最迟 310s 后才进入 liveness 循环判死,恢复延迟增加 期间 readiness 仍会失败,副本不会接流量;异常可通过 kubectl logs -c webservice 定位
HPA 依赖 metrics-server 中 本次观察到 FailedGetResourceMetric,HPA 短时无法评估 CPU 已列入行动项:为 metrics-server 增加可用性监控
sidekiq 同源配置风险 中 sidekiq 亦为 3Gi limit,实测已用 1742Mi(58%) 已列入行动项:按同一原则评估后单独变更

附录:交付物清单

文件 说明
gitlab-502-webservice-oom/SOP.md 本文档(变更实施、验证、回滚)
gitlab-502-webservice-oom/INCIDENT-20260920-webservice-oom-502.md 故障报告(现象、根因、证据、时间线、预防建议)
gitlab-502-webservice-oom/backup/README.md 备份台账(备份文件、原路径、用途、可删时机)
gitlab-502-webservice-oom/backup/gitlab-deploy-v2/values-gitlab.yaml.bak.20260920-221939 变更前 values 备份(回滚依据)
gitlab-502-webservice-oom/backup/helm-values-effective-before-*.yaml 变更前集群生效 values 快照
gitlab-502-webservice-oom/backup/deploy-webservice-before-*.yaml 变更前 webservice Deployment 定义
gitlab-502-webservice-oom/backup/pods-webservice-before-*.txt 变更前副本状态记录
gitlab-deploy-v2/values-gitlab.yaml 实现体(已按本次变更修改)

来源:/home/rain/Documents/k8s_ins/gitlab-502-webservice-oom/SOP.md | 最后更新:2026-09-20 22:29(由 build/sync.py 同步,请勿在站点侧修改)