Implemente uma base de dados PostgreSQL de elevada disponibilidade no GKE

O PostgreSQL é uma base de dados relacional de objetos de código aberto conhecida pela fiabilidade e integridade dos dados. É compatível com ACID e suporta chaves estrangeiras, junções, vistas, acionadores e procedimentos armazenados.

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 PostgreSQL de elevada disponibilidade no Google Kubernetes Engine (GKE).

Crie a infraestrutura do cluster

Nesta secção, vai executar um script do Terraform para criar uma nuvem virtual privada (VPC) personalizada, um repositório do Artifact Registry para armazenar imagens do PostgreSQL e dois clusters regionais do GKE. Um cluster vai ser implementado em us-central1 e o segundo cluster para cópia de segurança vai ser implementado em us-west1.

Para criar o cluster, siga estes passos:

Piloto automático

No Cloud Shell, execute os seguintes comandos:

terraform -chdir=terraform/gke-autopilot init
terraform -chdir=terraform/gke-autopilot apply -var project_id=$PROJECT_ID

Quando lhe for pedido, escreva yes.

Compreenda a configuração do Terraform

Os ficheiros de configuração do Terraform criam os seguintes recursos para implementar a sua infraestrutura:

  • Crie um repositório do Artifact Registry para armazenar as imagens Docker.
    resource "google_artifact_registry_repository" "main" {
      location      = "us"
      repository_id = "main"
      format        = "DOCKER"
      project       = var.project_id
    }
  • Crie a rede VPC e a sub-rede para a interface de rede da VM.
    module "gcp-network" {
      source  = "terraform-google-modules/network/google"
      version = "< 8.0.0"
    
      project_id   = var.project_id
      network_name = "vpc-gke-postgresql"
    
      subnets = [
        {
          subnet_name           = "snet-gke-postgresql-us-central1"
          subnet_ip             = "10.0.0.0/17"
          subnet_region         = "us-central1"
          subnet_private_access = true
        },
        {
          subnet_name           = "snet-gke-postgresql-us-west1"
          subnet_ip             = "10.0.128.0/17"
          subnet_region         = "us-west1"
          subnet_private_access = true
        },
      ]
    
      secondary_ranges = {
        ("snet-gke-postgresql-us-central1") = [
          {
            range_name    = "ip-range-pods-db1"
            ip_cidr_range = "192.168.0.0/18"
          },
          {
            range_name    = "ip-range-svc-db1"
            ip_cidr_range = "192.168.64.0/18"
          },
        ],
        ("snet-gke-postgresql-us-west1") = [
          {
            range_name    = "ip-range-pods-db2"
            ip_cidr_range = "192.168.128.0/18"
          },
          {
            range_name    = "ip-range-svc-db2"
            ip_cidr_range = "192.168.192.0/18"
          },
        ]
      }
    }
    
    output "network_name" {
      value = module.gcp-network.network_name
    }
    
    output "primary_subnet_name" {
      value = module.gcp-network.subnets_names[0]
    }
    
    output "secondary_subnet_name" {
      value = module.gcp-network.subnets_names[1]
    }
  • Crie um cluster principal do GKE.

    O Terraform cria um cluster privado na região us-central1 e ativa a Cópia de segurança do GKE para recuperação de desastres e o serviço gerido para Prometheus para monitorização de clusters.

    O Managed Service for Prometheus só é suportado em clusters do Autopilot com a versão 1.25 ou posterior do GKE.

    module "gke-db1-autopilot" {
      source                          = "../modules/beta-autopilot-private-cluster"
      project_id                      = var.project_id
      name                            = "cluster-db1"
      kubernetes_version              = "1.25" # Will be ignored if use "REGULAR" release_channel
      region                          = "us-central1"
      regional                        = true
      zones                           = ["us-central1-a", "us-central1-b", "us-central1-c"]
      network                         = module.network.network_name
      subnetwork                      = module.network.primary_subnet_name
      ip_range_pods                   = "ip-range-pods-db1"
      ip_range_services               = "ip-range-svc-db1"
      horizontal_pod_autoscaling      = true
      release_channel                 = "RAPID" # Default version is 1.22 in REGULAR. GMP on Autopilot requires V1.25 via var.kubernetes_version
      enable_vertical_pod_autoscaling = true
      enable_private_endpoint         = false
      enable_private_nodes            = true
      master_ipv4_cidr_block          = "172.16.0.0/28"
      create_service_account          = false
    }

  • Crie um cluster de cópia de segurança na região us-west1 para recuperação de desastres.

    module "gke-db2-autopilot" {
      source                          = "../modules/beta-autopilot-private-cluster"
      project_id                      = var.project_id
      name                            = "cluster-db2"
      kubernetes_version              = "1.25" # Will be ignored if use "REGULAR" release_channel
      region                          = "us-west1"
      regional                        = true
      zones                           = ["us-west1-a", "us-west1-b", "us-west1-c"]
      network                         = module.network.network_name
      subnetwork                      = module.network.secondary_subnet_name
      ip_range_pods                   = "ip-range-pods-db2"
      ip_range_services               = "ip-range-svc-db2"
      horizontal_pod_autoscaling      = true
      release_channel                 = "RAPID" # Default version is 1.22 in REGULAR. GMP on Autopilot requires V1.25 via var.kubernetes_version
      enable_vertical_pod_autoscaling = true
      enable_private_endpoint         = false
      enable_private_nodes            = true
      master_ipv4_cidr_block          = "172.16.0.16/28"
      create_service_account          = false
    }

Standard

No Cloud Shell, execute os seguintes comandos:

terraform -chdir=terraform/gke-standard init
terraform -chdir=terraform/gke-standard apply -var project_id=$PROJECT_ID

Quando lhe for pedido, escreva yes.

Compreenda a configuração do Terraform

