Esegui il deployment di un cluster MySQL stateful su GKE


Questo documento è rivolto ad amministratori di database, architetti cloud e professionisti delle operazioni interessati a eseguire il deployment di una topologia MySQL ad alta disponibilità su Google Kubernetes Engine.

Segui questo tutorial per scoprire come eseguire il deployment di un cluster InnoDB di MySQL e di un ClusterSet InnoDB di MySQL, oltre al middleware MySQL Router sul tuo cluster GKE e come eseguire gli upgrade.

Obiettivi

In questo tutorial imparerai a:

  • Crea ed esegui il deployment di un servizio Kubernetes con stato.
  • Esegui il deployment di un cluster MySQL InnoDB per l'alta disponibilità.
  • Esegui il deployment del middleware Router per il routing delle operazioni del database.
  • Esegui il deployment di un ClusterSet MySQL InnoDB per la tolleranza in caso di emergenza.
  • Simula un failover del cluster MySQL.
  • Esegui un upgrade della versione di MySQL.

Le seguenti sezioni descrivono l'architettura della soluzione che utilizzerai in questo tutorial.

Cluster MySQL InnoDB

Nel tuo cluster GKE regionale, utilizzando un StatefulSet, esegui il deployment di un'istanza del database MySQL con la denominazione e la configurazione necessarie per creare un cluster MySQL InnoDB. per fornire la tolleranza agli errori. e ad alta disponibilità, eseguirai il deployment di Compute Engine. Ciò garantisce che la maggior parte dei pod in zone diverse disponibili in qualsiasi momento per il successo di elezioni primarie utilizzando un di consenso e rende il tuo cluster MySQL InnoDB tollerante ai singoli a livello di zona.

Diagramma dell'architettura che mostra la relazione tra applicazioni, router MySQL e cluster MySQL
Figura 1: architettura di esempio di un singolo cluster InnoDB MySQL

Una volta disegnato, designa un pod come istanza principale per gestire sia le operazioni di lettura che quelle di scrittura. La gli altri due pod sono repliche di sola lettura secondarie. Se l'istanza principale presenta un errore di infrastruttura, puoi promuovere uno di questi due pod di replica in modo che diventi quello principale.

In uno spazio dei nomi separato, esegui il deployment di tre pod del router MySQL il routing della connessione per una migliore resilienza. Invece di connetterti direttamente il servizio di database, le applicazioni si connettono ai pod del router MySQL. Ogni pod del router è a conoscenza dello stato e dello scopo di ogni MySQL InnoDB il pod del cluster e instrada le operazioni dell'applicazione al rispettivo pod integro. Lo stato di routing viene memorizzato nella cache nei pod del router e aggiornato dal cluster archiviati su ciascun nodo del cluster MySQL InnoDB. In caso di errore di un'istanza, il router regola il routing della connessione a un'istanza attiva.

Set di cluster MySQL InnoDB

Puoi creare un ClusterSet MySQL InnoDB da un cluster MySQL InnoDB iniziale. Ciò consente di aumentare la tolleranza in caso di emergenza se il cluster principale non è più disponibili.

Il diagramma mostra come i cluster MySQL InnoDB principali e di replica vengono mantenuti sincronizzati tramite la replica asincrona.
Figura 2: esempio di architettura ClusterSet multiregionale che contiene un cluster principale e un cluster di replica

Se l'istanza principale del cluster InnoDB di MySQL non è più disponibile, puoi promuovere un cluster di repliche nel ClusterSet a principale. Quando utilizzi il middleware MySQL Router, la tua applicazione non deve monitorare lo stato dell'istanza del database principale. Il routing viene modificato in modo da inviare le connessioni alla nuova istanza principale dopo le elezioni. Tuttavia, è tua responsabilità assicurarti che le applicazioni che si connettono al middleware MySQL Router seguano le best practice per la resilienza, in modo che le connessioni vengano riprovate se si verifica un errore durante il failover del cluster.

Costi

In questo documento utilizzi i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi basata sull'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud potrebbero essere idonei per una prova gratuita.

Una volta completate le attività descritte in questo documento, puoi evitare la fatturazione continua eliminando le risorse che hai creato. Per ulteriori informazioni, consulta la pagina Pulizia.

Prima di iniziare

Configura il progetto

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the GKE API.

    Enable the API

  5. In the Google Cloud console, on the project selector page, click Create project to begin creating a new Google Cloud project.

    Go to project selector

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the GKE API.

    Enable the API

Configurare i ruoli

  1. Grant roles to your user account. Run the following command once for each of the following IAM roles: role/storage.objectViewer, role/logging.logWriter, role/artifactregistry.Admin, roles/container.clusterAdmin, role/container.serviceAgent, roles/serviceusage.serviceUsageAdmin, roles/iam.serviceAccountAdmin

    $ gcloud projects add-iam-policy-binding PROJECT_ID --member="USER_IDENTIFIER" --role=ROLE
    • Replace PROJECT_ID with your project ID.
    • Replace USER_IDENTIFIER with the identifier for your user account. For example, user:myemail@example.com.

    • Replace ROLE with each individual role.

Configura l'ambiente

In questo tutorial utilizzerai Cloud Shell per gestire le risorse ospitate in Google Cloud. Cloud Shell è preinstallato con Docker e con le interfacce a riga di comando kubectl e gcloud.

Per utilizzare Cloud Shell per configurare l'ambiente:

  1. Imposta le variabili di ambiente.

    export PROJECT_ID=PROJECT_ID
    export CLUSTER_NAME=gkemulti-west
    export REGION=COMPUTE_REGION
    

    Sostituisci i seguenti valori:

    • PROJECT_ID: il tuo ID progetto Google Cloud.
    • COMPUTE_REGION: la tua regione Compute Engine. Per questo tutorial, la regione è us-west1. In genere, è preferibile scegliere una regione vicina a te.
  2. Imposta le variabili di ambiente predefinite.

     gcloud config set project PROJECT_ID
     gcloud config set compute/region COMPUTE_REGION
    
  3. Clonare il repository di codice.

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  4. Passa alla directory di lavoro.

    cd kubernetes-engine-samples/databases/gke-stateful-mysql/kubernetes
    

