이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- 원인 격리는 어떤 축으로 좁혀 가야 하는가?
- 임시 완화는 근본 해결과 무엇이 다른가?
- 장애 중 여러 조치를 동시에 하면 왜 위험한가?
개요
원인 격리는 장애의 가능성을 줄여 실제 영향을 만든 지점을 찾는 과정이다.
임시 완화는 원인을 완전히 고치기 전 사용자 영향을 줄이는 조치다.
장애 중에는 두 작업이 함께 진행된다.
원인을 찾는 동안 영향이 커지면 먼저 완화해야 하고, 완화 후에도 원인 격리를 멈추면 안 된다.
백엔드 개발자는 코드, 설정, 데이터, 외부 의존성, 배치 흐름을 기준으로 원인 후보를 좁히는 역할을 한다.
격리 축
원인 후보는 한 번에 맞히려 하지 말고 축으로 나눠 좁힌다.
- 시간: 언제부터 시작되었는가?
- 버전: 어떤 배포 이후인가?
- 기능: 어떤 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 -20select 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가 줄어야 한다”처럼 관찰 가능한 결과를 정한다.
한 번에 하나씩
장애 중 여러 조치를 동시에 하면 무엇이 효과가 있었는지 알 수 없다.
동시에 바꾼 설정이 서로 다른 부작용을 만들 수도 있다.
가능하면 다음 순서를 지킨다.
- 조치 의도를 적는다.
- 기대 신호를 적는다.
- 실행 시각을 남긴다.
- 5~10분 관찰한다.
- 결과를 보고 유지, 되돌림, 다음 조치를 결정한다.
긴급한 SEV-1에서는 여러 조치가 필요할 수 있다.
그 경우에도 어떤 조치를 언제 했는지 타임라인을 남겨야 한다.
운영 체크리스트
- 시작 시각과 최근 변경을 먼저 맞춘다.
- 영향 endpoint와 사용자 범위를 좁힌다.
- 읽기 명령으로 DB, queue, 외부 의존성 상태를 확인한다.
- 임시 완화마다 기대 신호와 되돌림 조건을 정한다.
- 여러 조치를 동시에 실행한 경우 타임라인을 더 자세히 남긴다.
- 완화 후에도 근본 원인 후보와 재발 가능성을 확인한다.
- 데이터 손실 가능성이 있으면 복구보다 증거 보존을 먼저 한다.
장애 신호
- 조치를 했는데 어떤 신호가 좋아져야 하는지 정하지 않았다.
- 여러 설정을 동시에 바꿔 효과를 판별할 수 없다.
- 원인 후보가 계속 늘기만 하고 배제되는 항목이 없다.
- 완화 후 사용자 영향은 줄었지만 같은 오류가 계속 발생한다.
- batch를 재시작하자 같은 문제가 반복된다.
- 외부 API 실패를 내부 retry가 증폭시키고 있다.
이 신호는 원인 격리 방식이나 완화 방식이 충분히 구조화되지 않았다는 뜻이다.
안전한 완화 조치
- 영향이 큰 기능부터 제한하고 핵심 조회는 유지한다.
- 쓰기 중단이 필요한 경우 사용자 안내와 복구 기준을 함께 정한다.
- retry 폭주가 의심되면 retry를 줄이고 queue 적체를 관찰한다.
- DB lock이 의심되면 장기 transaction과 배치 상태를 먼저 읽기 명령으로 확인한다.
- 외부 의존성이 불안정하면 timeout을 짧게 하고 fallback을 검토한다.
- 완화 조치는 되돌릴 수 있는 형태를 우선 선택한다.
하지 말아야 할 대응
- 원인 후보를 확인하지 않고 운영 데이터를 직접 수정하지 않는다.
- 효과 측정 없이 설정을 여러 개 동시에 바꾸지 않는다.
- 완화가 성공했다고 근본 원인 분석을 생략하지 않는다.
- 사용자 영향이 계속되는 동안 긴 원인 토론만 하지 않는다.
- 실패한 batch를 원인 확인 없이 반복 재실행하지 않는다.
인프라 담당자와 공유할 자료
- 영향 endpoint, 사용자 범위, 시작 시각
- 최근 배포와 설정 변경 내역
- DB, queue, 외부 API, 자원 사용량 중 의심 축
- 실행한 완화 조치와 기대 신호
- 조치 후 실제 변화
- 되돌림 조건과 다음 후보 조치
공유 자료는 원인 후보를 줄이는 데 쓰여야 한다.
확인 질문
확인 질문
- 원인 격리를 축으로 나눠야 하는 이유는 무엇인가?
- 시간, 버전, 기능, 사용자, 데이터, 의존성을 분리해야 후보를 근거 있게 줄일 수 있기 때문이다.
- 임시 완화와 근본 해결의 차이는 무엇인가?
- 임시 완화는 사용자 영향을 줄이는 조치이고, 근본 해결은 같은 원인이 반복되지 않게 고치는 조치다.
- 여러 조치를 동시에 실행할 때 가장 큰 위험은 무엇인가?
- 어떤 조치가 효과를 냈는지 알 수 없고 새로운 부작용을 만들 수 있다는 점이다.
리뷰 체크포인트
- 원인 격리 축이 문서에 나뉘어 있는가?
- 완화 조치마다 기대 신호와 되돌림 조건이 있는가?
- 읽기 명령과 변경 명령이 구분되어 있는가?
- 조치 시각과 결과가 타임라인에 남는가?
- 완화 후 근본 원인 분석으로 이어지는가?