[운영#3] 장애 복구 자동화 (리트라이·백오프·대체 경로·휴면 후 재시도)

한줄 요약:
“언젠가는 오류가 난다.”
그래서 장애를 ‘예방’보다 ‘복구’ 중심으로 설계하자.
👉 자동 재시도·대체경로·휴면 복구 루프를 구현해 완전 무중단 시스템으로!


1. 목표

  • 리트라이 + 지수 백오프(Exponential Backoff) 로 자동 재시도
  • 대체 경로(Fallback): 예비 API나 저장소로 전환
  • 휴면 후 복구(Retry Loop): 일정 시간 후 자동 재시작
  • 에러 유형별 대응(4xx/5xx/Timeout/Network)
  • 알림 & 상태 리포트 자동 발송

2. 장애 복구 기본 구조

[실패 감지] 
   ├─ 1차 리트라이(3회, 5초 간격)
   ├─ 2차 백오프(10·20·40초)
   ├─ 3차 대체 API 전환
   └─ 4차 휴면 모드(5분 후 재시작)

3. 공통 유틸 — resilience.py (복붙)

import time, random, logging
from functools import wraps

logger = logging.getLogger("resilience")
logger.setLevel(logging.INFO)
if not logger.handlers:
    h = logging.StreamHandler()
    h.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
    logger.addHandler(h)

# ───────────────────────────────
# 1️⃣ 지수 백오프 리트라이
# ───────────────────────────────
def retry_with_backoff(max_retries=3, base_delay=3, jitter=0.3, exceptions=(Exception,)):
    """
    실패 시 지수 백오프 + 랜덤 지연
    예: 3초 → 6초 → 12초 (+랜덤 0~0.3초)
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, max_retries + 1):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    wait = base_delay * (2 ** (attempt - 1))
                    wait += random.uniform(0, jitter)
                    logger.warning(f"⚠️ {func.__name__} 실패({attempt}/{max_retries}): {e} → {wait:.1f}s 후 재시도")
                    time.sleep(wait)
            raise
        return wrapper
    return decorator

# ───────────────────────────────
# 2️⃣ 대체 경로(Fallback)
# ───────────────────────────────
def fallback(primary, secondary):
    """기본 함수 실패 시 대체 함수 실행"""
    try:
        return primary()
    except Exception as e:
        logger.warning(f"⚠️ 주 경로 실패, 대체 경로로 전환: {e}")
        return secondary()

# ───────────────────────────────
# 3️⃣ 휴면 복구 루프 (일정 주기로 재실행)
# ───────────────────────────────
def recovery_loop(job, sleep_min=5):
    """무한 루프형 자동 복구"""
    while True:
        try:
            job()
            logger.info("✅ 정상 완료, 다음 스케줄까지 대기")
            break
        except Exception as e:
            logger.error(f"💥 오류 발생: {e} → {sleep_min}분 후 재시도")
            time.sleep(sleep_min * 60)

4. 실제 적용 예시

예시 ① — LLM 호출 안정화

from resilience import retry_with_backoff
from openai import OpenAI

client = OpenAI()

@retry_with_backoff(max_retries=4, base_delay=2)
def call_llm(prompt):
    resp = client.responses.create(
        model="o4-mini",
        input=prompt,
    )
    return resp.output_text

try:
    text = call_llm("오늘의 주요 뉴스 요약")
    print("✅ 결과:", text[:200])
except Exception as e:
    print("❌ 완전 실패:", e)

⚙️ retry_with_backoff 덕분에 일시적 네트워크 문제나 429 Rate limit에도 자동 재시도


예시 ② — 워드프레스 API 대체 경로

from resilience import fallback
import requests, os

def post_main():
    return requests.post(f"{os.getenv('WP_URL')}/wp-json/wp/v2/posts", timeout=5)

def post_backup():
    return requests.post("https://backup-blog.example.com/wp-json/wp/v2/posts", timeout=5)

response = fallback(post_main, post_backup)
print("응답 코드:", response.status_code)

⚡ 워드프레스 서버 장애 시 자동으로 백업 블로그로 전송


예시 ③ — 전체 파이프라인 자동 복구 루프

from resilience import recovery_loop
import subprocess

def pipeline():
    subprocess.check_call(["python", "orchestrator.py"])

# 5분 후 재시작
recovery_loop(pipeline, sleep_min=5)

💡 크론/스케줄러에 등록해두면, 오류 발생 후 자동 휴면 복구 가능


5. 장애 유형별 대응전략

장애유형탐지기준조치
네트워크 타임아웃응답 없음 > 15초백오프 재시도
API 429Rate limit 응답지수 백오프 후 재호출
5xx 서버오류응답 코드 500~599대체 경로(Fallback) 전환
데이터 포맷 오류JSONDecodeError 등예외 포착 → 로그 → 스킵
워드프레스 인증실패401/403재인증 or 키 회전 실행
LLM 오류(모델/토큰 초과)APIError / InvalidRequest입력 자르기 + 재시도

6. 백오프 알고리즘 요약

단계지연 공식설명
1base_delay * 2^(n-1)1, 2, 4, 8, 16초 등 기하급수 증가
2+ 랜덤 jitter과도한 동시 재시도 방지
3최대 지연 제한예: 60초 이상은 고정
4재시도 로그 남기기시각, 횟수, 예외 타입

☑️ “짧게 자주보다, 길게 간격 두고” 접근이 서버 보호에 효과적


7. 보강 팁 — “자동화의 실패도 자동으로 보고하라”

from common_ops import notify_webhook
from resilience import recovery_loop

def safe_pipeline():
    try:
        subprocess.check_call(["python", "orchestrator.py"])
    except Exception as e:
        notify_webhook(f"🔥 파이프라인 실패: {e}")
        raise

recovery_loop(safe_pipeline, sleep_min=10)

실패 시 슬랙 알림 + 10분 후 자동 재시작.
완전 무인 운영이 가능해진다.


8. 체크리스트

  • retry_with_backoff() 적용 완료
  • 예외 유형별 처리 분리 (429, 5xx, Timeout 등)
  • fallback() 대체 경로 등록
  • recovery_loop() 휴면 복구 루프 활성화
  • 알림(슬랙/메일) 연동
  • 실패/재시도 로그 구조화

9. 요약 한 줄

“오류가 나면 알아서 쉬고, 다시 일어나게 하라.”
백오프·대체·복구 루프만 있어도 시스템은 절대 멈추지 않는다 ✅


이전 강좌 👈 [운영#2] 보안·비밀관리(.env/키 회전/권한/로그 마스킹)
다음 강좌 👉 [운영#4] 성능 튜닝 & 확장 (멀티스레드·비동기·큐 기반 처리)

댓글 남기기

광고 차단 알림

광고 클릭 제한을 초과하여 광고가 차단되었습니다.

단시간에 반복적인 광고 클릭은 시스템에 의해 감지되며, IP가 수집되어 사이트 관리자가 확인 가능합니다.