Migra tus datos de MySQL de Persistent Disk a Hyperdisk con la Copia de seguridad para GKE

En este instructivo, se muestra cómo migrar aplicaciones con estado en GKE desde tipos de máquinas de generaciones anteriores, como N2, con volúmenes de Persistent Disk adjuntos, a tipos de máquinas de generaciones más recientes, como N4, con volúmenes de Hyperdisk adjuntos con Backup para GKE. Para obtener más información sobre los tipos de máquinas que admiten Hyperdisk, consulta la documentación de Compute Engine.

Para demostrar la migración, en este instructivo, se usan las bases de datos Sakila y World para proporcionar conjuntos de datos de muestra. Sakila es una base de datos de muestra proporcionada por MySQL que representa una tienda de alquiler de DVD ficticia. La base de datos World contiene datos sobre países y ciudades. En el instructivo, se usan dos conjuntos de datos diferentes en espacios de nombres separados para simular un entorno complejo de múltiples usuarios.

Este instructivo está dirigido a los especialistas y administradores de almacenamiento que crean y asignan almacenamiento, y administran la seguridad y el acceso a los datos. Para obtener más información sobre los roles comunes y las tareas de ejemplo a las que se hace referencia en el contenido de Google Cloud, consulta Roles y tareas comunes del usuario de GKE.

Arquitectura de implementación

En el siguiente diagrama, se ilustra el proceso de uso de Backup for GKE para migrar cargas de trabajo de MySQL con estado desde Persistent Disk en tipos de máquinas N2 a Hyperdisk en tipos de máquinas N4.

  • Clúster de origen: Dos implementaciones de MySQL residen en espacios de nombres separados, namespace-a y namespace-b, en un grupo de nodos de la serie de máquinas N2. Estas implementaciones usan discos persistentes SSD para el almacenamiento de datos.
  • Estrategia de copia de seguridad: Habilita el agente de Copia de seguridad para GKE en el clúster y crea un plan de copia de seguridad para capturar los espacios de nombres, los datos de volúmenes y los secretos. Luego, ejecutas una copia de seguridad manual para crear un punto de recuperación de un momento determinado.
  • Transformación y restablecimiento: Defines un plan de restablecimiento con reglas de transformación para adaptar los recursos al entorno de destino. Estas reglas hacen lo siguiente:
    • Intercambia el StorageClass de premium-rwo (PD) por una clase de almacenamiento de Hyperdisk llamada balanced-storage.
    • Modifica las reglas de afinidad de Pods para asegurarte de que las cargas de trabajo restablecidas se programen en un grupo de nodos N4 nuevo.
  • Entorno de destino: Aprovisionas un clúster de GKE nuevo con tipos de máquina N4. El proceso de restablecimiento vuelve a crear los discos como volúmenes de Hyperdisk a partir de la copia de seguridad y, luego, implementa las instancias de MySQL en los nodos N4 compatibles.
Diagrama de arquitectura que muestra la migración de datos de MySQL de Persistent Disk a Hyperdisk con Copia de seguridad para GKE.
Figura 1: Migración de datos de MySQL de Persistent Disk a Hyperdisk con Copia de seguridad para GKE.

Objetivos

En este instructivo, aprenderás a realizar las siguientes tareas:

  • Prepara aplicaciones con estado de GKE para la copia de seguridad.
  • Habilita el complemento de Copia de seguridad para GKE.
  • Crea un plan de copia de seguridad y haz una copia de seguridad del clúster de origen.
  • Crea un plan de restablecimiento que use reglas de transformación para migrar el almacenamiento a Hyperdisk.
  • Restablece la carga de trabajo en un clúster nuevo y verifica los datos.

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios.

Es posible que los usuarios nuevos de Google Cloud cumplan con los requisitos para acceder a una prueba gratuita.

