Déployer un cluster MySQL avec état sur GKE


Ce document est destiné aux administrateurs de bases de données, aux architectes cloud et aux professionnels des opérations qui souhaitent déployer une topologie MySQL à disponibilité élevée sur Google Kubernetes Engine.

Suivez ce tutoriel pour découvrir comment déployer un cluster MySQL InnoDB, un ClusterSet MySQL InnoDB et un middleware MySQL Router sur votre cluster GKE, et comment effectuer des mises à niveau.

Objectifs

Dans ce tutoriel, vous allez apprendre à effectuer les opérations suivantes :

  • Créer et déployer un service Kubernetes avec état
  • Déployer un cluster MySQL InnoDB pour la haute disponibilité
  • Déployer un middleware Router pour le routage des opérations de base de données
  • Déployer un ClusterSet MySQL InnoDB pour la tolérance aux sinistres
  • Simuler un basculement de cluster MySQL
  • Mettre à niveau une version MySQL

Les sections suivantes décrivent l'architecture de la solution que vous allez créer dans ce tutoriel.

Cluster MySQL InnoDB

Dans votre cluster GKE régional, à l'aide d'un StatefulSet, vous déployez une instance de base de données MySQL avec la dénomination et la configuration nécessaires pour créer un cluster MySQL InnoDB. Pour assurer la tolérance aux pannes et la haute disponibilité, vous déployez trois pods d'instance de base de données. Ainsi, la majorité des pods de différentes zones sont disponibles à tout moment pour une élection primaire réussie à l'aide d'un protocole de consensus, et votre cluster InnoDB MySQL est tolérant aux défaillances de zone uniques.

Schéma d'une architecture illustrant la relation entre des applications, MySQL Router et un cluster MySQL
Figure 1 : Exemple d'architecture d'un cluster InnoDB MySQL

Une fois le déploiement effectué, vous désignez un pod comme instance principale pour diffuser les opérations de lecture et d'écriture. Les deux autres pods sont des instances répliquées secondaires en lecture seule. Si l'instance principale subit une défaillance d'infrastructure, vous pouvez promouvoir l'un de ces deux pods d'instances répliquées en instance principale.

Vous déployez trois pods MySQL Router dans un espace de noms distinct pour assurer le routage de connexion afin d'améliorer la résilience. Au lieu de se connecter directement au service de base de données, vos applications se connectent aux pods MySQL Router. Les pods Router connaissent l'état et l'objectif de chaque pod du cluster MySQL InnoDB, et acheminent les opérations d'application vers le pod opérationnel correspondant. L'état de routage est mis en cache dans les pods Router et mis à jour à partir des métadonnées du cluster stockées sur chaque nœud du cluster InnoDB MySQL. En cas de défaillance d'une instance, le routeur ajuste le routage des connexions vers une instance active.

ClusterSet MySQL InnoDB

Vous pouvez créer un ClusterSet MySQL InnoDB à partir d'un cluster MySQL InnoDB initial. Cela vous permet d'augmenter la tolérance aux sinistres si le cluster principal n'est plus disponible.

Le schéma montre comment les clusters MySQL InnoDB principal et d'instances répliquées sont synchronisés via la réplication asynchrone.
Figure 2 : Exemple d'architecture ClusterSet multirégionale contenant un cluster principal et un cluster d'instances répliquées

Si l'instance principale de cluster MySQL InnoDB n'est plus disponible, vous pouvez promouvoir un cluster d'instances répliquées du ClusterSet en instance principale. Lorsque vous utilisez le middleware MySQL Router, votre application n'a pas besoin de suivre l'état de l'instance de base de données principale. Le routage est ajusté pour envoyer des connexions à la nouvelle instance principale une fois l'élection terminée. Cependant, il est de votre responsabilité de vérifier que les applications qui se connectent à votre middleware MySQL Router suivent les bonnes pratiques de résilience, afin que de nouvelles tentatives d'exécution des connexions soient effectuées en cas d'erreur lors du basculement du cluster.