Os ficheiros de configuração do Terraform criam os seguintes recursos para implementar a sua infraestrutura:

  • Crie um repositório do Artifact Registry para armazenar as imagens Docker.
    resource "google_artifact_registry_repository" "main" {
      location      = "us"
      repository_id = "main"
      format        = "DOCKER"
      project       = var.project_id
    }
    resource "google_artifact_registry_repository_iam_binding" "binding" {
      provider   = google-beta
      project    = google_artifact_registry_repository.main.project
      location   = google_artifact_registry_repository.main.location
      repository = google_artifact_registry_repository.main.name
      role       = "roles/artifactregistry.reader"
      members = [
        "serviceAccount:${module.gke-db1.service_account}",
      ]
    }
  • Crie a rede VPC e a sub-rede para a interface de rede da VM.
    module "gcp-network" {
      source  = "terraform-google-modules/network/google"
      version = "< 8.0.0"
    
      project_id   = var.project_id
      network_name = "vpc-gke-postgresql"
    
      subnets = [
        {
          subnet_name           = "snet-gke-postgresql-us-central1"
          subnet_ip             = "10.0.0.0/17"
          subnet_region         = "us-central1"
          subnet_private_access = true
        },
        {
          subnet_name           = "snet-gke-postgresql-us-west1"
          subnet_ip             = "10.0.128.0/17"
          subnet_region         = "us-west1"
          subnet_private_access = true
        },
      ]
    
      secondary_ranges = {
        ("snet-gke-postgresql-us-central1") = [
          {
            range_name    = "ip-range-pods-db1"
            ip_cidr_range = "192.168.0.0/18"
          },
          {
            range_name    = "ip-range-svc-db1"
            ip_cidr_range = "192.168.64.0/18"
          },
        ],
        ("snet-gke-postgresql-us-west1") = [
          {
            range_name    = "ip-range-pods-db2"
            ip_cidr_range = "192.168.128.0/18"
          },
          {
            range_name    = "ip-range-svc-db2"
            ip_cidr_range = "192.168.192.0/18"
          },
        ]
      }
    }
    
    output "network_name" {
      value = module.gcp-network.network_name
    }
    
    output "primary_subnet_name" {
      value = module.gcp-network.subnets_names[0]
    }
    
    output "secondary_subnet_name" {
      value = module.gcp-network.subnets_names[1]
    }
  • Crie um cluster principal do GKE.

    O Terraform cria um cluster privado na região us-central1 e ativa a cópia de segurança do GKE para recuperação de desastres e o serviço gerido para o Prometheus para monitorização de clusters.

    module "gke-db1" {
      source                   = "../modules/beta-private-cluster"
      project_id               = var.project_id
      name                     = "cluster-db1"
      regional                 = true
      region                   = "us-central1"
      network                  = module.network.network_name
      subnetwork               = module.network.primary_subnet_name
      ip_range_pods            = "ip-range-pods-db1"
      ip_range_services        = "ip-range-svc-db1"
      create_service_account   = true
      enable_private_endpoint  = false
      enable_private_nodes     = true
      master_ipv4_cidr_block   = "172.16.0.0/28"
      network_policy           = true
      cluster_autoscaling = {
        "autoscaling_profile": "OPTIMIZE_UTILIZATION",
        "enabled" : true,
        "gpu_resources" : [],
        "min_cpu_cores" : 36,
        "min_memory_gb" : 144,
        "max_cpu_cores" : 48,
        "max_memory_gb" : 192,
      }
      monitoring_enable_managed_prometheus = true
      gke_backup_agent_config = true
    
      node_pools = [
        {
          name            = "pool-sys"
          autoscaling     = true
          min_count       = 1
          max_count       = 3
          max_surge       = 1
          max_unavailable = 0
          machine_type    = "e2-standard-4"
          node_locations  = "us-central1-a,us-central1-b,us-central1-c"
          auto_repair     = true
        },
        {
          name            = "pool-db"
          autoscaling     = true
          max_surge       = 1
          max_unavailable = 0
          machine_type    = "e2-standard-8"
          node_locations  = "us-central1-a,us-central1-b,us-central1-c"
          auto_repair     = true
        },
      ]
      node_pools_labels = {
        all = {}
        pool-db = {
          "app.stateful/component" = "postgresql"
        }
        pool-sys = {
          "app.stateful/component" = "postgresql-pgpool"
        }
      }
      node_pools_taints = {
        all = []
        pool-db = [
          {
            key    = "app.stateful/component"
            value  = "postgresql"
            effect = "NO_SCHEDULE"
          },
        ],
        pool-sys = [
          {
            key    = "app.stateful/component"
            value  = "postgresql-pgpool"
            effect = "NO_SCHEDULE"
          },
        ],
      }
      gce_pd_csi_driver = true
    }

  • Crie um cluster de cópia de segurança na região us-west1 para recuperação de desastres.

    module "gke-db2" {
      source                   = "../modules/beta-private-cluster"
      project_id               = var.project_id
      name                     = "cluster-db2"
      regional                 = true
      region                   = "us-west1"
      network                  = module.network.network_name
      subnetwork               = module.network.secondary_subnet_name
      ip_range_pods            = "ip-range-pods-db2"
      ip_range_services        = "ip-range-svc-db2"
      create_service_account   = false
      service_account          = module.gke-db1.service_account
      enable_private_endpoint  = false
      enable_private_nodes     = true
      master_ipv4_cidr_block   = "172.16.0.16/28"
      network_policy           = true
      cluster_autoscaling = {
        "autoscaling_profile": "OPTIMIZE_UTILIZATION",
        "enabled" : true,
        "gpu_resources" : [],
        "min_cpu_cores" : 10,
        "min_memory_gb" : 144,
        "max_cpu_cores" : 48,
        "max_memory_gb" : 192,
      }
      monitoring_enable_managed_prometheus = true
      gke_backup_agent_config = true
      node_pools = [
        {
          name            = "pool-sys"
          autoscaling     = true
          min_count       = 1
          max_count       = 3
          max_surge       = 1
          max_unavailable = 0
          machine_type    = "e2-standard-4"
          node_locations  = "us-west1-a,us-west1-b,us-west1-c"
          auto_repair     = true
        },
        {
          name            = "pool-db"
          autoscaling     = true
          max_surge       = 1
          max_unavailable = 0
          machine_type    = "e2-standard-8"
          node_locations  = "us-west1-a,us-west1-b,us-west1-c"
          auto_repair     = true
        },
      ]
      node_pools_labels = {
        all = {}
        pool-db = {
          "app.stateful/component" = "postgresql"
        }
        pool-sys = {
          "app.stateful/component" = "postgresql-pgpool"
        }
      }
      node_pools_taints = {
        all = []
        pool-db = [
          {
            key    = "app.stateful/component"
            value  = "postgresql"
            effect = "NO_SCHEDULE"
          },
        ],
        pool-sys = [
          {
            key    = "app.stateful/component"
            value  = "postgresql-pgpool"
            effect = "NO_SCHEDULE"
          },
        ],
      }
      gce_pd_csi_driver = true
    }

