diff --git a/product_docs/docs/postgres_for_kubernetes/1/backup.mdx b/product_docs/docs/postgres_for_kubernetes/1/backup.mdx index a89b0e2085..0bbf61befd 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/backup.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/backup.mdx @@ -33,7 +33,9 @@ two main ways: - **Natively**, with support for: - [Object storage via Barman Cloud](backup_barmanobjectstore.md) - *(although deprecated from 1.26 in favor of the Barman Cloud Plugin)* + *(deprecated from 1.26 in favor of the Barman Cloud Plugin; 1.31 is the + last minor release with native support, which is removed starting from + 1.32.0)* - [Kubernetes Volume Snapshots](backup_volumesnapshot.md), if supported by the underlying storage class @@ -126,6 +128,7 @@ for your disaster recovery plans. - **Object store–based backups**, via the [**Barman Cloud Plugin**](https://cloudnative-pg.io/plugin-barman-cloud/) or the [**deprecated native integration**](backup_barmanobjectstore.md) + (removed starting from 1.32.0) - [**Volume Snapshots**](backup_volumesnapshot.md), using the Kubernetes CSI interface and supported storage classes @@ -386,7 +389,18 @@ and on-demand backups: - `barmanObjectStore` – Uses [Barman Cloud for object storage](backup_barmanobjectstore.md) *(deprecated starting with v1.26 in favor of the [Barman Cloud Plugin](https://cloudnative-pg.io/plugin-barman-cloud/), - but still the default for backward compatibility)* + but still the default for backward compatibility; 1.31 is the last minor + release with native support, which is removed starting from 1.32.0)* + +!!!warning + +Native Barman Cloud support, including the `barmanObjectStore` backup +method and the `.spec.backup.barmanObjectStore` stanza, is removed starting +from {{name.ln}} 1.32.0. Releases up to and including 1.31, and their +patch releases, keep native support for as long as they are supported. +Follow [Migrating from Built-in {{name.ln}} Backup](https://cloudnative-pg.io/plugin-barman-cloud/docs/migration/) +to move to the Barman Cloud Plugin before upgrading to 1.32 or later. +!!! Specify the method using the `.spec.method` field (defaults to `barmanObjectStore`). diff --git a/product_docs/docs/postgres_for_kubernetes/1/backup_barmanobjectstore.mdx b/product_docs/docs/postgres_for_kubernetes/1/backup_barmanobjectstore.mdx index ea01fb9633..d475a04923 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/backup_barmanobjectstore.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/backup_barmanobjectstore.mdx @@ -8,9 +8,12 @@ originalFilePath: 'src/appendixes/backup_barmanobjectstore.md' !!!warning As of {{name.ln}} 1.26, **native Barman Cloud support is deprecated** in favor of the **Barman Cloud Plugin**. This page has been moved to the appendix -for reference purposes. While the native integration remains functional for -now, we strongly recommend beginning a gradual migration to the plugin-based -interface after appropriate testing. For guidance, see +for reference purposes. **{{name.ln}} 1.31 is the last minor release that +ships with native Barman Cloud support, which is removed starting from +1.32.0.** Releases up to and including 1.31, and their patch releases, keep +native support for as long as they are supported. You must migrate to the +plugin before upgrading to 1.32 or later. We strongly recommend migrating to +the plugin-based interface after appropriate testing. For guidance, see [Migrating from Built-in {{name.ln}} Backup](https://cloudnative-pg.io/plugin-barman-cloud/docs/migration/). !!! diff --git a/product_docs/docs/postgres_for_kubernetes/1/bootstrap.mdx b/product_docs/docs/postgres_for_kubernetes/1/bootstrap.mdx index bceeaacea5..f46fe0e3ef 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/bootstrap.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/bootstrap.mdx @@ -553,7 +553,10 @@ the operation has been thoroughly tested. The {{name.ln}} community does not endorse this approach for such use cases, and recommends using logical import instead. It is exceedingly rare that all requirements for physical replication are met in a -way that seamlessly works with {{name.ln}}. +way that seamlessly works with {{name.ln}}. If you still need to go down +this path, we strongly recommend relying on one of the +[professional support companies](https://cloudnative-pg.io/support/) +listed on the {{name.ln}} website. !!! !!!warning @@ -591,7 +594,10 @@ The following requirements apply to the `pg_basebackup` bootstrap method: - target and source must have the same hardware architecture - target and source must have the same major PostgreSQL version -- target and source must have the same tablespaces +- target and source must have the same tablespaces (same names), with the + locations in the source mapped through `tablespaceMapping` when they differ + from the {{name.ln}} ones (see + [Mapping tablespaces from the source](#mapping-tablespaces-from-the-source)) - source must be configured with enough `max_wal_senders` to grant access from the target for this one-off operation by providing at least one *walsender* for the backup plus one for WAL streaming @@ -798,6 +804,109 @@ completed**: password for the application user (the `app` user in this case) will be updated to the `password` value in the secret. +#### Passing additional options to `pg_basebackup` + +This is an advanced option. You can append custom options to the +`pg_basebackup` command built by the operator with +`.spec.bootstrap.pg_basebackup.additionalArgs`, for example to reduce the +impact on the source server or to speed up the copy of a very large data +directory: + +```yaml +spec: + bootstrap: + pg_basebackup: + source: cluster-example + additionalArgs: + - --max-rate=50M + - --checkpoint=fast + - --sync-method=syncfs +``` + +In this example: + +- `--max-rate=50M` limits the transfer rate, reducing the load on the source +- `--checkpoint=fast` avoids waiting for a spread checkpoint on the source +- `--sync-method=syncfs` avoids a slow per-file `fsync` phase at the end of the + copy (PostgreSQL 17 and later only) + +!!!warning + +The operator rejects the options it relies on: the target directory and output +format (`-D`/`--pgdata`, `-F`/`--format`, `-R`/`--write-recovery-conf`, +`-X`/`--wal-method`, `--waldir`, `-t`/`--target`), the connection parameters +(`-d`/`--dbname`, `-h`/`--host`, `-p`/`--port`, `-U`/`--username`, +`-w`/`--no-password`, `-W`/`--password`, taken from `externalClusters`), the +tablespace relocation (`-T`/`--tablespace-mapping`), the creation of a +permanent replication slot on the source (`-C`/`--create-slot`), which the new +cluster would never use, and `--help` and `--version`. Abbreviated long options +that match one of them are rejected too. + +All the other options are passed as they are, and some of them may still +interfere with the operator. You are responsible for the arguments and for +their compatibility with the PostgreSQL version in use. The project does not +test combinations of `pg_basebackup` options. +!!! + +This setting only applies to the bootstrap of the cluster. It does not affect +the `pg_basebackup` executions used to join new replicas. + +#### Mapping tablespaces from the source + +If the source instance uses tablespaces, declare each of them in +`spec.tablespaces`, using the same name it has in the source instance. +`pg_basebackup` relocates tablespaces by path and not by name, so if the source +does not already use the {{name.ln}} location for a tablespace, map it with +`tablespaceMapping`, which associates the name of the tablespace with its +location in the source instance: + +```yaml +spec: + tablespaces: + - name: data_ts + storage: + size: 1Gi + bootstrap: + pg_basebackup: + source: external-postgres + tablespaceMapping: + - name: data_ts + sourceLocation: /data/pg/data_ts +``` + +!!!warning + +Use `tablespaceMapping` with caution, as it is meant for sources that are not +managed by {{name.ln}}, such as an instance running outside Kubernetes. As +explained in the [introduction of this method](#bootstrap-from-a-live-cluster-pg_basebackup), +the {{name.ln}} community does not endorse that kind of migration, and +recommends logical import instead. If you still need it, we strongly +recommend relying on one of the +[professional support companies](https://cloudnative-pg.io/support/) +listed on the {{name.ln}} website. The operator does not validate that the +resulting tablespace layout will work correctly. +!!! + +!!!warning + +The operator does not check which tablespaces the source instance has. Make +sure that every tablespace of the source instance is declared in +`spec.tablespaces`, and mapped through `tablespaceMapping` unless the source +already uses the {{name.ln}} location. A tablespace that is not mapped is +restored under the path it has in the source instance, inside the Pod. If that +path is not writable, the bootstrap fails. If it is, the data ends up in an +unexpected location. +!!! + +!!!warning + +Do not run `CREATE TABLESPACE` on the source from the start of the base backup +until the target stops following the source. For a standalone cluster, that is +the end of the cloning operation; for a replica cluster, it is the promotion of +the cluster. WAL replay carries the source path unmodified, so replaying such a +record fails on the target. +!!! + #### Current limitations ##### Snapshot copy diff --git a/product_docs/docs/postgres_for_kubernetes/1/cluster_conf.mdx b/product_docs/docs/postgres_for_kubernetes/1/cluster_conf.mdx index b3672a89af..381d9053c3 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/cluster_conf.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/cluster_conf.mdx @@ -87,6 +87,17 @@ the `.spec.ephemeralVolumesSizeLimit.shm` field in the cluster spec. Use this field only in case of [PostgreSQL running with `posix` shared memory dynamic allocation](postgresql_conf.md#dynamic-shared-memory-settings). +## ServiceAccount token mount + +The `automountServiceAccountToken` field of the cluster specification +controls the automatic mount of the `ServiceAccount` token in the instance +Pods and in the Jobs run by the operator. Setting it to `false` helps comply +with admission policies enforced in hardened environments; the operator then +mounts an equivalent projected volume so that the instance manager can keep +working. See +["Disabling the automatic mount of the ServiceAccount token"](security.md#disabling-the-automatic-mount-of-the-serviceaccount-token) +for the details. + ## Environment variables !!!important diff --git a/product_docs/docs/postgres_for_kubernetes/1/failover.mdx b/product_docs/docs/postgres_for_kubernetes/1/failover.mdx index c453b113ff..feb56d2f29 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/failover.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/failover.mdx @@ -87,35 +87,29 @@ it. On a clean shutdown the release happens after PostgreSQL has flushed and archived its remaining WAL, so the replica that takes over sees the archive at its definitive end. If the previous primary cannot release the lease (crash, node failure, or the instance -manager itself unreachable), the lease expires after -`leaseDurationSeconds` elapses and the replica can promote. In that +manager itself unreachable), the replica can promote once the lease has been +unchanged for `leaseDurationSeconds` since the operator named it the target +primary. In that path the archive may not have caught up, and any writes the previous primary did not finish archiving are lost. ### Relationship with the primary isolation check -The lease does not fence a primary that has lost connectivity to the -Kubernetes API server but is still running; that is the job of the -[primary isolation](instance_manager.md#primary-isolation) check. +The lease alone does not stop a primary that has lost connectivity to the +Kubernetes API server but is still running. Stopping it is the job of the +[primary isolation](instance_manager.md#primary-isolation) check, which watches +how long the lease has gone without a successful renewal. The two mechanisms are complementary and both are enabled by default: - The lease prevents *premature* promotion: a replica cannot promote while the former primary still holds the lease. -- The isolation check reports an isolated primary as unhealthy, which - the kubelet then restarts through its normal container-termination - path. That path is a *smart* shutdown: it refuses new connections - but lets sessions that are already open keep committing until - `.spec.smartShutdownTimeout` elapses (180 seconds by default), so - it narrows the write window during a partition rather than closing - it outright. +- The isolation check stops an *isolated* primary from continuing to + accept writes, by shutting PostgreSQL down immediately. Keep both enabled. Disabling the isolation check leaves the lease alone responsible for primary safety, and the lease alone cannot prevent split-brain when the former primary cannot reach the API -server but remains otherwise healthy. If that write window matters -for your workload, set `.spec.smartShutdownTimeout: 0` so the -restart goes straight to a fast shutdown instead of waiting out the -full window. +server but remains otherwise healthy. ### Inspecting the primary lease @@ -147,12 +141,12 @@ The lease timings are exposed under `.spec.primaryLease` and default to values suitable for most clusters. They map directly onto the underlying Kubernetes leader-election parameters. -| Field | Default | Description | -| ------------------------------ | ------- | ------------------------------------------------------------------------ | -| `leaseDurationSeconds` | `15` | How long the lease is valid before another instance may acquire it. | -| `renewDeadlineSeconds` | `10` | How long the primary keeps retrying to renew the lease before giving up. | -| `retryPeriodSeconds` | `2` | How frequently a non-holder retries acquiring or renewing the lease. | -| `releasedLeaseDurationSeconds` | `1` | TTL written when the primary releases the lease on a clean shutdown. | +| Field | Default | Description | +| ------------------------------ | ------- | ------------------------------------------------------------------------- | +| `leaseDurationSeconds` | `15` | How long the target primary waits on an unchanged lease before taking it. | +| `renewDeadlineSeconds` | `10` | How long the primary keeps retrying to renew the lease before giving up. | +| `retryPeriodSeconds` | `2` | How frequently a non-holder retries acquiring or renewing the lease. | +| `releasedLeaseDurationSeconds` | `1` | TTL written when the primary releases the lease on a clean shutdown. | For example, to make the cluster more tolerant of a slow or briefly unreachable API server: @@ -176,13 +170,21 @@ admission webhook: `leaseDurationSeconds` must be greater than underlying Kubernetes leader election. !!! +!!!warning + +These timings also set how fast an isolated primary stops. See +[Primary Isolation](instance_manager.md#primary-isolation) for the margin to +keep between them and the isolation check `requestTimeout`. +!!! + !!!note `leaseDurationSeconds` and `retryPeriodSeconds` govern two different timings. -After an abrupt primary loss (the previous primary did not release the lease), a +After an abrupt primary loss (the previous primary did not release the lease), the candidate must observe the lease unchanged for a full `leaseDurationSeconds` -before it may take over: this is what holds back a premature promotion while the -former primary may still be alive. After a clean switchover (the previous primary +before it may take over, counted from the moment it becomes the target primary: +this is what holds back a premature promotion while the former primary may still +be alive. After a clean switchover (the previous primary released the lease), there is no such wait; the candidate simply notices the released lease on its next poll, so the hand-over latency is bounded by `retryPeriodSeconds`. Lowering `retryPeriodSeconds` speeds up switchover without @@ -284,17 +286,17 @@ mechanism, driven by `tolerationSeconds` on the `node.kubernetes.io/unreachable` `NoExecute` taint (`300s` by default). That timer does not hold up the operator's failover decision; {{name.ln}} promotes a new primary as soon as the `Ready` condition flips. By that point -the kubelet on the isolated node has already noticed the problem: with the +PostgreSQL on the isolated node has already been stopped locally: with the default `.spec.probes.liveness.isolationCheck.enabled: true`, the instance -manager fails its own liveness probe once it can reach neither the API -server nor the rest of the cluster, and the kubelet restarts the container -within approximately three probe periods (`~30s`). That restart goes -through the normal termination path, a smart shutdown that lets sessions -already open on the isolated primary keep committing until -`.spec.smartShutdownTimeout` elapses (180 seconds by default) on top of -that ~30s detection window. Full high availability (recreation of the old -primary on a healthy node by the operator) is still gated on the -taint-based eviction actually deleting the pod. +manager requests an immediate PostgreSQL shutdown once its last successful lease +renewal is older than the renew deadline and either at least one other instance +is unreachable or a reachable one names a different target primary, within the +timings described under [Primary Isolation](instance_manager.md#primary-isolation). +When PostgreSQL stops, the instance manager exits and the kubelet restarts the +container, so the step-down shows up as a container restart, not as a liveness +probe failure. Full high availability (recreation of the old primary on +a healthy node by the operator) is still gated on the taint-based eviction +actually deleting the pod. ## Failover Quorum (Quorum-based Failover) diff --git a/product_docs/docs/postgres_for_kubernetes/1/imagevolume_extensions.mdx b/product_docs/docs/postgres_for_kubernetes/1/imagevolume_extensions.mdx index 61f3c64c3a..7cd522eb51 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/imagevolume_extensions.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/imagevolume_extensions.mdx @@ -142,6 +142,15 @@ By default, {{name.ln}} automatically manages the relevant GUCs by setting: PostgreSQL to locate any extension control file within `/extensions//share/extension` - `dynamic_library_path` to `/extensions//lib` +A directory is added to these GUCs only if it exists in the mounted extension +image. This is the case, for example, of a module that ships only a `lib` +directory and no `share` one: it contributes to `dynamic_library_path` but +leaves `extension_control_path` untouched, instead of adding an entry that +points to a missing directory. The same rule applies to the custom paths +described in [Setting Custom Paths](#setting-custom-paths). The instance +manager performs this check every time it generates the PostgreSQL +configuration. + !!!note Extension names containing underscores (e.g., `pg_ivm`) are converted to use hyphens (e.g., `pg-ivm`) for Kubernetes volume names to comply with RFC 1123 @@ -406,6 +415,12 @@ following fields: - `env`: A list of `name` and `value` pairs to be set as environment variables within the Postgres process. +Entries of `extension_control_path` and `dynamic_library_path` that do not +exist in the extension image are skipped, as described in +[Mounting and PostgreSQL Configuration](#mounting-and-postgresql-configuration). +Entries of `ld_library_path` and `bin_path` are added as declared, without any +existence check. + This flexibility enables you to support complex or non-standard extension images while maintaining clarity and predictability. @@ -439,6 +454,15 @@ spec: - `/extensions/my-extension/my/share/path` appended to `extension_control_path` - `/extensions/my-extension/my/lib/path` appended to `dynamic_library_path` +!!!note + +As with the default directories, a custom path that does not exist in the image +is skipped without raising an error. A typo in a custom path therefore shows up +as a missing entry in the GUC, not as a failure. To verify what was applied, +run `SHOW extension_control_path;` or `SHOW dynamic_library_path;`. Skipped +paths are also logged by the instance manager at `debug` level. +!!! + This allows PostgreSQL to discover your extension’s control files and shared libraries correctly, even with a non-standard layout. diff --git a/product_docs/docs/postgres_for_kubernetes/1/installation_upgrade.mdx b/product_docs/docs/postgres_for_kubernetes/1/installation_upgrade.mdx index 8b8de1ddea..3eb5f5babd 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/installation_upgrade.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/installation_upgrade.mdx @@ -71,12 +71,12 @@ kubectl create secret -n postgresql-operator-system docker-registry edb-pull-sec Now that the pull-secret has been added to the namespace, the operator can be installed like any other resource in Kubernetes, through a YAML manifest applied via `kubectl`. -You can install the [latest operator manifest](https://get.enterprisedb.io/pg4k/pg4k-1.30.1.yaml) +You can install the [latest operator manifest](https://get.enterprisedb.io/pg4k/pg4k-1.30.0.yaml) for this minor release as follows: ```sh kubectl apply --server-side -f \ - https://get.enterprisedb.io/pg4k/pg4k-1.30.1.yaml + https://get.enterprisedb.io/pg4k/pg4k-1.30.0.yaml ``` You can verify that with: @@ -413,9 +413,14 @@ requires no configuration; the lease timings can optionally be tuned via This feature requires the operator to manage `Lease` objects in the `coordination.k8s.io` API group. The bundled operator manifest and the Helm chart grant the required permissions automatically. If you manage the -operator's RBAC yourself, you must add the `create`, `get`, `list`, `update` -and `watch` verbs on `leases` in the `coordination.k8s.io` API group before -upgrading, otherwise primaries will be unable to promote. +operator's RBAC yourself, you must add the `create`, `delete`, `get`, `list`, +`update` and `watch` verbs on `leases` in the `coordination.k8s.io` API group +before upgrading, otherwise primaries will be unable to promote. The `delete` +verb is needed when a `Cluster` is recreated over the resources left behind by +a previous one, for example by a backup tool. The API server only lets the +operator adopt the leftover `Lease` if it can delete it. When the `Cluster` is +restored over orphan PVCs, the operator deletes the `Lease` along with the +orphan Pods instead, and creates a new one. !!! #### Operator-to-instance authentication (`GHSA-7qwx-x8ff-3px9`) @@ -425,24 +430,14 @@ sensitive endpoints of the instance manager's status port (backup, `pg_controldata`, partial WAL archive, and instance-manager upgrade) by pinning an in-memory client certificate. This is enabled automatically and requires no configuration. Status, health, and probe endpoints remain unauthenticated. -This hardening is not backported; on releases earlier than 1.30.0, continue to -restrict the status port (TCP 8000) with a `NetworkPolicy`, which remains its -security boundary there. See +This protection has a hard requirement: the status port **must** be served +over TLS, which has been the default since v1.24. This hardening is not +backported; on releases earlier than 1.30.0, continue to restrict the status +port (TCP 8000) with a `NetworkPolicy`, which remains its security boundary +there. See [Operator-to-instance authentication](security.md#operator-to-instance-authentication) for details. -!!!warning - -This protection has a hard requirement: the status port **must** be served over -TLS, which has been the default since v1.24. Instances created by an operator -older than v1.24 serve the status port over plain HTTP; once their instance -manager is upgraded to 1.30.0 the operator can no longer authenticate to them -and every call to the protected endpoints is **permanently** rejected with -`401 Unauthorized`. If you still run such instances, perform a rolling update so -their Pods are recreated with a TLS-enabled status port. Instances created by -v1.24 or later are unaffected. -!!! - #### Metrics exporter privilege separation (`CVE-2026-44477`) This applies only if you are upgrading from a release **older than 1.29.1, diff --git a/product_docs/docs/postgres_for_kubernetes/1/instance_manager.mdx b/product_docs/docs/postgres_for_kubernetes/1/instance_manager.mdx index 88efb164a9..d7272ea656 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/instance_manager.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/instance_manager.mdx @@ -168,7 +168,11 @@ time for recovery to complete. The liveness probe begins after the startup probe successfully completes. Its primary role is to ensure the PostgreSQL instance manager is operating -correctly. +correctly. It fails when the primary lease loop has stopped attempting to renew +or take over the lease, and when PostgreSQL is still running after +`.spec.immediateShutdownTimeout` seconds from an immediate shutdown request. +Primary isolation itself is not handled by this probe, but by the primary lease +(see [Primary Isolation](#primary-isolation)). Following Kubernetes standards, if the liveness probe fails, the kubelet will terminate the container, which will then be restarted. @@ -225,14 +229,42 @@ spec: ### Primary Isolation -{{name.ln}} 1.27 introduces an additional behavior for the liveness -probe of a PostgreSQL primary, which will report a failure if **both** of the -following conditions are met: - -1. The instance manager cannot reach the Kubernetes API server -2. The instance manager cannot reach **any** other instance via the instance manager’s REST API - -The effect of this behavior is to consider an isolated primary to be not alive: the liveness probe fails, and the kubelet restarts the container through its normal termination path. That path is a **smart** shutdown: it refuses new connections but lets sessions that are already open keep committing until `.spec.smartShutdownTimeout` elapses (180 seconds by default). Set `.spec.smartShutdownTimeout: 0` if you need the restart to skip straight to a fast shutdown instead of waiting out that window. +A primary that cannot renew its primary lease, typically because it cannot reach +the Kubernetes API server, and has also lost contact with part of its cluster, +cannot tell a network partition from a cluster in which it has already been +replaced. {{name.ln}} **stops such a primary** so that it stops accepting +writes, if **both** of the following conditions are met: + +1. The instance manager has not managed to renew the + [primary lease](failover.md#safe-primary-election) for longer than its renew + deadline, typically because it cannot reach the Kubernetes API server, but + whatever the cause +2. The instance manager cannot reach **at least one** of the other instances via + the instance manager's REST API, or an instance it can reach reports a + different instance as the target primary + +The second condition is deliberately conservative: a single instance the primary +can no longer reach is enough, because a primary that has lost both the API +server and a peer cannot rule out having been replaced. + +The check is driven by the primary lease. The instance manager keeps track of +the last time it renewed the lease successfully. On its first retry-period tick +after that time is older than the renew deadline, it probes the other instances +and, if they confirm the isolation, requests an immediate PostgreSQL shutdown. +Open transactions are aborted and the next start runs crash recovery. The +decision is taken once, and the request is retried until the instance manager +accepts it, unless a lease renewal lands first and withdraws it. The step-down +is logged under the `primary-lease` logger. + +The instance manager may be busy with another command, for example a restart, +or the immediate shutdown may fail. Once the instance manager has decided to +step down, if it is still running on the last retry-period tick before a +replica is allowed to claim the lease, it sends `SIGQUIT` to the PostgreSQL +postmaster and exits with code `5`. Once the immediate shutdown has been +accepted, a later lease renewal does not postpone this stop. The kubelet restarts the container. If the pod is still the target +primary, it waits for the lease before it starts PostgreSQL again, otherwise it +rejoins as a replica. The check never acts when it is disabled or when the +cluster has a single instance. It is **enabled by default** and can be disabled by adding the following: @@ -245,9 +277,14 @@ spec: ``` !!!info Important -Be aware that the default liveness probe settings—automatically derived from `livenessProbeTimeout`—might -be aggressive (30 seconds). As such, we recommend explicitly setting the -liveness probe configuration to suit your environment. + +The reaction time is set by the primary lease timings +(`.spec.primaryLease`) and by `isolationCheck.requestTimeout`, not by the +liveness probe periods: with the defaults the shutdown is requested between +10 and 13 seconds after the last successful lease renewal. `periodSeconds`, +`failureThreshold` and `livenessProbeTimeout` have no effect on it. Keep +`renewDeadlineSeconds` plus `retryPeriodSeconds` plus `requestTimeout` below +`leaseDurationSeconds`; the admission webhook warns otherwise. !!! The spec also accepts two optional network settings: `requestTimeout` @@ -267,12 +304,12 @@ spec: !!!info -Primary isolation is distinct from the [safe primary election](failover.md#safe-primary-election) -mechanism. The isolation check reports a primary that has lost connectivity to -both the API server and the other instances as unhealthy, leaving the kubelet -to restart it through the normal container-termination path described above; -the primary lease coordinates *which instance is allowed to promote*. The two -mechanisms are complementary. +Primary isolation and [safe primary election](failover.md#safe-primary-election) +are two uses of the same primary lease, and they answer different questions. The +lease coordinates *which instance is allowed to promote*; the isolation check +uses the age of that lease to decide *when the current primary has to stop*. The +two are complementary, and disabling the isolation check leaves the lease +coordinating promotions with no mechanism stopping an isolated primary. !!! ## Readiness Probe diff --git a/product_docs/docs/postgres_for_kubernetes/1/kubectl-plugin.mdx b/product_docs/docs/postgres_for_kubernetes/1/kubectl-plugin.mdx index 97b87d83fb..0838e9bffd 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/kubectl-plugin.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/kubectl-plugin.mdx @@ -36,11 +36,11 @@ them in your systems. #### Debian packages -For example, let's install the 1.30.1 release of the plugin, for an Intel based +For example, let's install the 1.30.0 release of the plugin, for an Intel based 64 bit server. First, we download the right `.deb` file. ```sh -wget https://github.com/EnterpriseDB/kubectl-cnp/releases/download/v1.30.1/kubectl-cnp_1.30.1_linux_x86_64.deb \ +wget https://github.com/EnterpriseDB/kubectl-cnp/releases/download/v1.30.0/kubectl-cnp_1.30.0_linux_x86_64.deb \ --output-document kube-plugin.deb ``` @@ -54,17 +54,17 @@ sudo dpkg -i kube-plugin.deb Selecting previously unselected package cnp. (Reading database ... 6688 files and directories currently installed.) Preparing to unpack kube-plugin.deb ... -Unpacking kubectl-cnp (1.30.1) ... -Setting up kubectl-cnp (1.30.1) ... +Unpacking kubectl-cnp (1.30.0) ... +Setting up kubectl-cnp (1.30.0) ... ``` #### RPM packages -As in the example for `.rpm` packages, let's install the 1.30.1 release for an +As in the example for `.rpm` packages, let's install the 1.30.0 release for an Intel 64 bit machine. Note the `--output` flag to provide a file name. ```sh -curl -L https://github.com/EnterpriseDB/kubectl-cnp/releases/download/v1.30.1/kubectl-cnp_1.30.1_linux_x86_64.rpm \ +curl -L https://github.com/EnterpriseDB/kubectl-cnp/releases/download/v1.30.0/kubectl-cnp_1.30.0_linux_x86_64.rpm \ --output kube-plugin.rpm ``` @@ -81,7 +81,7 @@ Dependencies resolved. Package Architecture Version Repository Size ==================================================================================================== Installing: - cnp x86_64 1.30.1-1 @commandline 20 M + cnp x86_64 1.30.0-1 @commandline 20 M Transaction Summary ==================================================================================================== @@ -252,9 +252,9 @@ sandbox-3 0/604DE38 0/604DE38 0/604DE38 0/604DE38 00:00:00 00:00:00 00 Instances status Name Current LSN Replication role Status QoS Manager Version Node ---- ----------- ---------------- ------ --- --------------- ---- -sandbox-1 0/604DE38 Primary OK BestEffort 1.30.1 k8s-eu-worker -sandbox-2 0/604DE38 Standby (async) OK BestEffort 1.30.1 k8s-eu-worker2 -sandbox-3 0/604DE38 Standby (async) OK BestEffort 1.30.1 k8s-eu-worker +sandbox-1 0/604DE38 Primary OK BestEffort 1.30.0 k8s-eu-worker +sandbox-2 0/604DE38 Standby (async) OK BestEffort 1.30.0 k8s-eu-worker2 +sandbox-3 0/604DE38 Standby (async) OK BestEffort 1.30.0 k8s-eu-worker ``` If you require more detailed status information, use the `--verbose` option (or @@ -308,9 +308,9 @@ sandbox-primary primary 1 1 1 Instances status Name Current LSN Replication role Status QoS Manager Version Node ---- ----------- ---------------- ------ --- --------------- ---- -sandbox-1 0/6053720 Primary OK BestEffort 1.30.1 k8s-eu-worker -sandbox-2 0/6053720 Standby (async) OK BestEffort 1.30.1 k8s-eu-worker2 -sandbox-3 0/6053720 Standby (async) OK BestEffort 1.30.1 k8s-eu-worker +sandbox-1 0/6053720 Primary OK BestEffort 1.30.0 k8s-eu-worker +sandbox-2 0/6053720 Standby (async) OK BestEffort 1.30.0 k8s-eu-worker2 +sandbox-3 0/6053720 Standby (async) OK BestEffort 1.30.0 k8s-eu-worker ``` With an additional `-v` (e.g. `kubectl cnp status sandbox -v -v`), you can @@ -538,12 +538,12 @@ and previous logs are available, it will show them both. ```output ====== Begin of Previous Log ===== -2023-03-28T12:56:41.251711811Z {"level":"info","ts":"2023-03-28T12:56:41Z","logger":"setup","msg":"Starting EDB Postgres for Kubernetes Operator","version":"1.30.1","build":{"Version":"1.30.1+dev107","Commit":"cc9bab17","Date":"2023-03-28"}} +2023-03-28T12:56:41.251711811Z {"level":"info","ts":"2023-03-28T12:56:41Z","logger":"setup","msg":"Starting EDB Postgres for Kubernetes Operator","version":"1.30.0","build":{"Version":"1.30.0+dev107","Commit":"cc9bab17","Date":"2023-03-28"}} 2023-03-28T12:56:41.251851909Z {"level":"info","ts":"2023-03-28T12:56:41Z","logger":"setup","msg":"Starting pprof HTTP server","addr":"0.0.0.0:6060"} ====== End of Previous Log ===== -2023-03-28T12:57:09.854306024Z {"level":"info","ts":"2023-03-28T12:57:09Z","logger":"setup","msg":"Starting EDB Postgres for Kubernetes Operator","version":"1.30.1","build":{"Version":"1.30.1+dev107","Commit":"cc9bab17","Date":"2023-03-28"}} +2023-03-28T12:57:09.854306024Z {"level":"info","ts":"2023-03-28T12:57:09Z","logger":"setup","msg":"Starting EDB Postgres for Kubernetes Operator","version":"1.30.0","build":{"Version":"1.30.0+dev107","Commit":"cc9bab17","Date":"2023-03-28"}} 2023-03-28T12:57:09.854363943Z {"level":"info","ts":"2023-03-28T12:57:09Z","logger":"setup","msg":"Starting pprof HTTP server","addr":"0.0.0.0:6060"} ``` diff --git a/product_docs/docs/postgres_for_kubernetes/1/labels_annotations.mdx b/product_docs/docs/postgres_for_kubernetes/1/labels_annotations.mdx index 9efdc648f8..f185e78287 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/labels_annotations.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/labels_annotations.mdx @@ -70,7 +70,7 @@ instead of alternative approaches such as pod templates. deprecated `postgresql` label). - **`k8s.enterprisedb.io/jobRole`** - Role of the job (that is, `import`, `initdb`, `join`, ...) + Role of the job (that is, `major-upgrade`) - **`k8s.enterprisedb.io/majorVersion`** Integer PostgreSQL major version of the backup's data directory (for example, `17`). diff --git a/product_docs/docs/postgres_for_kubernetes/1/monitoring.mdx b/product_docs/docs/postgres_for_kubernetes/1/monitoring.mdx index 5fb29d548c..0a2a4c9b96 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/monitoring.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/monitoring.mdx @@ -1003,7 +1003,7 @@ metadata: spec: containers: - name: curl - image: curlimages/curl:8.22.0 + image: curlimages/curl:8.23.0 command: ['sleep', '3600'] EOF ``` diff --git a/product_docs/docs/postgres_for_kubernetes/1/object_stores.mdx b/product_docs/docs/postgres_for_kubernetes/1/object_stores.mdx index cd86ab30b8..7fcdf75095 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/object_stores.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/object_stores.mdx @@ -7,11 +7,15 @@ originalFilePath: 'src/appendixes/object_stores.md' !!!warning As of {{name.ln}} 1.26, **native Barman Cloud support is deprecated** in -favor of the **Barman Cloud Plugin**. While the native integration remains -functional for now, we strongly recommend beginning a gradual migration to -the plugin-based interface after appropriate testing. The Barman Cloud -Plugin documentation describes -[how to use common object stores](https://cloudnative-pg.io/plugin-barman-cloud/docs/object_stores/). +favor of the **Barman Cloud Plugin**. **{{name.ln}} 1.31 is the last +minor release that ships with native Barman Cloud support, which is removed +starting from 1.32.0.** Releases up to and including 1.31, and their patch +releases, keep native support for as long as they are supported. You must +migrate to the plugin before upgrading to 1.32 or later. We strongly +recommend migrating to the plugin-based interface after appropriate testing. +The Barman Cloud Plugin documentation describes +[how to use common object stores](https://cloudnative-pg.io/plugin-barman-cloud/docs/object_stores/) +and [how to migrate from the built-in backup](https://cloudnative-pg.io/plugin-barman-cloud/docs/migration/). !!! You can store the [backup](backup.md) files in any service that is supported @@ -135,6 +139,16 @@ In case you're using S3-compatible object storage, like **MinIO** or **Linode Object Storage**, you can specify an endpoint instead of using the default S3 one. +!!!warning + +Barman Cloud uses the Boto3 SDK and takes AWS S3 as its reference +implementation. Issues you can reproduce on AWS S3 are treated as Barman Cloud +defects. Issues that happen only on a third-party object store are API +incompatibilities, and you must resolve them with your storage vendor. See the +[Barman Cloud documentation](https://docs.pgbarman.org/release/latest/) for +known workarounds. +!!! + In this example, it will use the `bucket` of **Linode** in the region `us-east1`. diff --git a/product_docs/docs/postgres_for_kubernetes/1/operator_capability_levels.mdx b/product_docs/docs/postgres_for_kubernetes/1/operator_capability_levels.mdx index 87b9bf0e29..54f44874bd 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/operator_capability_levels.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/operator_capability_levels.mdx @@ -79,7 +79,7 @@ basis. The operator supports any operand container image containing PostgreSQL. While it defaults to the latest stable minor version of the most recent -Community-supported major version on `docker.enterprisedb.com`, you can override this by +Community-supported major version on `ghcr.io` by EDB, you can override this by setting the `.spec.imageName` attribute in the `Cluster` resource. This direct method supports `imagePullSecrets` for private registries and allows the use of both tags and SHA256 digests to ensure container diff --git a/product_docs/docs/postgres_for_kubernetes/1/pg4k.v1/index.mdx b/product_docs/docs/postgres_for_kubernetes/1/pg4k.v1/v1.30.1-next.mdx similarity index 97% rename from product_docs/docs/postgres_for_kubernetes/1/pg4k.v1/index.mdx rename to product_docs/docs/postgres_for_kubernetes/1/pg4k.v1/v1.30.1-next.mdx index 2508683ec6..c5d9c7f1a7 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/pg4k.v1/index.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/pg4k.v1/v1.30.1-next.mdx @@ -1,117 +1,7 @@ --- -title: API Reference - v1.30.1 -originalFilePath: src/pg4k.v1.md -navTitle: API Reference -navigation: - - v1.30.1 - - v1.30.0 - - v1.29.1 - - v1.29.0 - - v1.28.1 - - v1.28.0 - - v1.27.1 - - v1.27.0 - - v1.26.1 - - v1.26.0 - - v1.25.1 - - v1.25.0 - - v1.24.3 - - v1.24.2 - - v1.24.1 - - v1.24.0 - - v1.23.6 - - v1.23.5 - - v1.23.4 - - v1.23.3 - - v1.23.2 - - v1.23.1 - - v1.23.0 - - v1.22.9 - - v1.22.8 - - v1.22.7 - - v1.22.6 - - v1.22.5 - - v1.22.4 - - v1.22.3 - - v1.22.2 - - v1.22.1 - - v1.22.0 - - v1.21.6 - - v1.21.5 - - v1.21.4 - - v1.21.3 - - v1.21.2 - - v1.21.1 - - v1.21.0 - - v1.20.6 - - v1.20.5 - - v1.20.4 - - v1.20.3 - - v1.20.2 - - v1.20.1 - - v1.20.0 - - v1.19.6 - - v1.19.5 - - v1.19.4 - - v1.19.3 - - v1.19.2 - - v1.19.1 - - v1.19.0 - - v1.18.13 - - v1.18.12 - - v1.18.11 - - v1.18.10 - - v1.18.9 - - v1.18.8 - - v1.18.7 - - v1.18.6 - - v1.18.5 - - v1.18.4 - - v1.18.3 - - v1.18.2 - - v1.18.1 - - v1.18.0 - - v1.17.5 - - v1.17.4 - - v1.17.3 - - v1.17.2 - - v1.17.1 - - v1.17.0 - - v1.16.5 - - v1.16.4 - - v1.16.3 - - v1.16.2 - - v1.16.1 - - v1.16.0 - - v1.15.5 - - v1.15.4 - - v1.15.3 - - v1.15.2 - - v1.15.1 - - v1.15.0 - - v1.14.0 - - v1.13.0 - - v1.12.0 - - v1.11.0 - - v1.10.0 - - v1.9.2 - - v1.9.1 - - v1.9.0 - - v1.8.0 - - v1.7.1 - - v1.7.0 - - v1.6.0 - - v1.5.1 - - v1.5.0 - - v1.4.0 - - v1.3.0 - - v1.2.1 - - v1.2.0 - - v1.1.0 - - v1.0.0 - - v0.8.0 - - v0.7.0 - - v0.6.0 +title: API Reference - v1.30.1-next +navTitle: v1.30.1-next +pdfExclude: 'true' --- @@ -418,12 +308,14 @@ a physical backup of an existing PostgreSQL cluster - [BootstrapConfiguration](#bootstrapconfiguration) -| Field | Description | Required | Default | Validation | -| ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------- | ------------------- | -| `source` *string* | The name of the server of which we need to take a physical backup | True | | MinLength: 1
| -| `database` *string* | Name of the database used by the application. Default: `app`. | | | | -| `owner` *string* | Name of the owner of the database in the instance to be used
by applications. Defaults to the value of the `database` key. | | | | -| `secret` *[LocalObjectReference](https://pkg.go.dev/github.com/cloudnative-pg/machinery/pkg/api#LocalObjectReference)* | Name of the secret containing the initial credentials for the
owner of the user database. If empty a new secret will be
created from scratch | | | | +| Field | Description | Required | Default | Validation | +| ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- | ------------------- | +| `source` *string* | The name of the server of which we need to take a physical backup | True | | MinLength: 1
| +| `database` *string* | Name of the database used by the application. Default: `app`. | | | | +| `owner` *string* | Name of the owner of the database in the instance to be used
by applications. Defaults to the value of the `database` key. | | | | +| `secret` *[LocalObjectReference](https://pkg.go.dev/github.com/cloudnative-pg/machinery/pkg/api#LocalObjectReference)* | Name of the secret containing the initial credentials for the
owner of the user database. If empty a new secret will be
created from scratch | | | | +| `additionalArgs` *string array* | List of custom options to pass to the `pg_basebackup` command.
IMPORTANT: Use with caution. The operator rejects the options it relies on,
as listed in the bootstrap documentation, but it does not validate the
others, and some of them may still interfere with its intended
functionality or design.
You are responsible for ensuring that the provided options are compatible
with your environment and PostgreSQL version. | | | | +| `tablespaceMapping` *[TablespaceMappingEntry](#tablespacemappingentry) array* | Maps each tablespace declared in `spec.tablespaces` to its location
in the source instance. | | | | #### BootstrapRecovery @@ -617,6 +509,7 @@ ClusterSpec defines the desired state of a PostgreSQL cluster managed by | `smartStopDelay` *integer* | Deprecated: please use SmartShutdownTimeout instead | | | | | `smartShutdownTimeout` *integer* | The time in seconds that controls the window of time reserved for the smart shutdown of Postgres to complete.
Make sure you reserve enough time for the operator to request a fast shutdown of Postgres
(that is: `stopDelay` - `smartShutdownTimeout`). Default is 180 seconds. | | 180 | | | `switchoverDelay` *integer* | The time in seconds that is allowed for a primary PostgreSQL instance
to gracefully shutdown during a switchover.
Default value is 3600 seconds (1 hour). | | 3600 | | +| `immediateShutdownTimeout` *integer* | The time in seconds that is allowed for a PostgreSQL instance to stop
after an immediate shutdown request (used, for example, when a primary
must step down and cannot wait for a graceful shutdown). Once this time
has passed with PostgreSQL still running, the instance is considered
unresponsive. Default value is 30 seconds. | | 30 | | | `failoverDelay` *integer* | The amount of time (in seconds) to wait before triggering a failover
after the primary PostgreSQL instance in the cluster was detected
to be unhealthy | | 0 | | | `livenessProbeTimeout` *integer* | LivenessProbeTimeout is the time (in seconds) that is allowed for a PostgreSQL instance
to successfully respond to the liveness probe (default 30).
The Liveness probe failure threshold is derived from this value using the formula:
ceiling(livenessProbe / 10). | | | | | `affinity` *[AffinityConfiguration](#affinityconfiguration)* | Affinity/Anti-affinity rules for Pods | | | | @@ -1282,8 +1175,8 @@ InstanceReportedState describes the last reported state of an instance during a #### IsolationCheckConfiguration -IsolationCheckConfiguration contains the configuration for the isolation check -functionality in the liveness probe +IsolationCheckConfiguration contains the configuration for the primary +isolation check *Appears in:* @@ -1291,7 +1184,7 @@ functionality in the liveness probe | Field | Description | Required | Default | Validation | | ----------------------------- | -------------------------------------------------------------------------- | -------- | ------- | ---------- | -| `enabled` *boolean* | Whether primary isolation checking is enabled for the liveness probe | | true | | +| `enabled` *boolean* | Whether primary isolation checking is enabled | | true | | | `requestTimeout` *integer* | Timeout in milliseconds for requests during the primary isolation check | | 1000 | | | `connectionTimeout` *integer* | Timeout in milliseconds for connections during the primary isolation check | | 1000 | | @@ -1374,7 +1267,7 @@ LivenessProbe is the configuration of the liveness probe | `successThreshold` *integer* | Minimum consecutive successes for the probe to be considered successful after having failed.
Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. | | | | | `failureThreshold` *integer* | Minimum consecutive failures for the probe to be considered failed after having succeeded.
Defaults to 3. Minimum value is 1. | | | | | `terminationGracePeriodSeconds` *integer* | Optional duration in seconds the pod needs to terminate gracefully upon probe failure.
The grace period is the duration in seconds after the processes running in the pod are sent
a termination signal and the time when the processes are forcibly halted with a kill signal.
Set this value longer than the expected cleanup time for your process.
If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this
value overrides the value provided by the pod spec.
Value must be non-negative integer. The value zero indicates stop immediately via
the kill signal (no opportunity to shut down).
This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate.
Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. | | | | -| `isolationCheck` *[IsolationCheckConfiguration](#isolationcheckconfiguration)* | Configure the feature that extends the liveness probe for a primary
instance. In addition to the basic checks, this reports whether the
primary is isolated from the Kubernetes API server and from its
replicas, so the kubelet restarts it through the normal
container-termination path (a smart shutdown, bounded by
`.spec.smartShutdownTimeout`) when a network partition or API
unavailability is detected. Enabled by default. | | | | +| `isolationCheck` *[IsolationCheckConfiguration](#isolationcheckconfiguration)* | Configure the primary isolation check, which stops a primary whose
primary lease has gone past its renew deadline without a successful
renewal, when at least one other instance is unreachable or names a
different target primary, so that it stops accepting writes it can no
longer safely serve. Its reaction time is set by the `primaryLease`
timings and by `requestTimeout`, not by the liveness probe settings this
stanza lives under. Enabled by default. | | | | #### ManagedConfiguration @@ -2582,15 +2475,17 @@ need to be considered. - [PostgresConfiguration](#postgresconfiguration) -| Field | Description | Required | Default | Validation | -| ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- | --------------------------------- | -| `method` *[SynchronousReplicaConfigurationMethod](#synchronousreplicaconfigurationmethod)* | Method to select synchronous replication standbys from the listed
servers, accepting 'any' (quorum-based synchronous replication) or
'first' (priority-based synchronous replication) as values. | True | | Enum: [any first]
| -| `number` *integer* | Specifies the number of synchronous standby servers that
transactions must wait for responses from. | True | | | -| `maxStandbyNamesFromCluster` *integer* | Specifies the maximum number of local cluster pods that can be
automatically included in the `synchronous_standby_names` option in
PostgreSQL. | | | | -| `standbyNamesPre` *string array* | A user-defined list of application names to be added to
`synchronous_standby_names` before local cluster pods (the order is
only useful for priority-based synchronous replication). | | | | -| `standbyNamesPost` *string array* | A user-defined list of application names to be added to
`synchronous_standby_names` after local cluster pods (the order is
only useful for priority-based synchronous replication). | | | | -| `dataDurability` *[DataDurabilityLevel](#datadurabilitylevel)* | If set to "required", data durability is strictly enforced. Write operations
with synchronous commit settings (`on`, `remote_write`, or `remote_apply`) will
block if there are insufficient healthy replicas, ensuring data persistence.
If set to "preferred", data durability is maintained when healthy replicas
are available, but the required number of instances will adjust dynamically
if replicas become unavailable. This setting relaxes strict durability enforcement
to allow for operational continuity. This setting is only applicable if both
`standbyNamesPre` and `standbyNamesPost` are unset (empty). | | | Enum: [required preferred]
| -| `failoverQuorum` *boolean* | FailoverQuorum enables a quorum-based check before failover, improving
data durability and safety during failover events in {{name.ln}}-managed
PostgreSQL clusters. | | | | +| Field | Description | Required | Default | Validation | +| ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- | --------------------------------- | +| `method` *[SynchronousReplicaConfigurationMethod](#synchronousreplicaconfigurationmethod)* | Method to select synchronous replication standbys from the listed
servers, accepting 'any' (quorum-based synchronous replication) or
'first' (priority-based synchronous replication) as values. | True | | Enum: [any first]
| +| `number` *integer* | Specifies the number of synchronous standby servers that
transactions must wait for responses from. | True | | | +| `maxStandbyNamesFromCluster` *integer* | Specifies the maximum number of local cluster pods that can be
automatically included in the `synchronous_standby_names` option in
PostgreSQL. | | | | +| `standbyNamesPre` *string array* | A user-defined list of application names to be added to
`synchronous_standby_names` before local cluster pods (the order is
only useful for priority-based synchronous replication). | | | | +| `standbyNamesPost` *string array* | A user-defined list of application names to be added to
`synchronous_standby_names` after local cluster pods (the order is
only useful for priority-based synchronous replication). | | | | +| `dataDurability` *[DataDurabilityLevel](#datadurabilitylevel)* | If set to "required", data durability is strictly enforced. Write operations
with synchronous commit settings (`on`, `remote_write`, or `remote_apply`) will
block if there are insufficient healthy replicas, ensuring data persistence.
If set to "preferred", data durability is maintained when healthy replicas
are available, but the required number of instances will adjust dynamically
if replicas become unavailable. This setting relaxes strict durability enforcement
to allow for operational continuity. This setting is only applicable if both
`standbyNamesPre` and `standbyNamesPost` are unset (empty). | | | Enum: [required preferred]
| +| `failoverQuorum` *boolean* | FailoverQuorum enables a quorum-based check before failover, improving
data durability and safety during failover events in {{name.ln}}-managed
PostgreSQL clusters. | | | | +| `podFailureDomainKeys` *string array* | PodFailureDomainKeys is a list of Pod label keys used to define failure
domains. The values are read exclusively from the labels of each
instance Pod: a listed label that is not present on the Pod makes the
whole topology extraction fail, so that the constraint is not applied,
and the Node hosting the Pod is never consulted. When
set, the operator selects synchronous replicas from instances whose
label values differ from the primary's for all the specified keys, so
that synchronous replication spans failure domains such as availability
zones or regions. Starting from Kubernetes 1.35, the
`topology.kubernetes.io/zone` and `topology.kubernetes.io/region`
labels are automatically copied from the Node onto each Pod at
scheduling time.
When the replicas in failure domains different from the primary's are
not enough to satisfy the configured number of synchronous standbys,
the constraint is not applied and synchronous replicas are selected as
if this field were not set.
Mutually exclusive with `nodeFailureDomainKeys`. | | | | +| `nodeFailureDomainKeys` *string array* | NodeFailureDomainKeys is a list of Node label keys used to define
failure domains. The values are read exclusively from the labels of the
Node hosting each instance Pod: when a Node cannot be found (for
example, drained or deleted after the Pod was scheduled), the whole
topology extraction fails and the constraint is not applied. When set,
the operator selects synchronous replicas from instances running on
nodes whose label values differ from the primary's node for all the
specified keys, so that synchronous replication spans failure domains
such as availability zones or regions.
When the replicas in failure domains different from the primary's are
not enough to satisfy the configured number of synchronous standbys,
the constraint is not applied and synchronous replicas are selected as
if this field were not set.
Mutually exclusive with `podFailureDomainKeys`. | | | | #### SynchronousReplicaConfigurationMethod @@ -2646,6 +2541,21 @@ the storage specification for the tablespace | `owner` *[DatabaseRoleRef](#databaseroleref)* | Owner is the PostgreSQL user owning the tablespace | | | | | `temporary` *boolean* | When set to true, the tablespace will be added as a `temp_tablespaces`
entry in PostgreSQL, and will be available to automatically house temp
database objects, or other temporary files. Please refer to PostgreSQL
documentation for more information on the `temp_tablespaces` GUC. | | false | | +#### TablespaceMappingEntry + +TablespaceMappingEntry maps a tablespace managed by {{name.ln}} to the +location of the same tablespace in the source instance of a `pg_basebackup` +bootstrap. + +*Appears in:* + +- [BootstrapPgBaseBackup](#bootstrappgbasebackup) + +| Field | Description | Required | Default | Validation | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------- | ------------------- | +| `name` *string* | Name of the tablespace, as declared in `spec.tablespaces`. | True | | MinLength: 1
| +| `sourceLocation` *string* | Absolute path of the tablespace in the source instance. It must not
contain the `=` or `\` characters and must be unique among the entries. | True | | MinLength: 1
| + #### TablespaceState TablespaceState represents the state of a tablespace in a cluster diff --git a/product_docs/docs/postgres_for_kubernetes/1/quickstart.mdx b/product_docs/docs/postgres_for_kubernetes/1/quickstart.mdx index 10e0e7de16..aab00a0d98 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/quickstart.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/quickstart.mdx @@ -133,6 +133,11 @@ metadata: spec: instances: 3 + postgresql: + synchronous: + method: any + number: 1 + storage: size: 1Gi ``` diff --git a/product_docs/docs/postgres_for_kubernetes/1/recovery.mdx b/product_docs/docs/postgres_for_kubernetes/1/recovery.mdx index eb86f2e2dd..1621b0df37 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/recovery.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/recovery.mdx @@ -35,7 +35,9 @@ a consistent and optionally point-in-time state. - **Native recovery from volume snapshots**, where supported by the underlying Kubernetes storage infrastructure. - **Native recovery from object stores via Barman Cloud**, which is - **deprecated** as of version 1.26 in favor of the plugin-based approach. + **deprecated** as of version 1.26 in favor of the plugin-based approach + (1.31 is the last minor release with native support, which is removed + starting from 1.32.0). With the deprecation of native Barman Cloud support in version 1.26, this section now focuses on two supported recovery methods: using the **Barman Cloud diff --git a/product_docs/docs/postgres_for_kubernetes/1/replica_cluster.mdx b/product_docs/docs/postgres_for_kubernetes/1/replica_cluster.mdx index 88ada09d40..182599b7a6 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/replica_cluster.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/replica_cluster.mdx @@ -127,7 +127,8 @@ When configuring the external cluster, you have the following options: designated primary instance, initiating a WAL receiver process to connect to the source cluster and receive data. -You still have access to the **`barmanObjectStore` section**, although deprecated: +You still have access to the **`barmanObjectStore` section**, although +deprecated and removed starting from {{name.ln}} 1.32.0: - Enables use of the WAL archive, with {{name.ln}} automatically setting the `restore_command` in the designated primary instance. diff --git a/product_docs/docs/postgres_for_kubernetes/1/replication.mdx b/product_docs/docs/postgres_for_kubernetes/1/replication.mdx index e30cf52e0c..516d2b2a67 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/replication.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/replication.mdx @@ -123,9 +123,12 @@ fallback option whenever pulling WALs via streaming replication fails. By default, synchronous replication pauses write operations if the required number of standby nodes for WAL replication during transaction commits is unavailable. This behavior prioritizes data durability and aligns with -PostgreSQL DBA best practices. However, if self-healing is a higher priority +PostgreSQL DBA best practices. As a general rule, plan for synchronous +replication in clusters with at least three instances, to tolerate the loss +of one of them. However, if self-healing is a higher priority than strict data durability in your setup, this setting can be adjusted. For -details on managing this behavior, refer to the [Data Durability and Synchronous Replication](#data-durability-and-synchronous-replication) +details on managing this behavior, refer to the +[Data Durability and Synchronous Replication](#data-durability-and-synchronous-replication) section. !!! @@ -474,6 +477,94 @@ spec: 5. When the replicas are back, `synchronous_standby_names` will be back to the initial state. +### Failure Domain-Aware Synchronous Replication + +{{name.ln}} can restrict the choice of synchronous standbys to instances +located in a failure domain different from the primary's, such as another +availability zone. The labels that define the failure domains are declared +through one of the following mutually exclusive options of the +`.spec.postgresql.synchronous` stanza: + +- `podFailureDomainKeys`: a list of pod label keys, resolved from the labels of + each instance pod, without ever consulting the node (recommended) +- `nodeFailureDomainKeys`: a list of node label keys, resolved from the labels + of the node hosting each instance (for example, + `topology.kubernetes.io/zone`) + +Two instances belong to the same failure domain when all the listed labels +carry the same values. + +::::note[Topology labels on pods] +Starting from Kubernetes 1.35, the `topology.kubernetes.io/zone` and +`topology.kubernetes.io/region` labels are automatically copied from the node +onto each pod at scheduling time. As a result, `topology.kubernetes.io/zone` +(the most common failure domain identifier) is available on pods with no extra +setup. If you are on Kubernetes 1.34 or older, or the +`PodTopologyLabelsAdmission` feature is not available or enabled (see +[KEP-4742](https://github.com/kubernetes/enhancements/issues/4742)), use +`nodeFailureDomainKeys` instead. +:::: + +{{name.ln}} applies the failure domain constraint only when there are +enough cross-domain standbys to satisfy it completely: the full configured +`number` with `required` data durability, or just one with `preferred`. When +that holds, only the standbys outside the primary's failure domain populate +`synchronous_standby_names`. + +With `preferred` data durability this means the placement preference can lower +the number of required acknowledgments: as ["Preferred Data +Durability"](#preferred-data-durability) covers, that number is capped to the +standbys in the list, so a single cross-domain standby out of two healthy +replicas results in one required acknowledgment instead of the two a cluster +without failure domain keys would ask for. Writes are never blocked, but the +durability of each commit follows the failure domain distribution. + +If that isn't the case (too few cross-domain standbys, or the topology could +not be extracted, for example because the node hosting a pod was deleted, or +a label listed in `podFailureDomainKeys` is missing from a pod), the +constraint is dropped entirely and standbys are selected exactly as if the +keys were never configured. This all-or-nothing behavior guarantees that +write transactions never wait for acknowledgments that cannot arrive. + +The `SyncReplicationTopologySatisfied` condition in the cluster status reports +whether the placement preference is currently honored. The condition's reason +is also shown in the `SYNCTOPOLOGY` column of `kubectl get clusters -o wide`. +The `kubectl cnp status` command shows how the instances are spread across the +failure domains. +The underlying data, including the failure domain of each instance and +whether the topology was successfully extracted, is available under the +cluster's `.status.topology` key. + +!!!important + +The condition only evaluates the cluster instances: standby +names supplied through `standbyNamesPre` and `standbyNamesPost` are outside +the operator's knowledge and are not part of the check. +!!! + +For example, the following cluster keeps two synchronous standbys, chosen from +the instances running in a different availability zone than the primary +whenever possible: + +```yaml +apiVersion: postgresql.k8s.enterprisedb.io/v1 +kind: Cluster +metadata: + name: cluster-example-syncreplicas +spec: + instances: 5 + + postgresql: + synchronous: + podFailureDomainKeys: + - topology.kubernetes.io/zone + method: first + number: 2 + + storage: + size: 1Gi +``` + ## Synchronous Replication (Deprecated) !!!warning @@ -543,6 +634,15 @@ more generally, when `maxSyncReplicas < (instances - 1)`. ### Select nodes for synchronous replication +!!!info Use the new failure domain API + +Unless you specifically need the deprecated quorum-based synchronous +replication API, prefer the current API instead. See +["Failure Domain-Aware Synchronous Replication"](#failure-domain-aware-synchronous-replication) +for the equivalent, more flexible `podFailureDomainKeys` and +`nodeFailureDomainKeys` options. +!!! + {{name.ln}} enables you to select which PostgreSQL instances are eligible to participate in a quorum-based synchronous replication set through anti-affinity rules based on the node labels where the PVC holding the PGDATA and the diff --git a/product_docs/docs/postgres_for_kubernetes/1/samples/cluster-clone-tablespaces.yaml b/product_docs/docs/postgres_for_kubernetes/1/samples/cluster-clone-tablespaces.yaml new file mode 100644 index 0000000000..f6981340f6 --- /dev/null +++ b/product_docs/docs/postgres_for_kubernetes/1/samples/cluster-clone-tablespaces.yaml @@ -0,0 +1,38 @@ +apiVersion: postgresql.k8s.enterprisedb.io/v1 +kind: Cluster +metadata: + name: cluster-clone-tablespaces +spec: + instances: 3 + + bootstrap: + pg_basebackup: + # Name of the entry in `externalClusters` to clone from + source: source-cluster + tablespaceMapping: + # `name` must match a tablespace defined in `spec.tablespaces` below + # `sourceLocation` is the absolute path of that tablespace on the source server + - name: first + sourceLocation: /data/pg/first + - name: second + sourceLocation: /data/pg/second + + storage: + size: 1Gi + + # One volume per tablespace of the source, using the same names + tablespaces: + - name: first + storage: + size: 1Gi + - name: second + storage: + size: 1Gi + + externalClusters: + - name: source-cluster + # How to reach the source PostgreSQL server + connectionParameters: + host: source-postgres.example.com + # Role with the REPLICATION attribute on the source server + user: cnp_import diff --git a/product_docs/docs/postgres_for_kubernetes/1/samples/cluster-example-syncreplicas-explicit.yaml b/product_docs/docs/postgres_for_kubernetes/1/samples/cluster-example-syncreplicas-explicit.yaml index 43681203a5..7dc6ebd546 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/samples/cluster-example-syncreplicas-explicit.yaml +++ b/product_docs/docs/postgres_for_kubernetes/1/samples/cluster-example-syncreplicas-explicit.yaml @@ -7,6 +7,8 @@ spec: postgresql: synchronous: + podFailureDomainKeys: + - topology.kubernetes.io/zone method: first number: 2 diff --git a/product_docs/docs/postgres_for_kubernetes/1/samples/cluster-example.yaml b/product_docs/docs/postgres_for_kubernetes/1/samples/cluster-example.yaml index a282ddf358..483fe7ae26 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/samples/cluster-example.yaml +++ b/product_docs/docs/postgres_for_kubernetes/1/samples/cluster-example.yaml @@ -4,7 +4,12 @@ metadata: name: cluster-example spec: instances: 3 - + + postgresql: + synchronous: + method: any + number: 1 + storage: size: 1Gi diff --git a/product_docs/docs/postgres_for_kubernetes/1/security.mdx b/product_docs/docs/postgres_for_kubernetes/1/security.mdx index f826ee50dd..7307b47041 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/security.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/security.mdx @@ -391,10 +391,55 @@ that are associated with that Postgres cluster. Such calls are performed through a dedicated `ServiceAccount` created by the operator that shares the same PostgreSQL `Cluster` resource name. -!!! Important - The operand can only access a specific and limited subset of resources - through the API server. A service account is the - [recommended way to access the API server from within a Pod](https://kubernetes.io/docs/tasks/run-application/access-api-from-pod/). +!!!info Important + +The operand can only access a specific and limited subset of resources +through the API server. A service account is the +[recommended way to access the API server from within a Pod](https://kubernetes.io/docs/tasks/run-application/access-api-from-pod/). +!!! + +#### Disabling the automatic mount of the ServiceAccount token + +Some hardened environments enforce admission policies that require +`automountServiceAccountToken: false` on every Pod and on the +`ServiceAccount` itself. You can comply with such policies by setting the +`automountServiceAccountToken` field in the cluster specification: + +```yaml +apiVersion: postgresql.k8s.enterprisedb.io/v1 +kind: Cluster +metadata: + name: cluster-example +spec: + automountServiceAccountToken: false + instances: 3 + storage: + size: 1Gi +``` + +The value is copied verbatim to the specification of the instance Pods, to +the pod template of the Jobs run by the operator, and to the +`ServiceAccount` generated by the operator. + +Since the instance manager requires access to the Kubernetes API server, when +the automatic mount is disabled the operator adds to those Pods and Jobs an +explicit projected volume named `kube-api-access`, equivalent to the volume +that Kubernetes would have automounted, and mounts it on every container at +`/var/run/secrets/kubernetes.io/serviceaccount`. As a result, the workload +keeps the same API server access it would have with the default settings, +while satisfying policies that check the `automountServiceAccountToken` +fields. + +Changing the field triggers a rolling update of the cluster. + +!!!note + +When a shared `ServiceAccount` is referenced through `serviceAccountName` +(see below), the operator does not modify it: setting +`automountServiceAccountToken: false` on the `ServiceAccount` itself is your +responsibility in that case. +!!! + #### Using a shared ServiceAccount By default, {{name.ln}} creates a dedicated `ServiceAccount` for each cluster, @@ -778,23 +823,9 @@ unauthenticated. The certificate is never written to disk and is regenerated on every operator restart, so trust derives from fingerprint pinning rather than CA validation. -This protection has a hard requirement: the status port **must** be served over -TLS, which has been the default since v1.24. A client certificate can only be -presented over a TLS connection, so the protected endpoints are reachable by the -operator only when the status port uses TLS. - -!!!warning -If the status port is not served over TLS, the instance manager cannot -authenticate the operator and **permanently** rejects every call to its -protected endpoints (backup, `pg_controldata`, partial WAL archive, and -instance-manager upgrade) with `401 Unauthorized`. This is not a transient -condition and will not resolve on its own. It can affect instances created by an -operator older than v1.24 (whose status port serves plain HTTP) once their -instance manager is upgraded to a version that enforces this authentication: such -instances must be rolled out so their Pods are recreated with a TLS-enabled status -port. Newly created instances always enable TLS on the status port and are -unaffected. -!!! +A client certificate can only be presented over a TLS connection, and the +instance manager always serves the status port over TLS, so this protection is +unconditional. ### PostgreSQL diff --git a/product_docs/docs/postgres_for_kubernetes/1/troubleshooting.mdx b/product_docs/docs/postgres_for_kubernetes/1/troubleshooting.mdx index a40a67570c..3f07687da0 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/troubleshooting.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/troubleshooting.mdx @@ -841,9 +841,9 @@ operator to connect cross-namespace to cluster pods. ### Error while bootstrapping the data directory -If your Cluster's initialization job crashes with a "Bus error (core dumped) -child process exited with exit code 135", you likely need to fix the Cluster -hugepages settings. +If your Cluster's bootstrap init container crashes with a "Bus error (core +dumped) child process exited with exit code 135", you likely need to fix the +Cluster hugepages settings. The reason is the incomplete support of hugepages in the cgroup v1 that should be fixed in v2. For more information, check the PostgreSQL [BUG #17757: Not @@ -875,14 +875,14 @@ Please remember that you must have enough hugepages memory available to schedule every Pod in the Cluster (in the example above, at least 512MiB per Pod must be free). -### Bootstrap job hangs in running status +### Bootstrap init container hangs in running status -If your Cluster's initialization job hangs while in `Running` status with the -message: -"error while waiting for the API server to be reachable", you probably have -a network issue preventing communication with the Kubernetes API server. -Initialization jobs (like most of jobs) need to access the Kubernetes -API. Please check your networking. +If your Cluster's instance Pod hangs while its bootstrap init container is in +`Running` status with the message "error while waiting for the API server to +be reachable", you probably have a network issue preventing communication +with the Kubernetes API server. The bootstrap init container, like most of the +operator's components, needs to access the Kubernetes API. Please check your +networking. Another possible cause is when you have sidecar injection configured. Sidecars such as Istio may make the network temporarily unavailable during startup. If diff --git a/product_docs/docs/postgres_for_kubernetes/1/wal_archiving.mdx b/product_docs/docs/postgres_for_kubernetes/1/wal_archiving.mdx index 2ea35ce22a..13275d144b 100644 --- a/product_docs/docs/postgres_for_kubernetes/1/wal_archiving.mdx +++ b/product_docs/docs/postgres_for_kubernetes/1/wal_archiving.mdx @@ -32,7 +32,8 @@ For full documentation, configuration options, and best practices, see the {{name.ln}} still supports WAL archiving natively through the `.spec.backup.barmanObjectStore` field. While still functional, **this -interface is deprecated** and will be removed in a future release. +interface is deprecated**: {{name.ln}} 1.31 is the last minor release that +ships with it, and it is removed starting from 1.32.0. !!!info Important All new deployments are strongly encouraged to adopt the plugin-based