이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • 성능 장애 원인 격리 루틴에서 붙잡아야 할 핵심 판단 기준은 무엇인가?
  • 원인 격리는 가설 제거다라는 기준은 어떤 JVM/OS 신호로 확인되는가?
  • Java/Spring 코드에서 성능 장애 원인 격리 루틴 판단 기준은 어떤 장애 신호로 드러나는가?

개요

이 문서의 주제는 성능 장애 원인 격리 루틴이다. CPU, memory, I/O, thread, socket, JVM 후보를 순서대로 지우는 장애 분석 루틴을 정리한다.

먼저 붙잡을 기준은 다음 문장이다. 원인 격리는 가장 그럴듯한 설명을 빨리 고르는 일이 아니라 틀린 가설을 증거로 지워 가는 일이다. 장애 중에는 가장 크게 보이는 지표와 가장 먼저 변한 지표가 다를 수 있으므로, 같은 시간축의 Application, JVM, OS 자료를 묶어 판단한다.

핵심 모델

  • 판단 기준: 원인 격리는 가설 제거다.
  • 장애 분석은 영향 범위, 시작 시각, 첫 변화 지표, 현재 포화 지표를 분리해서 시작한다.
  • CPU, memory, disk, socket, JVM, pool은 한 번에 하나씩 확정하는 대상이 아니라 서로 맞물린 후보군이다.
  • 완화 조치가 성공해도 그것이 원인 증명은 아니다.
  • 같은 명령을 시간 간격을 두고 반복해야 상태 전이와 회복 방향을 볼 수 있다.
  • 최종 기록에는 맞았던 가설뿐 아니라 배제한 가설과 배제 근거도 남긴다.

문서별 핵심 구분

  • 원인 격리는 가장 그럴듯한 답을 찾는 일이 아니라 후보를 지워 가는 일이다.
  • 가장 먼저 변한 지표와 가장 크게 보이는 지표는 다를 수 있다.
  • 완화 조치와 근본 수정은 반드시 분리해서 기록한다.

원리

원인 격리는 순서가 중요하다. 먼저 사용자가 겪은 증상과 시작 시각을 고정하고, 그 다음 같은 시각의 배포, 설정 변경, traffic 변화, 외부 의존성 상태, OS/JVM 지표를 붙인다.

가장 크게 튄 지표가 항상 원인은 아니다. DB 지연이 먼저 시작되고 그 결과 Hikari pending과 Tomcat busy가 뒤따를 수 있으며, memory pressure가 먼저 생기고 그 결과 swap I/O와 iowait가 증가할 수도 있다.

따라서 장애 분석은 “CPU 문제”, “DB 문제” 같은 단어로 시작하지 않는다. CPU, memory, disk, network/socket, JVM/GC, pool, downstream 후보를 놓고 각 후보를 지지하는 증거와 반박하는 증거를 하나씩 적는다.

Java/Spring 연결

  • Actuator는 JVM, HTTP, Tomcat, HikariCP 지표를 빠르게 묶어 보는 시작점이다.
  • application log와 trace는 어떤 endpoint, job, dependency가 느렸는지를 보여 준다.
  • thread dump는 요청 thread가 CPU 계산, lock, pool 대기, I/O, 외부 호출 중 어디에 머무는지 보여 준다.
  • OS 명령은 Actuator가 직접 말해 주지 않는 CPU run queue, RSS, swap, disk wait, socket 상태를 확인한다.
  • 배포 시각, 설정 변경, traffic spike, retry 증가, dependency 장애는 지표와 같은 타임라인에 올려야 한다.
  • Spring 코드 리뷰에서는 성능 장애 원인 격리 루틴 판단 기준이 어떤 thread, pool, timeout, resource 설정으로 드러나는지 확인한다.

코드와 설정 예시

date -Is
uptime
vmstat 1 5
iostat -xz 1 5
ss -s
jcmd <pid> Thread.print
jcmd <pid> GC.heap_info

예시는 결론을 내리기 위한 전체 목록이 아니라 첫 분기점을 잡기 위한 최소 증거다. 시각을 먼저 찍고, OS 지표와 JVM 상태를 같은 창 안에서 수집해야 “먼저 변한 것”과 “뒤따라 포화된 것”을 분리할 수 있다.

관찰 명령

date -Is
uptime
vmstat 1 5
iostat -xz 1 5
ss -s
jcmd <pid> Thread.print
jcmd <pid> GC.heap_info
curl -s localhost:8080/actuator/metrics/http.server.requests

성능 장애 원인 격리 루틴 관찰 명령은 결론이 아니라 가설 확인 도구다. 운영 중에는 반복 간격, 대상 PID, 수집 시각을 함께 남기고 출력이 큰 명령은 범위를 제한한다.

지표 해석

신호먼저 의심할 것다음 확인
첫 변화 지표트리거 후보배포/traffic/dependency 타임라인
현재 포화 지표사용자가 겪는 직접 병목CPU, memory, disk, pool, socket saturation
완화 후 좋아진 지표증상 경로완화 전후 latency와 saturation
반박 증거배제할 후보같은 시각 정상 지표, 정상 dependency