Implemente o PostgreSQL no seu cluster

Nesta secção, vai implementar uma instância da base de dados PostgreSQL para ser executada no GKE através de um gráfico Helm.

Instale o PostgreSQL

Para instalar o PostgreSQL no seu cluster, siga estes passos.

  1. Configure o acesso ao Docker.

    gcloud auth configure-docker us-docker.pkg.dev
    
  2. Preencha o Artifact Registry com as imagens Docker do PostgreSQL necessárias.

    ./scripts/gcr.sh bitnami/postgresql-repmgr 15.1.0-debian-11-r0
    ./scripts/gcr.sh bitnami/postgres-exporter 0.11.1-debian-11-r27
    ./scripts/gcr.sh bitnami/pgpool 4.3.3-debian-11-r28
    

    O script envia as seguintes imagens do Bitnami para o Artifact Registry para o Helm instalar:

    • postgresql-repmgr: Esta solução de cluster do PostgreSQL inclui o gestor de replicação do PostgreSQL (repmgr), uma ferramenta de código aberto para gerir a replicação e a comutação por falha em clusters do PostgreSQL.
    • postgres-exporter: O PostgreSQL Exporter recolhe métricas do PostgreSQL para consumo do Prometheus.
    • pgpool: o Pgpool-II é o proxy do PostgreSQL. Oferece agrupamento de ligações e balanceamento de carga.
  3. Verifique se as imagens corretas estão armazenadas no repositório.

    gcloud artifacts docker images list us-docker.pkg.dev/$PROJECT_ID/main \
        --format="flattened(package)"
    

    O resultado é semelhante ao seguinte:

    ---
    image: us-docker.pkg.dev/[PROJECT_ID]/main/bitnami/pgpool
    ---
    image: us-docker.pkg.dev/[PROJECT_ID]/main/bitnami/postgres-exporter
    ---
    image: us-docker.pkg.dev/h[PROJECT_ID]/main/bitnami/postgresql-repmgr
    
  4. Configure o acesso à linha de comandos kubectl ao cluster principal.

    gcloud container clusters get-credentials $SOURCE_CLUSTER \
    --location=$REGION --project=$PROJECT_ID
    
  5. Crie um espaço de nomes.

    export NAMESPACE=postgresql
    kubectl create namespace $NAMESPACE
    
  6. Se estiver a implementar num cluster do Autopilot, configure o aprovisionamento de nós em três zonas. Pode ignorar este passo se estiver a implementar num cluster padrão.

    Por predefinição, o piloto automático aprovisiona recursos apenas em duas zonas. A implementação definida em prepareforha.yaml garante que o Autopilot aprovisiona nós em três zonas no seu cluster, definindo estes valores:

    • replicas:3
    • podAntiAffinity com requiredDuringSchedulingIgnoredDuringExecution e topologyKey: "topology.kubernetes.io/zone"
    kubectl -n $NAMESPACE apply -f scripts/prepareforha.yaml
    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: prepare-three-zone-ha
      labels:
        app: prepare-three-zone-ha
        app.kubernetes.io/name: postgresql-ha
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: prepare-three-zone-ha
          app.kubernetes.io/name: postgresql-ha
      template:
        metadata:
          labels:
            app: prepare-three-zone-ha
            app.kubernetes.io/name: postgresql-ha
        spec:
          affinity:
            podAntiAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                  - key: app
                    operator: In
                    values:
                    - prepare-three-zone-ha
                topologyKey: "topology.kubernetes.io/zone"
            nodeAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - preference:
                  matchExpressions:
                  - key: cloud.google.com/compute-class
                    operator: In
                    values:
                    - "Scale-Out"
                weight: 1
          nodeSelector:
            app.stateful/component: postgresql
          tolerations:
          - effect: NoSchedule
            key: app.stateful/component
            operator: Equal
            value: postgresql
          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"
    
  7. Atualize a dependência do Helm.

    cd helm/postgresql-bootstrap
    helm dependency update
    
  8. Inspecione e valide os gráficos que o Helm vai instalar.

    helm -n postgresql template postgresql . \
      --set global.imageRegistry="us-docker.pkg.dev/$PROJECT_ID/main"
    
  9. Instale o gráfico Helm.

    helm -n postgresql upgrade --install postgresql . \
        --set global.imageRegistry="us-docker.pkg.dev/$PROJECT_ID/main"
    

    O resultado é semelhante ao seguinte:

    NAMESPACE: postgresql
    STATUS: deployed
    REVISION: 1
    TEST SUITE: None
    
  10. Verifique se as réplicas do PostgreSQL estão em execução.

    kubectl get all -n $NAMESPACE
    

    O resultado é semelhante ao seguinte:

    NAME                                                          READY   STATUS    RESTARTS   AGE
    pod/postgresql-postgresql-bootstrap-pgpool-75664444cb-dkl24   1/1     Running   0          8m39s
    pod/postgresql-postgresql-ha-pgpool-6d86bf9b58-ff2bg          1/1     Running   0          8m39s
    pod/postgresql-postgresql-ha-postgresql-0                     2/2     Running   0          8m39s
    pod/postgresql-postgresql-ha-postgresql-1                     2/2     Running   0          8m39s
    pod/postgresql-postgresql-ha-postgresql-2                     2/2     Running   0          8m38s
    
    NAME                                                   TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)    AGE
    service/postgresql-postgresql-ha-pgpool                ClusterIP   192.168.99.236    <none>        5432/TCP   8m39s
    service/postgresql-postgresql-ha-postgresql            ClusterIP   192.168.90.20     <none>        5432/TCP   8m39s
    service/postgresql-postgresql-ha-postgresql-headless   ClusterIP   None              <none>        5432/TCP   8m39s
    service/postgresql-postgresql-ha-postgresql-metrics    ClusterIP   192.168.127.198   <none>        9187/TCP   8m39s
    
    NAME                                                     READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/postgresql-postgresql-bootstrap-pgpool   1/1     1            1           8m39s
    deployment.apps/postgresql-postgresql-ha-pgpool          1/1     1            1           8m39s
    
    NAME                                                                DESIRED   CURRENT   READY   AGE
    replicaset.apps/postgresql-postgresql-bootstrap-pgpool-75664444cb   1         1         1       8m39s
    replicaset.apps/postgresql-postgresql-ha-pgpool-6d86bf9b58          1         1         1       8m39s
    
    NAME                                                   READY   AGE
    statefulset.apps/postgresql-postgresql-ha-postgresql   3/3     8m39s
    

