이중 쓰기 프록시를 사용하여 Azure Managed Instance for Apache Cassandra로 실시간 마이그레이션

가능하면 하이브리드 클러스터를 구성하여 기존 클러스터에서 Azure Managed Instance for Apache Cassandra로 데이터를 마이그레이션할 때 Apache Cassandra 네이티브 기능을 사용하는 것이 좋습니다. 이 기능은 Apache Cassandra의 가십 프로토콜을 사용하여 원본 데이터 센터에서 새로운 관리형 인스턴스 데이터 센터로 원활하게 데이터를 복제합니다. 그러나 원본 데이터베이스 버전이 호환되지 않거나 하이브리드 클러스터를 설정할 수 없는 경우가 있을 수 있습니다.

이 자습서에서는 이중 쓰기 프록시 및 Apache Spark를 사용하여 실시간으로 데이터를 Azure Managed Instance for Apache Cassandra로 마이그레이션하는 방법을 설명합니다. 이중 쓰기 프록시는 실시간 변경 사항을 캡처하는 데 사용되고, 기록 데이터는 Apache Spark를 사용하여 대량으로 복사됩니다. 이 방법의 이점은 다음과 같습니다.

  • 최소한의 애플리케이션 변경. 프록시가 최소한의 구성 변경으로 또는 구성 변경 없이 애플리케이션 코드의 연결을 허용할 수 있습니다. 모든 요청을 원본 데이터베이스에 라우팅하고 비동기 방식으로 쓰기를 보조 대상으로 라우팅합니다.
  • 클라이언트 유선 프로토콜 종속성. 이 방법은 백 엔드 리소스나 내부 프로토콜에 종속되지 않으므로 Apache Cassandra 유선 프로토콜을 구현하는 모든 원본 또는 대상 Cassandra 시스템에서 사용할 수 있습니다.

다음 이미지는 이 방법을 설명합니다.

Animation that shows the live migration of data to Azure Managed Instance for Apache Cassandra.

필수 조건

Spark 클러스터 프로비저닝

Spark 3.0을 지원하는 Azure Databricks 런타임 버전 7.5를 선택하는 것이 좋습니다.

Screenshot that shows finding the Azure Databricks runtime version.

Spark 종속성 추가

모든 유선 프로토콜 호환 Apache Cassandra 엔드포인트에 연결하려면 Apache Spark Cassandra 커넥터 라이브러리를 클러스터에 추가해야 합니다. 클러스터에서 라이브러리>새로 설치>Maven을 선택한 다음 Maven 좌표에 com.datastax.spark:spark-cassandra-connector-assembly_2.12:3.0.0을 추가합니다.

Important

마이그레이션하는 동안 각 행의 Apache Cassandra writetime을 보존해야 하는 요구 사항이 있는 경우 이 샘플을 사용하는 것이 좋습니다. 이 샘플의 종속성 jar에는 Spark 커넥터도 포함되어 있으므로 위의 커넥터 어셈블리 대신 설치해야 합니다. 이 샘플은 기록 데이터 로드가 완료된 후에 원본과 대상 간에 행 비교 유효성 검사를 수행하려는 경우에도 유용합니다. 자세한 내용은 아래의 "기록 데이터 로드 실행" 및 "원본 및 대상의 유효성 검사" 섹션을 참조하세요.

Screenshot that shows searching for Maven packages in Azure Databricks.

설치를 선택한 다음 설치가 완료되면 클러스터를 다시 시작합니다.

참고 항목

Cassandra 커넥터 라이브러리가 설치된 후 Azure Databricks 클러스터를 다시 시작해야 합니다.

이중 쓰기 프록시 설치

이중 쓰기 중 성능을 최적화하려면 원본 Cassandra 클러스터의 모든 노드에 프록시를 설치하는 것이 좋습니다.

#assuming you do not have git already installed
sudo apt-get install git 

#assuming you do not have maven already installed
sudo apt install maven

#clone repo for dual-write proxy
git clone https://github.com/Azure-Samples/cassandra-proxy.git

#change directory
cd cassandra-proxy

#compile the proxy
mvn package

이중 쓰기 프록시 시작

원본 Cassandra 클러스터의 모든 노드에 프록시를 설치하는 것이 좋습니다. 최소한 다음 명령을 실행하여 각 노드에서 프록시를 시작합니다. <target-server>를 대상 클러스터에 있는 노드 중 하나의 IP 또는 서버 주소로 바꿉니다. <path to JKS file>을 로컬 .jks 파일 경로로 바꾸고, <keystore password>를 해당 암호로 바꿉니다.

java -jar target/cassandra-proxy-1.0-SNAPSHOT-fat.jar localhost <target-server> --proxy-jks-file <path to JKS file> --proxy-jks-password <keystore password>

이 방법으로 프록시를 시작하면 다음 조건이 충족된다고 가정합니다.

  • 원본 및 대상 엔드포인트의 사용자 이름과 암호가 동일합니다.
  • 원본 및 대상 엔드포인트가 SSL(Secure Sockets Layer)을 구현합니다.

원본 및 대상 엔드포인트가 이러한 조건을 충족할 수 없는 경우 계속해서 추가 구성 옵션을 살펴보세요.

