Saltar a contenido

Base de datos con CloudNativePG

Con postgres.mode: cnpg el chart crea, junto a kubelatch, un cluster de Postgres que lleva el operador CloudNativePG. Nadie escribe ni copia una contraseña: kubelatch lee la conexión del Secret que genera el operador.

Los comandos usan $MGMT_KUBECONFIG y $KUBELATCH_VERSION, como en Instalar. Todos los values postgres.cnpg, con su valor por defecto, están en Values de Helm.

Qué crea el chart

Objeto Qué es
Cluster kubelatch-db Postgres 17 (postgres.cnpg.imageName), postgres.cnpg.instances instancias, base de datos kubelatch cuyo dueño es kubelatch. Lleva helm.sh/resource-policy: keep: helm uninstall lo deja en su sitio, con sus volúmenes.
Secret kubelatch-db-app Lo crea el operador. kubelatch toma DATABASE_URL de su clave uri, que apunta al servicio de lectura y escritura kubelatch-db-rw.
Secret kubelatch-db-ca Lo crea el operador: su CA. kubelatch monta solo ca.crt, de solo lectura, para comprobar el certificado del servidor (TLS hacia la base de datos).
NetworkPolicy kubelatch-db El puerto 5432 solo desde los pods de kubelatch, desde las otras instancias y desde los pods del operador: los que llevan la etiqueta app.kubernetes.io/name: cloudnative-pg en postgres.cnpg.operatorNamespace (cnpg-system por defecto). El puerto 8000, el de estado del instance manager, desde las instancias y el operador. Como la de kubelatch, necesita un CNI que aplique NetworkPolicy.
ObjectStore kubelatch-db y ScheduledBackup kubelatch-db-daily Solo con las copias activadas (Copias de seguridad). Se conservan como el Cluster.

Instalar el operador

El chart no instala operadores: CloudNativePG va antes, una vez por cluster. kubelatch se prueba con CloudNativePG 1.30.1; las copias necesitan la 1.26 o superior.

kubectl --kubeconfig "$MGMT_KUBECONFIG" apply --server-side -f \
  https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.30/releases/cnpg-1.30.1.yaml
kubectl --kubeconfig "$MGMT_KUBECONFIG" -n cnpg-system rollout status deploy/cnpg-controller-manager

Su webhook de admisión responde unos segundos después del rollout: dale un momento antes del helm install.

Sin el operador, helm install se para con postgres.mode=cnpg needs the CloudNativePG operator y no crea nada. Si el tuyo vive en otro namespace, ponlo en postgres.cnpg.operatorNamespace para que la NetworkPolicy lo deje pasar. Sus pods deben llevar la etiqueta app.kubernetes.io/name: cloudnative-pg: la ponen tanto el manifiesto del operador como su chart de Helm.

Instalar kubelatch con la base de datos

Sigue Instalar con dos cambios:

  1. En kubelatch-secrets, solo la clave de cifrado (o ningún Secret, con secrets.existingSecret: "" y secrets.generateEncryptionKey: true). El chart toma siempre DATABASE_URL de kubelatch-db-app: una en kubelatch-secrets se ignora, y una en secrets.env (con secrets.create) detiene la instalación.

    kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch create secret generic kubelatch-secrets \
      --from-literal=KUBELATCH_ENCRYPTION_KEY="$(head -c32 /dev/urandom | base64)"
    
  2. En values.yaml:

    postgres:
      mode: cnpg
      cnpg:
        storage:
          size: 10Gi
    

El operador crea la base de datos mientras kubelatch arranca; el pod de kubelatch espera a los Secrets del operador y se reinicia hasta que puede conectar. Espera a los dos:

kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch wait --for=condition=Ready clusters.postgresql.cnpg.io/kubelatch-db --timeout=5m
kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch rollout status deploy/kubelatch --timeout=5m

TLS hacia la base de datos