Antes de comenzar

  1. Accede a tu cuenta de Google Cloud . Si eres nuevo en Google Cloud, crea una cuenta para evaluar el rendimiento de nuestros productos en situaciones reales. Los clientes nuevos también obtienen $300 en créditos gratuitos para ejecutar, probar y, además, implementar cargas de trabajo.
  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, Backup for GKE, and IAM 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, Backup for GKE, and IAM 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. Asegúrate de tener los siguientes roles en el proyecto: roles/container.admin, roles/iam.serviceAccountAdmin, roles/compute.admin, roles/gkebackup.admin, roles/monitoring.viewer

    Verifica los roles

    1. En la consola de Google Cloud , dirígete a la página IAM.

      Ir a IAM
    2. Selecciona el proyecto.
    3. En la columna Principal, busca todas las filas que te identifiquen a ti o a un grupo en el que se te incluya. Para saber en qué grupos estás incluido, comunícate con tu administrador.

    4. Para todas las filas en las que se te especifique o se te incluya, verifica la columna Rol para ver si la lista de roles incluye los roles necesarios.

    Otorga los roles

    1. En la consola de Google Cloud , dirígete a la página IAM.

      Ir a IAM
    2. Selecciona el proyecto.
    3. Haz clic en Otorgar acceso.
    4. En el campo Principales nuevas, ingresa tu identificador de usuario. Esta suele ser la dirección de correo electrónico de una Cuenta de Google.

    5. Haz clic en Seleccionar un rol y, luego, busca el rol.
    6. Para otorgar roles adicionales, haz clic en Agregar otro rol y agrega uno más.
    7. Haz clic en Guardar.

Configura Cloud Shell

  1. En la consola de Google Cloud , activa Cloud Shell.

    Activa Cloud Shell

    Se inicia una sesión de Cloud Shell y se muestra una ventana de línea de comandos. La sesión puede tardar unos segundos en inicializarse.

  2. Configura el proyecto predeterminado:

      gcloud config set project PROJECT_ID
    

    Reemplaza PROJECT_ID con el ID del proyecto.

Configura el entorno

En esta sección, prepararás las variables de entorno y clonarás el repositorio de muestra.

  1. Configura las variables de entorno para tu proyecto, los nombres del clúster y la zona:

    export PROJECT_ID=PROJECT_ID
    export KUBERNETES_CLUSTER_PREFIX=backup-gke-migration
    export TARGET_CLUSTER_PREFIX=restore-gke-migration
    export ZONE=us-central1-a
    

    Reemplaza PROJECT_ID por el ID del proyecto de Google Cloud.

  2. Clona el repositorio de código de muestra y navega al directorio:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd kubernetes-engine-samples/databases/backup-migration
    

Crea el clúster de GKE de origen

Crea un clúster zonal con un grupo de nodos que use tipos de máquinas N2 y volúmenes de Persistent Disk conectados.

  1. Crea el clúster:

    gcloud container clusters create ${KUBERNETES_CLUSTER_PREFIX}-cluster \
      --location ${ZONE} \
      --node-locations ${ZONE} \
      --shielded-secure-boot \
      --shielded-integrity-monitoring \
      --machine-type "e2-micro" \
      --num-nodes "1"
    
  2. Crea un grupo de nodos con tipos de máquinas n2-standard-4 para la carga de trabajo de origen:

    gcloud container node-pools create regular-pool \
      --cluster ${KUBERNETES_CLUSTER_PREFIX}-cluster \
      --machine-type n2-standard-4 \
      --zone ${ZONE} \
      --num-nodes 1
    
  3. Habilita el complemento de Copia de seguridad para GKE en el clúster de origen:

    gcloud container clusters update ${KUBERNETES_CLUSTER_PREFIX}-cluster \
      --project=${PROJECT_ID}  \
      --location=${ZONE} \
      --update-addons=BackupRestore=ENABLED
    
  4. Obtén credenciales para el clúster:

    gcloud container clusters get-credentials ${KUBERNETES_CLUSTER_PREFIX}-cluster --zone ${ZONE}
    
  5. Verifica que el agente de Copia de seguridad para GKE esté habilitado:

    gcloud container clusters describe ${KUBERNETES_CLUSTER_PREFIX}-cluster \
      --project=${PROJECT_ID}  \
      --location=${ZONE}
    

    El resultado es similar al siguiente y confirma que el agente de copias de seguridad está habilitado:

    addonsConfig:
      gkeBackupAgentConfig:
        enabled: true
    

