Implementa PostgreSQL en GKE con CloudNativePG


En la guía, se muestra cómo implementar clústeres de PostgreSQL en Google Kubernetes Engine (GKE) mediante el operador CloudNativePG.

PostgreSQL es una base de datos relacional de objetos de código abierto con varias décadas de desarrollo activo, lo que garantiza un rendimiento estable del cliente. Ofrece una variedad de funciones, incluidas la replicación, la recuperación de un momento determinado, las funciones de seguridad y la extensión. PostgreSQL es compatible con los principales sistemas operativos y cumple por completo con los estándares ACID (atomicidad, coherencia, aislamiento y durabilidad).

Esta guía está dirigida a administradores de plataformas, arquitectos de nube y profesionales de operaciones interesados en implementar clústeres de Postgres en GKE. Ejecutar Postgres en GKE en lugar de usar Cloud SQL puede brindar más flexibilidad y control de configuración a los administradores de bases de datos experimentados.

Ventajas

CloudNativePG es un operador de código abierto desarrollado por EDB con una licencia Apache 2. Brinda las siguientes funciones en la implementación de PostgreSQL:

  • Una forma declarativa y nativa de Kubernetes de administrar, configurar los clústeres de PostgreSQL
  • Administración de copias de seguridad mediante instantáneas de volumen o Cloud Storage
  • La conexión de encriptado en tránsito TLS, la capacidad de usar tu propia autoridad certificadora e integrarse en el Administrador de certificados para la rotación y emisión de certificados TLS automatizada
  • Actualizaciones progresivas para versiones secundarias de PostgreSQL
  • Uso del servidor de la API de Kubernetes para mantener el estado del clúster de PostgreSQL y conmutaciones por error para lograr una alta disponibilidad sin necesidad de herramientas adicionales
  • Una configuración integrada de exportador de Prometheus a través de métricas definidas por el usuario escritas en SQL

Objetivos

  • Planificar y, además, implementar la infraestructura de GKE para Postgres
  • Implementa y configura el operador de CloudNativePG Postgres con Helm
  • Implementa un clúster de PostgreSQL
  • Configura la autenticación y la observabilidad de PostgreSQL

Arquitectura de implementación

PostgreSQL tiene varias opciones de implementación desde un servidor de base de datos independiente hasta un clúster replicado con alta disponibilidad. Este instructivo se centra en la implementación del clúster con alta disponibilidad en GKE.

En esta implementación, las cargas de trabajo del clúster de PostgreSQL se distribuyen en varias zonas de disponibilidad dentro del clúster de GKE regional, lo que garantiza una alta disponibilidad y redundancia. Para obtener más información, consulta Clústeres regionales.

En el siguiente diagrama, se muestra un clúster de Postgres que se ejecuta en varios nodos y zonas en un clúster de GKE:

Clúster de Postgres en GKE

  • La configuración predeterminada incluye un servidor principal de PostgreSQL y dos servidores de copia de seguridad listos para actuar en caso de que el servidor principal falle, lo que garantiza una disponibilidad continua de la base de datos.

  • Los recursos del operador CloudNativePG usan un espacio de nombres independiente del clúster de GKE para obtener un mejor aislamiento de recursos y el enfoque de microservicios recomendado de una base de datos por clúster de PostgreSQL. La base de datos y su usuario correspondiente (usuario de la app) se definen en el recurso personalizado de Kubernetes que representa el clúster.

  • El almacenamiento es un componente fundamental cuando se analizan las bases de datos. El almacenamiento debe tener un rendimiento eficiente, garantizar la disponibilidad continua y la coherencia de los datos. Por estos motivos, recomendamos la clase de almacenamiento premium-rwo, que se basa en discos SSD. El operador CloudNativePG crea automáticamente PersistentVolumeClaims según sea necesario cuando se configuran Pods para el Clúster de PostgreSQL.

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios. Es posible que los usuarios nuevos de Google Cloud califiquen para obtener una prueba gratuita.

Cuando finalices las tareas que se describen en este documento, puedes borrar los recursos que creaste para evitar que continúe la facturación. Para obtener más información, consulta Cómo realizar una limpieza.

Antes de comenzar