Coûts

Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Une fois que vous avez terminé les tâches décrites dans ce document, vous pouvez éviter de continuer à payer des frais en supprimant les ressources que vous avez créées. Pour en savoir plus, consultez la section Effectuer un nettoyage.

Avant de commencer

Configurer votre projet

  1. Connectez-vous à votre compte Google Cloud. Si vous débutez sur Google Cloud, créez un compte pour évaluer les performances de nos produits en conditions réelles. Les nouveaux clients bénéficient également de 300 $ de crédits gratuits pour exécuter, tester et déployer des charges de travail.
  2. Dans Google Cloud Console, sur la page de sélection du projet, cliquez sur Créer un projet pour commencer à créer un projet Google Cloud.

    Accéder au sélecteur de projet

  3. Vérifiez que la facturation est activée pour votre projet Google Cloud.

  4. Activez l'API GKE

    Activer l'API

  5. Dans Google Cloud Console, sur la page de sélection du projet, cliquez sur Créer un projet pour commencer à créer un projet Google Cloud.

    Accéder au sélecteur de projet

  6. Vérifiez que la facturation est activée pour votre projet Google Cloud.

  7. Activez l'API GKE

    Activer l'API

Configurer les rôles

  1. Attribuez des rôles à votre compte Google. Exécutez la commande suivante une fois pour chacun des rôles IAM suivants : 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:EMAIL_ADDRESS" --role=ROLE
    • en remplaçant PROJECT_ID par l'ID de votre projet :
    • Remplacez EMAIL_ADDRESS par votre adresse e-mail.
    • Remplacez ROLE par chaque rôle individuel.

Configurer votre environnement

Dans ce tutoriel, vous utilisez Cloud Shell pour gérer les ressources hébergées sur Google Cloud. Cloud Shell est préinstallé avec Docker et les CLI kubectl et gcloud.

Pour utiliser Cloud Shell afin de configurer votre environnement, procédez comme suit :

  1. Définir des variables d'environnement.

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

    Remplacez les valeurs suivantes :

    • PROJECT_ID : ID de votre projet Google Cloud.
    • COMPUTE_REGION : votre région Compute Engine. Pour ce tutoriel, la région est us-west1. En règle générale, vous définissez une région proche de vous.
  2. Définissez les variables d'environnement par défaut.

     gcloud config set project PROJECT_ID
     gcloud config set compute/region COMPUTE_REGION
    
  3. Clonez le dépôt de code.

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  4. Accédez au répertoire de travail.

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

Créer un cluster GKE

Dans cette section, vous allez créer un cluster GKE régional. Contrairement à un cluster zonal, le plan de contrôle d'un cluster régional est répliqué dans plusieurs zones. Par conséquent, l'interruption d'une zone unique n'entraîne pas l'indisponibilité du plan de contrôle.

Pour créer un cluster GKE, procédez comme suit :

Autopilot

  1. Dans Cloud Shell, créez un cluster GKE Autopilot dans la région us-west1.

    gcloud container clusters create-auto $CLUSTER_NAME \
        --region=$REGION
    
  2. Obtenez les identifiants du cluster GKE.

    gcloud container clusters get-credentials $CLUSTER_NAME \
      --region=$REGION
    
  3. Déployez un service dans trois zones.

    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
    

    Par défaut, Autopilot provisionne des ressources dans deux zones. Le déploiement défini dans prepare-for-ha.yaml garantit que Autopilot provisionne les nœuds dans trois zones de votre cluster en définissant replicas:3, podAntiAffinity avec requiredDuringSchedulingIgnoredDuringExecution, et topologyKey: "topology.kubernetes.io/zone".

  4. Vérifiez l'état du déploiement.

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

    Lorsque trois pods sont prêts, annulez cette commande avec CTRL+C. Le résultat ressemble à ce qui suit :

    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. Exécutez ce script pour vérifier que vos pods ont été déployés dans trois zones.

    bash ../scripts/inspect_pod_node.sh default
    

    Chaque ligne de la sortie correspond à un pod et la deuxième colonne indique la zone cloud. Le résultat ressemble à ce qui suit :

    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. Dans Cloud Shell, créez un cluster GKE standard dans la région us-west1.

    gcloud container clusters create $CLUSTER_NAME \
      --region=$REGION \
      --machine-type="e2-standard-2" \
      --disk-type="pd-standard" \
      --num-nodes="5"
    
  2. Obtenez les identifiants du cluster GKE.

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

Déployer des StatefulSets MySQL

Dans cette section, vous allez déployer un StatefulSet MySQL. Chaque StatefulSet est constitué de trois instances répliquées MySQL.

Pour déployer le StatefulSet MySQL, procédez comme suit :

  1. Créez un espace de noms pour le StatefulSet.

    kubectl create namespace mysql1
    
  2. Créez le 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
    

    Le mot de passe est déployé avec chaque pod. Il permet aux scripts et aux commandes de gestion de déployer le cluster et le ClusterSet MySQL InnoDB dans ce tutoriel.

  3. Créez la ressource 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
    

    Cette classe de stockage utilise le type de disque persistant pd-balanced qui équilibre les performances et les coûts. Le champ volumeBindingMode est défini sur WaitForFirstConsumer, ce qui signifie que GKE retarde le provisionnement d'un PersistentVolume jusqu'à ce que le pod soit créé. Ce paramètre garantit que le disque est provisionné dans la zone dans laquelle le pod est programmé.

  4. Déployez le StatefulSet des pods d'instances 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
    

    Cette commande déploie le StatefulSet composé de trois instances répliquées. Dans ce tutoriel, le cluster MySQL principal est déployé dans trois zones de la région us-west1. Le résultat ressemble à ce qui suit :

    service/mysql created
    statefulset.apps/dbc1 created
    

    Dans ce tutoriel, les limites et demandes de ressources sont définies sur des valeurs minimales afin de réduire les coûts. Lorsque vous planifiez une charge de travail de production, assurez-vous de définir ces valeurs de manière appropriée pour les besoins de votre organisation.

  5. Vérifiez que le StatefulSet a bien été créé.

    kubectl get statefulset -n mysql1 --watch
    

    La préparation du StatefulSet peut prendre environ 10 minutes.

  6. Lorsque les trois pods sont prêts, quittez la commande à l'aide de Ctrl+C. Si vous voyez des erreurs PodUnscheduleable en raison d'un processeur ou d'une mémoire insuffisants, attendez quelques minutes que le plan de contrôle procède au redimensionnement pour s'adapter à la charge de travail volumineuse.

    Le résultat ressemble à ce qui suit :

    NAME   READY   AGE
    dbc1   1/3     39s
    dbc1   2/3     50s
    dbc1   3/3     73s
    
  7. Pour inspecter l'emplacement des pods sur les nœuds du cluster GKE, exécutez le script suivant :

    bash ../scripts/inspect_pod_node.sh mysql1 mysql
    

    La sortie affiche le nom du pod, le nom du nœud GKE et la zone dans laquelle le nœud est provisionné. Elle ressemble à ce qui suit :

    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
    

    Les colonnes de la sortie représentent respectivement le nom d'hôte, la zone cloud et le nom du pod.

    La règle topologySpreadConstraints de la spécification StatefulSet (c1-mysql.yaml) indique au programmeur de placer les pods uniformément dans le domaine de défaillance (topology.kubernetes.io/zone).

    La règle podAntiAffinity applique la contrainte selon laquelle les pods ne doivent pas être placés sur le même nœud du cluster GKE (kubernetes.io/hostname). Pour les pods des instances MySQL, cette règle entraîne le déploiement uniforme des pods dans les trois zones de la région Google Cloud. Cet emplacement permet d'assurer la haute disponibilité du cluster MySQL InnoDB en plaçant chaque instance de base de données dans un domaine de défaillance distinct.

Préparer le cluster MySQL InnoDB principal

