העברת נתוני MySQL מ-Persistent Disk ל-Hyperdisk ב-GKE

במדריך הזה מוסבר איך להעביר נתוני MySQL קיימים מדיסק מתמיד (PD) אל Hyperdisk ב-Google Kubernetes Engine כדי לשדרג את ביצועי האחסון. ל-Hyperdisk יש IOPS ותפוקה גבוהים יותר מאשר ל-Persistent Disk, מה שיכול לשפר את הביצועים של MySQL על ידי צמצום זמן האחזור של שאילתות וטרנזקציות במסד הנתונים. אפשר להשתמש בתמונות מצב של דיסקים כדי להעביר את הנתונים לסוגים שונים של דיסקים, בהתאם לתאימות של סוג המכונה. לדוגמה, נפחי Hyperdisk תואמים רק לחלק מסוגי המכונות מהדור השלישי, הרביעי והדורות הבאים, כמו N4, שלא תומכים ב-Persistent Disks. מידע נוסף מופיע במאמר בנושא סדרות מכונות.

כדי להדגים את ההעברה מ-Persistent Disk ל-Hyperdisk, במדריך הזה נעשה שימוש במסד הנתונים של Sakila כדי לספק קבוצת נתונים לדוגמה. ‫Sakila הוא מסד נתונים לדוגמה שמסופק על ידי MySQL, ואפשר להשתמש בו כסכימה במדריכים ובדוגמאות. הוא מייצג חנות בדיונית להשכרת DVD וכולל טבלאות של סרטים, שחקנים, לקוחות והשכרות.

המדריך הזה מיועד למומחי אחסון ולאדמינים של אחסון שיוצרים ומקצים אחסון, ומנהלים את אבטחת הנתונים והגישה לנתונים. כדי לקבל מידע נוסף על תפקידים נפוצים ועל משימות לדוגמה שאנחנו מתייחסים אליהן ב Google Cloud תוכן, אפשר לעיין במאמר תפקידים נפוצים של משתמשי GKE ומשימות.

ארכיטקטורת פריסה

התרשים הבא מדגים את תהליך ההעברה מ-Persistent Disk ל-Hyperdisk.

  • אפליקציית MySQL פועלת במאגר צמתים של GKE עם סוגי מכונות N2, והנתונים שלה מאוחסנים ב-SSD של Persistent Disk.
  • כדי להבטיח עקביות בנתונים, האפליקציה מצטמצמת כדי למנוע כתיבה של נתונים חדשים.
  • נוצרת תמונת מצב של דיסק האחסון המתמיד, שמשמשת כגיבוי מלא של הנתונים בנקודת זמן מסוימת.
  • מתוך התמונה נוצר Hyperdisk חדש, ומופע חדש של MySQL נפרס במאגר צמתים נפרד מסוג N4 שתואם ל-Hyperdisk. המופע החדש הזה מצורף ל-Hyperdisk החדש שנוצר, וכך מסתיים המעבר לאחסון עם ביצועים טובים יותר.
דיאגרמת ארכיטקטורה שבה רואים העברה של נתוני MySQL מ-Persistent Disk ל-Hyperdisk באמצעות תמונת מצב.
איור 1: העברת נתונים מ-MySQL מ-Persistent Disk ל-Hyperdisk באמצעות snapshot.

מטרות

במדריך הזה תלמדו איך:

  • פריסת אשכול MySQL.
  • מעלים מערך נתונים לבדיקה.
  • יוצרים תמונת מצב של הנתונים.
  • יוצרים Hyperdisk מקובץ ה-snapshot.
  • מפעילים אשכול MySQL חדש במאגר צמתים מסוג מכונה N4 עם Hyperdisk.
  • כדי לוודא שההעברה בוצעה בהצלחה, צריך לאמת את תקינות הנתונים.

עלויות

במסמך הזה משתמשים ברכיבים הבאים של 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, צרו חשבון כדי שתוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 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. נכנסים לדף IAM במסוף Google Cloud .

      כניסה לדף IAM
    2. בוחרים את הפרויקט.
    3. בעמודה Principal (חשבון המשתמש), מוצאים את כל השורות שבהן מופיע השם שלכם או של קבוצה שאתם נכללים בה. כדי לברר באילו קבוצות אתם נכללים, פנו לאדמין.

    4. בודקים את העמודה Role בכל השורות שבהן מצוין או מופיע השם שלכם, כדי לראות אם רשימת התפקידים כוללת את התפקידים הנדרשים.

    מתן התפקידים

    1. נכנסים לדף IAM במסוף Google Cloud .

      כניסה לדף IAM
    2. בוחרים את הפרויקט.
    3. לוחצים על Grant access.
    4. בשדה New principals, מזינים את מזהה המשתמש. ‫ בדרך כלל מזהה המשתמש הוא כתובת האימייל של חשבון Google.

    5. לוחצים על Select a role ומחפשים את התפקיד.
    6. כדי לתת עוד תפקידים, לוחצים על Add another role ומוסיפים אותם.
    7. לוחצים על Save.