Cloud Shell tiene preinstalado el software que necesitas para este instructivo, incluido kubectl, gcloud CLI, Helm y Terraform. Si no usas Cloud Shell, debes instalar gcloud CLI.

  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. Install the Google Cloud CLI.
  3. To initialize the gcloud CLI, run the following command:

    gcloud init
  4. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

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

  6. Enable the Compute Engine, IAM, GKE, Resource Manager APIs:

    gcloud services enable compute.googleapis.com iam.googleapis.com container.googleapis.com cloudresourcemanager.googleapis.com
  7. Install the Google Cloud CLI.
  8. To initialize the gcloud CLI, run the following command:

    gcloud init
  9. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

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

  11. Enable the Compute Engine, IAM, GKE, Resource Manager APIs:

    gcloud services enable compute.googleapis.com iam.googleapis.com container.googleapis.com cloudresourcemanager.googleapis.com
  12. Grant roles to your user account. Run the following command once for each of the following IAM roles: roles/compute.securityAdmin, roles/compute.viewer, roles/container.clusterAdmin, roles/container.admin, roles/iam.serviceAccountAdmin, roles/iam.serviceAccountUser

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

    • Replace ROLE with each individual role.

Configura tu entorno

Para configurar tu entorno, sigue estos pasos:

  1. Establece las variables de entorno:

    export PROJECT_ID=PROJECT_ID
    export KUBERNETES_CLUSTER_PREFIX=postgres
    export REGION=us-central1
    

    Reemplaza PROJECT_ID por el ID del proyecto de Google Cloud.

  2. Clona el repositorio de GitHub:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  3. Cambia al directorio de trabajo:

    cd kubernetes-engine-samples/databases/postgresql-cloudnativepg
    

Crea la infraestructura del clúster

En esta sección, debes ejecutar una secuencia de comandos de Terraform para crear un clúster de GKE privado, regional y con alta disponibilidad.

Puedes instalar el operador mediante un clúster de Standard o Autopilot.

Estándar

En el siguiente diagrama, se muestra un clúster de GKE estándar regional privado implementado en tres zonas diferentes:

Para implementar esta infraestructura, ejecuta los siguientes comandos:

export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=terraform/gke-standard init
terraform -chdir=terraform/gke-standard apply \
-var project_id=${PROJECT_ID}   \
-var region=${REGION}  \
-var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}

Cuando se te solicite, escribe yes. Es posible que este comando tarde varios minutos en completarse y que el clúster muestre un estado de preparación.

Terraform crea los siguientes recursos:

  • Una red de VPC y una subred privada para los nodos de Kubernetes
  • Un router para acceder a Internet a través de NAT
  • Un clúster de GKE privado en la región us-central1
  • Un grupo de nodos con ajuste de escala automático habilitado (de uno a dos nodos por zona y un nodo por zona como mínimo)

El resultado es similar al siguiente:

...
Apply complete! Resources: 14 added, 0 changed, 0 destroyed.
...

Autopilot

En el siguiente diagrama, se muestra un clúster de GKE de Autopilot regional privado:

Para implementar la infraestructura, ejecuta los siguientes comandos:

export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=terraform/gke-autopilot init
terraform -chdir=terraform/gke-autopilot apply \
-var project_id=${PROJECT_ID} \
-var region=${REGION} \
-var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}

Cuando se te solicite, escribe yes. Es posible que este comando tarde varios minutos en completarse y que el clúster muestre un estado de preparación.

Terraform crea los siguientes recursos:

  • Una red de VPC y una subred privada para los nodos de Kubernetes
  • Un router para acceder a Internet a través de NAT
  • Un clúster de GKE privado en la región us-central1
  • Un ServiceAccount con permiso de registro y supervisión
  • Google Cloud Managed Service para Prometheus para la supervisión de clústeres

El resultado es similar al siguiente:

...
Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
...

Conéctate al clúster

Configura kubectl para comunicarse con el clúster:

gcloud container clusters get-credentials ${KUBERNETES_CLUSTER_PREFIX}-cluster --region ${REGION}

Implementa el operador CloudNativePG

Implementa CloudNativePG en tu clúster de Kubernetes con un gráfico de Helm:

  1. Agrega el repositorio de gráficos de Helm para operadores de CloudNativePG:

    helm repo add cnpg https://cloudnative-pg.github.io/charts
    
  2. Implementa el operador de CloudNativePG con la herramienta de línea de comandos de Helm:

    helm upgrade --install cnpg \
        --namespace cnpg-system \
        --create-namespace \
        cnpg/cloudnative-pg
    

    El resultado es similar al siguiente:

    Release "cnpg" does not exist. Installing it now.
    NAME: cnpg
    LAST DEPLOYED: Fri Oct 13 13:52:36 2023
    NAMESPACE: cnpg-system
    STATUS: deployed
    REVISION: 1
    TEST SUITE: None
    ...
    

