홈서버 자동 복구 1탄 - 커널이 죽으면 스스로 재부팅하게 만들기
일요일 저녁, 사이트가 안 열린다는 걸 알았을 때 서버는 이미 3시간 반째 멈춰 있었습니다. 전원은 켜져 있고 팬도 도는데 화면도 네트워크도 반응이 없는 상태라, 결국 전원 버튼을 길게 눌러 되살렸습니다. 그리고 그날 새벽에 한 번 더 같은 일이 벌어졌습니다. 이 글은 그 밤 이후 홈서버를 "죽어도 스스로 살아나게" 만든 과정의 1탄입니다.
원인 찾기 - 부팅 기록에 다 남아 있다
서버가 멈췄다 되살아나면 언제, 왜 죽었는지부터 확인해야 합니다. systemd 리눅스라면 journalctl(시스템 로그를 모아 보여주는 기본 명령)이 부팅 단위로 기록을 관리하기 때문에, 명령 두 개면 사고 시각과 죽기 직전 마지막 로그를 볼 수 있습니다.
# 부팅 기록 목록 - 각 줄의 끝 시각이 곧 "죽은 시각"
journalctl --list-boots
# 지난 부팅의 마지막 로그 (-b -1 = 한 부팅 전, -e = 끝부분으로 이동)
journalctl -b -1 -e
제 경우 범인은 USB 랜카드였습니다. 드라이버(r8152)가 전송 큐 멈춤(transmit queue timeout)을 일으키며 커널 오류로 번졌는데, 온보드 랜포트가 고장 나 USB 랜카드를 쓸 수밖에 없는 서버라 부품 교체로는 답이 없었습니다. 그런데 로그를 보다가 진짜 문제를 깨달았습니다. 크래시 자체는 한순간이었고, 3시간 반의 다운타임 대부분은 죽은 서버가 "사람이 올 때까지 그냥 기다린 시간"이었다는 점입니다.
리눅스 기본값은 "죽으면 영원히 대기"
리눅스 커널은 패닉(복구 불가능한 치명적 오류) 상태가 되면 기본적으로 재부팅하지 않고 그 자리에 멈춰 있습니다(kernel.panic=0). 데이터센터라면 곧 누군가 콘솔을 보러 오겠지만, 집에 둔 서버는 아무도 모릅니다. 설정 파일 하나면 이 기본값을 바꿀 수 있습니다.
# /etc/sysctl.d/99-autoreboot.conf
# 패닉 10초 후 자동 재부팅 (기본값 0 = 영원히 멈춰 있음)
kernel.panic = 10
# oops(치명적 커널 오류)도 패닉으로 처리
kernel.panic_on_oops = 1
# 로그도 못 남기고 통째로 얼어붙는 유형(락업)도 패닉으로 전환
kernel.hardlockup_panic = 1
kernel.softlockup_panic = 1
# 적용: sudo sysctl --system
# 확인: sysctl kernel.panic <- 10이 나오면 성공
처음에는 위의 두 줄만 넣었습니다. 그런데 바로 다음 날 밤, 이번에는 오류 로그조차 남기지 못하고 통째로 얼어붙는 유형(하드락업)이 와서 또 전원 버튼을 눌렀습니다. 그래서 아래 두 줄을 추가했습니다. 커널에 내장된 감시 장치가 CPU가 몇 초째 반응이 없는 것을 감지하면 강제로 패닉을 일으켜, 위의 자동 재부팅 경로에 태우는 설정입니다.
7월 30일 새벽, 설정이 일했다
그리고 7월 30일 새벽 1시 39분, 커널이 또 죽었습니다. 마지막 로그는 "traps: PANIC: double fault" 한 줄. 그런데 부팅 기록을 보니 35초 뒤 서버가 스스로 다시 켜져 있었습니다. 저는 자고 있었고, 아침에 로그를 보고서야 죽었었다는 걸 알았습니다. 같은 종류의 사고가 3시간 반짜리 다운에서 35초짜리로 줄어든 겁니다.
짝을 맞출 것이 하나 있습니다. 자동 재부팅은 부팅 후 서비스가 스스로 켜질 때만 의미가 있으니, 웹 서버든 게임 서버든 systemd 등록(enable)이 되어 있는지 함께 확인하세요. 그리고 이 설정이 잡는 건 어디까지나 "커널이 죽는" 유형입니다. 서버는 멀쩡한데 랜카드만 조용히 먹통이 되는 날은 패닉이 없으니 재부팅도 없습니다. 2탄에서는 이 유형을 잡는 워치독 스크립트(1분마다 스스로 점검해 단계적으로 복구)를 다룹니다.