Implementa MySQL con datos de muestra

Implementa dos bases de datos de MySQL en espacios de nombres separados para simular un entorno de producción.

  1. Crea los espacios de nombres namespace-a y namespace-b:

    kubectl create namespace namespace-a
    kubectl create namespace namespace-b
    
  2. Implementa las cargas de trabajo de MySQL en namespace-a y namespace-b:

    • Implementa el archivo mysql-a-deployment.yaml:

      kubectl apply -f manifests/02-mysql/mysql-a-deployment.yaml -n namespace-a
      

      El siguiente manifiesto crea un Pod de MySQL en namespace-a con discos SSD de disco persistente aprovisionados de forma dinámica en los nodos regular-pool. La contraseña raíz se establece en migration:

      apiVersion: v1
      kind: Service
      metadata:
        name: mysql-a
        labels:
          app: mysql
      spec:
        ports:
          - port: 3306
        selector:
          app: mysql
        clusterIP: None
      ---
      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: mysql-a-pv-claim
        labels:
          app: mysql
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 30Gi
        storageClassName: premium-rwo
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: existing-mysql-a
        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-a
              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-a-pv-claim
    • Implementa el archivo mysql-b-deployment.yaml:

      kubectl apply -f manifests/02-mysql/mysql-b-deployment.yaml -n namespace-b
      

      El siguiente manifiesto crea un Pod de MySQL en namespace-b con discos SSD de disco persistente aprovisionados de forma dinámica en los nodos regular-pool. La contraseña raíz se establece en migration:

      apiVersion: v1
      kind: Service
      metadata:
        name: mysql-b
        labels:
          app: mysql
      spec:
        ports:
          - port: 3306
        selector:
          app: mysql
        clusterIP: None
      ---
      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: mysql-b-pv-claim
        labels:
          app: mysql
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 30Gi
        storageClassName: premium-rwo
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: existing-mysql-b
        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-b
              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-b-pv-claim
  3. Implementa un Pod cliente de MySQL para subir conjuntos de datos de muestra:

    kubectl apply -f manifests/02-mysql/mysql-client.yaml
    kubectl wait pods mysql-client --for condition=Ready --timeout=300s
    

    En el siguiente manifiesto, se implementa un Pod de cliente de 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
  4. Conéctate al Pod cliente:

    kubectl exec -it mysql-client -- bash
    
  5. Dentro del Pod, descarga los conjuntos de datos de muestra Sakila y World:

    curl --output dataset.tgz "https://downloads.mysql.com/docs/sakila-db.tar.gz"
    tar -xvzf dataset.tgz -C ./
    
    curl --output world-db.tar.gz "https://downloads.mysql.com/docs/world-db.tar.gz"
    tar xvzf world-db.tar.gz -C ./
    
  6. Importa el conjunto de datos de Sakila a la base de datos mysql-a:

    mysql -u root -h mysql-a.namespace-a -p
    # Enter password: migration
    
    SOURCE /sakila-db/sakila-schema.sql;
    SOURCE /sakila-db/sakila-data.sql;
    
  7. Verifica los datos de Sakila importados:

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

    Sal de MySQL:

    exit
    
  8. Importa el conjunto de datos World a la base de datos mysql-b:

    mysql -u root -h mysql-b.namespace-b -p
    # Enter password: migration
    
    SOURCE /world-db/world.sql;
    
  9. Verifica los datos importados de World:

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

    El resultado es similar a lo siguiente:

    +-----------------+------------+
    | table_name      | table_rows |
    +-----------------+------------+
    | city            |       4079 |
    | country         |        239 |
    | countrylanguage |        984 |
    +-----------------+------------+
    

    Sal de MySQL:

    exit
    
  10. Sal de la shell del Pod del cliente:

    exit
    