Implementa Postgres

En el siguiente manifiesto, se describe un clúster de PostgreSQL, tal como se define en el recurso personalizado del operador CloudNativePG:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: gke-pg-cluster
spec:
  description: "Standard GKE PostgreSQL cluster"
  imageName: ghcr.io/cloudnative-pg/postgresql:16.2
  enableSuperuserAccess: true
  instances: 3
  startDelay: 300
  primaryUpdateStrategy: unsupervised
  postgresql:
    pg_hba:
      - host all all 10.48.0.0/20 md5
  bootstrap:
    initdb:
      database: app
  storage:
    storageClass: premium-rwo
    size: 2Gi
  resources:
    requests:
      memory: "1Gi"
      cpu: "1000m"
    limits:
      memory: "1Gi"
      cpu: "1000m"
  affinity:
    enablePodAntiAffinity: true
    tolerations:
    - key: cnpg.io/cluster
      effect: NoSchedule
      value: gke-pg-cluster
      operator: Equal
    additionalPodAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app.component
              operator: In
              values:
              - "pg-cluster"
          topologyKey: topology.kubernetes.io/zone
  monitoring:
    enablePodMonitor: true

Este manifiesto tiene los siguientes campos:

  • spec.instances: la cantidad de Pods del clúster
  • spec.primaryUpdateStrategy: la estrategia de actualización progresiva:
    • Unsupervised: actualiza de forma autónoma el nodo del clúster principal después de los nodos de réplica
    • Supervised: se requiere un cambio manual para el nodo del clúster principal
  • spec.postgresql: Anulaciones de parámetros del archivo postgres.conf, como las reglas pg-hba, LDAP y los requisitos para que se cumplan las réplicas de sincronización.
  • spec.storage: la configuración relacionada con el almacenamiento, como la clase de almacenamiento, el tamaño del volumen y la configuración de registro de escritura por adelantado.
  • spec.bootstrap: parámetros de la base de datos inicial creada en el clúster, credenciales de usuario y opciones de restablecimiento de la base de datos
  • spec.resources: solicitudes y límites para Pods del clúster
  • spec.affinity: reglas de afinidad y antiafinidad de las cargas de trabajo del clúster

Crea un clúster básico de Postgres

  1. Crea un espacio de nombres:

    kubectl create ns pg-ns
    
  2. Crea el clúster de PostgreSQL mediante el recurso personalizado:

    kubectl apply -n pg-ns -f manifests/01-basic-cluster/postgreSQL_cluster.yaml
    

    Este comando puede tardar varios minutos en completarse.

  3. Verifica el estado del clúster:

    kubectl get cluster -n pg-ns --watch
    

    Espera a que el resultado muestre un estado de Cluster in healthy state antes de continuar con el siguiente paso.

    NAME             AGE     INSTANCES   READY   STATUS                     PRIMARY
    gke-pg-cluster   2m53s   3           3       Cluster in healthy state   gke-pg-cluster-1
    

Inspecciona los recursos

Confirma que GKE creó los recursos para el clúster:

kubectl get cluster,pod,svc,pvc,pdb,secret,cm -n pg-ns

El resultado es similar al siguiente:

NAME                                        AGE   INSTANCES   READY   STATUS                     PRIMARY
cluster.postgresql.cnpg.io/gke-pg-cluster   32m   3           3       Cluster in healthy state   gke-pg-cluster-1

NAME                   READY   STATUS    RESTARTS   AGE
pod/gke-pg-cluster-1   1/1     Running   0          31m
pod/gke-pg-cluster-2   1/1     Running   0          30m
pod/gke-pg-cluster-3   1/1     Running   0          29m

NAME                        TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/gke-pg-cluster-r    ClusterIP   10.52.11.24   <none>        5432/TCP   32m
service/gke-pg-cluster-ro   ClusterIP   10.52.9.233   <none>        5432/TCP   32m
service/gke-pg-cluster-rw   ClusterIP   10.52.1.135   <none>        5432/TCP   32m

