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

通过 Zalando Postgres Operator 实现 PostgreSQL 高可用性:多云 Kubernetes 部署指南

使用 Zalando Operator 在任何 Kubernetes 平台上部署生产级 PostgreSQL HA

Balinder Walia2026年4月12日18 min read

简介:为什么 Kubernetes 上的 PostgreSQL 高可用性很重要

在生产中运行 PostgreSQL 需要高可用性 (HA)。以分钟为单位的停机时间可能会给企业造成数百万美元的收入损失、削弱客户信任并违反服务级别协议。 Kubernetes 已成为编排容器化工作负载的事实平台,但在 Kubernetes 上运行 PostgreSQL 等有状态服务会带来独特的挑战:持久存储管理、领导者选举、自动故障转移、备份编排和连接池。

Zalando Postgres Operator是欧洲最大的在线时装零售商 Zalando 构建的一款开源、经过实战考验的解决方案,用于管理生产中的数百个 PostgreSQL 集群。它利用Patroni进行基于共识的领导者选举,利用Spilo作为 PostgreSQL 容器映像,利用WAL-G进行持续归档和时间点恢复,利用PgBouncer进行连接池。这些组件共同提供了完全自动化、自我修复的 PostgreSQL 部署,适用于任何 Kubernetes 发行版 — 从 AWS EKS、Azure AKS 和 Google GKE 等托管云服务到使用 Rancher 运行 k3s 的裸机集群。

在本综合指南中,我们将探索 Zalando Postgres Operator 的架构,逐步介绍多个 Kubernetes 平台上的安装和配置,深入探讨复制、备份、灾难恢复、监控和生产调整。最后,您将掌握在任何 Kubernetes 基础设施上部署和操作生产级 PostgreSQL HA 集群的知识。

Zalando Postgres 操作架构

在部署之前了解架构至关重要。 Zalando Postgres Operator 遵循 Kubernetes 运算符模式:它监视postgresql类型的自定义资源定义 (CRD) 并将所需状态协调为实际 Kubernetes 资源。 以下是组件的组合方式:

Zalando Postgres 操作架构PostgreSQL CRD类型:postgresqlPostgres 操作员手表协调StatefulSet管理 Pod 生命周期主 PodSpilo (PostgreSQL)Patroni 代理副本 Pod 1Spilo (PostgreSQL)Patroni 代理副本 Pod 2Spilo (PostgreSQL)Patroni 代理Patroni DCS (Kubernetes API)领导者选举&集群状态PgBouncer连接池WAL-G备份到 S3/GCS/Azure主复制品共识/DCS备份连接池

Spilo:PostgreSQL 容器映像

Spilo是 Zalando 的 Docker 映像,它将 PostgreSQL 与 Patroni、WAL-G 和基本扩展捆绑在一起。 StatefulSet 中的每个 Pod 都运行一个 Spilo 容器。 Spilo 手柄:

  • PostgreSQL 服务器— 数据库引擎本身,支持版本 13 至 16
  • Patroni— 管理领导者选举、复制和故障转移的 HA 代理
  • WAL-G— 连续 WAL 归档和基础备份工具
  • pg_cron、pg_stat_statements、PostGIS— 预装的常用扩展

Patroni:领导者选举和自动故障转移

Patroni 是 HA 机制的核心。它使用分布式配置存储(DCS)来维护集群状态并执行领导者选举。在 Zalando 运营商环境中,Patroni 使用Kubernetes API本身作为 DCS(通过端点或 ConfigMap),从而无需外部 etcd 或 ZooKeeper 集群。

以下是 Patroni 故障转移过程的工作原理:

  1. 运行状况检查— 每个 Patroni 代理持续监控其本地 PostgreSQL 实例并向 DCS 报告运行状况。
  2. 领导者锁— 主设备在 DCS(Kubernetes 端点对象)中持有领导者锁。锁有一个 TTL(默认 30 秒)。
  3. 故障检测— 如果主节点未能在 TTL 内更新其锁定,副本将检测到这种情况。
  4. 选举— 合格的副本竞争领导者锁。复制延迟最小的副本获胜。
  5. 升级— 获胜副本将自身升级为主副本,更新 DCS,并且 Kubernetesmaster服务端点自动更新。
  6. 防护— 旧的主节点被防护(停止或降级为副本)以防止脑裂。

整个故障转移过程通常会在15-30 秒内完成,从而确保最短的应用程序停机时间。

WAL-G:连续归档和备份

WAL-G 是 PostgreSQL 的下一代归档工具,支持备份到 S3、Google Cloud Storage (GCS) 和 Azure Blob Storage。它提供:

  • 基本备份— 使用pg_basebackup
  • 进行完整物理备份
  • WAL 归档— 用于时间点恢复的连续预写日志传送
  • 增量备份— 仅存储更改页面的增量备份
  • 加密— 静态备份的 AES-256 加密
  • 压缩— LZ4 或 ZSTD 压缩可降低存储成本

Kubernetes 资源层次结构

当您创建postgresql自定义资源时,操作员将创建一组全面的 Kubernetes 资源来管理集群。了解此层次结构对于故障排除和监控非常重要:

Kubernetes 资源层次结构 — Zalando Operatorpostgresql CRDPostgres 操作员StatefulSet服务(主)服务(复制品)端点PDBPodPVCs的秘密PgBouncer 部署服务(池化器)主复制品复制品实线=直接创建|虚线 = 有条件创建由操作员创建的