Crea un cluster GKE

In questa sezione crei un cluster GKE regionale. A differenza di un cluster a livello di zona, il piano di controllo di un cluster a livello di regione viene replicato in più zone, quindi un'interruzione in una singola zona non rende il piano di controllo non disponibile.

Per creare un cluster GKE, segui questi passaggi:

Autopilot

  1. In Cloud Shell, crea un cluster GKE Autopilot regione us-west1.

    gcloud container clusters create-auto $CLUSTER_NAME \
        --region=$REGION
    
  2. Recupera le credenziali del cluster GKE.

    gcloud container clusters get-credentials $CLUSTER_NAME \
      --region=$REGION
    
  3. Eseguire il deployment di un servizio in tre zone.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: prepare-three-zone-ha
      labels:
        app: prepare-three-zone-ha
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: prepare-three-zone-ha
      template:
        metadata:
          labels:
            app: prepare-three-zone-ha
        spec:
          affinity:
            # Tell Kubernetes to avoid scheduling a replica in a zone where there
            # is already a replica with the label "app: prepare-three-zone-ha"
            podAntiAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                  - key: app
                    operator: In
                    values:
                    - prepare-three-zone-ha
                topologyKey: "topology.kubernetes.io/zone"
          containers:
          - name: prepare-three-zone-ha
            image: busybox:latest
            command:
                - "/bin/sh"
                - "-c"
                - "while true; do sleep 3600; done"
            resources:
              limits:
                cpu: "500m"
                ephemeral-storage: "10Mi"
                memory: "0.5Gi"
              requests:
                cpu: "500m"
                ephemeral-storage: "10Mi"
                memory: "0.5Gi"
    kubectl apply -f prepare-for-ha.yaml
    

    Per impostazione predefinita, Autopilot esegue il provisioning delle risorse in due zone. Il deployment definito in prepare-for-ha.yaml garantisce che Autopilot esegue il provisioning dei nodi in tre zone cluster, impostando replicas:3, podAntiAffinity con requiredDuringSchedulingIgnoredDuringExecution e topologyKey: "topology.kubernetes.io/zone".

  4. Controlla lo stato del deployment.

    kubectl get deployment prepare-three-zone-ha --watch
    

    Quando vedi tre pod in stato pronto, annulla questo comando con CTRL+C. L'output è simile al seguente:

    NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
    prepare-three-zone-ha   0/3     3            0           9s
    prepare-three-zone-ha   1/3     3            1           116s
    prepare-three-zone-ha   2/3     3            2           119s
    prepare-three-zone-ha   3/3     3            3           2m16s
    
  5. Esegui questo script per verificare che sia stato eseguito il deployment dei pod in tre zone.

    bash ../scripts/inspect_pod_node.sh default
    

    Ogni riga dell'output corrisponde a un pod e la seconda colonna indica la zona cloud. L'output è simile al seguente:

    gk3-gkemulti-west1-default-pool-eb354e2d-z6mv us-west1-b prepare-three-zone-ha-7885d77d9c-8f7qb
    gk3-gkemulti-west1-nap-25b73chq-739a9d40-4csr us-west1-c prepare-three-zone-ha-7885d77d9c-98fpn
    gk3-gkemulti-west1-default-pool-160c3578-bmm2 us-west1-a prepare-three-zone-ha-7885d77d9c-phmhj
    

Standard

  1. In Cloud Shell, crea un cluster GKE Standard nella regione us-west1.

    gcloud container clusters create $CLUSTER_NAME \
      --region=$REGION \
      --machine-type="e2-standard-2" \
      --disk-type="pd-standard" \
      --num-nodes="5"
    
  2. Recupera le credenziali del cluster GKE.

    gcloud container clusters get-credentials $CLUSTER_NAME \
      --region=$REGION
    

Esegui il deployment degli StatefulSet MySQL

In questa sezione eseguirai il deployment di un database MySQL StatefulSet: Ogni StatefulSet è composto da tre repliche MySQL.