הגדרת Cloud Shell

  1. במסוף Google Cloud , מפעילים את Cloud Shell.

    הפעלת Cloud Shell

    בחלק התחתון של Google Cloud המסוף יתחיל סשן של Cloud Shell ותופיע הודעה של שורת הפקודה. Cloud Shell היא סביבת מעטפת שבה ה-CLI של Google Cloud מותקן ומוגדרים ערכים לפרויקט הקיים. הסשן יופעל תוך כמה שניות.

  2. יתחיל סשן של Cloud Shell ותופיע הודעה של שורת הפקודה. הסשן יופעל תוך כמה שניות.

  3. מגדירים את פרויקט ברירת המחדל:

      gcloud config set project PROJECT_ID
    

    מחליפים את PROJECT_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 מזהה הפרויקט.
    • 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. מוסיפים מאגר צמתים עם סוג מכונה N2 לפריסת MySQL הראשונית:

    gcloud container node-pools create regular-pool \
        --cluster ${KUBERNETES_CLUSTER_PREFIX}-cluster \
        --machine-type n2-standard-4 \
        --location ${LOCATION} \
        --num-nodes 1
    
  3. מוסיפים מאגר צמתים עם סוג מכונה N4 ב-Hyperdisk, שאליו תועבר פריסת 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 שמשתמש ב-Persistent Disk לאחסון, וטוענים בו נתונים לדוגמה.

  1. יצירה והחלה של StorageClass ל-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"
    kubectl apply -f manifests/01-storage-class/storage-class-hdb.yaml
    
  2. יוצרים ומפעילים מכונת MySQL שכוללת קשרי צומת כדי לוודא שה-Pods מתוזמנים ב-regular-pool צמתים, ומקצים נפח SSD של 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
    

    קובץ המניפסט הזה יוצר פריסה ושירות של MySQL, עם דיסק אחסון מתמיד (persistent disk) שהוקצה באופן דינמי לאחסון נתונים. הסיסמה של המשתמש root היא migration.

  3. פורסים Pod של לקוח 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
    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 /
    
    # 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. מקבלים את השם של PersistentVolume ‏ (PV) שנוצר עבור MySQL ומאחסנים אותו במשתנה סביבה:

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

העברת הנתונים לנפח אחסון מסוג Hyperdisk

עכשיו יש לכם עומס עבודה של MySQL עם נתונים שמאוחסנים בנפח SSD של Persistent Disk. בקטע הזה מוסבר איך להעביר את הנתונים האלה לנפח Hyperdisk באמצעות תמונת מצב. בנוסף, הגישה הזו להעברה שומרת את נפח הדיסק הקשיח המקורי, כך שאם צריך, אפשר לחזור לשימוש במופע המקורי של MySQL.

  1. אפשר ליצור קובצי snapshot מדיסקים בלי לנתק אותם מעומסי עבודה, אבל כדי להבטיח את תקינות הנתונים ב-MySQL, צריך להפסיק את כל הפעולות החדשות של כתיבה לדיסק במהלך יצירת קובץ ה-snapshot. מצמצמים את הפריסה של MySQL ל-0 רפליקות כדי להפסיק את פעולות הכתיבה:

    kubectl scale deployment existing-mysql --replicas=0
    
  2. יוצרים קובץ snapshot מדיסק האחסון המתמיד הקיים:

    gcloud compute disks snapshot ${PV_NAME} --zone=${LOCATION} --snapshot-names=original-snapshot --description="snapshot taken from pd-ssd"
    
  3. יוצרים נפח Hyperdisk חדש בשם mysql-recovery מה-snapshot:

    gcloud compute disks create mysql-recovery --project=${PROJECT_ID} \
        --type=hyperdisk-balanced \
        --size=150GB --zone=${LOCATION} \
        --source-snapshot=projects/${PROJECT_ID}/global/snapshots/original-snapshot
    
  4. מעדכנים את קובץ המניפסט של ה-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. יוצרים את נפח האחסון המתמיד (PVC) ואת בקשת נפח האחסון המתמיד מה-Hyperdisk החדש:

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

אימות העברת הנתונים

פורסים מכונת MySQL חדשה שמשתמשת בנפח Hyperdisk שנוצר לאחרונה. ה-Pod הזה יתוזמן במאגר הצמתים hyperdisk-pool שכולל צמתי N4.

  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. כדי לאמת את תקינות הנתונים, מתחברים שוב ל-Pod של לקוח MySQL:

    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 בכרך של Persistent Disk.

  4. יציאה מהסשן של mysql:

    exit;
    
  5. יוצאים מהמעטפת של ה-Pod של הלקוח:

    exit
    

הסרת המשאבים

כדי להימנע מחיובים בחשבון Google Cloud בגלל השימוש במשאבים שנעשה במסגרת המדריך הזה, אפשר למחוק את הפרויקט שמכיל את המשאבים, או להשאיר את הפרויקט ולמחוק את המשאבים בנפרד.

מחיקת הפרויקט

    כדי למחוק Google Cloud פרויקט:

    gcloud projects delete PROJECT_ID

מחיקת משאבים בודדים

אם השתמשתם בפרויקט קיים ואתם לא רוצים למחוק אותו, אתם יכולים למחוק את המשאבים הבודדים:

  1. מגדירים משתני סביבה לניקוי ומאחזרים את השם של נפח האחסון של הדיסק המתמיד שנוצר על ידי mysql-pv-claim PersistentVolumeClaim:

    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 במזהה הפרויקט.

  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} --zone=${LOCATION} --quiet
    gcloud compute disks delete mysql-recovery --zone=${LOCATION} --quiet
    

המאמרים הבאים