엔드 투 엔드 마이그레이션: CloudSQL 데이터베이스에서 Cloud Spanner (GoogleSQL)로

1. 시작하기 전에

이 Codelab에서는 Cloud SQL의 단일 MySQL 데이터베이스를 GoogleSQL 언어를 사용하는 Cloud Spanner 데이터베이스로 마이그레이션하는 방법을 안내합니다. 핵심 단계를 보여주는 기본적인 엔드 투 엔드 마이그레이션 흐름에 중점을 둡니다. Spanner Migration Tool (SMT), Dataflow, Datastream, PubSub, Google Cloud Storage 등의 Google Cloud 서비스를 사용합니다.

다루는 내용:

  • 샘플 Cloud SQL 및 Cloud Spanner 인스턴스를 설정하는 방법
  • Spanner 마이그레이션 도구 (SMT)를 사용하여 Cloud SQL MySQL 스키마를 Spanner 호환 스키마로 변환하는 방법
  • Dataflow를 사용하여 Cloud SQL에서 Cloud Spanner로 대량 데이터 마이그레이션을 실행하는 방법
  • Datastream 및 Dataflow를 사용하여 Cloud SQL에서 Cloud Spanner로 지속적인 복제 (CDC)를 설정하는 방법
  • Cloud Spanner에서 Cloud SQL로의 역방향 복제를 설정하는 방법

이 Codelab에서 다루지 않는 내용은 다음과 같습니다.

  • 샤딩된 인스턴스에서 이전
  • 마이그레이션 중 복잡한 데이터 변환
  • 고급 오류 처리 또는 데드 레터 큐 (DLQ)
  • 마이그레이션 성능 조정
  • 애플리케이션 이전: 이 Codelab에서는 데이터베이스 레이어 (스키마 및 데이터)에 중점을 둡니다. 애플리케이션 서비스를 재배포하거나 이전하는 운영 프로세스는 다루지 않습니다.

필요한 항목

  • 결제가 사용 설정된 Google Cloud 프로젝트.
  • API를 사용 설정하고 Cloud SQL, Spanner, Dataflow, Datastream, GCS 리소스를 만들고 관리할 수 있는 충분한 IAM 권한 프로젝트 Owner 역할이 Codelab에서는 가장 간단하지만, 더 구체적인 역할은 '환경 설정'에서 다룹니다.
  • 웹브라우저(예: Chrome)
  • Google Cloud 콘솔 및 gcloud와 같은 명령줄 도구에 대한 기본적인 이해
  • 셸 환경에 대한 액세스 gcloud이 포함된 Cloud Shell을 사용하는 것이 좋습니다.

위 설정에 대한 자세한 내용은 환경 설정 섹션을 참고하세요.

2. 마이그레이션 프로세스 이해

데이터베이스를 마이그레이션하려면 소스 CloudSQL 데이터베이스 인스턴스에서 Spanner 인스턴스로 데이터를 마이그레이션해야 합니다. 이 섹션에서는 마이그레이션에 사용되는 아키텍처와 주요 도구를 간략하게 설명합니다.

마이그레이션 흐름 아키텍처

마이그레이션 프로세스에는 다음 단계가 포함됩니다.

1. 스키마 변환:

  • 목적: 소스 데이터베이스 스키마를 호환되는 Cloud Spanner 스키마로 변환합니다.
  • 도구: Spanner 마이그레이션 도구 (SMT)
  • 프로세스: SMT는 소스 데이터베이스 스키마를 분석하고 이에 상응하는 Spanner 데이터 정의 언어 (DDL)를 생성합니다. 타겟 Spanner 인스턴스에서 데이터베이스가 생성되고 DDL이 자동으로 적용됩니다.

2. 대량 데이터 이전:

  • 목적: 소스 데이터베이스에서 프로비저닝된 Spanner 테이블로 기존 데이터를 초기 전체 로드합니다.
  • 도구: Google에서 제공하는 Sourcedb to Spanner 템플릿을 사용하는 Dataflow
  • 프로세스: 이 Dataflow 작업은 지정된 소스 테이블에서 모든 데이터를 읽어 해당 Spanner 테이블에 씁니다. 이는 Spanner 스키마가 생성된 후에 실행됩니다.

3. 라이브 마이그레이션 (CDC):

  • 목적: 소스 데이터베이스의 진행 중인 변경사항을 거의 실시간으로 Cloud Spanner에 캡처하고 적용하여 마이그레이션 중 다운타임을 최소화합니다.
  • 도구:
  • Datastream: 소스 데이터베이스의 변경사항 (삽입, 업데이트, 삭제)을 캡처하여 Cloud Storage (GCS)에 씁니다.
  • Dataflow: Datastream to Spanner 템플릿을 사용하여 GCS에서 변경 이벤트를 읽고 Cloud Spanner에 적용합니다.

4. 역방향 복제:

  • 목적: Cloud Spanner에서 소스 데이터베이스로 데이터 변경사항을 복제합니다. 이는 대체 전략, 단계적 마이그레이션 또는 특정 사용 사례를 위해 소스에 복제본을 유지하는 데 유용할 수 있습니다.
  • 도구: Spanner to SourceDb 템플릿을 사용하는 Dataflow
  • 처리: 이 작업은 Spanner 변경 내역을 활용하여 Spanner의 수정사항을 캡처하고 소스 데이터베이스 인스턴스에 다시 씁니다.

다음 다이어그램은 구성요소와 데이터 흐름을 보여줍니다.

b9e12d4151bf3bb7.png