Per eseguire il deployment dello StatefulSet MySQL, segui questi passaggi:

  1. Crea uno spazio dei nomi per il StatefulSet.

    kubectl create namespace mysql1
    
  2. Crea il secret MySQL.

    apiVersion: v1
    kind: Secret
    metadata:
      name: mysql-secret
    type: Opaque
    data:
      password: UGFzc3dvcmQkMTIzNDU2 # Password$123456
      admin-password: UGFzc3dvcmQkMTIzNDU2 # Password$123456
    kubectl apply -n mysql1 -f secret.yaml
    

    La password viene dispiattata con ogni pod e utilizzata dagli script di gestione e dai comandi per il dispiacciamento di MySQL InnoDB Cluster e ClusterSet in questo tutorial.

  3. Creare un oggetto StorageClass.

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: fast-storageclass
    provisioner: pd.csi.storage.gke.io
    volumeBindingMode: WaitForFirstConsumer
    reclaimPolicy: Retain
    allowVolumeExpansion: true
    parameters:
      type: pd-balanced
    kubectl apply -n mysql1 -f storageclass.yaml
    

    Questa classe di archiviazione utilizza il tipo di disco permanente pd-balanced che bilancia prestazioni e costi. Il campo volumeBindingMode è impostato su WaitForFirstConsumer, il che significa che GKE ritarda il provisioning di un PersistentVolume fino alla creazione del pod. Questa impostazione garantisce che il provisioning del disco venga eseguito nella stessa zona in cui è pianificato il pod.

  4. Esegui il deployment del StatefulSet dei pod dell'istanza MySQL.

    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: dbc1
      labels:
        app: mysql
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: mysql
      serviceName: mysql
      template:
        metadata:
          labels:
            app: mysql
        spec:
          topologySpreadConstraints:
          - maxSkew: 1
            topologyKey: "topology.kubernetes.io/zone"
            whenUnsatisfiable: DoNotSchedule
            labelSelector:
              matchLabels:
                app: mysql
          affinity:
            podAntiAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                  - key: app
                    operator: In
                    values:
                    - mysql
                topologyKey: "kubernetes.io/hostname"
          containers:
          - name: mysql
            image: mysql/mysql-server:8.0.28
            command:
            - /bin/bash
            args:
            - -c
            - >-
              /entrypoint.sh
              --server-id=$((20 +  $(echo $HOSTNAME | grep -o '[^-]*$') + 1))
              --report-host=${HOSTNAME}.mysql.mysql1.svc.cluster.local
              --binlog-checksum=NONE
              --enforce-gtid-consistency=ON
              --gtid-mode=ON
              --default-authentication-plugin=mysql_native_password
            env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: password
            - name: MYSQL_ADMIN_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: admin-password
            - name: MYSQL_ROOT_HOST
              value: '%'
            ports:
            - name: mysql
              containerPort: 3306
            - name: mysqlx
              containerPort: 33060
            - name: xcom
              containerPort: 33061
            resources:
              limits:
                cpu: "500m"
                ephemeral-storage: "1Gi"
                memory: "1Gi"
              requests:
                cpu: "500m"
                ephemeral-storage: "1Gi"
                memory: "1Gi"
            volumeMounts:
            - name: mysql
              mountPath: /var/lib/mysql
              subPath: mysql
            readinessProbe:
              exec:
                command:
                - bash
                - "-c"
                - |
                  mysql -h127.0.0.1 -uroot -p$MYSQL_ROOT_PASSWORD -e'SELECT 1'
              initialDelaySeconds: 30
              periodSeconds: 2
              timeoutSeconds: 1
            livenessProbe:
              exec:
                command:
                - bash
                - "-c"
                - |
                  mysqladmin -uroot -p$MYSQL_ROOT_PASSWORD ping
              initialDelaySeconds: 30
              periodSeconds: 10
              timeoutSeconds: 5
      updateStrategy:
        rollingUpdate:
          partition: 0
        type: RollingUpdate
      volumeClaimTemplates:
      - metadata:
          name: mysql
          labels:
            app: mysql
        spec:
          storageClassName: fast-storageclass
          volumeMode: Filesystem
          accessModes:
          - ReadWriteOnce
          resources:
            requests:
              storage: 10Gi
    kubectl apply -n mysql1 -f c1-mysql.yaml
    

    Questo comando esegue il deployment dello StatefulSet composto da tre repliche. In questo il deployment del cluster MySQL primario viene eseguito in tre zone us-west1. L'output è simile al seguente:

    service/mysql created
    statefulset.apps/dbc1 created
    

    In questo tutorial, le richieste e i limiti delle risorse sono impostati su valori minimi per risparmiare sui costi. Quando pianifichi un carico di lavoro di produzione, assicurati di impostare questi valori in modo appropriato per le esigenze della tua organizzazione.

  5. Verifica che l'oggetto StatefulSet sia stato creato correttamente.

    kubectl get statefulset -n mysql1 --watch
    

    La preparazione dello StatefulSet potrebbe richiedere circa 10 minuti.

  6. Quando tutti e tre i pod sono in stato pronto, esci dal comando utilizzando Ctrl+C. Se vengono visualizzati PodUnscheduleable errori dovuti a CPU o memoria insufficiente, attendi un per consentire il ridimensionamento del piano di controllo per adattarlo al carico di lavoro di grandi dimensioni.

    L'output è simile al seguente:

    NAME   READY   AGE
    dbc1   1/3     39s
    dbc1   2/3     50s
    dbc1   3/3     73s
    
  7. Per esaminare il posizionamento dei pod nei nodi dei cluster GKE, esegui questo script:

    bash ../scripts/inspect_pod_node.sh mysql1 mysql
    

    L'output mostra il nome del pod, il nome del nodo GKE e zona in cui viene eseguito il provisioning del nodo ed è simile alla seguente:

    gke-gkemulti-west-5-default-pool-4bcaca65-jch0 us-west1-b dbc1-0
    gke-gkemulti-west-5-default-pool-1ac6e8b5-ddjx us-west1-c dbc1-1
    gke-gkemulti-west-5-default-pool-1f5baa66-bf8t us-west1-a dbc1-2
    

    Le colonne nell'output rappresentano il nome host, la zona cloud e il nome del pod. rispettivamente.

    Il criterio topologySpreadConstraints nella specifica StatefulSet (c1-mysql.yaml) indica allo scheduler di posizionare i pod in modo uniforme nel dominio di errore (topology.kubernetes.io/zone).

    Il criterio podAntiAffinity applica il vincolo a cui i pod sono richiesti non essere posizionati sullo stesso nodo del cluster GKE (kubernetes.io/hostname). Per Pod di istanze MySQL, questo criterio determina il deployment dei pod in modo uniforme tra le tre zone della regione Google Cloud. Questo posizionamento consente disponibilità elevata del cluster MySQL InnoDB posizionando ogni istanza di database in un dominio in errore separato.

Prepara il cluster InnoDB MySQL principale