kubelatch conecta siempre con TLS y comprueba el certificado del servidor y el nombre del host (PGSSLMODE=verify-full) contra la CA del operador. Del Secret kubelatch-db-ca el chart monta solo ca.crt, nunca la clave privada de la CA, en /etc/kubelatch/db-ca/ca.crt (PGSSLROOTCERT).

CloudNativePG renueva su CA por su cuenta (por defecto cada 90 días). kubelatch vuelve a leer el fichero cada vez que cambia, así que las conexiones nuevas comprueban contra la CA renovada sin reiniciar. Si el fichero no se puede leer, las conexiones nuevas fallan: kubelatch nunca recurre a una conexión sin comprobar.

Dimensionado

Value Por defecto Guía
postgres.cnpg.instances 1 Una para evaluación y equipos pequeños; tres para alta disponibilidad (De una instancia a tres).
postgres.cnpg.storage.size 10Gi La auditoría ocupa casi todo: ver Auditoría y retención. CloudNativePG puede ampliar el volumen después si la StorageClass lo permite.
postgres.cnpg.storage.storageClass la del cluster Una clase con discos replicados o con snapshots.
postgres.cnpg.resources ninguno Por ejemplo {requests: {cpu: 250m, memory: 512Mi}, limits: {memory: 1Gi}}. La carga de kubelatch es pequeña: una escritura por petición a través del proxy.
postgres.cnpg.affinity la del operador El bloque affinity de CloudNativePG, por ejemplo {topologyKey: topology.kubernetes.io/zone} para repartir las instancias entre zonas.

idle_in_transaction_session_timeout se queda en 0 (lo de Postgres por defecto), como necesita el reconciliador de kubelatch (Ajustes de Postgres).

Copias de seguridad

Las copias están desactivadas por defecto, y las notas de la instalación lo recuerdan. Activadas, el chart configura el archivado continuo del WAL y una copia base diaria a un almacenamiento de objetos (S3, GCS o Azure Blob) con el Barman Cloud Plugin, la vía soportada de CloudNativePG. El plugin necesita cert-manager y va en el namespace del operador. El test de instalación no cubre las copias: valídalas en tu cluster.

kubectl --kubeconfig "$MGMT_KUBECONFIG" apply -f \
  https://github.com/cloudnative-pg/plugin-barman-cloud/releases/download/v0.15.0/manifest.yaml
kubectl --kubeconfig "$MGMT_KUBECONFIG" -n cnpg-system rollout status deploy/barman-cloud

Pon las credenciales del almacenamiento en un Secret (S3 en este ejemplo) y activa las copias:

kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch create secret generic kubelatch-backup \
  --from-literal=ACCESS_KEY_ID=<id-de-la-clave> --from-literal=ACCESS_SECRET_KEY=<secreto>
postgres:
  mode: cnpg
  cnpg:
    backup:
      enabled: true
      destinationPath: s3://<bucket>/kubelatch
      credentials:
        s3Credentials:
          accessKeyId: {name: kubelatch-backup, key: ACCESS_KEY_ID}
          secretAccessKey: {name: kubelatch-backup, key: ACCESS_SECRET_KEY}
      retentionPolicy: 30d

Para GCS y Azure, credentials lleva googleCredentials o azureCredentials, como los documenta el plugin. schedule (a diario a las 03:00 por defecto) usa la sintaxis cron de CloudNativePG, con segundos.

Comprueba que el WAL se archiva y que las copias terminan:

kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch get clusters.postgresql.cnpg.io kubelatch-db -o jsonpath='{.status.conditions[?(@.type=="ContinuousArchiving")].status}'
kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch get backups.postgresql.cnpg.io

La clave no está en la copia

Una copia de Postgres sin KUBELATCH_ENCRYPTION_KEY no permite usar los tokens de los clusters. Guarda la clave en tu gestor de secretos, aparte del almacenamiento de objetos.