Crie um conjunto de dados de teste

Nesta secção, vai criar uma base de dados e uma tabela com valores de exemplo. A base de dados serve como um conjunto de dados de teste para o processo de comutação por falha que vai testar mais tarde neste tutorial.

  1. Estabeleça ligação à sua instância do PostgreSQL.

    cd ../../
    ./scripts/launch-client.sh
    

    O resultado é semelhante ao seguinte:

    Launching Pod pg-client in the namespace postgresql ...
    pod/pg-client created
    waiting for the Pod to be ready
    Copying script files to the target Pod pg-client ...
    Pod: pg-client is healthy
    
  2. Inicie uma sessão de shell.

    kubectl exec -it pg-client -n postgresql -- /bin/bash
    
  3. Crie uma base de dados e uma tabela e, em seguida, insira algumas linhas de teste.

    psql -h $HOST_PGPOOL -U postgres -a -q -f /tmp/scripts/generate-db.sql
    
  4. Verifique o número de linhas de cada tabela.

    psql -h $HOST_PGPOOL -U postgres -a -q -f /tmp/scripts/count-rows.sql
    

    O resultado é semelhante ao seguinte:

    select COUNT(*) from tb01;
     count
    --------
     300000
    (1 row)
    
    select COUNT(*) from tb02;
     count
    --------
     300000
    (1 row)
    
  5. Gere dados de teste.

    export DB=postgres
    pgbench -i -h $HOST_PGPOOL -U postgres $DB -s 50
    

    O resultado é semelhante ao seguinte:

    dropping old tables...
    creating tables...
    generating data (client-side)...
    5000000 of 5000000 tuples (100%) done (elapsed 29.85 s, remaining 0.00 s)
    vacuuming...
    creating primary keys...
    done in 36.86 s (drop tables 0.00 s, create tables 0.01 s, client-side generate 31.10 s, vacuum 1.88 s, primary keys 3.86 s).
    
  6. Saia do pod do cliente postgres.

    exit
    

Monitorize o PostgreSQL

Nesta secção, vai ver métricas e configurar alertas para a sua instância do PostgreSQL. Vai usar o serviço gerido do Google Cloud para o Prometheus para realizar a monitorização e os alertas.

Ver métricas

A sua implementação do PostgreSQL inclui um contentor secundário postgresql-exporter. Este contentor expõe um ponto final /metrics. O Google Cloud Managed Service for Prometheus está configurado para monitorizar os pods do PostgreSQL neste ponto final. Pode ver estas métricas através dos Google Cloud painéis de controlo da consola.

A Google Cloud consola oferece algumas formas de criar e guardar a configuração do painel de controlo:

  • Criação e exportação: pode criar painéis de controlo diretamente na Google Cloud consola, e, em seguida, exportá-los e armazená-los num repositório de código. Para o fazer, na barra de ferramentas do painel de controlo, abra o editor JSON e transfira o ficheiro JSON do painel de controlo.
  • Armazenamento e importação: pode importar um painel de controlo a partir de um ficheiro JSON clicando em +Criar painel de controlo e carregando o conteúdo JSON do painel de controlo através do menu Editor JSON.

Para visualizar dados da sua aplicação PostgreSQL e cluster do GKE, siga estes passos:

  1. Crie os seguintes painéis de controlo.

    cd monitoring
    gcloud monitoring dashboards create \
            --config-from-file=dashboard/postgresql-overview.json \
            --project=$PROJECT_ID
    gcloud monitoring dashboards create \
            --config-from-file dashboard/gke-postgresql.json \
            --project $PROJECT_ID
    
  2. Na Google Cloud consola, navegue até ao painel de controlo do Cloud Monitoring. Aceda ao painel de controlo do Cloud Monitoring

  3. Selecione Personalizado na lista do painel de controlo. São apresentados os seguintes painéis de controlo:

    • Vista geral do PostgreSQL: apresenta métricas da aplicação PostgreSQL, incluindo o tempo de atividade da base de dados, o tamanho da base de dados e a latência das transações.
    • Cluster do GKE PostgreSQL: apresenta métricas do cluster do GKE no qual o PostgreSQL está a ser executado, incluindo a utilização da CPU, a utilização da memória e a utilização do volume.
  4. Clique em cada link para examinar os painéis de controlo gerados.

