Workstation Logo
产品
AI 实验室OpenAI代理Claude 代理Grok BotWorkstation CRM (WSL CRM)营销全部产品
AI 解决方案
AI 工作站AI SME Packages私有 AIGPU 集群边缘 AI企业 AI 实验室按行业分类的 AI
服务
Platform ModernisationDigital EngineeringData Foundations & AIAutonomous OperationsAI 咨询DevOps 自动化网络安全软件开发智能体构建MLOps 搭建
关于我们
合作伙伴客户案例
文章
文档
WSL ProxyRing PromoterWSL VaultJobshoutSysOps 24/7
博客
联系我们Login
Workstation

面向现代企业的 AI 工作站、AI 多智能体软件、GPU 基础设施和智能代理解决方案。

联系我们

AI 解决方案

AI 工作站AI SME Packages私有 AIGPU 集群边缘 AI企业 AI 实验室按行业分类的 AI

产品

全部产品WSL CRM 与 ERP营销OpenAI代理WSL ProxyRing PromoterWSL VaultJobshoutSysOps 24/7

公司

关于我们为什么选择Workstation合作伙伴客户案例价格联系

资源

文章文档博客搜索网站地图
英国办公室
77-79 Marlowes, Hemel Hempstead HP1 1LF路线指引 — 从 M25 外环伦敦 20 号出口驶出公司编号: 11641870周一至周五:上午 9:00 - 下午 6:00 GMT
+44 7515 356 146
比利时办公室
Workstation SRL, Rue Vanderkindere 34, 1180 Uccle, BrusselsBE 0751.518.683周一至周五:上午 9:00 - 下午 6:00 CET
+32 492 45 67 46
印度办公室
#159 Sector 9, Pocket 1, DDA Flats, 110077 Dwarka, New Delhi
+91 98881 98841

© 2026 Workstation AI。保留所有权利。

隐私Cookie服务条款网站地图

Loading blog...

Home / Blog
DatabaseKubernetesDevOpsBackend

PostgreSQL 高可用性和关键数据 PGO:企业 Kubernetes 部署

企业 PostgreSQL HA 与 Kubernetes 上的 Crunchy Data PGO

Balinder Walia2026年4月12日16 min read

在 Kubernetes 上运行 PostgreSQL 需要的不仅仅是具有持久卷的 StatefulSet。您需要自动故障转移、持续备份和时间点恢复、连接池、TLS 加密、监控以及跨云提供商和裸机一致部署的能力。 Crunchy Data PGO (Postgres Operator) v5 通过单个 Kubernetes 原生自定义资源 —PostgresClusterCRD — 提供所有这些功能,并由经过考验的组件提供支持:用于基于共识的 HA 的 Patroni、用于企业备份的 pgBackRest、用于连接池的 PgBouncer 以及用于与 Prometheus 兼容的可观察性的 pgMonitor。

本指南涵盖了将 PGO 管理的 PostgreSQL 集群从初始部署到跨 AWS EKS、Azure AKS、GCP GKE 和裸机 k3s 与 Rancher 进行生产级操作所需的一切。每个部分都包含具体的 YAML、Helm 值以及您可以适应您的环境的操作程序。

脆脆数据 PGO v5 架构

PGO v5 是对 Crunchy Postgres Operator 的完全重写。它用单个PostgresCluster资源取代了以前的 pgcluster/pgreplica/pgpolicy CRD,该资源以声明方式描述了 PostgreSQL 部署的各个方面。操作员监视此资源的更改,并协调底层 Kubernetes 对象(StatefulSet、服务、ConfigMap、Secret、作业)以匹配所需的状态。

该架构建立在四个支柱之上。Patroni在每个 PostgreSQL Pod 中作为 sidecar 运行,并使用 Kubernetes 原生分布式共识来管理领导者选举、复制拓扑和自动故障转移。pgBackRest处理完整备份、差异备份和增量备份以及到对象存储(S3、GCS、Azure Blob)或本地 PVC 的连续 WAL 归档。PgBouncer提供轻量级连接池,可保护 PostgreSQL 免受连接风暴的影响。pgMonitor通过 Prometheus 导出器 sidecar 公开 PostgreSQL 指标,以便与现有的可观测性堆栈集成。

脆弱的数据 PGO 架构PGO 操作员盒Postgres集群 CRD主实例StatefulSet(1 个 Pod)Patroni LeaderpgMonitor 导出器副本实例StatefulSet(N 个 Pod)Patroni 复制品流复制pgBackRest 回购S3 / GCS / Azure Blob完整 + 差异 + 增量WAL 存档沃尔PgBouncer 池子部署(2 个以上 Pod)Prometheus + GrafanapgMonitor 指标应用 Pod通过 PgBouncer连接主复制品备份池化器监控

