backup 명령으로 self-managed ClickHouse에서 ClickHouse Cloud로 마이그레이션

backup 명령으로 self-managed ClickHouse에서 ClickHouse Cloud로 마이그레이션

self-managed ClickHouse(OSS)에서 ClickHouse Cloud로 데이터를 마이그레이션하는 두 가지 주요 방법 중 하나가 바로 BACKUP/RESTORE 명령을 클라우드 오브젝트 스토리지로 사용하는 거예요. 이 가이드는 S3 버킷을 통해 오픈소스 ClickHouse의 데이터베이스나 전체 서비스를 Cloud로 마이그레이션하는 실용 예시를 제공해요.

출처: Migrating from self-managed ClickHouse to ClickHouse Cloud using backup commands

본문

개요 (Overview)

self-managed ClickHouse(OSS)에서 ClickHouse Cloud로 데이터를 마이그레이션하는 주요 방법은 두 가지예요:

  • 데이터를 직접 pull/push하는 remoteSecure() 함수 사용
  • 클라우드 오브젝트 스토리지를 통한 BACKUP/RESTORE 명령 사용

이 마이그레이션 가이드는 BACKUP/RESTORE 접근에 초점을 맞추고, S3 버킷을 통해 오픈소스 ClickHouse의 데이터베이스 또는 전체 서비스를 Cloud로 마이그레이션하는 실용 예시를 제공해요.

사전 요구사항 (Prerequisites):

이 가이드의 단계를 쉽게 따라 하고 재현할 수 있도록, 두 개의 샤드와 두 개의 레플리카를 가진 ClickHouse 클러스터용 docker compose 레시피 중 하나를 사용할 거예요.

클러스터 필요: 이 backup 방법은 테이블을 MergeTree 엔진에서 ReplicatedMergeTree로 변환해야 하므로 ClickHouse 클러스터가 필요해요. 단일 인스턴스를 실행 중이라면 대신 "remoteSecure를 사용한 self-managed ClickHouse와 ClickHouse Cloud 간 마이그레이션"의 단계를 따르세요.

OSS 준비 (OSS preparation)

먼저 examples 저장소의 Docker Compose 구성을 사용해 ClickHouse 클러스터를 띄울게요. 이미 실행 중인 ClickHouse 클러스터가 있다면 클러스터를 띄우는 것은 무시해도 돼요.

  1. examples 저장소를 로컬 머신에 클론합니다.
  2. 터미널에서 examples/docker-compose-recipes/recipes/cluster_2S_2Rcd합니다.
  3. Docker가 실행 중인지 확인한 뒤 ClickHouse 클러스터를 시작합니다:
docker compose up

다음이 보일 거예요:

[+] Running 7/7
 ✔ Container clickhouse-keeper-01  Created  0.1s
 ✔ Container clickhouse-keeper-02  Created  0.1s
 ✔ Container clickhouse-keeper-03  Created  0.1s
 ✔ Container clickhouse-01         Created  0.1s
 ✔ Container clickhouse-02         Created  0.1s
 ✔ Container clickhouse-04         Created  0.1s
 ✔ Container clickhouse-03         Created  0.1s

폴더 루트에서 새 터미널 창을 열고 다음 명령으로 클러스터의 첫 번째 노드에 연결하세요:

docker exec -it clickhouse-01 clickhouse-client

MergeTree 테이블에서 ReplicatedMergeTree 테이블로

ClickHouse Cloud는 SharedMergeTree와 함께 동작해요. 백업을 복원할 때 ClickHouse는 ReplicatedMergeTree 테이블을 자동으로 SharedMergeTree 테이블로 변환해요. 클러스터를 실행 중이라면 테이블이 이미 ReplicatedMergeTree 엔진을 쓰고 있을 가능성이 높아요. 아니라면 백업 전에 MergeTree 테이블을 ReplicatedMergeTree로 변환해야 해요.

MergeTree 테이블을 ReplicatedMergeTree로 변환하는 방법을 시연하기 위해, MergeTree 테이블로 시작해서 나중에 ReplicatedMergeTree로 변환할 거예요. New York 택시 데이터 가이드의 첫 두 단계를 따라 샘플 테이블을 만들고 데이터를 로드할 거예요. 편의를 위해 그 단계를 아래에 포함했어요. 다음 명령을 실행해서 새 데이터베이스를 만들고 S3 버킷의 데이터를 새 테이블에 삽입하세요:

CREATE DATABASE nyc_taxi;

