浏览器侧看到请求超时,status code 502,即 bad gateway,可能的原因有哪些呢?本文从 SRE 视角给一些常见的排查思路。先上一张省流卡片,把核心思路罗列出来,大家可以保存这个卡片,真遇到故障时可以对照思路排查。

  • 使用 Chrome 开发者工具确认报错的接口,拿到基础信息
  • 修改 Chrome 生成的 cURL 命令直接请求后端,绕过 Nginx,快速确认是负载均衡的问题还是后端问题
  • 查看 Nginx 的 error.log,在 access log 中检索报错的接口
  • 如果是 Nginx 层面的问题,查看 Nginx 的配置文件,确认是否有超时配置
  • 如果是后端服务的问题,根据 response 做基本判断
  • Connection refused 大概率是后端服务没启动
  • Empty reply from server 大概率是后端服务在处理请求的时候 panic 了,或者被 OOM kill 了
  • 如果后端返回 502,说明后端至少没 crash,可以继续查看后端服务日志
  • dmesg –T |grep –i "out of memory" 确认是否是 OOM 问题

我录制了一个视频,来讲解整个排查的过程,大家可以通过 SRETalk 视频号查看。下面我们逐一展开这些思路。

使用 Chrome 开发者工具确认报错的接口

首先,我们需要确认报错的接口是哪个。打开 Chrome 开发者工具,切换到 Network 标签页,勾选 Preserve log 和 Disable cache,刷新页面,找到报错的接口,查看其请求和响应信息。

排查 502 Bad Gateway 的常见思路_后端服务

在报错的请求上面右键,可以拷贝 cURL 命令:

排查 502 Bad Gateway 的常见思路_Chrome_02

修改 Chrome 生成的 cURL 命令直接请求后端

这个方式可以绕过中间网络环节,快速确认是否是后端的问题。如果后端是正常的,那就是中间网络环节的问题,尤其是在一些复杂的网络环境下(比如你的服务前面可能还有其他的负载均衡、WAF 等),这个方法非常有效。

当然,Chrome 生成的 cURL 命令,需要做一些修改才能使用,核心改动就是 URL 中的目标地址,把它改成后端服务的地址,就可以直接测试后端,注意,尽量在 nginx 所在的机器上运行 cURL,模仿真实的网络链路。如果把目标地址改成 127.0.0.1,那就相当于直接请求 nginx,这样可以快速确认是否是 nginx 前面的某个环节的问题。

如果接口是 POST 或者 PUT 请求,生成的 cURL 命令中可能会有 --raw-data 参数,不同的 cURL 版本参数可能不同,如果 --raw-data 参数不支持,可以改成 --data 参数。

查看 Nginx 的日志

Nginx 的日志分为 error.log 和 access.log,error.log 一般记录了 Nginx 的错误信息,access.log 记录了 Nginx 的访问日志。这俩日志都要看,error.log 通常内容比较少,access.log 内容比较多,可以通过 grep 等命令筛选出报错的接口。

access log 中要记录 upstream status 和 nginx 直接返回给前端的 status,如果发现 access log 中缺少这些信息,那就要修改 Nginx 的 log_format,增加这些信息。

Nginx 层面也有超时控制,可以通过配置文件查看,比如 proxy_connect_timeout、proxy_read_timeout、proxy_send_timeout 等。

后端服务的问题

使用 curl 请求后端服务,根据 response 可以做一些基本判断,通常都会给 curl 命令添加 -v 参数,这样可以看到请求和响应的详细信息。

如果后端服务没启动,curl 会报 Connection refused 错误,如果后端服务在处理请求的时候 panic 了,或者被 OOM kill 了,curl 会报 Empty reply from server 错误。这些都是常见报错。

如果是 OOM 了,一般系统日志中能够看到,可以通过 dmesg –T |grep –i "out of memory" 或者 grep -i 'out of memory' /var/log/messages 来确认。

后端服务如果在容器里

如果后端服务是在容器里,可以通过 docker logs 命令查看容器日志。如果容器里的 1 号进程是 supervisord,问题就会更复杂,可能 supervisord 托管的进程挂了,但是 supervisord 本身没挂,这时候从外部来看,容器是正常的,但是容器里的服务是不正常的。需要进入容器里排查。