DevOps & SRE/Disaster Recovery
Kafka + CDC 로 재해복구(DR)를 만들면 생기는 일 — DR 설계 여정 #1
재해복구(DR, Disaster Recovery)는 살아 있는 동안은 쓸 일이 없으면서, 막상 필요한 순간에는 모든 것이 걸려 있는 시스템이다. 평소에는 보이지 않다가, 어느 날 데이터센터가 멈췄을 때 "우리가 준비해 둔 것이 살아 있는가?"라는 질문에 답해야 하는 순간이 온다.
이 글에서는 CDC(Change Data Capture)와 Kafka를 활용해 DR을 구축하면서 마주한 질문들과 설계 결정을 공유한다. 연재로 진행될 예정이며, 첫 편에서는 DR 설계의 출발점과 아키텍처 방향을 다룬다.
왜 CDC + Kafka인가
DR을 구축한다고 했을 때 가장 먼저 떠오르는 질문은 "데이터를 어떻게 복제할 것인가"다. 데이터베이스 자체의 복제 기능(물리 복제, 동기 스트리밍)을 쓸 수도 있고, CDC로 변경 사항을 캡처하여 별도의 파이프라인으로 전달할 수도 있다.
우리가 CDC + Kafka 조합을 선택한 이유는 단순했다. 이미 Kafka 인프라가 운영 중이었고, CDC 기반 논리 복제는 향후 다른 데이터센터로 확장하거나 이기종 데이터베이스를 연동할 때 유연하다. PostgreSQL의 동기 물리 복제로 같은 효과를 낼 수도 있었지만, Kafka 생태계의 활용 가능성과 확장성이 더 큰 장점이었다.
다만 이 선택에는 대가가 따른다. 그 대가가 무엇인지 이해하는 것이 이 연재의 출발점이다.
비유로 시작하자: 은행 계좌와 거래 내역
CDC가 무엇인지 일상어로 풀어보자. 은행 계좌를 상상해 보자.
계좌의 현재 잔액은 "상태"다. 오늘 잔액이 100만 원이라면, 그 100만 원이라는 숫자 자체가 데이터베이스에 저장된 값이다. 한편, 거래 내역(입금·출금 기록)은 "변경 이력"이다. 어제 50만 원을 입금하고, 오늘 30만 원을 출금했다면, 그 기록들이 누적되어 현재 잔액이 만들어진다.
PostgreSQL의 물리 복제는 계좌 통장 자체를 복사하는 것과 같다. 잔액이라는 "상태"를 바이트 단위로 동일하게 만든다. 빠르고 정확하지만, 같은 은행(같은 DB 엔진)에서만 통한다.
CDC는 거래 내역을 복사하는 것과 같다. 잔액이 아니라 "무슨 변경이 있었는지"를 하나씩 전달한다. 받는 쪽에서 그 변경 이력을 다시 적용하면, 결과적으로 같은 잔액이 만들어진다. 거래 내역은 다른 은행(다른 DB 엔진)에서도 해석할 수 있다. 이것이 논리 복제의 핵심이다.
CDC + Kafka DR의 본질: "논리·비동기·단방향"
CDC로 변경 이력을 캡처해서 Kafka로 보내고, 반대쪽에서 받아 적용하는 구조를 만들었다고 해보자. 데이터는 잘 흐르는 것 같다. 그런데 이것을 "DR이 구축됐다"고 부를 수 있을까?
정답은 "부분적으로만 그렇다"다. CDC + Kafka 기반 DR은 물리 복제 기반 DR과 근본적으로 성격이 다르다.
| 물리 복제 DR (DB 내장 동기 스트리밍) | CDC + Kafka DR (논리 복제) | |
|---|---|---|
| 손실 (RPO) | 0 (동기) | 0보다 큼 (비동기, 항상 손실 구간 존재) |
| 충실도 | 바이트 단위 동일 | 논리 재생, 어긋날 수 있음 |
| 커버리지 | 로그에 남는 전부 | DML(INSERT/UPDATE/DELETE)만. 테이블 구조 변경(DDL)이나 대량 적재는 별도 처리 |
| 장애 전환 | 대칭적 승격 | 단방향 마이그레이션 성격 |
핵심은 "비동기라서 항상 손실 구간이 있다"는 점이다. Active 노드에서 변경이 발생해 Kafka에 적재되기까지, 그리고 Kafka에서 DR 노드로 반영되기까지 시간차가 존재한다. Active 노드가 장애로 멈추는 순간, 그 시간차 안에 있던 변경은 유실된다.
이 손실을 "수용한다"는 전제에서 출발해야 한다. 무손실이 필요하다면 동기 물리 복제를 선택해야 한다. 우리는 수 초에서 수 분 정도의 손실 구간을 수용하고, 그 대가로 Kafka 인프라 재사용과 확장성을 얻기로 했다.
설계 방향: Active/Standby 교차 운영
DR 아키텍처를 설계할 때 "양쪽에서 동시에 쓰는 active/active"를 원하는 경우가 많다. 두 데이터센터가 모두 살아 있을 때 각자 로컬에서 쓰기를 처리하면 리소스를 최대한 활용할 수 있기 때문이다. 하지만 CDC 기반에서 active/active를 만들면 세 가지 문제가 따라온다.
첫째, 데이터가 무한히 왕복한다. A 노드에서 쓴 변경이 B로 가고, B의 CDC가 그 변경을 다시 캡처해서 A로 역전파하면, 데이터가 양쪽을 무한히 오간다. 이것을 에코 루프라고 부른다.
둘째, 같은 데이터를 양쪽에서 동시에 수정하면 충돌이 난다. 같은 행을 A에서 100으로 바꾸고 B에서 200으로 바꾸면, 어느 쪽이 맞는지 Kafka가 결정해 주지 않는다. 분산 데이터베이스(CockroachDB 등)가 내장하는 합의 메커니즘을 직접 구현해야 한다.
셋째, 양방향 모두 비동기라서 손실이 불가피하다. active/active를 원하는 이유 중 하나가 "무손실"이라면, CDC 경로로는 그 요구를 충족할 수 없다.
우리는 이 세 가지 문제를 감안하여 active/standby 교차 운영(cross-active) 방식을 선택했다. 각 인프라 노드가 서로 다른 서비스의 운영(Active)과 대기(Standby)를 동시에 맡는 구조다. 한 서비스의 관점에서 보면 한쪽 노드에서만 쓰기가 발생하므로, 충돌은 장애 전환 찰나에만 국한된다.
flowchart TB
subgraph NODE_A["인프라 노드 A"]
SA["서비스 A — 운영"]
SB["서비스 B — 대기"]
end
subgraph NODE_B["인프라 노드 B"]
SA2["서비스 A — 대기"]
SB2["서비스 B — 운영"]
end
SA <-->|"CDC + Kafka 양방향"| SA2
SB <-->|"CDC + Kafka 양방향"| SB2
양방향 복제와 에코 억제
active/standby 구조지만, Kafka 복제 파이프라인(MirrorMaker 2)은 양방향으로 상시 운영한다. 정상 상태에서는 운영→대기 방향으로만 데이터가 흐르지만, 장애 전환 후 반대 방향(신규 운영→구 운영)으로도 데이터가 흐를 수 있도록 파이프라인을 미리 양쪽으로 열어두는 것이다.
이렇게 하면 장애 전환 후 구 운영 노드가 복구되었을 때, 별도의 파이프라인 재구성 없이 역방향 동기화가 자동으로 이루어진다. 수동 조치는 CDC 재개(커넥터 재설정)에 국한된다.
양방향 운영의 걸림돌인 에코 루프는 origin 마커로 차단한다. 데이터를 적재할 때 출처를 표시하고, 자기가 출처인 데이터는 역전파하지 않는 방식이다. 로컬 PoC에서 이 방식이 작동함을 검증했다 — 1행 삽입 시 복제 메시지가 안정적으로 +1만 증가하고, 무한 왕복이 발생하지 않았다. 에코 억제의 기술적 상세는 다음 포스트에서 다룬다.
수용한 것과 남은 과제
설계 방향을 정리하면, 우리가 수용한 것과 남은 과제는 이렇게 나뉜다.
수용한 것:
- RPO > 0 (수 초~수 분 손실 구간). 비동기 복제의 본질적 한계.
- CDC 자동 재개 불가. 장애 전환 후 CDC 파이프라인을 사람이 수동으로 재설정해야 한다. PgPool-II가 DB 페일오버를 자동 처리해도, CDC(Debezium)는 Active 데이터베이스에 직접 연결되어 있어 자동으로 따라가지 못한다.
- DDL 커버리지 빈틈. CDC는 데이터(행) 변경만 잡는다. 테이블 구조 변경(DDL)은 별도로 동기화해야 한다.
남은 과제:
- CDC 재개 수동 절차를 runbook으로 표준화하고, 정기 훈련으로 소요 시간을 측정하기.
- DDL 빈틈을 보정하기 위한 정기 재동기화 절차 설계.
- 현장 단말(엣지)과 통신하는 서비스의 DR — 이 부분은 데이터베이스 DR과는 별개의 문제다.
이 연재에서 다룰 것
이 포스트는 DR 설계 여정의 출발점이었다. 앞으로 이어질 포스트에서는 구체적인 구현과 검증 결과를 다룰 예정이다.
- #2: PoC로 검증한 CDC + Kafka DR의 실체 — 에코 억제가 실제로 작동하는 과정, 장애 전환 시 CDC가 왜 자동으로 재개되지 않는지의 기술적 원인, 그리고 active/active 충돌이 어떻게 발생하는지.
- #3: 장애 전환 runbook 실전 — Active 노드 장애 시 운영자가 실제로 수행하는 단계별 절차.
- #4: 엣지 컴퓨팅 환경의 DR — 현장 단말과 통신하는 서비스에서 DR이 왜 더 어려운지, 그리고 해결 방향.
DR은 "설계해 놓고 잊으면 안 되는" 시스템이다. 정기적으로 실제로 전환해 보고, 절차가 작동하는지 확인해야 한다. "세팅해 두었다"는 것과 "장애 때 실제로 돌아간다"는 것은 다른 말이다. 이 연재가 같은 고민을 하는 누군가에게 참고가 되길 바란다.