资源
  • StatefulSet— 管理具有稳定网络身份和有序部署的 PostgreSQL Pod
  • 服务— 两个 ClusterIP 服务:用于主数据库的<cluster-name>和用于只读副本的<cluster-name>-repl
  • 端点— Patroni 更新端点以指向当前领导者以实现无缝故障转移
  • PodDisruptionBudgets (PDB)— 确保至少一个实例在自愿中断期间保持可用
  • Secrets— PostgreSQL 超级用户、复制和应用程序凭证存储为 Kubernetes Secrets
  • PersistentVolumeClaims (PVCs)— 每个 Pod 一个 PVC,用于 PostgreSQL 数据存储
  • PgBouncer 部署— 可选连接池,作为单独的部署部署,具有自己的服务

在多个 Kubernetes 平台上的安装

先决条件

在安装 Zalando Postgres Operator 之前,请确保您拥有:

  • 正在运行的 Kubernetes 集群 (v1.25+)
  • kubectl配置有集群管理员访问权限
  • helmv3 安装
  • 默认 StorageClass 配置的
通过 Helm

操作员安装

推荐安装方式使用Helm。这在所有 Kubernetes 平台上一致工作:

# Add the Zalando Postgres Operator Helm repository
helm repo add postgres-operator-charts https://opensource.zalando.com/postgres-operator/charts/postgres-operator
helm repo add postgres-operator-ui-charts https://opensource.zalando.com/postgres-operator/charts/postgres-operator-ui
helm repo update

# Create a dedicated namespace
kubectl create namespace postgres-operator

# Install the operator with custom values
helm install postgres-operator postgres-operator-charts/postgres-operator \
  --namespace postgres-operator \
  --set configKubernetes.enable_pod_antiaffinity=true \
  --set configKubernetes.pod_environment_configmap=postgres-pod-config \
  --set configAwsOrGcp.aws_region=us-east-1 \
  --set configLoadBalancer.db_hosted_zone=db.example.com \
  --set configConnectionPooler.connection_pooler_default_cpu_request=500m \
  --set configConnectionPooler.connection_pooler_default_memory_request=100Mi

# Optionally install the operator UI for visual management
helm install postgres-operator-ui postgres-operator-ui-charts/postgres-operator-ui \
  --namespace postgres-operator

验证操作员是否正在运行:

kubectl get pods -n postgres-operator
# Expected output:
# NAME                                 READY   STATUS    RESTARTS   AGE
# postgres-operator-7f8b9c6d4-x2k9j   1/1     Running   0          2m

AWS EKS 部署细节

Amazon EKS 需要特定配置才能实现最佳 PostgreSQL 性能:

# EKS-specific Helm values (eks-values.yaml)
configAwsOrGcp:
  aws_region: us-east-1
  enable_ebs_gp3_migration: true
  additional_secret_mount: "aws-iam-token"

configKubernetes:
  enable_pod_antiaffinity: true
  pod_environment_configmap: "postgres-pod-config"
  spilo_privileged: false
  storage_resize_mode: pvc

# Use EBS gp3 StorageClass for better performance
# Create the StorageClass first:
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ebs-gp3-postgres
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  iops: "6000"
  throughput: "400"
  encrypted: "true"
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true

对于 EKS 上的 WAL-G 备份,为服务账户 (IRSA) 配置 IAM 角色:

# Create IAM policy for WAL-G S3 access
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:ListBucket",
        "s3:DeleteObject",
        "s3:GetBucketLocation"
      ],
      "Resource": [
        "arn:aws:s3:::my-pg-backups",
        "arn:aws:s3:::my-pg-backups/*"
      ]
    }
  ]
}

Azure AKS 部署

Azure AKS 使用 Azure 磁盘进行持久存储,使用托管身份进行备份身份验证:

# AKS-specific StorageClass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-premium-postgres
provisioner: disk.csi.azure.com
parameters:
  skuName: Premium_LRS
  cachingMode: ReadOnly
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# WAL-G backup to Azure Blob Storage environment variables
apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-pod-config
  namespace: default
data:
  AZURE_STORAGE_ACCOUNT: "pgbackupsstorage"
  AZURE_STORAGE_ACCESS_KEY: "" # Use Managed Identity instead
  WALG_AZ_PREFIX: "azure://pg-wal-backups/$(SCOPE)"
  USE_WALG_BACKUP: "true"
  USE_WALG_RESTORE: "true"
  BACKUP_SCHEDULE: "0 2 * * *"
  BACKUP_NUM_TO_RETAIN: "7"

Google GKE 部署

Google Kubernetes 引擎使用持久磁盘和工作负载身份进行 GCS 备份访问:

# GKE StorageClass for SSD Persistent Disks
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: ssd-postgres
provisioner: pd.csi.storage.gke.io
parameters:
  type: pd-ssd
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
---
# GCS backup configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-pod-config
  namespace: default
data:
  WALG_GS_PREFIX: "gs://my-pg-backups/$(SCOPE)"
  USE_WALG_BACKUP: "true"
  USE_WALG_RESTORE: "true"
  GOOGLE_APPLICATION_CREDENTIALS: "/var/secrets/google/key.json"
  BACKUP_SCHEDULE: "0 2 * * *"
  BACKUP_NUM_TO_RETAIN: "7"

裸机 k3s/带 Longhorn 存储的 Rancher

对于本地部署,k3s 提供轻量级 Kubernetes 发行版,Longhorn 提供分布式块存储。当您需要完全控制基础设施而不被云供应商锁定时,这种组合是理想的选择。

