스토리지 탐구 시리즈 (3/3). 앞 두 편에서 문제를 파고들었다면, 마지막 편은 현실이다. 고객 앞에서 하루 만에 DR을 시연해야 했던 48시간과, 시리즈 전체를 관통하던 패턴 하나를 정리한다.
11월 초, 고객 PoC 시연 요청이 들어왔다. 주제는 재해복구. 보여줘야 하는 건 그런 그림이다. 복제가 돌고 있고, 원본이 죽어도 전환이 되고, 다시 살아나면 돌아온다. 시간은 이틀. 그리고 검토 중인 DR 방식이 세 종류라는 조건이 붙었다.
이 시리즈를 관통하는 패턴도 이 편에서 정리한다. 문제를 정의하고, 검증 장치를 만들고, 완성도를 끌어올리는 일은 점점 좋아졌다. 그런데 뭔가가 매번 마지막에 나타났다. 그것이 무엇인지 지도로 그려보니 여섯 개의 관문이었다.
TL;DR
- 시연 요청: 이틀 안에 DR 시연. 검토 중인 방식은 replicd, bzfs, DRBD 세 종
- 키트 완성: setup, replicate, failover, failback 스크립트와 런북, 오프라인 번들
- 시나리오 8종 통과: 부하 중 증분, 전송 중 kill, 외부 쓰기 거부, 짝 컨트롤러 전환까지
- 관문 여섯 개: 수요자, 팀 자산, 백엔드 양쪽, 유지보수 담당자, 무패치 경로, 완료 기준
- 정리: 기술 검증 습관 11개는 있었지만 도입 관문을 받쳐 주는 습관은 없었다
1. 시연 키트: 48시간의 결정들
요구를 시나리오로 번역하다
“DR을 보여줘”라는 말은 시연장에서 아무것도 보여주지 않는다. 그래서 먼저 시나리오를 썼다. 등록과 시드 스냅샷(S1), 부하 중 증분 추종(S2), 전송 중 강제 종료 후 재개(S3), 받는 쪽 외부 쓰기 거부 후 재시드(S4), 공통 스냅샷 없는 원본 삭제 거부(S5), native와의 병행 비교(S7). 여기에 컨트롤러 짝 전환(F1’)과 소유 상실 후 자동 회복(F2’)까지 붙였다.
설계 원칙은 단순했다. 시연은 리허설의 재현이어야 한다. 현장에서 처음 보는 단계가 하나라도 있으면 그것이 시연을 먹는다. 그래서 모든 단계를 스크립트 하나로 실행 가능하게 만들고, 런북에는 각 단계의 기대 결과를 적어뒀다.
bzfs 어댑터: 기존 엔진에 붙이는 법
replicd는 이미 있던 방식이다. kstat 감지로 그룹 스냅샷을 만들고 증분 send로 전송하며, HMAC 짝 인증에 ssh 없이 전용 수신 데몬이 받는다. 여기에 bzfs 전송을 얹는 작업이 내 몫이었다. 제어는 ssh zfs, 전송은 bzfs, 재개 토큰은 bzfs에 맡기고, 수신 후 readonly와 origin 표시까지. 11커밋, 24파일에 970줄.
단위시험 14개를 새로 쓰면서 기존 CI의 결함 4건도 같이 고쳤다. 어댑터는 작지만 새 경로가 아니라는 게 요점이다. 기존 엔진의 시험 체계 안에서 검증되는 작은 부품. 2편의 vouch가 도달한 결론, “경쟁하지 말고 붙어라”가 여기서 먼저 현실이 됐다.
리허설: 숫자가 말해주는 것
리허설은 CI에서 두 번 돌렸다. bzfs 키트는 첫 전송 768MiB를 8.1초에 올리고, 200MB 증분이 약 2초. failover 중에도 쓰기가 이어졌고, failback은 증분 경로가 실패해 –force 전체 재동기(969MB 11.2초)로 회복됐다. 정상 증분 복귀는 실측이 없다는 것까지 기록에 남겼다. DRBD 키트는 초기 동기 100G가 약 40분, 전 단계 통과.
성능도 측정했다. 같은 장비에서 200MB 증분 3회의 전송 중앙값이 native 0.79초, bzfs 1.66초. RPO는 1.40초 대 3.25초. 차이의 정체도 함께 적었다. bzfs 기동과 제어 ssh의 고정비. 전환은 bzfs 약 2초, DRBD 약 1초였다.
정직한 기록도 남겼다. 리허설 문서에 인용돼 있던 “100MB/s 지속 쓰기에서 지연 143초” 표는 이번 측정이 아니라 다른 작성자의 문서 인용이었다는 것. DC 빌드 환경의 컨트롤러 디스크가 로컬이라 실제 풀 이동은 시험할 수 없었고 F1’·F2’로 대체 검증했다는 것. 반입 장비가 NVMe여서 절대 속도는 해석하지 말 것. 시연용 숫자일수록 출처가 명확해야 한다는 게 이 키트의 규칙이었다.
2. 관문 여섯 개: 시리즈 전체의 지도
패턴의 발견
시리즈를 되짚으며 프로젝트마다 “무엇이 마지막 관문이었나”를 표로 그려봤다. 태그 색인은 ZFS 전용 구조(백엔드 한쪽), 커널 changelog는 수요 질문(수요자), zchangelog는 시장 신호(수요자), vouch는 RPO 0 로드맵 시점(수요자·시점), 시연 키트는 채택 검토(완료 기준 앞). 공통점이 하나 있었다. 관문은 전부 구현이 끝난 뒤에 나타났다.
그래서 관문을 왼쪽부터 순서대로 정리했다. 착수 전에 이 순서대로 확인하면 늦게 드러나는 벽을 미리 걸러낼 수 있다.
| 관문 | 확인할 것 | 여기서 멈춘 탐구 |
|---|---|---|
| ① 수요자·시점 | 일감, 고객, PoC와 시점 | changelog·zchangelog·vouch |
| ② 팀 기존 자산 | 내부 문서·브랜치·담당 검색 | zfs-dr(이미 있던 것과 겹침) |
| ③ 백엔드 양쪽 | Lustre와 ZFS 모두 | 태그 색인 |
| ④ 유지보수 담당자 | 팀의 언어·런타임 | vouch(C 데몬·nbdkit) |
| ⑤ 무패치 1단계 | 제품 빌드 위 E2E | zfs-dr·vouch zfs 어댑터 |
| ⑥ 완료 기준 | 일감 + MR + 릴리스 | 시연 키트 |
습관의 지도
이 관문에 갖다 놓을 습관도 점검했다. 시리즈를 관통한 기술 검증 습관 11개, 차등 테스트(H1)부터 매트릭스 테스트(H11)까지. 그런데 지도에 그려보니 이 습관들은 ⑥ 완료 기준과 ④의 일부(회귀·CI·문서)에 몰려 있었다. 프로젝트가 실제로 가장 많이 멈춘 ① 수요자·시점을 받쳐 주는 습관은 하나도 없었다.
그래서 6개를 추가로 정의했다. 착수 전에 일감과 수요자를 먼저 연결하고 없으면 처음부터 실험으로 표시하는 것(A1). 설계서 첫머리에 팀 자산 조사 결과를 적는 것(A2). 매트릭스 테스트 조건 축에 백엔드를 넣는 것(A3). 만든 사람이 아닌 사람이 문서만 보고 재현해 보는 인수 시험(A4). 패치 모듈이 아니라 제품 빌드로 E2E를 먼저 통과시키는 것(A5). 실측과 문서가 끝나도 일감 상태와 MR과 릴리스 노트가 갖춰져야 완료로 보는 것(A6).
기술 검증을 습관으로 만들었던 것과 같은 방식이다. 도입 관문에도 반복 가능한 행동을 붙이는 것. 그게 이 회고의 결론이다.
3. 시리즈를 닫으며
세 편의 한 줄 요약
1편. 검색을 트랜잭션 안으로 끌어들이려다 같은 문제를 커널·인터페이스·사용자 공간의 세 층에서 공격했다. 벽은 매번 생태계에 있었다. 2편. 저널 먼저 보내기로 RPO 0에 도전했고, 전원을 뽑으며 보장을 증명했으며, 특수함을 범용 엔진의 어댑터로 풀었다. 3편. 그 모든 걸 48시간 안에 시연으로 만들었고, 시리즈의 벽을 여섯 개의 관문으로 지도화했다.
남기는 질문
이 시리즈에서 기술적으로 안 된 것은 하나도 없다. 그리고 그 문장이 자랑이 아니라 데이터라는 게 이 글의 요점이다. 다음에 무언가를 만들기 전에 가장 먼저 물을 질문은 정해졌다. 내가 떠나도 팀이 이걸 고칠 수 있는가. 접은 프로젝트는 전부 이 질문 앞에서 멈췄다. 이제는 시작 전에 묻는다.
vouch는 로드맵에 자리를 등록해 뒀다. 시연 키트는 채택 검토 중이고. 이 지도가 다음 탐구의 출발점이 되길 바라며 시리즈를 닫는다.
참고자료
- 1편 검색은 왜 트랜잭션 밖에 있나(/2026/11/12/Storage-Exploration-01-Search-Stream/)
- 2편 RPO 0와 싸우는 법(/2026/11/12/Storage-Exploration-02-RPO-Zero/)
- DRBD docs, bzfs(내부 어댑터 브랜치 feature/replicd-bzfs), replicd(제품 1.4.3)
- 2026-11-04 ~ 11-05 리허설 기록과 시연 런북