
서비스 운영 중에는 데이터베이스에 수많은 INSERT, UPDATE, DELETE가 발생한다.
이 데이터를 다른 시스템에서 즉시 활용해야 할 때가 있다. 예를 들어:
- 사용자 활동을 기반으로 한 실시간 추천
- 재고 변경 알림
- 주문 상태 업데이트
- 실시간 데이터 파이프라인 구축
전통적인 방식에서는 주기적으로 MySQL을 폴링(polling)하거나, 애플리케이션 코드에서 변경 이벤트를 직접 발행해야 한다. 하지만 이런 방식은 지연이 크고, DB와 애플리케이션 모두에 불필요한 부하를 준다.
Debezium은 CDC(Change Data Capture) 도구로 MySQL의 binlog를 읽어 데이터 변경 이벤트를 추출하고, 이를 메시지 큐나 스트리밍 시스템으로 보낸다.
MySQL → Debezium → Pub/Sub 구조는 데이터 변경을 실시간으로 수집하고, 이를 다양한 시스템에서 즉시 활용할 수 있도록 해준다.
특히 마이크로서비스 환경이나 실시간 분석이 중요한 환경에서 강력한 선택지다. 운영 DB에 부하를 최소화하고, 변경 데이터를 안정적으로 다른 시스템으로 전달해야 한다면 고려해 볼 만한 아키텍처다.
해당 실습의 폴더 구성은 다음과 같습니다.
.
├── config
│ └── application.properties
├── data
│ ├── offsets.dat
│ └── schemahistory.dat
├── docker-compose.yaml
└── secrets
└── gcp-sa.json
추가로 mysql은 전편인 2. mysql master - slave Replication 구성 및 테스트 를 그대로 사용합니다.
.
├── docker-compose.yaml
├── master
│ ├── init.sql
│ └── my.cnf
└── slave
└── my.cnf
1. MySQL 설정
먼저 MySQL에서 binlog를 활성화하고 CDC에 필요한 설정을 한다.
master/my.cnf에 다음 내용을 추가한다.
[mysqld]
server-id=1
log-bin=mysql-bin
binlog_format=ROW
binlog_row_image=FULL
gtid_mode=ON
enforce_gtid_consistency=ON
MySQL 재시작 후 설정이 적용됐는지 확인한다.
docker exec -it mysql-master mysql -uroot -proot
설정 확인
mysql> SHOW VARIABLES LIKE 'log_bin';
mysql> SHOW VARIABLES LIKE 'binlog_format';
mysql> SHOW VARIABLES LIKE 'binlog_row_image';
출력 예시:
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON |
+---------------+-------+
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW |
+---------------+-------+
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| binlog_row_image | FULL |
+------------------+-------+
2. Debezium 계정 생성
Debezium이 binlog를 읽을 수 있도록 MySQL에 계정을 만든다.
master/init.sql 에 다음을 추가한다.
CREATE USER 'debezium'@'%' IDENTIFIED BY 'debezium_pw';
GRANT SELECT, RELOAD, REPLICATION SLAVE, REPLICATION CLIENT, LOCK TABLES ON *.* TO 'debezium'@'%';
FLUSH PRIVILEGES;
3. Pub/Sub 토픽 생성
GCP에서 CDC 데이터를 받을 Pub/Sub 토픽을 만든다.
테이블별로 토픽이 생성된다.
-- gcloud pubsub topics create <복사할 테이터베이스.테이블명> --project=<gcp 프로젝트이름>
gcloud pubsub topics create cdc.appdb.users --project=<gcp 프로젝트이름>
4. Debezium Server 설정
config/application.properties 파일을 작성한다.
# ---- Sink: Google Pub/Sub ----
debezium.sink.type=pubsub
debezium.sink.pubsub.project.id=<gcp 프로젝트이름>
# ---- Value format & unwrap ----
debezium.format.value=json
debezium.transforms=unwrap
debezium.transforms.unwrap.type=io.debezium.transforms.ExtractNewRecordState
debezium.transforms.unwrap.delete.tombstone.handling.mode=drop
debezium.transforms.unwrap.delete.handling.mode=rewrite
# ---- Source: MySQL ----
debezium.source.connector.class=io.debezium.connector.mysql.MySqlConnector
debezium.source.database.hostname=host.docker.internal
debezium.source.database.port=3307
debezium.source.database.user=debezium
debezium.source.database.password=debezium_pw
debezium.source.database.include.list=appdb
# 특정 테이블만 캡처하려면 다음 라인 사용 (없으면 전체 테이블 캡처)
# debezium.source.table.include.list=appdb.users,appdb.orders
debezium.source.table.include.list=appdb.users
# 필수: 고유 server.id 지정 (레플리케이션 ID)
debezium.source.database.server.id=1
# 토픽 prefix (영문/숫자/.-_ 만 허용)
debezium.source.topic.prefix=cdc
# 스냅샷/오프셋/스키마 히스토리
debezium.source.snapshot.mode=initial
debezium.source.snapshot.locking.mode=none
debezium.source.include.schema.changes=false
debezium.source.offset.storage=org.apache.kafka.connect.storage.FileOffsetBackingStore
debezium.source.offset.storage.file.filename=/debezium/data/offsets.dat
debezium.source.offset.flush.interval.ms=10000
debezium.source.schema.history.internal=io.debezium.storage.file.history.FileSchemaHistory
debezium.source.schema.history.internal.file.filename=/debezium/data/schemahistory.dat
# 로그 확인용. 없어도 됨
quarkus.log.category."io.debezium".level=DEBUG
5. Docker-Compose 실행
docker-compose.yml 작성 후 실행한다.
version: "3.8"
services:
debezium-server:
image: quay.io/debezium/server:3.0
container_name: debezium-server
environment:
- GOOGLE_APPLICATION_CREDENTIALS=/secrets/gcp-sa.json
volumes:
- ./config/application.properties:/debezium/config/application.properties:ro
- ./secrets/gcp-sa.json:/secrets/gcp-sa.json:ro
- ./data:/debezium/data
ports:
- "8080:8080"
restart: unless-stopped
여기에서 gcp-sa.json은 gcp 프로젝트에서 사용할 키를 생성후 넣어주면 된다. (gcp IAM에서 생성해주면 된다. - 권한은 다음과 같이 주면 된다. )