# Install k3s on all nodes
# Master node:
curl -sfL https://get.k3s.io | sh -s - server \
  --cluster-init \
  --disable traefik \
  --write-kubeconfig-mode 644

# Worker nodes:
curl -sfL https://get.k3s.io | sh -s - agent \
  --server https://master-ip:6443 \
  --token $(cat /var/lib/rancher/k3s/server/node-token)

# Install Longhorn for persistent storage
helm repo add longhorn https://charts.longhorn.io
helm install longhorn longhorn/longhorn \
  --namespace longhorn-system \
  --create-namespace \
  --set defaultSettings.defaultReplicaCount=3 \
  --set defaultSettings.defaultDataPath=/mnt/longhorn

# Install MetalLB for LoadBalancer services
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.5/config/manifests/metallb-native.yaml

# Configure MetalLB IP pool
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: postgres-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.200-192.168.1.210
k3s / Rancher 裸机部署Rancher 管理服务器节点 1(k3s 服务器)PostgreSQL 主Spilo + PatroniZalando 运营商PgBouncer 池Longhorn 音量/mnt/longhorn(3 个副本)节点 2(k3s 服务器)PostgreSQL 复制品Spilo + PatroniPgBouncer 池Prometheus 导出器Longhorn 音量/mnt/longhorn(3 个副本)节点 3(k3s 代理)PostgreSQL 复制品Spilo + PatroniPgBouncer 池Grafana 仪表板Longhorn 音量/mnt/longhorn(3 个副本)HAProxy / MetalLB 负载均衡器WAL-G 备份NFS / MinIO S3 兼容主复制品PgBouncerLonghorn流复制

PostgreSQL 集群 CRD 规格

部署核心 具有 Zalando 运营商的 PostgreSQL 集群是postgresql自定义资源。此 YAML 清单声明了集群的所需状态,操作员将其调整为现实。以下是全面的、可投入生产的 CRD 规格:

apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
  name: pg-production-cluster
  namespace: databases
  labels:
    team: platform
    environment: production
spec:
  teamId: "platform"
  volume:
    size: 100Gi
    storageClass: ebs-gp3-postgres
  numberOfInstances: 3
  enableConnectionPooler: true
  enableReplicaConnectionPooler: true
  connectionPooler:
    numberOfInstances: 2
    mode: transaction
    schema: pooler
    user: pooler
    resources:
      requests:
        cpu: 500m
        memory: 100Mi
      limits:
        cpu: "1"
        memory: 256Mi
  users:
    app_user:
    - superuser
    - createdb
    readonly_user: []
  databases:
    app_database: app_user
  postgresql:
    version: "16"
    parameters:
      shared_buffers: "2GB"
      max_connections: "200"
      work_mem: "64MB"
      maintenance_work_mem: "512MB"
      effective_cache_size: "6GB"
      random_page_cost: "1.1"
      effective_io_concurrency: "200"
      wal_buffers: "64MB"
      max_wal_size: "4GB"
      min_wal_size: "1GB"
      checkpoint_completion_target: "0.9"
      default_statistics_target: "100"
      log_statement: "ddl"
      log_min_duration_statement: "1000"
      idle_in_transaction_session_timeout: "600000"
      lock_timeout: "30000"
      statement_timeout: "60000"
  patroni:
    initdb:
      encoding: "UTF8"
      locale: "en_US.UTF-8"
      data-checksums: "true"
    pg_hba:
    - hostssl all all 0.0.0.0/0 md5
    - host    all all 0.0.0.0/0 md5
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    synchronous_mode: false
    synchronous_mode_strict: false
    maximum_lag_on_failover: 33554432
  resources:
    requests:
      cpu: "2"
      memory: 8Gi
    limits:
      cpu: "4"
      memory: 16Gi
  podAnnotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "9187"
  tolerations:
  - key: "database"
    operator: "Equal"
    value: "postgres"
    effect: "NoSchedule"
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
      - matchExpressions:
        - key: workload-type
          operator: In
          values:
          - database
  enableShmVolume: true
  spiloRunAsUser: 101
  spiloRunAsGroup: 103
  spiloFSGroup: 103

CRD 关键字段说明

  • numberOfInstances— Pod 总数。操作员会自动将其中一个指定为主副本,将其余副本指定为流副本。
  • enableConnectionPooler— 为主要服务部署 PgBouncer sidecar,从而减少连接开销。
  • enableReplicaConnectionPooler— 为副本服务部署单独的 PgBouncer,这对于读取繁重的工作负载至关重要。
  • postgresql.parameters— 直接传递到postgresql.conf的 PostgreSQL 配置参数。
  • patroni— 配置 Patroni 行为,包括 TTL、循环等待、重试超时和同步复制模式。
  • volume.storageClass— 映射到特定于平台的 StorageClass(AWS 上的 EBS gp3、Azure 上的 Premium SSD、GCP 上的 SSD PD、k3s 上的 Longhorn)。
  • enableShmVolume— 在/dev/shm上安装tmpfs,用于 PostgreSQL 共享内存,这对性能至关重要。

与 PgBouncer

的连接池

PostgreSQL 的按连接处理模型使得处理大量客户端连接的成本很高。每个连接消耗大约 10MB 的 RAM。 PgBouncer 通过在一小部分实际 PostgreSQL 连接上复用数千个客户端连接来解决这个问题。

Zalando 运营商原生支持 PgBouncer 部署。当您在CRD中设置enableConnectionPooler: true时,操作员创建:

  • 具有可配置副本数量的 PgBouncer 部署
  • 用于池连接的专用服务 (<cluster-name>-pooler)
  • 与 PostgreSQL
  • 自动凭证同步