操作器本身是无状态的 - 所有持久状态都存在于 PostgreSQL 集群及其备份存储库中。 这意味着您可以升级或重新启动 Operator,而不会影响正在运行的数据库。运算符协调循环是幂等的:多次应用相同的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          45s

PostgresCluster 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 同步复制。 Pod 反关联性规则确保所有三个实例都落在不同的 Kubernetes 节点上。

基于 Patroni 的 HA 和自动故障转移

Patroni 是嵌入在每个 PGO 管理的 PostgreSQL Pod 中的 HA 框架。它使用 Kubernetes API 作为其分布式配置存储 (DCS) — 不需要单独的 etcd 或 ZooKeeper 集群。 Patroni 持续监控 PostgreSQL 主服务器和副本服务器的运行状况。当主副本无响应时,Patroni 会启动自动故障转移:它将最新的副本提升为主副本,并重新配置其余副本以跟随新的领导者。

PGO 中的故障转移过程的工作原理如下。每个 pod 上的 Patroni 都持有 Kubernetes 中的领导者锁(通过 Endpoints 或 ConfigMap 对象)。当前主节点必须以可配置的时间间隔(默认 10 秒 TTL,3 秒循环等待)更新此锁。如果主节点无法更新(因为它崩溃、节点死亡或网络对其进行分区),最接近主节点 WAL 位置的副本将获取锁并提升自身。整个过程通常在 10 到 30 秒内完成。

