在 GKE 中將 MySQL 資料從 Persistent Disk 遷移至 Hyperdisk

本教學課程說明如何將現有的 MySQL 資料從 Persistent Disk (PD) 遷移到 Google Kubernetes Engine 的 Hyperdisk,以提升儲存空間效能。Hyperdisk 的 IOPS 和總處理量比永久磁碟更高,可減少資料庫查詢和交易的延遲時間,進而提升 MySQL 效能。您可以根據機器類型相容性,使用磁碟快照將資料遷移至不同磁碟類型。舉例來說,Hyperdisk 磁碟區僅與部分第三代、第四代和後續世代的機型 (例如 N4) 相容,這些機型不支援永久磁碟。詳情請參閱可用的機器系列

為示範從永久磁碟遷移至 Hyperdisk,本教學課程使用 Sakila 資料庫提供範例資料集。Sakila 是 MySQL 提供的範例資料庫,可用於教學課程和範例的結構定義。這個資料庫代表虛構的 DVD 租賃商店,內含電影、演員、顧客和租賃的資料表。

本指南適用於儲存空間專家和儲存空間管理員,可協助他們建立及分配儲存空間,並管理資料安全和資料存取權。如要進一步瞭解我們在 Google Cloud 內容中提及的常見角色和範例工作,請參閱「常見的 GKE 使用者角色和工作」。

部署架構

下圖說明從 Persistent Disk 遷移至 Hyperdisk 的程序。

  • MySQL 應用程式會在 N2 機器類型的 GKE 節點集區上執行,並將資料儲存在 Persistent Disk SSD 上。
  • 為確保資料一致性,應用程式會縮減規模,防止寫入新資料。
  • 系統會建立永久磁碟的快照,做為資料的完整時間點備份。
  • 系統會從快照佈建新的 Hyperdisk,並在獨立的 Hyperdisk 相容 N4 節點集區中部署新的 MySQL 執行個體。這個新執行個體會附加至新建立的 Hyperdisk,完成遷移至高效能儲存空間的作業。
架構圖:顯示使用快照將 MySQL 資料從永久磁碟遷移至 Hyperdisk。
圖 1:使用快照將 MySQL 資料從永久磁碟遷移至 Hyperdisk。

準備環境

  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. 在 Hyperdisk 上新增機型為 N4 的節點集區,MySQL 部署作業將遷移至該處並執行:

    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}
    

在永久磁碟上部署 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. 建立及部署包含節點親和性的 MySQL 執行個體,確保 Pod 會排定在 regular-pool 節點上,並佈建永久磁碟 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
    

    這份資訊清單會建立 MySQL 部署作業和服務,並動態佈建 Persistent Disk 來儲存資料。root 使用者的密碼為 migration

  3. 部署 MySQL 用戶端 Pod 來載入資料,並驗證資料移轉作業:

    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. 連線至用戶端 Pod:

    kubectl exec -it mysql-client -- bash
    
  5. 從用戶端 Pod 殼層下載並匯入 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. 結束用戶端 Pod 殼層:

    exit
    
  9. 取得為 MySQL 建立的 PersistentVolume (PV) 名稱,並儲存在環境變數中:

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

將資料遷移至 Hyperdisk 磁碟區

現在您有一個 MySQL 工作負載,資料儲存在 Persistent Disk SSD 磁碟區。本節說明如何使用快照將這項資料遷移至 Hyperdisk 磁碟區。這種遷移方式也會保留原始的 Persistent Disk 磁碟區,讓您在必要時還原使用原始的 MySQL 執行個體。

  1. 雖然您可以從磁碟建立快照,而不必將磁碟從工作負載中卸離,但為確保 MySQL 的資料完整性,您必須在建立快照期間停止對磁碟進行任何新的寫入作業。將 MySQL 部署作業縮減為 0 個副本,停止寫入作業:

    kubectl scale deployment regular-mysql --replicas=0
    
  2. 從現有永久磁碟建立快照:

    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. 使用專案 ID 更新已還原 PV 的資訊清單檔案:

    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 執行個體。這個 Pod 會排定在由 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 用戶端 Pod:

    kubectl exec -it mysql-client -- bash
    
  3. 在用戶端 Pod 內,連線至新的 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';
    

    資料應與永久磁碟磁碟區上的原始 MySQL 執行個體相同。

  4. 結束 mysql 工作階段:

    exit;
    
  5. 結束用戶端 Pod 殼層:

    exit