☰
OpenShift 上部署 Patroni 高可用 PostgreSQL 集群:从模板安装到 Jenkins 集成压测
2026/9/25 7:24:49 网站建设 项目流程
  • 数据库
  • 高可用
  • 集群管理
  • 运维
  • 后端

【免费下载链接】patroni

A template for PostgreSQL High Availability with Etcd, Consul, ZooKeeper, or Kubernetes

项目地址:https://gitcode.com/gh_mirrors/pa/patroni
点击查看免费下载

本指南围绕 Patroni 仓库中的kubernetes/openshift-example示例,完整讲解在 OpenShift(含restrictedSCC)环境下部署三节点 Patroni 高可用 PostgreSQL 集群的步骤,以及如何借助仓库自带的 Jenkins 管道(kubernetes/openshift-example/test/Jenkinsfile)自动创建 pgbench 压测 Pod 对集群执行基准测试。读完本文,你将掌握 OpenShift 模板化的集群交付方式、动态 UID/GID 镜像适配原理,以及一套可自定义的 CI 验证方案。

一、为什么 Patroni 能在 OpenShift 上运行

Patroni 的 Kubernetes 发行版默认基于 kubernetes/README.md 中描述的 StatefulSet 三 Pod 部署模型。而 OpenShift 与原生 Kubernetes 的主要差异在于安全约束:OpenShift 默认以随机 UID 运行容器,并施加restrictedSCC(Security Context Constraints)。

为此,仓库在 kubernetes/entrypoint.sh 中做了针对性适配:当容器内 UID ≥ 10000 时,脚本会把当前运行 UID/GID 回写进/etc/passwd中的postgres用户,保证 PostgreSQL 进程能以非 root 身份、动态分配的 UID 正常读写数据目录:

if [[ $UID -ge 10000 ]]; then GID=$(id -g) sed -e "s/^postgres:x:[^:]*:[^:]*:/postgres:x:$UID:$GID:/" /etc/passwd > /tmp/passwd cat /tmp/passwd > /etc/passwd rm /tmp/passwd fi

同时,kubernetes/Dockerfile 在构建阶段执行chmod 775 $PGHOME与chmod 664 /etc/passwd,使目录和文件具备 group 可写权限,以配合 OpenShift 的随机 UID 运行模型。这两处改动是 OpenShift 版镜像区别于普通 K8s 镜像的核心。

二、整体部署流程(官方示例逐步操作)

以下步骤全部来自 kubernetes/openshift-example/README.md,是官方给出的可执行流程。

1. 创建测试项目

oc new-project patroni-test

2. 构建镜像

在 OpenShift 中,镜像以 ImageStream 方式管理。先在openshift命名空间导入 PostgreSQL 基础镜像,再基于 Patroni 仓库源码直接触发构建(构建上下文指向kubernetes目录,即该目录下的 Dockerfile 与 entrypoint.sh 会被使用):

oc import-image postgres:10 --confirm -n openshift oc new-build https://github.com/patroni/patroni --context-dir=kubernetes -n openshift

说明:原示例使用的postgres:10标签可在导入时替换为你环境中实际存在的镜像标签;仓库当前 Dockerfile 已基于postgres:16构建,实际使用时应保持基础镜像与构建上下文一致。

官方文档特别提醒两点:

  • 若此构建上游已合并进 Patroni 主仓库,请同步更新引用;
  • 若该模板要供多用户共享,上述构建命令应在共享命名空间(如openshift)中执行,而不是各用户项目内重复构建。

3. 部署镜像:两种模板的选择

templates 目录 下提供两个 OpenShift Template:

模板文件用途
template_patroni_ephemeral.yml无持久化存储,Pod 销毁即数据丢失,仅用于测试
template_patroni_persistent.yaml通过 StatefulSet 的volumeClaimTemplates申请 PVC,数据持久化

两者唯一的本质区别在于:StatefulSet 是否请求持久化存储。Ephemeral 模板的注解中明确警告:

WARNING: Any data stored will be lost upon pod destruction. Only use this template for testing.

4. 创建并实例化模板

若模板需要跨项目共享,先安装到openshift命名空间:

oc create -f templates/template_patroni_ephemeral.yml -n openshift