주요 용어:

  • Spanner 마이그레이션 도구 (SMT): MySQL 스키마를 평가하고, Spanner 스키마에 상응하는 항목을 제안하고, Spanner 데이터 정의 언어 (DDL)를 생성하는 데 사용되는 도구입니다.
  • 데이터 정의 언어 (DDL): CREATE TABLE 문과 같이 데이터베이스 구조를 정의하고 수정하는 데 사용되는 문입니다. SMT는 Cloud SQL 스키마를 기반으로 Spanner DDL을 생성합니다.
  • Dataflow: 완전 관리형 서버리스 데이터 처리 서비스입니다. 이 Codelab에서는 일괄 데이터 전송, Datastream 변경사항 적용, 역방향 복제를 위해 Google에서 제공하는 템플릿을 실행하는 데 사용됩니다.
  • Datastream: 서버리스 변경 데이터 캡처 (CDC) 및 복제 서비스입니다. 이 Codelab에서는 Cloud SQL의 변경사항을 Cloud Storage로 스트리밍하는 데 사용됩니다.
  • Spanner 변경 스트림: 데이터 변경사항 (삽입, 업데이트, 삭제)을 실시간으로 스트리밍할 수 있는 Spanner 기능으로, 역방향 복제의 소스로 사용됩니다.
  • Pub/Sub: 이벤트를 생성하는 서비스와 이벤트를 처리하는 서비스를 분리하는 데 사용되는 메시징 서비스입니다. 이 Codelab에서는 Datastream이 Cloud Storage에 새 변경사항 파일을 업로드할 때마다 Dataflow가 업데이트를 처리하도록 트리거합니다.

3. 환경 설정

마이그레이션을 시작하기 전에 Google Cloud 프로젝트를 설정하고 필요한 서비스를 사용 설정해야 합니다.

1. Google Cloud 프로젝트 선택 또는 만들기

이 Codelab의 서비스를 사용하려면 결제가 사용 설정된 Google Cloud 프로젝트가 필요합니다.

  1. Google Cloud 콘솔에서 프로젝트 선택기 페이지로 이동합니다. 프로젝트 선택기로 이동
  2. Google Cloud 프로젝트를 선택하거나 만듭니다.
  3. 프로젝트에 결제가 사용 설정되어 있는지 확인하세요. 프로젝트에 결제가 사용 설정되어 있는지 확인하는 방법을 알아보세요.

2. Cloud Shell 열기

Cloud Shell은 Google Cloud에서 실행되는 명령줄 환경으로, gcloud CLI 및 기타 필요한 도구가 미리 로드되어 있습니다.

  • Google Cloud 콘솔의 오른쪽 상단에 있는 Cloud Shell 활성화 버튼을 클릭합니다.
  • 콘솔 하단에 있는 새 프레임 내에 Cloud Shell 세션이 열리면서 명령줄 프롬프트가 표시됩니다.

22d57633bc12106d.png

3. 프로젝트 및 환경 변수 설정

Cloud Shell에서 프로젝트 ID와 사용할 리전의 환경 변수를 설정합니다.

export PROJECT_ID=$(gcloud config get-value project)
export REGION="us-central1" # Or your preferred region
export ZONE="us-central1-a" # Or a zone within your selected region

gcloud config set project $PROJECT_ID
gcloud config set compute/region $REGION
gcloud config set compute/zone $ZONE

echo "Project ID: $PROJECT_ID"
echo "Region: $REGION"
echo "Zone: $ZONE"

4. 필수 Google Cloud API 사용 설정

Cloud Spanner, Dataflow, Datastream 및 기타 관련 서비스에 필요한 API를 사용 설정합니다.

gcloud services enable \
  spanner.googleapis.com \
  dataflow.googleapis.com \
  datastream.googleapis.com \
  pubsub.googleapis.com \
  storage.googleapis.com \
  compute.googleapis.com \
  sqladmin.googleapis.com \
  servicenetworking.googleapis.com \
  cloudresourcemanager.googleapis.com

이 명령어를 완료하는 데 몇 분이 걸릴 수 있습니다.

5. 서비스 계정 권한 구성

Dataflow 작업과 Datastream은 다른 Google Cloud 서비스와 상호작용하기 위해 특정 권한이 필요합니다. 이 Codelab의 Dataflow 작업은 기본 Compute Engine 서비스 계정을 사용합니다.

먼저 프로젝트 번호를 가져옵니다.

export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
export SA_EMAIL="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"

이제 Compute Engine 기본 서비스 계정에 필요한 IAM 역할을 부여합니다.

# Role for Dataflow to run jobs
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/dataflow.admin" \
    --condition=None

# Roles for Dataflow workers
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/dataflow.worker" \
    --condition=None

# Role to connect to Cloud SQL instance
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/cloudsql.client" \
    --condition=None

# Role to read/write from Cloud Spanner
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/spanner.databaseUser" \
    --condition=None

# Role to access GCS buckets (Datastream output, Dataflow temp, JDBC driver)
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/storage.objectAdmin" \
    --condition=None

# Roles for Datastream and Pub/Sub (for CDC)
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/datastream.viewer"

gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/pubsub.subscriber"

6. Cloud Storage 버킷 만들기

다른 리소스와 동일한 리전에 GCS 버킷을 만듭니다. 이 버킷은 JDBC 드라이버와 Datastream 출력을 저장하며 Dataflow에서 임시 파일에 사용됩니다.

export BUCKET_NAME="migration-${PROJECT_ID}-bucket"
gcloud storage buckets create gs://$BUCKET_NAME --location=$REGION
echo "Created bucket: gs://$BUCKET_NAME"

7. Spanner 마이그레이션 도구 (SMT) 설치

Cloud Shell 환경에 Spanner 마이그레이션 도구 (SMT)가 설치되어 있는지 확인합니다.

sudo apt-get update && sudo apt-get install google-cloud-cli-spanner-migration-tool

# Verify installation 
gcloud alpha spanner migrate web --help

이 명령어를 실행하면 SMT 웹 인터페이스의 도움말 정보가 표시되어 gcloud 구성요소가 설치되었음을 확인할 수 있습니다. 이 Codelab에서는 동일한 구성요소에 포함된 SMT의 CLI 기능을 사용합니다.

4. 소스 Cloud SQL 데이터베이스 설정

이 섹션에서는 소스 데이터베이스 역할을 하는 공개 IP를 사용하여 MySQL용 Cloud SQL 인스턴스를 만들고 구성합니다.

1. MySQL용 Cloud SQL 인스턴스 만들기

