홈서버 자동 복구 2탄 - 커널은 멀쩡한데 네트워크만 죽었을 때
1탄에서는 커널이 죽으면 스스로 재부팅하는 설정을 다뤘습니다. 그런데 이 방법에는 구멍이 하나 있습니다. 제 서버의 단골 고장은 USB 랜카드가 조용히 먹통이 되는 것인데, 이때 커널은 멀쩡히 살아 있어서 패닉도 재부팅도 일어나지 않습니다. 서버는 돌아가는데 밖에서 보면 죽은 것과 똑같고, 네트워크가 끊겼으니 알림조차 못 나갑니다. 이번 글은 이 유형을 잡는 워치독(주기적으로 상태를 확인하다 이상하면 개입하는 감시 장치)입니다.
재부팅은 최후의 수단 - 덜 아픈 순서대로
핵심은 서버가 1분마다 스스로 안부를 확인하는 것입니다. 공유기(게이트웨이)에 ping을 보내 응답이 없으면 고장으로 판정하고, 덜 아픈 방법부터 차례로 시도합니다. 1단계는 USB 랜카드를 소프트웨어로 뽑았다 꽂기(재바인드) - 성공하면 중단 30초 정도로 끝나고 재부팅이 없습니다. 2단계는 드라이버(랜카드를 움직이는 커널 프로그램)를 내렸다 다시 올리기 - 약 1분 중단. 3단계 재부팅은 둘 다 실패했을 때만 씁니다.
순서만큼 중요한 게 안전장치입니다. 부팅 직후 3분은 네트워크가 아직 올라오는 중일 수 있어 판정을 유예하고, 부팅한 지 15분이 안 됐으면 3단계 재부팅을 보류합니다 - 이게 없으면 고장이 재부팅으로 안 낫는 날 무한 재부팅 루프에 빠집니다. 복구 후에는 DHCP(공유기가 IP를 자동으로 나눠주는 방식)를 강제로 다시 받게 합니다. 링크만 올리면 IP 없이 붙어 있는 어정쩡한 상태가 될 수 있어서요. 알림은 복구가 끝난 뒤에 보냅니다 - 끊겨 있는 동안은 어차피 못 보내니까요.
#!/bin/bash
# 1분마다 실행. 게이트웨이 응답이 없으면 단계적으로 복구한다. (축약판)
NIC="eth0" # 랜카드 이름 (ip link 로 확인)
DRIVER="r8152" # 랜카드 드라이버 (ethtool -i eth0 으로 확인)
GW=$(ip route | awk '/^default/{print $3; exit}')
alive() { ping -c3 -W2 "$GW" >/dev/null 2>&1; }
# 부팅 직후 3분은 판정 유예 (네트워크가 올라오는 중일 수 있음)
[ "$(awk '{print int($1)}' /proc/uptime)" -lt 180 ] && exit 0
alive && exit 0
# 1단계: USB 재바인드 (성공 시 중단 약 30초, 재부팅 없음)
USBID=$(basename "$(readlink -f "/sys/class/net/$NIC/device")")
echo "$USBID" > "/sys/bus/usb/drivers/$DRIVER/unbind"; sleep 3
echo "$USBID" > "/sys/bus/usb/drivers/$DRIVER/bind"; sleep 15
networkctl reconfigure "$NIC" # DHCP 다시 받기
alive && exit 0
# 2단계: 드라이버 리로드 (중단 약 1분)
modprobe -r "$DRIVER"; sleep 3; modprobe "$DRIVER"; sleep 20
networkctl reconfigure "$NIC"
alive && exit 0
# 3단계: 재부팅 - 부팅 15분이 안 지났으면 보류 (재부팅 루프 방지)
[ "$(awk '{print int($1)}' /proc/uptime)" -lt 900 ] && exit 0
systemctl reboot --force
1분마다 돌리기 - systemd 타이머
위는 뼈대만 남긴 축약판이고, 로그 기록과 복구 후 웹훅 알림, IP 변경 감지까지 붙은 전체 스크립트는 GitHub에 올려뒀습니다. 반복 실행은 cron으로도 되지만 systemd 타이머를 썼습니다. 파일 두 개를 만들고 타이머만 켜면 되고, 실행 이력과 실패를 systemctl 명령으로 확인할 수 있는 게 장점입니다.
# /etc/systemd/system/net-watchdog.service
[Unit]
Description=Network watchdog
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/net_watchdog.sh
# /etc/systemd/system/net-watchdog.timer
[Unit]
Description=Run network watchdog every minute
[Timer]
OnCalendar=*:*:00
[Install]
WantedBy=timers.target
# 적용
# systemctl daemon-reload && systemctl enable --now net-watchdog.timer
2주 운영 기록 - 출동 0회도 성과다
이 워치독은 설치하고 2주가 지나도록 아직 한 번도 출동하지 않았습니다. 함께 적용한 드라이버 완화 설정(전송 오프로드 끄기, USB 절전 끄기) 덕에 랜카드가 먹통이 되는 일 자체가 드물어진 영향입니다. 보험이 그렇듯 안 터지는 게 최선이고, 터지는 날엔 늦어도 1분 안에 복구가 시작됩니다. 반대로 같은 기간 커널이 죽는 유형의 사고는 실제로 또 있었고, 그쪽은 1탄의 자동 재부팅이 잡아냈습니다.
이렇게 고장 유형별 분업이 만들어졌습니다. 커널이 죽으면 1탄의 sysctl 설정이 재부팅하고, 네트워크만 죽으면 이번 워치독이 단계적으로 살립니다. 남은 구멍은 하나, 하드웨어가 통째로 못 일어나는 날입니다. 다음 글에서는 서버가 아예 안 켜져도 사이트는 계속 열리게 하는 무료 클라우드 대기 서버(페일오버) 구성을 다룰 예정입니다.