Configure alertas

Os alertas permitem-lhe ter conhecimento atempado dos problemas nas suas aplicações para que possa resolvê-los rapidamente. Pode criar uma política de alertas para especificar as circunstâncias em que quer receber alertas e como quer ser notificado. Também pode criar canais de notificação que lhe permitem selecionar onde os alertas são enviados.

Nesta secção, vai usar o Terraform para configurar os seguintes alertas de exemplo:

  • db_max_transaction: monitoriza o atraso máximo das transações em segundos. É acionado um alerta se o valor for superior a 10.
  • db_node_up: monitoriza o estado dos agrupamentos da base de dados; 0 significa que um agrupamento está inativo e aciona um alerta.

Para configurar alertas, siga estes passos:

  1. Configure alertas com o Terraform.

    EMAIL=YOUR_EMAIL
    cd alerting/terraform
    terraform init
    terraform plan -var project_id=$PROJECT_ID -var email_address=$EMAIL
    terraform apply -var project_id=$PROJECT_ID -var email_address=$EMAIL
    

    Substitua os seguintes valores:

    • YOUR_EMAIL: o seu endereço de email.

    O resultado é semelhante ao seguinte :

    Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
    
  2. Associe-o ao Pod do cliente.

    cd ../../../
    kubectl exec -it --namespace postgresql pg-client -- /bin/bash
    
  3. Gere um teste de carga para testar o alerta db_max_transaction.

    pgbench -i -h $HOST_PGPOOL -U postgres -s 200 postgres
    

    O resultado é semelhante ao seguinte:

    dropping old tables...
    creating tables...
    generating data (client-side)...
    20000000 of 20000000 tuples (100%) done (elapsed 163.22 s, remaining 0.00 s)
    vacuuming...
    creating primary keys...
    done in 191.30 s (drop tables 0.14 s, create tables 0.01 s, client-side generate 165.62 s, vacuum 4.52 s, primary keys 21.00 s).
    

    O alerta é acionado e envia um email para YOUR_EMAIL com uma linha de assunto que começa por "[ALERTA] Atraso máximo da transação".

  4. Na Google Cloud consola, navegue para a página Política de alertas.

    Aceder à política de alertas

  5. Selecione db_max_transaction nas políticas apresentadas. No gráfico, deve ver um pico do teste de carga que excede o limite de 10 para a métrica do Prometheus pg_stat_activity_max_tx_duration/gauge.

  6. Saia do pod do cliente postgres.

    exit
    

Faça a gestão das atualizações do PostgreSQL e do GKE

As atualizações de versões do PostgreSQL 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 do cluster e do conjunto de nós por si.

Atualize o PostgreSQL

Esta secção mostra como pode fazer uma atualização de versão do PostgreSQL. Para este tutorial, vai usar uma estratégia de atualização contínua para atualizar os seus pods, de modo que, em nenhum momento, todos os pods fiquem inativos.

Para fazer uma atualização de versão, siga estes passos:

  1. Envie uma versão atualizada da imagem postgresql-repmgr para o Artifact Registry. Defina a nova versão (por exemplo, postgresql-repmgr 15.1.0-debian-11-r1).

    NEW_IMAGE=us-docker.pkg.dev/$PROJECT_ID/main/bitnami/postgresql-repmgr:15.1.0-debian-11-r1
    ./scripts/gcr.sh bitnami/postgresql-repmgr 15.1.0-debian-11-r1
    
  2. Acione uma atualização contínua através de kubectl.

    kubectl set image statefulset -n postgresql postgresql-postgresql-ha-postgresql postgresql=$NEW_IMAGE
    kubectl rollout restart statefulsets -n postgresql postgresql-postgresql-ha-postgresql
    kubectl rollout status statefulset -n postgresql postgresql-postgresql-ha-postgresql
    

    O StatefulSet conclui uma atualização contínua, começando pela réplica ordinal mais alta e terminando na mais baixa.

    O resultado é semelhante ao seguinte:

    Waiting for 1 pods to be ready...
    waiting for statefulset rolling update to complete 1 pods at revision postgresql-postgresql-ha-postgresql-5c566ccf49...
    Waiting for 1 pods to be ready...
    Waiting for 1 pods to be ready...
    waiting for statefulset rolling update to complete 2 pods at revision postgresql-postgresql-ha-postgresql-5c566ccf49...
    Waiting for 1 pods to be ready...
    Waiting for 1 pods to be ready...
    statefulset rolling update complete 3 pods at revision postgresql-postgresql-ha-postgresql-5c566ccf49...
    

Planeie atualizações do GKE em clusters Standard

Esta secção é aplicável se estiver a executar clusters padrão. Pode tomar medidas proativas e definir configurações para mitigar o risco e facilitar uma atualização mais suave do cluster quando estiver a executar serviços com estado, incluindo:

  • 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 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.

  • Use o serviço Recommender para verificar se existem estatísticas e recomendações de descontinuação para evitar interrupções do serviço.

  • 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 atualizados, use sondas de prontidão e de atividade para garantir que estão prontas 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.

Valide a disponibilidade da base de dados durante as atualizações de clusters padrão