NAME                                     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/gke-pg-cluster-1   Bound    pvc-bbdd1cdd-bdd9-4e7c-8f8c-1a14a87e5329   2Gi        RWO            standard       32m
persistentvolumeclaim/gke-pg-cluster-2   Bound    pvc-e7a8b4df-6a3e-43ce-beb0-b54ec1d24011   2Gi        RWO            standard       31m
persistentvolumeclaim/gke-pg-cluster-3   Bound    pvc-dac7f931-6ac5-425f-ac61-0cfc55aae72f   2Gi        RWO            standard       30m

NAME                                                MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
poddisruptionbudget.policy/gke-pg-cluster           1               N/A               1                     32m
poddisruptionbudget.policy/gke-pg-cluster-primary   1               N/A               0                     32m

NAME                                TYPE                       DATA   AGE
secret/gke-pg-cluster-app           kubernetes.io/basic-auth   3      32m
secret/gke-pg-cluster-ca            Opaque                     2      32m
secret/gke-pg-cluster-replication   kubernetes.io/tls          2      32m
secret/gke-pg-cluster-server        kubernetes.io/tls          2      32m
secret/gke-pg-cluster-superuser     kubernetes.io/basic-auth   3      32m

NAME                                DATA   AGE
configmap/cnpg-default-monitoring   1      32m
configmap/kube-root-ca.crt          1      135m

El operador crea los siguientes recursos:

  • Un recurso de clúster personalizado que representa el clúster de PostgreSQL y que controla el operador
  • Recursos PersistentVolumeClaim con Volúmenes persistentes correspondientes
  • Secrets con credenciales de usuario para acceder a la base de datos y replicación entre los nodos de Postgres.
  • Tres servicios de extremo de base de datos: <name>-rw, <name>-ro y <name>-r para conectarse al clúster. Para obtener más información, consulta Arquitectura de PostgreSQL.

Autentícate en Postgres

Puedes conectarte a la base de datos de PostgreSQL y verificar el acceso a través de diferentes extremos de servicio creados por el operador. Para hacerlo, usa un pod adicional con un cliente PostgreSQL y credenciales de usuario de la aplicación sincronizadas activadas como variables de entorno.

  1. Ejecuta el Pod de cliente para interactuar con tu clúster de Postgres:

    kubectl apply -n pg-ns -f manifests/02-auth/pg-client.yaml
    
  2. Ejecuta un comando exec en el Pod pg-client y accede al Service gke-pg-cluster-rw:

    kubectl wait --for=condition=Ready -n pg-ns pod/pg-client --timeout=300s
    kubectl exec -n pg-ns -i -t pg-client -- /bin/sh
    
  3. Accede a la base de datos con el servicio gke-pg-cluster-rw para establecer una conexión con privilegios de lectura y escritura:

    psql postgresql://$CLIENTUSERNAME:$CLIENTPASSWORD@gke-pg-cluster-rw.pg-ns/app
    

    La terminal empieza con el nombre de tu base de datos:

    app=>
    
  4. Crear una tabla:

    CREATE TABLE travel_agency_clients (
    client VARCHAR ( 50 ) UNIQUE NOT NULL,
    address VARCHAR ( 50 ) UNIQUE NOT NULL,
    phone VARCHAR ( 50 ) UNIQUE NOT NULL);
    
  5. Inserta datos en la tabla:

    INSERT INTO travel_agency_clients(client, address, phone)
    VALUES ('Tom', 'Warsaw', '+55555')
    RETURNING *;
    
  6. Visualiza los datos que creaste:

    SELECT * FROM travel_agency_clients ;
    

    El resultado es similar al siguiente:

    client | address |  phone
    --------+---------+---------
    Tom    | Warsaw  | +55555
    (1 row)
    
  7. Sal de la sesión actual de la base de datos:

    exit
    
  8. Accede a la base de datos con el servicio gke-pg-cluster-ro para la verificación del acceso de solo lectura. Este servicio permite consultar datos, pero restringe las operaciones de escritura:

    psql postgresql://$CLIENTUSERNAME:$CLIENTPASSWORD@gke-pg-cluster-ro.pg-ns/app
    
  9. Intenta insertar datos nuevos:

    INSERT INTO travel_agency_clients(client, address, phone)
    VALUES ('John', 'Paris', '+55555')
    RETURNING *;
    

    El resultado es similar al siguiente:

    ERROR:  cannot execute INSERT in a read-only transaction
    
  10. Intenta leer datos:

    SELECT * FROM travel_agency_clients ;
    

    El resultado es similar al siguiente:

    client | address |  phone
    --------+---------+---------
    Tom    | Warsaw  | +55555
    (1 row)
    
  11. Sal de la sesión actual de la base de datos:

    exit
    
  12. Sal de la shell del Pod:

    exit
    

