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

  • 원인 격리는 어떤 축으로 좁혀 가야 하는가?
  • 임시 완화는 근본 해결과 무엇이 다른가?
  • 장애 중 여러 조치를 동시에 하면 왜 위험한가?

개요

원인 격리는 장애의 가능성을 줄여 실제 영향을 만든 지점을 찾는 과정이다.

임시 완화는 원인을 완전히 고치기 전 사용자 영향을 줄이는 조치다.

장애 중에는 두 작업이 함께 진행된다.

원인을 찾는 동안 영향이 커지면 먼저 완화해야 하고, 완화 후에도 원인 격리를 멈추면 안 된다.

백엔드 개발자는 코드, 설정, 데이터, 외부 의존성, 배치 흐름을 기준으로 원인 후보를 좁히는 역할을 한다.

격리 축

원인 후보는 한 번에 맞히려 하지 말고 축으로 나눠 좁힌다.

  • 시간: 언제부터 시작되었는가?
  • 버전: 어떤 배포 이후인가?
  • 기능: 어떤 endpoint나 job에서만 발생하는가?
  • 사용자 범위: 특정 tenant, 지역, 계정 유형에만 발생하는가?
  • 데이터 범위: 특정 상태 값, 특정 테이블, 특정 파일 prefix와 관련 있는가?
  • 의존성: DB, cache, message broker, 외부 API 중 어디와 연결되는가?
  • 자원: CPU, memory, thread, connection pool 중 무엇이 포화되었는가?

각 축을 확인할 때는 근거와 배제한 이유를 기록한다.

Spring 장애 격리

Spring 백엔드에서 자주 확인하는 지점은 다음과 같다.

  • controller 진입 전 실패인지, service 내부 실패인지 구분한다.
  • 특정 exception이 증가했는지 확인한다.
  • transaction timeout인지 외부 API timeout인지 분리한다.
  • connection pool 대기 시간이 늘었는지 확인한다.
  • 스케줄 작업이나 consumer가 같은 테이블을 잠그고 있는지 확인한다.
  • 최근 설정 변경으로 timeout, pool size, retry가 바뀌었는지 확인한다.
  • feature flag로 영향 기능을 분리할 수 있는지 확인한다.

중요한 것은 “가능성 있는 모든 원인”을 나열하는 것이 아니라 다음 조치를 결정할 만큼 좁히는 것이다.

관찰 예시

date
grep "OrderController" application.log | tail -50
grep "SQLTransientConnectionException" application.log | tail -20
grep "paymentClient" application.log | tail -20
select state, count(*)
from pg_stat_activity
group by state;
 
select wait_event_type, wait_event, count(*)
from pg_stat_activity
where wait_event is not null
group by wait_event_type, wait_event;

명령은 읽기 중심이어야 하며, 실행 시각과 대상 환경을 기록한다.

장애 중 쓰기 명령은 완화 목적과 되돌림 조건이 있을 때만 실행한다.

임시 완화의 종류

임시 완화는 사용자 영향을 줄이는 조치다.

예시는 다음과 같다.

  • 문제가 된 기능을 feature flag로 끈다.
  • 쓰기 기능을 제한하고 조회 기능만 유지한다.
  • batch나 consumer를 잠시 멈춘다.
  • 외부 API 호출을 줄이거나 degrade response를 제공한다.
  • rate limit으로 과부하를 낮춘다.
  • cache TTL이나 fallback 값을 조정한다.
  • 이전 애플리케이션 버전으로 되돌린다.

완화 조치는 반드시 기대 신호를 가져야 한다.

“consumer 중단 후 DB lock wait가 줄어야 한다”처럼 관찰 가능한 결과를 정한다.

한 번에 하나씩

장애 중 여러 조치를 동시에 하면 무엇이 효과가 있었는지 알 수 없다.

동시에 바꾼 설정이 서로 다른 부작용을 만들 수도 있다.

가능하면 다음 순서를 지킨다.

  1. 조치 의도를 적는다.
  2. 기대 신호를 적는다.
  3. 실행 시각을 남긴다.
  4. 5~10분 관찰한다.
  5. 결과를 보고 유지, 되돌림, 다음 조치를 결정한다.

