NVR 페일오버는 녹화 중이던 장비가 멈췄을 때 다른 장비가 그 카메라의 수집과 녹화를 이어받는 기능이다. NOX는 여러 대의 운영 장비(primary)를 한 대의 대기 장비(standby)가 백업하는 n:1 구조로 이 기능을 만들었다.
용어는 제품과 업계에서 쓰는 것을 그대로 쓴다. standby가 죽은 primary의 역할을 넘겨받는 것이 takeover, primary가 복구된 뒤 원래대로 되돌리는 것이 failback, 장애 기간에 standby가 녹화해 둔 영상을 primary 타임라인으로 되돌려 넣는 것이 record-back이다.
이 글은 기능 소개가 아니라 개발 기록이다. 무엇을 보장하기로 정했는지, 구현하면서 무엇이 깨졌는지, 그리고 아직 화면에 붙어 있는 베타 배지를 떼기 위해 무엇을 해왔는지를 정리한다. 본문에 적은 내용은 저장소의 설계 문서와 커밋 기록, 그리고 실제로 돌아가는 장비에서 확인한 사실에 근거한다.
출발점: 업계가 이미 답을 정해 둔 항목들
설계보다 먼저 한 일은 상용 VMS·NVR 6종의 공식 문서를 읽고 항목별로 비교표를 만드는 것이었다. 페일오버는 고객이 "다른 제품은 어떻게 하느냐"고 반드시 묻는 기능이고, 근거 없이 다르게 만들면 그 차이를 전부 설명해야 한다.
| 제품 | 장애 감지 | 제조사가 밝힌 takeover 시간 | 장애 기간 영상 처리 |
|---|---|---|---|
| Milestone XProtect | standby가 0.5초 주기 폴링, 2초 무응답이면 판정 | cold standby 5초 + 엔진 기동 + 카메라 접속 | 자동 병합. 병합 중 해당 구간 열람 불가 |
| Genetec Security Center | 중앙 Directory가 관장 | 60초 이내, 녹화 공백 최대 5초 | 복구 후 복사, 또는 평소부터 이중 녹화 |
| Nx Witness 계열 | 서버 간 keep-alive | 약 1분(녹화 갭 약 30초) | 넘겨받은 서버에 그대로 남음, 회수 없음 |
| Hikvision N+1 | spare가 상시 감시(주기 미공개) | 미공개 | 자동 회수, 동시 1대 제한 |
| Dahua N+M | slave·스케줄러, 90~120초 판정 | 90~120초 | 회수 지원, 전송 속도 3단 제어 |
| Exacq exacqVision | 중앙 관리 서버, "녹화가 멈추면" 트리거 | 설정한 타임아웃 + α | failback 때 회수, 진행률·일시정지 제공 |
여기서 세 가지가 분명해졌다.
첫째, 설정은 미리 복제해 두는 것이 표준이다. 장애 시점에 중앙 관리 서버에서 설정을 받아 오는 방식은 그 관리 서버가 또 하나의 단일 장애점이 된다. Milestone조차 이 방식을 hot standby(사전 동기화)로 보완한다.
둘째, 감지에서 takeover까지의 녹화 유실은 모든 제품이 허용한다. 유실이 0인 방법은 평소부터 두 장비에 같이 녹화해 두는 것뿐이고, 그건 저장소와 대역폭을 두 배로 쓰는 다른 기능이다.
셋째, 가상 IP(IP takeover)는 비주류다. 6종 중 Dahua만 쓰고, 그 대가로 전 장비를 같은 L2 세그먼트에 두라는 제약을 받는다. 나머지는 전부 애플리케이션 레벨에서 재연결한다.
목표: 숫자로 확정한 것
조사해 보니 장애 판정은 2초(Milestone)에서 120초(Dahua)까지, takeover 완료는 15~60초에 걸쳐 있었다. NOX의 목표는 그 사이에 뒀다.
| 항목 | 목표 | 근거 |
|---|---|---|
| 장애 판정 | 약 10초 (heartbeat 2초 × 연속 실패 5회) | 어플라이언스 진영(90~120초)을 크게 앞서고, 오판 위험이 커지는 2초대는 피한다 |
| takeover 완료 | 트리거 → 첫 세그먼트 30초 이내 | 엔터프라이즈 VMS 진영과 동급 |
| 동시 다중 장애 | 라이선스 채널 용량이 허용하는 만큼 동시 takeover | 1대만 넘겨받는 방식(Hikvision·Milestone)보다 한 단계 앞선다 |
| 장애 기간 영상 | failback 후 primary 타임라인으로 전량 record-back | 병합 중 열람 제한 없이 |
| failback | 기본 수동, 자동은 선택 | failback 순간에 한 번 더 발생하는 단절을 운영자가 통제 |
설계에서 먼저 확정한 여섯 가지
1. 판정 기준은 "살아 있나"가 아니라 "녹화되고 있나"
ping 응답은 장비가 켜져 있다는 사실만 알려 준다. 디스크가 빠졌거나 파티션이 마운트되지 않았거나 쓰기가 실패하는 장비는 ping에 잘 응답하면서 아무것도 저장하지 않는다. 그래서 heartbeat 응답에 "지금 저장할 스트림이 있는데 실제로 저장되고 있는가"를 함께 실었다. 활성 녹화가 없는 정상 대기 상태는 장애로 치지 않는다. 이 구분을 놓치면 카메라를 한 대도 등록하지 않은 신품 장비가 즉시 장애로 판정된다.
2. IP takeover는 하지 않는다
standby는 죽은 장비의 주소를 물려받지 않는다. 대신 스냅샷에 들어 있는 카메라 주소로 직접 다시 연결한다. NOX가 카메라 영상을 가져오는 방식이 순수 pull(ONVIF/RTSP)이라 가능한 선택이다. 카메라 쪽 설정은 손대지 않아도 된다. 대가는 하나다. standby가 그 카메라들과 같은 네트워크에서 도달 가능해야 한다. 이 조건은 감추지 않고 화면에 드러냈다. takeover 직후 대상 카메라에 실제로 닿는지 점검해서, 닿지 않는 카메라가 있으면 경고 알림을 낸다. 다만 경고일 뿐 takeover를 막지는 않는다. 절반만 녹화되는 것이 하나도 녹화되지 않는 것보다 낫다.
3. standby는 자기 네트워크부터 의심한다
standby가 직접 감시하는 구조에는 특유의 위험이 있다. primary는 멀쩡한데 standby 쪽 네트워크만 끊긴 경우, standby는 "primary가 죽었다"고 판단하고 같은 카메라에 붙어 영상을 가져가기 시작한다. 그래서 장애라고 판단하기 전에 중재 IP(기본 게이트웨이 등)에 닿는지부터 확인한다. 중재 IP에 닿지 못하면 "내 쪽 네트워크 문제"로 보고 판단을 미룬다. 기본 게이트웨이가 여러 개인 장비에서는 어느 쪽을 기준으로 삼을지 애매해지므로, 중재 IP를 직접 지정하는 설정을 두고, 지정하지 않은 채 게이트웨이가 여러 개 잡히면 경고를 띄운다.
4. 카메라 서비스는 페일오버를 모른다
페일오버는 federation 서비스 안에만 존재한다. 카메라 서비스와 녹화 서비스는 takeover라는 개념 자체를 모르고, 평소와 똑같이 "카메라를 등록하고 녹화를 시작하라"는 요청을 받을 뿐이다. 이 경계를 지키면 페일오버에서 문제가 생겨도 평소 녹화 경로까지 번지지 않는다.
대신 이 경계를 지키느라 생긴 빈틈이 있다. standby로 전환한 장비에서 로컬 카메라 추가를 막는 일이 화면에서만 걸러진다. 그래서 서버 쪽에 확인 절차를 두 겹 더 뒀다. 보호 대상 등록 시점에 "standby 라이선스 채널 − 로컬 녹화 채널"로 여유 용량을 계산해 부족하면 등록을 거부하고, 실제 takeover 시점에는 트랜잭션 안에서 채널 수를 다시 세어 초과하면 일부만 takeover하지 않고 통째로 거부한다. 화면에서 막는 것은 안내일 뿐이고, 용량이 되는지는 결국 서버가 판단한다.
5. failback은 삭제가 아니라 비활성화
takeover했던 카메라를 failback 시점에 지우면, 그 카메라로 녹화한 장애 기간 영상이 함께 사라질 위험이 생긴다. 그래서 failback 경로에서 호출하는 인터페이스에는 삭제 메서드를 아예 만들지 않았다. failback은 넘겨받았던 카메라와 녹화를 비활성으로 돌려놓을 뿐이다. 같은 장비를 나중에 다시 takeover할 때는 비활성 상태로 남아 있던 항목을 다시 켜므로 중복 등록도 생기지 않는다. 실제로 지우는 일은 그 카메라에 남은 영상이 전부 없어진 뒤에 정리 워커가 한다.
6. 라이선스는 하드웨어에 묶여 있다
NOX 라이선스는 머신 ID와 제품 UUID에 결합돼 있어 다른 장비로 옮길 수 없다. 그래서 standby도 자기 라이선스가 필요하고, 그 채널 수가 보호 대상 이상이어야 한다. 무료 대기 라이선스를 두는 선택지도 있었지만 두지 않았다. 대신 한 대의 standby가 용량이 허용하는 한 여러 대를 동시에 takeover하도록 만들어서, 장비 한 대의 라이선스로 여러 대를 보호할 수 있게 했다. 등록 채널 합계가 용량을 넘는 오버커밋 구성도 허용한다. "모든 primary가 동시에 죽지는 않는다"는 전제로 용량을 아껴 쓰는 정상적인 운용이기 때문이다. 대신 대시보드에 경고를 띄운다.
구현하면서 깨진 것들
설계 문서를 쓴 날은 2026년 7월 2일, 기반 계층부터 record-back까지의 초기 구현을 올린 것이 7월 3~4일이다. 베타 배지를 붙인 날은 7월 8일이다. 초기 구현과 베타 배지 사이의 나흘이 아니라, 그 뒤로 두 달 반이 실제 작업이었다.
동시에 두 대를 takeover할 수 있었다. 그리고 테스트가 그걸 감췄다
초기 구현은 "이미 takeover 중이면 다른 takeover를 거부한다"는 조건을 UPDATE 문 하나에 담았다. 코드 리뷰에서 이것이 PostgreSQL 기본 격리 수준에서 write-skew에 취약하다는 지적이 나왔다. 서로 다른 두 보호 관계를 동시에 takeover하면 두 문장이 각각 독립 스냅샷을 보고 양쪽 모두 "takeover 중인 것 없음"으로 평가해 둘 다 성공할 수 있었다.
더 나쁜 것은 그걸 검증하기로 돼 있던 동시성 테스트였다. 테스트가 사용하는 가짜 저장소가 뮤텍스로 원자성을 흉내 내고 있었다. 프로세스 안에서 직렬화되므로 실제 데이터베이스 경쟁은 재현되지 않았고, 테스트는 언제나 통과했다. 결함을 막아야 할 테스트가 결함을 덮고 있었던 셈이다.
수정은 부분 유니크 인덱스로 데이터베이스 레벨에서 유일성을 강제하고, 중복 키 오류가 나면 거부된 것으로 처리하는 것이었다. 그리고 실제 데이터베이스에 붙는 동시성 통합 테스트를 추가했다. 이후 이 조건은 "전역 1건"에서 "용량이 허용하는 만큼"으로 확장됐지만, 채널 수를 한 번에 확인해 초과를 막는다는 점은 그대로 뒀다.
takeover는 성공했는데 화면에서 녹화만 사라졌다
takeover 후 카메라는 보이는데 녹화 항목이 전부 없어지는 현상이 있었다. 원인은 권한 쪽이었다. 카메라와 녹화는 생성될 때 기본 리소스 그룹에 매핑되는데, takeover 경로만 이 매핑을 부르지 않았다. 카메라 가져오기는 매핑을 호출하고 녹화의 정상 생성 경로도 호출하는데, takeover용 설정 묶음 가져오기 경로에서만 빠져 있었다.
결과적으로 녹화 설정은 데이터베이스에 그대로 있고 파이프라인도 정상 동작하는데, 권한 필터가 전부 걸러내서 화면에서만 사라지는 형태가 됐다. takeover 같은 예외 경로가 정상 경로와 똑같은 뒷처리를 거치는지 따로 확인해야 한다는 교훈이었고, 이후로도 같은 자리에서 몇 번 더 걸렸다.
재부팅을 장애로 읽었다
standby는 2초마다 primary의 상태를 확인하고 5회 연속 실패하면 장애로 본다. 그러면 유지보수 모드 진입, 소프트웨어 업데이트, 재부팅, 시스템 종료가 전부 장애와 똑같이 보인다. 자동 takeover를 켠 환경이라면 관리자가 재부팅 버튼을 누른 10초 뒤에 takeover가 시작된다.
해결은 primary가 계획된 작업을 시작할 때 standby에 먼저 알리는 것이다. 전용 통신로를 새로 만들지 않고, standby가 보내는 heartbeat의 응답에 현재 계획 작업을 실어 보내는 방식을 택했다. 재부팅·종료·유지보수 진입·업데이트 요청은 standby가 이 값을 받아갈 때까지 최대 6~8초 기다린 뒤 진행한다. 보호 중인 장비에서 재부팅 버튼 응답이 몇 초 늦어지는 이유가 이것이다.
여기서 결정이 하나 더 필요했다. 유예를 언제 풀 것인가. 시간으로 끊으면(예: 30분) 그 시간 안에 복구되지 않는 작업에서 잘못된 takeover가 다시 발생한다. 그래서 기간 제한을 두지 않고 primary가 정상으로 안정화될 때까지 기다리되, 계획 작업이 끝났는데도 녹화 가능 상태로 돌아오지 않으면 10분 뒤 감시를 강제로 재개한다. 결함을 유예로 덮지 않기 위해서다. 반대로 유예가 6시간 넘게 지속되면 "감시가 꺼져 있다"는 경고를 따로 발행한다.
record-back이 "완료"라고 말하면서 일부를 버렸다
장애 기간에 standby가 녹화한 영상을 failback 후 primary 타임라인으로 옮기는 기능(record-back)에서 가장 큰 결함이 나왔다. 실제 운영 장비에서 보니 적지 않은 세그먼트가 크기 상한에 걸려 조용히 버려졌는데, 실패가 남은 채 끝난 작업이 초록색 "완료" 배지를 달고 있었다.
원인은 두 겹이었다. 전송 워커가 세그먼트 전체를 메모리에 올린 뒤 보내고 있었고, 그 때문에 메모리를 지키려고 걸어 둔 크기 상한이 사실상 데이터를 버리는 기준선이 됐다. 게다가 작업이 끝날 때 붙는 상태에 "일부 실패"가 없어서, 실패가 남은 작업도 그냥 "완료"였다. 완료로 표시된 작업에는 재시도 버튼이 없으므로 사용자는 복구 수단조차 볼 수 없었다.
수정은 세 갈래였다.
- 전송을 메모리 버퍼 없이 스트림 그대로 흘려보내도록 바꿨다. 버퍼링의 유일한 이유였던 "해시가 데이터보다 먼저 와야 한다"는 제약은 원본 쪽이 해시를 미리 제공하는 것으로 풀었다.
- 크기 상한은 더 이상 메모리 보호 장치가 아니게 됐다. 보내는 쪽은 초과해도 버리지 않고 경고만 남기며, 진짜 상한은 밖에서 들어오는 데이터를 직접 받는 수신 측에 둔다.
- "일부 오류로 완료"라는 상태를 새로 만들었다. 완료는 "하나도 빠뜨리지 않고 끝났다"는 뜻으로 좁아졌고, 실패가 남은 작업에는 경고 배지와 재시도 버튼이 붙는다. 이미 완료로 기록돼 버린 과거 작업도 거슬러 올라가 상태를 다시 매겼다. 그러지 않으면 정작 피해를 본 작업만 재시도 버튼을 못 보게 된다.
record-back한 영상이 도착하자마자 지워졌다
그다음에 나온 결함이 더 고약했다. record-back으로 옮기는 영상은 장애 기간의 과거 시각을 가진다. 그 시각이 primary의 보관 기간을 이미 벗어났다면, primary의 정리 워커가 도착하는 족족 지운다. 실제로 확인해 보니 정상 수신했다고 응답한 세그먼트 대부분이 얼마 지나지 않아 사라져 있었다.
게다가 마지막 세그먼트가 지워질 때 빈 트랙과 세션까지 같은 트랜잭션에서 삭제되고, 그게 수신 처리 중간에 끼면 외래 키 위반으로 500 오류가 났다. 처음에는 이 500이 문제로 보였지만, 그건 증상이었다. 더 위험한 것은 record-back 후 원본 삭제가 기본값이라는 점이었다. 오류만 고치면 그 경로가 열려서 "전송에 성공했는데 양쪽 어디에도 없는" 상태가 된다. 500 오류가 우연히 방어 역할을 하고 있었던 것이다.
수정은 primary에게 지금 보내면 지워지지 않고 남는 가장 이른 시각(보관 기간으로 계산한 시각과 실제로 남아 있는 가장 오래된 영상의 시각 중 나중 쪽)을 물어보는 기능을 만들고, 그보다 오래된 구간은 보내기 전에 건너뛰는 것이었다. 여기서 원칙을 하나 정했다. 그 시각을 알아내지 못하면 건너뛰지 않는다. 건너뛰는 쪽이 데이터를 버리는 선택이라, 모를 때의 기본 동작이 될 수 없다. 같은 이유로, 그 시각보다 오래된 구간이 섞인 세션은 원본을 지우지 않는다. primary가 받아 주지 않는 구간은 standby가 유일한 사본이기 때문이다.
어떤 정리 경로에도 걸리지 않는 잔재
takeover로 받아 온 녹화를 정리하는 워커는 두 개의 경로를 돌고 있었는데, 둘 다 카메라를 기준으로 돌고 있었다. 그래서 카메라에 연결되지 않은 채로 들어온 녹화 항목은 두 경로를 모두 빠져나갔다. 카메라가 없으니 삭제 연쇄에도 걸리지 않았고, 폐기 기능도 세션만 지우고 녹화 행은 건드리지 않았다. 결과적으로 어떤 경로로도 지워지지 않는 잔재가 됐고, 한 운영 장비의 탐색기에 이 잔재가 쌓인 것을 사용자가 발견해 제기했다.
수정은 카메라가 아니라 어느 장비에서 넘겨받았는지를 기준으로 도는 세 번째 정리 경로를 만드는 것이었다. 다만 takeover 직후의 정상 데이터가 잔재와 모양이 똑같아서(카메라 미연결 + takeover 출처 표시 + 세션 없음), takeover 중인 보호 관계를 정리 대상에서 배제하는 것이 유일한 구분 수단이었다. 삭제 조건은 호출하는 쪽에 맡기지 않고 삭제 쿼리 안에 직접 넣었다.
베타를 떼기 위해 해온 일
위 결함들을 고친 것은 출발점이다. 베타 배지를 떼려면 "고쳤다"가 아니라 "다음에 같은 일이 생겨도 사용자가 빠져나올 수 있다"가 필요했다.
1. 탈출구를 먼저 만든다. record-back 작업은 일시정지·재개·재시도가 가능하다. record-back을 포기하고 넘겨받아 녹화한 원본을 폐기하는 경로도 만들었다. 라이선스가 잠긴 상태에서도 failback·보호 해제·record-back 일시정지·감시 일시정지는 항상 허용한다. 되돌리고 멈추는 동작은 잠그지 않는다는 원칙이다.
2. 상태를 정직하게 표기한다. 진행률을 "옮긴 데이터 양"과 "처리한 세그먼트 수"로 나눠 표시한다. 하나로 합치면 "완료인데 69%" 같은 표시가 나온다. 라이브 영상 전환도 마찬가지다. takeover 시 라이브 소스는 자동으로 standby로 옮겨 가지만 무중단은 아니다. 수 초 끊겼다가 자동 재연결된다. 그래서 화면 문구를 "자동 전환, 수 초 내 재연결"로 쓰고, "무중단"이나 "seamless"로 잘못 표기하지 못하도록 회귀 테스트를 걸어 뒀다.
3. 계획된 작업과 장애를 구분한다. 앞서 설명한 자동 유예다. 통지가 실패하면 primary 쪽에 경고를 띄워 관리자가 수동으로 정지할 수 있게 했다. 보호를 해제하거나 연결을 끊을 때는 상대 장비에 철회를 통지해서, "보호받고 있습니다"라는 안내가 영구히 남지 않게 했다. 통지가 실패해도 해제는 진행하되, 그 경우 상대 장비는 감시 신호 두절 경고로 그 사실을 드러낸다.
4. 권한을 쪼갠다. 장비 간 통신 권한을 페일오버 감시용, 상태 조회용, record-back용으로 분리했다. 통합 검색 화면이 takeover 상태를 조회할 때는 상태 조회 권한만 받으므로 카메라 자격증명이 들어 있는 설정 스냅샷에 접근할 수 없다. 스냅샷은 항상 AES-256-GCM으로 암호화해 저장하고, 암호화 키를 쓸 수 없는 상태에서는 등록과 스냅샷 자체를 거부한다. record-back으로 옮기는 영상은 복호화 없이 암호화된 상태 그대로 이동하며, 세션 키만 전송 구간에서 공개키 봉투로 한 번 더 봉인한다.
5. 사후에 추적할 수 있게 한다. 등록·해제·takeover·failback·정리 삭제가 감사 로그에 남는다. 자동으로 실행된 takeover와 failback은 수행 주체가 system으로 기록되어 관리자의 수동 조작과 구분된다. 감시를 일시 정지한 구간과 그 주체도 남는다. 계획 작업 통지가 상대에게 전달됐는지도 재부팅·종료 감사 로그 상세에 함께 기록한다. 감시가 꺼져 있던 시간을 나중에 확인할 수 없으면, 페일오버는 믿고 맡길 수 있는 기능이 되지 못한다.
6. 테스트를 붙인다. 현재 파일 이름에 failover가 들어간 Go 테스트 파일이 61개, 그 안의 테스트 함수가 587개다. 여기에는 앞서 뮤텍스로 흉내 냈던 원자성을 실제 데이터베이스에 붙어 검증하는 동시성 테스트, 새로 만든 파괴적 경로의 인증 회귀 테스트, 배포 파일 양쪽의 설정 동기화 테스트가 포함된다. 이 기능 때문에 추가한 데이터베이스 마이그레이션은 14건이고, 그중 절반이 베타 배지를 붙인 뒤에 들어갔다.
이걸로 확보한 안정성
| 상황 | 페일오버 이전 | 현재 |
|---|---|---|
| 장비 전원·하드웨어 고장 | 복구까지 전 채널 녹화 중단 | 약 10초 판정 후 takeover, 목표 30초 내 녹화 재개 |
| 디스크·스토리지 고장 | 장비는 살아 있어 아무도 모름 | "녹화 가능 상태"를 감시하므로 감지 대상 |
| 장애 기간 영상 | 존재하지 않음 | standby에 녹화 후 failback 뒤 record-back으로 primary 타임라인에 편입 |
| 통합 검색·재생 | 그 장비의 채널이 통째로 사라짐 | takeover 감지 후 standby로 자동 재라우팅(목표 15초 이내) |
| 유지보수·업데이트 | (해당 없음) | 계획 작업 통지로 자동 유예, 완료 후 자동 재개 |
| 동시 다중 장애 | (해당 없음) | 용량이 허용하는 만큼 동시 takeover, 모자라면 알림을 내고 용량이 생기면 이어서 takeover |
그중 현장에서 가장 체감되는 것은 디스크 장애다. 상주 인력이 없는 현장에서 가장 흔한 사고는 장비가 꺼지는 것이 아니라, 장비는 켜져 있는데 녹화가 되지 않는 상태로 몇 주가 지나가는 것이다. ping 기반 감시로는 이걸 잡지 못한다.
보장하지 않는 것도 같이 적어 둔다.
- 죽은 장비의 과거 영상은 대신 보여줄 수 없다. 각 장비의 녹화는 그 장비의 로컬 디스크에만 있다. standby는 takeover 이후 자신이 녹화한 구간만 제공한다. 공유 스토리지를 쓰지 않는 한 업계 공통의 구조적 한계다.
- 판정과 takeover 사이에 녹화 공백이 생긴다. 이 구간이 비는 것은 설계 단계에서 감수하기로 한 부분이다. 유실을 0으로 만들려면 평소부터 이중으로 녹화해야 하고, 그건 다른 기능이다.
- 라이브는 무중단이 아니다. 수 초 끊겼다가 자동 재연결된다.
- takeover 중 primary가 다시 켜지면 같은 카메라를 양쪽이 동시에 수집한다. 가용성을 우선해 primary가 자기 녹화를 자동으로 멈추지는 않는다. 대신 양쪽에서 수집 중이라는 사실을 primary 화면에 경고로 띄워 failback을 재촉한다.
- 일시 정지 중에는 실제 장애를 감지하지 못한다. 수동 정지는 자동으로 풀리지 않는다.
- 한 primary를 채널 단위로 쪼개 나눠서 takeover하지는 않는다. 통째로 takeover하거나 거부한다.
아직 베타 배지가 붙어 있는 이유
이 글을 쓰는 시점에도 페일오버 설정 화면에는 베타 배지와 상시 안내가 남아 있다. 남은 조건은 기능 목록이 아니라 운영 시간이다.
가장 늦게 고친 두 건, 즉 record-back한 영상이 보관 기간을 벗어나 있으면 도착하자마자 지워지던 문제와 어떤 경로로도 지워지지 않던 잔재는 이번 달에 수정했다. 둘 다 코드 리뷰가 아니라 실제로 돌아가는 장비에서 발견됐다. takeover와 failback을 여러 차례 반복하고, 장애가 하루를 넘겨 보관 기간과 겹치고, record-back 도중에 다시 장애가 나는 상황까지 실제 환경에서 충분히 겪어 보기 전까지는 같은 종류의 결함이 더 남아 있다고 보는 편이 안전하다.
배지를 떼는 기준은 이렇게 잡았다. takeover에서 failback, record-back까지 한 바퀴가 관리자 손을 타지 않고 끝나는 것을 여러 현장에서 거듭 확인하고, 그 과정에서 나오는 문제가 새로운 상태나 새로운 정리 경로를 만들어야 할 만큼 크지 않을 때. 최근 세 건의 수정이 각각 새로운 상태값, 새로운 정리 경로, 새로운 조회 기능을 만들고 나서야 끝났다는 사실 자체가 아직 이르다는 신호다.
결론
페일오버에서 어려운 부분은 takeover가 아니었다. 스냅샷을 복호화해 카메라를 등록하고 녹화를 시작하는 경로는 며칠이면 동작한다. 시간을 쓴 곳은 takeover하지 말아야 할 때 takeover하지 않는 것, takeover 이후에 만들어진 데이터를 잃지 않는 것, 그리고 무언가 실패했을 때 사용자가 그 사실을 알고 빠져나올 수 있게 하는 것이었다.
그래서 판단 기준도 하나로 정리됐다. 페일오버가 잘 만들어졌는지는 takeover 성공률이 아니라, 실패했을 때 무엇이 남아 있는지로 판단해야 한다. record-back하지 못한 영상이 남아 있는지, 실패가 완료로 표시되지는 않는지, 감시가 꺼져 있던 구간을 나중에 확인할 수 있는지다. 베타 배지는 그 질문들에 전부 "그렇다"고 답할 수 있게 된 뒤에 뗀다.
👉 NOX 제품 페이지에서 실제 운영 화면 확인 — 페일오버 설정과 시스템 대시보드
NOX NVR 파트너십 및 POC 프로그램 문의: yiyol.com/contact
관련 글
- 헤드리스 NVR이란? 모니터 출력을 걷어낸 NVR의 사양과 원가 구조 — 로컬 출력을 걷어내 확보한 자원을 어디에 쓰는지
- 중국산 IP카메라, 인터넷 차단으로 격리하고 NOX를 이용해서 안전하게 쓰는 법 — 카메라를 격리망에 두는 구성과 그 위에서 동작하는 장비 간 연결
- NVR 디스크 계산기 — 대기 장비의 저장 용량을 산정할 때 쓰는 계산기