随后在自有项目中实例化(oc new-app会按模板参数自动生成 Service、StatefulSet、Secret、RBAC 等资源对象):

oc new-app patroni-pgsql-ephemeral

注意:oc new-app patroni-pgsql-ephemeral引用的是模板的metadata.name(即patroni-pgsql-ephemeral),并非文件名。Persistent 模板的对应名称为patroni-pgsql-persistent。

5. 验证集群初始化

Pod 运行起来后,应能看到两个 Patroni 用于 DCS 协调的 ConfigMap(以默认集群名patroni-persistent为例,这里展示的是 README 中的示例输出):

$ oc get configmap NAME DATA AGE patroniocp-config 0 1m patroniocp-leader 0 1m

这两个 ConfigMap 由 Patroni 的 Kubernetes DCS 实现自动创建与维护:-config保存集群动态配置与历史时间线,-leader记录当前 leader 租约。对应源码位于 patroni/dcs/kubernetes.py,其内部通过 leader 路径(leader_path)与 annotations 机制实现选主与续约。

三、模板资源拆解:OpenShift 模板到底创建了什么

以 template_patroni_persistent.yaml 为例,模板的objects数组依次定义了以下资源:

1. 三类 Service 与角色标签

模板创建三个ClusterIP类型 Service,全部监听/转发 5432 端口:

  • 集群名 Service(如patroni-persistent):不设 selector,配合同名 Endpoints 使用,由 Patroni 动态维护成员地址;
  • Primary Service(如patroni-persistent-primary):selector 为role: primary;
  • Replica Service(如patroni-persistent-replica):selector 为role: replica。

role: primary/role: replica标签正是 Patroni 写入 Pod 的。在 patroni/dcs/kubernetes.py 中可看到默认配置:

self._leader_label_value = config.get('leader_label_value', 'primary') self._follower_label_value = config.get('follower_label_value', 'replica')

