GKE에서 Persistent Disk의 MySQL 데이터를 Hyperdisk로 마이그레이션

이 튜토리얼에서는 Google Kubernetes Engine에서 Persistent Disk(PD)의 기존 MySQL 데이터를 Hyperdisk로 마이그레이션하여 스토리지 성능을 업그레이드하는 방법을 보여줍니다. Hyperdisk는 Persistent Disk보다 높은 IOPS와 처리량을 제공하므로 데이터베이스 쿼리 및 트랜잭션의 지연 시간을 줄여 MySQL 성능을 개선할 수 있습니다. 디스크 스냅샷을 사용하여 머신 유형 호환성에 따라 데이터를 다른 디스크 유형으로 마이그레이션할 수 있습니다. 예를 들어 Hyperdisk 볼륨은 Persistent Disk를 지원하지 않는 일부 3세대, 4세대 및 이후 세대 머신 유형(예: N4)과만 호환됩니다. 자세한 내용은 사용 가능한 머신 시리즈를 참조하세요.

Persistent Disk에서 Hyperdisk로의 마이그레이션을 보여주기 위해 이 튜토리얼에서는 Sakila 데이터베이스를 사용하여 샘플 데이터 세트를 제공합니다. Sakila는 튜토리얼과 예시의 스키마로 사용할 수 있는 MySQL에서 제공하는 샘플 데이터베이스입니다. 가상의 DVD 대여점을 나타내며 영화, 배우, 고객, 대여 테이블이 포함되어 있습니다.

이 가이드는 스토리지를 만들고 할당하며 데이터 보안 및 데이터 액세스를 관리하는 스토리지 전문가와 스토리지 관리자를 대상으로 합니다. Google Cloud 콘텐츠에서 참조하는 일반적인 역할 및 예시 태스크에 대해 자세히 알아보려면 일반 GKE 사용자 역할 및 태스크를 참조하세요.

배포 아키텍처

다음 다이어그램은 Persistent Disk에서 Hyperdisk로의 마이그레이션 프로세스를 보여줍니다.

  • MySQL 애플리케이션이 N2 머신 유형이 있는 GKE 노드 풀에서 실행되며 Persistent Disk SSD에 데이터를 저장합니다.
  • 데이터 일관성을 보장하기 위해 애플리케이션이 축소되어 새로운 쓰기가 방지됩니다.
  • Persistent Disk의 스냅샷이 생성되어 데이터의 완전한 특정 시점 백업 역할을 합니다.
  • 스냅샷에서 새 하이퍼디스크가 프로비저닝되고 별도의 하이퍼디스크 호환 N4 노드 풀에 새 MySQL 인스턴스가 배포됩니다. 이 새 인스턴스는 새로 생성된 Hyperdisk에 연결되어 고성능 스토리지로의 마이그레이션을 완료합니다.
스냅샷을 사용하여 Persistent Disk에서 Hyperdisk로 MySQL 데이터를 마이그레이션하는 방법을 보여주는 아키텍처 다이어그램
그림 1: 스냅샷을 사용하여 Persistent Disk에서 Hyperdisk로 MySQL 데이터를 마이그레이션합니다.

목표

이 튜토리얼에서는 다음 작업을 수행하는 방법을 알아봅니다.

  • MySQL 클러스터를 배포합니다.
  • 테스트 데이터 세트를 업로드합니다.
  • 데이터의 스냅샷을 만듭니다.
  • 스냅샷에서 하이퍼디스크를 만듭니다.
  • Hyperdisk 지원 N4 머신 유형 노드 풀에서 새 MySQL 클러스터를 시작합니다.
  • 데이터 무결성을 확인하여 마이그레이션이 성공적으로 완료되었는지 확인합니다.

비용

이 문서에서는 비용이 청구될 수 있는 Google Cloud구성요소를 사용합니다.

  • GKE
  • Compute Engine, which includes:
    • Storage capacity provisioned for both Persistent Disk and Hyperdisk.
    • Storage costs for the snapshots.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요.

Google Cloud 신규 사용자는 무료 체험판을 사용할 수 있습니다.