이 표는 특정 자원을 바로 확정하기 위한 표가 아니라 분석 메모의 골격이다. 후보마다 “왜 맞는가”와 “왜 아닐 수 있는가”를 함께 적어야 결론이 운영 가능한 형태가 된다.

장애 상황에서 보는 순서

  1. 영향 범위, 시작 시각, 종료 시각, 사용자 증상을 한 문장으로 고정한다.
  2. 배포, 설정 변경, traffic spike, batch, 외부 의존성 장애 여부를 같은 타임라인에 올린다.
  3. Application, JVM, OS에서 최소 지표 묶음을 수집하고 수집 시각을 남긴다.
  4. 후보를 CPU, memory, disk, socket, GC, lock, pool, downstream으로 나누어 증거와 반박 증거를 적는다.
  5. 사용자를 보호하는 완화 조치를 먼저 적용하되, 완화가 어떤 후보를 지지하거나 반박하는지 기록한다.
  6. 근본 수정은 코드, 설정, capacity, dependency, 운영 절차 중 어디에 속하는지 분리한다.

장애 중에는 모든 자료를 완벽하게 모을 수 없다. 그래도 결론을 내릴 때는 최소 두 계층의 증거를 맞추고, 모으지 못한 자료는 회고의 남은 리스크로 적는다.

실전 팁

  • 첫 5분에는 원인 이름보다 타임라인이 더 중요하다. 언제부터, 누구에게, 어느 endpoint에서 시작됐는지 고정한다.
  • “지금 가장 높은 지표”와 “장애 직전에 먼저 변한 지표”를 분리해 적는다.
  • 완화 조치는 원인 분석을 방해하지 않는 범위에서 즉시 적용하되, 적용 시각과 효과를 남긴다.
  • 한 번의 snapshot으로 결론을 내리지 말고 같은 명령을 2~3회 반복해 증가, 유지, 회복 중 무엇인지 본다.
  • 회고에는 재발 시 바로 실행할 수 있는 명령 묶음과 판단 기준을 함께 남긴다.

주니어 팁

  • 장애 분석을 “아마 DB일 것 같다”로 시작하지 말고 “DB를 지지하는 증거와 반박하는 증거”로 적는다.
  • 명령 결과를 붙일 때는 반드시 시각과 PID를 같이 남긴다.
  • 정상 기준선을 모르면 장애 값의 크기를 해석하기 어렵다. 평소의 CPU, heap, pool, disk 값을 미리 저장해 둔다.

시니어 팁

  • runbook은 명령 목록이 아니라 분기표여야 한다. CPU가 높을 때, iowait가 높을 때, pool pending이 높을 때 다음 확인이 달라져야 한다.
  • 장애 대시보드는 layer별로 나누되 한 타임라인에서 볼 수 있어야 원인과 결과를 분리할 수 있다.
  • 완화 자동화는 관측 자동화와 같이 설계한다. 자동으로 traffic을 줄였다면 어떤 지표가 좋아졌는지도 자동으로 남겨야 한다.

Guru급 팁

  • 원인 격리 문서는 결론보다 의사결정 로그가 중요하다. 어떤 증거로 어떤 후보를 버렸는지 남겨야 다음 장애에서 반복을 줄인다.
  • 자동 완화가 있다면 자동 관측도 같이 있어야 한다. traffic shed, circuit open, scale-out 시각과 지표 변화를 한 묶음으로 남긴다.
  • 회고의 품질은 “맞힌 원인”보다 “다음 번에 더 빨리 배제할 후보”를 얼마나 남겼는지로 평가한다.

위험 신호!

  • 가장 크게 보이는 지표 하나로 원인을 확정한다.
  • 증상 시작 시각 없이 현재 snapshot만 모은다.
  • 재시작으로 좋아졌다는 이유로 thread dump, GC 상태, OS 지표 기록을 생략한다.
  • 완화 조치와 근본 수정을 같은 항목으로 적는다.
  • 배포, batch, traffic, dependency 이벤트를 지표 타임라인과 분리해서 본다.
  • 배제한 후보와 배제 근거를 회고에 남기지 않는다.

확인 질문

확인 질문

  • 성능 장애에서 첫 번째로 고정해야 할 정보는 무엇인가?
    • 영향 범위, 시작 시각, 사용자 증상이다. 이 기준이 없으면 이후 지표가 원인인지 결과인지 판단하기 어렵다.
  • 가장 크게 튄 지표가 항상 원인이 아닌 이유는 무엇인가?
    • 하위 dependency 지연이 pool pending을 만들고, memory pressure가 swap과 iowait를 만들듯 원인의 결과로 더 큰 saturation이 나타날 수 있기 때문이다.
  • 회고에 배제한 후보를 남겨야 하는 이유는 무엇인가?
    • 다음 장애 때 같은 가설을 다시 반복하지 않고, 당시 결론이 어떤 증거의 부족이나 확보에 의존했는지 검증할 수 있기 때문이다.

참고 문서