긴급한 SEV-1에서는 여러 조치가 필요할 수 있다.

그 경우에도 어떤 조치를 언제 했는지 타임라인을 남겨야 한다.

운영 체크리스트

  • 시작 시각과 최근 변경을 먼저 맞춘다.
  • 영향 endpoint와 사용자 범위를 좁힌다.
  • 읽기 명령으로 DB, queue, 외부 의존성 상태를 확인한다.
  • 임시 완화마다 기대 신호와 되돌림 조건을 정한다.
  • 여러 조치를 동시에 실행한 경우 타임라인을 더 자세히 남긴다.
  • 완화 후에도 근본 원인 후보와 재발 가능성을 확인한다.
  • 데이터 손실 가능성이 있으면 복구보다 증거 보존을 먼저 한다.

장애 신호

  • 조치를 했는데 어떤 신호가 좋아져야 하는지 정하지 않았다.
  • 여러 설정을 동시에 바꿔 효과를 판별할 수 없다.
  • 원인 후보가 계속 늘기만 하고 배제되는 항목이 없다.
  • 완화 후 사용자 영향은 줄었지만 같은 오류가 계속 발생한다.
  • batch를 재시작하자 같은 문제가 반복된다.
  • 외부 API 실패를 내부 retry가 증폭시키고 있다.

이 신호는 원인 격리 방식이나 완화 방식이 충분히 구조화되지 않았다는 뜻이다.

안전한 완화 조치

  • 영향이 큰 기능부터 제한하고 핵심 조회는 유지한다.
  • 쓰기 중단이 필요한 경우 사용자 안내와 복구 기준을 함께 정한다.
  • retry 폭주가 의심되면 retry를 줄이고 queue 적체를 관찰한다.
  • DB lock이 의심되면 장기 transaction과 배치 상태를 먼저 읽기 명령으로 확인한다.
  • 외부 의존성이 불안정하면 timeout을 짧게 하고 fallback을 검토한다.
  • 완화 조치는 되돌릴 수 있는 형태를 우선 선택한다.

하지 말아야 할 대응

  • 원인 후보를 확인하지 않고 운영 데이터를 직접 수정하지 않는다.
  • 효과 측정 없이 설정을 여러 개 동시에 바꾸지 않는다.
  • 완화가 성공했다고 근본 원인 분석을 생략하지 않는다.
  • 사용자 영향이 계속되는 동안 긴 원인 토론만 하지 않는다.
  • 실패한 batch를 원인 확인 없이 반복 재실행하지 않는다.

인프라 담당자와 공유할 자료

  • 영향 endpoint, 사용자 범위, 시작 시각
  • 최근 배포와 설정 변경 내역
  • DB, queue, 외부 API, 자원 사용량 중 의심 축
  • 실행한 완화 조치와 기대 신호
  • 조치 후 실제 변화
  • 되돌림 조건과 다음 후보 조치

공유 자료는 원인 후보를 줄이는 데 쓰여야 한다.

확인 질문

확인 질문

  • 원인 격리를 축으로 나눠야 하는 이유는 무엇인가?
    • 시간, 버전, 기능, 사용자, 데이터, 의존성을 분리해야 후보를 근거 있게 줄일 수 있기 때문이다.
  • 임시 완화와 근본 해결의 차이는 무엇인가?
    • 임시 완화는 사용자 영향을 줄이는 조치이고, 근본 해결은 같은 원인이 반복되지 않게 고치는 조치다.
  • 여러 조치를 동시에 실행할 때 가장 큰 위험은 무엇인가?
    • 어떤 조치가 효과를 냈는지 알 수 없고 새로운 부작용을 만들 수 있다는 점이다.

리뷰 체크포인트

  • 원인 격리 축이 문서에 나뉘어 있는가?
  • 완화 조치마다 기대 신호와 되돌림 조건이 있는가?
  • 읽기 명령과 변경 명령이 구분되어 있는가?
  • 조치 시각과 결과가 타임라인에 남는가?
  • 완화 후 근본 원인 분석으로 이어지는가?