복구는 5분 만에 끝나지만 원인은 영영 모릅니다. 재발하면 또 재기동하고, 그게 대응 방식으로 굳어집니다.
이 파트에서 다루는 내용
재기동은 가장 빠른 복구이자 가장 확실한 증거 파괴입니다
멈춘 서버를 재기동하면 대개 살아납니다. 그래서 재기동은 언제나 유혹적입니다.
문제는 그 순간 메모리에 있던 모든 상태가 사라진다는 것입니다. 어떤 스레드가 무엇을 기다리고 있었는지, 힙에 무엇이 쌓여 있었는지가 통째로 없어집니다.
그리고 이런 장애는 대개 다시 납니다. 원인을 못 찾았으니까요. 두 번째 발생 때는 사용자 신뢰도 함께 잃습니다. "저번에도 그러더니"라는 말이 나오기 시작합니다.
복구가 30초 늦어지는 대신 원인을 찾을 자료가 남습니다. 대부분의 상황에서 이 30초는 감당할 수 있습니다.
30초조차 낼 수 없을 만큼 손실이 큰 상황이 있습니다. 그때는 복구가 먼저입니다. 다만 그런 상황은 생각보다 드뭅니다.
재기동 전 30초에 확보할 것
Java 애플리케이션이라면 아래 세 가지가 핵심입니다. 미리 스크립트로 만들어 두면 장애 상황에서 명령을 떠올릴 필요가 없습니다.
- 스레드 덤프를 3회 뜨는 이유는 비교하기 위해서입니다. 한 장으로는 그 스레드가 멈춰 있는 것인지 지나가는 중인지 구분할 수 없습니다.
- GC 로그가 켜져 있다면 그 파일도 함께 확보합니다. 켜져 있지 않다면 이번 장애가 끝난 뒤 켜 두는 것이 후속 과제입니다.
- 컨테이너 환경이면 재기동 시 컨테이너가 통째로 교체되어 파일도 사라집니다. 확보한 파일을 호스트나 외부로 먼저 옮깁니다.
- 로그는 계속 쌓이므로 사본을 뜹니다. 원본을 보는 사이에 로테이션으로 사라질 수 있습니다.
#!/bin/bash
# collect.sh - 장애 시 증거 확보
PID=$(jcmd -l | grep -v jcmd | awk 'NR==1{print $1}')
OUT=/tmp/incident_$(date +%Y%m%d_%H%M%S)
mkdir -p $OUT
# 1) 스레드 덤프 3회 (5초 간격)
for i in 1 2 3; do
jcmd $PID Thread.print > $OUT/thread_$i.txt
sleep 5
done
# 2) 서버 상태
top -b -n 1 > $OUT/top.txt
df -h > $OUT/df.txt
free -m > $OUT/free.txt
netstat -an | grep ESTABLISHED | wc -l > $OUT/conn_count.txt
# 3) 로그 사본
cp /app/logs/application.log $OUT/ 2>/dev/null
echo "수집 완료: $OUT"PID를 자동으로 찾는 부분은 환경에 맞게 고쳐야 합니다. 여러 Java 프로세스가 뜬 서버라면 대상을 명시하는 편이 안전합니다. 이 스크립트를 운영 서버에 미리 배치해 두는 것이 이 파트의 실질적인 결론입니다.
# 힙 덤프: 메모리 문제가 의심될 때만
jcmd <PID> GC.heap_dump /tmp/heap.hprof
# 주의
# - 힙 크기만큼 파일이 생깁니다 (8GB 힙이면 8GB 파일)
# - 뜨는 동안 애플리케이션이 멈춥니다 (수십 초 이상)
# - 디스크 여유 공간을 먼저 확인해야 합니다힙 덤프는 메모리 관련 증상이 있을 때만 뜹니다. 무조건 뜨면 디스크가 차서 2차 장애가 납니다. 판단 기준은 Part 7에서 다룹니다.
복구를 먼저 해야 하는 상황
증거 확보가 원칙이지만 예외가 있습니다. 판단은 두 가지를 곱해서 합니다. 지금 발생하는 손실의 크기와, 나중에 같은 증거를 다시 얻을 수 있는 가능성입니다.
실시간 거래가 중단되거나, 안전과 관련되거나, 계약상 배상이 걸린 경우입니다. 이때도 스레드 덤프 한 장은 뜰 수 있습니다.
간헐적으로 나는 장애라면 지금이 유일한 기회일 수 있습니다. 놓치면 다음 발생까지 몇 주를 기다립니다.
여러 대로 운영 중이라면 한 대만 남겨 증거를 확보하고 나머지를 재기동하는 방법이 있습니다. 서비스는 살고 증거도 남습니다.
로드밸런서에서 한 인스턴스만 제외해 트래픽을 끊고 프로세스는 살려 둡니다. 서비스는 나머지 인스턴스로 계속되고, 남긴 한 대에서 여유 있게 상태를 확인할 수 있습니다. 다중화된 환경이라면 이 방법을 먼저 검토합니다.
되돌릴 것인가
배포 직후에 장애가 났다면 롤백이 가장 빠르고 확실한 복구입니다. 원인을 분석하는 것보다 일단 되돌리고 안정된 상태에서 분석하는 편이 낫습니다.
다만 되돌릴 수 없는 경우가 있습니다. 롤백을 결정하기 전에 이걸 먼저 확인해야 합니다.
- 롤백 가능 여부는 배포 전에 판단해 두는 것이 원칙입니다. 장애 상황에서 확인하려면 이미 늦습니다.
- 스키마 변경이 있는 배포는 되돌릴 수 없다는 것을 배포 계획에 명시합니다.
- 롤백했다면 그 사실과 시각을 반드시 기록합니다. 이후 분석에서 기준점이 됩니다.
- Git 코스에서 다룬 태그와 릴리스 이력이 있으면 어느 시점으로 되돌릴지가 명확해집니다.
컬럼을 추가하거나 바꿨다면 이전 버전이 그 스키마에서 못 돌 수 있습니다. 반대로 컬럼을 지웠다면 데이터가 이미 사라졌습니다.
새 버전이 만든 데이터를 이전 버전이 해석하지 못하면 롤백 후에 다른 장애가 납니다.
상대 시스템이 이미 새 규격으로 바뀌었다면 우리만 되돌릴 수 없습니다.
위 세 가지에 해당하지 않으면 대개 안전합니다. 이 경우 망설이지 말고 되돌립니다.
임시 조치에는 만료일을 붙입니다
복구를 위해 임시 조치를 하게 됩니다. 메모리를 늘리거나, 타임아웃을 늘리거나, 재기동을 스케줄에 걸어 두거나, 특정 기능을 잠시 막습니다.
이 조치들의 공통점은 원인을 그대로 둔 채 증상만 가린다는 것입니다. 그리고 대부분 그대로 남습니다. 급한 불이 꺼지면 아무도 다시 안 봅니다.
몇 년 뒤 아무도 이유를 모르는 설정이 되고, 건드리면 위험할 것 같아 그대로 둡니다. 협업·성장 코스에서 다룬 기술 부채가 이렇게 쌓입니다.
[임시 조치 기록]
조치 내용 : WAS 힙 메모리 4GB → 8GB 상향
적용 시각 : 2026-07-29 15:40
적용 이유 : OutOfMemoryError 반복 발생, 원인 미규명 상태에서 서비스 복구 우선
부작용/위험 : 메모리 누수가 원인이라면 증상 발현이 늦어질 뿐 재발함
발현 주기가 길어져 원인 추적이 더 어려워질 수 있음
후속 과제 : 힙 덤프 분석으로 누수 지점 확인 (담당: OOO)
재검토 기한 : 2026-08-12
되돌리는 법 : setenv.sh 의 -Xmx 값을 4g 로 복원 후 재기동재검토 기한과 되돌리는 방법을 함께 적는 것이 핵심입니다. 이 두 줄이 없으면 임시 조치는 영구 조치가 됩니다. 트랙 C에서 이 기록들을 어떻게 관리하는지 다룹니다.
복구와 원인 규명은 대립하지 않습니다. 30초의 증거 확보로 둘 다 얻을 수 있습니다. 다중화된 환경이라면 한 대를 남기는 방법으로 더 여유 있게 얻을 수 있습니다. 진짜 대립이 생기는 것은 그 30초조차 낼 수 없을 때뿐이고, 그 판단은 손실 크기와 재현 가능성으로 합니다.