아무도 안 보고 있으면 소용없다 — 네트워크 감시 데몬을 만든 이유
와이파이가 잠깐 끊겼다가 반쪽짜리로 남아있었던 사고를 계기로, 집 네트워크를 대신 지켜보는 작은 프로그램을 하나 더 만들었습니다
퀀트봇 본체와는 완전히 분리된 작은 프로젝트를 하나 더 만들었습니다. 이름은 그냥 "네트워크 감시병" 정도의 뜻입니다. 하는 일은 단순합니다 — 집 네트워크가 이상해지면 대신 알아채고, 필요하면 스스로 고칩니다.
계기가 된 사고
어느 날 밤 집 와이파이가 15초 정도 끊겼습니다. 곧바로 같은 공유기에 다시 "접속 성공"까지 됐는데, 문제는 그 이후였습니다.
겉보기엔 연결된 상태인데 실제로는 데이터가 하나도 안 오가는 반쪽짜리 상태로 몇 시간 동안 그냥 방치됐습니다. 그러다 새벽에 우연히 다른 무선 채널로 넘어가면서 완전히 복구됐습니다. 끊긴 건 15초였는데, 피해는 4시간 45분이었습니다.
더 허탈했던 건, 운영체제는 이미 4분 만에 "지금 인터넷이 제대로 안 된다"는 걸 정확히 알고 있었다는 사실입니다. 상태를 낮춰서 계속 표시하고 있었는데, 아무도 그 신호를 보고 있지 않았을 뿐입니다.
왜 그냥 못 넘어갔나
이 사고로 밤새 자동으로 돌아가던 분석 작업 몇 개가 몇 시간씩 헛돌았습니다. 그런데 진짜 문제는 피해 규모가 아니라, 신호는 있었는데 아무도 안 읽었다는 구조였습니다.
사람이 매일 밤 로그를 지켜볼 수는 없으니, 이 역할을 대신할 무언가가 필요했습니다. 그래서 본체 프로젝트와는 완전히 분리된 별도 프로그램으로 만들기로 했습니다.
두 갈래로 감지한다
감지 방식은 두 가지를 같이 씁니다.
하나는 이번에 겪은 사고와 정확히 같은 패턴(연결은 됐다고 뜨는데 실제로는 데이터가 하나도 안 오가는 상태)을 빠르게 잡아내는 전용 경로입니다. 이 패턴은 정상적인 순간엔 절대 오래 지속되지 않기 때문에, 일정 시간 이상 계속되면 바로 문제로 판단합니다.
다른 하나는 그 밖의 모든 네트워크 이상을 더 보수적으로 잡아내는 일반 경로입니다. 실제로 외부와 통신이 되는지를 직접 확인해보고, 계속 실패하면 문제로 판단합니다. 링크 상태나 DNS 응답만 보고 판단하지 않는 이유는, 끊긴 상태에서도 캐시 때문에 DNS는 멀쩡하게 응답하는 경우가 실제로 있었기 때문입니다.
복구도 단계별로
문제를 확인하면 바로 가장 센 수단을 쓰지 않고, 약한 것부터 순서대로 시도합니다.
먼저 재접속을 유도해봅니다. 안 되면 무선 기능을 껐다 켭니다.
그래도 안 되면 더 낮은 층에서 한 번 더 같은 걸 시도합니다. 실제로 이번 사고를 재현해보니 두 번째 단계(무선 기능 껐다 켜기)가 1초 만에 문제를 해결했습니다.
여기에 국지적인 판단도 하나 더합니다. "연결은 됐는데 데이터가 안 나가는" 상태는 리셋이 잘 듣는 로컬 문제일 가능성이 높고, "데이터는 나가는데 외부 인터넷 자체가 안 되는" 상태는 리셋이 소용없는 상위 회선 문제일 가능성이 높습니다.
후자라면 괜히 반복해서 껐다 켜지 않고, 그냥 기다립니다.
지켜보되, 함부로 손대지 않는다
이 프로그램은 기본값이 "관측만" 하는 모드입니다. 아무 설정도 안 하면 이상을 감지하고 기록하고 알림만 보낼 뿐, 실제로 무선을 껐다 켜거나 뭔가를 재시작하지는 않습니다.
실제 조치를 허용하려면 명시적으로 스위치를 켜야 합니다. 그것도 하나가 아니라 세 개로 나눠뒀습니다 — 무선을 껐다 켜는 것, 그래도 안 되면 시스템을 재부팅하는 것, 원격 접속 프로그램만 죽었을 때 그것만 다시 살리는 것을 각각 따로 허용해야 합니다.
오작동하면 스스로를 다시 못 켤 수도 있는 조치라, 신중하게 나눴습니다. 지금은 아직 세 스위치 모두 꺼둔 채로, 실전에 켜기 전 마지막 리허설 단계입니다.
두 가지 다른 위험
복구 수단 중 재부팅은 특히 조심스럽게 다룹니다. 판단 기준은 하나입니다 — 지금 살아있는, 재부팅하면 죽는 작업이 있는가.
네트워크 자체가 완전히 죽은 상태라면, 어차피 그 위에서 돌아가던 자동매매·분석 작업도 이미 다 멈춰 있는 상태입니다. 이 경우엔 재부팅해도 추가로 죽일 게 없으니, 다른 수단이 다 실패했을 때 최후 수단으로 재부팅까지 갑니다.
반대로 네트워크는 멀쩡한데 원격 접속 프로그램만 죽은 경우엔 얘기가 다릅니다. 이때는 본체의 자동매매·분석이 실제로 돌아가고 있을 가능성이 높은 상태라, 재부팅은 무조건 금지하고 딱 그 프로그램만 다시 살리는 선에서 멈춥니다.
자기가 지키는 것에 기대지 않는다
설계 원칙을 하나 세워뒀습니다. 이 프로그램은 자기가 보호해야 할 대상에 의존하지 않는다는 겁니다.
본체 프로젝트의 코드를 가져다 쓰지 않고, 본체의 프로세스가 살아있어야만 작동하는 구조도 아닙니다. 운영체제 기본 기능과 최소한의 표준 도구만으로 혼자 돌아갑니다. 본체 쪽에서 이 프로그램의 상태를 읽어가는 건 괜찮지만, 반대 방향(이 프로그램이 본체에 기대는 것)은 원칙적으로 금지했습니다.
네트워크가 완전히 죽은 상황에서, 정작 그 상황을 감지해야 할 감시병이 본체와 함께 죽어있으면 아무 의미가 없기 때문입니다.
감시자도 감시받는다
이 프로그램 혼자 조용히 죽어버리는 경우도 대비해뒀습니다. 본체 프로젝트 쪽에 이미 있던 상태 점검 로직이, 이 감시병이 살아있다는 신호를 주기적으로 확인합니다.
신호가 일정 시간 이상 끊기면 그것도 별도로 경보가 갑니다. 서로가 서로를 지켜보는 구조입니다.
지금은 어디까지 왔나
만든 지 얼마 안 된 프로젝트라, 지금은 감지·복구 로직을 전부 코드로 짜고 결정론적인 시나리오로 검증하는 단계까지 왔습니다. 필요한 시스템 권한도 실제로 실행해보며 확인을 마쳤습니다.
다만 아직은 세 조치 스위치를 모두 끈 관측 모드로만 돌리고 있습니다. 진짜로 무선을 껐다 켜는 리허설을 한 번 해보고, 재부팅 후에도 이 시스템 전체가 스스로 다시 일어서는지까지 확인한 뒤에 실전 스위치를 켤 생각입니다.
단일 파일짜리 작은 프로그램으로 유지하는 것도 원칙으로 정해뒀습니다. 이 정도 역할에 테스트 프레임워크나 여러 모듈로 쪼개는 구조까지는 과합니다.
돌이켜보면 이번 사고에서 진짜 무서웠던 건 끊김 그 자체가 아니라 "아무도 몰랐다"는 사실이었습니다. 다음에 또 끊기면, 이번엔 사람 대신 이 프로그램이 먼저 알아챌 겁니다.