Pour configurer un cluster InnoDB MySQL, procédez comme suit :

  1. Dans le terminal Cloud Shell, définissez les configurations de réplication de groupes pour les instances MySQL à ajouter à votre 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

    Le script se connectera à distance à chacune des trois instances MySQL pour définir et conserver les variables d'environnement suivantes :

    • group_replication_ip_allowlist : permet à l'instance du cluster de se connecter à n'importe quelle instance du groupe.
    • binlog_transaction_dependency_tracking='WRITESET' : autorise les transactions en parallèle qui n'entrent pas en conflit.

    Dans les versions de MySQL antérieures à la version 8.0.22, utilisez group_replication_ip_whitelist au lieu de group_replication_ip_allowlist.

  2. Ouvrez un deuxième terminal, de sorte que vous n'ayez pas à créer un shell pour chaque pod.

  3. Connectez-vous à MySQL Shell sur le 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. Vérifiez la liste d'autorisation de réplication de groupes MySQL pour vous connecter à d'autres instances.

    \sql SELECT @@group_replication_ip_allowlist;
    

    Le résultat ressemble à ce qui suit :

    +----------------------------------+
    | @@group_replication_ip_allowlist |
    +----------------------------------+
    | mysql.mysql1.svc.cluster.local   |
    +----------------------------------+
    
  5. Vérifiez que l'identifiant server-id est unique sur chacune des instances.

    \sql SELECT @@server_id;
    

    Le résultat ressemble à ce qui suit :

    +-------------+
    | @@server_id |
    +-------------+
    |          21 |
    +-------------+
    
  6. Configurez les instances de sorte qu'elles utilisent le cluster InnoDB MySQL et créez un compte administrateur sur chaque instance.

    \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")});
    

    Toutes les instances doivent avoir le même nom d'utilisateur et le même mot de passe pour que le cluster MySQL InnoDB fonctionne correctement. Chaque commande génère une sortie semblable à celle-ci :

    ...
    
    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. Vérifiez que l'instance est prête à être utilisée dans un cluster MySQL InnoDB.

    dba.checkInstanceConfiguration()
    

    Le résultat ressemble à ce qui suit :

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

    Vous pouvez éventuellement vous connecter à chaque instance MySQL et répéter cette commande. Par exemple, exécutez la commande suivante pour vérifier l'état sur l'instance 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()"'
    

Créer le cluster MySQL InnoDB principal

Créez ensuite le cluster MySQL InnoDB à l'aide de la commande d'administration MySQL createCluster. Commencez avec l'instance dbc1-0, qui sera l'instance principale du cluster, puis ajoutez deux autres instances répliquées au cluster.

