← 목록

새벽에 서버가 혼자 재부팅됐다 (알림은 안 왔다)

#incident#monitoring#docker#systemd

낮에 마인크래프트 서버(모드팩 쪽)가 죽어있는 걸 발견했다. 서버에 들어가보니 자바 프로세스가 아예 없었음. 바닐라 쪽은 멀쩡히 돌고 있었다. 근데 제일 이상한 건 서버가 죽었으면 디스코드로 알림이 왔어야 하는데 아무것도 안 왔다는 것.

언제 죽었나

uptime을 보니 서버 머신 자체가 12시간 전, 그러니까 새벽 4시쯤 재부팅돼 있었다. 저널을 보면 4시 11분까지 평소처럼 로그를 찍다가 아무 종료 절차 없이 뚝 끊기고 4시 13분에 부팅 로그가 시작된다. last -x에도 shutdown 기록이 없다. 정상 종료가 아니었다는 얘기다.

바닐라 서버는 systemd 서비스로 등록해놔서 부팅하면서 알아서 살아났고, 모드팩 서버는 tmux에서 수동으로 띄우는 방식이라 그대로 죽어있던 거였음. 같은 사고에서 한쪽만 살아남은 이유가 이거다.

모니터링은 뭐 하고 있었나

이 서버에는 Prometheus + Alertmanager 스택이 있고 마크 서버 포트도 감시하고 있다. 12시간 죽어있었으면 알림이 열 번은 왔어야 한다. 왜 조용했지?

docker ps -a를 떠보니 모니터링 스택이 통째로 Exited (255) 상태였다. 13시간째.

에러 메시지는 이거였다. failed to bind host port 10.100.0.1:9093: cannot assign requested address. 보안 때문에 모니터링 UI들은 전부 WireGuard IP에만 바인딩해뒀는데, 그 wg0 인터페이스를 만들어주는 wg-easy도 도커 컨테이너다. 부팅 직후 도커가 컨테이너들을 되살리는 시점엔 wg0이 아직 없고, 그래서 거기 바인딩하는 애들만 실패한다. 도커는 이런 실패를 재시도하지 않고 그냥 포기함.

결과적으로 감시자가 감시 대상보다 먼저 죽어있었다. 나머지 컨테이너 20여 개는 0.0.0.0이나 LAN IP 바인딩이라 전부 무사했음. 하필 죽은 4개가 알림 파이프라인 전부였던 것.

docker start의 함정

죽은 컨테이너를 docker start로 올렸더니 상태는 Up인데 curl이 안 먹었다. inspect를 떠보니 포트 바인딩 설정은 있는데 실제 발행된 포트가 비어있음. 실패했던 네트워크 엔드포인트가 낡은 채로 남아서 그런 듯. compose로 --force-recreate 하니까 그제서야 정상으로 돌아왔다. docker start는 에러도 안 내고 조용히 이 상태가 되니까 더 고약하다.

수습

두 가지를 고쳤다.

하나는 모드팩 서버도 systemd 서비스로 이관. 이제 재부팅하면 알아서 올라오고 크래시 나도 10초 뒤 재기동된다. 여기서 함정이 하나 더 있었는데, 두 마크 서버가 같은 계정의 tmux를 쓰다 보니 새로 띄운 세션이 기존 서비스가 만든 tmux 서버에 얹혀서 그 서비스의 cgroup에 들어가버린다. 이러면 바닐라 서버를 systemctl stop 할 때 모드팩 서버까지 같이 죽는 구조가 됨. tmux 전용 소켓(-L)으로 분리해서 해결했다.

또 하나는 net.ipv4.ip_nonlocal_bind=1. 아직 존재하지 않는 IP에도 바인딩을 허용하는 커널 설정이다. wg0이 늦게 떠도 바인딩이 성공하니까 부팅 순서 문제 자체가 사라진다.

그래서 왜 재부팅됐는데?

혹시 내가 뭔가 걸어둔 게 있나 싶어서 다 뒤졌다.

  • crontab, cron.d 전부 reboot 계열 없음
  • unattended-upgrades 자동 재부팅 꺼져있음
  • systemd 타이머 17개 전부 logrotate 같은 일상 작업
  • kernel.panic=0이라 커널 패닉이었으면 재부팅이 아니라 멈춰있었어야 함

메모리 부족도 의심해봤는데 sysstat이 10분마다 남긴 스냅샷이 결정적이었다. 죽기 70초 전 기록을 보면 로드 0.89(12코어), 메모리 10GB 여유, OOM 없음. 놀고 있다가 죽었다는 얘기다... 소프트웨어 쪽 알리바이는 다 성립해서 남는 건 전원이나 하드웨어. 꺼졌다가 2분 만에 혼자 켜진 것도 BIOS의 전원 복구 자동 부팅 동작이랑 맞아떨어진다. 새벽 4시에 순간 정전이 있었던걸까.

남은 숙제

이번에 확실히 배운 것. 서버 자신이 죽는 사고는 서버 자신이 알릴 수 없다. 감시자가 장애 도메인 안에 같이 있으면 소용없는 거였음.

그래서 부팅 알림을 고려중이다. crontab @reboot으로 부팅할 때마다 디스코드에 "방금 재부팅됨" 한 줄 쏘는 방식. 이런 짧은 블립은 실시간으로 잡는 게 애초에 불가능하니까 사후 통보라도 받는 게 맞는 것 같다. healthchecks.io 같은 외부 감시 서비스도 후보인데 외부 의존이 하나 늘어나는 거라 고민중...

사실은...

모드팩 서버 systemd 등록을 처음부터 해놨으면 최소한 마크는 아무 일 없었을 것이다.

마치며

정전 한 방에 약한 고리가 세 개나 드러났다. 이만하면 싸게 배운 셈이다.

← 목록으로
오늘 0·전체 0