PgBouncer 配置模式

# PgBouncer connection pooler modes:
#
# transaction (recommended for most workloads)
#   - Connection returned to pool after each transaction
#   - Best balance of efficiency and compatibility
#   - Cannot use session-level features (prepared statements, temp tables)
#
# session
#   - Connection held for entire client session
#   - Full PostgreSQL compatibility
#   - Lower pooling efficiency
#
# statement
#   - Connection returned after each statement
#   - Most efficient but most restrictive
#   - Only works with autocommit queries

# Custom PgBouncer configuration via operator
spec:
  connectionPooler:
    numberOfInstances: 3
    mode: transaction
    schema: pooler
    user: pooler
    defaultPoolSize: 25
    maxDBConnections: 100
    resources:
      requests:
        cpu: 250m
        memory: 128Mi
      limits:
        cpu: "1"
        memory: 256Mi

对于需要准备好的语句或会话级功能的应用程序,可以绕过 PgBouncer 直接连接到 PostgreSQL 服务,或者使用session池模式,但会降低连接效率。

多区域 PostgreSQL 复制

对于需要从多个地理位置低延迟读取或跨区域灾难恢复的全球应用程序,多区域复制至关重要。 Zalando 操作员通过备用集群支持这一点,这些备用集群通过流复制或 WAL-G 存档从主集群进行复制。

多区域 PostgreSQL 流复制US-EAST-1 (AWS EKS)主集群pg-prod-us(3 个 Pod)主复制品AZ内的同步复制WAL-G → S3(连续)PgBouncer 池子EU-WEST-1 (Azure AKS)备用集群pg-standby-eu(2 个 Pod)备用复制品级联复制WAL-G → Azure BlobPgBouncer(只读)AP-东南 (GCP GKE)备用集群pg-standby-ap(2 个 Pod)备用复制品级联复制WAL-G → GCS 铲斗PgBouncer(只读)异步异步(WAL 发货)复制拓扑主要(美国东部)→ 异步流式传输到欧盟西部和欧洲AP-SOUTHEAST备用集群| RPO:~秒 | RTO:<5 分钟,手动升级同步复制异步复制主备用领导者只读副本

流复制配置

PostgreSQL 流复制是 Zalando 运营商中 HA 的基础。它的工作原理是近乎实时地将预写日志 (WAL) 记录从主服务器传送到副本。操作员会自动进行配置,但了解详细信息有助于调整和故障排除。

  • 同步复制— 主节点在提交事务之前等待至少一个副本确认 WAL 接收。这保证了零数据丢失 (RPO=0),但增加了延迟。使用patroni.synchronous_mode: true启用。
  • 异步复制— 主节点立即提交并异步传送 WAL。延迟稍低,但故障转移期间可能会丢失数据。这是默认设置。
  • 级联复制— 副本可以从其他副本而不是主副本进行复制,从而减少大型集群中主副本的负载。
# Enable synchronous replication for zero data loss
spec:
  patroni:
    synchronous_mode: true
    synchronous_mode_strict: false  # Allow async if no sync replica available
    synchronous_node_count: 1       # Number of sync replicas required
  postgresql:
    parameters:
      synchronous_commit: "on"      # Matches Patroni synchronous_mode
      max_wal_senders: "10"         # Maximum WAL sender processes
      wal_keep_size: "1GB"          # WAL retention for replica catch-up
      hot_standby: "on"             # Allow queries on replicas
      hot_standby_feedback: "on"    # Reduce query conflicts on replicas

使用 WAL-G

进行备份和恢复

WAL-G 备份配置

正确的备份配置对于灾难恢复至关重要。 Zalando 操作员集成了 WAL-G 以连续备份到对象存储。以下是 S3 兼容存储的完整配置:

# ConfigMap for WAL-G backup configuration
apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-pod-config
  namespace: databases
data:
  # S3 backup configuration
  AWS_ENDPOINT: "https://s3.us-east-1.amazonaws.com"
  AWS_S3_FORCE_PATH_STYLE: "false"
  AWS_REGION: "us-east-1"
  WALG_S3_PREFIX: "s3://my-pg-backups/$(SCOPE)"
  WALG_DISABLE_S3_SSE: "false"
  WALG_S3_SSE: "aws:kms"
  WALG_S3_SSE_KMS_ID: "arn:aws:kms:us-east-1:123456789:key/mrk-abcdef"

  # Backup scheduling and retention
  USE_WALG_BACKUP: "true"
  USE_WALG_RESTORE: "true"
  BACKUP_SCHEDULE: "0 1 * * *"       # Daily at 1 AM UTC
  BACKUP_NUM_TO_RETAIN: "14"          # Keep 14 daily backups

  # WAL archiving
  WALG_COMPRESSION_METHOD: "zstd"     # Better compression than lz4
  WALG_DELTA_MAX_STEPS: "6"           # Delta backups between full backups
  WALG_UPLOAD_CONCURRENCY: "4"        # Parallel upload streams
  WALG_DOWNLOAD_CONCURRENCY: "4"      # Parallel download for restore
  WALG_UPLOAD_DISK_CONCURRENCY: "4"   # Disk read concurrency

  # Clone configuration
  CLONE_AWS_ENDPOINT: "https://s3.us-east-1.amazonaws.com"
  CLONE_AWS_REGION: "us-east-1"
  CLONE_WALG_S3_PREFIX: "s3://my-pg-backups/$(CLONE_SCOPE)"
  CLONE_METHOD: "CLONE_WITH_WALG"
  CLONE_USE_WALG_RESTORE: "true"