同步复制模式通过 Patroni 配置中的synchronous_mode: true启用,可保证零数据丢失 (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 --force

PGO 自动配置 Kubernetes 服务来跟踪 Patroni 领导者。production-db-primary服务始终指向当前持有领导者锁的 Pod,因此通过此服务连接的应用程序只需短暂的连接重置即可体验无缝故障转移。

pgBackRest 备份和恢复

pgBackRest 是集成到 PGO 中的备份引擎。它支持三种备份类型——完整备份、差异备份和增量备份——以及用于时间点恢复 (PITR) 的连续 WAL 归档。 了解它们如何组合在一起对于设计平衡存储成本、备份速度和恢复时间的备份策略至关重要。

pgBackRest 备份架构PostgreSQL 主数据文件 (PGDATA)WAL 段pg_wal/目录pgBackRest 代理连续 WAL 存档计划备份pgBackRest 存储库S3 / GCS / Azure Blob / PVC完全备份完整副本差分自上次完整以来增量(自上次以来)WAL 存档000000030000000A000000030000000B000000030000000C... 连续 ...启用 PITR时间点恢复时间线完整周日凌晨 2 点差分差分差异差分差分完整周日凌晨 2 点连续 WAL 流 →PITR 目标恢复到任意时间点完全备份差分WAL 流恢复目标

完整备份复制整个 PostgreSQL 数据目录,并且是所有其他备份类型的基准。差异备份仅复制自上次完整备份以来更改的页面。增量备份仅复制自上次任何类型备份以来更改的页面。在恢复期间,pgBackRest 自动将所需的备份链接在一起 - 例如,从增量恢复需要增量加上先前的差异(或完整)加上完整备份,加上到达目标恢复点所需的任何 WAL 段。

备份计划配置

备份计划在PostgresCluster规范内 pgBackRest 配置的repos部分中定义。可靠的生产计划通常运行每周完整备份、每日差异备份以及更频繁的增量备份。

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 段直至目标时间,然后启动集群。整个操作由操作员精心安排,无需对各个 Pod 进行手动干预。

PgBouncer 连接池

PostgreSQL 为每个客户端连接创建一个新进程。在规模上——数百或数千个应用程序 Pod,每个应用程序 Pod 维护连接池——这种模型就会崩溃。 PgBouncer 位于您的应用程序和 PostgreSQL 之间,通过少量服务器连接复用许多客户端连接。

PGO 将 PgBouncer 部署为具有自己服务的单独部署。您的应用程序应连接到production-db-pgbouncer服务,而不是直接连接到 PostgreSQL 主服务。 PgBouncer 支持三种池模式:

  • 会话池— 在客户端连接的生命周期内将服务器连接分配给客户端。最安全,但效率最低。
  • 事务池— 仅在事务期间分配服务器连接。对于 Web 工作负载最有效。这是推荐的默认值。
  • 语句池— 为单个语句分配服务器连接。仅适用于简单的非事务性工作负载。
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 的监控集成在每个 PostgreSQL Pod 中部署crunchy-postgres-exportersidecar。该导出器抓取 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: 10s

Crunchy Data 提供了一组可直接导入的预构建 Grafana 仪表板。这些内容涵盖 PostgreSQL 概述、复制状态、pgBackRest 备份状态、PgBouncer 统计信息和 Pod 级资源使用情况。通过 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 部署

在 AWS EKS 上部署 PGO 需要配置用于持久卷的 EBS CSI 驱动程序和用于 S3 备份访问的服务帐户 (IRSA) 的 IAM 角色。此方法避免将长期存在的 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 将自动使用 Pod 的服务帐户凭据(通过 IRSA)——无需显式访问密钥。

backups:
  pgbackrest:
    repos:
      - name: repo1
        s3:
          bucket: mycompany-pg-backups
          endpoint: s3.eu-west-1.amazonaws.com
          region: eu-west-1

对于多可用区弹性,请确保您的 EKS 节点组至少跨越三个可用区,并使用前面显示的拓扑分布约束在它们之间分布 PostgreSQL Pod。

Azure AKS 部署

Azure AKS 使用托管磁盘进行持久存储,使用 Azure Blob 存储进行 pgBackRest 备份。建议的存储类别使用 Premium SSD 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

对于工作负载身份(Azure 相当于 AWS IRSA),使用 OIDC 颁发者配置 AKS 群集,并为 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支持备用集群,实现跨地域容灾。备用集群不断地从主集群的 pgBackRest 存储库重播 WAL,维护一个热副本,在发生区域故障时可以将其提升为独立主集群。

具有 PGO 备用集群的多区域 DRA 区(主要)例如,AWS eu-west-1 / Azure 西欧PostgreSQL 主读/写Patroni Leader复制品 (2)只读同步复制WAL 存档pgBackRest 存储库 (S3/GCS/Blob)启用跨区域复制S3 跨区域复制B 区(备用)例如,AWS us-east-1 / Azure East USPostgreSQL 备用集群来自存储库的连续 WAL 重放只读(待机模式)pgBackRest 存储库(B 区)复制自 A 区WAL 重播故障转移/升级同步模式RPO ≈分钟(WAL 运输)RTO ≈ 5-15 分钟异步(默认)RPO ≈秒-分钟RTO ≈ 5-15 分钟备用集群可通过 PostgresCluster 规格更改升级为独立主集群
# 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 部署

在具有 Rancher 管理的裸机 k3s 上运行 PGO 需要仔细注意存储和网络,因为您没有云提供商管理的磁盘或负载均衡器。

k3s / Rancher PGO 部署(裸机)Rancher 管理服务器k3s 集群(3 个服务器 + N 个代理节点)PGO 操作器代理节点 1PostgreSQL 主+ pgMonitor 导出器Longhorn PVC(数据 + WAL)pgBackRest 存储库(本地 PVC)代理节点 2PostgreSQL 副本 1+ pgMonitor 导出器Longhorn PVC(数据 + WAL)PgBouncer Pod代理节点 3PostgreSQL 复制品 2+ pgMonitor 导出器Longhorn PVC(数据 + WAL)PgBouncer PodMetalLB 负载均衡器 —暴露 pg-primary:5432 + pgbouncer:5432主复制品Longhorn备份PgBouncer金属LB操作员

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: Retain

MetalLB 用于负载平衡

# 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 后,LoadBalancer类型的 PGO 服务将从您的池中接收 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 均通过加密通道进行通信,无需任何手动证书管理。但是,对于生产环境,您通常希望使用由组织的 CA 签名的证书。

# 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-tls

PGO 使用ssl = on配置 PostgreSQL,并设置适当的ssl_cert_file、ssl_key_file和ssl_ca_file参数。 PgBouncer 的配置类似,需要 TLS 进行客户端连接,并在连接到 PostgreSQL 后端时使用 TLS。

# 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 通过 Patroni 动态配置部分公开 PostgreSQL 配置。这是推荐的方法,因为 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 内核和专用于 PostgreSQL 的 32 GB RAM。 将shared_buffers调整为可用 RAM 的大约 25%,将effective_cache_size调整为大约 75%。 WAL 和检查点设置针对写入密集型工作负载进行了调整 — 对于检查点频率影响较小的读取密集型系统,减少max_wal_size。

用户和数据库管理

PGO 通过PostgresCluster规范的users部分以声明方式管理 PostgreSQL 用户和数据库。添加用户时,PGO 在 PostgreSQL 中创建角色,生成随机密码,并将凭据存储在 Kubernetes Secret 中。

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 活动与数据 I/O 发生争用。
  • 使用高 IOPS 存储:gp3 (AWS)、Premium SSD v2 (Azure)、pd-ssd (GCP) 或裸机上支持 NVMe 的 Longhorn。 PostgreSQL 的随机 I/O 模式需要低延迟存储。
  • 卷扩展:确保您的 StorageClass 具有allowVolumeExpansion: true。 PGO 可以在支持的存储提供商上无中断地扩展 PVC。

Pod 中断预算

PGO 会自动为您的 PostgreSQL 实例创建 PDB,但请验证它们是否适合您的 HA 要求。

# Check PDBs
kubectl get pdb -n databases
NAME                    MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
production-db-pgha1     1               N/A               2                     7d

资源请求和限制

  • 将内存请求设置为等于数据库 Pod 的限制,以防止 OOM 终止并确保有保证的 QoS 级别。
  • 保守地设置 CPU 请求并限制更高,以允许在真空或维护操作期间突发。
  • 通过 pgMonitor 指标监控实际使用情况并每季度进行调整。

连接管理

  • 始终通过 PgBouncer 连接,而不是直接连接到 PostgreSQL。
  • 将 PostgreSQL 中的max_connections设置为考虑 PgBouncer 池大小加上系统连接(复制、监控、超级用户)的值。
  • 对 Web 应用程序使用事务池模式。仅当您的应用程序使用准备好的语句或会话级状态(例如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

监控清单

  • 复制延迟:当任何副本超过 100MB 或延迟 60 秒时发出警报。
  • 连接饱和:当活动连接超过max_connections的 80% 时发出警报。
  • 交易率异常:确定 TPS 基线并针对重大偏差发出警报。
  • 磁盘使用情况:使用卷扩展运行手册在 70% 和 85% 阈值时发出警报。
  • 备份新鲜度:当上次完整备份早于 RPO 窗口时发出警报。
  • 锁定争用:针对可能指示应用程序错误的长期锁定(> 30 秒)发出警报。
  • 自动清理运行状况:当表在超过 24 小时内未清理时发出警报。

灾难恢复运行手册

灾难恢复计划只有经过测试才有用。以下运行手册概述了从常见故障场景中恢复的关键过程。

单 Pod 故障

Patroni 和 Kubernetes 自动处理此问题。如果主 Pod 崩溃,Patroni 将在 10-30 秒内升级副本。 Kubernetes 重新启动发生故障的 Pod,该 Pod 作为副本重新加入。

单节点故障

如果运行 PostgreSQL Pod 的节点死亡,Kubernetes 会在健康节点上重新调度 Pod。 Pod 连接到其现有的 PVC(如果存储连接到网络)或从备份恢复(如果使用本地存储)。 Pod 反关联性规则可确保剩余实例继续提供流量服务。

完全集群丢失

如果整个 Kubernetes 集群丢失,请部署新集群,安装 PGO,并创建一个新的PostgresCluster,其中dataSource指向备份存储库。 PGO 恢复最新的备份并重播 WAL 到最近的可用点。

区域故障转移

如果主区域丢失,可通过设置standby.enabled: false提升备用集群。更新您的 DNS 或负载均衡器以指向新的主要区域。当原Region恢复后,可以反向重建备用关系。

# 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 将 Kubernetes 上的 PostgreSQL 从操作负担转变为可管理的声明性系统。PostgresClusterCRD 在单个版本控制资源中捕获整个部署(实例、复制、备份、池化、监控、TLS)。 Patroni 提供经过实战检验的自动故障转移。 pgBackRest 为任何云对象存储提供企业级备份和时间点恢复。 PgBouncer 处理 PostgreSQL 的每连接一个进程模型所需的大规模连接池。 pgMonitor 将您需要的指标提供给 Prometheus 和 Grafana 以实现操作可见性。

AWS EKS、Azure AKS、GCP GKE 和裸机 k3s 的部署模式共享相同的核心PostgresCluster规范 - 变化的是存储类、备份存储库配置和网络层。这种一致性是基于操作员的方法的真正价值:您的团队学习一种工具、一种操作模型和一套适用于任何地方的操作手册。

从三实例集群开始,事务池模式下的 PgBouncer、每周完整加每日差异备份计划以及核心 Prometheus 警报。在第一天就验证您的备份恢复过程,而不是在您第一次需要它时。随着可用性要求和运营成熟度的增长,扩展到多区域备用集群、同步复制和高级调整。操作员负责机械操作;您的责任是充分了解架构,以便针对您的工作负载做出正确的权衡。