GitLab webservice OOM 与启动期误杀修复 —— 变更 SOP¶
- 交付物目录:
gitlab-502-webservice-oom/ - 主题:修复 GitLab Web 502(webservice 容器 cgroup OOM + liveness 启动期误杀)
- 变更对象:
server命名空间gitlabrelease(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。
两条隐患:
limits.memory: 3Gi低于实际常驻水位(重启后 6 分钟即回到 2892Mi/3072Mi),且 chart 默认puma.disableWorkerKiller: true,worker 内存只增不减,必然周期性 OOM;- chart 默认 liveness 探针(initialDelay 20s / period 60s / failureThreshold 3)短于 Puma preload 时长,容器在启动期即被判死杀掉(另一副本
v74jx22:17:22 即因此重启),使服务难以稳定。
详细根因与证据见 INCIDENT-20260920-webservice-oom-502.md。
2. 目标¶
变更完成后应达到的可验证状态:
gitlab-webservice-default副本2/2 Running且RESTARTS不再增长;- workhorse 日志近 15 分钟
"status":5xx计数为 0; https://gitlab.rainchen.top/返回302、/users/sign_in返回200;- webservice 容器内存水位 < limit 的 70%(实测 2541Mi / 6Gi = 41%);
- 新副本带 startupProbe,preload 期间不再被 liveness 误杀;
- 相同参数再次
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同步,请勿在站点侧修改)