시작하기 전에

  1. Google Cloud 계정에 로그인합니다. Google Cloud를 처음 사용하는 경우 계정을 만들고 Google 제품의 실제 성능을 평가해 보세요. 신규 고객에게는 워크로드를 실행, 테스트, 배포하는 데 사용할 수 있는 $300의 무료 크레딧이 제공됩니다.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. Enable the Compute Engine, GKE, Identity and Access Management Service Account Credentials APIs.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  6. Verify that billing is enabled for your Google Cloud project.

  7. Enable the Compute Engine, GKE, Identity and Access Management Service Account Credentials APIs.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  8. 프로젝트에 다음 역할이 있는지 확인합니다. roles/container.admin, roles/iam.serviceAccountAdmin, roles/compute.admin

    역할 확인

    1. Google Cloud 콘솔에서 IAM 페이지로 이동합니다.

      IAM으로 이동
    2. 프로젝트를 선택합니다.
    3. 주 구성원 열에서 나 또는 내가 속한 그룹을 식별하는 모든 행을 찾습니다. 내가 속한 그룹을 알아보려면 관리자에게 문의하세요.

    4. 나를 지정하거나 포함하는 모든 행의 역할 열을 확인하여 역할 목록에 필요한 역할이 포함되어 있는지 확인합니다.

    역할 부여

    1. Google Cloud 콘솔에서 IAM 페이지로 이동합니다.

      IAM으로 이동
    2. 프로젝트를 선택합니다.
    3. 액세스 권한 부여를 클릭합니다.
    4. 새 주 구성원 필드에 사용자 식별자를 입력합니다. 일반적으로 Google 계정의 이메일 주소입니다.

    5. 역할 선택을 클릭한 후 역할을 검색합니다.
    6. 역할을 추가로 부여하려면 다른 역할 추가를 클릭하고 각 역할을 추가합니다.
    7. 저장을 클릭합니다.

Cloud Shell 설정

  1. Google Cloud 콘솔에서 Cloud Shell을 활성화합니다.

    Cloud Shell 활성화

    Google Cloud 콘솔 하단에 Cloud Shell 세션이 시작되고 명령줄 프롬프트가 표시됩니다. Cloud Shell은 Google Cloud CLI가 사전 설치된 셸 환경으로, 현재 프로젝트의 값이 이미 설정되어 있습니다. 세션이 초기화되는 데 몇 초 정도 걸릴 수 있습니다.

  2. Cloud Shell 세션이 시작되면 명령줄 프롬프트가 표시됩니다. 세션이 초기화되는 데 몇 초 정도 걸릴 수 있습니다.

  3. 기본 프로젝트를 설정합니다.

      gcloud config set project PROJECT_ID
    

    PROJECT_ID를 프로젝트 ID로 바꿉니다.

환경 준비

  1. Cloud Shell에서 프로젝트, 위치, 클러스터 프리픽스의 환경 변수를 설정합니다.

    export PROJECT_ID=PROJECT_ID
    export EMAIL_ADDRESS=EMAIL_ADDRESS
    export KUBERNETES_CLUSTER_PREFIX=offline-hyperdisk-migration
    export LOCATION=us-central1-a
    

    다음을 바꿉니다.

    • PROJECT_ID: Google Cloud 프로젝트 ID
    • EMAIL_ADDRESS: 이메일 주소입니다.
    • LOCATION: 배포 리소스를 만들려는 영역입니다. 이 튜토리얼에서는 us-central1-a 영역을 사용합니다.
  2. GitHub에서 샘플 코드 저장소를 클론합니다.

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  3. offline-hyperdisk-migration 디렉터리로 이동하여 배포 리소스 만들기를 시작합니다.

    cd kubernetes-engine-samples/databases/offline-hyperdisk-migration
    

GKE 클러스터 및 노드 풀 만들기

