
1. 问题现象与排查思路总览代码托管平台突然打不开浏览器上赫然显示 500、502 或 503这种场景对运维和开发来说都不陌生。尤其是自建 GitLab 的团队一旦 Web 界面报出这几类错误往往意味着整个研发协作流程被卡住——代码推不了、合并请求看不了、CI 流水线也停了。我经历过好几次类似的故障最典型的一次是服务器重启之后GitLab 的 Web 界面直接返回 502查了半天发现是 Nginx 根本没起来。这三个错误码虽然看起来差不多但背后的含义完全不同。500 是内部服务器错误说明请求已经到达了应用层但应用在处理过程中抛了异常502 是网关错误通常意味着 Nginx 作为反向代理无法从上游服务比如 GitLab 的 Workhorse 或 Puma拿到有效响应503 是服务不可用多半是上游服务还没启动完或者已经被压垮了。搞清楚这个基本分类排查方向就能大致确定。这篇文章面向的是自建 GitLab 的运维人员和后端开发不管你用的是 Omnibus 一体化安装包还是源码编译部署排查思路都是通用的。我会从 Nginx 无法启动这个核心线索出发把整个排查链路拆开讲清楚怎么确认 Nginx 的状态、怎么定位它为什么起不来、怎么判断问题是出在 Nginx 本身还是 GitLab 的其他组件、以及修复之后怎么验证。中间会穿插我实际踩过的坑和一些文档里不会写的经验。注意以下所有操作都假设你有服务器的 SSH 登录权限并且具备 sudo 或 root 权限。如果你用的是容器化部署比如 Docker 或 Kubernetes排查路径会有所不同但核心逻辑一致。在正式开始之前先明确一个排查原则从外到内从下到上。从外到内是指先看 Nginx 的状态和日志再看上游服务的状态和日志从下到上是指先确认最底层的依赖端口、权限、配置文件没问题再往上查应用层。这个顺序能帮你避免一上来就扎进 GitLab 的日志里结果发现只是 Nginx 的配置文件写错了一个分号。2. Nginx 无法启动的核心原因拆解2.1 先确认 Nginx 到底有没有在运行很多人看到 502 就默认 Nginx 挂了但实际上 Nginx 可能正在运行只是上游服务出了问题。所以第一步永远是确认 Nginx 的真实状态。在 Omnibus 安装的 GitLab 中Nginx 是由gitlab-ctl管理的不是系统级的systemctl。这一点非常关键我见过不少人在systemctl status nginx上折腾半天结果发现 GitLab 自带的 Nginx 根本不走 systemd。正确的检查方式是这样# 查看 GitLab 所有组件的运行状态 sudo gitlab-ctl status # 单独查看 Nginx 的状态 sudo gitlab-ctl status nginx如果输出中 nginx 前面显示down或者fail那说明 Nginx 确实没起来。如果显示run那问题可能不在 Nginx 本身需要继续往上游查。还有一种情况是 Nginx 进程存在但端口没监听这种“假活”状态用gitlab-ctl status看不出来需要用ss或netstat确认端口# 检查 80 和 443 端口是否在监听 sudo ss -tlnp | grep -E :80|:443如果端口没有监听即使进程在Nginx 也无法正常提供服务。这时候需要看 Nginx 的错误日志来定位原因。2.2 配置文件语法错误是最常见的启动失败原因Nginx 启动失败十有八九是配置文件有问题。GitLab 的 Nginx 配置分散在多个位置主配置通常在/var/opt/gitlab/nginx/conf/nginx.conf而站点级别的配置在/var/opt/gitlab/nginx/conf/gitlab-http.conf。这些文件大部分是由gitlab-ctl reconfigure自动生成的但如果你手动改过/etc/gitlab/gitlab.rb中的某些参数重新生成配置时就可能产生语法错误。排查配置文件语法问题最直接的方法是用 Nginx 自带的测试命令# 测试 Nginx 配置语法 sudo /opt/gitlab/embedded/sbin/nginx -t -c /var/opt/gitlab/nginx/conf/nginx.conf如果配置有问题这条命令会直接告诉你哪个文件、哪一行出了什么错。常见的错误包括少了分号、括号不匹配、指令拼写错误、引号没有闭合等。我遇到过一次是因为在gitlab.rb里配置了自定义的nginx[custom_gitlab_server_config]结果里面写了一个不存在的指令导致每次 reconfigure 之后 Nginx 都起不来。实操心得每次修改/etc/gitlab/gitlab.rb之后先运行sudo gitlab-ctl reconfigure然后立刻用nginx -t测试配置。不要等到重启服务之后才发现问题那时候影响面就大了。2.3 端口被占用导致 Nginx 绑定失败另一个高频原因是端口冲突。GitLab 的 Nginx 默认监听 80 和 443如果服务器上已经装了其他 Web 服务比如 Apache、Caddy或者另一个 Nginx 实例端口就会被占用。这种情况下 Nginx 启动时会报bind() to 0.0.0.0:80 failed (98: Address already in use)。排查端口占用的命令# 查看 80 端口被哪个进程占用 sudo lsof -i :80 # 或者用 ss 命令 sudo ss -tlnp | grep :80找到占用端口的进程之后你有两个选择要么停掉那个服务要么修改 GitLab Nginx 的监听端口。修改监听端口需要在/etc/gitlab/gitlab.rb中设置# 修改 GitLab 内置 Nginx 的监听端口 nginx[listen_port] 8080 nginx[listen_https] false改完之后执行sudo gitlab-ctl reconfigure让配置生效。不过要注意改了端口之后GitLab 的 external_url 也要相应调整否则生成的链接会不对。2.4 权限问题与目录缺失Nginx 启动时需要对某些目录有读写权限比如日志目录/var/log/gitlab/nginx/、PID 文件目录/var/opt/gitlab/nginx/、以及临时文件目录。如果这些目录的属主或权限被误改Nginx 就会启动失败。常见的情况是有人手动清理了/var/opt/gitlab/下的某些目录或者用 root 身份创建了文件导致权限混乱。检查权限的方法# 查看关键目录的权限 ls -la /var/opt/gitlab/nginx/ ls -la /var/log/gitlab/nginx/正常情况下这些目录的属主应该是gitlab-www或root具体取决于你的 GitLab 版本和配置。如果发现权限不对可以用gitlab-ctl reconfigure来修复它会重新设置正确的权限。如果 reconfigure 也修不好可能需要手动调整# 修复目录权限谨慎操作先确认正确的属主 sudo chown -R gitlab-www:gitlab-www /var/opt/gitlab/nginx/ sudo chown -R gitlab-www:gitlab-www /var/log/gitlab/nginx/注意不要随意用chmod 777来“解决”权限问题这会带来严重的安全隐患。正确的做法是找到正确的属主和权限设置然后精确修复。3. 从 Nginx 到上游服务的完整排查链路3.1 理解 GitLab 的组件架构要排查 502/503 这类错误必须对 GitLab 的组件架构有个基本认识。GitLab 的 Web 请求链路大致是这样的用户请求先到达 NginxNginx 根据路径规则把请求转发给 GitLab WorkhorseWorkhorse 再根据请求类型分发给 Puma处理 Rails 应用逻辑或直接处理静态文件。如果其中任何一个环节断了用户就会看到 502 或 503。具体来说Nginx 监听 80/443 端口负责 SSL 终止和反向代理Workhorse 监听 Unix Socket 或 TCP 端口负责处理大文件上传、Git 操作等重活Puma 是 Rails 应用服务器负责处理业务逻辑Sidekiq 负责后台任务Gitaly 负责 Git 仓库的读写。这些组件之间通过 Socket 或本地端口通信。当 Nginx 无法启动时整条链路就断了用户直接看到 502。当 Nginx 正常但 Workhorse 或 Puma 没起来时Nginx 会返回 502 或 503。所以排查的时候不能只盯着 Nginx要把整条链路都检查一遍。3.2 逐层检查上游服务状态用gitlab-ctl status可以一次性看到所有组件的状态。正常的输出应该是每个组件都显示run后面跟着 PID 和运行时间。如果某个组件显示down那就是问题所在。常见的上游服务问题包括Puma 启动失败通常是因为数据库连接问题、内存不足、或者配置文件错误。Puma 的日志在/var/log/gitlab/puma/puma.log。Workhorse 启动失败可能是因为 Socket 文件路径不对、权限问题、或者与 Nginx 的配置不匹配。日志在/var/log/gitlab/gitlab-workhorse/。Gitaly 启动失败通常是因为仓库目录权限问题或磁盘空间不足。日志在/var/log/gitlab/gitaly/。PostgreSQL 启动失败可能是因为数据目录权限问题、磁盘满、或者版本升级导致的数据格式不兼容。日志在/var/log/gitlab/postgresql/。排查的时候我习惯先用gitlab-ctl status看全局然后针对显示down的组件去看它的日志。日志的路径规律是/var/log/gitlab/组件名/大部分组件的日志都在这个目录下。3.3 用 curl 和日志交叉验证当你确认 Nginx 在运行但用户仍然看到 502 时可以用curl在服务器本地直接请求绕过 DNS 和外部网络的影响# 直接请求本地 Nginx curl -v http://127.0.0.1/ # 如果配置了 HTTPS curl -vk https://127.0.0.1/如果本地 curl 也返回 502那问题确实在服务器端。这时候去看 Nginx 的错误日志# 查看 Nginx 错误日志的最后 50 行 sudo tail -50 /var/log/gitlab/nginx/gitlab_error.log日志里通常会明确写出上游服务的连接失败原因比如connect() failed (111: Connection refused) while connecting to upstream这说明 Nginx 无法连接到 Workhorse 或 Puma。根据日志中的 upstream 地址你就能定位到具体是哪个组件出了问题。3.4 数据库和 Redis 的连带影响GitLab 的 Web 界面依赖 PostgreSQL 和 Redis。如果这两个服务有问题Puma 就起不来进而导致 Nginx 返回 502。数据库问题的表现通常是 Puma 日志中出现PG::ConnectionBad或could not connect to server之类的错误。Redis 问题的表现则是Redis::CannotConnectError。检查数据库和 Redis 的状态# 检查 PostgreSQL 状态 sudo gitlab-ctl status postgresql # 检查 Redis 状态 sudo gitlab-ctl status redis # 测试数据库连接 sudo gitlab-psql -c SELECT 1; # 测试 Redis 连接 sudo gitlab-redis-cli ping如果数据库或 Redis 没起来需要先解决它们的问题再重启 GitLab 的其他组件。有时候磁盘满了会导致 PostgreSQL 无法写入这时候清理磁盘空间比重启服务更有效。4. 实操修复流程与验证方法4.1 标准修复流程当你定位到问题之后修复的流程一般是这样的先修根因再重启服务最后验证。根因可能是配置文件错误、权限问题、端口冲突、磁盘满、内存不足等。不要一上来就gitlab-ctl restart那样只能暂时缓解过不了多久又会复发。以配置文件错误为例完整的修复流程是# 第一步备份当前配置 sudo cp /etc/gitlab/gitlab.rb /etc/gitlab/gitlab.rb.bak # 第二步编辑配置文件修复错误 sudo vim /etc/gitlab/gitlab.rb # 第三步重新生成配置 sudo gitlab-ctl reconfigure # 第四步测试 Nginx 配置 sudo /opt/gitlab/embedded/sbin/nginx -t -c /var/opt/gitlab/nginx/conf/nginx.conf # 第五步如果测试通过重启 Nginx sudo gitlab-ctl restart nginx # 第六步验证服务状态 sudo gitlab-ctl status curl -v http://127.0.0.1/如果是权限问题修复流程类似但重点在chown和chmod操作上。如果是端口冲突需要先停掉占用端口的服务或者修改 GitLab 的监听端口。4.2 重启顺序有讲究GitLab 的组件之间有依赖关系重启的时候如果顺序不对可能会导致某些组件起不来。推荐的重启顺序是先重启底层依赖PostgreSQL、Redis再重启中间层Gitaly、Puma、Workhorse最后重启 Nginx。用gitlab-ctl restart不带参数时它会按照内置的依赖顺序重启所有组件一般不需要手动控制顺序。但如果只重启单个组件就要注意依赖关系。比如你重启了 Puma 但没重启 NginxNginx 可能还保持着旧的连接导致 502。这种情况下重启完 Puma 之后最好也 reload 一下 Nginx# 重启 Puma sudo gitlab-ctl restart puma # reload Nginx不中断现有连接 sudo gitlab-ctl reload nginx实操心得gitlab-ctl reload nginx比restart nginx更温和它不会中断正在处理的请求。在生产环境中优先用 reload。4.3 验证修复效果修复之后不能只看gitlab-ctl status显示run就完事了还要实际验证 Web 界面能正常访问。验证的步骤包括本地 curl 测试curl -v http://127.0.0.1/确认返回 200 或 302。检查关键页面登录页面、项目列表、合并请求页面是否能正常打开。检查 Git 操作用git clone或git pull测试 Git over HTTP 是否正常。检查 CI/CD如果用了 GitLab CI触发一个简单的流水线看是否能正常运行。观察日志修复后观察 10-15 分钟确认没有新的错误日志产生。如果验证过程中发现问题依旧需要回到排查阶段重新检查日志和配置。有时候一个问题解决了另一个隐藏的问题才暴露出来这种情况在复杂的自建环境中很常见。4.4 预防措施与监控建议故障修复之后更重要的是防止再次发生。我总结了几个实用的预防措施定期备份/etc/gitlab/gitlab.rb这是 GitLab 的核心配置文件一旦丢失或损坏恢复起来很麻烦。监控磁盘空间磁盘满是导致 GitLab 各种故障的头号原因。建议设置磁盘使用率超过 80% 就告警。监控内存使用Puma 和 PostgreSQL 都是内存大户内存不足会导致服务被 OOM Killer 杀掉。配置日志轮转GitLab 的日志增长很快不配置轮转的话磁盘很快就会被撑满。定期执行gitlab-ctl reconfigure这能确保配置文件和服务状态保持一致但要在低峰期执行。监控方面可以用 Prometheus Grafana 搭配 GitLab 自带的监控端点或者用简单的 Shell 脚本定期检查gitlab-ctl status和端口监听状态。关键是要在用户报障之前发现问题。5. 常见问题速查与避坑指南5.1 典型问题速查表现象可能原因排查命令解决方案Nginx 显示 down配置文件语法错误nginx -t修复配置文件后 reconfigureNginx 显示 down端口被占用ss -tlnp | grep :80停掉占用进程或改端口Nginx 显示 down权限问题ls -la /var/opt/gitlab/nginx/修复目录属主和权限Nginx 运行但 502上游服务未启动gitlab-ctl status启动对应组件Nginx 运行但 502数据库连接失败gitlab-psql -c SELECT 1;修复数据库问题Nginx 运行但 503Puma 过载或未就绪tail /var/log/gitlab/puma/puma.log增加 Puma worker 或等待启动重启后仍然 502配置未生效gitlab-ctl reconfigure重新生成配置并重启间歇性 502内存不足导致 OOMdmesg | grep -i oom增加内存或调整 worker 数量5.2 我踩过的坑第一个坑是盲目重启。早期遇到 502 的时候我的第一反应是gitlab-ctl restart有时候确实能好但过一会儿又挂了。后来才养成先看日志再动手的习惯。盲目重启最大的问题是会丢失现场日志被覆盖之后排查难度反而更大。第二个坑是忽略了gitlab.rb和生成配置的关系。GitLab 的 Nginx 配置不是直接编辑的而是由gitlab-ctl reconfigure根据gitlab.rb生成的。如果你直接改了/var/opt/gitlab/nginx/conf/下的文件下次 reconfigure 的时候就会被覆盖。正确的做法是改gitlab.rb然后 reconfigure。第三个坑是磁盘空间。有一次 502 排查了很久最后发现是/var/log分区满了导致 Nginx 无法写日志而启动失败。这个问题的隐蔽性在于df -h看根分区还有空间但/var/log是单独挂载的已经 100% 了。所以排查的时候一定要用df -h看所有挂载点。第四个坑是SELinux 或防火墙。在某些 Linux 发行版上SELinux 会阻止 Nginx 绑定端口或访问某些目录。如果日志中出现Permission denied但权限看起来没问题可以检查 SELinux 状态# 查看 SELinux 状态 getenforce # 查看 SELinux 拒绝日志 sudo ausearch -m avc -ts recent5.3 独家避坑技巧保留一份“已知良好”的配置备份在 GitLab 正常运行的时候把/etc/gitlab/gitlab.rb和/var/opt/gitlab/nginx/conf/下的关键配置文件备份一份。出问题的时候可以快速对比差异。用gitlab-ctl tail实时看日志这个命令会实时输出所有组件的日志排查的时候非常方便。可以配合grep过滤关键字比如sudo gitlab-ctl tail | grep -i error。修改配置前先gitlab-ctl reconfigure一次确保当前状态是干净的然后再改配置。这样如果改出问题可以快速回滚。注意 GitLab 版本升级后的配置变化大版本升级后某些配置项可能会被废弃或改名升级前一定要看官方的升级说明。不要在生产环境直接测试如果条件允许先在测试环境验证修复方案确认没问题再上生产。6. 个人经验总结与后续扩展排查 GitLab 的 500/502/503 错误核心就三件事看状态、看日志、看配置。状态告诉你哪个组件出了问题日志告诉你为什么出问题配置告诉你问题是怎么产生的。这三者结合起来大部分故障都能在半小时内定位。我个人的习惯是每次故障修复之后都会在运维笔记里记一笔现象是什么、根因是什么、怎么修的、以后怎么预防。时间长了这份笔记就成了团队内部最有价值的排查手册。毕竟文档里的内容是通用的而你自己踩过的坑才是最适合你环境的。后续如果想让排查更高效可以考虑几个方向一是搭建集中式日志系统把所有 GitLab 组件的日志收集到一起方便搜索和关联分析二是配置自动化监控在用户报障之前就发现异常三是把常见的修复流程脚本化减少手动操作带来的失误。这些扩展不一定马上要做但值得放在规划里。最后分享一个小技巧如果你不确定某个配置项的作用可以用gitlab-ctl show-config查看当前生效的完整配置。这个命令输出的内容比gitlab.rb更全面包含了所有默认值和计算后的值排查配置相关问题时非常有用。