Crea una copia de seguridad del clúster de GKE

Crea una copia de seguridad de todo el clúster, incluidos los secretos y los volúmenes.

  1. Crea un plan de copias de seguridad:

    gcloud beta container backup-restore backup-plans create main-plan \
      --project=${PROJECT_ID} \
      --location=us-central1 \
      --cluster=projects/${PROJECT_ID}/locations/${ZONE}/clusters/${KUBERNETES_CLUSTER_PREFIX}-cluster \
      --selected-namespaces=namespace-a,namespace-b,default \
      --include-secrets \
      --include-volume-data \
      --target-rpo-minutes=1440 \
      --backup-retain-days=7 \
      --backup-delete-lock-days=3 \
      --locked
    
    • --selected-namespaces: Crea copias de seguridad de espacios de nombres específicos para evitar conflictos con los recursos del sistema.
    • --include-volume-data: Ayuda a garantizar que se cree una copia de seguridad de los datos del Persistent Disk.
    • --target-rpo-minutes: Configura la programación de copias de seguridad según el objetivo de punto de recuperación (RPO). El RPO es el período máximo aceptable durante el cual se podrían perder datos y determina la frecuencia de las copias de seguridad. Con 1440 minutos (1 día), las copias de seguridad se programan para ejecutarse a diario.
  2. Crea una copia de seguridad:

    gcloud beta container backup-restore backups create first-backup \
        --project=${PROJECT_ID} \
        --location=us-central1 \
        --backup-plan=main-plan \
        --wait-for-completion
    

    Espera a que el resultado muestre Backup state: SUCCEEDED.

  3. Verifica que se haya creado la copia de seguridad:

    gcloud beta container backup-restore backups list \
        --project=${PROJECT_ID} \
        --location=us-central1 \
        --backup-plan=main-plan
    

Restauración con transformación de Hyperdisk

Restablece la copia de seguridad en un clúster nuevo. La restauración transforma el almacenamiento de Persistent Disk a Hyperdisk y mueve las cargas de trabajo a nodos N4.

  1. Crea el clúster de GKE de destino en un nodo N4:

    gcloud container clusters create ${TARGET_CLUSTER_PREFIX}-cluster \
      --location ${ZONE} \
      --node-locations ${ZONE} \
      --shielded-secure-boot \
      --shielded-integrity-monitoring \
      --machine-type "e2-micro" \
      --num-nodes "1"
    
  2. Crea un grupo de nodos con tipos de máquinas n4-standard-4, que son necesarios para Hyperdisk:

    gcloud container node-pools create hyperdisk-pool \
      --cluster ${TARGET_CLUSTER_PREFIX}-cluster \
      --machine-type n4-standard-4 \
      --zone ${ZONE} \
      --num-nodes 1
    
  3. Obtén credenciales para el clúster de destino:

    gcloud container clusters get-credentials ${TARGET_CLUSTER_PREFIX}-cluster --zone ${ZONE}
    
  4. Aplica el Hyperdisk StorageClass llamado balanced-storage:

    kubectl apply -f manifests/01-storage-class/storage-class-hdb.yaml
    

    En el siguiente manifiesto, se define un Hyperdisk 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"
  5. Revisa las reglas de transformación en el archivo manifests/03-transformation-rule/volume.yaml. Este archivo define cómo se modifican los recursos durante el restablecimiento:

    transformationRules:
    - description: Change the StorageClass on PVCs from premium-rwo to balanced-storage
      resourceFilter:
        namespaces: ["namespace-a","namespace-b"]
        groupKinds:
        - resourceGroup: ""
          resourceKind: PersistentVolumeClaim
      fieldActions:
      - op: REPLACE
        path: "/spec/storageClassName"
        value: "balanced-storage"
    - description: Change node type from n2-standard-4 to n4-standard-4
      resourceFilter:
        namespaces: ["namespace-a","namespace-b"]
        jsonPath: ".metadata[?(@.name == 'existing-mysql')]"
        groupKinds:
        - resourceGroup: apps
          resourceKind: Deployment
      fieldActions:
      - op: REPLACE
        path: "/spec/template/spec/affinity/nodeAffinity/preferredDuringSchedulingIgnoredDuringExecution/0/preference/matchExpressions/0/values/0"
        value: "n4-standard-4"
    • Transformación de PVC: Cambia storageClassName a balanced-storage (Hyperdisk).
    • Transformación de la implementación: Actualiza la afinidad de nodos para programar Pods en nodos n4-standard-4.
  6. Crea un plan de restablecimiento con las siguientes reglas de transformación:

    gcloud beta container backup-restore restore-plans create main-restore \
      --project=${PROJECT_ID} \
      --location=us-central1 \
      --backup-plan=projects/${PROJECT_ID}/locations/us-central1/backupPlans/main-plan \
      --cluster=projects/${PROJECT_ID}/locations/${ZONE}/clusters/${TARGET_CLUSTER_PREFIX}-cluster \
      --namespaced-resource-restore-mode=merge-replace-on-conflict \
      --all-namespaces \
      --cluster-resource-conflict-policy=use-existing-version  \
      --cluster-resource-scope-selected-group-kinds=cluster-resource-scope-all-group-kinds \
      --volume-data-restore-policy=restore-volume-data-from-backup \
      --transformation-rules-file=manifests/03-transformation-rule/volume.yaml
    
  7. Realiza el restablecimiento:

    gcloud beta container backup-restore restores create first-restore \
       --project=${PROJECT_ID} \
       --location=us-central1 \
       --restore-plan=main-restore \
       --backup=projects/${PROJECT_ID}/locations/us-central1/backupPlans/main-plan/backups/first-backup
    