Per configurare un cluster MySQL InnoDB:

  1. Nel terminale Cloud Shell, imposta la replica del gruppo per le istanze MySQL da aggiungere al cluster.

    bash ../scripts/c1-clustersetup.sh
    
    POD_ORDINAL_START=${1:-0}
    POD_ORDINAL_END=${2:-2}
    for i in $(seq ${POD_ORDINAL_START} ${POD_ORDINAL_END}); do
      echo "Configuring pod mysql1/dbc1-${i}"
      cat <<'  EOF' | kubectl -n mysql1 exec -i dbc1-${i} -- bash -c 'mysql -uroot -proot --password=${MYSQL_ROOT_PASSWORD}'
    INSTALL PLUGIN group_replication SONAME 'group_replication.so';
    RESET PERSIST IF EXISTS group_replication_ip_allowlist;
    RESET PERSIST IF EXISTS binlog_transaction_dependency_tracking;
    SET @@PERSIST.group_replication_ip_allowlist = 'mysql.mysql1.svc.cluster.local';
    SET @@PERSIST.binlog_transaction_dependency_tracking = 'WRITESET';
      EOF
    done

    Lo script si connetterà da remoto a ciascuna delle tre istanze MySQL Impostare e mantenere le seguenti variabili di ambiente:

    • group_replication_ip_allowlist: consente l'istanza all'interno del cluster per la connessione a qualsiasi istanza del gruppo.
    • binlog_transaction_dependency_tracking='WRITESET': consente transazioni parallelizzate che non entrano in conflitto.

    Nelle versioni di MySQL precedenti alla 8.0.22, utilizza group_replication_ip_whitelist instead of group_replication_ip_allowlist.

  2. Apri un secondo terminale per non dover creare una shell per ogni pod.

  3. Connettiti a MySQL Shell sul pod dbc1-0.

    kubectl -n mysql1 exec -it dbc1-0 -- \
        /bin/bash \
        -c 'mysqlsh --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-0.mysql.mysql1.svc.cluster.local"'
    
  4. Verifica la lista consentita della replica di gruppo MySQL per la connessione ad altre istanze.

    \sql SELECT @@group_replication_ip_allowlist;
    

    L'output è simile al seguente:

    +----------------------------------+
    | @@group_replication_ip_allowlist |
    +----------------------------------+
    | mysql.mysql1.svc.cluster.local   |
    +----------------------------------+
    
  5. Verifica che server-id sia univoco in ciascuna istanza.

    \sql SELECT @@server_id;
    

    L'output è simile al seguente:

    +-------------+
    | @@server_id |
    +-------------+
    |          21 |
    +-------------+
    
  6. Configura ogni istanza per l'utilizzo del cluster MySQL InnoDB e crea un su ogni istanza.

    \js
    dba.configureInstance('root@dbc1-0.mysql.mysql1.svc.cluster.local', {password: os.getenv("MYSQL_ROOT_PASSWORD"),clusterAdmin: 'icadmin', clusterAdminPassword: os.getenv("MYSQL_ADMIN_PASSWORD")});
    dba.configureInstance('root@dbc1-1.mysql.mysql1.svc.cluster.local', {password: os.getenv("MYSQL_ROOT_PASSWORD"),clusterAdmin: 'icadmin', clusterAdminPassword: os.getenv("MYSQL_ADMIN_PASSWORD")});
    dba.configureInstance('root@dbc1-2.mysql.mysql1.svc.cluster.local', {password: os.getenv("MYSQL_ROOT_PASSWORD"),clusterAdmin: 'icadmin', clusterAdminPassword: os.getenv("MYSQL_ADMIN_PASSWORD")});
    

    Tutte le istanze devono avere lo stesso nome utente e la stessa password affinché Il cluster MySQL InnoDB deve funzionare correttamente. Ogni comando produce simile al seguente:

    ...
    
    The instance 'dbc1-2.mysql:3306' is valid to be used in an InnoDB cluster.
    
    Cluster admin user 'icadmin'@'%' created.
    The instance 'dbc1-2.mysql.mysql1.svc.cluster.local:3306' is already
    ready to be used in an InnoDB cluster.
    
    Successfully enabled parallel appliers.
    
  7. Verifica che l'istanza sia pronta per essere utilizzata in un cluster MySQL InnoDB.

    dba.checkInstanceConfiguration()
    

    L'output è simile al seguente:

    ...
    
    The instance 'dbc1-0.mysql.mysql1.svc.cluster.local:3306' is valid to be used in an InnoDB cluster.
    
    {
        "status": "ok"
    }
    

    Facoltativamente, puoi connetterti a ciascuna istanza MySQL e ripetere questo comando. Ad esempio, esegui questo comando per verificare lo stato nella Istanza dbc1-1:

    kubectl -n mysql1 exec -it dbc1-0 -- \
        /bin/bash \
        -c 'mysqlsh --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-1.mysql.mysql1.svc.cluster.local" \
        --js --execute "dba.checkInstanceConfiguration()"'
    

Crea il cluster MySQL InnoDB principale

Successivamente, crea il cluster MySQL InnoDB utilizzando MySQL Admin Comando createCluster. Inizia con l'istanza dbc1-0, che sarà l'istanza principale del cluster, quindi aggiungi altre due repliche al cluster.

