Merge branch 'master' into gh-pages

This commit is contained in:
Felix Kunde
2026-07-28 00:55:23 +02:00
184 changed files with 17953 additions and 9396 deletions
+104 -25
View File
@@ -65,7 +65,10 @@ the `PGVERSION` environment variable is set for the database pods. Since
In-place major version upgrades can be configured to be executed by the
operator with the `major_version_upgrade_mode` option. By default, it is
enabled (mode: `manual`). In any case, altering the version in the manifest
will trigger a rolling update of pods to update the `PGVERSION` env variable.
will update the desired `PGVERSION`. If `maintenanceWindows` are configured,
major-version-related pod rotation is deferred until the next maintenance
window. Without maintenance windows, the operator will trigger a rolling
update of pods to apply the new `PGVERSION`.
Spilo's [`configure_spilo`](https://github.com/zalando/spilo/blob/master/postgres-appliance/scripts/configure_spilo.py)
script will notice the version mismatch but start the current version again.
@@ -92,10 +95,11 @@ Thus, the `full` mode can create drift between desired and actual state.
### Upgrade during maintenance windows
When `maintenanceWindows` are defined in the Postgres manifest the operator
will trigger a major version upgrade only during these periods. Make sure they
are at least twice as long as your configured `resync_period` to guarantee
that operator actions can be triggered.
When `maintenanceWindows` are defined in the Postgres manifest or in the global
config the operator will trigger major-version-related pod rotation and the
major version upgrade only during these periods. Make sure they are at least
twice as long as your configured `resync_period` to guarantee that operator
actions can be triggered.
### Upgrade annotations
@@ -635,9 +639,11 @@ masters in single-node clusters and/or the last remaining running instance in a
cluster.
## PDB for critical operations
The `MinAvailable` parameter of this PDB is equal to the `numberOfInstances` set in the
cluster manifest, while label selector includes `critical-operation=true` condition. This
allows to protect all pods of a cluster, given they are labeled accordingly.
The `MaxUnavailable` parameter of this PDB is set to `0`, while label selector includes
`critical-operation=true` condition. This blocks voluntary disruptions for all pods of a
cluster that are labeled accordingly, without leaving an unsatisfiable budget behind when
no pods carry the label (which previously kept monitoring alerts like
`KubePdbNotEnoughHealthyPods` firing permanently).
For example, Operator labels all Spilo pods with `critical-operation=true` during the major
version upgrade run. You may want to protect cluster pods during other critical operations
by assigning the label to pods yourself or using other means of automation.
@@ -647,7 +653,14 @@ The PDB is only relaxed in two scenarios:
* If a cluster is scaled down to `0` instances (e.g. for draining nodes)
* If the PDB is disabled in the configuration (`enable_pod_disruption_budget`)
The PDBs are still in place having `MinAvailable` set to `0`. Disabling PDBs
The PDBs are still in place but fully relaxed: the primary PDB with `MinAvailable`
set to `0` and the critical operations PDB with `MaxUnavailable` set to `100%`.
The two PDBs intentionally use different budget fields matching their purposes:
the primary PDB guarantees a minimum count of always-present pods
(`MinAvailable`), while the critical operations PDB freezes disruptions for
whatever pods currently carry the `critical-operation=true` label - a
usually-empty set, which `MaxUnavailable: 0` expresses without producing an
unsatisfiable budget while idle. Disabling PDBs
helps avoiding blocking Kubernetes upgrades in managed K8s environments at the
cost of prolonged DB downtime. See PR [#384](https://github.com/zalando/postgres-operator/pull/384)
for the use case.
@@ -721,9 +734,9 @@ that cannot be overridden to guarantee core functionality. Only variables with
shipping to be specified differently. There are three ways to specify extra
environment variables (or override existing ones) for database pods:
* [Via ConfigMap](#via-configmap)
* [Via Secret](#via-secret)
* [Via Postgres Cluster Manifest](#via-postgres-cluster-manifest)
* [Globally via ConfigMap](#via-configmap)
* [Globally via Secret](#via-secret)
* [Locally via Postgres Cluster Manifest](#via-postgres-cluster-manifest)
The first two options must be referenced from the operator configuration
making them global settings for all Postgres cluster the operator watches.
@@ -732,10 +745,10 @@ environment variables. Another case could be to provide custom cloud
provider or backup settings.
The last options allows for specifying environment variables individual to
every cluster via the `env` section in the manifest. For example, if you use
individual backup locations for each of your clusters. Or you want to disable
WAL archiving for a certain cluster by setting `WAL_S3_BUCKET`, `WAL_GS_BUCKET`
or `AZURE_STORAGE_ACCOUNT` to an empty string.
every cluster via the `env` or `envFrom` section in the manifest. For example,
if you use individual backup locations for each of your clusters. Or you want
to disable WAL archiving for a certain cluster by setting `WAL_S3_BUCKET`,
`WAL_GS_BUCKET` or `AZURE_STORAGE_ACCOUNT` to an empty string.
The operator will give precedence to environment variables in the following
order (e.g. a variable defined in 4. overrides a variable with the same name
@@ -749,6 +762,9 @@ in 5.):
6. Pod environment config map via operator config
7. WAL and logical backup settings from operator config
The `envFrom` section is treated separately and allows for a very flexible
local configuration referencing a ConfigMap or a Secret.
### Via ConfigMap
The ConfigMap with the additional settings is referenced in the operator's
@@ -887,15 +903,13 @@ cluster manifest. In the case any of these variables are omitted from the
manifest, the operator configuration settings `enable_master_load_balancer` and
`enable_replica_load_balancer` apply. Note that the operator settings affect
all Postgresql services running in all namespaces watched by the operator.
If load balancing is enabled two default annotations will be applied to its
services:
If load balancing is enabled the following default annotation will be applied to
its services:
- `external-dns.alpha.kubernetes.io/hostname` with the value defined by the
operator configs `master_dns_name_format` and `replica_dns_name_format`.
This value can't be overwritten. If any changing in its value is needed, it
MUST be done changing the DNS format operator config parameters; and
- `service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout` with
a default value of "3600".
There are multiple options to specify service annotations that will be merged
with each other and override in the following order (where latter take
@@ -926,6 +940,41 @@ For the `external-dns.alpha.kubernetes.io/hostname` annotation the `-pooler`
suffix will be appended to the cluster name used in the template which is
defined in `master|replica_dns_name_format`.
## Node Ports
Alternatively to Load Balancers Node Ports can be used. Kubernetes services with type
`NodePort` redirect traffic from a specified port on your kubernetes nodes to your service.
To expose your services to an external network with NodePorts you can set `enableMasterNodePort` and/or `enableReplicaNodePort` to `true`
in your cluster manifest. In the case any of these variables are omitted from the manifest, the operator configuration settings `enable_master_node_port` and `enable_replica_node_port` apply.
Note that the operator settings affect all Postgresql services running in all namespaces watched
by the operator.
**Enabling a NodePort configuration will override the corresponding LoadBalancer configuration.**
There are multiple options to specify service annotations that will be merged
with each other and override in the following order (where latter take
precedence):
1. Globally configured `custom_service_annotations`
2. `serviceAnnotations` specified in the cluster manifest
3. `masterServiceAnnotations` and `replicaServiceAnnotations` specified in the cluster manifest
Load-Balancer specific annotations are not applied.
Node port services can also be configured for the [connection pooler](user.md#connection-pooler) pods
with the manifest flags `enableMasterPoolerNodePort` and/or `enableReplicaPoolerNodePort` or in the operator configuration with `enable_master_pooler_node_port`
and/or `enable_replica_pooler_node_port`.
To configure which ports Kubernetes should use for your NodePort service you can configure ports in your cluster manifest
for each type:
- masterNodePort
- masterPoolerNodePort
- replicaNodePort
- replicaPoolerNodePort
When not defined or set to 0 kubernetes will choose a port for you from [your kubernetes cluster's configured range](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport).
## Running periodic 'autorepair' scans of K8s objects
The Postgres Operator periodically scans all K8s objects belonging to each
@@ -1057,6 +1106,32 @@ configuration:
wal_s3_bucket: your-backup-path
```
Alternatively, if your cluster uses EKS with OIDC, you can use
[IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)
(IAM Roles for Service Accounts) instead of kube2iam. Set `irsa_role_arn` to
the full ARN of the IAM role:
**OperatorConfiguration**
```yaml
apiVersion: "acid.zalan.do/v1"
kind: OperatorConfiguration
metadata:
name: postgresql-operator-configuration
configuration:
aws_or_gcp:
aws_region: eu-central-1
irsa_role_arn: arn:aws:iam::123456789012:role/postgres-pod-role
wal_s3_bucket: your-backup-path
```
When `irsa_role_arn` is set the operator annotates the pod service account with
`eks.amazonaws.com/role-arn` on every reconcile. The EKS OIDC webhook then
injects an AWS web identity token into each pod, which takes precedence over
the EC2 metadata credentials used by kube2iam. Both `kube_iam_role` and
`irsa_role_arn` can coexist during a migration — existing pods retain the
kube2iam annotation until they are rotated, at which point only IRSA is used.
The referenced IAM role should contain the following privileges to make sure
Postgres can send compressed WAL files to the given S3 bucket:
@@ -1167,6 +1242,7 @@ aws_or_gcp:
# additional_secret_mount_path: ""
# aws_region: eu-central-1
# kube_iam_role: ""
# irsa_role_arn: ""
# log_s3_bucket: ""
# wal_s3_bucket: ""
wal_gs_bucket: "postgres-backups-bucket-28302F2" # name of bucket on where to save the WAL-E logs
@@ -1216,6 +1292,7 @@ aws_or_gcp:
additional_secret_mount_path: "/var/secrets/google" # or where ever you want to mount the file
# aws_region: eu-central-1
# kube_iam_role: ""
# irsa_role_arn: ""
# log_s3_bucket: ""
# wal_s3_bucket: ""
wal_gs_bucket: "postgres-backups-bucket-28302F2" # name of bucket on where to save the WAL-E logs
@@ -1312,7 +1389,7 @@ aws_or_gcp:
If cluster members have to be (re)initialized restoring physical backups
happens automatically either from the backup location or by running
[pg_basebackup](https://www.postgresql.org/docs/17/app-pgbasebackup.html)
[pg_basebackup](https://www.postgresql.org/docs/18/app-pgbasebackup.html)
on one of the other running instances (preferably replicas if they do not lag
behind). You can test restoring backups by [cloning](user.md#how-to-clone-an-existing-postgresql-cluster)
clusters.
@@ -1346,10 +1423,12 @@ If you are using [additional environment variables](#custom-pod-environment-vari
to access your backup location you have to copy those variables and prepend
the `STANDBY_` prefix for Spilo to find the backups and WAL files to stream.
Alternatively, standby clusters can also stream from a remote primary cluster.
Standby clusters can also stream from a remote primary cluster.
You have to specify the host address. Port is optional and defaults to 5432.
Note, that only one of the options (`s3_wal_path`, `gs_wal_path`,
`standby_host`) can be present under the `standby` top-level key.
You can combine `standby_host` with either `s3_wal_path` or `gs_wal_path`
for additional redundancy. Note that `s3_wal_path` and `gs_wal_path` are
mutually exclusive. At least one of `s3_wal_path`, `gs_wal_path`, or
`standby_host` must be specified under the `standby` top-level key.
## Logical backups
@@ -1498,7 +1577,7 @@ make docker
# build in image in minikube docker env
eval $(minikube docker-env)
docker build -t ghcr.io/zalando/postgres-operator-ui:v1.15.1 .
docker buildx build --load -t ghcr.io/zalando/postgres-operator-ui:v2.0.0 .
# apply UI manifests next to a running Postgres Operator
kubectl apply -f manifests/
+15 -34
View File
@@ -27,23 +27,10 @@ git clone https://github.com/zalando/postgres-operator.git
## Building the operator
We use [Go Modules](https://github.com/golang/go/wiki/Modules) for handling
dependencies. When using Go below v1.13 you need to explicitly enable Go modules
by setting the `GO111MODULE` environment variable to `on`. The make targets do
this for you, so simply run
We use [Go Modules](https://github.com/golang/go/wiki/Modules) for handling dependencies.
Run `go mod vendor && go mod tidy` to install them.
```bash
make deps
```
This would take a while to complete. You have to redo `make deps` every time
your dependencies list changes, i.e. after adding a new library dependency.
Build the operator with the `make docker` command. You may define the TAG
variable to assign an explicit tag to your Docker image and the IMAGE to set
the image name. By default, the tag is computed with
`git describe --tags --always --dirty` and the image is
`ghcr.io/zalando/postgres-operator`
Build the operator with the `make docker` command. You may define the TAG variable to assign an explicit tag to your Docker image and the IMAGE to set the image name. By default, the tag is computed with `git describe --tags --always --dirty` and the image is `ghcr.io/zalando/postgres-operator`.
```bash
export TAG=$(git describe --tags --always --dirty)
@@ -223,14 +210,13 @@ dlv connect 127.0.0.1:DLV_PORT
Prerequisites:
```bash
make deps
make mocks
```
To run all unit tests, you can simply do:
```bash
go test ./pkg/...
make test
```
In case if you need to debug your unit test, it's possible to use delve:
@@ -300,8 +286,7 @@ Please run flake8 [before submitting a PR](http://flake8.pycqa.org/en/latest/use
In the case you want to add functionality to the operator that shall be
controlled via the operator configuration there are a few places that need to
be updated. As explained [here](reference/operator_parameters.md), it's possible
to configure the operator either with a ConfigMap or CRD, but currently we aim
to synchronize parameters everywhere.
to configure the operator either with a ConfigMap or CRD.
When choosing a parameter name for a new option in a Postgres cluster manifest,
keep in mind the naming conventions there. We use `camelCase` for manifest
@@ -324,32 +309,28 @@ manifest files:
Postgres manifest parameters are defined in the [api package](https://github.com/zalando/postgres-operator/blob/master/pkg/apis/acid.zalan.do/v1/postgresql_type.go).
The operator behavior has to be implemented at least in [k8sres.go](https://github.com/zalando/postgres-operator/blob/master/pkg/cluster/k8sres.go).
Validation of CRD parameters is controlled in [crds.go](https://github.com/zalando/postgres-operator/blob/master/pkg/apis/acid.zalan.do/v1/crds.go).
Please, reflect your changes in tests, for example in:
* [config_test.go](https://github.com/zalando/postgres-operator/blob/master/pkg/util/config/config_test.go)
* [k8sres_test.go](https://github.com/zalando/postgres-operator/blob/master/pkg/cluster/k8sres_test.go)
* [util_test.go](https://github.com/zalando/postgres-operator/blob/master/pkg/apis/acid.zalan.do/v1/util_test.go)
### Updating manifest files
### Generating the CRDs
For the CRD-based configuration, please update the following files:
* the default [OperatorConfiguration](https://github.com/zalando/postgres-operator/blob/master/manifests/postgresql-operator-default-configuration.yaml)
* the CRD's [validation](https://github.com/zalando/postgres-operator/blob/master/manifests/operatorconfiguration.crd.yaml)
* the CRD's validation in the [Helm chart](https://github.com/zalando/postgres-operator/blob/master/charts/postgres-operator/crds/operatorconfigurations.yaml)
Add new options also to the Helm chart's [values file](https://github.com/zalando/postgres-operator/blob/master/charts/postgres-operator/values.yaml) file.
It follows the OperatorConfiguration CRD layout. Nested values will be flattened for the ConfigMap.
Last but no least, update the [ConfigMap](https://github.com/zalando/postgres-operator/blob/master/manifests/configmap.yaml) manifest example as well.
The CRDs can be automatically generated from the go structs. Use the correct kubebuilder annotations for defining the validation, constraints or default values etc.. Run `make` to update the CRDs which are stored in three locations:
- In the Go api package
- The example manifests folder
- The helm chart folder
### Updating documentation
Finally, add a section for each new configuration option and/or cluster manifest
Config changes need to be reflected in the Helm chart's [values file](https://github.com/zalando/postgres-operator/blob/master/charts/postgres-operator/values.yaml), too. It follows the OperatorConfiguration CRD layout. Nested values will be flattened for the ConfigMap.
Add a section for each new configuration option and/or cluster manifest
parameter in the reference documents:
* [config reference](reference/operator_parameters.md)
* [manifest reference](reference/cluster_manifest.md)
It also helps users to explain new features with examples in the
[administrator docs](administrator.md).
It can also help other K8s admins to explain new features with examples in the
[administrator docs](administrator.md) and also update the [OperatorConfiguration CRD](https://github.com/zalando/postgres-operator/blob/master/manifests/postgresql-operator-default-configuration.yaml) and [ConfigMap](https://github.com/zalando/postgres-operator/blob/master/manifests/configmap.yaml) manifest examples.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 KiB

-101
View File
@@ -1,101 +0,0 @@
\documentclass{article}
\usepackage{tikz}
\usepackage[graphics,tightpage,active]{preview}
\usetikzlibrary{arrows, shadows.blur, positioning, fit, calc, backgrounds}
\usepackage{lscape}
\pagenumbering{gobble}
\PreviewEnvironment{tikzpicture}
\PreviewEnvironment{equation}
\PreviewEnvironment{equation*}
\newlength{\imagewidth}
\newlength{\imagescale}
\pagestyle{empty}
\thispagestyle{empty}
\begin{document}
\begin{center}
\begin{tikzpicture}[
scale=0.5,transform shape,
font=\sffamily,
every matrix/.style={ampersand replacement=\&,column sep=2cm,row sep=2cm},
operator/.style={draw,solid,thick,circle,fill=red!20,inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
component/.style={draw,solid,thick,rounded corners,fill=yellow!20,inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
border/.style={draw,dashed,rounded corners,fill=gray!20,inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
pod/.style={draw,solid,thick,rounded corners,fill=blue!20, inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
service/.style={draw,solid,thick,rounded corners,fill=blue!20, inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
endpoint/.style={draw,solid,thick,rounded corners,fill=blue!20, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
secret/.style={draw,solid,thick,rounded corners,fill=blue!20, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
pvc/.style={draw,solid,thick,rounded corners,fill=blue!20, inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
label/.style={rectangle,inner sep=0,outer sep=0},
to/.style={->,>=stealth',shorten >=1pt,semithick,font=\sffamily\footnotesize},
every node/.style={align=center}]
% Position the nodes using a matrix layout
\matrix{
\& \node[component] (crd) {CRD}; \\
\& \node[operator] (operator) {Operator}; \\
\path
node[service] (service-master) {Master}
node[label, right of=service-master] (service-middle) {}
node[label, below of=service-middle] (services-label) {Services}
node[service, right=.5cm of service-master] (service-replica) {Replica}
node[border, behind path,
fit=(service-master)(service-replica)(services-label)
] (services) {};
\&
\node[component] (sts) {Statefulset}; \& \node[component] (pdb) {Pod Disruption Budget}; \\
\path
node[service] (master-endpoint) {Master}
node[service, right=.5cm of master-endpoint] (replica-endpoint) {Replica}
node[label, right of=master-endpoint] (endpoint-middle) {}
node[label, below of=endpoint-middle] (endpoint-label) {Endpoints}
node[border, behind path,
fit=(master-endpoint)(replica-endpoint)(endpoint-label)
] (endpoints) {}; \&
\node[component] (pod-template) {Pod Template}; \&
\node[border] (secrets) {
\begin{tikzpicture}[]
\node[secret] (users-secret) at (0, 0) {Users};
\node[secret] (robots-secret) at (2, 0) {Robots};
\node[secret] (standby-secret) at (4, 0) {Standby};
\end{tikzpicture} \\
Secrets
}; \\ \&
\path
node[pod] (replica1-pod) {Replica}
node[pod, left=.5cm of replica1-pod] (master-pod) {Master}
node[pod, right=.5cm of replica1-pod] (replica2-pod) {Replica}
node[label, below of=replica1-pod] (pod-label) {Pods}
node[border, behind path,
fit=(master-pod)(replica1-pod)(replica2-pod)(pod-label)
] (pods) {}; \\ \&
\path
node[pvc] (replica1-pvc) {Replica}
node[pvc, left=.5cm of replica1-pvc] (master-pvc) {Master}
node[pvc, right=.5cm of replica1-pvc] (replica2-pvc) {Replica}
node[label, below of=replica1-pvc] (pvc-label) {Persistent Volume Claims}
node[border, behind path,
fit=(master-pvc)(replica1-pvc)(replica2-pvc)(pvc-label)
] (pvcs) {}; \&
\\ \& \\
};
% Draw the arrows between the nodes and label them.
\draw[to] (crd) -- node[midway,above] {} node[midway,below] {} (operator);
\draw[to] (operator) -- node[midway,above] {} node[midway,below] {} (sts);
\draw[to] (operator) -- node[midway,above] {} node[midway,below] {} (secrets);
\draw[to] (operator) -| node[midway,above] {} node[midway,below] {} (pdb);
\draw[to] (service-master) -- node[midway,above] {} node[midway,below] {} (master-endpoint);
\draw[to] (service-replica) -- node[midway,above] {} node[midway,below] {} (replica-endpoint);
\draw[to] (master-pod) -- node[midway,above] {} node[midway,below] {} (master-pvc);
\draw[to] (replica1-pod) -- node[midway,above] {} node[midway,below] {} (replica1-pvc);
\draw[to] (replica2-pod) -- node[midway,above] {} node[midway,below] {} (replica2-pvc);
\draw[to] (operator) -| node[midway,above] {} node[midway,below] {} (services);
\draw[to] (sts) -- node[midway,above] {} node[midway,below] {} (pod-template);
\draw[to] (pod-template) -- node[midway,above] {} node[midway,below] {} (pods);
\end{tikzpicture}
\end{center}
\end{document}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

-92
View File
@@ -1,92 +0,0 @@
\documentclass{article}
\usepackage{tikz}
\usepackage[graphics,tightpage,active]{preview}
\usetikzlibrary{arrows, shadows.blur, positioning, fit, calc, backgrounds}
\usepackage{lscape}
\pagenumbering{gobble}
\PreviewEnvironment{tikzpicture}
\PreviewEnvironment{equation}
\PreviewEnvironment{equation*}
\newlength{\imagewidth}
\newlength{\imagescale}
\pagestyle{empty}
\thispagestyle{empty}
\begin{document}
\begin{center}
\begin{tikzpicture}[
scale=0.5,transform shape,
font=\sffamily,
every matrix/.style={ampersand replacement=\&,column sep=2cm,row sep=2cm},
pod/.style={draw,solid,thick,circle,fill=red!20,inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
component/.style={draw,solid,thick,rounded corners,fill=yellow!20,inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
border/.style={draw,dashed,rounded corners,fill=gray!20,inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
volume/.style={draw,solid,thick,rounded corners,fill=blue!20, inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
sidecar/.style={draw,solid,thick,rounded corners,fill=blue!20, inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
k8s-label/.style={draw,solid,thick,rounded corners,fill=blue!20, minimum width=1.5cm, inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
affinity/.style={draw,solid,thick,rounded corners,fill=blue!20, minimum width=2cm, inner sep=.3cm, blur shadow={shadow blur steps=5,shadow blur extra rounding=1.3pt}},
label/.style={rectangle,inner sep=0,outer sep=0},
to/.style={->,>=stealth',shorten >=1pt,semithick,font=\sffamily\footnotesize},
every node/.style={align=center}]
% Position the nodes using a matrix layout
\matrix{
\path
node[k8s-label] (app-label) {App}
node[k8s-label, right=.25cm of app-label] (role-label) {Role}
node[k8s-label, right=.25cm of role-label] (custom-label) {Custom}
node[label, below of=role-label] (k8s-label-label) {K8s Labels}
node[border, behind path,
fit=(app-label)(role-label)(custom-label)(k8s-label-label)
] (k8s-labels) {}; \& \&
\path
node[affinity] (affinity) {Affinity}
node[label, right=.25cm of affinity] (affinity-middle) {}
node[affinity, right=.25cm of affinity-middle] (anti-affinity) {Anti-affinity}
node[label, below of=affinity-middle] (affinity-label) {Assigning to nodes}
node[border, behind path,
fit=(affinity)(anti-affinity)(affinity-label)
] (affinity) {}; \\
\& \node[pod] (pod) {Pod}; \& \\
\path
node[volume, minimum width={width("shm-volume")}] (data-volume) {Data}
node[volume, right=.25cm of data-volume, minimum width={width("shm-volume")}] (tokens-volume) {Tokens}
node[volume, right=.25cm of tokens-volume] (shm-volume) {/dev/shm}
node[label, below of=tokens-volume] (volumes-label) {Volumes}
node[border, behind path,
fit=(data-volume)(shm-volume)(tokens-volume)(volumes-label)
] (volumes) {}; \&
\node[component] (spilo) {Spilo}; \&
\node[sidecar] (scalyr) {Scalyr}; \& \\ \&
\path
node[component] (patroni) {Patroni}
node[component, below=.25cm of patroni] (postgres) {PostgreSQL}
node[border, behind path,
fit=(postgres)(patroni)
] (spilo-components) {}; \&
\path
node[sidecar] (custom-sidecar1) {User defined}
node[label, right=.25cm of custom-sidecar1] (sidecars-middle) {}
node[sidecar, right=.25cm of sidecars-middle] (custom-sidecar2) {User defined}
node[label, below of=sidecars-middle] (sidecars-label) {Custom sidecars}
node[border, behind path,
fit=(custom-sidecar1)(custom-sidecar2)(sidecars-label)
] (sidecars) {};
\\ \& \\
};
% Draw the arrows between the nodes and label them.
\draw[to] (pod) to [bend left=25] (volumes);
\draw[to] (pod) to [bend left=25] (k8s-labels);
\draw[to] (pod) to [bend right=25] (affinity);
\draw[to] (pod) to [bend right=25] (scalyr);
\draw[to] (pod) to [bend right=25] (sidecars);
\draw[to] (pod) -- node[midway,above] {} node[midway,below] {} (spilo);
\draw[to] (spilo) -- node[midway,above] {} node[midway,below] {} (spilo-components);
\end{tikzpicture}
\end{center}
\end{document}
+2 -9
View File
@@ -47,15 +47,8 @@ flexibility to complement it with other tools like [ZMON](https://opensource.zal
Here is a diagram, that summarizes what would be created by the operator, when a
new Postgres cluster CRD is submitted:
![postgresql-operator](diagrams/operator.png "K8s resources, created by operator")
This picture is not complete without an overview of what is inside a single
cluster pod, so let's zoom in:
![pod](diagrams/pod.png "Database pod components")
These two diagrams should help you to understand the basics of what kind of
functionality the operator provides.
![Features](diagrams/neutral_operator_dark.png#gh-dark-mode-only)
![Features](diagrams/neutral_operator_light.png#gh-light-mode-only)
## Status
+34
View File
@@ -0,0 +1,34 @@
<h1>Migrate from v1 to v2</h1>
Version 2.0 changes some default settings and removes deprecated fields. Please read the following sections before upgrading the Postgres Operator deployment.
## scram-sha-256 by default
The new operator will default password encryption to `scram-sha-256`. Unless you configure `password_encryption: md5` in the manifest under `spec.postgresql.parameters` the operator will encrypt existing passwords in the secrets with `scram-sha-256` and alter the database passwords. Make sure that your used clients and drivers support `scram-sha-256` as pods will get rotated in rolling fashion after updating to Postgres Operator v2.
The default Spilo image (`spilo-18:4.1-p2`) still configures the pg_hba.conf file to allow `md5` passwords but Postgres will validate `scram-sha-256` passwords correctly. Passwords of users that are not managed by the operator and are still `md5` encrypted need be altered before the next tagged Spilo image which will drop `md5` completely.
## K8s Endpoints are deprecated
If your current operator v1.x deployment is relying on K8s endpoints (the default setup) for Patroni to manage the HA state you have to start planning to switch to configmaps, because endpoints are deprecated from K8s 1.33 onwards. The default of the corresponding parameter `kubernetes_use_configmaps` is changing to `true` with v2.0 of the operator. This means you have to explicity set it to `false` in your configuration before you start the upgrade.
We explicitly warn you to go straight to configmap-based HA management with database clusters that use replicas, because there's is a danger to run into split-brain scenarios during the rolling update of pods when there exists a leader endpoint and leader config map at the same time. To play it safe, here is what you should do - before or after the Postgres Operator upgrade:
1. Scale-in all your database clusters to only one primary instance. This can be done by changing the global config options `max_instances` and `min_instances` to `1`. If you have allowed users to ignore globally defined instance limits by configuring an `ignore_instance_limits_annotation_key`, remove it for now.
2. Wait for all clusters to be healthy and change the `kubernetes_use_configmaps` setting to `true`. This will trigger the replacement of the primary pod of all clusters and cause downtime for as long as the pods are rescheduled and start up.
3. Check again that all clusters are healthy with configmaps created. There should be three for each cluster called like cluster name with suffixes `-config`, `-failover` and `-leader`. Now, revert the changes from step 1 and scale-out the to number of instances set in the manifests.
4. The orphaned endpoints, which use the same names like the new configmaps, have to be deleted by you or your K8s garbage collection.
## Dropped manifest fields
We removed some deprecated fields from the Postgresql CRD. Please, make sure that you do not specify them in any of your cluster manifests. If you do, switch to the listed alternative:
| Removed field in v2 | Alternative |
| --- | --- |
| init_containers | initContainers |
| pod_priority_class_name | podPriorityClassName |
| replicaLoadBalancer | enableReplicaLoadBalancer|
| useLoadBalancer | enableMasterLoadBalancer |
+71 -13
View File
@@ -85,6 +85,10 @@ These parameters are grouped directly under the `spec` key in the manifest.
requires a custom Spilo image. Note the FSGroup of a Pod cannot be changed
without recreating a new Pod. Optional.
* **livenessProbe**
Allows for adding a liveness probe to the Spilo container to detect if it's
running properly.
* **enableMasterLoadBalancer**
boolean flag to override the operator defaults (set by the
`enable_master_load_balancer` parameter) to define whether to enable the load
@@ -109,16 +113,61 @@ These parameters are grouped directly under the `spec` key in the manifest.
* **allowedSourceRanges**
when one or more load balancers are enabled for the cluster, this parameter
defines the comma-separated range of IP networks (in CIDR-notation). The
corresponding load balancer is accessible only to the networks defined by
this parameter. Optional, when empty the load balancer service becomes
inaccessible from outside of the Kubernetes cluster.
defines the comma-separated range of IP networks (in CIDR-notation). Both
IPv4 (e.g. `192.168.1.0/24`) and IPv6 (e.g. `fd01::/48`) CIDR ranges are
supported. The corresponding load balancer is accessible only to the networks
defined by this parameter. Optional, when empty the load balancer service
becomes inaccessible from outside of the Kubernetes cluster.
* **enableMasterNodePort**
boolean flag to override the operator defaults (set by the
`enable_master_node_port` parameter) to define whether to enable the node
port pointing to the Postgres primary. Optional. Overrides `enableMasterLoadBalancer`.
* **enableMasterPoolerNodePort**
boolean flag to override the operator defaults (set by the
`enable_master_pooler_node_port` parameter) to define whether to enable
the node port for master pooler pods pointing to the Postgres primary.
Optional. Overrides `enableMasterPoolerLoadBalancer`.
* **enableReplicaNodePort**
boolean flag to override the operator defaults (set by the
`enable_replica_node_port` parameter) to define whether to enable the node
port pointing to the Postgres standby instances. Optional. Overrides `enableReplicaLoadBalancer`.
* **enableReplicaPoolerNodePort**
boolean flag to override the operator defaults (set by the
`enable_replica_pooler_node_port` parameter) to define whether to enable
the node port for replica pooler pods pointing to the Postgres standby
instances. Optional. Overrides `enableReplicaPoolerLoadBalancer`.
* **masterNodePort**
integer flag to specify a port number for the node port to the Postgres primary.
Only used when `enableMasterNodePort` or `enable_master_node_port` are enabled.
Optional. Kubernetes will provide a port number for you if not specified.
* **masterPoolerNodePort**
integer flag to specify a port number for the node port for the master pooler pods pointing to the Postgres primary.
Only used when `enableMasterPoolerNodePort` or `enable_master_pooler_node_port` are enabled.
Optional. Kubernetes will provide a port number for you if not specified.
* **replicaNodePort**
integer flag to specify a port number for the node port pointing to the Postgres standby instances.
Only used when `enableReplicaNodePort` or `enable_replica_node_port` are enabled.
Optional. Kubernetes will provide a port number for you if not specified.
* **replicaPoolerNodePort**
integer flag to specify a port number for the node port for the replica pooler pods pointing to the Postgres standby instances
Only used when `enableReplicaPoolerNodePort` or `enable_replica_pooler_node_port` are enabled.
Optional. Kubernetes will provide a port number for you if not specified.
* **maintenanceWindows**
a list which defines specific time frames when certain maintenance operations
such as automatic major upgrades or master pod migration. Accepted formats
are "01:00-06:00" for daily maintenance windows or "Sat:00:00-04:00" for specific
days, with all times in UTC.
such as automatic major upgrades or master pod migration are allowed to happen.
Accepted formats are "01:00-06:00" for daily maintenance windows or
"Sat:00:00-04:00" for specific days, with all times in UTC. Note, when the
global config option `enable_maintenance_windows` is false, the specified
windows will be ignored.
* **users**
a map of usernames to user flags for the users that should be created in the
@@ -457,22 +506,31 @@ under the `clone` top-level key and do not affect the already running cluster.
On startup, an existing `standby` top-level key creates a standby Postgres
cluster streaming from a remote location - either from a S3 or GCS WAL
archive or a remote primary. Only one of options is allowed and required
if the `standby` key is present.
archive, a remote primary, or a combination of both. At least one of
`s3_wal_path`, `gs_wal_path`, or `standby_host` must be specified.
Note that `s3_wal_path` and `gs_wal_path` are mutually exclusive.
* **s3_wal_path**
the url to S3 bucket containing the WAL archive of the remote primary.
Can be combined with `standby_host` for additional redundancy.
* **gs_wal_path**
the url to GS bucket containing the WAL archive of the remote primary.
Can be combined with `standby_host` for additional redundancy.
* **standby_host**
hostname or IP address of the primary to stream from.
Can be specified alone or combined with either `s3_wal_path` or `gs_wal_path`.
* **standby_port**
TCP port on which the primary is listening for connections. Patroni will
use `"5432"` if not set.
* **standby_primary_slot_name**
name of the replication slot to use on the primary server when streaming
from a remote primary. See the Patroni documentation
[here](https://patroni.readthedocs.io/en/latest/standby_cluster.html) for more details. Optional.
## Volume properties
Those parameters are grouped under the `volume` top-level key and define the
@@ -496,11 +554,11 @@ properties of the persistent storage that stores Postgres data.
* **iops**
When running the operator on AWS the latest generation of EBS volumes (`gp3`)
allows for configuring the number of IOPS. Maximum is 16000. Optional.
allows for configuring the number of IOPS. Maximum is 80000. Optional.
* **throughput**
When running the operator on AWS the latest generation of EBS volumes (`gp3`)
allows for configuring the throughput in MB/s. Maximum is 1000. Optional.
allows for configuring the throughput in MB/s. Maximum is 2000. Optional.
* **selector**
A label query over PVs to consider for binding. See the [Kubernetes
@@ -638,7 +696,7 @@ the global configuration before adding the `tls` section'.
## Change data capture streams
This sections enables change data capture (CDC) streams via Postgres'
[logical decoding](https://www.postgresql.org/docs/17/logicaldecoding.html)
[logical decoding](https://www.postgresql.org/docs/18/logicaldecoding.html)
feature and `pgoutput` plugin. While the Postgres operator takes responsibility
for providing the setup to publish change events, it relies on external tools
to consume them. At Zalando, we are using a workflow based on
@@ -671,7 +729,7 @@ can have the following properties:
The CDC operator is following the [outbox pattern](https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/).
The application is responsible for putting events into a (JSON/B or VARCHAR)
payload column of the outbox table in the structure of the specified target
event type. The operator will create a [PUBLICATION](https://www.postgresql.org/docs/17/logical-replication-publication.html)
event type. The operator will create a [PUBLICATION](https://www.postgresql.org/docs/18/logical-replication-publication.html)
in Postgres for all tables specified for one `database` and `applicationId`.
The CDC operator will consume from it shortly after transactions are
committed to the outbox table. The `idColumn` will be used in telemetry for
@@ -23,6 +23,12 @@ The following command-line options are supported for the operator:
off can can be overridden by the aforementioned operator configuration
option.
* **-kubeqps**
set the maximum number of Kubernetes API requests per second. Default is 10.
* **-kubeburst**
set the burst limit for Kubernetes API requests, allowing temporary spikes beyond the configured QPS. Default is 20.
In addition to that, standard [glog
flags](https://godoc.org/github.com/golang/glog) are also supported. For
instance, one may want to add `-alsologtostderr` and `-v=8` to debug the
+68 -31
View File
@@ -42,14 +42,14 @@ and change it.
To test the CRD-based configuration locally, use the following
```bash
kubectl create -f manifests/operatorconfiguration.crd.yaml # registers the CRD
kubectl create -f manifests/postgresql-operator-default-configuration.yaml
```
kubectl create -f manifests/operatorconfiguration.crd.yaml # registers the CRD
kubectl create -f manifests/postgresql-operator-default-configuration.yaml
kubectl create -f manifests/operator-service-account-rbac.yaml
kubectl create -f manifests/postgres-operator.yaml # set the env var as mentioned above
kubectl create -f manifests/operator-service-account-rbac.yaml
kubectl create -f manifests/postgres-operator.yaml # set the env var as mentioned above
kubectl get operatorconfigurations postgresql-operator-default-configuration -o yaml
kubectl get operatorconfigurations postgresql-operator-default-configuration -o yaml
```
The CRD-based configuration is more powerful than the one based on ConfigMaps
@@ -79,11 +79,6 @@ Those are top-level keys, containing both leaf keys and groups.
Instruct the operator to create/update the CRDs. If disabled the operator will rely on the CRDs being managed separately.
The default is `true`.
* **enable_crd_validation**
*deprecated*: toggles if the operator will create or update CRDs with
[OpenAPI v3 schema validation](https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#validation)
The default is `true`. `false` will be ignored, since `apiextensions.io/v1` requires a structural schema definition.
* **crd_categories**
The operator will register CRDs in the `all` category by default so that they will be returned by a `kubectl get all` call. You are free to change categories or leave them empty.
@@ -105,15 +100,13 @@ Those are top-level keys, containing both leaf keys and groups.
Kubernetes-native DCS).
* **kubernetes_use_configmaps**
Select if setup uses endpoints (default), or configmaps to manage leader when
Select if setup uses endpoints or configmaps (default) to manage leader when
DCS is kubernetes (not etcd or similar). In OpenShift it is not possible to
use endpoints option, and configmaps is required. Starting with K8s 1.33,
endpoints are marked as deprecated. It's recommended to switch to config maps
instead. But, to do so make sure you scale the Postgres cluster down to just
one primary pod (e.g. using `max_instances` option). Otherwise, you risk
running into a split-brain scenario.
By default, `kubernetes_use_configmaps: false`, meaning endpoints will be used.
Starting from v1.16.0 the default will be changed to `true`.
running into a split-brain scenario. Default is `true`.
* **docker_image**
Spilo Docker image for Postgres instances. For production, don't rely on the
@@ -163,7 +156,26 @@ Those are top-level keys, containing both leaf keys and groups.
for some clusters it might be required to scale beyond the limits that can be
configured with `min_instances` and `max_instances` options. You can define
an annotation key that can be used as a toggle in cluster manifests to ignore
globally configured instance limits. The default is empty.
globally configured instance limits. The value must be `"true"` to be
effective. The default is empty which means the feature is disabled.
* **ignore_resources_limits_annotation_key**
for some clusters it might be required to request resources beyond the globally
configured thresholds for maximum requests and minimum limits. You can define
an annotation key that can be used as a toggle in cluster manifests to ignore
the thresholds. The value must be `"true"` to be effective. The default is empty
which means the feature is disabled.
* **enable_maintenance_windows**
toggle for using the maintenance windows feature. Default is `"true"`.
* **maintenance_windows**
a list which defines specific time frames when certain maintenance
operations such as automatic major upgrades or master pod migration are
allowed to happen for all database clusters. Accepted formats are
"01:00-06:00" for daily maintenance windows or "Sat:00:00-04:00" for
specific days, with all times in UTC. Locally defined maintenance
windows take precedence over globally configured ones.
* **resync_period**
period between consecutive sync requests. The default is `30m`.
@@ -252,12 +264,12 @@ CRD-configuration, they are grouped under the `major_version_upgrade` key.
* **minimal_major_version**
The minimal Postgres major version that will not automatically be upgraded
when `major_version_upgrade_mode` is set to `"full"`. The default is `"13"`.
when `major_version_upgrade_mode` is set to `"full"`. The default is `"14"`.
* **target_major_version**
The target Postgres major version when upgrading clusters automatically
which violate the configured allowed `minimal_major_version` when
`major_version_upgrade_mode` is set to `"full"`. The default is `"17"`.
`major_version_upgrade_mode` is set to `"full"`. The default is `"18"`.
## Kubernetes resources
@@ -315,6 +327,10 @@ configuration they are grouped under the `kubernetes` key.
Postgres pods are [terminated forcefully](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination)
after this timeout. The default is `5m`.
* **liveness_probe**
Allows for adding a liveness probe to the Spilo container to detect if it's
running properly. Cannot be configured via ConfigMap. Default is empty.
* **custom_pod_annotations**
This key/value map provides a list of annotations that get attached to each pod
of a database created by the operator. If the annotation key is also provided
@@ -566,7 +582,7 @@ configuration they are grouped under the `kubernetes` key.
1. `ebs` : operator resizes EBS volumes directly and executes `resizefs` within a pod
2. `pvc` : operator only changes PVC definition
3. `off` : disables resize of the volumes.
4. `mixed` : operator uses AWS API to adjust size, throughput, and IOPS, and calls pvc change for file system resize
4. `mixed` : operator uses AWS API to adjust size, type, throughput, and IOPS, and calls pvc change for file system resize
Default is "pvc".
## Kubernetes resource requests
@@ -782,6 +798,15 @@ yet officially supported.
[kube2iam](https://github.com/jtblin/kube2iam) project on AWS. The default is
empty.
* **irsa_role_arn**
Full AWS IAM role ARN to supply in the `eks.amazonaws.com/role-arn` annotation
of the Postgres pod service account, enabling
[IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)
(IAM Roles for Service Accounts) on EKS. When set, the operator annotates the
pod service account on every sync so that the EKS OIDC webhook can inject AWS
credentials directly into pods. Must be a full ARN, e.g.
`arn:aws:iam::123456789012:role/my-postgres-role`. The default is empty.
* **aws_region**
AWS region used to store EBS volumes. The default is `eu-central-1`. Note,
this option is not meant for specifying the AWS region for backups and
@@ -796,16 +821,6 @@ yet officially supported.
Path to mount the above Secret in the filesystem of the container(s).
The default is empty.
* **enable_ebs_gp3_migration**
enable automatic migration on AWS from gp2 to gp3 volumes, that are smaller
than the configured max size (see below). This ignores that EBS gp3 is by
default only 125 MB/sec vs 250 MB/sec for gp2 >= 333GB.
The default is `false`.
* **enable_ebs_gp3_migration_max_size**
defines the maximum volume size in GB until which auto migration happens.
Default is 1000 (1TB) which matches 3000 IOPS.
## Logical backup
These parameters configure a K8s cron job managed by the operator to produce
@@ -824,7 +839,7 @@ grouped under the `logical_backup` key.
runs `pg_dumpall` on a replica if possible and uploads compressed results to
an S3 bucket under the key `/<configured-s3-bucket-prefix>/<pg_cluster_name>/<cluster_k8s_uuid>/logical_backups`.
The default image is the same image built with the Zalando-internal CI
pipeline. Default: "ghcr.io/zalando/postgres-operator/logical-backup:v1.15.1"
pipeline. Default: "ghcr.io/zalando/postgres-operator/logical-backup:v2.0.0"
* **logical_backup_google_application_credentials**
Specifies the path of the google cloud service account json file. Default is empty.
@@ -881,6 +896,28 @@ grouped under the `logical_backup` key.
* **logical_backup_cronjob_environment_secret**
Reference to a Kubernetes secret, which keys will be added as environment variables to the cronjob. Default: ""
* **logical_backup_successful_jobs_history_limit**
number of successful backup jobs to keep in cronjob history. The default is `3`.
* **logical_backup_failed_jobs_history_limit**
number of failed backup jobs to keep in cronjob history. The default is `3`.
* **logical_backup_ttl_seconds_after_finished**
TTL in seconds after which finished backup jobs are automatically deleted. The default is `86400`.
The following environment variables can be passed to the logical backup
cronjob via `logical_backup_cronjob_environment_secret` to control
connectivity checks before the backup starts:
* **LOGICAL_BACKUP_CONNECT_RETRIES**
Number of times to retry connecting to the target PostgreSQL pod before
giving up. This is useful when NetworkPolicy enforcement introduces a
short delay before a newly-created pod's IP is allowed through ingress
rules on the destination node. Default: "10"
* **LOGICAL_BACKUP_CONNECT_RETRY_DELAY**
Delay in seconds between connectivity retries. Default: "2"
## Debugging the operator
Options to aid debugging of the operator itself. Grouped under the `debug` key.
@@ -1043,7 +1080,7 @@ operator being able to provide some reasonable defaults.
* **connection_pooler_image**
Docker image to use for connection pooler deployment.
Default: "registry.opensource.zalan.do/acid/pgbouncer"
Default: "ghcr.io/zalando/postgres-operator/pgbouncer:latest"
* **connection_pooler_max_db_connections**
How many connections the pooler can max hold. This value is divided among the
+43 -18
View File
@@ -30,7 +30,7 @@ spec:
databases:
foo: zalando
postgresql:
version: "17"
version: "18"
```
Once you cloned the Postgres Operator [repository](https://github.com/zalando/postgres-operator)
@@ -96,10 +96,7 @@ psql -U postgres -h localhost -p 6432
## Password encryption
Passwords are encrypted with `md5` hash generation by default. However, it is
possible to use the more recent `scram-sha-256` method by changing the
`password_encryption` parameter in the Postgres config. You can define it
directly from the cluster manifest:
Passwords are encrypted using the `scram-sha-256` hashing method by default. Other methods can be configured by changing the `password_encryption` parameter in the cluster manifest:
```yaml
apiVersion: "acid.zalan.do/v1"
@@ -109,7 +106,7 @@ metadata:
spec:
[...]
postgresql:
version: "17"
version: "18"
parameters:
password_encryption: scram-sha-256
```
@@ -517,7 +514,7 @@ Postgres Operator will create the following NOLOGIN roles:
The `<dbname>_owner` role is the database owner and should be used when creating
new database objects. All members of the `admin` role, e.g. teams API roles, can
become the owner with the `SET ROLE` command. [Default privileges](https://www.postgresql.org/docs/17/sql-alterdefaultprivileges.html)
become the owner with the `SET ROLE` command. [Default privileges](https://www.postgresql.org/docs/18/sql-alterdefaultprivileges.html)
are configured for the owner role so that the `<dbname>_reader` role
automatically gets read-access (SELECT) to new tables and sequences and the
`<dbname>_writer` receives write-access (INSERT, UPDATE, DELETE on tables,
@@ -594,7 +591,7 @@ spec:
### Schema `search_path` for default roles
The schema [`search_path`](https://www.postgresql.org/docs/17/ddl-schemas.html#DDL-SCHEMAS-PATH)
The schema [`search_path`](https://www.postgresql.org/docs/18/ddl-schemas.html#DDL-SCHEMAS-PATH)
for each role will include the role name and the schemas, this role should have
access to. So `foo_bar_writer` does not have to schema-qualify tables from
schemas `foo_bar_writer, bar`, while `foo_writer` can look up `foo_writer` and
@@ -695,7 +692,7 @@ handle it.
### HugePages support
The operator supports [HugePages](https://www.postgresql.org/docs/17/kernel-resources.html#LINUX-HUGEPAGES).
The operator supports [HugePages](https://www.postgresql.org/docs/18/kernel-resources.html#LINUX-HUGEPAGES).
To enable HugePages, set the matching resource requests and/or limits in the manifest:
```yaml
@@ -714,7 +711,7 @@ but Kubernetes will not spin up the pod if the requested HugePages cannot be all
For more information on HugePages in Kubernetes, see also
[https://kubernetes.io/docs/tasks/manage-hugepages/scheduling-hugepages/](https://kubernetes.io/docs/tasks/manage-hugepages/scheduling-hugepages/)
## Use taints, tolerations and node affinity for dedicated PostgreSQL nodes
## Use taints, tolerations, node affinity and topology spread constraint for dedicated PostgreSQL nodes
To ensure Postgres pods are running on nodes without any other application pods,
you can use [taints and tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/)
@@ -755,9 +752,26 @@ spec:
If you need to define a `nodeAffinity` for all your Postgres clusters use the
`node_readiness_label` [configuration](administrator.md#node-readiness-labels).
If you need PostgreSQL Pods to run on separate nodes, you can use the
[topologySpreadConstraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/) to control how they are distributed across your cluster.
This ensures they are spread among failure domains such as
regions, zones, nodes, or other user-defined topology domains.
```yaml
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: acid-minimal-cluster
spec:
topologySpreadConstraints:
- maxskew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
```
## In-place major version upgrade
Starting with Spilo 13, operator supports in-place major version upgrade to a
Starting with Spilo 14, operator supports in-place major version upgrade to a
higher major version (e.g. from PG 14 to PG 16). To trigger the upgrade,
simply increase the version in the manifest. It is your responsibility to test
your applications against the new version before the upgrade; downgrading is
@@ -792,7 +806,7 @@ spec:
clone:
uid: "efd12e58-5786-11e8-b5a7-06148230260c"
cluster: "acid-minimal-cluster"
timestamp: "2017-12-19T12:40:33+01:00"
timestamp: "2025-12-19T12:40:33+01:00"
```
Here `cluster` is a name of a source cluster that is going to be cloned. A new
@@ -827,7 +841,7 @@ spec:
clone:
uid: "efd12e58-5786-11e8-b5a7-06148230260c"
cluster: "acid-minimal-cluster"
timestamp: "2017-12-19T12:40:33+01:00"
timestamp: "2025-12-19T12:40:33+01:00"
s3_wal_path: "s3://custom/path/to/bucket"
s3_endpoint: https://s3.acme.org
s3_access_key_id: 0123456789abcdef0123456789abcdef
@@ -838,7 +852,7 @@ spec:
### Clone directly
Another way to get a fresh copy of your source DB cluster is via
[pg_basebackup](https://www.postgresql.org/docs/17/app-pgbasebackup.html). To
[pg_basebackup](https://www.postgresql.org/docs/18/app-pgbasebackup.html). To
use this feature simply leave out the timestamp field from the clone section.
The operator will connect to the service of the source cluster by name. If the
cluster is called test, then the connection string will look like host=test
@@ -900,8 +914,9 @@ the PostgreSQL version between source and target cluster has to be the same.
To start a cluster as standby, add the following `standby` section in the YAML
file. You can stream changes from archived WAL files (AWS S3 or Google Cloud
Storage) or from a remote primary. Only one option can be specified in the
manifest:
Storage), from a remote primary, or combine a remote primary with a WAL archive.
At least one of `s3_wal_path`, `gs_wal_path`, or `standby_host` must be specified.
Note that `s3_wal_path` and `gs_wal_path` are mutually exclusive.
```yaml
spec:
@@ -929,6 +944,16 @@ spec:
standby_port: "5433"
```
You can also combine a remote primary with a WAL archive for additional redundancy:
```yaml
spec:
standby:
standby_host: "acid-minimal-cluster.default"
standby_port: "5433"
s3_wal_path: "s3://<bucketname>/spilo/<source_db_cluster>/<UID>/wal/<PGVERSION>"
```
Note, that the pods and services use the same role labels like for normal clusters:
The standby leader is labeled as `master`. When using the `standby_host` option
you have to copy the credentials from the source cluster's secrets to successfully
@@ -1053,7 +1078,7 @@ spec:
- all
volumeSource:
emptyDir: {}
sidecars:
sidecars:
- name: "container-name"
image: "company/image:tag"
volumeMounts:
@@ -1104,7 +1129,7 @@ When using AWS with gp3 volumes you should set the mode to `mixed` because it
will also adjust the IOPS and throughput that can be defined in the manifest.
Check the [AWS docs](https://aws.amazon.com/ebs/general-purpose/) to learn
about default and maximum values. Keep in mind that AWS rate-limits updating
volume specs to no more than once every 6 hours.
volume specs to no more than 4 times within 24 hours.
```yaml
spec: