- 数据库
- 高可用
- 集群管理
- 运维
- 后端
【免费下载链接】patroni
A template for PostgreSQL High Availability with Etcd, Consul, ZooKeeper, or Kubernetes
本指南围绕 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-test2. 构建镜像
在 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 模板包含:
initContainer
fix-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_NAME | patroni-persistent | 所有资源的应用标签 |
PATRONI_CLUSTER_NAME | patroni-persistent | 集群名(Scope) |
PATRONI_PRIMARY_SERVICE_NAME | patroni-persistent-primary | 主库 Service 名 |
PATRONI_REPLICA_SERVICE_NAME | patroni-persistent-replica | 备库 Service 名 |
MEMORY_LIMIT | 512Mi | 容器内存上限 |
NAMESPACE | openshift | ImageStream 所在命名空间 |
PATRONI_SUPERUSER_USERNAME | postgres | 超级用户 |
PATRONI_SUPERUSER_PASSWORD | postgres | 超级用户密码(生产环境务必修改) |
PATRONI_REPLICATION_USERNAME | postgres | 复制用户 |
PATRONI_REPLICATION_PASSWORD | postgres | 复制密码(生产环境务必修改) |
SERVICE_ACCOUNT | patroni-persistent | Pod 与 RoleBinding 使用的 SA |
PVC_SIZE | 5Gi | 持久卷大小(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" )参数含义:
| 参数 | 值 | 说明 |
|---|---|---|
PGHOST | patroni-persistent-primary | 指向模板创建的Primary Service 名,压测流量永远打到当前主库 |
PGUSER/PGPASSWORD | postgres/postgres | 与模板默认超级用户凭据一致,改动模板密码时需同步修改 |
TEST_CLIENT_COUNT | 20 | 并发客户端数 |
TEST_DURATION | 120 | 压测时长(秒) |
创建后使用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=pgbenchoc 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 全部所需。
六、部署与定制的关键注意事项
- 镜像构建上下文:
oc new-build ... --context-dir=kubernetes必须指向kubernetes目录,因为 OpenShift 版 Dockerfile 与 entrypoint 都位于该目录; - Ephemeral 仅限测试:无 PVC 的模板在 Pod 重建后数据全部丢失,切勿用于生产;
- 凭据必须修改:模板默认超级用户与复制用户密码均为
postgres,实例化时应通过参数覆盖,并依赖 Secret 机制传递,避免明文暴露; - 服务名耦合:Jenkins 管道中的
PGHOST、客户端使用的*-primary/*-replica服务名,都直接绑定模板参数PATRONI_PRIMARY_SERVICE_NAME等,改名时需同步更新消费方; - RBAC 按需裁剪:
patroni-k8s-ep-accessClusterRole 仅在使用PATRONI_KUBERNETES_BYPASS_API_SERVICE=true且非 default 命名空间时需要,可按注释说明评估是否保留; - Jenkins 管道是示例:
test/Jenkinsfile中 pgbench 镜像、测试时长、并发数、凭据均为示例值,原文明确要求按自身环境定制后再接入 CI。
通过上述模板化部署与 Jenkins 压测管道,你可以在 OpenShift 上快速获得一套可验证、可伸缩的 Patroni 高可用 PostgreSQL 集群交付方案,并把基准测试固化为持续集成的一部分。
- 数据库
- 高可用
- 集群管理
- 运维
- 后端
【免费下载链接】patroni
A template for PostgreSQL High Availability with Etcd, Consul, ZooKeeper, or Kubernetes
相关推荐
在 OpenShift 上部署 Patroni PostgreSQL 高可用集群:基于模板的动态 UID/GID 适配实战
在 OpenShift 上部署 Patroni PostgreSQL 高可用集群:基于模板的动态 UID/GID 适配实战 本篇技术指南围绕 Patroni 项
数据库高可用集群管理运维后端Patroni高可用性部署完整指南:从零构建PostgreSQL集群
Patroni高可用性部署完整指南:从零构建PostgreSQL集群 Patroni作为PostgreSQL高可用性解决方案的领导者,能够帮助您快速构建稳定可靠
数据库高可用集群管理运维后端免费 API 认证实战:无需认证、apiKey 与 OAuth 的选型与落地
免费 API 认证实战:无需认证、apiKey 与 OAuth 的选型与落地 第一次给项目接免费 API,多半会被认证这一关卡住:有的接口拿来就能调,有的要先申
文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考