
무인 서버가 혼자 살아나게 — 복원력 5계층 설계
집이나 사무실에 둔 단일보드·미니 서버를 24/7로 돌리다 보면 결국 마주치는 질문이 있습니다. "내가 안 보고 있을 때 이게 죽으면 어떻게 되지?" 새벽 3시에 프로세스가 죽거나, 메모리가 폭주하거나, 정전 후 재부팅됐을 때 — 사람이 개입하지 않아도 스스로 살아나거나, 최소한 알려줘야 합니다.
여러 서비스를 무인으로 운영하며 정착한 건 계층 방어였습니다. 하나가 못 잡으면 다음 계층이 잡는 구조입니다.
왜 한 가지로는 부족한가
흔히 "restart: always 걸어두면 되지"라고 생각합니다. 맞지만 그건 프로세스가 죽었을 때만 해결합니다. 현실의 장애는 다양합니다.
- 프로세스는 살아있는데 응답을 멈춤(데드락·외부 연결 끊김)
- 메모리 폭주로 OOM 킬러가 엉뚱한 걸 죽임
- 호스트 자체가 다운(정전·커널 패닉) — 이러면 내부 감시도 같이 죽음
그래서 한 지점이 아니라 여러 고도에서 지켜봐야 합니다.
L0 — 프로세스 매니저 (자동 재기동)
가장 아래. systemd나 Docker의 restart: unless-stopped. 프로세스가 죽으면 즉시 다시 띄웁니다.
restart: unless-stopped # 재부팅 후에도 자동 기동, 단 수동 stop은 존중
always보다unless-stopped를 권합니다. 의도적으로 멈춘 걸 재부팅 때 되살리지 않기 때문입니다.
L1 — 앱 내부 자가복구
앱이 자기 의존성(DB·외부 API·소켓)의 건강을 스스로 체크하고, 끊기면 재연결합니다. 프로세스는 안 죽었지만 "멍해진" 상태를 L0는 못 잡습니다. 앱 안의 헬스 프로브가 잡습니다.
L2 — 워치독 (외부에서 프로세스 감시)
별도 프로세스가 주기적으로 heartbeat / 헬스 엔드포인트를 찔러보고, 응답이 없으면 재시작합니다. "살아있지만 응답 없음"을 잡는 계층입니다.
L3 — 리소스 감시
메모리 폭주·과열·디스크 풀을 감시합니다. 특히 메모리가 차면 OS의 OOM 킬러가 중요한 프로세스를 죽일 수 있어, 핵심 프로세스엔 oom_score_adj를 음수로 줘 보호합니다. 과열은 능동 팬 + 온도 알림으로.
L4 — 외부 데드맨 (가장 중요)
다른 기기에서 이 서버를 핑합니다. 왜 외부냐 — 서버 자체가 죽으면 L0~L3가 전부 같이 죽기 때문입니다. 내부 감시만 있으면 "죽었는데 아무도 모르는" 상태가 됩니다. NAS·다른 보드·외부 uptime 서비스 중 하나가 1분마다 핑 → 실패 시 알림.
가장 자주 빠지는 함정 — cron은 환경변수를 상속하지 않는다
복원력 스크립트를 cron으로 돌릴 때 반드시 겪는 함정입니다. cron 잡은 systemd 서비스의 Environment를 상속하지 않습니다. 그래서 알림 토큰 같은 환경변수가 없어, "조용히 알림이 안 가는" 최악의 상태가 됩니다 — 장애는 났는데 통지가 안 옴.
해법: cron 잡 헤더에 필요한 env를 명시적으로 주입하거나, 서비스의 환경 파일을 스크립트가 직접 source 하게.
정리 — 계층별 "무엇을 잡는가"
| 계층 | 잡는 장애 |
|---|---|
| L0 프로세스 매니저 | 프로세스 죽음 |
| L1 앱 자가복구 | 의존성 끊김(멍한 상태) |
| L2 워치독 | 살아있지만 응답 없음 |
| L3 리소스 감시 | 메모리 폭주·과열 |
| L4 외부 데드맨 | 호스트 전체 다운 |
핵심은 L4(외부 눈)를 빼먹지 않는 것, 그리고 알림 경로가 실제로 작동하는지 한 번 테스트하는 것입니다. 알림은 정작 장애 때 안 가면 없는 것과 같습니다.
실제 24/7 무인 서버 운영에서 정착한 패턴을 정리했습니다.