6. Pub/Sub 메시지 전송 테스트
서비스 계정 키를 지정하고 Pub/Sub에 메시지를 직접 발행해 본다.
gcloud pubsub topics publish cdc.appdb.users \
--project=<gcp 프로젝트 이름 > \
--message='{"ping":"pong"}' \
--credential-file-override="./secrets/gcp-sa.json"
만일 다음 에러가 난다면 권한 설정이 만료되었거나, 권한을 잘못준거다. IAM에서 권한을 다시 주면 된다.
ERROR: (gcloud.pubsub.topics.publish) PERMISSION_DENIED: User not authorized to perform this action. This command is authenticated as 계정명 using the credentials in ./secrets/gcp-sa.json, specified by the [auth/credential_file_override] property.
8. MySQL 변경 테스트
CDC가 정상 동작하는지 확인하기 위해 MySQL 데이터 변경을 해본다.
docker exec -it mysql-master mysql -uroot -proot testdb
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
email VARCHAR(100)
);
INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com');
INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com');
데이터 확인

이렇게 하면 MySQL → Debezium → Pub/Sub CDC 파이프라인이 완성된다.
pub/sub으로 데이터를 보냈기 때문에 이후 bigquery 혹은 ES로 보낼수 있도록 컨슈머를 만들어주면 된다.
다만 운영 환경에서는 Master 대신 Slave(Replica)를 바라보게 설정해 부하를 줄이는 것이 좋다.
'Database > DB' 카테고리의 다른 글
| MySQL id 범위 조건 하나로 10분 → 2분 (0) | 2025.09.04 |
|---|---|
| [논문] Presto: SQL on Everything / A Decade of SQL Analytics at Meta / History-based Query Optimizer (2) | 2025.08.27 |
| 2. mysql master - slave Replication (2) | 2025.08.16 |
| 1. mysql binlog (0) | 2025.08.11 |
| [논문] The Log-Structured Merge-Tree (LSM-Tree) (5) | 2025.08.09 |