Высокая доступность PostgreSQL с Crunchy Data PGO: развертывание Kubernetes на предприятии
Корпоративный PostgreSQL HA с Crunchy Data PGO на Kubernetes
Для запуска PostgreSQL в рабочей среде на Kubernetes требуется нечто большее, чем StatefulSet с постоянным томом. Вам необходимы автоматическое переключение при сбое, непрерывное резервное копирование и восстановление на определенный момент времени, объединение пулов соединений, шифрование TLS, мониторинг и возможность согласованного развертывания среди облачных провайдеров и на «голом железе». Crunchy Data PGO (Postgres Оператор) v5 обеспечивает все это через единый собственный ресурс Kubernetes —PostgresClusterCRD, поддерживаемый проверенными в боевых условиях компонентами: Patroni для обеспечения высокой доступности на основе консенсуса, pgBackRest для корпоративного резервного копирования, PgBouncer для пула соединений и pgMonitor для Prometheus-совместимой наблюдаемости.
В этом руководстве описано все, что необходимо для перехода кластера PostgreSQL под управлением PGO от первоначального развертывания к эксплуатации промышленного уровня на AWS EKS, Azure AKS, GCP GKE и «голом железе» k3s с Rancher. Каждый раздел содержит конкретные значения YAML, Helm и рабочие процедуры, которые вы можете адаптировать к своей среде.
Архитектура Crunchy Data PGO v5
PGO v5 — это полная переработка оператора Crunchy Postgres. Он заменяет предыдущие pgcluster/pgreplica/pgpolicy CRD одним ресурсомPostgresCluster, который декларативно описывает каждый аспект развертывания PostgreSQL. Оператор отслеживает изменения в этом ресурсе и согласовывает базовые объекты Kubernetes — StatefulSets, Services, ConfigMaps, Secrets, Jobs — для соответствия желаемому состоянию.
Архитектура построена на четырех столпах.Patroniработает в качестве дополнительного модуля в каждом модуле PostgreSQL и управляет выбором лидера, топологией репликации и автоматическим переходом на другой ресурс с использованием встроенного распределенного консенсуса Kubernetes.pgBackRestобеспечивает полное, дифференциальное и инкрементное резервное копирование, а также непрерывное архивирование WAL в объектное хранилище (S3, GCS, Azure Blob) или локальные PVC.PgBouncerобеспечивает упрощенное создание пулов соединений, защищающее PostgreSQL от штормов соединений.pgMonitorпредоставляет метрики PostgreSQL через дополнительный экспортер Prometheus для интеграции с существующим стеком наблюдения.
Сам оператор не имеет состояния — все постоянное состояние находится в кластере PostgreSQL и его резервном хранилище. Это означает, что вы можете обновить или перезапустить оператор, не затрагивая работающие базы данных. Цикл согласования операторов является идемпотентным: многократное применение одной и той же спецификацииPostgresClusterсоздает один и тот же набор объектов Kubernetes.
Установка PGO v5
PGO v5 можно установить через Helm или напрямую через манифесты kubectl. Подход Helm предпочтителен для производства, поскольку он четко интегрируется с рабочими процессами GitOps и обеспечивает обновления с контролем версий.
# Add the Crunchy Data Helm repository
helm repo add crunchydata https://charts.crunchydata.com
helm repo update
# Install PGO into its own namespace
helm install pgo crunchydata/pgo \
--namespace postgres-operator \
--create-namespace \
--set controllerImages.cluster=registry.crunchydata.com/crunchydata/postgres-operator:ubi8-5.7.2-0 \
--set relatedImages.postgres_16=registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.6-1 \
--set relatedImages.pgbackrest=registry.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.54.1-0 \
--set relatedImages.pgbouncer=registry.crunchydata.com/crunchydata/crunchy-pgbouncer:ubi8-1.23.1-0 \
--set relatedImages.pgexporter=registry.crunchydata.com/crunchydata/crunchy-postgres-exporter:ubi8-0.16.0-0Для сред с воздушным зазором зеркально отразите необходимые образы во внутреннем реестре и соответствующим образом обновите значенияrelatedImages. PGO будет извлекать только изображения, указанные в значениях Helm или спецификацииPostgresCluster— он никогда не обращается к внешним реестрам во время выполнения, если это явно не настроено.
# Verify the operator is running
kubectl get pods -n postgres-operator
NAME READY STATUS RESTARTS AGE
pgo-controller-manager-7d8f9b6c4-xk2pt 1/1 Running 0 45sPostgresCluster CRD Технические характеристики
PostgresClusterCRD — это единая декларативная поверхность для всего развертывания PostgreSQL. Ниже представлена готовая к производству спецификация, демонстрирующая ключевые разделы. Каждый раздел подробно обсуждается в последующих частях данного руководства.
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: production-db
namespace: databases
spec:
postgresVersion: 16
image: registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.6-1
instances:
- name: pgha1
replicas: 3
resources:
requests:
cpu: "2"
memory: 8Gi
limits:
cpu: "4"
memory: 16Gi
dataVolumeClaimSpec:
storageClassName: gp3-csi
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
walVolumeClaimSpec:
storageClassName: gp3-csi
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
postgres-operator.crunchydata.com/cluster: production-db
tolerations:
- key: "workload"
operator: "Equal"
value: "database"
effect: "NoSchedule"
backups:
pgbackrest:
image: registry.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.54.1-0
configuration:
- secret:
name: pgbackrest-s3-creds
global:
repo1-retention-full: "7"
repo1-retention-diff: "14"
repo1-path: /pgbackrest/production-db/repo1
repo1-s3-uri-style: path
repos:
- name: repo1
schedules:
full: "0 2 * * 0" # Sunday 2am
differential: "0 2 * * 1-6" # Mon-Sat 2am
incremental: "0 */4 * * *" # Every 4 hours
s3:
bucket: mycompany-pg-backups
endpoint: s3.eu-west-1.amazonaws.com
region: eu-west-1
proxy:
pgBouncer:
image: registry.crunchydata.com/crunchydata/crunchy-pgbouncer:ubi8-1.23.1-0
replicas: 2
resources:
requests:
cpu: 500m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
config:
global:
pool_mode: transaction
max_client_conn: "1000"
default_pool_size: "25"
min_pool_size: "5"
reserve_pool_size: "5"
reserve_pool_timeout: "3"
monitoring:
pgmonitor:
exporter:
image: registry.crunchydata.com/crunchydata/crunchy-postgres-exporter:ubi8-0.16.0-0
resources:
requests:
cpu: 100m
memory: 128Mi
patroni:
dynamicConfiguration:
synchronous_mode: true
postgresql:
parameters:
shared_buffers: 2GB
effective_cache_size: 6GB
work_mem: 32MB
maintenance_work_mem: 512MB
max_connections: 200
wal_buffers: 64MB
checkpoint_completion_target: 0.9
random_page_cost: 1.1
effective_io_concurrency: 200
min_wal_size: 1GB
max_wal_size: 4GB
max_worker_processes: 8
max_parallel_workers_per_gather: 4
max_parallel_workers: 8
log_min_duration_statement: 500
log_checkpoints: "on"
log_connections: "on"
log_disconnections: "on"
log_lock_waits: "on"
users:
- name: appuser
databases: ["appdb"]
options: "NOSUPERUSER"
- name: readonly
databases: ["appdb"]
options: "NOSUPERUSER"
databaseInitSQL:
name: init-sql-configmap
key: init.sqlЭта спецификация создает кластер PostgreSQL 16 с тремя экземплярами с отдельными томами данных и WAL, резервным копированием на базе S3 по полному/дифференциальному/инкрементному расписанию, пулу соединений PgBouncer в режиме транзакций, экспорту метрик Prometheus и синхронной репликации Patroni. Правило антисходства модулей гарантирует, что все три экземпляра размещаются на разных узлах Kubernetes.
Высокая доступность на базе Patroni и автоматическое аварийное переключение
Patroni — это платформа высокой доступности, встроенная в каждый модуль PostgreSQL, управляемый PGO. В качестве распределенного хранилища конфигурации (DCS) он использует Kubernetes API — отдельный кластер etcd или ZooKeeper не требуется. Patroni постоянно контролирует состояние основного устройства и реплик PostgreSQL. Когда первичная реплика перестает отвечать на запросы, Patroni инициирует автоматический переход на другой ресурс: он переводит самую последнюю реплику в первичную и перенастраивает оставшиеся реплики, чтобы они следовали за новым лидером.
Процесс аварийного переключения в PGO работает следующим образом. Patroni на каждом модуле удерживает блокировку лидера в Kubernetes (через объекты Endpoints или ConfigMap). Текущий первичный блок должен обновлять эту блокировку с настраиваемым интервалом (по умолчанию 10 секунд TTL, 3-секундное ожидание цикла). Если первичный узел не может быть продлен — из-за его сбоя, сбоя узла или его разделения сетью — реплика, которая находится ближе всего к позиции первичного узла в WAL, получит блокировку и повысит свою эффективность. Весь процесс обычно занимает от 10 до 30 секунд.
Режим синхронной репликации, активируемый черезsynchronous_mode: trueв конфигурации Patroni, гарантирует нулевую потерю данных (RPO = 0) за счет несколько более высокой задержки записи. В синхронном режиме транзакция не подтверждается клиенту до тех пор, пока хотя бы одна реплика не подтвердит получение WAL. Если синхронная реплика недоступна, Patroni временно отключает синхронный режим для поддержания доступности — вы можете переопределить это с помощьюsynchronous_mode_strict: true, если предпочитаете пожертвовать доступностью ради согласованности.
# Check Patroni cluster status
kubectl exec -it production-db-pgha1-0 -n databases -- patronictl list
+----------------------------+-------------------+---------+---------+----+-----------+
| Member | Host | Role | State | TL | Lag in MB |
+----------------------------+-------------------+---------+---------+----+-----------+
| production-db-pgha1-0 | 10.244.0.15 | Leader | running | 3 | |
| production-db-pgha1-1 | 10.244.1.22 | Replica | running | 3 | 0 |
| production-db-pgha1-2 | 10.244.2.18 | Replica | running | 3 | 0 |
+----------------------------+-------------------+---------+---------+----+-----------+
# Manual switchover (planned, zero downtime)
kubectl exec -it production-db-pgha1-0 -n databases -- \
patronictl switchover --master production-db-pgha1-0 --candidate production-db-pgha1-1 --force
# Manual failover (forced, for emergencies)
kubectl exec -it production-db-pgha1-1 -n databases -- \
patronictl failover --candidate production-db-pgha1-1 --forcePGO автоматически настраивает службы Kubernetes для отслеживания лидера Patroni. Службаproduction-db-primaryвсегда указывает на тот модуль, который в данный момент удерживает лидерскую блокировку, поэтому приложения, подключающиеся через эту службу, обеспечивают плавное переключение при сбое с лишь кратковременным сбросом соединения.
pgBackRest Резервное копирование и восстановление
pgBackRest — это механизм резервного копирования, интегрированный в PGO. Он поддерживает три типа резервного копирования — полное, дифференциальное и инкрементное, а также непрерывное архивирование WAL для восстановления на определенный момент времени (PITR). Понимание того, как они сочетаются друг с другом, важно для разработки стратегии резервного копирования, которая сбалансирует стоимость хранилища, скорость резервного копирования и время восстановления.
Полная резервная копиякопирует весь каталог данных PostgreSQL и является базовой для всех других типов резервного копирования. Дифференциальное резервное копированиекопирует только те страницы, которые изменились с момента последнего полного резервного копирования. Инкрементное резервное копированиекопирует только страницы, которые изменились с момента последнего резервного копирования любого типа. Во время восстановления pgBackRest автоматически связывает необходимые резервные копии вместе — например, для восстановления из инкрементальной требуется инкрементальная плюс предыдущая дифференциальная (или полная) плюс полная резервная копия, а также любые сегменты WAL, необходимые для достижения целевой точки восстановления.
Конфигурация расписания резервного копирования
Расписание резервного копирования определяется в разделеreposконфигурации pgBackRest спецификацииPostgresCluster. В четком производственном графике обычно выполняется еженедельное полное, ежедневное дифференциальное и более частое инкрементальное резервное копирование.
backups:
pgbackrest:
global:
repo1-retention-full: "4" # Keep 4 full backups
repo1-retention-full-type: count
repo1-retention-diff: "14" # Keep 14 differentials
repos:
- name: repo1
schedules:
full: "0 2 * * 0" # Weekly full: Sunday 2am
differential: "0 2 * * 1-6" # Daily diff: Mon-Sat 2am
incremental: "0 */6 * * *" # Every 6 hours
s3:
bucket: mycompany-pg-backups
endpoint: s3.eu-west-1.amazonaws.com
region: eu-west-1Ручное резервное копирование и восстановление
# Trigger a manual full backup
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/pgbackrest-backup="$(date +%s)" \
--overwrite
# Check backup status
kubectl exec -it production-db-pgha1-0 -n databases -- \
pgbackrest info --stanza=db
# Restore to a specific point in time
# First, update the PostgresCluster spec:
spec:
backups:
pgbackrest:
restore:
enabled: true
repoName: repo1
options:
- --type=time
- --target="2026-04-12 08:30:00+00"
- --target-action=promote
# Apply the restore annotation
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/pgbackrest-restore="$(date +%s)" \
--overwriteВо время восстановления PITR PGO отключает все экземпляры PostgreSQL, выполняет восстановление из ближайшей полной или дифференциальной резервной копии, воспроизводит сегменты WAL до целевого времени, а затем запускает кластер. Вся операция контролируется оператором — ручное вмешательство в работу отдельных модулей не требуется.
Пул соединений PgBouncer
PostgreSQL создает новый процесс для каждого клиентского соединения. В масштабе — сотни или тысячи модулей приложений, каждый из которых поддерживает пулы соединений — эта модель терпит неудачу. PgBouncer находится между вашим приложением и PostgreSQL, мультиплексируя множество клиентских соединений с меньшим количеством серверных соединений.
PGO развертывает PgBouncer как отдельное развертывание со своей собственной службой. Ваши приложения должны подключаться к службеproduction-db-pgbouncer, а не к основной службе PostgreSQL напрямую. PgBouncer поддерживает три режима пула:
- Объединение сеансов в пул— соединение с сервером назначается клиенту на время существования клиентского соединения. Самый безопасный, но наименее эффективный.
- Пул транзакций— соединение с сервером назначается только на время транзакции. Наиболее эффективен для веб-задач. Это рекомендуемое значение по умолчанию.
- Пул операторов— соединение с сервером назначается для одного оператора. Работает только для простых, нетранзакционных рабочих нагрузок.
proxy:
pgBouncer:
replicas: 3
config:
global:
pool_mode: transaction
max_client_conn: "2000"
default_pool_size: "30"
min_pool_size: "10"
reserve_pool_size: "10"
reserve_pool_timeout: "3"
server_idle_timeout: "300"
server_lifetime: "3600"
client_idle_timeout: "600"
tcp_keepalive: "1"
tcp_keepidle: "30"
tcp_keepintvl: "10"
tcp_keepcnt: "3"
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
postgres-operator.crunchydata.com/role: pgbouncerОбратите внимание на настройкиtcp_keepalive— они имеют решающее значение, когда PgBouncer работает за облачным балансировщиком нагрузки или службой Kubernetes. Без агрессивных проверок активности простаивающие соединения могут автоматически прерываться промежуточными сетевыми компонентами, вызывая ошибки приложений при следующей попытке запроса.
pgMonitor и мониторинг Prometheus/Grafana
Интеграция мониторингаPGO позволяет использовать коляскуcrunchy-postgres-exporterв каждом модуле PostgreSQL. Этот экспортер очищает представления внутренней статистики PostgreSQL и представляет их как метрики Prometheus на порту 9187. Экспортер по умолчанию охватывает более 150 показателей, включая соединения, задержку репликации, скорость транзакций, коэффициенты попадания в кэш, статистику таблиц и индексов, конфликты блокировок и скорость генерации WAL.
# ServiceMonitor for Prometheus Operator auto-discovery
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: postgres-monitoring
namespace: databases
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
postgres-operator.crunchydata.com/cluster: production-db
postgres-operator.crunchydata.com/crunchy-postgres-exporter: "true"
endpoints:
- port: exporter
interval: 15s
scrapeTimeout: 10sCrunchy Data предоставляет набор готовых информационных панелей Grafana, которые можно напрямую импортировать. Они охватывают обзор PostgreSQL, состояние репликации, состояние резервного копирования pgBackRest, статистику PgBouncer и использование ресурсов на уровне модуля. Импортируйте их с помощью панели управления Grafana или вручную из репозитория примеров Crunchy Data.
# Install Grafana dashboards as ConfigMaps
kubectl create configmap grafana-pg-overview \
--from-file=pg-overview.json=dashboards/pg-overview.json \
-n monitoring
kubectl label configmap grafana-pg-overview grafana_dashboard=1 -n monitoringПравила оповещений о критических ситуациях для PostgreSQL
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: postgres-alerts
namespace: databases
labels:
release: kube-prometheus-stack
spec:
groups:
- name: postgresql-health
rules:
- alert: PostgresReplicationLagHigh
expr: ccp_replication_lag_size_bytes > 104857600
for: 5m
labels:
severity: warning
annotations:
summary: "Replica {{ $labels.pod }} lag exceeds 100MB"
- alert: PostgresConnectionsExhausted
expr: ccp_connection_stats_active / ccp_connection_stats_max_connections > 0.85
for: 2m
labels:
severity: critical
annotations:
summary: "PostgreSQL connections above 85% capacity"
- alert: PostgresDeadlockDetected
expr: rate(ccp_stat_database_deadlocks[5m]) > 0
for: 1m
labels:
severity: warning
annotations:
summary: "Deadlocks detected in {{ $labels.datname }}"
- alert: PostgresCacheHitRatioLow
expr: ccp_stat_database_blks_hit / (ccp_stat_database_blks_hit + ccp_stat_database_blks_read) < 0.95
for: 10m
labels:
severity: warning
annotations:
summary: "Cache hit ratio below 95% for {{ $labels.datname }}"
- alert: PgBackRestStaleBackup
expr: time() - ccp_backrest_last_full_backup_time_since_completion_seconds > 604800
for: 1h
labels:
severity: critical
annotations:
summary: "No full backup in the last 7 days"Развертывание AWS EKS
Для развертывания PGO на AWS EKS требуется настроить драйвер EBS CSI для постоянных томов и роли IAM для учетных записей служб (IRSA) для доступа к резервному копированию S3. Этот подход позволяет избежать хранения долговременных учетных данных AWS в секретах Kubernetes.
# Install the EBS CSI driver (required for gp3 volumes)
eksctl create addon --name aws-ebs-csi-driver --cluster my-cluster --region eu-west-1 \
--service-account-role-arn arn:aws:iam::111122223333:role/AmazonEKS_EBS_CSI_DriverRole
# Create a gp3 StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: gp3-csi
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iops: "3000"
throughput: "125"
encrypted: "true"
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true# IAM policy for pgBackRest S3 access
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::mycompany-pg-backups",
"arn:aws:s3:::mycompany-pg-backups/*"
]
}
]
}
# Create an IRSA-enabled service account
eksctl create iamserviceaccount \
--name pgbackrest-sa \
--namespace databases \
--cluster my-cluster \
--attach-policy-arn arn:aws:iam::111122223333:policy/PGBackRestS3Policy \
--approveВ спецификацииPostgresClusterукажите сегмент S3 и регион. PGO будет автоматически использовать учетные данные сервисной учетной записи модуля (через IRSA) — явные ключи доступа не требуются.
backups:
pgbackrest:
repos:
- name: repo1
s3:
bucket: mycompany-pg-backups
endpoint: s3.eu-west-1.amazonaws.com
region: eu-west-1Для обеспечения устойчивости в нескольких зонах доступности убедитесь, что группы узлов EKS охватывают как минимум три зоны доступности, и используйте ограничения распространения топологии, показанные ранее, для распределения модулей PostgreSQL между ними.
Развертывание Azure AKS
Azure AKS использует управляемые диски для постоянного хранения и хранилище BLOB-объектов Azure для резервных копий pgBackRest. Рекомендуемый класс хранилища использует SSD Premium v2 или Premium LRS для рабочих нагрузок базы данных.
# StorageClass for Azure Premium SSD
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: azure-premium-ssd
provisioner: disk.csi.azure.com
parameters:
skuName: Premium_LRS
kind: Managed
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# pgBackRest with Azure Blob Storage
backups:
pgbackrest:
configuration:
- secret:
name: pgbackrest-azure-creds
global:
repo1-azure-account: mystorageaccount
repo1-retention-full: "7"
repos:
- name: repo1
schedules:
full: "0 2 * * 0"
differential: "0 2 * * 1-6"
azure:
container: pg-backups
---
# Secret with Azure credentials
apiVersion: v1
kind: Secret
metadata:
name: pgbackrest-azure-creds
namespace: databases
stringData:
azure.conf: |
[global]
repo1-azure-key=BASE64_STORAGE_ACCOUNT_KEYДля идентификации рабочей нагрузки (эквивалент AWS IRSA для Azure) настройте кластер AKS с эмитентом OIDC и создайте учетные данные федеративного удостоверения для учетной записи службы pgBackRest. Это устраняет необходимость хранить ключи учетной записи хранения в секретах.
Развертывание GCP GKE
GKE использует постоянный диск (pd-ssd) для хранения и GCS для резервных копий pgBackRest. GKE Workload Identity сопоставляет учетные записи служб Kubernetes с учетными записями служб Google Cloud для безопасной аутентификации без ключа.
# StorageClass for GKE SSD Persistent Disk
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: pd-ssd
provisioner: pd.csi.storage.gke.io
parameters:
type: pd-ssd
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# pgBackRest with GCS
backups:
pgbackrest:
configuration:
- secret:
name: pgbackrest-gcs-creds
repos:
- name: repo1
gcs:
bucket: mycompany-pg-backups
---
# GCS service account key secret
apiVersion: v1
kind: Secret
metadata:
name: pgbackrest-gcs-creds
namespace: databases
stringData:
gcs-key.json: |
{
"type": "service_account",
"project_id": "my-project",
...
}# Enable Workload Identity on GKE
gcloud container clusters update my-cluster \
--workload-pool=my-project.svc.id.goog
# Create and bind IAM service account
gcloud iam service-accounts create pgbackrest-gcs \
--display-name="pgBackRest GCS Access"
gcloud projects add-iam-policy-binding my-project \
--member="serviceAccount:pgbackrest-gcs@my-project.iam.gserviceaccount.com" \
--role="roles/storage.objectAdmin"
gcloud iam service-accounts add-iam-policy-binding \
pgbackrest-gcs@my-project.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="serviceAccount:my-project.svc.id.goog[databases/production-db-pgbackrest]"Многорегиональное аварийное восстановление
PGO поддерживает резервные кластеры для аварийного восстановления между регионами. Резервный кластер постоянно воспроизводит WAL из репозитория pgBackRest основного кластера, сохраняя теплую копию, которую можно повысить до независимого основного в случае регионального сбоя.
# Standby cluster in Region B
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: production-db-standby
namespace: databases
spec:
postgresVersion: 16
image: registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.6-1
standby:
enabled: true
repoName: repo1
instances:
- name: pgha1
replicas: 2
dataVolumeClaimSpec:
storageClassName: gp3-csi
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
backups:
pgbackrest:
image: registry.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.54.1-0
configuration:
- secret:
name: pgbackrest-s3-creds
repos:
- name: repo1
s3:
bucket: mycompany-pg-backups
endpoint: s3.us-east-1.amazonaws.com
region: us-east-1Чтобы повысить резервный кластер во время аварии, установитеstandby.enabled: falseв спецификации и примените изменения. PGO переводит резервный в независимый первичный кластер. Это повышение необратимо — вам придется перестроить отношения репликации с нуля после восстановления исходного основного региона.
Развертывание k3s/Rancher без операционной системы
Запуск PGO на «голом железе» k3s с управлением Rancher требует пристального внимания к хранилищу и сети, поскольку у вас нет дисков, управляемых облачным провайдером, или балансировщиков нагрузки.
Система хранения данных Longhorn
Longhorn — это легкая распределенная блочная система хранения данных для Kubernetes, которая идеально подходит для сред с «голым железом». Он реплицирует тома на нескольких узлах для обеспечения надежности и поддерживает моментальные снимки, резервное копирование и расширение томов.
# Install Longhorn via Helm
helm repo add longhorn https://charts.longhorn.io
helm repo update
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--create-namespace \
--set defaultSettings.defaultReplicaCount=3 \
--set defaultSettings.storageOverProvisioningPercentage=100 \
--set defaultSettings.storageMinimalAvailablePercentage=15
---
# Longhorn StorageClass for PostgreSQL
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: longhorn-pg
provisioner: driver.longhorn.io
parameters:
numberOfReplicas: "3"
staleReplicaTimeout: "2880"
fsType: ext4
dataLocality: best-effort
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
reclaimPolicy: RetainMetalLB для балансировки нагрузки
# Install MetalLB
helm repo add metallb https://metallb.github.io/metallb
helm install metallb metallb/metallb --namespace metallb-system --create-namespace
---
# Configure IP address pool
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: pg-pool
namespace: metallb-system
spec:
addresses:
- 192.168.1.200-192.168.1.210
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: pg-l2
namespace: metallb-system
spec:
ipAddressPools:
- pg-poolПри настройке MetalLB службы PGO типаLoadBalancerбудут получать IP-адреса из вашего пула, что делает основные конечные точки PostgreSQL и конечные точки PgBouncer напрямую доступными из вашей сети без ручной переадресации портов.
pgBackRest с локальным PVC или NFS для «голого металла»
# For environments without S3/GCS/Azure Blob, use a PVC-based repo
backups:
pgbackrest:
repos:
- name: repo1
schedules:
full: "0 2 * * 0"
differential: "0 2 * * 1-6"
volume:
volumeClaimSpec:
storageClassName: longhorn-pg
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 200GiДля изолированных сред вы также можете настроить вторичный репозиторий pgBackRest, который отправляет резервные копии в общий ресурс NFS или экземпляр MinIO, работающий локально, обеспечивая резервную копию за пределами кластера без облачных зависимостей.
Конфигурация шифрования TLS/SSL
PGO по умолчанию генерирует самозаверяющие сертификаты TLS для всех внутренних коммуникаций — экземпляры PostgreSQL, репликация, pgBackRest и PgBouncer обмениваются данными по зашифрованным каналам без какого-либо ручного управления сертификатами. Однако для производственных сред обычно требуется использовать сертификаты, подписанные центром сертификации вашей организации.
# Create a TLS secret with your CA-signed certificates
kubectl create secret tls production-db-tls \
--cert=server.crt \
--key=server.key \
-n databases
kubectl create secret generic production-db-ca \
--from-file=ca.crt=ca.crt \
-n databases
---
# Reference in PostgresCluster spec
spec:
customTLSSecret:
name: production-db-tls
customReplicationTLSSecret:
name: production-db-replication-tlsPGO настраивает PostgreSQL сssl = onи устанавливает соответствующие параметрыssl_cert_file,ssl_key_fileиssl_ca_file. PgBouncer аналогичным образом настроен на использование TLS для клиентских подключений и на использование TLS при подключении к серверным модулям PostgreSQL.
# Enforce TLS for all client connections via pg_hba.conf
spec:
patroni:
dynamicConfiguration:
postgresql:
pg_hba:
- hostssl all all 0.0.0.0/0 scram-sha-256
- hostssl replication all 0.0.0.0/0 scram-sha-256Пользовательская конфигурация PostgreSQL
PGO предоставляет конфигурацию PostgreSQL через раздел динамической конфигурации Patroni. Это рекомендуемый подход, поскольку Patroni гарантирует, что все экземпляры поддерживают согласованную конфигурацию, и при необходимости выполняет перезапуск.
patroni:
dynamicConfiguration:
postgresql:
parameters:
# Memory
shared_buffers: 4GB
effective_cache_size: 12GB
work_mem: 64MB
maintenance_work_mem: 1GB
huge_pages: "try"
# WAL
wal_buffers: 128MB
min_wal_size: 2GB
max_wal_size: 8GB
checkpoint_completion_target: 0.9
wal_compression: lz4
# Query Planning
random_page_cost: 1.1
effective_io_concurrency: 200
default_statistics_target: 200
# Parallelism
max_worker_processes: 16
max_parallel_workers_per_gather: 4
max_parallel_workers: 8
max_parallel_maintenance_workers: 4
# Connections
max_connections: 200
idle_in_transaction_session_timeout: 60000
statement_timeout: 300000
# Logging
log_min_duration_statement: 250
log_checkpoints: "on"
log_connections: "on"
log_disconnections: "on"
log_lock_waits: "on"
log_temp_files: 0
log_autovacuum_min_duration: 0
# Autovacuum Tuning
autovacuum_max_workers: 5
autovacuum_naptime: 30
autovacuum_vacuum_cost_limit: 800
autovacuum_vacuum_scale_factor: 0.02
autovacuum_analyze_scale_factor: 0.01Эти параметры предполагают наличие узла с 16 ядрами CPU и 32 ГБ оперативной памяти, выделенной для PostgreSQL. Настройтеshared_buffersпримерно до 25% доступной оперативной памяти, аeffective_cache_sizeпримерно до 75%. Настройки WAL и контрольных точек настроены для рабочих нагрузок с большим объемом записи — уменьшитеmax_wal_sizeдля систем с большим объемом чтения, где частота контрольных точек имеет меньшее значение.
Управление пользователями и базами данных
PGO управляет пользователями и базами данных PostgreSQL декларативно через разделusersспецификацииPostgresCluster. Когда вы добавляете пользователя, PGO создает роль в PostgreSQL, генерирует случайный пароль и сохраняет учетные данные в секрете Kubernetes.
users:
- name: appuser
databases: ["appdb"]
options: "NOSUPERUSER NOCREATEDB NOCREATEROLE"
- name: readonly
databases: ["appdb"]
options: "NOSUPERUSER NOCREATEDB NOCREATEROLE"
- name: migrations
databases: ["appdb"]
options: "NOSUPERUSER CREATEDB"
- name: monitoring
databases: ["postgres"]
options: "NOSUPERUSER LOGIN"
---
# Access credentials from the generated secret
kubectl get secret production-db-pguser-appuser -n databases -o jsonpath='{.data.password}' | base64 -d
# The secret also contains a full connection URI
kubectl get secret production-db-pguser-appuser -n databases -o jsonpath='{.data.uri}' | base64 -d
# postgresql://appuser:PASSWORD@production-db-primary.databases.svc:5432/appdbДля предоставления доступа только для чтения используйте функциюdatabaseInitSQLдля запуска SQL при создании кластера, который создает роль только для чтения и предоставляет ей SELECT для всех таблиц.
# init.sql ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: init-sql-configmap
namespace: databases
data:
init.sql: |
-- Read-only role for reporting users
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly;
GRANT CONNECT ON DATABASE appdb TO readonly;
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
-- Extensions
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS pg_trgm;Последовательные обновления и обновления основных версий
PGO обрабатывает незначительные обновления версий посредством последовательных обновлений. Когда вы меняете тег изображения в спецификацииPostgresClusterна более новую версию исправления, PGO обновляет экземпляры по одному, начиная с реплик и заканчивая основным (что запускает переключение Patroni для минимизации времени простоя).
# Minor version upgrade: change the image tag
spec:
image: registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.7-0Для обновления основной версии(например, PostgreSQL с 15 по 16) требуетсяpg_upgrade, который PGO организует через отдельныйPGUpgradeCRD.
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PGUpgrade
metadata:
name: production-db-upgrade
namespace: databases
spec:
postgresClusterName: production-db
fromPostgresVersion: 15
toPostgresVersion: 16
image: registry.crunchydata.com/crunchydata/crunchy-upgrade:ubi8-5.7.2-0В процессе обновления существующий кластер выключается, запускаетсяpg_upgrade --linkдля выполнения обновления на месте с использованием жестких ссылок (минимизирует копирование данных), проверяет обновление и запускает кластер на новой версии. Всегда создавайте полную резервную копию перед началом обновления основной версии и сначала проверяйте процедуру на непроизводственном кластере.
# Pre-upgrade checklist
# 1. Take a full backup
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/pgbackrest-backup="$(date +%s)" \
--overwrite
# 2. Verify backup completed
kubectl exec -it production-db-pgha1-0 -n databases -- pgbackrest info --stanza=db
# 3. Shutdown the cluster (set replicas to 0 or use PGO shutdown annotation)
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/shutdown="$(date +%s)" \
--overwrite
# 4. Apply the PGUpgrade resource
kubectl apply -f pg-upgrade.yaml
# 5. Update the PostgresCluster spec with the new version and image
# 6. Remove the shutdown annotation to start the upgraded clusterРекомендации по настройке производстваЗапуск PGO в производственной среде требует внимания к нескольким областям эксплуатации, выходящим за рамки первоначального развертывания.
Производительность системы хранения данных
- Отдельные тома данных и WAL: всегда используйте
walVolumeClaimSpecдля размещения WAL на выделенном PVC. Это предотвращает конфликт активности WAL с интенсивной записью с вводом-выводом данных. - Используйте хранилищес высоким числом операций ввода-вывода в секунду: gp3 (AWS), SSD премиум-класса v2 (Azure), pd-ssd (GCP) или Longhorn с поддержкой NVMe на голом железе. Для произвольных шаблонов ввода-вывода PostgreSQL требуется хранилище с малой задержкой.
- Расширение тома: убедитесь, что ваш StorageClass имеет
allowVolumeExpansion: true. PGO может без прерывания работы расширять возможности PVC у поддерживаемых поставщиков систем хранения данных.
Бюджеты для прерывания работы модулей
PGO автоматически создает PDB для ваших экземпляров PostgreSQL, но убедитесь, что они соответствуют вашим требованиям к высокой доступности.
# Check PDBs
kubectl get pdb -n databases
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
production-db-pgha1 1 N/A 2 7dЗапросы и ограничения ресурсов- Установите запросы памяти, равные ограничениям для модулей базы данных, чтобы предотвратить уничтожение OOM и обеспечить гарантированный класс QoS.
- Консервативно устанавливайте запросы CPU и повышайте ограничения, чтобы обеспечить разрыв во время вакуумирования или операций технического обслуживания.
- Отслеживайте фактическое использование с помощью показателей pgMonitor и корректируйте ежеквартально.
Управление соединениями
- Всегда подключайтесь через PgBouncer, а не напрямую к PostgreSQL.
- Установите для
max_connectionsв PostgreSQL значение, учитывающее размер пула PgBouncer плюс системные подключения (репликация, мониторинг, суперпользователь). - Использовать режим пула транзакций для веб-приложений. Переключайтесь на пул сеансов, только если ваше приложение использует подготовленные операторы или состояние уровня сеанса (например, команды
SET, временные таблицы).
Проверка резервной копии
- Регулярно тестируйте восстановление, создавая кластер клонов из резервной копии и выполняя дымовые тесты на уровне приложения.
- Отслеживайте предупреждение
PgBackRestStaleBackup, чтобы убедиться, что резервное копирование выполняется по расписанию. - Подтвердить PITR, восстановив определенные временные метки и проверив согласованность данных.
# Clone cluster for backup validation
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
name: backup-validation
namespace: databases
spec:
postgresVersion: 16
image: registry.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.6-1
dataSource:
postgresCluster:
clusterName: production-db
repoName: repo1
options:
- --type=time
- --target="2026-04-12 06:00:00+00"
instances:
- name: pgha1
replicas: 1
dataVolumeClaimSpec:
storageClassName: gp3-csi
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
backups:
pgbackrest:
repos:
- name: repo1
volume:
volumeClaimSpec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50GiСетевые политики# Restrict PostgreSQL traffic to known namespaces
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: postgres-network-policy
namespace: databases
spec:
podSelector:
matchLabels:
postgres-operator.crunchydata.com/cluster: production-db
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
app-access: database
- podSelector:
matchLabels:
postgres-operator.crunchydata.com/cluster: production-db
ports:
- port: 5432
protocol: TCP
- port: 9187
protocol: TCPКонтрольный список мониторинга- Задержка репликации: предупреждение, когда размер любой реплики превышает 100 МБ или задержка составляет 60 секунд.
- Насыщенность соединения: предупреждение, когда количество активных соединений превышает 80% от
max_connections. - Аномалии скорости транзакций: определите базовый уровень TPS и оповещайте о значительных отклонениях.
- Использование диска: оповещение при пороговых значениях 70 % и 85 % с помощью модулей Runbook расширения тома.
- Свежесть резервной копии: оповещение, когда последняя полная резервная копия старше вашего окна RPO.
- Конфликт блокировок: оповещение о длительных блокировках (> 30 секунд), которые могут указывать на ошибки приложения.
- Состояние автоматической очистки: оповещение, если столы не пылесосились более 24 часов.
Runbook для аварийного восстановления
План аварийного восстановления полезен только в том случае, если он был протестирован. В следующем модуле Runbook описаны ключевые процедуры восстановления после распространенных сценариев сбоя.
Отказ одного модуля
Patroni и Kubernetes обрабатывают это автоматически. Если основной модуль выходит из строя, Patroni продвигает реплику в течение 10–30 секунд. Kubernetes перезапускает вышедший из строя модуль, который снова подключается как реплика.
Отказ одного узла
Если узел, на котором работает модуль PostgreSQL, умирает, Kubernetes перепланирует модуль на работоспособный узел. Модуль подключается к существующему PVC (если хранилище подключено к сети) или восстанавливается из резервной копии (если использовалось локальное хранилище). Правила антисходства модулей гарантируют, что оставшиеся экземпляры продолжат обслуживать трафик.
Полная потеря кластера
Если весь кластер Kubernetes утерян, разверните новый кластер, установите PGO и создайте новыйPostgresCluster, указавdataSourceна резервный репозиторий. PGO восстанавливает последнюю резервную копию и воспроизводит WAL до самой последней доступной точки.
Региональное аварийное переключение
Если основной регион потерян, продвиньте резервный кластер, установивstandby.enabled: false. Обновите DNS или балансировщик нагрузки, чтобы он указывал на новый основной регион. После восстановления исходного региона вы можете восстановить резервные отношения в обратном порядке.
# Promote standby cluster
kubectl patch postgrescluster production-db-standby -n databases --type merge \
-p '{"spec":{"standby":{"enabled":false}}}'
# Verify the cluster is now an independent primary
kubectl exec -it production-db-standby-pgha1-0 -n databases -- patronictl listКраткое руководство по рабочим командам
# Cluster status
kubectl get postgrescluster -n databases
kubectl describe postgrescluster production-db -n databases
# Patroni status
kubectl exec -it production-db-pgha1-0 -n databases -- patronictl list
kubectl exec -it production-db-pgha1-0 -n databases -- patronictl history
# Connect to PostgreSQL
kubectl exec -it production-db-pgha1-0 -n databases -- psql -U postgres
# Backup info
kubectl exec -it production-db-pgha1-0 -n databases -- pgbackrest info --stanza=db
# Manual backup
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/pgbackrest-backup="$(date +%s)" --overwrite
# Restart PostgreSQL (rolling)
kubectl annotate postgrescluster production-db -n databases \
postgres-operator.crunchydata.com/restart="$(date +%s)" --overwrite
# Logs
kubectl logs production-db-pgha1-0 -n databases -c database
kubectl logs production-db-pgha1-0 -n databases -c pgbackrest
kubectl logs production-db-pgha1-0 -n databases -c exporter
# PgBouncer status
kubectl exec -it $(kubectl get pod -l postgres-operator.crunchydata.com/role=pgbouncer -n databases -o name | head -1) -n databases -- psql -U pgbouncer pgbouncer -c "SHOW POOLS;"
# Scale replicas
kubectl patch postgrescluster production-db -n databases --type merge \
-p '{"spec":{"instances":[{"name":"pgha1","replicas":5}]}}'Заключение
Crunchy Data PGO превращает PostgreSQL на Kubernetes из операционной нагрузки в управляемую декларативную систему.PostgresClusterCRD фиксирует все развертывание — экземпляры, репликацию, резервное копирование, объединение в пулы, мониторинг и TLS — в одном ресурсе с контролем версий. Patroni обеспечивает проверенное в боевых условиях автоматическое переключение при сбое. pgBackRest обеспечивает резервное копирование корпоративного уровня с восстановлением на определенный момент времени в любое хранилище облачных объектов. PgBouncer управляет пулом соединений, которого требует модель PostgreSQL «процесс на соединение» в масштабе. А pgMonitor передает необходимые вам показатели в Prometheus и Grafana для обеспечения операционной прозрачности.
Схемы развертывания в AWS EKS, Azure AKS, GCP GKE и k3s без операционной системы имеют одну и ту же базовую спецификациюPostgresCluster— изменения заключаются в классе хранилища, конфигурации хранилища резервных копий и сетевом уровне. Эта согласованность является реальной ценностью операторского подхода: ваша команда изучает один инструмент, одну операционную модель и один набор модулей Runbook, которые работают везде.
Начните с кластера из трех экземпляров, PgBouncer в режиме пула транзакций, еженедельного полного и ежедневного дифференциального расписания резервного копирования и основных оповещений Prometheus. Проверьте процедуру восстановления из резервной копии в первый же день, а не тогда, когда она вам впервые понадобится. Расширяйте возможности резервных кластеров с несколькими регионами, синхронной репликации и расширенной настройки по мере роста ваших требований к доступности и операционной зрелости. Оператор занимается механикой; ваша ответственность — достаточно хорошо понимать архитектуру, чтобы найти правильный компромисс для вашей рабочей нагрузки.