GKE で Persistent Disk から Hyperdisk に MySQL データを移行する

このチュートリアルでは、既存の MySQL データを Google Kubernetes Engine の Persistent Disk(PD)から Hyperdisk に移行して、ストレージ パフォーマンスをアップグレードする方法について説明します。Hyperdisk は Persistent Disk よりも高い IOPS とスループットを提供します。これにより、データベース クエリとトランザクションのレイテンシを減らし、MySQL のパフォーマンスを向上させることができます。ディスク スナップショットを使用すると、マシンタイプの互換性に応じて、データを別のディスクタイプに移行できます。たとえば、Hyperdisk ボリュームは、Persistent Disk をサポートしていない N4 などの第 3 世代、第 4 世代以降の一部のマシンタイプとのみ互換性があります。詳細については、利用可能なマシンシリーズをご覧ください。

このチュートリアルでは、Persistent Disk から Hyperdisk への移行を説明するために、Sakila データベースを使用してサンプル データセットを提供します。Sakila は、MySQL が提供するサンプル データベースで、チュートリアルや例のスキーマとして使用できます。これは架空の DVD レンタル店を表しており、映画、俳優、顧客、レンタルのテーブルが含まれています。

このガイドは、ストレージの作成と割り当てを行い、データ セキュリティとデータアクセスを管理するストレージ スペシャリストとストレージ管理者を対象としています。 Google Cloud のコンテンツで使用されている一般的なロールとタスクの例の詳細については、一般的な GKE ユーザーのロールとタスクをご覧ください。

デプロイ アーキテクチャ

次の図は、Persistent Disk から Hyperdisk への移行プロセスを示しています。

  • MySQL アプリケーションは、N2 マシンタイプを使用する GKE ノードプール上で実行され、データを Persistent Disk SSD に保存します。
  • データの整合性を確保するため、アプリケーションはスケールダウンされ、新しい書き込みが防止されます。
  • Persistent Disk のスナップショットが作成され、データの完全なポイントインタイム バックアップとして機能します。
  • スナップショットから新しい Hyperdisk がプロビジョニングされ、新しい MySQL インスタンスが Hyperdisk 互換の別の N4 ノードプールにデプロイされます。この新しいインスタンスが新しく作成された Hyperdisk にアタッチされ、高パフォーマンス ストレージへの移行が完了します。
スナップショットを使用して Persistent Disk から Hyperdisk に MySQL データを移行するアーキテクチャ図。
図 1: スナップショットを使用した Persistent Disk から Hyperdisk への MySQL データの移行。

環境を準備する

  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. ノード アフィニティを含む MySQL インスタンスを作成してデプロイし、Pod が regular-pool ノードでスケジュール設定されるようにして、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 クライアント 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}')
    

データを Hyperdisc ボリュームに移行する

これで、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 インスタンスをデプロイします。この 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';
    

    データは、Persistent Disk ボリューム上のオリジナルの MySQL インスタンスと同じである必要があります。

  4. mysql セッションを終了します。

    exit;
    
  5. クライアント Pod シェルを終了します。

    exit