Cloud Shell에서 다음 gcloud 명령어를 실행하여 MySQL 8.0 인스턴스를 만듭니다. 바이너리 로깅이 사용 설정되어 있고 (Datastream에 필요) 인스턴스가 공개 IP로 구성되어 있습니다.

export SQL_INSTANCE_NAME="source-mysql-instance"
export DB_ROOT_PASSWORD="Welcome@1" # Replace with a strong password if you prefer

gcloud sql instances create $SQL_INSTANCE_NAME \
  --database-version=MYSQL_8_0 \
  --tier=db-n1-standard-2 \
  --region=$REGION \
  --root-password=$DB_ROOT_PASSWORD \
  --enable-bin-log \
  --assign-ip
  • --enable-bin-log: Datastream이 변경사항을 캡처하는 데 필요합니다.
  • --assign-ip: 인스턴스에 공개 IP 주소가 할당되도록 합니다.

인스턴스를 만드는 데 몇 분 정도 걸립니다. CloudSQL 인스턴스 페이지에서 인스턴스가 생성되었는지 확인할 수 있습니다.

2. 승인된 네트워크 구성

공개 IP를 통해 인스턴스에 연결하려면 '승인된 네트워크' 목록에 IP 주소를 추가해야 합니다.

Cloud Shell IP를 가져옵니다.

export CLOUD_SHELL_IP=$(curl -s ipinfo.io/ip)
echo "Your Cloud Shell IP: $CLOUD_SHELL_IP"

Cloud Shell IP 승인 및 액세스 권한 열기

다음 명령어는 Cloud Shell IP를 추가합니다. 또한 모든 IP 주소에서 액세스를 허용하는 0.0.0.0/0도 추가됩니다. 이는 복잡한 네트워크 설정 없이 Dataflow 작업자에서 연결을 간소화하는 데 필요합니다.

gcloud sql instances patch $SQL_INSTANCE_NAME \
  --authorized-networks="${CLOUD_SHELL_IP}/32,0.0.0.0/0"

3. Cloud Shell에서 Cloud SQL 인스턴스에 연결

할당된 공개 IP 주소 가져오기

export SQL_INSTANCE_IP=$(gcloud sql instances list --filter="name=$SQL_INSTANCE_NAME" --format="value(PRIMARY_ADDRESS)") 
echo "Cloud SQL Public IP: $SQL_INSTANCE_IP"

이 IP 주소는 연결에 사용됩니다.

CloudShell에서 Cloud SQL 인스턴스에 연결

표준 mysql 클라이언트를 사용하여 획득한 공개 IP 주소를 사용하여 연결합니다.

mysql -h $SQL_INSTANCE_IP -u root -p

메시지가 표시되면 설정한 루트 비밀번호 (Welcome@1)를 입력합니다. 이제 mysql> 프롬프트가 표시됩니다.

4. 데이터베이스 및 샘플 데이터 만들기

mysql> 프롬프트 내에서 다음 SQL 명령어를 실행합니다.

CREATE DATABASE music_db;
USE music_db;

CREATE TABLE Singers (
    SingerId   BIGINT NOT NULL,
    FirstName  VARCHAR(1024),
    LastName   VARCHAR(1024),
    BirthDate  DATE,
    AlbumCount BIGINT,
    PRIMARY KEY (SingerId)
);

CREATE TABLE Albums (
    SingerId     BIGINT NOT NULL,
    AlbumId      BIGINT NOT NULL,
    AlbumTitle   VARCHAR(1024),
    ReleaseDate  DATE,
    PRIMARY KEY (SingerId, AlbumId),
    CONSTRAINT FK_Albums_Singers FOREIGN KEY (SingerId) REFERENCES Singers (SingerId)
);

INSERT INTO Singers (SingerId, FirstName, LastName, BirthDate, AlbumCount) VALUES
(1, 'Marc', 'Richards', '1970-09-03', 2),
(2, 'Catalina', 'Smith', '1990-08-17', 1),
(3, 'Alice', 'Trentor', '1991-10-02', 3);

INSERT INTO Albums (SingerId, AlbumId, AlbumTitle, ReleaseDate) VALUES
(1, 1, 'Total Junk', '2014-03-15'),
(1, 2, 'Go Go Go', '2016-11-01'),
(2, 1, 'Green', '2018-02-28'),
(3, 1, 'Blue', '2019-01-10'),
(3, 2, 'Red', '2020-05-22'),
(3, 3, 'Purple', '2022-11-11');

위 스키마의 덤프 파일은 여기에서 확인할 수 있습니다.

5. 데이터 확인

데이터가 있는지 빠르게 확인합니다.

SELECT 'Singers music_db' as tbl, COUNT(*) FROM music_db.Singers
UNION ALL
SELECT 'Albums music_db', COUNT(*) FROM music_db.Albums;

EXIT;

각 테이블의 개수가 표시됩니다.

+------------------+----------+
| tbl              | COUNT(*) |
+------------------+----------+
| Singers music_db |        3 |
| Albums music_db  |        6 |
+------------------+----------+

5. Cloud Spanner 설정

이제 데이터를 이전할 대상 Cloud Spanner 인스턴스를 설정합니다.

1. Cloud Spanner 인스턴스 만들기

Cloud SQL 인스턴스와 동일한 리전에 Cloud Spanner 인스턴스를 만듭니다. 이 명령어는 처리 단위 100개를 사용하여 이 Codelab에 적합한 작은 인스턴스를 만듭니다.

export SPANNER_INSTANCE_NAME="target-spanner-instance"
export SPANNER_DATABASE_NAME="music-db-migrated"
export SPANNER_CONFIG="regional-${REGION}"

gcloud spanner instances create $SPANNER_INSTANCE_NAME \
  --config=$SPANNER_CONFIG \
  --description="Target Spanner Instance" \
  --processing-units=100

인스턴스를 만드는 데 1~2분 정도 걸릴 수 있습니다.

6. Spanner 마이그레이션 도구 (SMT)를 사용하여 스키마 변환

