Implemente um cluster MySQL com estado no GKE

Este documento destina-se a administradores de bases de dados, arquitetos da nuvem e profissionais de operações interessados na implementação de uma topologia do MySQL de elevada disponibilidade no Google Kubernetes Engine.

Siga este tutorial para saber como implementar um MySQL InnoDB Cluster e um MySQL InnoDB ClusterSet, além do middleware MySQL Router no seu cluster do GKE e como fazer atualizações.

Objetivos

Neste tutorial, vai aprender a:

  • Crie e implemente um serviço Kubernetes com estado.
  • Implemente um cluster InnoDB do MySQL para alta disponibilidade.
  • Implemente o middleware do router para o encaminhamento de operações da base de dados.
  • Implemente um ClusterSet do MySQL InnoDB para tolerância a desastres.
  • Simule uma comutação por falha de um cluster MySQL.
  • Faça uma atualização da versão do MySQL.

As secções seguintes descrevem a arquitetura da solução que vai criar neste tutorial.

Cluster InnoDB do MySQL

No cluster do GKE regional, usando um StatefulSet, implementa uma instância da base de dados MySQL com a nomenclatura e a configuração necessárias para criar um cluster MySQL InnoDB. Para oferecer tolerância a falhas e alta disponibilidade, implementa três pods de instâncias de base de dados. Isto garante que a maioria dos pods em diferentes zonas está disponível em qualquer altura para uma eleição primária bem-sucedida através de um protocolo de consenso e torna o seu MySQL InnoDB Cluster tolerante a falhas zonais únicas.

Diagrama de arquitetura que mostra a relação entre as aplicações, o MySQL Router e o MySQL Cluster
Figura 1: exemplo de arquitetura de um único cluster InnoDB do MySQL

Após a implementação, designa um Pod como a instância principal para publicar operações de leitura e escrita. Os outros dois pods são réplicas secundárias só de leitura. Se a instância principal tiver uma falha de infraestrutura, pode promover um destes dois pods de réplica para se tornar o principal.

Num espaço de nomes separado, implementa três pods do MySQL Router para fornecer encaminhamento de ligações para uma resiliência melhorada. Em vez de se ligarem diretamente ao serviço de base de dados, as suas aplicações ligam-se aos pods do MySQL Router. Cada Router Pod tem conhecimento do estado e da finalidade de cada MySQL InnoDB Cluster Pod e encaminha as operações da aplicação para o Pod em bom estado de funcionamento respetivo. O estado de encaminhamento é colocado em cache nos pods do router e atualizado a partir dos metadados do cluster armazenados em cada nó do cluster MySQL InnoDB. No caso de uma falha da instância, o Router ajusta o encaminhamento da ligação para uma instância ativa.

MySQL InnoDB ClusterSet

Pode criar um MySQL InnoDB ClusterSet a partir de um MySQL InnoDB Cluster inicial. Isto permite-lhe aumentar a tolerância a desastres se o cluster principal deixar de estar disponível.

O diagrama mostra como os clusters MySQL InnoDB primários e de réplica são mantidos sincronizados através da replicação assíncrona.
Figura 2: exemplo de arquitetura de ClusterSet multirregião que contém um cluster principal e um cluster de réplica

Se a instância principal do MySQL InnoDB Cluster já não estiver disponível, pode promover uma réplica do cluster no ClusterSet para principal. Quando usa o middleware do MySQL Router, a sua aplicação não precisa de monitorizar o estado da instância da base de dados principal. O encaminhamento é ajustado para enviar ligações para o novo principal após a eleição. No entanto, é da sua responsabilidade garantir que as aplicações que se ligam ao middleware do MySQL Router seguem as práticas recomendadas para a resiliência, de modo que as ligações sejam repetidas se ocorrer um erro durante a comutação por falha do cluster.

Custos

Neste documento, usa os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custos com base na sua utilização prevista, use a calculadora de preços.

Os novos Google Cloud utilizadores podem ser elegíveis para uma avaliação sem custo financeiro.

Quando terminar as tarefas descritas neste documento, pode evitar a faturação contínua eliminando os recursos que criou. Para mais informações, consulte o artigo Limpe.

Antes de começar

Configure o seu projeto

  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.

    Roles required to create a project

    To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. Enable the GKE API.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the API

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

    Roles required to create a project

    To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  6. Verify that billing is enabled for your Google Cloud project.

  7. Enable the GKE API.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the API

  8. Configure funções

    1. Make sure that you have the following role or roles on the project: role/storage.objectViewer, role/logging.logWriter, role/artifactregistry.Admin, roles/container.clusterAdmin, role/container.serviceAgent, roles/serviceusage.serviceUsageAdmin, roles/iam.serviceAccountAdmin

      Check for the roles

      1. In the Google Cloud console, go to the IAM page.

        Go to IAM
      2. Select the project.
      3. In the Principal column, find all rows that identify you or a group that you're included in. To learn which groups you're included in, contact your administrator.

      4. For all rows that specify or include you, check the Role column to see whether the list of roles includes the required roles.

      Grant the roles

      1. In the Google Cloud console, go to the IAM page.

        Aceder ao IAM
      2. Selecione o projeto.
      3. Clique em Conceder acesso.
      4. No campo Novos responsáveis, introduza o identificador do utilizador. Normalmente, este é o endereço de email de uma Conta Google.

      5. Clique em Selecionar uma função e, de seguida, pesquise a função.
      6. Para conceder funções adicionais, clique em Adicionar outra função e adicione cada função adicional.
      7. Clique em Guardar.

      Configure o seu ambiente

      Neste tutorial, vai usar o Cloud Shell para gerir recursos alojados no Google Cloud. O Cloud Shell é pré-instalado com o Docker e a CLI kubectl e gcloud.

      Para usar o Cloud Shell para configurar o seu ambiente:

      1. Defina variáveis de ambiente.

        export PROJECT_ID=PROJECT_ID
        export CLUSTER_NAME=gkemulti-west
        export CONTROL_PLANE_LOCATION=CONTROL_PLANE_LOCATION
        

        Substitua os seguintes valores:

        • PROJECT_ID: o seu Google Cloud ID do projeto.
        • CONTROL_PLANE_LOCATION: a região do Compute Engine do plano de controlo do seu cluster. Para este tutorial, a região é us-west1. Normalmente, recomendamos uma região perto de si.
      2. Defina as variáveis de ambiente predefinidas.

         gcloud config set project PROJECT_ID
         gcloud config set compute/region CONTROL_PLANE_LOCATION
        
      3. Clone o repositório de código.

        git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
        
      4. Mude para o diretório de trabalho.

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

        Crie um cluster do GKE

        Nesta secção, cria um cluster do GKE regional. Ao contrário de um cluster zonal, o plano de controlo de um cluster regional é replicado em várias zonas, pelo que uma indisponibilidade numa única zona não torna o plano de controlo indisponível.

        Para criar um cluster do GKE, siga estes passos:

        Piloto automático

        1. No Cloud Shell, crie um cluster do GKE Autopilot na região us-west1.

          gcloud container clusters create-auto $CLUSTER_NAME \
              --location=$CONTROL_PLANE_LOCATION
          
        2. Obtenha as credenciais do cluster do GKE.

          gcloud container clusters get-credentials $CLUSTER_NAME \
            --location=$CONTROL_PLANE_LOCATION
          
        3. Implemente um serviço em três zonas. Este tutorial usa uma implementação do Kubernetes. Uma implementação é um objeto da API Kubernetes que lhe permite executar várias réplicas de pods distribuídos entre os nós num cluster.

          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
          

          Por predefinição, o Autopilot aprovisiona recursos em duas zonas. A implementação definida em prepare-for-ha.yaml garante que o Autopilot aprovisiona nós em três zonas no seu cluster, definindo replicas:3, podAntiAffinity com requiredDuringSchedulingIgnoredDuringExecution e topologyKey: "topology.kubernetes.io/zone".

        4. Verifique o estado da implementação.

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

          Quando vir três pods no estado de prontidão, cancele este comando com CTRL+C. O resultado é semelhante ao seguinte:

          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. Execute este script para validar se os seus pods foram implementados em três zonas.

          bash ../scripts/inspect_pod_node.sh default
          

          Cada linha do resultado corresponde a um agrupamento e a segunda coluna indica a zona. O resultado é semelhante ao seguinte:

          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. No Cloud Shell, crie um cluster padrão do GKE na região us-west1.

          gcloud container clusters create $CLUSTER_NAME \
            --location=$CONTROL_PLANE_LOCATION \
            --machine-type="e2-standard-2" \
            --disk-type="pd-standard" \
            --num-nodes="5"
          
        2. Obtenha as credenciais do cluster do GKE.

          gcloud container clusters get-credentials $CLUSTER_NAME \
            --location=$CONTROL_PLANE_LOCATION
          

        Implemente StatefulSets do MySQL

        Nesta secção, implementa um StatefulSet do MySQL. Um StatefulSet é um controlador do Kubernetes que mantém uma identidade exclusiva persistente para cada um dos respetivos pods.

        Cada StatefulSet consiste em três réplicas do MySQL.

        Para implementar o StatefulSet do MySQL, siga estes passos:

        1. Crie um espaço de nomes para o StatefulSet.

          kubectl create namespace mysql1
          
        2. Crie o segredo do 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
          

          A palavra-passe é implementada com cada pod e é usada por comandos e scripts de gestão para a implementação do MySQL InnoDB Cluster e do ClusterSet neste tutorial.

        3. Crie a 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
          

          Esta classe de armazenamento usa o tipo de disco persistente pd-balanced que equilibra o desempenho e o custo. O campo volumeBindingMode está definido como WaitForFirstConsumer, o que significa que o GKE atrasa o aprovisionamento de um PersistentVolume até o pod ser criado. Esta definição garante que o disco é aprovisionado na mesma zona onde o pod está agendado.

        4. Implemente o StatefulSet de pods de instâncias do 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
          

          Este comando implementa o StatefulSet composto por três réplicas. Neste tutorial, o cluster MySQL principal é implementado em três zonas em us-west1. O resultado é semelhante ao seguinte:

          service/mysql created
          statefulset.apps/dbc1 created
          

          Neste tutorial, os limites e os pedidos de recursos estão definidos para valores mínimos para poupar custos. Ao planear uma carga de trabalho de produção, certifique-se de que define estes valores adequadamente para as necessidades da sua organização.

        5. Verifique se o StatefulSet foi criado com êxito.

          kubectl get statefulset -n mysql1 --watch
          

          O StatefulSet pode demorar cerca de 10 minutos a ficar pronto.

        6. Quando todos os três pods estiverem no estado pronto, saia do comando com Ctrl+C. Se vir PodUnscheduleable erros devido a CPU ou memória insuficientes, aguarde alguns minutos para que o plano de controlo seja redimensionado de forma a acomodar a grande carga de trabalho.

          O resultado é semelhante ao seguinte:

          NAME   READY   AGE
          dbc1   1/3     39s
          dbc1   2/3     50s
          dbc1   3/3     73s
          
        7. Para inspecionar o posicionamento dos seus pods nos nós do cluster do GKE, execute este script:

          bash ../scripts/inspect_pod_node.sh mysql1 mysql
          

          O resultado mostra o nome do pod, o nome do nó do GKE e a zona onde o nó é aprovisionado, e é semelhante ao seguinte:

          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
          

          As colunas na saída representam o nome do anfitrião, a zona na nuvem e o nome do pod, respetivamente.

          A política topologySpreadConstraints na especificação StatefulSet (c1-mysql.yaml) direciona o programador para colocar os pods uniformemente no domínio de falhas (topology.kubernetes.io/zone).

          A política podAntiAffinity aplica a restrição de que os pods não podem ser colocados no mesmo nó do cluster do GKE (kubernetes.io/hostname). Para os pods da instância do MySQL, esta política resulta na implementação dos pods de forma uniforme nas três zonas na região Google Cloud . Este posicionamento permite a alta disponibilidade do cluster MySQL InnoDB ao colocar cada instância da base de dados num domínio de falha separado.

        Prepare o cluster InnoDB do MySQL principal

        Para configurar um cluster InnoDB do MySQL, siga estes passos:

        1. No terminal do Cloud Shell, defina as configurações de replicação de grupo para as instâncias do MySQL a adicionar ao 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

          O script vai estabelecer ligação remotamente a cada uma das três instâncias do MySQL para definir e manter as seguintes variáveis de ambiente:

          • group_replication_ip_allowlist: permite que a instância no cluster se ligue a qualquer instância no grupo.
          • binlog_transaction_dependency_tracking='WRITESET': permite transações paralelizadas que não entram em conflito.

          Nas versões do MySQL anteriores à 8.0.22, use group_replication_ip_whitelist em vez de group_replication_ip_allowlist.

        2. Abra um segundo terminal para não ter de criar uma shell para cada Pod.

        3. Estabeleça ligação ao MySQL Shell no 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. Valide a lista de autorizações da replicação de grupo do MySQL para estabelecer ligação a outras instâncias.

          \sql SELECT @@group_replication_ip_allowlist;
          

          O resultado é semelhante ao seguinte:

          +----------------------------------+
          | @@group_replication_ip_allowlist |
          +----------------------------------+
          | mysql.mysql1.svc.cluster.local   |
          +----------------------------------+
          
        5. Verifique se o server-id é único em cada uma das instâncias.

          \sql SELECT @@server_id;
          

          O resultado é semelhante ao seguinte:

          +-------------+
          | @@server_id |
          +-------------+
          |          21 |
          +-------------+
          
        6. Configure cada instância para utilização do cluster MySQL InnoDB e crie uma conta de administrador em cada instância.

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

          Todas as instâncias têm de ter o mesmo nome de utilizador e palavra-passe para que o cluster MySQL InnoDB funcione corretamente. Cada comando produz um resultado semelhante ao seguinte:

          ...
          
          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. Verifique se a instância está pronta para ser usada num cluster InnoDB do MySQL.

          dba.checkInstanceConfiguration()
          

          O resultado é semelhante ao seguinte:

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

          Opcionalmente, pode estabelecer ligação a cada instância do MySQL e repetir este comando. Por exemplo, execute este comando para verificar o estado na instância 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()"'
          

        Crie o cluster InnoDB do MySQL principal

        Em seguida, crie o cluster InnoDB do MySQL com o comando MySQL Admin createCluster. Comece com a instância dbc1-0, que vai ser a instância principal do cluster. Em seguida, adicione duas réplicas adicionais ao cluster.

        Para inicializar o MySQL InnoDB Cluster, siga estes passos:

        1. Crie o cluster InnoDB do MySQL.

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

          A execução do comando createCluster aciona estas operações:

          • Implemente o esquema de metadados.
          • Verifique se a configuração está correta para a replicação de grupos.
          • Registe-o como a instância inicial do novo cluster.
          • Crie as contas internas necessárias, como a conta de utilizador de replicação.
          • Inicie a replicação de grupo.

          Este comando inicializa um cluster MySQL InnoDB com o anfitrião dbc1-0 como o principal. A referência do cluster é armazenada na variável do cluster.

          O resultado tem um aspeto semelhante ao seguinte:

          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. Adicione a segunda instância ao cluster.

          cluster.addInstance('icadmin@dbc1-1.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
          
        3. Adicione a instância restante ao cluster.

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

          O resultado é semelhante ao seguinte:

          ...
          The instance 'dbc1-2.mysql:3306' was successfully added to the cluster.
          
        4. Valide o estado do cluster.

          cluster.status()
          

          Este comando mostra o estado do cluster. A topologia consiste em três anfitriões, uma instância principal e duas secundárias. Opcionalmente, pode ligar para cluster.status({extended:1}).

          O resultado é semelhante ao seguinte:

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

          Opcionalmente, pode ligar para cluster.status({extended:1}) para obter detalhes adicionais do estado.

        Crie uma base de dados de amostra

        Para criar uma base de dados de exemplo, siga estes passos:

        1. Crie uma base de dados e carregue dados na base de dados.

          \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. Inserir dados de amostra na base de dados. Para inserir dados, tem de ter ligação à instância principal do cluster.

          INSERT INTO loan (firstname, lastname, status) VALUES ( 'Fred','Flintstone','pending');
          INSERT INTO loan (firstname, lastname, status) VALUES ( 'Betty','Rubble','approved');
          
        3. Verifique se a tabela contém as três linhas inseridas no passo anterior.

          SELECT * FROM loan;
          

          O resultado é semelhante ao seguinte:

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

        Crie um ClusterSet do MySQL InnoDB

        Pode criar um ClusterSet do MySQL InnoDB para gerir a replicação do cluster principal para clusters de réplica, através de um canal de replicação do ClusterSet dedicado.

        Um MySQL InnoDB ClusterSet oferece tolerância a desastres para implementações do MySQL InnoDB Cluster, associando um MySQL InnoDB Cluster principal a uma ou mais réplicas do mesmo em localizações alternativas, como várias zonas e várias regiões.

        Se fechou o MySQL Shell, crie uma nova shell executando este comando num novo terminal do 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"'
        

        Para criar um MySQL InnoDB ClusterSet, siga estes passos:

        1. No terminal do MySQL Shell, obtenha um objeto de cluster.

          \js
          cluster=dba.getCluster()
          

          O resultado é semelhante ao seguinte:

          <Cluster:mycluster>
          
        2. Inicialize um MySQL InnoDB ClusterSet com o MySQL InnoDB Cluster existente armazenado no objeto de cluster como o principal.

          clusterset=cluster.createClusterSet('clusterset')
          

          O resultado é semelhante ao seguinte:

          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. Verifique o estado do seu MySQL InnoDB ClusterSet.

          clusterset.status()
          

          O resultado é semelhante ao seguinte:

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

          Opcionalmente, pode chamar clusterset.status({extended:1}) para obter detalhes adicionais do estado, incluindo informações sobre o cluster.

        4. Saia do MySQL Shell.

          \q
          

        Implemente um MySQL Router

        Pode implementar um MySQL Router para direcionar o tráfego da aplicação cliente para os clusters adequados. O encaminhamento baseia-se na porta de ligação da aplicação que emite uma operação de base de dados:

        • As gravações são encaminhadas para a instância do cluster principal no ClusterSet principal.
        • As leituras podem ser encaminhadas para qualquer instância no cluster principal.

        Quando inicia um MySQL Router, este é inicializado em relação à implementação do MySQL InnoDB ClusterSet. As instâncias do MySQL Router ligadas ao MySQL InnoDB ClusterSet têm conhecimento de quaisquer comutações controladas ou failovers de emergência e direcionam o tráfego para o novo cluster principal.

        Para implementar um MySQL Router, siga estes passos:

        1. No terminal do Cloud Shell, implemente o MySQL Router.

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

          O resultado é semelhante ao seguinte:

          configmap/mysql-router-config created
          service/mysql-router created
          deployment.apps/mysql-router created
          
        2. Verifique a prontidão da implementação do MySQL Router.

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

          Quando os três pods estiverem prontos, o resultado é semelhante ao seguinte:

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

          Se vir um erro PodUnschedulable na consola, aguarde um ou dois minutos enquanto o GKE aprovisiona mais nós. Atualize a página e deverá ver 3/3 OK.

        3. Inicie o MySQL Shell em qualquer membro do cluster existente.

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

          Este comando liga-se ao pod dbc1-0 e, em seguida, inicia um shell ligado à instância do MySQL dbc1-0.

        4. Valide a configuração do router.

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

          O resultado é semelhante ao seguinte:

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

          \q
          
        6. Execute este script para inspecionar o posicionamento dos pods do MySQL Router.

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

          O script mostra o posicionamento do nó e da zona na nuvem de todos os pods no espaço de nomes mysql1, em que o resultado é semelhante ao seguinte:

          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
          

          Pode observar que os pods do MySQL Router estão distribuídos igualmente pelas zonas, ou seja, não estão colocados no mesmo nó que um pod do MySQL nem no mesmo nó que outro pod do MySQL Router.

        Faça a gestão das atualizações do GKE e do MySQL InnoDB Cluster

        As atualizações do MySQL e do Kubernetes são lançadas regularmente. Siga as práticas recomendadas operacionais para atualizar regularmente o seu ambiente de software. Por predefinição, o GKE gere as atualizações de clusters e node pools por si. O Kubernetes e o GKE também oferecem funcionalidades adicionais para facilitar as atualizações de software do MySQL.

        Planeie as atualizações do GKE

        Pode tomar medidas proativas e definir configurações para mitigar o risco e facilitar uma atualização do cluster mais suave quando estiver a executar serviços com estado, incluindo:

        • Clusters padrão: siga as práticas recomendadas do GKE para atualizar clusters. Escolha uma estratégia de atualização adequada para garantir que as atualizações ocorrem durante o período de manutenção:

          • Escolha atualizações de picos se a otimização de custos for importante e se as suas cargas de trabalho puderem tolerar um encerramento normal em menos de 60 minutos.
          • Escolha atualizações azul-verde se as suas cargas de trabalho forem menos tolerantes a interrupções e se um aumento temporário do custo devido a uma maior utilização de recursos for aceitável.

          Para saber mais, consulte o artigo Atualize um cluster que execute uma carga de trabalho com estado. Os clusters do Autopilot são atualizados automaticamente com base no canal de lançamento que selecionou.

        • Use janelas de manutenção para garantir que as atualizações ocorrem quando pretende. Antes da janela de manutenção, certifique-se de que as cópias de segurança da base de dados são bem-sucedidas.

        • Antes de permitir o tráfego para os nós do MySQL atualizados, use Readiness Probes e Liveness Probes para garantir que estão prontos para o tráfego.

        • Crie sondagens que avaliem se a replicação está sincronizada antes de aceitar tráfego. Isto pode ser feito através de scripts personalizados, consoante a complexidade e a escala da sua base de dados.

        Defina uma política de orçamento de interrupção de pods (PDB)

        Quando um cluster InnoDB do MySQL está a ser executado no GKE, tem de haver um número suficiente de instâncias em execução em qualquer altura para cumprir o requisito de quórum.

        Neste tutorial, dado um cluster do MySQL de três instâncias, duas instâncias têm de estar disponíveis para formar um quórum. Uma política de PodDisruptionBudget permite-lhe limitar o número de agrupamentos que podem ser terminados em qualquer altura. Isto é útil para as operações de estado estável dos seus serviços com estado e para as atualizações de clusters.

        Para garantir que um número limitado de pods é interrompido em simultâneo, defina o PDB para a sua carga de trabalho como maxUnavailable: 1. Isto garante que, em qualquer ponto da operação do serviço, não há mais do que um pod em execução.

        O manifesto da política PodDisruptionBudget seguinte define o número máximo de pods indisponíveis como um para a sua aplicação MySQL.

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

        Para aplicar a política de PDB ao seu cluster, siga estes passos:

        1. Aplique a política de PDB através do kubectl.

          kubectl apply -n mysql1 -f mysql-pdb-maxunavailable.yaml
          
        2. Veja o estado da PDB.

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

          Na secção status do resultado, consulte as quantidades de currentHealthy e desiredHealthy pods. O resultado é semelhante ao seguinte:

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

        Planeie as atualizações binárias do MySQL

        O Kubernetes e o GKE oferecem funcionalidades para facilitar as atualizações do binário do MySQL. No entanto, tem de realizar algumas operações para se preparar para as atualizações.

        Tenha em atenção as seguintes considerações antes de iniciar o processo de atualização:

        • As atualizações devem ser realizadas primeiro num ambiente de teste. Para sistemas de produção, deve realizar mais testes num ambiente de pré-produção.
        • Para algumas versões binárias, não pode reverter a versão depois de fazer uma atualização. Dedique tempo a compreender as implicações de uma atualização.
        • As origens de replicação podem ser replicadas para uma versão mais recente. No entanto, a cópia de uma versão mais recente para uma versão mais antiga não é normalmente suportada.
        • Certifique-se de que tem uma cópia de segurança completa da base de dados antes de implementar a versão atualizada.
        • Tenha em atenção a natureza efémera dos pods do Kubernetes. Qualquer estado de configuração armazenado pelo pod que não esteja no volume persistente é perdido quando o pod é reimplementado.
        • Para atualizações binárias do MySQL, use a mesma estratégia de atualização do PDB, do conjunto de nós e das sondas, conforme descrito anteriormente.

        Num ambiente de produção, deve seguir estas práticas recomendadas:

        • Crie uma imagem de contentor com a nova versão do MySQL.
        • Persista as instruções de compilação de imagens num repositório de controlo de origem.
        • Use um pipeline de compilação e testes de imagens automatizado, como o Cloud Build, e armazene o ficheiro binário da imagem num registo de imagens, como o Artifact Registry.

        Para manter este tutorial simples, não vai criar nem persistir uma imagem de contentor; em alternativa, vai usar as imagens públicas do MySQL.

        Implemente o ficheiro binário do MySQL atualizado

        Para fazer a atualização binária do MySQL, emite um comando declarativo que modifica a versão da imagem do recurso StatefulSet. O GKE executa os passos necessários para parar o pod atual, implementar um novo pod com o binário atualizado e anexar o disco persistente ao novo pod.

        1. Verifique se o PDB foi criado.

          kubectl get poddisruptionbudgets -n mysql1
          
        2. Obtenha a lista de conjuntos com estado.

          kubectl get statefulsets -n mysql1
          
        3. Obtenha a lista de pods em execução com a etiqueta app.

          kubectl get pods --selector=app=mysql -n mysql1
          
        4. Atualize a imagem do MySQL no conjunto com estado.

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

          O resultado é semelhante ao seguinte:

          statefulset.apps/mysql image updated
          
        5. Verifique o estado dos pods de terminação e dos novos pods.

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

        Valide a atualização binária do MySQL

        Durante a atualização, pode verificar o estado da implementação, os novos pods e o serviço existente.

        1. Confirme a atualização executando o comando rollout status.

          kubectl rollout status statefulset/dbc1 -n mysql1
          

          O resultado é semelhante ao seguinte:

          partitioned roll out complete: 3 new pods have been updated...
          
        2. Confirme a versão da imagem inspecionando o conjunto com estado.

          kubectl get statefulsets -o wide -n mysql1
          

          O resultado é semelhante ao seguinte:

          NAME   READY   AGE   CONTAINERS   IMAGES
          dbc1   3/3     37m   mysql        mysql/mysql-server:8.0.30
          
        3. Verifique o estado do 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\")"'
          

          Para cada instância do cluster, procure os valores de estado e versão no resultado. O resultado é semelhante ao seguinte:

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

        Reverter a implementação da app mais recente

        Quando reverte a implementação de uma versão binária atualizada, o processo de implementação é invertido e é implementado um novo conjunto de pods com a versão da imagem anterior.

        Para reverter a implementação para a versão de trabalho anterior, use o comando rollout undo:

        kubectl rollout undo statefulset/dbc1 -n mysql1
        

        O resultado é semelhante ao seguinte:

        statefulset.apps/dbc1 rolled back
        

        Expanda o cluster da base de dados horizontalmente

        Para dimensionar o cluster MySQL InnoDB horizontalmente, adicione nós adicionais ao conjunto de nós do cluster GKE (apenas necessário se estiver a usar o Standard), implemente instâncias adicionais do MySQL e, em seguida, adicione cada instância ao cluster MySQL InnoDB existente.

        Adicione nós ao seu cluster padrão

        Esta operação não é necessária se estiver a usar um cluster do Autopilot.

        Para adicionar nós ao seu cluster Standard, siga as instruções abaixo para o Cloud Shell ou a Google Cloud consola. Para ver passos detalhados, consulte o artigo Redimensione um conjunto de nós

        gcloud

        No Cloud Shell, redimensione o conjunto de nós predefinido para oito instâncias em cada grupo de instâncias gerido.

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

        Consola

        Para adicionar nós ao cluster padrão:

        1. Abra a gkemulti-west1página Cluster na Google Cloud consola.
        2. Selecione Nodes e clique em default pool.
        3. Desloque a página para baixo até Grupos de instâncias.
        4. Para cada grupo de instâncias, redimensione o valor de Number of nodes de 5 para 8 nós.

        Adicione pods do MySQL ao cluster principal

        Para implementar pods do MySQL adicionais para dimensionar o cluster horizontalmente, siga estes passos:

        1. No Cloud Shell, atualize o número de réplicas na implementação do MySQL de três réplicas para cinco réplicas.

          kubectl scale  -n mysql1 --replicas=5 -f c1-mysql.yaml
          
        2. Verifique o progresso da implementação.

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

          Para determinar se os pods estão prontos, use a flag --watch para monitorizar a implementação. Se estiver a usar clusters do Autopilot e vir erros Pod Unschedulable, isto pode indicar que o GKE está a aprovisionar nós para acomodar os pods adicionais.

        3. Configure as definições de replicação de grupos para as novas instâncias do MySQL a adicionar ao cluster

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

          O script envia os comandos para as instâncias em execução nos pods com os ordinais 3 a 4.

        4. Abra o MySQL Shell.

          kubectl -n mysql1 \
            exec -it dbc1-0 -- \
                /bin/bash \
                  -c 'mysqlsh \
                  --uri="root:$MYSQL_ROOT_PASSWORD@dbc1-0.mysql"'
          
        5. Configure as duas novas instâncias do 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")});
          

          Os comandos verificam se a instância está configurada corretamente para a utilização do MySQL InnoDB Cluster e fazem as alterações de configuração necessárias.

        6. Adicione uma das novas instâncias ao cluster principal.

          cluster = dba.getCluster()
          cluster.addInstance('icadmin@dbc1-3.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
          
        7. Adicione uma segunda nova instância ao cluster principal.

          cluster.addInstance('icadmin@dbc1-4.mysql', {password: os.getenv("MYSQL_ROOT_PASSWORD"), recoveryMethod: 'clone'});
          
        8. Obtenha o estado do ClusterSet, que também inclui o estado do cluster.

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

          O resultado é semelhante ao seguinte:

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

          \q
          

        Limpar

        Para evitar incorrer em custos na sua conta do Google Cloud pelos recursos usados neste tutorial, elimine o projeto que contém os recursos ou mantenha o projeto e elimine os recursos individuais.

        Elimine o projeto

        A forma mais fácil de evitar a faturação é eliminar o projeto que criou para o tutorial.

      5. In the Google Cloud console, go to the Manage resources page.

        Go to Manage resources

      6. In the project list, select the project that you want to delete, and then click Delete.
      7. In the dialog, type the project ID, and then click Shut down to delete the project.
      8. O que se segue?