Migrar datos de MySQL de Persistent Disk a Hyperdisk en GKE

En este tutorial se muestra cómo migrar los datos de MySQL de un Persistent Disk (PD) a Hyperdisk en Google Kubernetes Engine para mejorar el rendimiento del almacenamiento. Hyperdisk ofrece más IOPS y un mayor rendimiento que Persistent Disk, lo que puede mejorar el rendimiento de MySQL al reducir la latencia de las consultas y las transacciones de la base de datos. Puedes usar instantáneas de disco para migrar tus datos a diferentes tipos de disco en función de la compatibilidad del tipo de máquina. Por ejemplo, los volúmenes de Hyperdisk solo son compatibles con algunos tipos de máquinas de tercera, cuarta y generaciones posteriores, como N4, que no admiten discos persistentes. Para obtener más información, consulta las series de máquinas disponibles.

Para mostrar la migración de Persistent Disk a Hyperdisk, en este tutorial se usa la base de datos Sakila para proporcionar un conjunto de datos de ejemplo. Sakila es una base de datos de ejemplo proporcionada por MySQL que puedes usar como esquema para tutoriales y ejemplos. Representa una tienda de alquiler de DVDs ficticia e incluye tablas de películas, actores, clientes y alquileres.

Esta guía está dirigida a especialistas y administradores de almacenamiento que crean y asignan almacenamiento, y gestionan la seguridad y el acceso a los datos. Para obtener más información sobre los roles habituales y las tareas de ejemplo a las que hacemos referencia en el Google Cloud contenido, consulta Roles y tareas habituales de los usuarios de GKE.

Arquitectura de despliegue

En el siguiente diagrama se ilustra el proceso de migración de un Persistent Disk a un Hyperdisk.

  • Una aplicación MySQL se ejecuta en un grupo de nodos de GKE con tipos de máquinas N2 y almacena sus datos en un disco persistente SSD.
  • Para garantizar la coherencia de los datos, la aplicación se reduce para evitar nuevas escrituras.
  • Se crea una captura del Persistent Disk, que sirve como copia de seguridad completa de los datos en un momento determinado.
  • Se aprovisiona un nuevo Hyperdisk a partir de la captura y se implementa una nueva instancia de MySQL en un grupo de nodos N4 independiente compatible con Hyperdisk. Esta nueva instancia se adjunta al Hyperdisk recién creado, lo que finaliza la migración al almacenamiento de mayor rendimiento.
Diagrama de arquitectura que muestra la migración de datos de MySQL de Persistent Disk a Hyperdisk mediante una instantánea.
Ilustración 1: Migración de datos de MySQL de Persistent Disk a Hyperdisk mediante una instantánea.

Preparar el entorno

  1. En Cloud Shell, define las variables de entorno de tu proyecto, ubicación y prefijo de clúster.

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

    Haz los cambios siguientes:

    • PROJECT_ID: tu Google Cloud ID de proyecto.
    • EMAIL_ADDRESS: tu dirección de correo electrónico.
    • LOCATION: la zona en la que quieres crear los recursos de implementación. En este tutorial, usaremos la zona us-central1-a.
  2. Clona el repositorio de código de ejemplo de GitHub:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  3. Ve al directorio offline-hyperdisk-migration para empezar a crear recursos de implementación:

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

Crear el clúster de GKE y los grupos de nodos

En este tutorial se usa un clúster de zona para simplificar el proceso, ya que los volúmenes de Hyperdisk son recursos de zona y solo se puede acceder a ellos desde una única zona.

  1. Crea un clúster de GKE de zona:

    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. Añade un grupo de nodos con un tipo de máquina N2 para la implementación inicial de MySQL:

    gcloud container node-pools create regular-pool \
        --cluster ${KUBERNETES_CLUSTER_PREFIX}-cluster \
        --machine-type n2-standard-4 \
        --location ${LOCATION} \
        --num-nodes 1
    
  3. Añade un grupo de nodos con un tipo de máquina N4 en Hyperdisk donde se migrará y se ejecutará la implementación de MySQL:

    gcloud container node-pools create hyperdisk-pool \
        --cluster ${KUBERNETES_CLUSTER_PREFIX}-cluster \
        --machine-type n4-standard-4 \
        --location ${LOCATION} \
        --num-nodes 1
    
  4. Conéctate al clúster:

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

Desplegar MySQL en un disco persistente

En esta sección, desplegarás una instancia de MySQL que usa un disco persistente para el almacenamiento y la cargarás con datos de ejemplo.

  1. Crea y aplica un StorageClass a Hyperdisk. Este StorageClass se usará más adelante en el tutorial.

    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. Crea y despliega una instancia de MySQL que incluya la afinidad de nodos para asegurarte de que los pods se programan en nodos regular-pool y aprovisiona un volumen SSD de Persistent Disk.

    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
    

    Este manifiesto crea una implementación y un servicio de MySQL, con un disco persistente aprovisionado dinámicamente para el almacenamiento de datos. La contraseña del usuario root es migration.

  3. Despliega un pod de cliente de MySQL para cargar datos y verifica la migración de datos:

    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. Conéctate al pod del cliente:

    kubectl exec -it mysql-client -- bash
    
  5. En el shell del pod del cliente, descarga e importa el conjunto de datos de muestra 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. Verifica que los datos se han importado:

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

    El resultado muestra una lista de tablas con el número de filas.

    | 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. Salir de la sesión de mysql:

    exit;
    
  8. Sal del shell del pod del cliente:

    exit
    
  9. Obtén el nombre del PersistentVolume (PV) creado para MySQL y almacénalo en una variable de entorno:

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

Migrar los datos a un volumen de Hyperdisk

Ahora tienes una carga de trabajo de MySQL con datos almacenados en un volumen de disco persistente SSD. En esta sección se describe cómo migrar estos datos a un volumen de Hyperdisk mediante una instantánea. Este método de migración también conserva el volumen del disco persistente original, lo que te permite volver a usar la instancia de MySQL original si es necesario.

  1. Aunque puedes crear capturas de discos sin separarlos de las cargas de trabajo, para asegurar la integridad de los datos de MySQL, debes detener cualquier escritura nueva en el disco durante la creación de la captura. Reduce la escala de la implementación de MySQL a 0 réplicas para detener las escrituras:

    kubectl scale deployment regular-mysql --replicas=0
    
  2. Crea una captura a partir del Persistent Disk:

    gcloud compute disks snapshot ${PV_NAME} --location=${LOCATION} --snapshot-name=original-snapshot --description="snapshot taken from pd-ssd"
    
  3. Crea un volumen de Hyperdisk llamado mysql-recovery a partir de la captura:

    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. Actualiza el archivo de manifiesto del PV restaurado con el ID de tu proyecto:

    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. Crea el PersistentVolume (PV) y el PersistentVolumeClaim (PVC) a partir del nuevo Hyperdisk:

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

Verificar la migración de datos

Implementa una instancia de MySQL que use el volumen de Hyperdisk que acabas de crear. Este pod se programará en el grupo de nodos hyperdisk-pool, que consta de nodos N4.

  1. Despliega la nueva instancia de 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. Para verificar la integridad de los datos, vuelve a conectarte al pod del cliente MySQL:

    kubectl exec -it mysql-client -- bash
    
  3. Dentro del pod del cliente, conéctate a la nueva base de datos de MySQL (recovered-mysql.default) y verifica los datos. La contraseña es migration.

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

    Los datos deben ser los mismos que los de tu instancia de MySQL original en el volumen de disco persistente.

  4. Salir de la sesión de mysql:

    exit;
    
  5. Sal del shell del pod del cliente:

    exit