SMT CLI를 사용하여 MySQL 데이터베이스를 분석 (music_db)하고 Spanner 스키마 정의 언어 (DDL)를 생성합니다. Cloud SQL 인스턴스가 공개 IP와 적절한 승인된 네트워크로 구성되어 있으므로 SMT가 직접 연결할 수 있습니다.

1. SMT 환경 준비

이전 단계에서 필요한 환경 변수가 설정되었는지 확인합니다.

echo "Cloud SQL Instance Public IP: $SQL_INSTANCE_IP" 
echo "Cloud SQL Root Password: $DB_ROOT_PASSWORD" 
echo "Spanner Instance: $SPANNER_INSTANCE_NAME" 
echo "Spanner Database: $SPANNER_DATABASE_NAME" 
echo "Project ID: $PROJECT_ID"

2. music_db의 스키마 변환 실행

Cloud SQL 공개 IP 주소에 직접 연결하는 SMT schema 명령어를 실행합니다.

gcloud alpha spanner migrate schema \
--source=mysql \
--source-profile="host=${SQL_INSTANCE_IP},port=3306,user=root,password=${DB_ROOT_PASSWORD},dbName=music_db" \
--target-profile="project=${PROJECT_ID},instance=${SPANNER_INSTANCE_NAME},dbName=${SPANNER_DATABASE_NAME}" \
--prefix="music-db"

이 명령어는 프록시를 통해 Cloud SQL 인스턴스에 연결하고 music-db로 시작하는 스키마 파일을 생성합니다.

3. 생성된 파일 검토

SMT는 현재 디렉터리에 몇 개의 파일을 만듭니다. 주요 항목은 다음과 같습니다.

  • music-db.schema.ddl.txt: 생성된 Spanner DDL 문입니다.
  • music-db-.overrides.json: 수동 매핑 변경사항이 포함된 스키마 재정의 파일입니다.
  • music-db.session.json: 스키마 이전의 세션 파일입니다.
  • music-db.report.txt: 스키마 변환에 관한 평가 보고서입니다.

ls music-db-*를 사용하여 나열할 수 있습니다.

4. Cloud Spanner에서 스키마 확인

Spanner 데이터베이스에 테이블이 생성되었는지 확인합니다.

gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME \
  --instance=$SPANNER_INSTANCE_NAME \
  --sql="SELECT table_name FROM information_schema.tables WHERE table_schema = '' ORDER BY table_name"

다음과 같은 출력이 표시됩니다.

table_name: Albums
table_name: Singers

선택사항: Spanner DDL을 확인하려면 다음 명령어를 실행합니다.

gcloud spanner databases ddl describe $SPANNER_DATABASE_NAME \
  --instance=$SPANNER_INSTANCE_NAME

7. 변경 데이터 캡처 (CDC) 초기화

이 섹션에서는 이전의 '레코더'를 설정합니다. 일괄 데이터 로드가 시작되기 전에 DatastreamPub/Sub을 구성하면 소스 데이터베이스에 적용된 모든 변경사항이 캡처되고 대기열에 추가되어 전환 중에 데이터가 손실되지 않습니다. 이 설정은 라이브 마이그레이션에 필요합니다.

1. Datastream 연결 프로필 만들기

소스 프로필 (Cloud SQL)

이 프로필은 Cloud SQL 인스턴스의 공개 IP에 연결됩니다. Datastream은 연결에 IP 허용 목록을 사용합니다.

export SQL_CP_NAME="mysql-src-cp"
gcloud datastream connection-profiles create $SQL_CP_NAME \
  --location=$REGION \
  --type=mysql \
  --mysql-hostname=$SQL_INSTANCE_IP \
  --mysql-port=3306 \
  --mysql-username=root \
  --mysql-password=$DB_ROOT_PASSWORD \
  --display-name="Cloud SQL Source - Public IP"

참고: 이 연결은 Cloud SQL 인스턴스의 승인된 네트워크에서 액세스를 허용하는 데 의존합니다. 앞서 0.0.0.0/0로 구성한 대로 Datastream의 공개 IP가 연결될 수 있습니다. 프로덕션 환경에서는 0.0.0.0/0Datastream IP 허용 목록 및 리전에 나열된 리전의 특정 IP 범위로 대체합니다.

대상 프로필 (Cloud Storage)

버킷의 루트를 가리킵니다.

export GCS_CP_NAME="gcs-dest-cp"
gcloud datastream connection-profiles create $GCS_CP_NAME \
  --location=$REGION \
  --type=google-cloud-storage \
  --bucket=$BUCKET_NAME \
  --root-path=/ \
  --display-name="GCS Destination" --force

2. Datastream 스트림 만들기

music_db에서 복제할 스트림을 만듭니다.

export STREAM_NAME="mysql-to-spanner-stream"
export GCS_STREAM_PATH="data/${STREAM_NAME}"