CREATE TABLE nyc_taxi.trips_small_adapted (
    trip_id             UInt32,
    pickup_datetime     DateTime,
    dropoff_datetime    DateTime,
    pickup_longitude    Nullable(Float64),
    pickup_latitude     Nullable(Float64),
    dropoff_longitude   Nullable(Float64),
    dropoff_latitude    Nullable(Float64),
    passenger_count     UInt8,
    trip_distance       Float32,
    fare_amount         Float32,
    extra               Float32,
    tip_amount          Float32,
    tolls_amount        Float32,
    total_amount        Float32,
    payment_type        Enum('CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4, 'UNK' = 5),
    pickup_ntaname      LowCardinality(String),
    dropoff_ntaname     LowCardinality(String)
)
ENGINE = MergeTree
PRIMARY KEY (pickup_datetime, dropoff_datetime);
INSERT INTO nyc_taxi.trips_small_adapted
SELECT
    trip_id,
    pickup_datetime,
    dropoff_datetime,
    pickup_longitude,
    pickup_latitude,
    dropoff_longitude,
    dropoff_latitude,
    passenger_count,
    trip_distance,
    fare_amount,
    extra,
    tip_amount,
    tolls_amount,
    total_amount,
    payment_type,
    pickup_ntaname,
    dropoff_ntaname
FROM s3(
    'https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_{0..2}.gz',
    'TabSeparatedWithNames'
);

다음 명령으로 테이블을 DETACH하세요:

DETACH TABLE nyc_taxi.trips_small_adapted;

그런 다음 replicated로 attach하세요:

ATTACH TABLE nyc_taxi.trips_small_adapted AS REPLICATED;

마지막으로 replica 메타데이터를 복원하세요:

SYSTEM RESTORE REPLICA nyc_taxi.trips_small_adapted;

ReplicatedMergeTree로 변환됐는지 확인하세요:

SELECT engine
FROM system.tables
WHERE name = 'trips_small_adapted' AND database = 'nyc_taxi';
┌─engine──────────────┐
│ ReplicatedMergeTree │
└─────────────────────┘

이제 나중에 S3 버킷에서 백업을 복원할 준비로 Cloud 서비스를 설정할 준비가 됐어요.

ReplicatedMergeTree가 있는 Distributed 테이블

여러 샤드에 걸쳐 distributed 테이블을 사용하는 설정이라면, 각 노드에 로컬 ReplicatedMergeTree 테이블이 있고 쿼리 진입점으로 Distributed 테이블이 있어야 해요. 다음 명령을 실행해서 모든 클러스터 노드에 로컬 replicated 테이블을 만드세요:

CREATE DATABASE IF NOT EXISTS nyc_taxi ON CLUSTER 'cluster_2S_2R';

CREATE TABLE nyc_taxi.trips_small_dist_local ON CLUSTER 'cluster_2S_2R'
(
    trip_id             UInt32,
    pickup_datetime     DateTime,
    dropoff_datetime    DateTime,
    pickup_longitude    Nullable(Float64),
    pickup_latitude     Nullable(Float64),
    dropoff_longitude   Nullable(Float64),
    dropoff_latitude    Nullable(Float64),
    passenger_count     UInt8,
    trip_distance       Float32,
    fare_amount         Float32,
    extra               Float32,
    tip_amount          Float32,
    tolls_amount        Float32,
    total_amount        Float32,
    payment_type        Enum('CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4, 'UNK' = 5),
    pickup_ntaname      LowCardinality(String),
    dropoff_ntaname     LowCardinality(String)
)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{database}/{table}/{shard}', '{replica}')
PRIMARY KEY (pickup_datetime, dropoff_datetime);

그런 다음 그 위에 Distributed 테이블을 만드세요:

CREATE TABLE nyc_taxi.trips_small_dist ON CLUSTER 'cluster_2S_2R'
(
    trip_id             UInt32,
    pickup_datetime     DateTime,
    dropoff_datetime    DateTime,
    pickup_longitude    Nullable(Float64),
    pickup_latitude     Nullable(Float64),
    dropoff_longitude   Nullable(Float64),
    dropoff_latitude    Nullable(Float64),
    passenger_count     UInt8,
    trip_distance       Float32,
    fare_amount         Float32,
    extra               Float32,
    tip_amount          Float32,
    tolls_amount        Float32,
    total_amount        Float32,
    payment_type        Enum('CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4, 'UNK' = 5),
    pickup_ntaname      LowCardinality(String),
    dropoff_ntaname     LowCardinality(String)
)
ENGINE = Distributed('cluster_2S_2R', 'nyc_taxi', 'trips_small_dist_local', rand());

distributed 테이블을 통해 데이터를 삽입하세요:

INSERT INTO nyc_taxi.trips_small_dist
SELECT
    trip_id,
    pickup_datetime,
    dropoff_datetime,
    pickup_longitude,
    pickup_latitude,
    dropoff_longitude,
    dropoff_latitude,
    passenger_count,
    trip_distance,
    fare_amount,
    extra,
    tip_amount,
    tolls_amount,
    total_amount,
    payment_type,
    pickup_ntaname,
    dropoff_ntaname
FROM s3(
    'https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_{0..2}.gz',
    'TabSeparatedWithNames'
);

Cloud 준비 (Cloud preparation)

데이터를 새 Cloud 서비스로 복원할 거예요. 아래 단계에 따라 새 Cloud 서비스를 만들세요.

  1. Cloud 콘솔 열기https://console.clickhouse.cloud/로 이동합니다.
  2. 새 서비스 만들기
  3. 서비스 구성 및 생성 — 원하는 리전과 구성을 선택한 뒤 Create service를 클릭합니다.
  4. 액세스 역할 만들기 — SQL 콘솔을 엽니다.

S3 액세스 설정

S3에서 백업을 복원하려면 ClickHouse Cloud와 S3 버킷 사이의 보안 액세스를 구성해야 해요.

  1. "S3 데이터 보안 액세스"의 단계를 따라 액세스 역할을 만들고 역할 ARN을 얻습니다.
  2. "S3 버킷과 IAM 역할 만드는 방법"에서 만든 S3 버킷 정책을 이전 단계의 역할 ARN을 추가해서 업데이트합니다.

업데이트된 S3 버킷 정책은 이런 모습이에요:

{
    "Version": "2012-10-17",
    "Id": "Policy123456",
    "Statement": [
        {
            "Sid": "abc123",
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::123456789123:role/ClickHouseAccess-001",
                    "arn:aws:iam::123456789123:user/docs-s3-user"
                ]
            },
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::ch-docs-s3-bucket",
                "arn:aws:s3:::ch-docs-s3-bucket/*"
            ]
        }
    ]
}