이 튜토리얼에서는 Hyperdisk 볼륨이 영역별 리소스이고 단일 영역 내에서만 액세스할 수 있으므로 단순화를 위해 영역별 클러스터를 사용합니다.

  1. 영역 GKE 클러스터를 만듭니다.

    gcloud container clusters create ${KUBERNETES_CLUSTER_PREFIX}-cluster \
        --location ${LOCATION} \
        --node-locations ${LOCATION} \
        --shielded-secure-boot \
        --shielded-integrity-monitoring \
        --machine-type "e2-micro" \
        --num-nodes "1"
    
  2. 초기 MySQL 배포를 위해 N2 머신 유형이 있는 노드 풀을 추가합니다.

    gcloud container node-pools create regular-pool \
        --cluster ${KUBERNETES_CLUSTER_PREFIX}-cluster \
        --machine-type n2-standard-4 \
        --location ${LOCATION} \
        --num-nodes 1
    
  3. MySQL 배포가 마이그레이션되고 실행될 Hyperdisk에 N4 머신 유형이 있는 노드 풀을 추가합니다.

    gcloud container node-pools create hyperdisk-pool \
        --cluster ${KUBERNETES_CLUSTER_PREFIX}-cluster \
        --machine-type n4-standard-4 \
        --location ${LOCATION} \
        --num-nodes 1
    
  4. 클러스터에 연결합니다.

    gcloud container clusters get-credentials ${KUBERNETES_CLUSTER_PREFIX}-cluster --location ${LOCATION}
    

Persistent Disk에 MySQL 배포

이 섹션에서는 스토리지를 위해 Persistent Disk를 사용하는 MySQL 인스턴스를 배포하고 샘플 데이터를 로드합니다.

  1. Hyperdisk용 StorageClass를 만들고 적용합니다. 이 StorageClass는 튜토리얼 뒷부분에서 사용됩니다.

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: balanced-storage
    provisioner: pd.csi.storage.gke.io
    volumeBindingMode: WaitForFirstConsumer
    allowVolumeExpansion: true
    parameters:
      type: hyperdisk-balanced
      provisioned-throughput-on-create: "250Mi"
      provisioned-iops-on-create: "7000"
    kubectl apply -f manifests/01-storage-class/storage-class-hdb.yaml
    
  2. 포드가 regular-pool 노드에 예약되도록 노드 어피니티가 포함된 MySQL 인스턴스를 만들고 배포하여 Persistent Disk SSD 볼륨을 프로비저닝합니다.

    apiVersion: v1
    kind: Service
    metadata:
      name: regular-mysql
      labels:
        app: mysql
    spec:
      ports:
        - port: 3306
      selector:
        app: mysql
      clusterIP: None
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: mysql-pv-claim
      labels:
        app: mysql
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 30Gi
      storageClassName: premium-rwo
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: existing-mysql
      labels:
        app: mysql
    spec:
      selector:
        matchLabels:
          app: mysql
      strategy:
        type: Recreate
      template:
        metadata:
          labels:
            app: mysql
        spec:
          containers:
          - image: mysql:8.0
            name: mysql
            env:
            - name: MYSQL_ROOT_PASSWORD
              value: migration
            - name: MYSQL_DATABASE
              value: mysql
            - name: MYSQL_USER
              value: app
            - name: MYSQL_PASSWORD
              value: migration
            ports:
            - containerPort: 3306
              name: mysql
            volumeMounts:
            - name: mysql-persistent-storage
              mountPath: /var/lib/mysql
          affinity: 
            nodeAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 1
                preference:
                  matchExpressions:
                  - key: "node.kubernetes.io/instance-type"
                    operator: In
                    values:
                    - "n2-standard-4"
          volumes:
          - name: mysql-persistent-storage
            persistentVolumeClaim:
              claimName: mysql-pv-claim
    kubectl apply -f manifests/02-mysql/mysql-deployment.yaml
    

    이 매니페스트는 데이터 스토리지를 위해 동적으로 프로비저닝된 Persistent Disk를 사용하여 MySQL 배포 및 서비스를 만듭니다. root 사용자의 비밀번호는 migration입니다.

  3. MySQL 클라이언트 포드를 배포하여 데이터를 로드하고 데이터 마이그레이션이 완료되었는지 확인합니다.

    apiVersion: v1
    kind: Pod
    metadata:
      name: mysql-client
    spec:
      containers:
      - name: main
        image: mysql:8.0
        command: ["sleep", "360000"]
        resources:
          requests:
            memory: 1Gi
            cpu: 500m
          limits:
            memory: 1Gi
            cpu: "1"
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: migration
    kubectl apply -f manifests/02-mysql/mysql-client.yaml
    kubectl wait pods mysql-client --for condition=Ready --timeout=300s
    
  4. 클라이언트 포드에 연결합니다.

    kubectl exec -it mysql-client -- bash
    
  5. 클라이언트 포드 셸에서 Sakila 샘플 데이터 세트를 다운로드하고 가져옵니다.

    # Download the dataset
    curl --output dataset.tgz "https://downloads.mysql.com/docs/sakila-db.tar.gz"
    
    # Extract the dataset
    tar -xvzf dataset.tgz -C /home/mysql
    
    # Import the dataset into MySQL (the password is "migration").
    mysql -u root -h regular-mysql.default -p
        SOURCE /sakila-db/sakila-schema.sql;
        SOURCE /sakila-db/sakila-data.sql;
    
  6. 데이터가 가져와졌는지 확인합니다.

    USE sakila;
    SELECT      table_name,      table_rows  FROM      INFORMATION_SCHEMA.TABLES  WHERE TABLE_SCHEMA = 'sakila';
    

    출력에 행 수가 포함된 테이블 목록이 표시됩니다.

    | TABLE_NAME                 | TABLE_ROWS |
    +----------------------------+------------+
    | actor                      |        200 |
    | actor_info                 |       NULL |
    | address                    |        603 |
    | category                   |         16 |
    | city                       |        600 |
    | country                    |        109 |
    | customer                   |        599 |
    | customer_list              |       NULL |
    | film                       |       1000 |
    | film_actor                 |       5462 |
    | film_category              |       1000 |
    | film_list                  |       NULL |
    | film_text                  |       1000 |
    | inventory                  |       4581 |
    | language                   |          6 |
    | nicer_but_slower_film_list |       NULL |
    | payment                    |      16086 |
    | rental                     |      16419 |
    | sales_by_film_category     |       NULL |
    | sales_by_store             |       NULL |
    | staff                      |          2 |
    | staff_list                 |       NULL |
    | store                      |          2 |
    +----------------------------+------------+
    23 rows in set (0.01 sec)
    
  7. mysql 세션을 종료합니다.

    exit;
    
  8. 클라이언트 포드 셸을 종료합니다.

    exit
    
  9. MySQL용으로 생성된 PersistentVolume(PV)의 이름을 가져와 환경 변수에 저장합니다.

    export PV_NAME=$(kubectl get pvc mysql-pv-claim -o jsonpath='{.spec.volumeName}')
    