Per inizializzare il cluster MySQL InnoDB, segui questi passaggi:

  1. Crea il cluster InnoDB di MySQL.

    var cluster=dba.createCluster('mycluster');
    

    L'esecuzione del comando createCluster attiva le seguenti operazioni:

    • Esegui il deployment dello schema dei metadati.
    • Verifica che la configurazione sia corretta per la replica di gruppo.
    • Registrala come istanza di origine del nuovo cluster.
    • Crea gli account interni necessari, ad esempio l'account utente di replica.
    • Avvia la replica di gruppo.

    Questo comando inizializza un cluster MySQL InnoDB con l'host dbc1-0 come principale. Il riferimento al cluster viene memorizzato nella variabile cluster.

    L'output è simile al seguente:

    A new InnoDB cluster will be created on instance 'dbc1-0.mysql:3306'.
    
    Validating instance configuration at dbc1-0.mysql:3306...
    
    This instance reports its own address as dbc1-0.mysql.mysql1.svc.cluster.local:3306
    
    Instance configuration is suitable.
    NOTE: Group Replication will communicate with other instances using
    'dbc1-0.mysql:33061'. Use the localAddress
    option to override.
    
    Creating InnoDB cluster 'mycluster' on
    'dbc1-0.mysql.mysql1.svc.cluster.local:3306'...
    
    Adding Seed Instance...
    Cluster successfully created. Use Cluster.addInstance() to add MySQL
    instances.
    At least 3 instances are needed for the cluster to be able to withstand
    up to one server failure.
    
  2. Aggiungi la seconda istanza al cluster.

    cluster.addInstance('icadmin@dbc1-1.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
    
  3. Aggiungi l'istanza rimanente al cluster.

    cluster.addInstance('icadmin@dbc1-2.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
    

    L'output è simile al seguente:

    ...
    The instance 'dbc1-2.mysql:3306' was successfully added to the cluster.
    
  4. Verifica lo stato del cluster.

    cluster.status()
    

    Questo comando mostra lo stato del cluster. La topologia è composta da tre host, un'istanza principale e due secondarie. Se vuoi, puoi chiamare cluster.status({extended:1}).

    L'output è simile al seguente:

    {
        "clusterName": "mysql1",
        "defaultReplicaSet": {
            "name": "default",
            "primary": "dbc1-0.mysql:3306",
            "ssl": "REQUIRED",
            "status": "OK",
            "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
            "topology": {
                "dbc1-0.mysql:3306": {
                    "address": "dbc1-0.mysql:3306",
                    "memberRole": "PRIMARY",
                    "mode": "R/W",
                    "readReplicas": {},
                    "replicationLag": null,
                    "role": "HA",
                    "status": "ONLINE",
                    "version": "8.0.28"
                },
                "dbc1-1.mysql:3306": {
                    "address": "dbc1-1.mysql:3306",
                    "memberRole": "SECONDARY",
                    "mode": "R/O",
                    "readReplicas": {},
                    "replicationLag": null,
                    "role": "HA",
                    "status": "ONLINE",
                    "version": "8.0.28"
                },
                "dbc1-2.mysql:3306": {
                    "address": "dbc1-2.mysql:3306",
                    "memberRole": "SECONDARY",
                    "mode": "R/O",
                    "readReplicas": {},
                    "replicationLag": null,
                    "role": "HA",
                    "status": "ONLINE",
                    "version": "8.0.28"
                }
            },
            "topologyMode": "Single-Primary"
        },
        "groupInformationSourceMember": "dbc1-0.mysql:3306"
    }
    

    Se vuoi, puoi chiamare il numero cluster.status({extended:1}) per ottenere ulteriori i dettagli dello stato.

Crea un database di esempio

Per creare un database di esempio:

  1. Creare un database e caricare i dati al suo interno.

    \sql
    create database loanapplication;
    use loanapplication
    CREATE TABLE loan (loan_id INT unsigned AUTO_INCREMENT PRIMARY KEY, firstname VARCHAR(30) NOT NULL, lastname VARCHAR(30) NOT NULL , status VARCHAR(30) );
    
  2. Inserisci dati di esempio nel database. Per inserire i dati, devi essere connesso all'istanza principale del cluster.

    INSERT INTO loan (firstname, lastname, status) VALUES ( 'Fred','Flintstone','pending');
    INSERT INTO loan (firstname, lastname, status) VALUES ( 'Betty','Rubble','approved');
    
  3. Verifica che la tabella contenga le tre righe inserite nel passaggio precedente.

    SELECT * FROM loan;
    

    L'output è simile al seguente:

    +---------+-----------+------------+----------+
    | loan_id | firstname | lastname   | status   |
    +---------+-----------+------------+----------+
    |       1 | Fred      | Flintstone | pending  |
    |       2 | Betty     | Rubble     | approved |
    +---------+-----------+------------+----------+
    2 rows in set (0.0010 sec)
    

Crea un ClusterSet InnoDB MySQL

Puoi creare un ClusterSet MySQL InnoDB per gestire la replica dal cluster principale ai cluster di replica utilizzando un canale di replica ClusterSet dedicato.

Un ClusterSet MySQL InnoDB fornisce tolleranza in caso di emergenza per un cluster MySQL InnoDB di deployment collegando un cluster MySQL InnoDB principale a una o più repliche in località alternative, ad esempio in più zone e regioni.

Se hai chiuso MySQL Shell, crea una nuova shell eseguendo questo comando in un nuovo terminale Cloud Shell:

  kubectl -n mysql1 exec -it dbc1-0 -- \
      /bin/bash -c 'mysqlsh \
      --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-0.mysql.mysql1.svc.cluster.local"'

Per creare un ClusterSet InnoDB MySQL, segui questi passaggi:

  1. Ottieni un oggetto cluster nel terminale MySQL Shell.

    \js
    cluster=dba.getCluster()
    

    L'output è simile al seguente:

    <Cluster:mycluster>
    
  2. Inizializza un ClusterSet InnoDB MySQL con il cluster InnoDB MySQL esistente memorizzato nell'oggetto cluster come principale.

    clusterset=cluster.createClusterSet('clusterset')
    

    L'output è simile al seguente:

    A new ClusterSet will be created based on the Cluster 'mycluster'.
    
    * Validating Cluster 'mycluster' for ClusterSet compliance.
    
    * Creating InnoDB ClusterSet 'clusterset' on 'mycluster'...
    
    * Updating metadata...
    
    ClusterSet successfully created. Use ClusterSet.createReplicaCluster() to add Replica Clusters to it.
    
    <ClusterSet:clusterset>
    
  3. Controlla lo stato del tuo MySQL InnoDB ClusterSet.

    clusterset.status()
    

    L'output è simile al seguente:

    {
        "clusters": {
            "mycluster": {
                "clusterRole": "PRIMARY",
                "globalStatus": "OK",
                "primary": "dbc1-0.mysql:3306"
            }
        },
        "domainName": "clusterset",
        "globalPrimaryInstance": "dbc1-0.mysql:3306",
        "primaryCluster": "mycluster",
        "status": "HEALTHY",
        "statusText": "All Clusters available."
    }
    

    Se vuoi, puoi chiamare il numero clusterset.status({extended:1}) per ottenere ulteriori i dettagli dello stato, incluse informazioni sul cluster.

  4. Esci da MySQL Shell.

    \q
    

Esegui il deployment di un router MySQL

Puoi eseguire il deployment di un router MySQL per indirizzare il traffico dell'applicazione client al cluster appropriati. Il routing si basa sulla porta di connessione dell'applicazione che esegue un'operazione di database:

  • Le scritture vengono instradate all'istanza del cluster principale nel ClusterSet principale.
  • Le letture possono essere instradate a qualsiasi istanza nel cluster principale.

Quando avvii un router MySQL, viene eseguito il bootstrap in base al deployment del cluster MySQL InnoDB. Le istanze MySQL Router collegate al ClusterSet MySQL InnoDB sono a conoscenza di eventuali switchover controllati o failover di emergenza e indirizzano il traffico al nuovo cluster principale.

Per eseguire il deployment di un router MySQL, segui questi passaggi:

  1. Nel terminale Cloud Shell, esegui il deployment di MySQL Router.

    kubectl apply -n mysql1 -f c1-router.yaml
    

    L'output è simile al seguente:

    configmap/mysql-router-config created
    service/mysql-router created
    deployment.apps/mysql-router created
    
  2. Verifica l'idoneità del deployment del router MySQL.

    kubectl -n mysql1 get deployment mysql-router --watch
    

    Quando tutti e tre i pod sono pronti, l'output è simile al seguente:

    NAME           READY   UP-TO-DATE   AVAILABLE   AGE
    mysql-router   3/3     3            0           3m36s
    

    Se viene visualizzato un errore PodUnschedulable nella console, attendi un minuto o due mentre GKE esegue il provisioning di più nodi. Aggiorna e dovresti vedere 3/3 OK.

  3. Avvia MySQL Shell su un membro del cluster esistente.

    kubectl -n mysql1 exec -it dbc1-0 -- \
        /bin/bash -c 'mysqlsh --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-0.mysql"'
    

    Questo comando si connette al pod dbc1-0, quindi avvia una shell connessa l'istanza MySQL dbc1-0.

  4. Verifica la configurazione del router.

    clusterset=dba.getClusterSet()
    clusterset.listRouters()
    

    L'output è simile al seguente:

    {
      "domainName": "clusterset",
      "routers": {
        "mysql-router-7cd8585fbc-74pkm::": {
            "hostname": "mysql-router-7cd8585fbc-74pkm",
            "lastCheckIn": "2022-09-22 23:26:26",
            "roPort": 6447,
            "roXPort": 6449,
            "rwPort": 6446,
            "rwXPort": 6448,
            "targetCluster": null,
            "version": "8.0.27"
        },
        "mysql-router-7cd8585fbc-824d4::": {
          ...
        },
        "mysql-router-7cd8585fbc-v2qxz::": {
          ...
        }
      }
    }
    
  5. Esci dalla shell di MySQL.

    \q
    
  6. Esegui questo script per ispezionare il posizionamento dei pod del router MySQL.

    bash ../scripts/inspect_pod_node.sh mysql1 | sort
    

    Lo script mostra il posizionamento dei nodi e delle zone cloud di tutti i pod nello spazio dei nomi mysql1, dove l'output è simile al seguente:

    gke-gkemulti-west-5-default-pool-1ac6e8b5-0h9v us-west1-c mysql-router-6654f985f5-df97q
    gke-gkemulti-west-5-default-pool-1ac6e8b5-ddjx us-west1-c dbc1-1
    gke-gkemulti-west-5-default-pool-1f5baa66-bf8t us-west1-a dbc1-2
    gke-gkemulti-west-5-default-pool-1f5baa66-kt03 us-west1-a mysql-router-6654f985f5-qlfj9
    gke-gkemulti-west-5-default-pool-4bcaca65-2l6s us-west1-b mysql-router-6654f985f5-5967d
    gke-gkemulti-west-5-default-pool-4bcaca65-jch0 us-west1-b dbc1-0
    

    Puoi osservare che i pod del router MySQL sono distribuiti equamente tra le zone; ovvero non posizionato sullo stesso nodo di un pod MySQL, sullo stesso nodo di un altro pod del router MySQL.

Gestire gli upgrade di GKE e MySQL InnoDB Cluster

Gli aggiornamenti sia per MySQL sia per Kubernetes vengono rilasciati con una cadenza regolare. Segui best practice operative per aggiornare regolarmente l'ambiente software. Di per impostazione predefinita, GKE gestisce per te gli upgrade del cluster e del pool di nodi. Kubernetes e GKE forniscono inoltre funzionalità aggiuntive per facilitare gli upgrade del software MySQL.

Pianifica gli upgrade di GKE

Puoi adottare misure proattive e impostare configurazioni per ridurre i rischi e per facilitare l'upgrade dei cluster quando esegui servizi stateful, tra cui:

  • Cluster standard: segui le best practice di GKE per l'upgrade dei cluster. Scegli una strategia di upgrade appropriata per assicurarti che gli upgrade vengano eseguiti durante il periodo del periodo di manutenzione:

    • Scegli gli upgrade per picchi se l'ottimizzazione dei costi è importante e se i tuoi carichi di lavoro possono tollerare un arresto graduale in meno di 60 minuti.
    • Scegli gli upgrade blu/verde se i tuoi carichi di lavoro tollerano meno le interruzioni e un aumento temporaneo dei costi dovuto a un maggiore utilizzo delle risorse è accettabile.

    Per scoprire di più, consulta Eseguire l'upgrade di un cluster che esegue un carico di lavoro stateful. Viene eseguito l'upgrade automatico dei cluster Autopilot in base al canale di rilascio selezionato.

  • Utilizzare periodi di manutenzione per assicurarti che gli upgrade vengano eseguiti quando vuoi. Prima della finestra di manutenzione, assicurati che i backup del database siano riusciti.

  • Prima di consentire il traffico ai nodi MySQL di cui è stato eseguito l'upgrade, utilizza i probe di idoneità e di attività per assicurarti che siano pronti per il traffico.

  • Crea probe che valutano se la replica è sincronizzata prima di accettare per via del traffico. Questa operazione può essere eseguita tramite script personalizzati, a seconda della complessità e delle dimensioni del database.

Imposta un criterio per il budget di interruzione dei pod (PDB)

Quando un cluster MySQL InnoDB è in esecuzione su GKE, deve essere presente di istanze in esecuzione in qualsiasi momento per soddisfare il requisito del quorum.

In questo tutorial, dato un cluster MySQL di tre istanze, devono essere disponibili due istanze per formare un quorum. Un criterio PodDisruptionBudget ti consente di limitare il numero di pod che possono essere terminati in un determinato momento. Questo è utile sia per le operazioni in stato stazionario dei servizi stateful sia per gli upgrade dei cluster.

Per assicurarti che un numero limitato di pod subiscano interruzioni contemporaneamente, imposti PDB per il carico di lavoro per maxUnavailable: 1. Questo assicura che in qualsiasi momento dell'operazione del servizio, non è in esecuzione più di un pod.

Il seguente manifest del criterio PodDisruptionBudget imposta il numero massimo di istanze non disponibili uno per l'applicazione MySQL.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: mysql-pdb
spec:
  maxUnavailable: 1
  selector:
    matchLabels:
      app: mysql

Per applicare il criterio PDB al cluster:

  1. Applica il criterio PDB utilizzando kubectl.

    kubectl apply -n mysql1 -f mysql-pdb-maxunavailable.yaml
    
  2. Visualizza lo stato del PDB.

    kubectl get poddisruptionbudgets -n mysql1 mysql-pdb -o yaml
    

    Nella sezione status dell'output, controlla currentHealthy e desiredHealthy I pod sono conteggiati. L'output è simile al seguente:

    status:
    ...
      currentHealthy: 3
      desiredHealthy: 2
      disruptionsAllowed: 1
      expectedPods: 3
    ...
    

Pianifica gli upgrade dei file binari di MySQL

Kubernetes e GKE forniscono funzionalità per semplificare gli upgrade del codice binario di MySQL. Tuttavia, devi eseguire alcune operazioni per prepararti agli upgrade.

Tieni presente le seguenti considerazioni prima di iniziare la procedura di upgrade:

  • Gli upgrade devono prima essere eseguiti in un ambiente di test. Per i sistemi di produzione, devi eseguire ulteriori test in un ambiente di pre-produzione.
  • Per alcune release binarie, non puoi eseguire il downgrade della versione dopo aver eseguito un upgrade. Prenditi il tempo necessario per comprendere le implicazioni di un upgrade.
  • Le origini di replica possono essere replicate in una versione più recente. Tuttavia, in genere la copia da una versione più recente a una precedente non è supportata.
  • Assicurati di avere un backup del database completo prima di eseguire il deployment della versione aggiornata.
  • Tieni presente la natura temporanea dei pod Kubernetes. Qualsiasi stato della configurazione archiviato dal pod che non si trova nel volume permanente andrà perso quando È stato eseguito nuovamente il deployment del pod.
  • Per gli upgrade dei binari MySQL, utilizza la stessa strategia di aggiornamento del pool di nodi, la stessa PDB e gli stessi probe descritti in precedenza.

In un ambiente di produzione, devi seguire queste best practice:

  • Crea un'immagine container con la nuova versione di MySQL.
  • Rendere persistenti le istruzioni per la build delle immagini in un repository di controlli del codice sorgente.
  • Utilizzare una pipeline di creazione e test di immagini automatizzate, come Cloud Build, e il file binario in un registro di immagini come Artifact Registry.

Per semplificare questo tutorial, non creerai e renderai un'immagine container. utilizza invece le immagini MySQL pubbliche.

Esegui il deployment del file binario MySQL di cui è stato eseguito l'upgrade

Per eseguire l'upgrade binario MySQL, devi emettere un comando dichiarativo che modifica la versione immagine della risorsa StatefulSet. GKE esegue i passaggi necessari per arrestare il pod corrente, eseguire il deployment di un nuovo pod con il programma binario di cui è stato eseguito l'upgrade e collegare il disco permanente al nuovo pod.

  1. Verifica che il PDB sia stato creato.

    kubectl get poddisruptionbudgets -n mysql1
    
  2. Recupera l'elenco dei set stateful.

    kubectl get statefulsets -n mysql1
    
  3. Recupera l'elenco dei pod in esecuzione utilizzando l'etichetta app.

    kubectl get pods --selector=app=mysql -n mysql1
    
  4. Aggiorna l'immagine MySQL nell'insieme stateful.

    kubectl  -n mysql1 \
        set image statefulset/dbc1 \
        mysql=mysql/mysql-server:8.0.30
    

    L'output è simile al seguente:

    statefulset.apps/mysql image updated
    
  5. Controlla lo stato dei pod in fase di terminazione e dei nuovi pod.

    kubectl get pods --selector=app=mysql -n mysql1
    

Convalida l'upgrade del file binario MySQL

Durante l'upgrade, puoi verificare lo stato dell'implementazione, i nuovi pod e il Servizio esistente.

  1. Conferma l'upgrade eseguendo il comando rollout status.

    kubectl rollout status statefulset/dbc1 -n mysql1
    

    L'output è simile al seguente:

    partitioned roll out complete: 3 new pods have been updated...
    
  2. Conferma la versione dell'immagine ispezionando il set stateful.

    kubectl get statefulsets -o wide -n mysql1
    

    L'output è simile al seguente:

    NAME   READY   AGE   CONTAINERS   IMAGES
    dbc1   3/3     37m   mysql        mysql/mysql-server:8.0.30
    
  3. Controlla lo stato del cluster.

    kubectl -n mysql1 \
         exec -it dbc1-0 -- \
           /bin/bash \
             -c 'mysqlsh \
             --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-1.mysql.mysql1.svc.cluster.local" \
             --js \
             --execute "print(dba.getClusterSet().status({extended:1})); print(\"\\n\")"'
    

    Per ogni istanza del cluster, cerca i valori di stato e versione nell'output. L'output è simile al seguente:

    ...
      "status": "ONLINE",
      "version": "8.0.30"
    ...
    

Esegui il rollback dell'ultima implementazione del deployment dell'app

Quando ripristini il deployment di una versione binaria di cui è stato eseguito l'upgrade, il processo di implementazione viene annullato e viene eseguito il deployment di un nuovo insieme di pod con la versione dell'immagine precedente.

Per ripristinare il deployment alla versione precedente funzionante, utilizza il comando rollout undo:

kubectl rollout undo statefulset/dbc1 -n mysql1

L'output è simile al seguente:

statefulset.apps/dbc1 rolled back

Scala il cluster di database in orizzontale

Per scalare il tuo cluster MySQL InnoDB orizzontalmente, aggiungi altri nodi al Pool di nodi del cluster GKE (richiesto solo se utilizzi Standard), eseguire il deployment di altre istanze MySQL aggiuntive, quindi aggiungere ogni istanza all'istanza esistente Cluster InnoDB MySQL.

Aggiungi nodi al cluster Standard

Questa operazione non è necessaria se utilizzi un cluster Autopilot.

Per aggiungere nodi al cluster standard, segui le istruzioni riportate di seguito per Cloud Shell o la console Google Cloud. Per la procedura dettagliata, consulta Ridimensionare un pool di nodi.

gcloud

In Cloud Shell, ridimensiona il pool di nodi predefinito a otto istanze in ciascuna gruppo di istanze gestite.

gcloud container clusters resize ${CLUSTER_NAME} \
     --node-pool default-pool \
     --num-nodes=8

Console

Per aggiungere nodi al cluster Standard:

  1. Apri la pagina Cluster gkemulti-west1 nella console Google Cloud.
  2. Seleziona Nodi e fai clic su pool predefinito.
  3. Scorri verso il basso fino a Gruppi di istanze.
  4. Per ogni gruppo di istanze, ridimensiona il valore Number of nodes da 5 a 8 nodi.

Aggiungi i pod MySQL al cluster principale

Per eseguire il deployment di pod MySQL aggiuntivi per scalare il cluster orizzontalmente:

  1. In Cloud Shell, aggiorna il numero di repliche nel deployment MySQL da tre repliche a cinque repliche.

    kubectl scale  -n mysql1 --replicas=5 -f c1-mysql.yaml
    
  2. Verifica l'avanzamento del deployment.

    kubectl -n mysql1 get pods --selector=app=mysql -o wide
    

    Per determinare se i pod sono pronti, utilizza il flag --watch per monitorare il deployment. Se utilizzi i cluster Autopilot e visualizzi erroriPod Unschedulable, è possibile che GKE stia eseguendo il provisioning dei nodi per supportare i pod aggiuntivi.

  3. Configura le impostazioni di replica del gruppo per le nuove istanze MySQL da aggiungere nel cluster

    bash ../scripts/c1-clustersetup.sh 3 4
    

    Lo script invia i comandi alle istanze in esecuzione sui pod con ordinali da 3 a 4.

  4. Apri la shell di MySQL.

    kubectl -n mysql1 \
      exec -it dbc1-0 -- \
          /bin/bash \
            -c 'mysqlsh \
            --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-0.mysql"'
    
  5. Configura le due nuove istanze MySQL.

    dba.configureInstance('root:$MYSQL_ROOT_PASSWORD@dbc1-3.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"),clusterAdmin: 'icadmin', clusterAdminPassword: os.getenv("MYSQL_ADMIN_PASSWORD")});
    dba.configureInstance('root:$MYSQL_ROOT_PASSWORD@dbc1-4.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"),clusterAdmin: 'icadmin', clusterAdminPassword: os.getenv("MYSQL_ADMIN_PASSWORD")});
    

    I comandi controllano se l'istanza è configurata correttamente per il cluster MySQL InnoDB all'utilizzo e apportare le modifiche necessarie alla configurazione.

  6. Aggiungi una delle nuove istanze al cluster principale.

    cluster = dba.getCluster()
    cluster.addInstance('icadmin@dbc1-3.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
    
  7. Aggiungi una seconda nuova istanza al cluster principale.

    cluster.addInstance('icadmin@dbc1-4.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
    
  8. Ottieni lo stato ClusterSet, che include anche lo stato Cluster.

    clusterset = dba.getClusterSet()
    clusterset.status({extended: 1})
    

    L'output è simile al seguente:

    "domainName": "clusterset",
    "globalPrimaryInstance": "dbc1-0.mysql:3306",
    "metadataServer": "dbc1-0.mysql:3306",
    "primaryCluster": "mycluster",
    "status": "HEALTHY",
    "statusText": "All Clusters available."
    
  9. Esci dalla shell di MySQL.

    \q
    

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo tutorial, elimina il progetto che contiene le risorse oppure mantieni il progetto ed elimina le singole risorse.

Elimina il progetto

Il modo più semplice per evitare la fatturazione è eliminare il progetto che hai creato per il tutorial.

Delete a Google Cloud project:

gcloud projects delete PROJECT_ID

Passaggi successivi