时间点恢复 (PITR)

PITR 允许您将数据库恢复到任何特定时刻,这对于从意外数据删除或损坏中恢复至关重要。 Zalando运营商通过克隆机制支持PITR:

# Clone a cluster with PITR
apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
  name: pg-restored-cluster
  namespace: databases
spec:
  teamId: "platform"
  volume:
    size: 100Gi
    storageClass: ebs-gp3-postgres
  numberOfInstances: 3
  postgresql:
    version: "16"
  clone:
    cluster: "pg-production-cluster"
    timestamp: "2026-04-11T14:30:00+00:00"  # Restore to this exact moment
    s3_wal_path: "s3://my-pg-backups/wal/16/pg-production-cluster"
    s3_endpoint: "https://s3.us-east-1.amazonaws.com"
    s3_access_key_id: ""      # Use IAM role instead
    s3_secret_access_key: ""  # Use IAM role instead

应用 CRD 时,操作员执行以下步骤:

  1. 查找目标时间戳之前的最新基础备份
  2. 将基础备份恢复到新 StatefulSet 的主 Pod
  3. 重放 WAL 段直至指定时间戳
  4. 打开数据库进行读写操作
  5. 设置到副本 Pod 的流式复制

用于灾难恢复的备用集群

备用集群持续从主集群进行复制,提供可在灾难期间升级的热备用。这与集群内的副本不同 - 备用集群是完全独立的 Kubernetes 资源,可以在不同的命名空间、集群甚至区域中运行。

# Standby cluster in a different region
apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
  name: pg-standby-eu
  namespace: databases
spec:
  teamId: "platform"
  volume:
    size: 100Gi
    storageClass: managed-premium-postgres
  numberOfInstances: 2
  postgresql:
    version: "16"
  standby:
    standby_host: "pg-production-cluster.databases.svc.cluster.local"
    standby_port: "5432"
    # Alternative: replicate from S3 WAL archive
    # s3_wal_path: "s3://my-pg-backups/wal/16/pg-production-cluster"
  enableConnectionPooler: true
  enableReplicaConnectionPooler: true

要将备用集群升级为独立主集群(在灾难恢复期间),只需从 CRD 中删除standby部分并应用:

# Edit the standby cluster CRD to remove standby section
kubectl patch postgresql pg-standby-eu -n databases --type json \
  -p '[{"op": "remove", "path": "/spec/standby"}]'

# The operator will promote the standby to primary
# Update your application DNS/service mesh to point to the new primary

使用 Prometheus 和 Grafana 进行监控

全面监控对于生产 PostgreSQL 部署来说是不容忽视的。 Zalando 运营商支持通过postgres_exportersidecar 导出 Prometheus 指标。以下是如何设置完整的监控堆栈:

适用于 Prometheus 的

ServiceMonitor

# ServiceMonitor for PostgreSQL metrics
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: postgres-monitor
  namespace: databases
  labels:
    team: platform
    release: prometheus
spec:
  selector:
    matchLabels:
      team: platform
  namespaceSelector:
    matchNames:
    - databases
  endpoints:
  - port: exporter
    interval: 15s
    scrapeTimeout: 10s
    path: /metrics
    relabelings:
    - sourceLabels: [__meta_kubernetes_pod_label_spilo_role]
      targetLabel: role
    - sourceLabels: [__meta_kubernetes_pod_label_cluster_name]
      targetLabel: cluster
---
# PodMonitor alternative (scrapes pods directly)
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: postgres-pod-monitor
  namespace: databases
spec:
  selector:
    matchLabels:
      application: spilo
  podMetricsEndpoints:
  - port: exporter
    interval: 15s
    path: /metrics

监控

的关键指标

以下是需要在 Grafana 仪表板中跟踪的最关键的 PostgreSQL 指标:

  • pg_stat_replication_lag— 复制延迟(以字节和秒为单位)。如果滞后超过 RPO 阈值,则发出警报。
  • pg_stat_activity_count— 按状态划分的活动连接。连接池耗尽时发出警报。
  • pg_stat_database_tup_fetched/returned/inserted/updated/deleted— 查询吞吐量指标。
  • pg_stat_bgwriter_buffers_checkpoint/clean/backend— 缓冲区管理效率。
  • pg_database_size_bytes— 容量规划的数据库大小随着时间的推移而增长。
  • pg_locks_count— 锁争用。对过多的等待锁发出警报。
  • pg_stat_statements_calls/mean_time— 查询性能统计信息以进行优化。
  • patroni_postgres_running— Patroni 运行状况(1 = 正在运行,0 = 关闭)。
  • patoni_master— 哪个 pod 是当前主节点(1 = 主节点,0 = 副本节点)。
  • pg_up— 基本 PostgreSQL 可用性探测器。

警报规则

# PrometheusRule for PostgreSQL alerts
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: postgres-alerts
  namespace: databases
spec:
  groups:
  - name: postgresql.rules
    rules:
    - alert: PostgreSQLDown
      expr: pg_up == 0
      for: 1m
      labels:
        severity: critical
      annotations:
        summary: "PostgreSQL instance {{ $labels.instance }} is down"
    - alert: PostgreSQLReplicationLag
      expr: pg_stat_replication_pg_wal_lsn_diff > 100000000
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "Replication lag is {{ $value }} bytes on {{ $labels.instance }}"
    - alert: PostgreSQLHighConnections
      expr: sum(pg_stat_activity_count) by (instance) > 180
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "{{ $value }} active connections on {{ $labels.instance }}"
    - alert: PostgreSQLDeadlocks
      expr: rate(pg_stat_database_deadlocks[5m]) > 0
      for: 1m
      labels:
        severity: warning
      annotations:
        summary: "Deadlocks detected on {{ $labels.datname }}"
    - alert: PatroniFailover
      expr: changes(patroni_master[5m]) > 0
      labels:
        severity: critical
      annotations:
        summary: "Patroni failover occurred in cluster {{ $labels.cluster }}"

