LabHub
学习 学习路径 课程

nginx 故障处理

自己造出 502 与 504,再用证据分开

在 LabHub 中继续学习

目标

亲手制造 502 和 504 后,不再根据状态码猜测原因,而是能够通过 error.log 中的语句 确认根因。尤其要区分同为 504、但触发了不同超时的两种情况。

为什么重要

故障响应中最浪费时间的事情,是确信了错误的原因。 如果看到 502 就断定“后端宕机”,随后调高超时,不会有任何效果, 因为连接被拒绝时根本没有可供等待的时间。 反过来,看到 504 也不代表调高 proxy_read_timeout 就能解决问题。 如果卡在连接阶段,即使把读取超时提高到 30 秒,请求仍会在 2 秒后中断。 本实验第 5 步正是这一场景。 区分两者的线索,仅仅是日志一行中 while 后面的短语。

步骤

  1. 保存 /root/ngi/upstream.py 并在后台启动。 应用会在 9101 端口启动,9109 端口会成为不接受连接的端口。 9999 端口不启动任何程序。 验证:9101 的 / 返回 200,/slow?d=2 在 2 秒后返回 200, /bighdr?n=8000 的响应中应包含 X-Session-Blob 请求头。
  2. 编写 /root/ngi/nginx.conf 并启动 nginx。 监听8088 端口并设置三条路径——/app/ 代理到 9101, /dead/ 代理到 9999,/hang/ 代理到 9109。 error_logwarn 级别写入 /root/ngi/logs/error.log, 访问日志写入 /root/ngi/logs/access.log,格式中必须同时包含 $upstream_response_time$request_time$upstream_status。 设置 proxy_connect_timeout 2s;proxy_read_timeout 3s;。 验证:http://127.0.0.1:8088/app/ 必须返回 200。 (可以使用浏览器预览打开。)
  3. 调用 http://127.0.0.1:8088/dead/,创建 /root/ngi/case-refused.txt。 文件包含三行。
    status=<받은 상태 코드>
    phrase=<error.log 에서 원인을 지목하는 구절 그대로>
    timeout=<실제로 발동한 타임아웃 이름, 없으면 none>
    
  4. 调用 http://127.0.0.1:8088/app/slow?d=10,并测量多少秒后中断, 创建 /root/ngi/case-read.txt。文件包含四行。
    status=<상태 코드>
    phrase=<error.log 의 while 로 시작하는 구절 그대로>
    timeout=<발동한 타임아웃 지시자 이름>
    elapsed=<끊기기까지 걸린 초, 정수>
    
  5. /hang/内部加入 proxy_read_timeout 30s; 并 reload, 然后调用 http://127.0.0.1:8088/hang/。即使给了读取操作 30 秒, 请求仍会早得多地中断。测量时间并按第 4 步相同的格式创建 /root/ngi/case-connect.txt。 状态码应与第 4 步相同,但 timeout 必须不同。
  6. 调用 http://127.0.0.1:8088/app/bighdr?n=8000 会得到 502。 确认原因短语后,增大代理缓冲区,将其修复到返回 200。 如果只增大一个指令,nginx 将无法启动;请阅读 emerg 消息并同时修正相关项。 创建 /root/ngi/case-header.txt,包含四行。
    before=<고치기 전 코드>
    after=<고친 뒤 코드>
    phrase=<error.log 에서 원인을 지목하는 구절 그대로>
    fix=<헤더 크기를 직접 정하는 지시자 이름>
    
    修复后,第 3~5 步中的故障仍应能够原样复现。
  7. 创建 /root/ngi/verdict.csv。第一行为 case,status,fix, 后面再写四行。case 分别为 refusedread-timeoutconnect-timeoutbig-headerfixnoneproxy_connect_timeoutproxy_read_timeoutproxy_buffer_size 中选择。 根据第 3~6 步中的观察确定对应关系。
  8. 编写 /root/ngi/rca.md。必须包含 ## 현상## 증거## 원인## 조치## 재발방지 五个 h2 标题, 正文中必须原样引用第 4、5 步的 elapsed 数字以及两个超时指令名称。

参考

启动故障发生器

保存 /root/ngi/upstream.py 并在后台启动。 应用会在 9101 端口启动,9109 端口会成为不接受连接的端口。 9999 端口不启动任何程序。 验证:9101 的 / 返回 200,/slow?d=2 在 2 秒后返回 200, /bighdr?n=8000 的响应中应包含 X-Session-Blob 请求头。

需要三种 upstream——正常响应的应用、永远不接受连接的端口,以及没有任何进程监听的端口。前两种由同一个 Python 文件同时启动。启动后,请分别确认应用返回 200,以及不接受连接的端口确实会一直挂起。

