GitLab 与 Redis 资源争用故障复盘
本文来自一次容器化 GitLab 偶发慢请求排查。主机名、域名、地址、项目名和访问凭据均已省略。
一、现象
GitLab 登录页多数时候可以正常打开,容器健康检查也显示 healthy,但用户仍会遇到随机慢和超时。多次请求耗时差异很大,日志中出现:
RedisClient::ReadTimeoutError;Waited 1.0 seconds;rack-timeout;- 容器历史状态包含
OOMKilled=true。
只看 HTTP 200 或容器健康状态,很容易误判为“服务正常”。
二、证据链
排查同时采集了容器、主机和应用三个层面的指标。
2.1 GitLab 容器
- 容器内存限制约 5 GiB;
- 实际使用已接近限制;
- 容器内存在十余个 Puma worker;
- 单个 worker 占用数百 MiB;
- 配置文件没有显式限制 Puma、Sidekiq 和监控组件资源。
2.2 宿主机
- 主机内存和 Swap 均处于高压状态;
- Swap 几乎被用满;
vmstat显示较高 IO wait;- 多个进程长时间处于 blocked 状态;
- Redis 的 RDB 后台保存进一步增加了内存和磁盘 IO 压力。
2.3 Redis
GitLab 日志中的 Redis 超时来自 Omnibus 自带的内部 Redis,并不是同机运行的外部业务 Redis。内部 Redis 数据量很小,真正的问题是应用进程和主机资源争用。
外部 Redis 则承载了多个系统的数据,规模达到数 GiB、百万级 Key。不同业务共用一个 Redis,会带来:
- 持久化时 IO 峰值叠加;
- 无法为不同业务设置独立内存策略;
- 安全边界模糊;
- 清理和故障恢复风险较高。
三、为什么默认配置不合适
GitLab 的默认资源模型并不知道容器只有约 5 GiB 内存。过多 Puma worker 会提高理论并发,却也会快速消耗内存,并把压力传递给 Swap 和磁盘。
历史活动审计显示,该实例主要用于少量运维配置仓库:
- 没有活跃的 Merge Request 流程;
- 没有 CI Runner 和构建记录;
- 没有复杂 Webhook 或自动化集成;
- 用户量和代码活动都较低。
因此没有必要维持面向高并发场景的 worker 数量。
四、已执行的止血操作
维护窗口内执行了以下操作:
- 备份 GitLab 主配置文件;
- 将 Puma worker 数显式调整为 2;
- 执行
gitlab-ctl reconfigure; - 重启 Puma 并确认新配置生效;
- 复查容器内存和页面响应时间。
调整后,GitLab 容器内存从约 4.85 GiB 降到约 3.23 GiB,登录页响应恢复到稳定的秒级以内,Puma 进程数量与配置一致。
五、不能混在一起做的事情
本次只先恢复 GitLab 稳定性,没有立刻同时改造外部 Redis。原因是同时修改应用资源、Redis 认证、持久化和网络策略,会让故障归因变得困难。
更安全的顺序是:
- 先降低 GitLab 内存压力;
- 观察超时和 OOM 是否停止增长;
- 识别外部 Redis 中各类 Key 的来源;
- 将不同业务拆分到独立实例;
- 再收口监听地址、防火墙和认证;
- 最后整理多入口 DNS 和反向代理配置。
六、Redis 后续治理
外部 Redis 不应长期由多个重要系统无边界共用。建议至少按用途拆分:
- 运维平台缓存与队列;
- 文件协作系统缓存;
- 监控和性能数据。
每个实例独立设置:
- 最大内存;
- 淘汰策略;
- RDB/AOF 持久化方式;
- 认证与网络访问控制;
- 备份、恢复和监控告警。
清理 Key 时不能使用 FLUSHALL 之类的全局操作,应先按前缀、数据库和客户端连接来源确认数据归属。
七、经验总结
healthy只表示健康检查通过,不代表服务没有资源瓶颈;- 应用报 Redis 超时,不等于 Redis 本身数据量过大;
- 容器内存限制、worker 数、Swap 和磁盘 IO 必须一起分析;
- 调整并发参数前,要先了解真实使用规模;
- 先做可回滚的止血,再做跨系统架构治理;
- 资源优化必须用变更前后的指标证明效果。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Zangyzhi!