生产调整指南

正确的调整对于在 Kubernetes 上从 PostgreSQL 获得最大性能至关重要。应根据您的 Pod 资源限制和工作负载特征调整以下参数。

存储器配置

# For a pod with 16GB memory limit:
postgresql:
  parameters:
    # shared_buffers: 25% of total memory
    shared_buffers: "4GB"

    # effective_cache_size: 75% of total memory
    # (tells planner how much OS cache to expect)
    effective_cache_size: "12GB"

    # work_mem: shared_buffers / (max_connections * 2)
    # Conservative to prevent OOM
    work_mem: "10MB"

    # maintenance_work_mem: 5-10% of total memory
    # Used for VACUUM, CREATE INDEX, ALTER TABLE
    maintenance_work_mem: "1GB"

    # temp_buffers: memory for temp tables per session
    temp_buffers: "32MB"

    # huge_pages: try to use huge pages (requires OS config)
    huge_pages: "try"

WAL 和检查点配置

postgresql:
  parameters:
    # WAL settings
    wal_buffers: "64MB"           # 1/32 of shared_buffers, max 64MB
    wal_compression: "zstd"        # Compress WAL (PG 15+)
    max_wal_size: "8GB"            # Before forced checkpoint
    min_wal_size: "2GB"            # WAL disk reservation
    wal_level: "replica"           # Required for replication

    # Checkpoint settings
    checkpoint_completion_target: "0.9"  # Spread I/O over 90% of interval
    checkpoint_timeout: "15min"          # Max time between checkpoints

查询规划器和 I/O

postgresql:
  parameters:
    # Cost parameters for SSD storage
    random_page_cost: "1.1"          # SSD: close to seq_page_cost
    seq_page_cost: "1.0"             # Sequential I/O baseline
    effective_io_concurrency: "200"   # Concurrent I/O for SSD

    # Planner behavior
    default_statistics_target: "200"  # More accurate statistics
    from_collapse_limit: 12           # JOIN planning threshold
    join_collapse_limit: 12           # JOIN planning threshold

    # Parallel queries
    max_parallel_workers_per_gather: "4"
    max_parallel_workers: "8"
    max_parallel_maintenance_workers: "4"
    parallel_tuple_cost: "0.01"
    parallel_setup_cost: "1000"

连接和记录

postgresql:
  parameters:
    # Connection limits
    max_connections: "200"                   # Keep low, use PgBouncer
    superuser_reserved_connections: "5"       # Reserve for admin access

    # Logging for troubleshooting
    log_statement: "ddl"                     # Log DDL statements
    log_min_duration_statement: "500"         # Log queries > 500ms
    log_checkpoints: "on"                    # Log checkpoint activity
    log_connections: "off"                   # Too noisy in production
    log_disconnections: "off"                # Too noisy in production
    log_lock_waits: "on"                     # Log lock waits
    log_temp_files: "0"                      # Log all temp file usage
    log_autovacuum_min_duration: "1000"       # Log slow autovacuum

    # Statement timeouts
    statement_timeout: "60000"               # 60 second query timeout
    lock_timeout: "30000"                    # 30 second lock timeout
    idle_in_transaction_session_timeout: "600000"  # 10 min idle txn timeout

滚动更新和版本升级

小版本升级

当您更新 Spilo 映像标签时,操作员会自动处理

次要版本升级(例如,16.2 至 16.3)。 操作员执行滚动重启:

# Update the operator configuration to use a new Spilo image
helm upgrade postgres-operator postgres-operator-charts/postgres-operator \
  --namespace postgres-operator \
  --set configGeneral.docker_image=ghcr.io/zalando/spilo-16:3.1-p1 \
  --reuse-values

# The operator will perform rolling updates:
# 1. Restart replicas one at a time
# 2. Failover the primary to a freshly updated replica
# 3. Restart the old primary (now a replica)

主要版本升级

主要版本升级(例如 PostgreSQL 15 至 16)需要更仔细的规划。 Zalando运营商支持使用pg_upgrade进行就地重大升级:

# Step 1: Update the CRD to the new major version
spec:
  postgresql:
    version: "16"  # Changed from "15"

# Step 2: The operator detects the version change and:
# a) Scales down the StatefulSet to 1 replica
# b) Runs pg_upgrade on the primary pod
# c) Scales back up to the desired numberOfInstances
# d) Replicas are rebuilt from the upgraded primary

# Step 3: Verify the upgrade
kubectl exec -it pg-production-cluster-0 -n databases -- \
  su postgres -c "psql -c 'SELECT version()'"

# Step 4: Run ANALYZE to update statistics after upgrade
kubectl exec -it pg-production-cluster-0 -n databases -- \
  su postgres -c "psql -c 'ANALYZE VERBOSE'"

重大升级的重要注意事项:

  • 在升级
  • 之前始终进行全新备份
  • 首先在克隆集群上测试升级
  • 主要升级需要停机(通常为 5-30 分钟,具体取决于数据库大小)
  • 查看 PostgreSQL 发行说明以了解重大更改
  • 副本重建后密切监控复制延迟
  • 在所有数据库上运行ANALYZE以重新生成查询规划器统计信息

