LabHub

nginx 장애 대응 · 502 와 504 를 증거로 가른다 · 실습

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 포트로 listen 하고 세 경로를 둡니다 — /app/ 은 9101 번,
/dead/ 는 9999 번, /hang/ 은 9109 번으로 프록시합니다.
error_log/root/ngi/logs/error.logwarn 수준으로,
액세스 로그는 /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 초를 줬는데도
요청이 훨씬 빨리 끊깁니다. 시간을 재서 /root/ngi/case-connect.txt
4 단계와 같은 형식으로 만듭니다.
상태 코드는 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 이고
네 줄이 이어집니다. caserefused, read-timeout, connect-timeout,
big-header 이고 fixnone, proxy_connect_timeout,
proxy_read_timeout, proxy_buffer_size 중 하나입니다.
어느 것이 어느 것과 짝인지는 3~6 단계에서 본 것으로 정하세요.
8. /root/ngi/rca.md 를 작성합니다. ## 현상, ## 증거, ## 원인,
## 조치, ## 재발방지 다섯 개의 h2 제목이 있어야 하고,
4 단계와 5 단계의 elapsed 숫자와 두 타임아웃 지시자 이름이 본문에
그대로 인용돼야 합니다.

참고

단계 8개

  1. 장애 발생기 띄우기
  2. 증거가 남는 프록시 세우기
  3. 죽은 업스트림 — 502
  4. 느린 업스트림 — 504, 읽기 타임아웃
  5. 올린 타임아웃은 발동한 타임아웃이 아니었다
  6. 서버는 멀쩡한데 특정 사용자만 502
  7. 네 줄짜리 판정표
  8. 숫자가 들어간 장애 보고서