정책은 두 ARN을 모두 포함해요:

  • IAM 사용자 (docs-s3-user): self-managed ClickHouse 클러스터가 S3로 백업할 수 있게 해 줌
  • ClickHouse Cloud 역할 (ClickHouseAccess-001): Cloud 서비스가 S3에서 복원할 수 있게 해 줌

백업 만들기 (self-managed 배포에서)

각 샤드는 독립적으로 백업해야 해요. 각 샤드의 노드에 연결하고 샤드별 고유한 목적지 경로로 백업 명령을 실행하세요. BUCKET_URL, KEY_ID, SECRET_KEY를 여러분의 AWS 자격 증명으로 바꾸세요. "S3 버킷과 IAM 역할 만드는 방법" 가이드가 아직 이 값들이 없다면 얻는 방법을 보여줘요.

Shard 1:

BACKUP DATABASE nyc_taxi
TO S3(
  'BUCKET_URL/backup_s1.zip',
  'KEY_ID',
  'SECRET_KEY'
)

Shard 2:

BACKUP DATABASE nyc_taxi
TO S3(
  'BUCKET_URL/backup_s2.zip',
  'KEY_ID',
  'SECRET_KEY'
)

모든 것이 올바르게 구성되었다면 백업에 할당된 고유 id와 백업 상태를 포함하는 아래와 유사한 응답을 볼 수 있어요.

Query id: efcaf053-75ed-4924-aeb1-525547ea8d45

┌─id───────────────────────────────────┬─status─────────┐
│ e73b99ab-f2a9-443a-80b4-533efe2d40b3 │ BACKUP_CREATED │
└──────────────────────────────────────┴────────────────┘

단일 노드 배포: distributed 테이블을 사용하지 않는다면 단일 명령으로 전체 데이터베이스를 백업할 수 있어요:

BACKUP DATABASE nyc_taxi
TO S3(
  'BUCKET_URL',
  'KEY_ID',
  'SECRET_KEY'
)

이전에 비어 있던 S3 버킷을 확인하면 폴더 몇 개가 생긴 걸 볼 수 있어요. 전체 마이그레이션을 수행한다면 다음 명령으로 전체 서버를 백업할 수 있어요:

BACKUP
TABLE system.users,
TABLE system.roles,
TABLE system.settings_profiles,
TABLE system.row_policies,
TABLE system.quotas,
TABLE system.functions,
ALL EXCEPT DATABASES INFORMATION_SCHEMA, information_schema, system
TO S3(
  'BUCKET_ID',
  'KEY_ID',
  'SECRET_ID'
)
SETTINGS
  compression_method='lzma',
  compression_level=3;

위 명령은 다음을 백업해요:

  • 모든 사용자 데이터베이스와 테이블
  • 사용자 계정과 비밀번호
  • 역할과 권한
  • 설정 프로파일
  • 행 정책
  • 쿼터
  • 사용자 정의 함수

다른 Cloud Service Provider(CSP)를 사용한다면 TO S3()(AWS와 GCP 둘 다)와 TO AzureBlobStorage() 문법을 사용할 수 있어요. 매우 큰 데이터베이스의 경우 ASYNC를 사용해서 백업을 백그라운드에서 실행하는 것을 고려하세요:

BACKUP DATABASE my_database 
TO S3('https://your-bucket.s3.amazonaws.com/backup.zip', 'key', 'secret')
ASYNC;
       
-- Returns immediately with backup ID
-- Example result:
-- ┌─id──────────────────────────────────┬─status────────────┐
-- │ abc123-def456-789                   │ CREATING_BACKUP   │
-- └─────────────────────────────────────┴───────────────────┘

백업 id는 백업 진행 상황을 모니터링하는 데 사용할 수 있어요:

SELECT * 
FROM system.backups 
WHERE id = 'abc123-def456-789'

증분 백업(incremental backups)도 가능해요. 백업 전반에 대한 자세한 내용은 backup and restore 문서를 참고하세요.

ClickHouse Cloud로 복원하기 (Restore to ClickHouse Cloud)

각 샤드의 백업을 한 번에 하나씩 Cloud 서비스로 복원하세요. ROLE_ARN"S3 데이터 보안 액세스"에서 얻은 값으로 설정하세요. 두 번째(및 이후) 복원에는 SETTINGS allow_non_empty_tables=true를 사용해서 샤드 데이터가 충돌로 실패하는 대신 이미 복원된 테이블에 추가되도록 하세요.

Shard 1:

RESTORE DATABASE nyc_taxi
FROM S3(
    'BUCKET_URL/backup_s1.zip',
    extra_credentials(role_arn = 'ROLE_ARN')
)

Shard 2:

RESTORE DATABASE nyc_taxi
FROM S3(
    'BUCKET_URL/backup_s2.zip',
    extra_credentials(role_arn = 'ROLE_ARN')
)
SETTINGS allow_non_empty_tables=true;

비-distributed 배포: distributed 테이블을 사용하지 않는다면 단일 명령으로 데이터베이스를 복원하세요:

RESTORE DATABASE nyc_taxi
FROM S3(
    'BUCKET_URL',
    extra_credentials(role_arn = 'ROLE_ARN')
)

비슷한 방식으로 전체 서비스 복원을 할 수 있어요:

RESTORE
    TABLE system.users,
    TABLE system.roles,
    TABLE system.settings_profiles,
    TABLE system.row_policies,
    TABLE system.quotas,
    ALL EXCEPT DATABASES INFORMATION_SCHEMA, information_schema, system
FROM S3(
    'BUCKET_URL',
    extra_credentials(role_arn = 'ROLE_ARN')
)

복원이 완료된 후 Cloud에서 데이터를 사용할 수 있는지 확인할 수 있어요:

-- ClickHouse Cloud restores everything in your local table
SELECT count() from nyc_taxi.trips_small_dist_local;
3000317

ClickHouse Cloud가 내부적으로 SharedMergeTree를 사용하므로 이제 이전 distributed 테이블은 더 이상 필요 없어요. 그것을 드롭하고, 쿼리에서 원래 테이블 이름을 보존하는 뷰로 대체할 수 있어요:

DROP TABLE drop table nyc_taxi.trips_small_dist;
CREATE VIEW nyc_taxi.trips_small_dist AS SELECT * FROM nyc_taxi.trips_small_dist_local;
SELECT count() from nyc_taxi.trips_small_dist;
3000317

비-distributed ReplicatedMergeTree 테이블은 SharedMergeTree로 복원돼요:

SELECT count() FROM nyc_taxi.trips_small_adapted;
3000317

더 알아보기 (Learn more)