Comprende cómo Prometheus recopila métricas para tu clúster de Postgres

En el siguiente diagrama, se muestra cómo funciona la recopilación de métricas de Prometheus:

En el diagrama, un clúster privado de GKE contiene lo siguiente:

  • Un Pod de Postgres que recopila métricas en la ruta de acceso / y el puerto 9187
  • Recopiladores basados en Prometheus que procesan las métricas del Pod de Postgres
  • Un recurso PodMonitoring que envía métricas a Cloud Monitoring

Para habilitar la recopilación de métricas de tus Pods, sigue estos pasos:

  1. Crea el recurso PodMonitoring:

    kubectl apply -f manifests/03-observability/pod-monitoring.yaml -n pg-ns
    
  2. En la consola de Google Cloud, ve a la página Explorador de métricas:

    Dirígete al Explorador de métricas

    En el panel, se muestra una tasa de transferencia de métricas distinta de cero.

  3. En Selecciona una métrica, ingresa Prometheus Target.

  4. En la sección Categorías de métricas activas, selecciona Cnpg.

Crea un panel de métricas

Para visualizar las métricas exportadas, crea un panel de métricas.

  1. Implementa un panel:

    gcloud --project "${PROJECT_ID}" monitoring dashboards create --config-from-file manifests/03-observability/gcp-pg.json
    
  2. En la consola de Google Cloud, ve a la página Paneles.

    Ir a Paneles

  3. Selecciona el panel PostgreSQL Prometheus Overview.

    Para revisar cómo los paneles supervisan las funciones, puedes reutilizar las acciones de la Autenticación de base de datos y aplicar solicitudes de lectura y escritura en la base de datos y, luego, revisa la visualización de las métricas recopiladas en un panel.

  4. Conéctate al Pod cliente:

    kubectl exec -n pg-ns -i -t pg-client -- /bin/sh
    
  5. Inserta datos aleatorios:

    psql postgresql://$CLIENTUSERNAME:$CLIENTPASSWORD@gke-pg-cluster-rw.pg-ns/app -c "CREATE TABLE test (id serial PRIMARY KEY, randomdata VARCHAR ( 50 ) NOT NULL);INSERT INTO test (randomdata) VALUES (generate_series(1, 1000));"
    
  6. Actualiza el panel. Los grafos se actualizan con métricas actualizadas.

  7. Sal de la shell del Pod:

    exit
    

Realiza una limpieza

Borra el proyecto

    Delete a Google Cloud project:

    gcloud projects delete PROJECT_ID

Borra los recursos individuales

  1. Configurar variables de entorno

    export PROJECT_ID=${PROJECT_ID}
    export KUBERNETES_CLUSTER_PREFIX=postgres
    export REGION=us-central1
    
  2. Ejecuta el comando terraform destroy:

    export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
    terraform  -chdir=terraform/FOLDER destroy \
      -var project_id=${PROJECT_ID} \
      -var region=${REGION} \
      -var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}
    

    Reemplaza FOLDER por gke-autopilot o gke-standard.

    Cuando se te solicite, escribe yes.

  3. Busca todos los discos no conectados:

    export disk_list=$(gcloud compute disks list --filter="-users:* AND labels.name=${KUBERNETES_CLUSTER_PREFIX}-cluster" --format "value[separator=|](name,zone)")
    
  4. Borra los discos:

    for i in $disk_list; do
      disk_name=$(echo $i| cut -d'|' -f1)
      disk_zone=$(echo $i| cut -d'|' -f2|sed 's|.*/||')
      echo "Deleting $disk_name"
      gcloud compute disks delete $disk_name --zone $disk_zone --quiet
    done
    

¿Qué sigue?

  • Explora arquitecturas de referencia, diagramas y prácticas recomendadas sobre Google Cloud. Consulta nuestro Cloud Architecture Center.