Esta secção é aplicável se estiver a executar clusters padrão. Para verificar a disponibilidade do PostgreSQL durante as atualizações, o processo geral consiste em gerar tráfego na base de dados PostgreSQL durante o processo de atualização. Em seguida, use pgbench para verificar se a base de dados consegue processar um nível base de tráfego durante uma atualização, em comparação com quando a base de dados está totalmente disponível.

  1. Estabeleça ligação à sua instância do PostgreSQL.

    ./scripts/launch-client.sh
    

    O resultado é semelhante ao seguinte:

    Launching Pod pg-client in the namespace postgresql ...
    pod/pg-client created
    waiting for the Pod to be ready
    Copying script files to the target Pod pg-client ...
    Pod: pg-client is healthy
    
  2. No Cloud Shell, aceda ao shell do pod do cliente.

    kubectl exec -it -n postgresql pg-client -- /bin/bash
    
  3. Inicialize o pgbench .

    pgbench -i -h $HOST_PGPOOL -U postgres postgres
    
  4. Use o seguinte comando para obter resultados de base para confirmar que a sua aplicação PostgreSQL permanece altamente disponível durante o período para uma atualização. Para obter um resultado de base, teste com várias ligações através de várias tarefas (threads) durante 30 segundos.

    pgbench -h $HOST_PGPOOL -U postgres postgres -c10 -j4 -T 30 -R 200
    

    O resultado tem um aspeto semelhante ao seguinte:

    pgbench (14.5)
    starting vacuum...end.
    transaction type: <builtin: TPC-B (sort of)>
    scaling factor: 1
    query mode: simple
    number of clients: 10
    number of threads: 4
    duration: 30 s
    number of transactions actually processed: 5980
    latency average = 7.613 ms
    latency stddev = 2.898 ms
    rate limit schedule lag: avg 0.256 (max 36.613) ms
    initial connection time = 397.804 ms
    tps = 201.955497 (without initial connection time)
    
  5. Para garantir a disponibilidade durante as atualizações, pode gerar alguma carga na sua base de dados e garantir que a aplicação PostgreSQL oferece uma taxa de resposta consistente durante a atualização. Para realizar este teste, gere algum tráfego em relação à base de dados através do comando pgbench. O seguinte comando é executado pgbench durante uma hora, segmentando 200 TPS (transações por segundo) e apresentando a taxa de pedidos a cada 2 segundos.

    pgbench -h $HOST_PGPOOL -U postgres postgres --client=10 --jobs=4 --rate=200 --time=3600 --progress=2 --select-only
    

    Onde:

    • --client: número de clientes simulados, ou seja, número de sessões de base de dados em simultâneo.
    • --jobs: número de threads de trabalho no pgbench. A utilização de mais do que uma discussão pode ser útil em máquinas com várias CPUs. Os clientes são distribuídos o mais uniformemente possível entre as threads disponíveis. A predefinição é 1.
    • --rate: a taxa é apresentada em transações por segundo
    • --progress: mostra o relatório de progresso a cada sec segundos.

    O resultado é semelhante ao seguinte:

    pgbench (14.5)
    starting vacuum...end.
    progress: 5.0 s, 354.8 tps, lat 25.222 ms stddev 15.038
    progress: 10.0 s, 393.8 tps, lat 25.396 ms stddev 16.459
    progress: 15.0 s, 412.8 tps, lat 24.216 ms stddev 14.548
    progress: 20.0 s, 405.0 tps, lat 24.656 ms stddev 14.066
    
  6. Na Google Cloud consola, navegue novamente para o painel de controlo Vista geral do PostgreSQL no Cloud Monitoring. Repare no pico nos gráficos Ligação por BD e Ligação por pod.

  7. Saia do pod do cliente.

    exit
    
  8. Elimine o pod do cliente.

    kubectl delete pod -n postgresql pg-client
    

Simule uma interrupção do serviço PostgreSQL

Nesta secção, vai simular uma interrupção do serviço numa das réplicas do PostgreSQL parando o serviço do gestor de replicação. Isto impede que o pod sirva tráfego para as respetivas réplicas de pares e que as respetivas sondas de atividade falhem.

  1. Abra uma nova sessão do Cloud Shell e configure o acesso à linha de comandos kubectl ao cluster principal.

    gcloud container clusters get-credentials $SOURCE_CLUSTER \
    --location=$REGION --project=$PROJECT_ID
    
  2. Veja os eventos do PostgreSQL emitidos no Kubernetes.

    kubectl get events -n postgresql --field-selector=involvedObject.name=postgresql-postgresql-ha-postgresql-0 --watch
    
  3. Na sessão anterior do Cloud Shell, simule uma falha de serviço parando o PostgreSQL repmgr.

    1. Anexe a sua sessão ao contentor da base de dados.

      kubectl exec -it -n $NAMESPACE postgresql-postgresql-ha-postgresql-0 -c postgresql -- /bin/bash
      
    2. Pare o serviço através de repmgr e remova o ponto de verificação e o argumento dry-run.

      export ENTRY='/opt/bitnami/scripts/postgresql-repmgr/entrypoint.sh'
      export RCONF='/opt/bitnami/repmgr/conf/repmgr.conf'
      $ENTRY repmgr -f $RCONF node service --action=stop --checkpoint
      

A sondagem de vitalidade configurada para o contentor PostgreSQL começa a falhar dentro de cinco segundos. Isto repete-se a cada dez segundos até ser atingido o limite de falhas de seis falhas. Quando o valor de failureThreshold é atingido, o contentor é reiniciado. Pode configurar estes parâmetros para diminuir a tolerância da sondagem de atividade para ajustar os requisitos de SLO da sua implementação.

Na stream de eventos, vê que as sondas de atividade e disponibilidade do pod falham e uma mensagem a indicar que o contentor tem de ser reiniciado. O resultado é semelhante ao seguinte:

