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 データの移行。

目標

このチュートリアルでは、次の方法について学びます。

  • MySQL クラスタをデプロイする。
  • テスト データセットをアップロードする。
  • データのスナップショットを作成する。
  • スナップショットから Hyperdisk を作成する。
  • 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. [新しいプリンシパル] フィールドに、ユーザー ID を入力します。 これは通常、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. ノード アフィニティを含む 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
    

クリーンアップ

このチュートリアルで使用したリソースについて、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
    

次のステップ