데이터를 Hyperdisk 볼륨으로 마이그레이션

이제 Persistent Disk SSD 볼륨에 데이터가 저장된 MySQL 워크로드가 있습니다. 이 섹션에서는 스냅샷을 사용하여 이 데이터를 Hyperdisk 볼륨으로 마이그레이션하는 방법을 설명합니다. 이 마이그레이션 접근 방식은 원래 Persistent Disk 볼륨도 보존하므로 필요한 경우 원래 MySQL 인스턴스로 롤백할 수 있습니다.

  1. 워크로드에서 디스크를 분리하지 않고 스냅샷을 만들 수 있지만 MySQL의 데이터 무결성을 보장하려면 스냅샷을 만드는 동안 디스크에 새 쓰기가 발생하지 않도록 해야 합니다. MySQL 배포를 0 복제본으로 축소하여 쓰기를 중지합니다.

    kubectl scale deployment regular-mysql --replicas=0
    
  2. 기존 Persistent Disk에서 스냅샷을 만듭니다.

    gcloud compute disks snapshot ${PV_NAME} --location=${LOCATION} --snapshot-name=original-snapshot --description="snapshot taken from pd-ssd"
    
  3. 스냅샷에서 mysql-recovery라는 새 Hyperdisk 볼륨을 만듭니다.

    gcloud compute disks create mysql-recovery --project=${PROJECT_ID} \
        --type=hyperdisk-balanced \
        --size=150GB --location=${LOCATION} \
        --source-snapshot=projects/${PROJECT_ID}/global/snapshots/original-snapshot
    
  4. 복원된 PV의 매니페스트 파일을 프로젝트 ID로 업데이트합니다.

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: backup
    spec:
      storageClassName: balanced-storage
      capacity:
        storage: 150G
      accessModes:
        - ReadWriteOnce
      claimRef:
        name: hyperdisk-recovery
        namespace: default
      csi:
        driver: pd.csi.storage.gke.io
        volumeHandle: projects/PRJCTID/zones/us-central1-a/disks/mysql-recovery
        fsType: ext4
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      namespace: default
      name: hyperdisk-recovery
    spec:
      storageClassName: balanced-storage
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 150G
    sed -i "s/PRJCTID/$PROJECT_ID/g" manifests/02-mysql/restore_pv.yaml
    
  5. 새 Hyperdisk에서 PersistentVolume(PVC) 및 PersistentVolumeClaim을 만듭니다.

    kubectl apply -f manifests/02-mysql/restore_pv.yaml
    

