온프레미스 또는 자체 관리형 ClickHouse OSS 환경에서 ClickHouse Cloud로의 전환은 운영 효율성, 확장성, 그리고 관리 부담 감소라는 명확한 이점을 제공합니다. 하지만 프로덕션 환경에서 데이터베이스를 마이그레이션하는 것은 항상 신중한 계획과 실행이 필요한 작업입니다.
이 글에서는 ClickHouse OSS에서 ClickHouse Cloud로 마이그레이션하는 세 가지 주요 접근 방식을 상세히 다룹니다. 각 방법의 장단점과 적용 시나리오를 이해함으로써, 여러분의 환경에 가장 적합한 마이그레이션 전략을 선택할 수 있을 것입니다.
마이그레이션 전 고려사항
마이그레이션 방법을 선택하기 전에 다음 사항들을 먼저 평가해야 합니다.
의사결정 플로우차트
다운타임 허용 여부
다운타임이 허용되는 경우: Backup & Restore 방식이 가장 간단합니다
제로 다운타임이 필요한 경우: Dual Writes 또는 Materialized Views 기반 복제를 고려해야 합니다
데이터 볼륨과 네트워크 환경
소규모 데이터셋 (< 1TB): 직접 복제 방식(remoteSecure)이 효율적입니다
대규모 데이터셋 (> 10TB): S3 오프로드를 통한 단계적 마이그레이션이 안전합니다
네트워크 제약: OSS와 Cloud 간 직접 연결이 어려운 경우 S3를 중간 저장소로 활용해야 합니다
인프라 변경 가능성
데이터 파이프라인 수정 가능: Dual Writes 방식을 적용할 수 있습니다
파이프라인 수정 불가: Materialized Views를 통한 투명한 복제가 적합합니다
방법 1: Backup & Restore를 통한 마이그레이션
가장 전통적이고 직관적인 방법으로, 짧은 다운타임이 허용되는 환경에 적합합니다.
작동 원리
ClickHouse의 네이티브 BACKUP/RESTORE 명령을 사용하거나, clickhouse-backup 도구를 활용하여 데이터를 S3나 다른 오브젝트 스토리지로 백업한 후 새로운 Cloud 환경에서 복원합니다.
‣
단계별 가이드
1단계: 백업 스토리지 구성
S3 백업 디스크 설정 (config.d/backup_disk.xml)
2단계: 전체 데이터베이스 백업
3단계: ClickHouse Cloud에서 복원
- 스키마 복원
RESTORE DATABASE production
FROM S3('https://your-bucket.s3.amazonaws.com/backup-schema', 'access_key', 'secret_key')
SETTINGS structure_only = true;
- 데이터 복원
RESTORE DATABASE production
FROM S3('https://your-bucket.s3.amazonaws.com/backup-full-20250311', 'access_key', 'secret_key');
clickhouse-backup 도구 활용
clickhouse-backup은 Altinity에서 제공하는 오픈소스 도구로, 더 강력한 백업/복원 기능을 제공합니다.
설치 및 설정
백업 생성 및 업로드
# 로컬 백업 생성
clickhouse-backup create backup-20250311
# S3로 업로드
clickhouse-backup upload backup-20250311
# 백업 목록 확인
clickhouse-backup list remote
복원 프로세스
# 새로운 Cloud 환경에서# S3에서 다운로드
clickhouse-backup download backup-20250311
# 복원
clickhouse-backup restore backup-20250311
# 특정 테이블만 복원
clickhouse-backup restore --table=production.events backup-20250311
장단점 및 적정 고려 환경
장점
간단한 프로세스: 검증된 방법으로 리스크가 낮습니다
완전한 백업: 모든 데이터, 스키마, 설정을 포함합니다
증분 백업 지원: 대규모 데이터셋의 경우 증분 백업으로 시간과 비용을 절약합니다
파이프라인 수정 불필요: 기존 인프라 변경이 필요 없습니다
단점
다운타임 필요: 백업 중에는 쓰기 작업을 중단해야 합니다
대규모 데이터셋의 경우 시간 소요: TB급 데이터는 수 시간이 걸릴 수 있습니다
스토리지 비용: 백업 데이터를 위한 추가 S3 비용이 발생합니다
적용 시나리오
계획된 마이그레이션 윈도우가 있는 경우
데이터 볼륨이 상대적으로 작은 경우 (< 5TB)
정기적인 백업 전략의 일환으로 마이그레이션을 수행하는 경우
방법 2: remoteSecure를 통한 실시간 복제
ClickHouse의 remoteSecure 함수를 사용하여 OSS 환경에서 Cloud 환경으로 직접 데이터를 복제하는 방법입니다.
작동 원리
Cloud 환경에서 OSS 환경의 테이블을 remoteSecure 함수로 참조하여, INSERT INTO ... SELECT 쿼리로 데이터를 pull 방식으로 복제합니다. 보안 연결(TLS/SSL)을 사용하는 것이 일반적입니다.
‣
단계별 가이드
1단계: 네트워크 연결 설정
ClickHouse Cloud의 경우 IP Access List에 OSS 서버의 IP를 추가하고 연결을 수행합니다.
2단계: OSS에 읽기 전용 사용자 생성
- OSS 환경에서 실행
CREATE USER readonly_migrator IDENTIFIED BY 'secure_password';
GRANT SELECT ON production.* TO readonly_migrator;
3단계: Cloud 환경에 스키마 생성
4단계: remoteSecure를 통한 데이터 복제
5단계: 데이터 검증
- 레코드 수 비교- OSS에서
SELECT count() FROM production.events;
- Cloud에서
SELECT count() FROM production.events;
- 체크섬 비교- OSS에서
SELECT cityHash64(toString(groupArray(event_time))) as time_hash, cityHash64(toString(groupArray(user_id))) as user_hash
FROM production.events;
- Cloud에서 동일한 쿼리 실행하여 비교
배치 처리 스크립트 예시
대규모 데이터셋의 경우 파티션별로 나누어 복제하는 것이 안전합니다.
장단점 및 적정 고려 환경
장점
외부 스토리지 불필요: S3 같은 중간 저장소가 필요 없습니다
직접 전송: 네트워크가 빠르다면 효율적입니다
파티션별 제어: 세밀한 마이그레이션 제어가 가능합니다
유연한 데이터 변환: SELECT 절에서 데이터 변환이 가능합니다
단점
OSS 부하: 대규모 SELECT 쿼리가 OSS 환경에 부하를 줄 수 있습니다
네트워크 의존성: 안정적인 네트워크 연결이 필수입니다
배치 관리: 대규모 데이터의 경우 파티션별 처리 로직이 필요합니다
적용 시나리오
OSS와 Cloud 간 안정적인 네트워크 연결이 보장되는 경우
S3 비용을 최소화하고 싶은 경우
특정 파티션이나 데이터 범위만 선택적으로 마이그레이션하는 경우
방법 3: Zero-Downtime 마이그레이션 전략
프로덕션 환경에서 무중단 마이그레이션이 필요한 경우 사용하는 고급 전략입니다.
3-1: Dual Writes + remoteSecure Backfill
작동 원리
Dual Writes: 데이터 파이프라인을 수정하여 신규 데이터를 OSS와 Cloud 양쪽에 동시에 기록
Historical Backfill: remoteSecure를 사용하여 과거 데이터를 Cloud로 복제
Cutover: 검증 후 트래픽을 Cloud로 전환
‣
구현 단계
1단계: Cloud 환경 준비
- OSS와 동일한 스키마 생성 (ENGINE만 수정)
CREATE TABLE production.events
( event_time DateTime, user_id UInt64, event_type String, properties String
)
ENGINE = ReplicatedMergeTree()
ORDER BY (event_time, user_id)
PARTITION BY toYYYYMM(event_time);
2단계: Dual Writes 구성
애플리케이션이나 데이터 파이프라인에서 두 개의 ClickHouse 엔드포인트로 동시에 쓰기를 수행합니다.
Kafka를 사용하는 경우
3단계: Historical Data Backfill
Dual writes가 활성화된 시점의 타임스탬프를 기록하고, 그 이전 데이터를 backfill합니다.
4단계: 데이터 일관성 검증
- 특정 기간의 데이터 수 비교- OSS
SELECT toYYYYMMDD(event_time) as day, count() as cnt
FROM production.events
WHERE event_time >= '2025-02-01' AND event_time < '2025-03-01'
GROUP BY day
ORDER BY day;
- Cloud (동일한 쿼리)- 결과를 비교하여 일치 여부 확인
5단계: 트래픽 전환
# Blue-Green 방식 전환
config = {
'clickhouse_host': 'cloud-cluster.clickhouse.cloud', # OSS에서 Cloud로 변경
'clickhouse_port': 9440,
'secure': True
}
# 또는 Load Balancer를 통한 점진적 전환# 10% -> 50% -> 100% 트래픽을 Cloud로 이동
3-2: Dual Writes + S3 Offload Backfill
네트워크 제약이 있거나 대규모 데이터셋의 경우, S3를 중간 저장소로 활용합니다.
구현 차이점
Historical Data 처리를 S3를 통해 수행
3-3: Materialized Views를 통한 자동 복제
데이터 파이프라인 수정이 어려운 경우, ClickHouse의 Materialized Views를 활용하여 투명한 복제를 구현합니다.
작동 원리
OSS 환경에서 Materialized View를 생성하고, INSERT 시 자동으로 remoteSecure를 통해 Cloud로 데이터를 push합니다.
‣
구현 단계
1단계: Cloud 환경에 대상 테이블 생성
- Cloud 환경
CREATE TABLE production.events
( event_time DateTime, user_id UInt64, event_type String, properties String
)
ENGINE = ReplicatedMergeTree()
ORDER BY (event_time, user_id)
PARTITION BY toYYYYMM(event_time);
2단계: OSS에 Materialized View 생성
- OSS 환경- remoteSecure를 포함한 Materialized View
CREATE MATERIALIZED VIEW production.events_to_cloud
TO FUNCTION remoteSecure( 'cloud-cluster.clickhouse.cloud:9440', 'production', 'events', 'migration_user', 'secure_password'
)
AS
SELECT *
FROM production.events;
View 트리거 방식 (권장)
더 안정적인 방법은 source 테이블에서 target remoteSecure로 직접 쓰는 것입니다.
3단계: Historical Data Backfill
신규 데이터는 Materialized View를 통해 자동 복제되므로, 과거 데이터만 별도로 backfill합니다.
- remoteSecure 또는 S3를 통한 backfill (방법 3-1, 3-2 참조)
INSERT INTO production.events
SELECT *
FROM remoteSecure( 'oss-server.example.com:9440', 'production', 'events', 'readonly_migrator', 'secure_password'
)
WHERE event_time < '2025-03-11 00:00:00'; - MV 활성화 시점 이전
4단계: 모니터링
- OSS에서 복제 상태 모니터링
SELECT table, last_exception, total_rows, total_bytes
FROM system.parts
WHERE database = 'production' AND table = 'events';
- Cloud에서 수신 확인
SELECT toStartOfHour(event_time) as hour, count() as events_count
FROM production.events
WHERE event_time > now() - INTERVAL 1 DAY
GROUP BY hour
ORDER BY hour DESC;
Zero-Downtime 방법 비교
특성
Dual Writes + remoteSecure
Dual Writes + S3
Materialized Views
파이프라인 수정
필요
필요
불필요
OSS 부하
Backfill 시 높음
낮음 (오프피크 처리)
MV로 인한 추가 부하
네트워크 요구사항
안정적 연결 필수
S3 연결만 필요
안정적 연결 필수
복잡도
중간
높음
높음
비용
네트워크 비용
S3 스토리지 비용
네트워크 비용
실시간성
즉시
Dual writes는 즉시
거의 실시간
장단점 및 적정 고려 환경
장점
제로 다운타임: 서비스 중단 없이 마이그레이션
점진적 전환: Blue-Green 방식으로 안전하게 전환
롤백 가능: 문제 발생 시 즉시 OSS로 롤백
단점
복잡한 구현: 세밀한 계획과 모니터링 필요
리소스 오버헤드: Dual writes나 MV로 인한 추가 부하
데이터 일관성 관리: 두 시스템 간 동기화 상태를 지속 모니터링 필요
적용 시나리오
24/7 가용성이 필요한 프로덕션 환경
대규모 사용자 기반으로 다운타임이 허용되지 않는 경우
점진적이고 제어된 마이그레이션이 필요한 경우
마이그레이션 체크리스트
사전 준비
현재 데이터 볼륨 측정 (테이블별, 파티션별)
네트워크 대역폭 및 지연시간 평가
ClickHouse Cloud 인스턴스 사이징 (CPU, 메모리, 스토리지)
백업 및 롤백 계획 수립
마이그레이션 중
스키마 정확성 검증 (특히 ENGINE 설정)
파티션별 데이터 카운트 비교
체크섬 검증으로 데이터 무결성 확인
샘플 쿼리로 성능 비교
복제 지연(lag) 모니터링 (Zero-downtime 방식)
전환 후
전체 데이터 카운트 최종 검증
애플리케이션 연결 문자열 업데이트
쿼리 성능 모니터링
OSS 환경 읽기 전용 모드 전환 (당분간 유지)
일정 기간 후 OSS 환경 해제
성능 최적화 팁
‣
Backfill 성능 향상
‣
네트워크 최적화
# ClickHouse 서버 설정에서 네트워크 버퍼 증가
<max_network_bandwidth>100000000</max_network_bandwidth> # 100 MB/s
<max_network_bandwidth_for_user>50000000</max_network_bandwidth_for_user>
‣
remoteSecure 커넥션 풀링
- 설정 최적화
SET max_threads = 16;
SET max_insert_threads = 8;
SET max_block_size = 1048576;
SET min_insert_block_size_rows = 1048576;
트러블슈팅 가이드
‣
문제 1: "Network error" 또는 연결 시간 초과
원인: 방화벽, Security Group, 또는 IP Access List 설정 오류
해결:
# 연결 테스트
clickhouse-client -h target-host --port 9440 --secure --query "SELECT 1"
# 방화벽 규칙 확인# OSS 서버 IP가 Cloud의 IP Access List에 있는지 확인
‣
문제 2: "Memory limit exceeded"
원인: 대량의 데이터를 한 번에 복제하려고 시도
해결:
- 배치 크기 줄이기
SET max_block_size = 65536;
SET max_insert_block_size_rows = 100000;
- 파티션별로 나누어 처리
INSERT INTO target_table
SELECT * FROM remoteSecure(...)
WHERE partition_id = 'specific_partition';
‣
문제 3: Dual Writes 중 데이터 불일치
원인: 네트워크 장애나 애플리케이션 오류로 한쪽만 성공
해결:
결론
ClickHouse OSS에서 ClickHouse Cloud로의 마이그레이션은 단순 Backup & Restore부터 고급 Zero-Downtime 전략까지 다양한 방법이 있습니다. 각 방법의 선택은 다음 기준으로 결정됩니다.
Backup & Restore 선택 조건:
다운타임 허용 가능
데이터 볼륨이 관리 가능한 수준 (< 5TB)
간단하고 검증된 방법 선호
remoteSecure 직접 복제 선택 조건:
안정적인 네트워크 연결 보장
S3 비용 최소화 필요
파티션별 제어 필요
Zero-Downtime 전략 선택 조건:
24/7 가용성 필수
프로덕션 서비스 중단 불가
점진적 전환과 롤백 계획 필요
무엇보다 중요한 것은 철저한 테스트와 검증입니다. 프로덕션 마이그레이션 전에 반드시 스테이징 환경에서 전체 프로세스를 시험하고, 데이터 무결성 검증 절차를 수립해야 합니다.
ClickHouse Cloud는 운영 부담을 크게 줄이고 확장성을 제공하지만, 마이그레이션 자체는 신중하게 계획하고 실행해야 하는 작업입니다. 이 가이드가 안전하고 성공적인 마이그레이션에 도움이 되기를 바랍니다.
- 스키마만 백업
BACKUP DATABASE production
TO S3('https://your-bucket.s3.amazonaws.com/backup-schema', 'access_key', 'secret_key')
SETTINGS structure_only = true;
- 전체 데이터 백업 (증분 백업 지원)
BACKUP DATABASE production
TO S3('https://your-bucket.s3.amazonaws.com/backup-full-20250311', 'access_key', 'secret_key')
SETTINGS compression_method = 'zstd', compression_level = 3;
# clickhouse-backup 설치
wget https://github.com/Altinity/clickhouse-backup/releases/latest/download/clickhouse-backup-linux-amd64.tar.gz
tar -xzvf clickhouse-backup-linux-amd64.tar.gz
sudo mv clickhouse-backup /usr/local/bin/
# S3 설정 (/etc/clickhouse-backup/config.yml)
s3:
access_key: YOUR_ACCESS_KEY
secret_key: YOUR_SECRET_KEY
bucket: your-backup-bucket
region: us-east-1
path: /clickhouse-backups/
compression_format: tar
compression_level: 1
- OSS에서 스키마 추출
SELECT create_table_query
FROM system.tables
WHERE database = 'production' AND name = 'events';
- Cloud 환경에서 실행 (ENGINE 수정 필요)- ReplicatedMergeTree 파라미터는 제거 (Cloud가 자동 관리)
CREATE TABLE production.events
( event_time DateTime, user_id UInt64, event_type String, properties String
)
ENGINE = ReplicatedMergeTree() - 파라미터 없이
ORDER BY (event_time, user_id)
PARTITION BY toYYYYMM(event_time);
- Cloud 환경에서 실행- 전체 데이터 복제
INSERT INTO production.events
SELECT *
FROM remoteSecure( 'oss-server.example.com:9440', 'production', 'events', 'readonly_migrator', 'secure_password'
);
- 파티션별 배치 복제 (권장)
INSERT INTO production.events
SELECT *
FROM remoteSecure( 'oss-server.example.com:9440', 'production', 'events', 'readonly_migrator', 'secure_password'
)
WHERE toYYYYMM(event_time) = 202501; - 2025년 1월 파티션- 진행 상황 모니터링
SELECT count() FROM production.events;
#!/bin/bash
# 파티션 목록 추출
PARTITIONS=$(clickhouse-client -h oss-server.example.com --port 9440 --secure \
--query "SELECT DISTINCT partition FROM system.parts WHERE database='production' AND table='events' AND active")
# 각 파티션 복제
for partition in $PARTITIONS; do
echo "Migrating partition: $partition"
clickhouse-client -h cloud-cluster.clickhouse.cloud --port 9440 --secure \
--query "INSERT INTO production.events
SELECT * FROM remoteSecure(
'oss-server.example.com:9440',
'production',
'events',
'readonly_migrator',
'secure_password'
)
WHERE partition = '$partition'"
# 검증
COUNT_SOURCE=$(clickhouse-client -h oss-server.example.com --port 9440 --secure \
--query "SELECT count() FROM production.events WHERE partition = '$partition'")
COUNT_DEST=$(clickhouse-client -h cloud-cluster.clickhouse.cloud --port 9440 --secure \
--query "SELECT count() FROM production.events WHERE partition = '$partition'")
if [ "$COUNT_SOURCE" != "$COUNT_DEST" ]; then
echo "Error: Partition $partition count mismatch!"
exit 1
fi
echo "Partition $partition migrated successfully"
done
# Python 예시 - 두 클러스터에 동시 쓰기
from clickhouse_driver import Client
oss_client = Client(host='oss-server.example.com', port=9440, secure=True)
cloud_client = Client(host='cloud-cluster.clickhouse.cloud', port=9440, secure=True)
def insert_event(event_data):
try:
# OSS에 삽입
oss_client.execute(
'INSERT INTO production.events VALUES',
[event_data]
)
# Cloud에 삽입
cloud_client.execute(
'INSERT INTO production.events VALUES',
[event_data]
)
return True
except Exception as e:
# 에러 처리 로직
logging.error(f"Dual write failed: {e}")
return False
- Dual writes 시작 시점: 2025-03-11 00:00:00
SET cutover_time = '2025-03-11 00:00:00';
- 과거 데이터 backfill (Cloud 환경에서 실행)
INSERT INTO production.events
SELECT *
FROM remoteSecure( 'oss-server.example.com:9440', 'production', 'events', 'readonly_migrator', 'secure_password'
)
WHERE event_time < '2025-03-11 00:00:00'
ORDER BY event_time, user_id;
- 파티션별로 backfill하는 것이 안전
INSERT INTO production.events
SELECT *
FROM remoteSecure( 'oss-server.example.com:9440', 'production', 'events', 'readonly_migrator', 'secure_password'
)
WHERE toYYYYMM(event_time) = 202502; - 2월 데이터
- OSS에서 S3로 export
INSERT INTO FUNCTION s3( 'https://your-bucket.s3.amazonaws.com/migration/events_202502.parquet', 'access_key', 'secret_key', 'Parquet'
)
SELECT *
FROM production.events
WHERE toYYYYMM(event_time) = 202502
ORDER BY event_time, user_id;
- Cloud에서 S3로부터 import
INSERT INTO production.events
SELECT *
FROM s3( 'https://your-bucket.s3.amazonaws.com/migration/events_202502.parquet', 'access_key', 'secret_key', 'Parquet'
);
- OSS 환경- Null 테이블 생성 (버퍼 역할)
CREATE TABLE production.events_local AS production.events
ENGINE = Null;
- Materialized View로 remoteSecure에 전달
CREATE MATERIALIZED VIEW production.events_replication
TO FUNCTION remoteSecure( 'cloud-cluster.clickhouse.cloud:9440', 'production', 'events', 'migration_user', 'secure_password'
)
AS
SELECT *
FROM production.events_local;
- 애플리케이션은 events_local에 INSERT- 자동으로 Cloud의 events로 복제됨
- 병렬 처리를 위한 파티션별 쿼리
INSERT INTO production.events
SELECT *
FROM remoteSecure('oss:9440', 'production', 'events', 'user', 'pass')
WHERE partition_id = 'XXX'
SETTINGS max_threads = 8, max_insert_threads = 4;
- Compression 설정 최적화
ALTER TABLE production.events
MODIFY SETTING min_compress_block_size = 65536, max_compress_block_size = 1048576;
# 재시도 로직이 포함된 Dual Writes
def insert_with_retry(data, max_retries=3):
oss_success = False
cloud_success = False
for attempt in range(max_retries):
if not oss_success:
try:
oss_client.execute('INSERT INTO ...', [data])
oss_success = True
except Exception as e:
logging.error(f"OSS insert failed: {e}")
if not cloud_success:
try:
cloud_client.execute('INSERT INTO ...', [data])
cloud_success = True
except Exception as e:
logging.error(f"Cloud insert failed: {e}")
if oss_success and cloud_success:
return True
time.sleep(2 ** attempt) # Exponential backoff
# 실패한 데이터를 별도 큐에 저장하여 나중에 재처리
dead_letter_queue.append(data)
return False