El ObjectStore kubelatch-db y el ScheduledBackup kubelatch-db-daily llevan helm.sh/resource-policy: keep, como el Cluster. Se quedan tras helm uninstall, al dejar postgres.mode: cnpg y al desactivar las copias. Un Cluster que se queda sigue archivando el WAL en ese almacenamiento: sin él, el archivado fallaría y pg_wal llenaría el volumen. Para parar las copias del todo, sigue Desinstalar y los datos.

Restaurar desde una copia

Una restauración crea un Cluster nuevo a partir del almacenamiento de objetos. El Cluster del chart se llama siempre kubelatch-db, así que el dañado va primero.

  1. Comprueba desde qué vas a restaurar. Al menos una copia debe estar completed, y el archivado del WAL debe decir True:

    kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch get backups.postgresql.cnpg.io
    kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch get clusters.postgresql.cnpg.io kubelatch-db -o jsonpath='{.status.conditions[?(@.type=="ContinuousArchiving")].status}'
    

    Si la base de datos todavía se puede leer, haz además un volcado: el paso siguiente borra sus volúmenes para siempre.

    primary=$(kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch get clusters.postgresql.cnpg.io kubelatch-db -o jsonpath='{.status.currentPrimary}')
    kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch exec "$primary" -c postgres -- pg_dump -Fc kubelatch > kubelatch.dump
    
  2. Para kubelatch y borra el Cluster dañado (sus volúmenes se van con él):

    kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch scale deploy/kubelatch --replicas=0
    kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch delete clusters.postgresql.cnpg.io kubelatch-db
    
  3. Añade la recuperación a tus values. El Cluster nuevo lee las copias viejas (serverName: kubelatch-db) y archiva en una carpeta nueva (backup.serverName). Cada restauración usa una carpeta nueva (kubelatch-db-2, luego kubelatch-db-3…), porque CloudNativePG se niega a archivar en una carpeta que ya tiene un archivo:

    postgres:
      mode: cnpg
      cnpg:
        bootstrap:
          recovery:
            source: origin
            # A un momento concreto: recoveryTarget: {targetTime: "2026-09-26 10:00:00+00"}
        externalClusters:
          - name: origin
            plugin:
              name: barman-cloud.cloudnative-pg.io
              parameters:
                barmanObjectName: kubelatch-db
                serverName: kubelatch-db
        backup:
          enabled: true
          serverName: kubelatch-db-2
          # destinationPath y credentials como antes
    

    postgres.cnpg.bootstrap lleva exactamente un método: initdb, recovery o pg_basebackup. Deja fuera database, owner y secret: el chart pone kubelatch en los dos primeros y rechaza cualquier otro valor (Solución de problemas).

    initdb con import sirve también para llevar un Postgres existente a CloudNativePG: apunta postgres.cnpg.externalClusters a tu base de datos actual y sigue la documentación de importación de CloudNativePG.

  4. Aplícalo y espera:

    helm --kubeconfig "$MGMT_KUBECONFIG" upgrade kubelatch oci://ghcr.io/picaportelabs/charts/kubelatch \
      --version "$KUBELATCH_VERSION" -n kubelatch -f values.yaml
    kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch wait --for=condition=Ready clusters.postgresql.cnpg.io/kubelatch-db --timeout=30m
    kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch rollout status deploy/kubelatch
    

    El upgrade devuelve replicas al valor del chart. El operador escribe una contraseña nueva en kubelatch-db-app: si los pods arrancaron antes, reinícialos con kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch rollout restart deploy/kubelatch.

  5. Haz enseguida una copia base. Hasta entonces la carpeta nueva solo tiene WAL y no se puede restaurar desde ella, y el ScheduledBackup diario solo hizo su copia inmediata cuando se creó:

    kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch create -f - <<'EOF'
    apiVersion: postgresql.cnpg.io/v1
    kind: Backup
    metadata:
      generateName: kubelatch-db-after-restore-
    spec:
      cluster:
        name: kubelatch-db
      method: plugin
      pluginConfiguration:
        name: barman-cloud.cloudnative-pg.io
    EOF
    kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch get backups.postgresql.cnpg.io
    
  6. Deja bootstrap y externalClusters en tus values: CloudNativePG solo los lee al crear el Cluster. La próxima vez, restaura desde kubelatch-db-2 y archiva en kubelatch-db-3.