搭建能够留下证据的代理

编写 /root/ngi/nginx.conf 并启动 nginx。 监听8088 端口并设置三条路径——/app/ 代理到 9101, /dead/ 代理到 9999,/hang/ 代理到 9109。 error_logwarn 级别写入 /root/ngi/logs/error.log, 访问日志写入 /root/ngi/logs/access.log,格式中必须同时包含 $upstream_response_time$request_time$upstream_status。 设置 proxy_connect_timeout 2s;proxy_read_timeout 3s;。 验证:http://127.0.0.1:8088/app/ 必须返回 200。 (可以使用浏览器预览打开。)

端口是 8088。这两份日志就是本实验的全部证据,请准确设置路径和级别——如果把 error_log 设为 error 级别,后续实验中的警告会全部消失。访问日志格式必须包含 upstream 耗时、总耗时和 upstream 状态码。

已宕机的 upstream——502

调用 http://127.0.0.1:8088/dead/,创建 /root/ngi/case-refused.txt。 文件包含三行。

status=<받은 상태 코드>
phrase=<error.log 에서 원인을 지목하는 구절 그대로>
timeout=<실제로 발동한 타임아웃 이름, 없으면 none>

观察代理到已宕机端口时返回什么状态码,以及当时 error.log 的第一个词是什么。再问问自己,这里是否存在讨论超时的余地,就能确定 timeout 一栏应该填写什么。

缓慢的 upstream——504,读取超时

调用 http://127.0.0.1:8088/app/slow?d=10,并测量多少秒后中断, 创建 /root/ngi/case-read.txt。文件包含四行。

status=<상태 코드>
phrase=<error.log 의 while 로 시작하는 구절 그대로>
timeout=<발동한 타임아웃 지시자 이름>
elapsed=<끊기기까지 걸린 초, 정수>

调用需要 10 秒才响应的路径,同时测量请求在几秒后中断。这个秒数原样写在配置文件的某个位置。读取 error.log 短语中 while 后面的内容,就能确定是哪一种超时。

调高的超时并非实际触发的超时

/hang/内部加入 proxy_read_timeout 30s; 并 reload, 然后调用 http://127.0.0.1:8088/hang/。即使给了读取操作 30 秒, 请求仍会早得多地中断。测量时间并按第 4 步相同的格式创建 /root/ngi/case-connect.txt。 状态码应与第 4 步相同,但 timeout 必须不同。

先在 /hang/ 块中加入 proxy_read_timeout 30s 并 reload。这表示允许读取等待 30 秒。尽管如此,请测量请求在几秒后中断,并比较 error.log 中 while 后面的短语与第 4 步有何不同。状态码和错误编号相同。

服务器正常,却只有特定用户得到 502

调用 http://127.0.0.1:8088/app/bighdr?n=8000 会得到 502。 确认原因短语后,增大代理缓冲区,将其修复到返回 200。 如果只增大一个指令,nginx 将无法启动;请阅读 emerg 消息并同时修正相关项。 创建 /root/ngi/case-header.txt,包含四行。

before=<고치기 전 코드>
after=<고친 뒤 코드>
phrase=<error.log 에서 원인을 지목하는 구절 그대로>
fix=<헤더 크기를 직접 정하는 지시자 이름>

修复后,第 3~5 步中的故障仍应能够原样复现。

有一条路径会把响应头膨胀到 8000 字节。观察仅仅是响应头变大时会得到什么状态码。修复时,如果只增大一个缓冲区指令,nginx 会完全无法启动——请从 emerg 消息中找出还必须同时增大的项目。

四行判定表

创建 /root/ngi/verdict.csv。第一行为 case,status,fix, 后面再写四行。case 分别为 refusedread-timeoutconnect-timeoutbig-headerfixnoneproxy_connect_timeoutproxy_read_timeoutproxy_buffer_size 中选择。 根据第 3~6 步中的观察确定对应关系。

把四种故障汇总到一张表中。fix 一栏填写 nginx 配置中真正能够解决该故障的指令名称;其中有一种故障无法通过 nginx 配置解决——该行填写 none。

包含数字的故障报告

编写 /root/ngi/rca.md。必须包含 ## 현상## 증거## 원인## 조치## 재발방지 五个 h2 标题, 正文中必须原样引用第 4、5 步的 elapsed 数字以及两个超时指令名称。

报告的价值在于数字。请原样引用前面步骤中测得的秒数。在预防复发部分,不要只写“注意”,而要写明将哪个指令设为什么值,以及针对哪条日志语句设置告警。