데이터 마이그레이션 확인

새로 만든 Hyperdisk 볼륨을 사용하는 새 MySQL 인스턴스를 배포합니다. 이 포드는 N4 노드로 구성된 hyperdisk-pool 노드 풀에 예약됩니다.

  1. 새 MySQL 인스턴스를 배포합니다.

    apiVersion: v1
    kind: Service
    metadata:
      name: recovered-mysql
      labels:
        app: new-mysql
    spec:
      ports:
        - port: 3306
      selector:
        app: new-mysql
      clusterIP: None
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: new-mysql
      labels:
        app: new-mysql
    spec:
      selector:
        matchLabels:
          app: new-mysql
      strategy:
        type: Recreate
      template:
        metadata:
          labels:
            app: new-mysql
        spec:
          containers:
          - image: mysql:8.0
            name: mysql
            env:
            - name: MYSQL_ROOT_PASSWORD
              value: migration
            - name: MYSQL_DATABASE
              value: mysql
            - name: MYSQL_USER
              value: app
            - name: MYSQL_PASSWORD
              value: migration
            ports:
            - containerPort: 3306
              name: mysql
            volumeMounts:
            - name: mysql-persistent-storage
              mountPath: /var/lib/mysql
          affinity: 
            nodeAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 1
                preference:
                  matchExpressions:
                  - key: "cloud.google.com/gke-nodepool"
                    operator: In
                    values:
                    - "hyperdisk-pool"      
          volumes:
          - name: mysql-persistent-storage
            persistentVolumeClaim:
              claimName: hyperdisk-recovery
    kubectl apply -f manifests/02-mysql/recovery_mysql_deployment.yaml
    
  2. 데이터 무결성을 확인하려면 MySQL 클라이언트 포드에 다시 연결합니다.

    kubectl exec -it mysql-client -- bash
    
  3. 클라이언트 포드 내에서 새 MySQL 데이터베이스(recovered-mysql.default)에 연결하고 데이터를 확인합니다. 비밀번호는 migration입니다.

    mysql -u root -h recovered-mysql.default -p
    USE sakila;
    SELECT table_name, table_rows FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'sakila';
    

    데이터는 Persistent Disk 볼륨의 원래 MySQL 인스턴스와 동일해야 합니다.

  4. mysql 세션을 종료합니다.

    exit;
    
  5. 클라이언트 포드 셸을 종료합니다.

    exit
    

삭제

이 튜토리얼에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요.

프로젝트 삭제

    Google Cloud 프로젝트를 삭제합니다.

    gcloud projects delete PROJECT_ID

개별 리소스 삭제

기존 프로젝트를 사용한 경우 삭제하지 않으려면 개별 리소스를 삭제합니다.

  1. 정리를 위한 환경 변수를 설정하고 mysql-pv-claim PersistentVolumeClaim으로 생성된 Persistent Disk 볼륨의 이름을 검색합니다.

    export PROJECT_ID=PROJECT_ID
    export KUBERNETES_CLUSTER_PREFIX=offline-hyperdisk-migration
    export location=us-central1-a
    export PV_NAME=$(kubectl get pvc mysql-pv-claim -o jsonpath='{.spec.volumeName}')
    

    PROJECT_ID를 프로젝트 ID로 바꿉니다.

  2. 스냅샷을 삭제합니다.

    gcloud compute snapshots delete original-snapshot --quiet
    
  3. GKE 클러스터를 삭제합니다.

    gcloud container clusters delete ${KUBERNETES_CLUSTER_PREFIX}-cluster --location=${LOCATION} --quiet
    
  4. Persistent Disk 및 Hyperdisk 볼륨을 삭제합니다.

    gcloud compute disks delete ${PV_NAME} --location=${LOCATION} --quiet
    gcloud compute disks delete mysql-recovery --location=${LOCATION} --quiet
    

다음 단계