Prueba el procedimiento antes de necesitarlo, en otro namespace u otro cluster, con la misma clave de cifrado y un backup.serverName desechable (u otro bucket o ruta). Una restauración de prueba nunca debe archivar en una carpeta que producción usa o va a usar después: CloudNativePG se niega a archivar en una carpeta que ya tiene un archivo, así que el Cluster que llegue segundo no consigue archivar el WAL y su pg_wal llena el volumen.

Actualizar Postgres

  • Versión menor (17.5 → 17.6): cambia postgres.cnpg.imageName a la etiqueta nueva y ejecuta helm upgrade. El operador reinicia las instancias una a una; con tres, mueve antes el primario (unos segundos sin escrituras). La etiqueta móvil por defecto (17-standard-trixie) no reinicia nada por su cuenta: fija una versión menor (17.6-standard-trixie) para decidir cuándo.
  • Versión mayor (17 → 18): cambia imageName a una imagen 18 y ejecuta helm upgrade. CloudNativePG (1.26 o superior) actualiza en el sitio con pg_upgrade: para las instancias, así que kubelatch responde 503 hasta que termina. Haz antes una copia; no puedes volver a la 17 sin restaurarla.

De una instancia a tres

postgres:
  mode: cnpg
  cnpg:
    instances: 3

helm upgrade añade dos réplicas, clonadas del primario. Si el primario falla, el operador promueve una réplica y mueve a ella el servicio kubelatch-db-rw; kubelatch vuelve a conectar solo (las peticiones durante el cambio reciben 503). El operador crea también el PodDisruptionBudget de las instancias.

Desinstalar y los datos

helm uninstall deja en su sitio el Cluster kubelatch-db, sus volúmenes y sus Secrets, y también la clave de cifrado generada. Con las copias activadas se quedan además el ObjectStore y el ScheduledBackup, que siguen archivando el WAL y haciendo la copia diaria. Instalar de nuevo con el mismo nombre de release y el mismo namespace los recupera, con todos los datos.

El keep de Helm solo protege frente a helm uninstall. Las herramientas GitOps aplican los manifiestos por su cuenta, así que el chart pone además en estos objetos el argocd.argoproj.io/sync-options: Delete=false,Prune=false de Argo CD. Con otra herramienta, comprueba que nunca poda el Cluster ni el Secret de la clave.

La NetworkPolicy kubelatch-db no se conserva: tras helm uninstall, cualquier pod puede llegar al puerto de la base de datos (sigue pidiendo la contraseña) hasta que instales de nuevo o borres el Cluster.

Para borrar la base de datos del todo, después de exportar lo que necesites:

kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch delete clusters.postgresql.cnpg.io kubelatch-db

Con las copias activadas, páralas en este orden, para que no empiece ninguna copia base nueva y el archivado del WAL termine antes de que desaparezca la definición del almacenamiento de objetos:

  1. Borra la copia diaria:

    kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch delete scheduledbackups.postgresql.cnpg.io kubelatch-db-daily
    
  2. Si conservas el Cluster, pon postgres.cnpg.backup.enabled: false y ejecuta helm upgrade con los mismos values: el chart quita el plugin del Cluster y el archivado del WAL se para. Si no, borra el Cluster como arriba.

  3. Borra la definición del almacenamiento de objetos:

    kubectl --kubeconfig "$MGMT_KUBECONFIG" -n kubelatch delete objectstores.barmancloud.cnpg.io kubelatch-db
    

Las copias que ya están en el almacenamiento de objetos se quedan allí hasta que las borres.