高级操作模式

逻辑备份

除了物理 WAL-G 备份外,运营商还支持使用pg_dump进行逻辑备份。逻辑备份对于跨版本迁移和选择性表恢复非常有用:

# Enable logical backups in the operator configuration
helm upgrade postgres-operator postgres-operator-charts/postgres-operator \
  --namespace postgres-operator \
  --set configLogicalBackup.logical_backup_schedule="0 3 * * *" \
  --set configLogicalBackup.logical_backup_s3_bucket="my-pg-logical-backups" \
  --set configLogicalBackup.logical_backup_s3_region="us-east-1" \
  --set configLogicalBackup.logical_backup_s3_sse="AES256" \
  --reuse-values

# The operator creates a CronJob for each cluster that:
# 1. Connects to the primary PostgreSQL instance
# 2. Runs pg_dumpall (or pg_dump per database)
# 3. Compresses and uploads to S3

自定义 Pod 环境变量

您可以使用 ConfigMap 将环境变量注入 Spilo pod。这对于配置 WAL-G、自定义脚本或调整操作系统级参数非常有用:

apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-pod-config
  namespace: databases
data:
  # Custom Spilo configurations
  SPILO_CONFIGURATION: |
    bootstrap:
      dcs:
        ttl: 30
        loop_wait: 10
        retry_timeout: 10
        maximum_lag_on_failover: 33554432
        postgresql:
          use_pg_rewind: true
          use_slots: true
          parameters:
            archive_mode: "on"
            archive_timeout: 1800s
  # Enable pg_stat_statements
  POSTGRESQL_SHARED_PRELOAD_LIBRARIES: "bg_mon,pg_stat_statements,pgextwlist,pg_auth_mon,set_user,timescaledb,pg_cron,pg_stat_kcache"
  # Cron jobs inside PostgreSQL
  ENABLE_PG_CRON: "true"

安全网络策略

在生产中,使用 Kubernetes 网络策略限制对 PostgreSQL Pod 的网络访问:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: postgres-network-policy
  namespace: databases
spec:
  podSelector:
    matchLabels:
      application: spilo
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: app-namespace
    - podSelector:
        matchLabels:
          app: backend
    ports:
    - protocol: TCP
      port: 5432
    - protocol: TCP
      port: 8008   # Patroni REST API
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
    ports:
    - protocol: TCP
      port: 9187   # Prometheus exporter
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
    ports:
    - protocol: TCP
      port: 443    # S3/GCS/Azure for backups

常见问题故障排除

即使实现自动化,也会出现问题。以下是使用 Zalando 操作器运行 PostgreSQL 时最常见的问题及其解决方案:

1. Pod 卡在待处理状态

# Check events for the pod
kubectl describe pod pg-production-cluster-0 -n databases

# Common causes:
# - No nodes with matching tolerations/affinity
# - Insufficient CPU or memory on nodes
# - PVC cannot be provisioned (check StorageClass)

# Fix: Check node resources and storage availability
kubectl get nodes -o custom-columns=NAME:.metadata.name,CPU:.status.allocatable.cpu,MEM:.status.allocatable.memory
kubectl get pvc -n databases

2. 复制延迟不断增长

# Check replication status on the primary
kubectl exec -it pg-production-cluster-0 -n databases -- \
  su postgres -c "psql -c 'SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag FROM pg_stat_replication'"

# Common causes:
# - Replica CPU/IO saturation
# - Network bandwidth limits
# - Long-running queries on replica blocking WAL replay
# - Insufficient wal_keep_size

# Fix: Check replica resources and cancel blocking queries
kubectl exec -it pg-production-cluster-1 -n databases -- \
  su postgres -c "psql -c 'SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE state = \"active\" AND query_start < now() - interval \"5 minutes\"'"

3. 故障转移不触发

# Check Patroni cluster status
kubectl exec -it pg-production-cluster-0 -n databases -- \
  su postgres -c "patronictl list"

# Check Patroni logs
kubectl logs pg-production-cluster-0 -n databases -c postgres | grep -i patroni

# Manual failover (if auto-failover is stuck)
kubectl exec -it pg-production-cluster-0 -n databases -- \
  su postgres -c "patronictl failover --candidate pg-production-cluster-1 --force"

4. 备份失败

# Check WAL-G backup status
kubectl exec -it pg-production-cluster-0 -n databases -- \
  su postgres -c "envdir /run/etc/wal-e.d/env wal-g backup-list"

# Check backup CronJob logs
kubectl logs -l application=spilo,cluster-name=pg-production-cluster -n databases | grep -i wal-g

# Verify S3/GCS credentials
kubectl exec -it pg-production-cluster-0 -n databases -- \
  su postgres -c "envdir /run/etc/wal-e.d/env aws s3 ls s3://my-pg-backups/"

安全最佳实践

在 Kubernetes 上保护 PostgreSQL 需要采用深度防御方法:

  • TLS 加密— 为所有客户端连接启用 SSL。操作员可以使用 cert-manager 自动提供证书。
  • 机密管理— 使用 Kubernetes 机密(或外部机密管理器,如 Vault)作为数据库凭证。切勿将密码存储在 ConfigMap 中。
  • RBAC— 限制操作员 ServiceAccount 权限。对应用程序数据库用户使用最低权限访问。
  • NetworkPolicies— 限制 Pod 到 Pod 的通信,如上一节所示。
  • pg_hba.conf— 配置基于主机的身份验证以限制哪些 IP 和用户可以连接。
  • 审核日志记录— 启用pgaudit扩展,以在受监管的环境中进行 SQL 审核日志记录。
  • 加密存储— 使用加密存储类(EBS 加密、Azure 磁盘加密等)。
  • Pod 安全标准— 在安全上下文受限的情况下以非 root 身份运行 Spilo Pod。