0s          Normal    Killing                pod/postgresql-postgresql-ha-postgresql-0   Container postgresql failed liveness probe, will be restarted
0s          Warning   Unhealthy              pod/postgresql-postgresql-ha-postgresql-0   Readiness probe failed: psql: error: connection to server at "127.0.0.1", port 5432 failed: Connection refused...
0s          Normal    Pulled                 pod/postgresql-postgresql-ha-postgresql-0   Container image "us-docker.pkg.dev/psch-gke-dev/main/bitnami/postgresql-repmgr:14.5.0-debian-11-r10" already present on machine
0s          Normal    Created                pod/postgresql-postgresql-ha-postgresql-0   Created container postgresql
0s          Normal    Started                pod/postgresql-postgresql-ha-postgresql-0   Started container postgresql

Prepare-se para a recuperação de desastres

Para garantir que as suas cargas de trabalho de produção permanecem disponíveis em caso de um evento que interrompa o serviço, deve preparar um plano de recuperação de desastres (RD). Para saber mais sobre o planeamento de recuperação de desastres, consulte o Guia de planeamento de recuperação de desastres.

A recuperação de desastres para o Kubernetes pode ser implementada em duas fases:

  • A cópia de segurança envolve a criação de um instantâneo do seu estado ou dados num determinado momento antes de ocorrer um evento que interrompa o serviço.
  • A recuperação envolve a restauração do seu estado ou dados a partir de uma cópia de segurança após a ocorrência de um desastre.

Para fazer uma cópia de segurança e restaurar as suas cargas de trabalho em clusters do GKE, pode usar a cópia de segurança do GKE. Pode ativar este serviço em clusters novos e existentes. Isto implementa um agente de cópia de segurança para o GKE que é executado nos seus clusters. O agente é responsável por capturar dados de configuração e de cópia de segurança de volumes, bem como por orquestrar a recuperação.

As cópias de segurança e os restauros podem estar no âmbito de um cluster completo, de um espaço de nomes ou de uma aplicação (definida por seletores como matchLabels).

Exemplo de cenário de cópia de segurança e restauro do PostgreSQL

O exemplo nesta secção mostra como pode realizar uma operação de cópia de segurança e restauro no âmbito da aplicação, usando o ProtectedApplication recurso personalizado.

O diagrama seguinte mostra os recursos de componentes na ProtectedApplication, nomeadamente um StatefulSet que representa a aplicação postgresql-ha e uma implementação de pgpool, que usam a mesma etiqueta (app.kubernetes.io/name: postgresql-ha).

O diagrama mostra um exemplo de solução de cópia de segurança e recuperação para um cluster PostgreSQL de alta disponibilidade.
Figura 2: exemplo de solução de cópia de segurança e recuperação para um cluster PostgreSQL de elevada disponibilidade.

Para se preparar para fazer uma cópia de segurança e restaurar a sua carga de trabalho do PostgreSQL, siga estes passos:

  1. Configure as variáveis de ambiente. Neste exemplo, vai usar uma ProtectedApplication para restaurar a carga de trabalho do PostgreSQL e os respetivos volumes a partir do cluster do GKE de origem (us-central1) e, em seguida, restaurá-los para outro cluster do GKE numa região diferente (us-west1).

    export SOURCE_CLUSTER=cluster-db1
    export TARGET_CLUSTER=cluster-db2
    export REGION=us-central1
    export DR_REGION=us-west1
    export NAME_PREFIX=g-db-protected-app
    export BACKUP_PLAN_NAME=$NAME_PREFIX-bkp-plan-01
    export BACKUP_NAME=bkp-$BACKUP_PLAN_NAME
    export RESTORE_PLAN_NAME=$NAME_PREFIX-rest-plan-01
    export RESTORE_NAME=rest-$RESTORE_PLAN_NAME
    
  2. Verifique se a cópia de segurança do GKE está ativada nos seus clusters. Já deve estar ativado como parte da configuração do Terraform que fez anteriormente.

    gcloud container clusters describe $SOURCE_CLUSTER \
        --project=$PROJECT_ID  \
        --location=$REGION \
        --format='value(addonsConfig.gkeBackupAgentConfig)'
    

    Se a cópia de segurança do GKE estiver ativada, o resultado do comando mostra enabled=True.

Configure um plano de cópia de segurança e faça um restauro

A Cópia de segurança do GKE permite-lhe criar um plano de cópia de segurança como uma tarefa cron. Um plano de cópia de segurança contém uma configuração de cópia de segurança, incluindo o cluster de origem, a seleção das cargas de trabalho das quais fazer uma cópia de segurança e a região na qual os artefactos de cópia de segurança produzidos ao abrigo deste plano são armazenados.