SSL 구성

SSL의 경우 기존 키 저장소(예: 원본 클러스터에서 사용하는 키 저장소)를 구현하거나 keytool을 사용하여 자체 서명된 인증서를 만들 수 있습니다.

keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass password -validity 360 -keysize 2048

SSL을 구현하지 않는 경우 원본 또는 대상 엔드포인트에 대해 SSL을 사용하지 않도록 설정할 수도 있습니다. --disable-source-tls 또는 --disable-target-tls 플래그를 사용합니다.

java -jar target/cassandra-proxy-1.0-SNAPSHOT-fat.jar localhost <target-server> --source-port 9042 --target-port 10350 --proxy-jks-file <path to JKS file> --proxy-jks-password <keystore password> --target-username <username> --target-password <password> --disable-source-tls true  --disable-target-tls true 

참고 항목

프록시를 통해 데이터베이스에 대한 SSL 연결을 설정하는 경우 클라이언트 애플리케이션이 이중 쓰기 프록시에 사용된 것과 동일한 키 저장소와 암호를 사용하는지 확인합니다.

자격 증명 및 포트 구성

기본적으로 원본 자격 증명은 클라이언트 앱에서 전달됩니다. 프록시는 원본 및 대상 클러스터에 연결하는 데 자격 증명을 사용합니다. 앞에서 설명한 것처럼 이 프로세스에서는 원본 및 대상 자격 증명이 동일하다고 가정합니다. 필요한 경우 프록시를 시작할 때 대상 Cassandra 엔드포인트의 사용자 이름과 암호를 다르게 지정할 수 있습니다.

java -jar target/cassandra-proxy-1.0-SNAPSHOT-fat.jar localhost <target-server> --proxy-jks-file <path to JKS file> --proxy-jks-password <keystore password> --target-username <username> --target-password <password>

기본 원본 및 대상 포트는 지정되지 않은 경우 9042가 됩니다. 대상 또는 원본 Cassandra 엔드포인트가 다른 포트에서 실행되는 경우 --source-port 또는 --target-port를 사용하여 다른 포트 번호를 지정할 수 있습니다.

java -jar target/cassandra-proxy-1.0-SNAPSHOT-fat.jar localhost <target-server> --source-port 9042 --target-port 10350 --proxy-jks-file <path to JKS file> --proxy-jks-password <keystore password> --target-username <username> --target-password <password>

원격으로 프록시 배포

클러스터 노드 자체에 프록시를 설치하지 않고 별도의 머신에 설치하려는 경우가 있을 수 있습니다. 이 경우 IP 주소를 <source-server>로 지정해야 합니다.

java -jar target/cassandra-proxy-1.0-SNAPSHOT-fat.jar <source-server> <destination-server>

Warning

원본 Apache Cassandra 클러스터의 모든 노드에서 프록시를 실행하는 대신 별도의 머신에서 원격으로 실행하려는 경우 클러스터에 있는 노드와 동일한 수의 머신에 프록시를 배포하고 여기에 언급된 프록시의 구성을 사용하여 system.peers에서 해당 IP 주소에 대한 대체를 설정하는 것이 좋습니다. 이렇게 하지 않으면 클라이언트 드라이버가 클러스터 내의 모든 노드에 대한 연결을 열 수 없으므로 실시간 마이그레이션이 발생하는 동안 성능에 영향을 줄 수 있습니다.

제로(0) 애플리케이션 코드 변경 허용

기본적으로 프록시는 포트 29042에서 수신 대기합니다. 이 포트를 가리키도록 애플리케이션 코드를 변경해야 합니다. 그러나 프록시가 수신 대기하는 포트를 변경할 수 있습니다. 다음을 수행하여 애플리케이션 수준 코드 변경 내용을 제거하면 됩니다.

  • 원본 Cassandra 서버가 다른 포트에서 실행되도록 합니다.
  • 프록시가 표준 Cassandra 포트 9042에서 실행되도록 합니다.
java -jar target/cassandra-proxy-1.0-SNAPSHOT-fat.jar source-server destination-server --proxy-port 9042

참고 항목

클러스터 노드에 프록시를 설치하면 노드를 다시 시작하지 않아도 됩니다. 그러나 애플리케이션 클라이언트가 많고 프록시를 표준 Cassandra 포트 9042에서 실행하여 애플리케이션 수준 코드가 변경되지 않도록 하려면 Apache Cassandra 기본 포트를 변경해야 합니다. 그런 다음 클러스터에서 노드를 다시 시작하고 원본 포트를 원본 Cassandra 클러스터에 대해 정의한 새 포트로 구성해야 합니다.

다음 예제에서는 포트 3074에서 실행되도록 원본 Cassandra 클러스터를 변경하고 포트 9042에서 클러스터를 시작합니다.

java -jar target/cassandra-proxy-1.0-SNAPSHOT-fat.jar source-server destination-server --proxy-port 9042 --source-port 3074

프로토콜 강제 적용