# Security-hardened CRD settings
spec:
  spiloRunAsUser: 101
  spiloRunAsGroup: 103
  spiloFSGroup: 103
  enableShmVolume: true
  patroni:
    pg_hba:
    - hostssl all all 0.0.0.0/0 md5
    - host    replication standby all md5
    - hostssl replication standby all md5
  postgresql:
    parameters:
      ssl: "on"
      ssl_min_protocol_version: "TLSv1.3"
      password_encryption: "scram-sha-256"
  additionalVolumes:
  - name: postgres-tls
    mountPath: /tls
    secret:
      secretName: pg-tls-cert
      defaultMode: 0640

容量规划和规模调整

适当的尺寸可确保稳定的性能和成本效率。 使用这些指南作为起点,并根据您的工作负载监控数据进行调整:

工作负载CPU内存存储实例
开发500m1Gi10Gi1
小量产2 核8Gi50Gi SSD3
中等量产4 核16Gi200Gi SSD3
大批量生产8 核32Gi500Gi SSD5
企业/分析16+ 内核64Gi+1Ti+ SSD5+

完整的端到端部署示例

让我们将所有内容放在一起,在新的 Kubernetes 集群上从头开始进行完整部署:

# Step 1: Create namespace and configure storage
kubectl create namespace databases
kubectl create namespace postgres-operator

# Step 2: Install the Zalando Postgres Operator
helm repo add postgres-operator-charts \
  https://opensource.zalando.com/postgres-operator/charts/postgres-operator
helm repo update

helm install postgres-operator postgres-operator-charts/postgres-operator \
  --namespace postgres-operator \
  --set configKubernetes.enable_pod_antiaffinity=true \
  --set configKubernetes.pod_environment_configmap=databases/postgres-pod-config

# Step 3: Create backup configuration
kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-pod-config
  namespace: databases
data:
  USE_WALG_BACKUP: "true"
  USE_WALG_RESTORE: "true"
  WALG_S3_PREFIX: "s3://my-pg-backups/\$(SCOPE)"
  BACKUP_SCHEDULE: "0 1 * * *"
  BACKUP_NUM_TO_RETAIN: "14"
EOF

# Step 4: Deploy the PostgreSQL cluster
kubectl apply -f - <<EOF
apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
  name: pg-app-cluster
  namespace: databases
  labels:
    team: platform
spec:
  teamId: "platform"
  volume:
    size: 50Gi
  numberOfInstances: 3
  enableConnectionPooler: true
  enableReplicaConnectionPooler: true
  users:
    app_user:
    - superuser
    - createdb
  databases:
    app_db: app_user
  postgresql:
    version: "16"
    parameters:
      shared_buffers: "2GB"
      work_mem: "64MB"
      effective_cache_size: "6GB"
  resources:
    requests:
      cpu: "2"
      memory: 8Gi
    limits:
      cpu: "4"
      memory: 16Gi
EOF

# Step 5: Wait for cluster readiness
kubectl wait --for=condition=Running postgresql/pg-app-cluster \
  -n databases --timeout=300s

# Step 6: Verify cluster status
kubectl get postgresql -n databases
kubectl get pods -n databases -l cluster-name=pg-app-cluster
kubectl get svc -n databases -l cluster-name=pg-app-cluster

# Step 7: Get connection credentials
export PGPASSWORD=$(kubectl get secret app-user.pg-app-cluster.credentials.postgresql.acid.zalan.do \
  -n databases -o jsonpath='{.data.password}' | base64 -d)

# Step 8: Connect and verify
kubectl run pg-client --rm -it --image=postgres:16 -n databases -- \
  psql -h pg-app-cluster-pooler -U app_user -d app_db -c "SELECT version();"

结论

Zalando Postgres Operator 将 Kubernetes 上的 PostgreSQL 从复杂的操作挑战转变为可管理的自动化部署。通过利用 Patroni 进行基于共识的故障转移、利用 Spilo 实现包含电池的容器映像、利用 WAL-G 进行持续备份和恢复以及利用 PgBouncer 实现高效连接池,您将获得一个跨 AWS EKS、Azure AKS、Google GKE 和裸机 k3s 集群一致工作的生产级数据库平台。

本指南的主要要点是:

  • 自动化一切— 让操作员处理 StatefulSet 管理、故障转移和备份调度。手动干预应该是例外。
  • 积极监控— 从第一天开始部署 Prometheus 和 Grafana。复制滞后、连接计数和锁争用是您的早期预警信号。
  • 灾难规划— 配置 WAL-G 备份、定期测试 PITR,并为关键工作负载维护备用集群。
  • 根据您的工作负载进行调整— 默认 PostgreSQL 参数比较保守。根据您的资源分配和查询模式调整shared_buffers、work_mem和检查点设置。
  • 默认安全— 启用 TLS、使用 SCRAM-SHA-256 身份验证、限制网络访问并加密静态存储。
  • 测试升级— 在将其应用于生产之前,始终克隆集群并测试主要版本升级。

凭借这一全面的基础,您可以在任何 Kubernetes 平台上部署和操作高度可用的 PostgreSQL 集群,为需要大规模可靠性、性能和数据完整性的应用程序提供服务。