自己造出 502 与 504,再用证据分开
目标
亲手制造 502 和 504 后,不再根据状态码猜测原因,而是能够通过 error.log 中的语句
确认根因。尤其要区分同为 504、但触发了不同超时的两种情况。
为什么重要
故障响应中最浪费时间的事情,是确信了错误的原因。
如果看到 502 就断定“后端宕机”,随后调高超时,不会有任何效果,
因为连接被拒绝时根本没有可供等待的时间。
反过来,看到 504 也不代表调高 proxy_read_timeout 就能解决问题。
如果卡在连接阶段,即使把读取超时提高到 30 秒,请求仍会在 2 秒后中断。
本实验第 5 步正是这一场景。
区分两者的线索,仅仅是日志一行中 while 后面的短语。
步骤
- 保存
/root/ngi/upstream.py并在后台启动。 应用会在 9101 端口启动,9109 端口会成为不接受连接的端口。 9999 端口不启动任何程序。 验证:9101 的/返回 200,/slow?d=2在 2 秒后返回 200,/bighdr?n=8000的响应中应包含X-Session-Blob请求头。 - 编写
/root/ngi/nginx.conf并启动 nginx。 监听8088 端口并设置三条路径——/app/代理到 9101,/dead/代理到 9999,/hang/代理到 9109。error_log以 warn 级别写入/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。 (可以使用浏览器预览打开。) - 调用
http://127.0.0.1:8088/dead/,创建/root/ngi/case-refused.txt。 文件包含三行。status=<받은 상태 코드> phrase=<error.log 에서 원인을 지목하는 구절 그대로> timeout=<실제로 발동한 타임아웃 이름, 없으면 none> - 调用
http://127.0.0.1:8088/app/slow?d=10,并测量多少秒后中断, 创建/root/ngi/case-read.txt。文件包含四行。status=<상태 코드> phrase=<error.log 의 while 로 시작하는 구절 그대로> timeout=<발동한 타임아웃 지시자 이름> elapsed=<끊기기까지 걸린 초, 정수> - 在
/hang/块内部加入proxy_read_timeout 30s;并 reload, 然后调用http://127.0.0.1:8088/hang/。即使给了读取操作 30 秒, 请求仍会早得多地中断。测量时间并按第 4 步相同的格式创建/root/ngi/case-connect.txt。 状态码应与第 4 步相同,但timeout必须不同。 - 调用
http://127.0.0.1:8088/app/bighdr?n=8000会得到 502。 确认原因短语后,增大代理缓冲区,将其修复到返回 200。 如果只增大一个指令,nginx 将无法启动;请阅读 emerg 消息并同时修正相关项。 创建/root/ngi/case-header.txt,包含四行。
修复后,第 3~5 步中的故障仍应能够原样复现。before=<고치기 전 코드> after=<고친 뒤 코드> phrase=<error.log 에서 원인을 지목하는 구절 그대로> fix=<헤더 크기를 직접 정하는 지시자 이름> - 创建
/root/ngi/verdict.csv。第一行为case,status,fix, 后面再写四行。case分别为refused、read-timeout、connect-timeout、big-header;fix从none、proxy_connect_timeout、proxy_read_timeout、proxy_buffer_size中选择。 根据第 3~6 步中的观察确定对应关系。 - 编写
/root/ngi/rca.md。必须包含## 현상、## 증거、## 원인、## 조치、## 재발방지五个 h2 标题, 正文中必须原样引用第 4、5 步的elapsed数字以及两个超时指令名称。
参考
- 启动:
nginx -c /root/ngi/nginx.conf -p /root/ngi - 语法检查:
nginx -t -c /root/ngi/nginx.conf -p /root/ngi - 重新应用:
nginx -s reload -c /root/ngi/nginx.conf -p /root/ngi - 测量时间:
curl -s -o /dev/null -w '%{http_code} %{time_total}\n' <URL> - 启动 upstream:
cd /root/ngi && nohup python3 upstream.py > up.err 2>&1 & - 常见错误 1:将
error_log设为error级别。 这样会丢失本课程中的所有警告行。 - 常见错误 2:看到 502 就先提高超时。 连接被拒绝时根本没有可供等待的时间。
- 常见错误 3:遗漏
proxy_pass后的斜杠,使 upstream 路径变成/app/slow。 必须像上面的示例一样让地址以根斜杠结尾,路径才会变为/slow。
启动故障发生器
保存 /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_log 以 warn 级别写入 /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 分别为 refused、read-timeout、connect-timeout、
big-header;fix 从 none、proxy_connect_timeout、
proxy_read_timeout、proxy_buffer_size 中选择。
根据第 3~6 步中的观察确定对应关系。
把四种故障汇总到一张表中。fix 一栏填写 nginx 配置中真正能够解决该故障的指令名称;其中有一种故障无法通过 nginx 配置解决——该行填写 none。
包含数字的故障报告
编写 /root/ngi/rca.md。必须包含 ## 현상、## 증거、## 원인、
## 조치、## 재발방지 五个 h2 标题,
正文中必须原样引用第 4、5 步的 elapsed 数字以及两个超时指令名称。
报告的价值在于数字。请原样引用前面步骤中测得的秒数。在预防复发部分,不要只写“注意”,而要写明将哪个指令设为什么值,以及针对哪条日志语句设置告警。