Para fazer uma cópia de segurança e um restauro, siga estes passos:

  1. Valide o estado de ProtectedApplication em cluster-db1.

    kubectl get ProtectedApplication -A
    

    O resultado tem um aspeto semelhante ao seguinte:

    NAMESPACE    NAME            READY TO BACKUP
    postgresql   postgresql-ha   true
    
  2. Crie um plano de cópia de segurança para a ProtectedApplication.

    export NAMESPACE=postgresql
    export PROTECTED_APP=$(kubectl get ProtectedApplication -n $NAMESPACE | grep -v 'NAME' | awk '{ print $1 }')
    
    gcloud beta container backup-restore backup-plans create $BACKUP_PLAN_NAME \
    --project=$PROJECT_ID \
    --location=$DR_REGION \
    --cluster=projects/$PROJECT_ID/locations/$REGION/clusters/$SOURCE_CLUSTER \
    --selected-applications=$NAMESPACE/$PROTECTED_APP \
    --include-secrets \
    --include-volume-data \
    --cron-schedule="0 3 * * *" \
    --backup-retain-days=7 \
    --backup-delete-lock-days=0
    
  3. Crie manualmente uma cópia de segurança.

    gcloud beta container backup-restore backups create $BACKUP_NAME \
    --project=$PROJECT_ID \
    --location=$DR_REGION \
    --backup-plan=$BACKUP_PLAN_NAME \
    --wait-for-completion
    
  4. Configure um plano de restauro.

    gcloud beta container backup-restore restore-plans create $RESTORE_PLAN_NAME \
      --project=$PROJECT_ID \
      --location=$DR_REGION \
      --backup-plan=projects/$PROJECT_ID/locations/$DR_REGION/backupPlans/$BACKUP_PLAN_NAME \
      --cluster=projects/$PROJECT_ID/locations/$DR_REGION/clusters/$TARGET_CLUSTER \
      --cluster-resource-conflict-policy=use-existing-version \
      --namespaced-resource-restore-mode=delete-and-restore \
      --volume-data-restore-policy=restore-volume-data-from-backup \
      --selected-applications=$NAMESPACE/$PROTECTED_APP \
      --cluster-resource-scope-selected-group-kinds="storage.k8s.io/StorageClass","scheduling.k8s.io/PriorityClass"
    
  5. Restaure a partir da cópia de segurança.

    gcloud beta container backup-restore restores create $RESTORE_NAME \
      --project=$PROJECT_ID \
      --location=$DR_REGION \
      --restore-plan=$RESTORE_PLAN_NAME \
      --backup=projects/$PROJECT_ID/locations/$DR_REGION/backupPlans/$BACKUP_PLAN_NAME/backups/$BACKUP_NAME \
      --wait-for-completion
    

Verifique se o cluster foi restaurado

Para verificar se o cluster restaurado tem todos os recursos de Pods, PersistentVolume e StorageClass esperados, siga estes passos:

  1. Configure o kubectl acesso à linha de comandos ao cluster de cópia de segurança cluster-db2.

    gcloud container clusters get-credentials $TARGET_CLUSTER --location $DR_REGION --project $PROJECT_ID
    
  2. Verifique se o StatefulSet está pronto com 3/3 pods.

    kubectl get all -n $NAMESPACE
    

    O resultado é semelhante ao seguinte:

    NAME                                                   READY   STATUS    RESTARTS        AGE
    pod/postgresql-postgresql-ha-pgpool-778798b5bd-k2q4b   1/1     Running   0               4m49s
    pod/postgresql-postgresql-ha-postgresql-0              2/2     Running   2 (4m13s ago)   4m49s
    pod/postgresql-postgresql-ha-postgresql-1              2/2     Running   0               4m49s
    pod/postgresql-postgresql-ha-postgresql-2              2/2     Running   0               4m49s
    
    NAME                                                   TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)    AGE
    service/postgresql-postgresql-ha-pgpool                ClusterIP   192.168.241.46    <none>        5432/TCP   4m49s
    service/postgresql-postgresql-ha-postgresql            ClusterIP   192.168.220.20    <none>        5432/TCP   4m49s
    service/postgresql-postgresql-ha-postgresql-headless   ClusterIP   None              <none>        5432/TCP   4m49s
    service/postgresql-postgresql-ha-postgresql-metrics    ClusterIP   192.168.226.235   <none>        9187/TCP   4m49s
    
    NAME                                              READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/postgresql-postgresql-ha-pgpool   1/1     1            1           4m49s
    
    NAME                                                         DESIRED   CURRENT   READY   AGE
    replicaset.apps/postgresql-postgresql-ha-pgpool-778798b5bd   1         1         1       4m49s
    
    NAME                                                   READY   AGE
    statefulset.apps/postgresql-postgresql-ha-postgresql   3/3     4m49s
    
  3. Verifique se todos os pods no espaço de nomes postgres estão em execução.

    kubectl get pods -n $NAMESPACE
    

    O resultado é semelhante ao seguinte:

    postgresql-postgresql-ha-pgpool-569d7b8dfc-2f9zx   1/1     Running   0          7m56s
    postgresql-postgresql-ha-postgresql-0              2/2     Running   0          7m56s
    postgresql-postgresql-ha-postgresql-1              2/2     Running   0          7m56s
    postgresql-postgresql-ha-postgresql-2              2/2     Running   0          7m56s
    
  4. Valide os PersistentVolumes e a StorageClass. Durante o processo de restauro, o Backup for GKE cria uma classe de proxy na carga de trabalho de destino para substituir a classe de armazenamento aprovisionada na carga de trabalho de origem (gce-pd-gkebackup-dn no exemplo de saída).

    kubectl get pvc -n $NAMESPACE
    

    O resultado é semelhante ao seguinte:

    NAME                                         STATUS   VOLUME                 CAPACITY   ACCESS MODES   STORAGECLASS          AGE
    data-postgresql-postgresql-ha-postgresql-0   Bound    pvc-be91c361e9303f96   8Gi        RWO            gce-pd-gkebackup-dn   10m
    data-postgresql-postgresql-ha-postgresql-1   Bound    pvc-6523044f8ce927d3   8Gi        RWO            gce-pd-gkebackup-dn   10m
    data-postgresql-postgresql-ha-postgresql-2   Bound    pvc-c9e71a99ccb99a4c   8Gi        RWO            gce-pd-gkebackup-dn   10m
    

Valide se os dados esperados foram restaurados

Para validar se os dados esperados foram restaurados, siga estes passos:

  1. Estabeleça ligação à sua instância do PostgreSQL.

    ./scripts/launch-client.sh
    kubectl exec -it pg-client -n postgresql -- /bin/bash
    
  2. Verifique o número de linhas de cada tabela.

    psql -h $HOST_PGPOOL -U postgres -a -q -f /tmp/scripts/count-rows.sql
    select COUNT(*) from tb01;
    

    Deve ver um resultado semelhante aos dados que escreveu anteriormente em Criar um conjunto de dados de teste. O resultado é semelhante ao seguinte:

    300000
    (1 row)
    
  3. Saia do pod do cliente.

    exit