Verifica la migración

Verifica que las aplicaciones se ejecuten en el clúster nuevo y que los datos estén intactos.

  1. Verifica si los Pods se están ejecutando:

    kubectl get pods -A
    
  2. Conéctate al Pod cliente de MySQL en el clúster nuevo:

    # Verify that the client Pod is running
    kubectl apply -f manifests/02-mysql/mysql-client.yaml
    kubectl wait pods mysql-client --for condition=Ready --timeout=300s
    kubectl exec -it mysql-client -- bash
    
  3. Verifica la base de datos Sakila restablecida en namespace-a:

    mysql -u root -h mysql-a.namespace-a -p
    # Password: migration
    
    USE sakila;
    SELECT table_name, table_rows FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'sakila';
    
  4. Verifica la base de datos World restablecida en namespace-b:

    mysql -u root -h mysql-b.namespace-b -p
    # Password: migration
    
    USE world;
    SELECT table_name, table_rows FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'world';
    

Realiza una limpieza

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos usados en este instructivo, borra el proyecto que contiene los recursos o conserva el proyecto y borra los recursos individuales.

  1. Borra los clústeres de GKE:

    gcloud container clusters delete ${KUBERNETES_CLUSTER_PREFIX}-cluster --location ${ZONE} --quiet
    gcloud container clusters delete ${TARGET_CLUSTER_PREFIX}-cluster --location ${ZONE} --quiet
    
  2. Borra los planes de copia de seguridad y restablecimiento:

    # Delete the restore plan
    gcloud beta container backup-restore restore-plans delete main-restore \
        --project=${PROJECT_ID} \
        --location=us-central1 \
        --quiet
    
    # Delete the Backup
    gcloud beta container backup-restore backups delete first-backup \
        --project=${PROJECT_ID} \
        --location=us-central1 \
        --backup-plan=main-plan \
        --quiet
    
    # Delete the backup plan
    gcloud beta container backup-restore backup-plans delete main-plan \
        --project=${PROJECT_ID} \
        --location=us-central1 \
        --quiet
    

¿Qué sigue?