而update_pods相关的标签同步逻辑会把当前角色写入role标签(patroni/dcs/kubernetes.py中tmp_role = 'primary'以及{'replica': self._follower_label_value}.get(data['role'], data['role'])的映射),从而保证 Service selector 总能指向正确的 Pod 集合——这也是客户端读写分离能直接依赖*-primary/*-replica服务名的原因。

2. Secret 保存数据库凭据

kind: Secret stringData: superuser-password: ${PATRONI_SUPERUSER_PASSWORD} replication-password: ${PATRONI_REPLICATION_PASSWORD}

后续容器环境变量通过secretKeyRef引用这两个键,避免密码明文进入模板实例。

3. StatefulSet:核心 Pod 规格

模板参数replicas: 3、podManagementPolicy: OrderedReady、updateStrategy: OnDelete,Pod 模板包含:

  • initContainerfix-perms:在数据卷挂载点预创建数据目录并收紧权限:

    command: ["sh", "-c", "mkdir -p /home/postgres/pgdata/pgroot/data && chmod 0700 /home/postgres/pgdata/pgroot/data"]

    这与 kubernetes/entrypoint.sh 中动态 UID 适配配合,解决 OpenShift 随机 UID 下 PVC 属主问题。

  • 关键环境变量(全部通过PATRONI_*前缀自动映射进 Patroni 配置):

    环境变量来源/值作用
    PATRONI_KUBERNETES_POD_IPfieldRef: status.podIPPatroni REST API 与 PostgreSQL 的 connect_address
    PATRONI_KUBERNETES_NAMESPACEfieldRef: metadata.namespace定位 ConfigMap/Endpoints
    PATRONI_KUBERNETES_BYPASS_API_SERVICE'true'绕过 kubernetes Service,直连 API(需要 ClusterRole 权限,见下文)
    PATRONI_KUBERNETES_LABELS{application: ..., cluster-name: ...}集群成员发现标签
    PATRONI_SUPERUSER_USERNAME/PASSWORD模板参数 + Secret超级用户凭据
    PATRONI_REPLICATION_USERNAME/PASSWORD模板参数 + Secret流复制凭据
    PATRONI_SCOPE模板参数PATRONI_CLUSTER_NAME集群作用域名
    PATRONI_NAMEfieldRef: metadata.name成员名(即 Pod 名)
    PATRONI_POSTGRESQL_DATA_DIR/home/postgres/pgdata/pgroot/data数据目录
    PATRONI_POSTGRESQL_LISTEN0.0.0.0:5432PostgreSQL 监听地址
    PATRONI_RESTAPI_LISTEN0.0.0.0:8008Patroni REST API 监听地址
  • 就绪探针:对 REST API/readiness端点做 HTTP GET(端口 8008),initialDelaySeconds: 3、periodSeconds: 10、failureThreshold: 3,确保只有健康的 Patroni 成员才对外服务。

  • 持久化:通过volumeClaimTemplates为每个副本申请 PVC(默认5Gi,ReadWriteOnce)。

4. RBAC:最小权限模型

模板为 ServiceAccount 创建Role/RoleBinding,授权范围严格限定在 ConfigMap、Endpoints、Pods 三类资源上(get/list/watch/patch/update/create/delete,其中delete仅用于patronictl remove)。此外,由于设置了PATRONI_KUBERNETES_BYPASS_API_SERVICE=true,还需 ClusterRolepatroni-k8s-ep-access(仅允许get名为kubernetes的 Endpoints)及其 ClusterRoleBinding——模板注释明确说明:该特权仅当部署在非 default 命名空间且启用 bypass 时必需。

5. 模板参数一览

模板末尾的parameters段定义了全部可定制项,默认值如下:

参数默认值说明
APPLICATION_NAMEpatroni-persistent所有资源的应用标签
PATRONI_CLUSTER_NAMEpatroni-persistent集群名(Scope)
PATRONI_PRIMARY_SERVICE_NAMEpatroni-persistent-primary主库 Service 名
PATRONI_REPLICA_SERVICE_NAMEpatroni-persistent-replica备库 Service 名
MEMORY_LIMIT512Mi容器内存上限
NAMESPACEopenshiftImageStream 所在命名空间
PATRONI_SUPERUSER_USERNAMEpostgres超级用户
PATRONI_SUPERUSER_PASSWORDpostgres超级用户密码(生产环境务必修改)
PATRONI_REPLICATION_USERNAMEpostgres复制用户
PATRONI_REPLICATION_PASSWORDpostgres复制密码(生产环境务必修改)
SERVICE_ACCOUNTpatroni-persistentPod 与 RoleBinding 使用的 SA
PVC_SIZE5Gi持久卷大小(Persistent 模板)

四、Jenkins 管道:自动对集群执行 pgbench 压测

test/README.md 的核心内容是一句话,但配套的 test/Jenkinsfile 提供了完整的声明式管道实现,原文明确说明:该管道会为 pgbench Pod 创建一个独立的 DeploymentConfig,并对 Patroni 集群执行测试;这是一个示例,应根据自身环境定制。

1. 管道全貌

Jenkinsfile 定义了三个阶段:

pipeline { agent any stages { stage ('Deploy test pod') { ... } // 部署 pgbench 压测 Pod stage ('Run benchmark Test') { ... } // 在 Pod 内执行 test.sh stage ('Clean up pgtest pod') { ... } // 清理压测资源 } }

2. 阶段一:Deploy test pod(条件化部署)

先检查当前项目中是否已存在名为pgbench的 DeploymentConfig,只有不存在时才执行部署,避免重复创建:

openshift.withCluster() { openshift.withProject() { return !openshift.selector( "dc", "pgbench" ).exists() } }

随后通过openshift.newApp从外部镜像创建应用,关键参数如下:

def pgbench = openshift.newApp( "https://github.com/stewartshea/docker-pgbench/", "--name=pgbench", "-e PGPASSWORD=postgres", "-e PGUSER=postgres", "-e PGHOST=patroni-persistent-primary", "-e PGDATABASE=postgres", "-e TEST_CLIENT_COUNT=20", "-e TEST_DURATION=120" )

参数含义:

参数值说明
PGHOSTpatroni-persistent-primary指向模板创建的Primary Service 名,压测流量永远打到当前主库
PGUSER/PGPASSWORDpostgres/postgres与模板默认超级用户凭据一致,改动模板密码时需同步修改
TEST_CLIENT_COUNT20并发客户端数
TEST_DURATION120压测时长(秒)

创建后使用timeout(5)包裹pgbenchdc.rollout().status(),等待 DeploymentConfig 滚动完成(最多等待 5 分钟)。

定制提示:PGHOST必须与你在 template_patroni_persistent.yaml 中设置的PATRONI_PRIMARY_SERVICE_NAME保持一致;TEST_CLIENT_COUNT、TEST_DURATION应依据集群规格调整。

3. 阶段二:Run benchmark Test

通过oc exec进入运行中的 pgbench Pod 并执行其内置测试脚本:

oc exec $(oc get pods -l app=pgbench | grep Running | awk '{print $1}') ./test.sh

先按app=pgbench标签筛选 Pod,再用grep Running+awk提取处于 Running 状态的 Pod 名,最后在容器内执行./test.sh发起压测。该 Pod 正是阶段一部署的 docker-pgbench 镜像中自带的测试入口。

4. 阶段三:Clean up pgtest pod

压测结束后按标签清理全部相关资源:

oc delete all -l app=pgbench

oc delete all会同时删除该标签下的 DeploymentConfig、Pod、Service 等 OpenShift 资源,保证测试环境不残留资源占用。

五、底层原理:Kubernetes DCS 如何支撑这套部署

理解 OpenShift 模板背后的协调逻辑,可以更自信地定制参数。Patroni 的 Kubernetes 实现位于 patroni/dcs/kubernetes.py,核心机制包括:

  • ConfigMap 即 DCS 存储:集群配置、leader 租约均以 ConfigMap annotations 形式存储,通过_patch_or_create、_update_leader等方法写入,并由watch方法(leader_version、timeout 机制)监听变更,实现故障快速感知;
  • 角色标签驱动 Service:正如前文所述,leader_label_value/follower_label_value默认分别为primary/replica,Patroni 持续将当前角色写入 Pod 标签,使*-primary、*-replicaService 的 selector 始终精确命中对应成员——这也是 Jenkins 压测直接连patroni-persistent-primary即可稳定打到主库的原理;
  • in-cluster 认证:K8sConfig从/var/run/secrets/kubernetes.io/serviceaccount/token与ca.crt读取 ServiceAccount 凭据(见load_incluster_config),因此模板中为 ServiceAccount 授予的最小 RBAC 权限就是 Patroni 全部所需。

六、部署与定制的关键注意事项

  1. 镜像构建上下文:oc new-build ... --context-dir=kubernetes必须指向kubernetes目录,因为 OpenShift 版 Dockerfile 与 entrypoint 都位于该目录;
  2. Ephemeral 仅限测试:无 PVC 的模板在 Pod 重建后数据全部丢失,切勿用于生产;
  3. 凭据必须修改:模板默认超级用户与复制用户密码均为postgres,实例化时应通过参数覆盖,并依赖 Secret 机制传递,避免明文暴露;
  4. 服务名耦合:Jenkins 管道中的PGHOST、客户端使用的*-primary/*-replica服务名,都直接绑定模板参数PATRONI_PRIMARY_SERVICE_NAME等,改名时需同步更新消费方;
  5. RBAC 按需裁剪:patroni-k8s-ep-accessClusterRole 仅在使用PATRONI_KUBERNETES_BYPASS_API_SERVICE=true且非 default 命名空间时需要,可按注释说明评估是否保留;
  6. Jenkins 管道是示例:test/Jenkinsfile中 pgbench 镜像、测试时长、并发数、凭据均为示例值,原文明确要求按自身环境定制后再接入 CI。

通过上述模板化部署与 Jenkins 压测管道,你可以在 OpenShift 上快速获得一套可验证、可伸缩的 Patroni 高可用 PostgreSQL 集群交付方案,并把基准测试固化为持续集成的一部分。

  • 数据库
  • 高可用
  • 集群管理
  • 运维
  • 后端

【免费下载链接】patroni

A template for PostgreSQL High Availability with Etcd, Consul, ZooKeeper, or Kubernetes

项目地址:https://gitcode.com/gh_mirrors/pa/patroni
点击查看免费下载

相关推荐

上一篇:riscv-gnu-toolchain核心组件解析:GCC、Binutils与Newlib深度剖析
下一篇:Qwen3.6-35B-A3B-Uncensored-Genesis-Hermes-V6-GGUF多语言能力测试:201种语言表现深度测评

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询