프록시에는 원본 엔드포인트가 대상보다 고급인 경우에 필요하고, 그러지 않으면 지원되지 않는 프로토콜 강제 적용 기능이 있습니다. 해당하는 경우 --protocol-version--cql-version을 지정하여 프로토콜이 대상을 따르도록 강제 적용할 수 있습니다.

java -jar target/cassandra-proxy-1.0-SNAPSHOT-fat.jar source-server destination-server --protocol-version 4 --cql-version 3.11

이중 쓰기 프록시가 실행된 후 애플리케이션 클라이언트에서 포트를 변경하고 다시 시작해야 합니다. (또는 해당 방법을 선택한 경우 Cassandra 포트를 변경하고 클러스터를 다시 시작합니다.) 그러면 프록시가 대상 엔드포인트로 쓰기를 전달하기 시작합니다. 프록시 도구에서 사용할 수 있는 모니터링 및 메트릭에 대해 알아볼 수 있습니다.

기록 데이터 로드 실행

데이터를 로드하려면 Azure Databricks 계정에 Scala Notebook을 만듭니다. 원본 및 대상 Cassandra 구성을 해당 자격 증명으로 바꾸고, 원본 및 대상 키스페이스와 테이블을 바꿉니다. 각 테이블에 대한 변수를 필요한 만큼 다음 샘플에 더 추가한 후 실행합니다. 애플리케이션에서 이중 쓰기 프록시로 요청을 보내기 시작하면 기록 데이터를 마이그레이션할 수 있습니다.

import com.datastax.spark.connector._
import com.datastax.spark.connector.cql._
import org.apache.spark.SparkContext

// source cassandra configs
val sourceCassandra = Map( 
    "spark.cassandra.connection.host" -> "<Source Cassandra Host>",
    "spark.cassandra.connection.port" -> "9042",
    "spark.cassandra.auth.username" -> "<USERNAME>",
    "spark.cassandra.auth.password" -> "<PASSWORD>",
    "spark.cassandra.connection.ssl.enabled" -> "true",
    "keyspace" -> "<KEYSPACE>",
    "table" -> "<TABLE>"
)

//target cassandra configs
val targetCassandra = Map( 
    "spark.cassandra.connection.host" -> "<Source Cassandra Host>",
    "spark.cassandra.connection.port" -> "9042",
    "spark.cassandra.auth.username" -> "<USERNAME>",
    "spark.cassandra.auth.password" -> "<PASSWORD>",
    "spark.cassandra.connection.ssl.enabled" -> "true",
    "keyspace" -> "<KEYSPACE>",
    "table" -> "<TABLE>",
    //throughput related settings below - tweak these depending on data volumes. 
    "spark.cassandra.output.batch.size.rows"-> "1",
    "spark.cassandra.output.concurrent.writes" -> "1000",
    "spark.cassandra.connection.remoteConnectionsPerExecutor" -> "1",
    "spark.cassandra.concurrent.reads" -> "512",
    "spark.cassandra.output.batch.grouping.buffer.size" -> "1000",
    "spark.cassandra.connection.keep_alive_ms" -> "600000000"
)

//set timestamp to ensure it is before read job starts
val timestamp: Long = System.currentTimeMillis / 1000

//Read from source Cassandra
val DFfromSourceCassandra = sqlContext
  .read
  .format("org.apache.spark.sql.cassandra")
  .options(sourceCassandra)
  .load
  
//Write to target Cassandra
DFfromSourceCassandra
  .write
  .format("org.apache.spark.sql.cassandra")
  .options(targetCassandra)
  .option("writetime", timestamp)
  .mode(SaveMode.Append)
  .save

참고 항목

위의 Scala 샘플에서 timestamp는 원본 테이블의 모든 데이터를 읽기 전 현재 시간으로 설정되는 것을 확인할 수 있습니다. writetime은 이 소급 적용된 타임스탬프로 설정됩니다. 따라서 기록 데이터가 대상 엔드포인트에 로드될 때 기록되는 레코드는 기록 데이터를 읽는 동안 이중 쓰기 프록시의 이후 타임스탬프로 제공되는 업데이트를 덮어쓸 수 없습니다.

Important

어떤 이유로든 ‘정확한’ 타임스탬프를 유지해야 하는 경우 이 샘플처럼 타임스탬프를 유지하는 기록 데이터 마이그레이션 방법을 사용해야 합니다. 샘플의 종속성 jar에는 Spark 커넥터도 포함되어 있으므로 앞의 필수 요구 사항에 언급된 Spark 커넥터 어셈블리를 설치할 필요가 없습니다. Spark 클러스터에 둘 다 설치하면 충돌이 발생할 수 있습니다.

원본 및 대상의 유효성 검사

기록 데이터 로드가 완료되면 데이터베이스가 동기화되고 중단할 준비가 된 것입니다. 그러나 최종적으로 컷오버하기 전에 원본과 대상의 유효성을 검사하여 일치하는지 확인하는 것이 좋습니다.

참고 항목

writetime을 보존하기 위해 위에서 언급한 cassandra 마이그레이터 샘플을 사용한 경우 특정 허용 오차에 따라 원본 및 대상의 행을 비교하여 마이그레이션의 유효성을 검사하는 기능이 포함됩니다.

다음 단계