Pour initialiser le cluster MySQL InnoDB, procédez comme suit :

  1. Créez le cluster MySQL InnoDB.

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

    L'exécution de la commande createCluster déclenche les opérations suivantes :

    • Déploiement du schéma de métadonnées
    • Vérification que la configuration de la réplication de groupe est correcte
    • Enregistrement de la configuration en tant qu'instance source du nouveau cluster
    • Création des comptes internes nécessaires, tels que le compte utilisateur de réplication
    • Démarrage de la réplication de groupe

    Cette commande initialise un cluster MySQL InnoDB avec l'hôte dbc1-0 en tant qu'instance principale. La référence du cluster est stockée dans la variable du cluster.

    La sortie ressemble à ceci :

    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. Ajoutez la deuxième instance au cluster.

    cluster.addInstance('icadmin@dbc1-1.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
    
  3. Ajoutez l'instance restante au cluster.

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

    Le résultat ressemble à ce qui suit :

    ...
    The instance 'dbc1-2.mysql:3306' was successfully added to the cluster.
    
  4. Vérifiez l'état du cluster.

    cluster.status()
    

    Cette commande affiche l'état du cluster. La topologie se compose de trois hôtes, une instance principale et deux secondaires. Vous pouvez éventuellement appeler cluster.status({extended:1}).

    Le résultat ressemble à ce qui suit :

    {
        "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"
    }
    

    Vous pouvez éventuellement appeler cluster.status({extended:1}) pour obtenir d'autres détails sur l'état.

Créer un exemple de base de données

Pour créer un exemple de base de données, procédez comme suit :

  1. Créez une base de données et chargez des données dans la base de données.

    \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. Insérez des exemples de données dans la base de données. Pour insérer des données, vous devez être connecté à l'instance principale du cluster.

    INSERT INTO loan (firstname, lastname, status) VALUES ( 'Fred','Flintstone','pending');
    INSERT INTO loan (firstname, lastname, status) VALUES ( 'Betty','Rubble','approved');
    
  3. Vérifiez que la table contient les trois lignes insérées à l'étape précédente.

    SELECT * FROM loan;
    

    Le résultat ressemble à ce qui suit :

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

Créer un ClusterSet MySQL InnoDB

Vous pouvez créer un ClusterSet MySQL InnoDB pour gérer la réplication de votre cluster principal vers des clusters d'instances répliquées à l'aide d'un canal de réplication ClusterSet dédié.

Un ClusterSet MySQL InnoDB assure la tolérance aux sinistres pour les déploiements de clusters MySQL InnoDB en associant un cluster MySQL InnoDB principal à une ou plusieurs instances répliquées situées dans d'autres emplacements, tels que plusieurs zones et plusieurs régions.

Si vous avez fermé MySQL Shell, créez un shell en exécutant la commande suivante dans un nouveau terminal 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"'

Pour créer un ClusterSet MySQL InnoDB, procédez comme suit :

  1. Dans votre terminal MySQL Shell, obtenez un objet de cluster.

    \js
    cluster=dba.getCluster()
    

    Le résultat ressemble à ce qui suit :

    <Cluster:mycluster>
    
  2. Initialisez un ClusterSet MySQL InnoDB avec le cluster MySQL InnoDB existant stocké dans l'objet de cluster en tant qu'instance principale.

    clusterset=cluster.createClusterSet('clusterset')
    

    Le résultat ressemble à ce qui suit :

    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. Vérifiez l'état de votre ClusterSet MySQL InnoDB.

    clusterset.status()
    

    Le résultat ressemble à ce qui suit :

    {
        "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."
    }
    

    Vous pouvez éventuellement appeler clusterset.status({extended:1}) pour obtenir d'autres détails sur l'état, y compris des informations sur le cluster.

  4. Quittez MySQL Shell.

    \q
    

Déployer un routeur MySQL

Vous pouvez déployer un routeur MySQL pour diriger le trafic des applications clientes vers les clusters appropriés. Le routage est basé sur le port de connexion de l'application qui émet une opération de base de données :

  • Les écritures sont acheminées vers l'instance de cluster principale du ClusterSet principal.
  • Les lectures peuvent être acheminées vers n'importe quelle instance du cluster principal.

Lorsque vous démarrez un routeur MySQL, il est amorcé lors du déploiement du ClusterSet MySQL InnoDB. Les instances MySQL Router connectées au ClusterSet MySQL InnoDB sont informées des commutations contrôlées ou des basculements d'urgence, et dirigent le trafic vers le nouveau cluster principal.

Pour déployer un routeur MySQL, procédez comme suit :

  1. Dans le terminal Cloud Shell, déployez le routeur MySQL.

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

    Le résultat ressemble à ce qui suit :

    configmap/mysql-router-config created
    service/mysql-router created
    deployment.apps/mysql-router created
    
  2. Vérifiez la disponibilité du déploiement MySQL Router.

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

    Lorsque les trois pods sont prêts, la sortie ressemble à ce qui suit :

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

    Si une erreur PodUnschedulable s'affiche dans la console, patientez une à deux minutes pendant que GKE provisionne davantage de nœuds. Actualisez la page. 3/3 OK devrait s'afficher.

  3. Démarrez MySQL Shell sur n'importe quel membre du cluster existant.

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

    Cette commande se connecte au pod dbc1-0, puis démarre un shell connecté à l'instance MySQL dbc1-0.

  4. Vérifiez la configuration du routeur.

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

    Le résultat ressemble à ce qui suit :

    {
      "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. Quittez MySQL Shell.

    \q
    
  6. Exécutez ce script pour inspecter l'emplacement des pods MySQL Router.

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

    Le script montre l'emplacement des nœuds et de la zone cloud de tous les pods de l'espace de noms mysql1. Sa sortie est semblable à la suivante :

    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
    

    Vous pouvez observer que les pods MySQL Router sont répartis équitablement entre les zones. Autrement dit, ils ne sont pas placés sur le même nœud qu'un pod MySQL ou sur le même nœud qu'un autre pod MySQL Router.

Gérer les mises à niveau des clusters MySQL InnoDB et GKE

Les mises à jour de MySQL et de Kubernetes sont publiées régulièrement. Suivez les bonnes pratiques opérationnelles pour mettre régulièrement à jour votre environnement logiciel. Par défaut, GKE gère automatiquement les mises à niveau des clusters et des pools de nœuds. Kubernetes et GKE fournissent également des fonctionnalités supplémentaires pour faciliter les mises à niveau logicielles MySQL.

Planifier les mises à niveau de GKE

Vous pouvez prendre des mesures proactives et définir des configurations pour limiter les risques et faciliter la mise à niveau des clusters lorsque vous exécutez des services avec état, y compris les suivants :

  • Clusters standards : suivez les bonnes pratiques de GKE pour mettre à niveau les clusters. Choisissez une stratégie de mise à niveau appropriée pour vous assurer que les mises à niveau se produisent pendant l'intervalle de maintenance :

    • Choisissez les mises à niveau de la surutilisation si l'optimisation des coûts est importante et si vos charges de travail peuvent tolérer un arrêt progressif en moins de 60 minutes.
    • Choisissez des mises à niveau bleu-vert si les charges de travail sont moins tolérantes aux perturbations, et qu'une augmentation temporaire des coûts due à une utilisation plus élevée des ressources est acceptable.

    Pour en savoir plus, consultez la page Mettre à niveau un cluster exécutant une charge de travail avec état. Les clusters Autopilot sont automatiquement mis à niveau en fonction de la version disponible sélectionnée.

  • Utilisez des intervalles de maintenance pour vous assurer que les mises à niveau se produisent lorsque vous le souhaitez. Avant l'intervalle de maintenance, assurez-vous que les sauvegardes de votre base de données ont abouti.

  • Avant d'autoriser le trafic vers les nœuds MySQL mis à niveau, utilisez les vérifications d'aptitude et d'activité pour vous assurer qu'ils sont prêts à recevoir le trafic.

  • Créez des vérifications qui évaluent si la réplication est synchronisée avant d'accepter le trafic. Cette opération peut être effectuée via des scripts personnalisés, en fonction de la complexité et de l'échelle de votre base de données.

Définir une règle de budget d'interruption de pod (PBD)

Lorsqu'un cluster MySQL InnoDB est exécuté sur GKE, il doit y avoir un nombre suffisant d'instances en cours d'exécution à tout moment pour répondre au quorum requis.

Dans ce tutoriel, en tenant compte d'un cluster MySQL composé de trois instances, deux instances doivent être disponibles pour former un quorum. Une règle PodDisruptionBudget vous permet de limiter le nombre de pods pouvant être arrêtés à tout moment. Elle est utile à la fois pour les opérations à état stable de vos services avec état et pour les mises à niveau de clusters.

Pour vous assurer qu'un nombre limité de pods sont interrompus simultanément, vous devez définir le budget d'interruption de pod de votre charge de travail sur maxUnavailable: 1. Cela garantit qu'un pod au maximum ne s'exécute pas dans l'opération du service à tout moment.

Le fichier manifeste suivant de la règle PodDisruptionBudget définit le nombre maximal de pods indisponibles sur un pour votre application MySQL.

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

Pour appliquer la règle PDB à votre cluster, procédez comme suit :

  1. Appliquez la règle PDB à l'aide de kubectl.

    kubectl apply -n mysql1 -f mysql-pdb-maxunavailable.yaml
    
  2. Affichez l'état du PDB.

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

    Dans la section status de la sortie, consultez le nombre de pods currentHealthy et desiredHealthy. Le résultat ressemble à ce qui suit :

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

Planifier les mises à niveau du binaire MySQL

Kubernetes et GKE fournissent des fonctionnalités qui facilitent les mises à niveau du binaire MySQL. Toutefois, vous devez effectuer certaines opérations pour préparer les mises à niveau.

Tenez compte des points suivants avant de commencer le processus de mise à niveau :

  • Les mises à niveau doivent d'abord être effectuées dans un environnement de test. Pour les systèmes de production, vous devez effectuer des tests supplémentaires dans un environnement de préproduction.
  • Pour certaines versions du binaire, vous ne pouvez pas revenir à une version antérieure une fois la mise à niveau effectuée. Prenez le temps nécessaire pour bien comprendre les implications d'une mise à niveau.
  • Les sources de réplication peuvent être répliquées sur une version plus récente. Toutefois, il n'est généralement pas possible d'effectuer une copie d'une version plus récente vers une version plus ancienne.
  • Vérifiez que vous disposez d'une sauvegarde complète de la base de données avant de déployer la version mise à niveau.
  • Tenez compte de la nature éphémère des pods Kubernetes. Tout état de configuration stocké par le pod qui ne se trouve pas sur le volume persistant sera perdu lors du redéploiement du pod.
  • Pour les mises à niveau du binaire MySQL, utilisez le même PDB, la même stratégie de mise à jour du pool de nœuds et les mêmes vérifications que celles décrites précédemment.

Dans un environnement de production, vous devez suivre les bonnes pratiques suivantes :

  • Créez une image de conteneur avec la nouvelle version de MySQL.
  • Conservez les instructions de création d'images dans un dépôt de contrôle de source.
  • Utilisez un pipeline automatisé de compilation et de test d'images, tel que Cloud Build, et stockez le binaire d'images dans un registre d'images tel qu'Artifact Registry.

Pour simplifier ce tutoriel, vous n'allez pas créer ni conserver d'image de conteneur, mais utiliser les images publiques MySQL.

Déployer le binaire MySQL mis à niveau

Pour effectuer la mise à niveau du binaire MySQL, vous devez exécuter une commande déclarative qui modifie la version de l'image de la ressource StatefulSet. GKE effectue les étapes nécessaires pour arrêter le pod actuel, déployer un nouveau pod avec le binaire mis à niveau et associer le disque persistant au nouveau pod.

  1. Vérifiez que le budget d'interruption de pod a été créé.

    kubectl get poddisruptionbudgets -n mysql1
    
  2. Obtenez la liste des ensembles avec état.

    kubectl get statefulsets -n mysql1
    
  3. Obtenez la liste des pods en cours d'exécution à l'aide de l'étiquette app.

    kubectl get pods --selector=app=mysql -n mysql1
    
  4. Mettez à jour l'image MySQL dans l'ensemble avec état.

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

    Le résultat ressemble à ce qui suit :

    statefulset.apps/mysql image updated
    
  5. Vérifiez l'état des pods arrêtés et des nouveaux pods.

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

Valider la mise à niveau du binaire MySQL

Pendant la mise à niveau, vous pouvez vérifier l'état du déploiement, des nouveaux pods et du service existant.

  1. Confirmez la mise à niveau en exécutant la commande rollout status.

    kubectl rollout status statefulset/dbc1 -n mysql1
    

    Le résultat ressemble à ce qui suit :

    partitioned roll out complete: 3 new pods have been updated...
    
  2. Confirmez la version de l'image en inspectant l'ensemble avec état.

    kubectl get statefulsets -o wide -n mysql1
    

    Le résultat ressemble à ce qui suit :

    NAME   READY   AGE   CONTAINERS   IMAGES
    dbc1   3/3     37m   mysql        mysql/mysql-server:8.0.30
    
  3. Vérifiez l'état du 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\")"'
    

    Pour chaque instance de cluster, recherchez les valeurs d'état et de version dans la sortie. Le résultat ressemble à ce qui suit :

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

Effectuer un rollback pour le dernier déploiement de l'application

Lorsque vous annulez le déploiement d'une version binaire mise à niveau, le processus de déploiement est annulé et un nouvel ensemble de pods est déployé avec la version d'image précédente.

Pour rétablir le déploiement à la version de travail précédente, exécutez la commande rollout undo :

kubectl rollout undo statefulset/dbc1 -n mysql1

Le résultat ressemble à ce qui suit :

statefulset.apps/dbc1 rolled back

Procéder au scaling horizontal de votre cluster de base de données

Pour procéder au scaling horizontal de votre cluster MySQL InnoDB, vous devez ajouter des nœuds supplémentaires au pool de nœuds du cluster GKE (uniquement si vous utilisez un cluster standard), déployer d'autres instances MySQL, puis ajouter chaque instance au cluster MySQL InnoDB existant.

Ajouter des nœuds à votre cluster standard

Cette opération n'est pas nécessaire si vous utilisez un cluster Autopilot.

Pour ajouter des nœuds à votre cluster standard, suivez les instructions ci-dessous pour Cloud Shell ou la console Google Cloud. Pour connaître la procédure détaillée, consultez la section Redimensionner un pool de nœuds.

gcloud

Dans Cloud Shell, redimensionnez le pool de nœuds par défaut en définissant huit instances dans chaque groupe d'instances géré.

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

Console

Pour ajouter des nœuds à votre cluster standard, procédez comme suit :

  1. Ouvrez la page du cluster gkemulti-west1 dans la console Google Cloud.
  2. Sélectionnez Nœuds, puis cliquez sur le pool par défaut.
  3. Faites défiler la page jusqu'à Groupes d'instances.
  4. Pour chaque groupe d'instances, redimensionnez la valeur Number of nodes de 5 à 8 nœuds.

Ajouter des pods MySQL au cluster principal

Pour déployer des pods MySQL supplémentaires afin d'effectuer le scaling horizontal de votre cluster, procédez comme suit :

  1. Dans Cloud Shell, passez le nombre d'instances répliquées du déploiement MySQL de trois à cinq.

    kubectl scale  -n mysql1 --replicas=5 -f c1-mysql.yaml
    
  2. Vérifiez l'avancement du déploiement.

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

    Pour déterminer si les pods sont prêts, utilisez l'option --watch pour surveiller le déploiement. Si vous utilisez des clusters Autopilot et que des erreurs Pod Unschedulable s'affichent, cela peut indiquer que GKE provisionne des nœuds pour accueillir les pods supplémentaires.

  3. Configurez les paramètres de réplication de groupe pour les nouvelles instances MySQL à ajouter au cluster.

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

    Le script envoie les commandes aux instances exécutées sur les pods avec les ordinaux 3 à 4.

  4. Ouvrez MySQL Shell.

    kubectl -n mysql1 \
      exec -it dbc1-0 -- \
          /bin/bash \
            -c 'mysqlsh \
            --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-0.mysql"'
    
  5. Configurez les deux nouvelles instances 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")});
    

    Les commandes vérifient si l'instance est correctement configurée pour utiliser le cluster MySQL InnoDB et effectuent les modifications de configuration nécessaires.

  6. Ajoutez l'une des nouvelles instances au cluster principal.

    cluster = dba.getCluster()
    cluster.addInstance('icadmin@dbc1-3.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
    
  7. Ajoutez une deuxième instance au cluster principal.

    cluster.addInstance('icadmin@dbc1-4.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
    
  8. Obtenez l'état du ClusterSet, qui inclut également l'état du cluster.

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

    Le résultat ressemble à ce qui suit :

    "domainName": "clusterset",
    "globalPrimaryInstance": "dbc1-0.mysql:3306",
    "metadataServer": "dbc1-0.mysql:3306",
    "primaryCluster": "mycluster",
    "status": "HEALTHY",
    "statusText": "All Clusters available."
    
  9. Quittez MySQL Shell.

    \q
    

Effectuer un nettoyage

Pour éviter que les ressources utilisées lors de ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et supprimez les ressources individuelles.

Supprimer le projet

Le moyen le plus simple d'empêcher la facturation est de supprimer le projet que vous avez créé pour ce tutoriel.

Supprimez un projet Google Cloud :

gcloud projects delete PROJECT_ID

Étapes suivantes