gcloud datastream streams create $STREAM_NAME \
  --location=$REGION \
  --display-name="MySQL to Spanner CDC Stream" \
  --source=$SQL_CP_NAME \
  --destination=$GCS_CP_NAME \
  --mysql-source-config=<(echo "
includeObjects:
  mysqlDatabases:
  - database: 'music_db'
") \
  --gcs-destination-config=<(echo "
path: ${GCS_STREAM_PATH}
fileRotationMb: 5
fileRotationInterval: 15s
avroFileFormat: {}
") \
  --backfill-none
  • Datastream은 gs://${BUCKET_NAME}/${GCS_STREAM_PREFIX}/ 아래에 파일을 씁니다.
  • Datastream은 파일을 Avro 형식으로 작성합니다. 실시간 마이그레이션 명령어를 실행하는 동안 파이프라인에서 파일을 올바르게 처리할 수 있도록 inputFileFormat을 avro로 지정합니다.
  • 더 작은 파일 순환 설정을 사용하면 코드랩에서 변경사항을 더 빠르게 확인할 수 있습니다.

이 명령은 완료하는 데 다소 시간이 걸릴 수 있습니다. 상태 확인: gcloud datastream streams describe $STREAM_NAME --location=$REGION

3. Datastream 스트림 시작

gcloud datastream streams update $STREAM_NAME \
  --location=$REGION \
  --state=RUNNING

상태 확인: gcloud datastream streams describe $STREAM_NAME --location=$REGION. 상태는 처음에는 STARTING이고 시간이 지나면 RUNNING가 됩니다. RUNNING 상태인지 확인한 후에만 다음 단계를 진행합니다.

4. GCS 알림을 위한 Pub/Sub 설정

Pub/Sub 주제를 만듭니다.

export PUBSUB_TOPIC="datastream-gcs-updates"
gcloud pubsub topics create $PUBSUB_TOPIC

GCS 알림 만들기

data/ 접두사 아래의 객체 생성 시 알림을 보냅니다.

gcloud storage buckets notifications create gs://${BUCKET_NAME} --topic=projects/$PROJECT_ID/topics/$PUBSUB_TOPIC --payload-format=json --object-prefix=data/

Pub/Sub 구독 만들기

권장 확인 기한을 포함합니다.

export PUBSUB_SUBSCRIPTION="datastream-gcs-sub"
gcloud pubsub subscriptions create $PUBSUB_SUBSCRIPTION \
  --topic=$PUBSUB_TOPIC \
  --ack-deadline=600

8. Cloud SQL에서 Spanner로 데이터 일괄 마이그레이션

이제 Spanner 스키마를 사용하여 Cloud SQL music_db 데이터베이스의 기존 데이터를 Cloud Spanner로 복사합니다. JDBC 액세스 가능 데이터베이스에서 Spanner로 데이터를 일괄 복사하도록 설계된 Sourcedb to Spanner Dataflow Flex 템플릿을 사용합니다.

1. music_db의 대량 마이그레이션 Dataflow 작업 실행

Cloud Shell에서 다음 명령어를 실행하여 Dataflow 작업을 시작합니다. 이 명령어는 gcloud dataflow flex-template run 명령어를 사용하여 Spanner로의 대량 JDBC 마이그레이션을 위한 Google 제공 템플릿을 참조합니다.

export JOB_NAME_MUSIC="mysql-music-db-to-spanner-bulk-$(date +%Y%m%d-%H%M%S)"
export MUSIC_DB_JDBC_URL="jdbc:mysql://${SQL_INSTANCE_IP}:3306/music_db"
export OUTPUT_DIR="gs://${BUCKET_NAME}/bulk-migration-output"

gcloud dataflow flex-template run $JOB_NAME_MUSIC \
  --project=$PROJECT_ID \
  --region=$REGION \
--template-file-gcs-location="gs://dataflow-templates-${REGION}/latest/flex/Sourcedb_to_Spanner_Flex" \
--max-workers=2 \
--num-workers=1 \
--worker-machine-type=n2-highmem-8 \
  --parameters \
sourceConfigURL="$MUSIC_DB_JDBC_URL",\
instanceId="$SPANNER_INSTANCE_NAME",\
databaseId="$SPANNER_DATABASE_NAME",\
projectId="$PROJECT_ID",\
outputDirectory="$OUTPUT_DIR/music_db",\
username="root",\
password="$DB_ROOT_PASSWORD",\
jdbcDriverClassName="com.mysql.cj.jdbc.Driver",\
jdbcDriverJars="gs://${BUCKET_NAME}/lib/mysql-connector-j-8.0.33.jar",\
spannerHost="https://batch-spanner.googleapis.com"

주요 매개변수 설명:

  • sourceConfigURL: 소스 music_db의 JDBC 연결 문자열입니다.
  • instanceId, databaseId, projectId: 타겟 Cloud Spanner 인스턴스 및 데이터베이스를 지정합니다.
  • outputDirectory: Dataflow가 이전하지 못한 레코드에 관한 정보를 쓰는 Cloud Storage 경로입니다.
  • jdbcDriverClassName: MySQL JDBC 드라이버를 지정합니다.
  • jdbcDriverJars: 스테이징된 JDBC 드라이버 JAR의 GCS 경로입니다.
  • spannerHost: Spanner 쓰기에 배치 최적화 엔드포인트를 사용합니다.
  • maxWorkers, numWorkers: Dataflow 작업의 확장/축소를 제어합니다. 이 작은 데이터 세트에서는 낮게 유지됩니다.

네트워크 참고: 이 작업은 공개 IP를 통해 Cloud SQL 인스턴스에 연결됩니다. 이는 이전에 인스턴스의 승인된 네트워크에 0.0.0.0/0를 추가했기 때문에 가능합니다. 이렇게 하면 외부 IP가 있는 Dataflow 작업자 VM이 데이터베이스에 도달할 수 있습니다.

2. Dataflow 작업 모니터링

Google Cloud 콘솔에서 작업 진행 상황을 추적할 수 있습니다.

  1. Dataflow 작업 페이지로 이동합니다. Dataflow 작업으로 이동
  2. mysql-music-db-to-spanner-bulk-...라는 작업을 찾아 클릭합니다.
  3. 작업 그래프와 측정항목을 확인합니다. 작업 상태가 성공으로 바뀔 때까지 기다립니다. 약 5~15분 정도 걸립니다.

ebbb94c0db535809.png

  • 작업에 문제가 발생하면 Dataflow 작업 세부정보 페이지의 로그 탭에서 오류 메시지를 검토하세요.
  • 작업 측정항목은 작업 진행 상황과 처리량, CPU 사용률과 같은 리소스 소비에 관한 자세한 정보를 제공합니다.

3. Cloud Spanner에서 데이터 확인

Dataflow 작업이 완료되면 데이터가 Spanner 테이블에 복사되었는지 확인합니다. gcloud를 사용하여 Spanner 데이터베이스를 쿼리합니다.

# Verify row counts
gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME --instance=$SPANNER_INSTANCE_NAME --sql="SELECT COUNT(*) as row_count FROM Singers" 
# Expected output: 3

gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME --instance=$SPANNER_INSTANCE_NAME --sql="SELECT COUNT(*) as row_count FROM Albums" 
# Expected output: 6 

# Inspect some data 
gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME --instance=$SPANNER_INSTANCE_NAME --sql="SELECT SingerId, FirstName, LastName FROM Singers ORDER BY SingerId"

예상 출력:

row_count: 3
row_count: 6
SingerId: 1
FirstName: Marc
LastName: Richards

SingerId: 2
FirstName: Catalina
LastName: Smith

SingerId: 3
FirstName: Alice
LastName: Trentor

이제 Cloud SQL에서 Cloud Spanner로의 초기 데이터 일괄 로드가 완료되었습니다. 다음 단계는 지속적인 변경사항을 포착하기 위해 실시간 복제를 설정하는 것입니다.

9. 라이브 마이그레이션 (CDC) 시작

이제 대량 데이터 로드가 완료되었으므로 Datastream을 사용하여 Cloud SQL에서 변경 데이터 캡처 (CDC) 이벤트를 캡처하고 Dataflow 스트리밍 작업을 사용하여 거의 실시간으로 Cloud Spanner에 이러한 변경사항을 적용하는 지속적인 복제 스트림을 설정합니다.

1. 라이브 마이그레이션 Dataflow 작업 실행

스트리밍 Dataflow 작업을 실행하여 GCS에서 읽고 Spanner에 씁니다. 이 템플릿은 GCS Pub/Sub 알림을 사용하여 새 파일을 즉시 처리합니다.

export JOB_NAME_CDC="datastream-to-spanner-cdc-$(date +%Y%m%d-%H%M%S)"
export DLQ_DIR="gs://${BUCKET_NAME}/dlq"

gcloud dataflow flex-template run $JOB_NAME_CDC \
  --project=$PROJECT_ID \
  --region=$REGION \
--worker-machine-type=n2-highmem-8 \
--template-file-gcs-location="gs://dataflow-templates-${REGION}/latest/flex/Cloud_Datastream_to_Spanner" \
  --parameters \
gcsPubSubSubscription="projects/${PROJECT_ID}/subscriptions/${PUBSUB_SUBSCRIPTION}",\
instanceId="$SPANNER_INSTANCE_NAME",\
databaseId="$SPANNER_DATABASE_NAME",\
projectId="$PROJECT_ID",\
inputFileFormat="avro",\
deadLetterQueueDirectory="$DLQ_DIR",\
streamName="projects/${PROJECT_ID}/locations/${REGION}/streams/${STREAM_NAME}"

주요 매개변수

  • gcsPubSubSubscription: GCS에서 새 파일 알림을 수신하는 Pub/Sub 구독입니다. 이렇게 하면 Datastream이 변경사항을 쓰는 즉시 작업에서 변경사항을 처리할 수 있습니다.
  • inputFileFormat="avro": Dataflow가 Datastream에서 Avro 파일을 예상하도록 지시합니다. 이는 Datastream '대상' 구성과 일치해야 합니다 (예: avroFileFormatjsonFileFormat).
  • deadLetterQueueDirectory: 나중에 수동 검토를 위해 처리되지 않은 레코드 (예: 스키마 불일치로 인해)를 작업에서 저장하는 GCS 경로입니다.
  • streamName: Dataflow 작업이 복제 상태와 메타데이터를 추적할 수 있도록 지원하는 Datastream 스트림의 전체 리소스 경로입니다.

Dataflow 작업 콘솔에서 작업 시작을 모니터링합니다.

2. 라이브 마이그레이션 테스트

CDC 파이프라인을 테스트하기 위해 소스 Cloud SQL music_db에 변경사항을 적용합니다.

Cloud SQL에 연결합니다.

mysql -h $SQL_INSTANCE_IP -u root -p

비밀번호 (Welcome@1)를 입력하고 데이터베이스를 선택합니다.

USE music_db;

-- INSERT
INSERT INTO Singers (SingerId, FirstName, LastName, BirthDate, AlbumCount) VALUES (4, 'Elena', 'Nadal', '1985-05-30', 0);
SELECT * FROM Singers WHERE SingerId = 4;

-- UPDATE
UPDATE Singers SET LastName = 'Richards-Smith' WHERE SingerId = 1;
SELECT * FROM Singers WHERE SingerId = 1;

-- DELETE
DELETE FROM Albums WHERE SingerId = 2; 
DELETE FROM Singers WHERE SingerId = 2;
SELECT * FROM Singers WHERE SingerId = 2;

EXIT;

Spanner에서 확인 (잠시 후):

# Verify INSERT: This should return the new row for Elena Nadal.
gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME \
  --instance=$SPANNER_INSTANCE_NAME \
  --sql="SELECT * FROM Singers WHERE SingerId = 4"

# Verify UPDATE: This should show LastName as Richards-Smith.
gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME \
  --instance=$SPANNER_INSTANCE_NAME \
  --sql="SELECT SingerId, FirstName, LastName FROM Singers WHERE SingerId = 1"

# Verify DELETE: This should now return 0 rows.
gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME \
  --instance=$SPANNER_INSTANCE_NAME \
  --sql="SELECT * FROM Albums WHERE SingerId = 2"

gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME \
  --instance=$SPANNER_INSTANCE_NAME \
  --sql="SELECT * FROM Singers WHERE SingerId = 2"

예상 출력:

SingerId: 4
FirstName: Elena
LastName: Nadal
BirthDate: 1985-05-30
AlbumCount: 0

SingerId: 1
FirstName: Marc
LastName: Richards-Smith

3. Spanner의 최종 확인

Spanner에서 Singers 테이블의 전체 상태를 확인합니다.

gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME \
  --instance=$SPANNER_INSTANCE_NAME \
  --sql="SELECT SingerId, FirstName, LastName, AlbumCount FROM Singers ORDER BY SingerId"

예상 출력:

SingerId: 1
FirstName: Marc
LastName: Richards-Smith
AlbumCount: 2

SingerId: 3
FirstName: Alice
LastName: Trentor
AlbumCount: 3

SingerId: 4
FirstName: Elena
LastName: Nadal
AlbumCount: 0

10. 역방향 복제 설정 (Spanner에서 Cloud SQL로)

Cloud SQL 데이터베이스를 롤백하거나 일정 기간 동안 Spanner와 동기화해야 하는 시나리오를 처리하려면 역방향 복제를 설정하면 됩니다. 이 파이프라인은 Spanner 변경 내역을 사용하여 Spanner의 변경사항을 캡처하고 Cloud SQL music_db에 다시 씁니다.

1. Spanner 변경 스트림 만들기

먼저 Spanner 데이터베이스에서 변경 스트림을 만들어 SingersAlbums 테이블의 변경사항을 추적해야 합니다.

export CHANGE_STREAM_NAME="MusicDBChangeStream"

gcloud spanner databases ddl update $SPANNER_DATABASE_NAME \
  --instance=$SPANNER_INSTANCE_NAME \
  --ddl="CREATE CHANGE STREAM $CHANGE_STREAM_NAME FOR Singers, Albums"

이제 이 변경 내역은 지정된 테이블에 대한 모든 데이터 수정사항을 기록합니다.

2. Dataflow 메타데이터용 Spanner 데이터베이스 만들기

Spanner to SourceDB Dataflow 템플릿에는 변경 내역 소비를 관리하기 위한 메타데이터를 저장할 별도의 Spanner 데이터베이스가 필요합니다.

export SPANNER_METADATA_DB_NAME="reverse-replication-metadata"

gcloud spanner databases create $SPANNER_METADATA_DB_NAME \
  --instance=$SPANNER_INSTANCE_NAME

3. Dataflow용 Cloud SQL 연결 구성 준비

Dataflow 템플릿에는 대상 Cloud SQL 데이터베이스의 연결 세부정보가 포함된 Cloud Storage의 JSON 파일이 필요합니다.

shard_config.json이라는 로컬 파일을 만듭니다.

cat << EOF > shard_config.json
[
  {
    "logicalShardId": "mysql_shard",
    "host": "${SQL_INSTANCE_IP}",
    "port": "3306",
    "user": "root",
    "password": "${DB_ROOT_PASSWORD}",
    "dbName": "music_db"
  }
]
EOF

이 파일을 GCS 버킷에 업로드합니다.

export SHARD_CONFIG_FILE="gs://${BUCKET_NAME}/shard_config.json"
gcloud storage cp shard_config.json $SHARD_CONFIG_FILE

4. 역방향 복제 Dataflow 작업 실행

Spanner_to_SourceDb Flex 템플릿을 사용하여 Dataflow 작업을 실행합니다.

export JOB_NAME_REVERSE="spanner-to-mysql-reverse-$(date +%Y%m%d-%H%M%S)"
export REVERSE_DLQ_DIR="gs://${BUCKET_NAME}/reverse-dlq"

gcloud dataflow flex-template run $JOB_NAME_REVERSE \
  --project=$PROJECT_ID \
  --region=$REGION \
--worker-machine-type=n2-highmem-8 \
--max-workers=2 \
--num-workers=1 \
--additional-experiments=use_runner_v2 \
--template-file-gcs-location="gs://dataflow-templates-${REGION}/latest/flex/Spanner_to_SourceDb" \
  --parameters \
changeStreamName="$CHANGE_STREAM_NAME",\
instanceId="$SPANNER_INSTANCE_NAME",\
databaseId="$SPANNER_DATABASE_NAME",\
spannerProjectId="$PROJECT_ID",\
metadataInstance="$SPANNER_INSTANCE_NAME",\
metadataDatabase="$SPANNER_METADATA_DB_NAME",\
sourceShardsFilePath="$SHARD_CONFIG_FILE",\
deadLetterQueueDirectory="$REVERSE_DLQ_DIR"

주요 매개변수

  • changeStreamName: 읽어올 Spanner 변경 내역의 이름입니다.
  • metadataInstance, metadataDatabase: 변경 내역 API 데이터 소비를 제어하기 위해 커넥터에서 사용되는 메타데이터를 저장하기 위한 Spanner 인스턴스/데이터베이스입니다.
  • sourceShardsFilePath: shard_config.json의 GCS 경로입니다.
  • filtrationMode: 기준에 따라 특정 레코드를 삭제하는 방법을 지정합니다. 기본값은 forward_migration (전방 마이그레이션 파이프라인을 사용하여 작성된 레코드 필터링)입니다.

네트워크 참고: Dataflow 작업자는 shard_config.json에 지정된 공개 IP를 사용하여 Cloud SQL 인스턴스에 연결합니다. 이 연결은 Cloud SQL 인스턴스의 승인된 네트워크에 있는 0.0.0.0/0 항목으로 인해 허용됩니다.

Dataflow 작업 콘솔에서 작업 시작을 모니터링합니다.

5. 역방향 복제 테스트

이제 Cloud Spanner에서 직접 변경하고 Cloud SQL에 반영되는지 확인합니다. 데이터 흐름 작업이 시작되고 처리 상태에 있는 경우에만 이렇게 하세요.

INSERT, UPDATE, DELETE 테스트

# INSERT: Insert a new singer (SingerId 5) into Spanner
gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME \
  --instance=$SPANNER_INSTANCE_NAME \
  --sql="INSERT INTO Singers (SingerId, FirstName, LastName, BirthDate, AlbumCount) VALUES (5, 'David', 'Chen', '1995-02-18', 0)"

# UPDATE: Update SingerId 3's AlbumCount in Spanner
gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME \
  --instance=$SPANNER_INSTANCE_NAME \
  --sql="UPDATE Singers SET AlbumCount = 5 WHERE SingerId = 3"

# DELETE: Delete SingerId 1 from Spanner
gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME \
--instance=$SPANNER_INSTANCE_NAME \
--sql="DELETE FROM Albums WHERE SingerId = 1"

gcloud spanner databases execute-sql $SPANNER_DATABASE_NAME \
  --instance=$SPANNER_INSTANCE_NAME \
  --sql="DELETE FROM Singers WHERE SingerId = 1"

Cloud SQL에서 확인 (잠시 후):

Cloud SQL에 연결합니다.

mysql -h $SQL_INSTANCE_IP -u root -p

메시지가 표시되면 비밀번호 (Welcome@1)를 입력한 다음 mysql> 프롬프트에서 다음 SQL 명령어를 실행합니다.

USE music_db; 
-- Verify INSERT: This should show the new row for David Chen
SELECT * FROM Singers WHERE SingerId = 5;

-- Verify UPDATE: This should show AlbumCount as 5.
SELECT SingerId, FirstName, AlbumCount FROM Singers WHERE SingerId = 3;

-- Verify DELETE: This should return an empty set.
SELECT * FROM Albums WHERE SingerId = 1; 
SELECT * FROM Singers WHERE SingerId = 1; 

-- Final Verification
SELECT SingerId, FirstName, LastName, AlbumCount FROM Singers ORDER BY SingerId;
EXIT;

Cloud SQL의 예상 출력은 Spanner에서 변경된 사항을 반영해야 합니다.

+----------+-----------+----------------+------------+
| SingerId | FirstName | LastName       | AlbumCount |
+----------+-----------+----------------+------------+
|        3 | Alice     | Trentor        |          5 |
|        4 | Elena     | Nadal          |          0 |
|        5 | David     | Chen           |          0 |
+----------+-----------+----------------+------------+

이를 통해 역방향 복제 파이프라인이 작동하여 Spanner의 변경사항을 Cloud SQL로 다시 동기화하는지 확인할 수 있습니다.

11. 리소스 정리

Google Cloud 계정에 추가 비용이 청구되지 않도록 하려면 이 Codelab 중에 생성된 리소스를 삭제합니다.

환경 변수 설정 (필요한 경우)

환경 변수가 올바르게 설정되었는지 확인합니다.

echo "PROJECT_ID: $PROJECT_ID"
echo "REGION: $REGION"
echo "SQL_INSTANCE_NAME: $SQL_INSTANCE_NAME"
echo "SPANNER_INSTANCE_NAME: $SPANNER_INSTANCE_NAME"
echo "BUCKET_NAME: $BUCKET_NAME"
echo "STREAM_NAME: $STREAM_NAME"
echo "SQL_CP_NAME: $SQL_CP_NAME"
echo "GCS_CP_NAME: $GCS_CP_NAME"
echo "PUBSUB_SUBSCRIPTION: $PUBSUB_SUBSCRIPTION"
echo "PUBSUB_TOPIC: $PUBSUB_TOPIC"
echo "CHANGE_STREAM_NAME: $CHANGE_STREAM_NAME"

작업을 나열하여 실행 중인 Dataflow 작업의 작업 ID를 찾습니다. JOB_ID_CDCJOB_ID_REVERSE을 적절하게 내보냅니다.

gcloud dataflow jobs list --region=$REGION --filter="state=Running"
export JOB_ID_CDC=<PASTE_JOB_ID_HERE>
export JOB_ID_REVERSE=<PASTE_JOB_ID_HERE>

새 Cloud Shell 세션에 있는 경우 키 환경 변수를 다시 내보냅니다.

export PROJECT_ID=$(gcloud config get-value project)
export REGION="us-central1" # Or the region you used
export SQL_INSTANCE_NAME="source-mysql-instance"
export SPANNER_INSTANCE_NAME="target-spanner-instance"
export BUCKET_NAME="migration-${PROJECT_ID}-bucket"
export STREAM_NAME="mysql-to-spanner-stream"
export SQL_CP_NAME="mysql-src-cp"
export GCS_CP_NAME="gcs-dest-cp"
export PUBSUB_TOPIC="datastream-gcs-updates"
export PUBSUB_SUBSCRIPTION="datastream-gcs-sub"
export CHANGE_STREAM_NAME="MusicDBChangeStream"

Dataflow 스트리밍 작업 중지

Datastream to Spanner (라이브 마이그레이션) 작업을 취소합니다.

gcloud dataflow jobs cancel $JOB_ID_CDC --region=$REGION --project=$PROJECT_ID

Spanner to Cloud SQL (역방향 복제) 작업을 취소합니다.

gcloud dataflow jobs cancel $JOB_ID_REVERSE --region=$REGION --project=$PROJECT_ID

데이터 스트림 리소스 삭제

스트림 중지 및 삭제:

gcloud datastream streams update $STREAM_NAME \
  --location=$REGION --state=PAUSED --project=$PROJECT_ID
# Wait a moment for the stream to pause
gcloud datastream streams delete $STREAM_NAME \
  --location=$REGION --project=$PROJECT_ID --quiet

연결 프로필 삭제

gcloud datastream connection-profiles delete $SQL_CP_NAME \
  --location=$REGION --project=$PROJECT_ID --quiet
gcloud datastream connection-profiles delete $GCS_CP_NAME \
  --location=$REGION --project=$PROJECT_ID --quiet

Pub/Sub 리소스 삭제

구독 삭제:

gcloud pubsub subscriptions delete $PUBSUB_SUBSCRIPTION \
  --project=$PROJECT_ID --quiet

주제 삭제:

gcloud pubsub topics delete $PUBSUB_TOPIC \
  --project=$PROJECT_ID --quiet

Cloud SQL 인스턴스 삭제

이렇게 하면 데이터베이스 (music_db)가 자동으로 삭제됩니다.

gcloud sql instances delete $SQL_INSTANCE_NAME \
  --project=$PROJECT_ID --quiet

Cloud Spanner 인스턴스 삭제

이 경우 데이터베이스 (music-db-migratedreverse-replication-metadata)도 삭제됩니다.

gcloud spanner instances delete $SPANNER_INSTANCE_NAME \
  --project=$PROJECT_ID --quiet

GCS 버킷 및 콘텐츠 삭제

gcloud storage rm --recursive gs://${BUCKET_NAME}

로컬 파일 삭제

Cloud Shell 홈 디렉터리에서 생성된 파일을 삭제합니다.

rm -f music-db* shard_config.json

이제 이 Codelab을 위해 만든 리소스를 정리했습니다.