用 CloudNativePG 为 Kubernetes 上的 OpenBao 构建高可用存储

2026-09-16 29 预计阅读时间: 1 分钟
来源: cncf.io AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

在 Kubernetes 中运行密钥管理服务,真正棘手的并不是启动一个 Pod,而是保证状态数据能够持久化、故障后自动恢复,并且不会被某一家云厂商的托管服务绑定。OpenBao 与 CloudNativePG 的组合提供了一条开源路径:OpenBao 负责密钥、策略和动态凭据,CloudNativePG 负责 PostgreSQL 的复制、主实例切换与声明式运维。

但要先划清边界:PostgreSQL 保存的是 OpenBao 的加密存储数据,它不能代替初始化、解封、TLS、数据库备份和灾难恢复方案。

两套控制平面,各自处理擅长的问题

OpenBao 是 HashiCorp Vault 的开源分支,由 Linux Foundation 体系下的项目治理。使用 PostgreSQL 存储后,OpenBao 实例本身可以保持相对无状态,多个副本通过数据库共享持久化数据和高可用锁。

CloudNativePG 则把 PostgreSQL 集群建模为 Kubernetes 自定义资源。数据库主实例异常时,Operator 可以从健康副本中选出新的主实例,并让稳定的读写 Service 指向它。OpenBao 只需要连接这个 Service,而不必感知数据库 Pod 名称变化。

这种架构中至少有三类状态需要分别保护:

  • OpenBao 数据:保存在 PostgreSQL 中,需要连续归档、备份和恢复演练。
  • 解封材料:应交给独立的 KMS、HSM 或受控的人工流程,不能只存在同一数据库中。
  • Kubernetes 配置:包括 CRD、工作负载、NetworkPolicy 和 Secret 引用,应进入版本管理,但真实凭据不能提交到仓库。

先创建 PostgreSQL 集群

下面是一个可以改造的最小示例。假设 CloudNativePG Operator 已经安装,命名空间为 security。生产环境需要根据存储类、可用区和备份目标调整配置。

apiVersion: v1
kind: Namespace
metadata:
  name: security
---
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: bao-db
  namespace: security
spec:
  instances: 3

  bootstrap:
    initdb:
      database: openbao
      owner: openbao

  storage:
    size: 10Gi

  resources:
    requests:
      cpu: 250m
      memory: 512Mi
    limits:
      memory: 1Gi

应用并等待集群就绪:

kubectl apply -f bao-db.yaml
kubectl wait \
  --namespace security \
  --for=condition=Ready cluster/bao-db \
  --timeout=10m

kubectl get cluster,pods,svc,secrets -n security

初始化完成后,CloudNativePG 通常会生成名为 bao-db-app 的应用 Secret,并创建 bao-db-rw 读写 Service。具体字段会受到 Operator 版本和集群配置影响,部署前应检查:

kubectl get secret bao-db-app -n security \
  -o jsonpath='{.data}'

echo
kubectl get svc bao-db-rw -n security

把数据库连接交给 OpenBao

OpenBao 的 PostgreSQL 存储配置核心如下。ha_enabled 让多个 OpenBao 副本通过 PostgreSQL 协调主备状态。

ui = true

listener "tcp" {
  address     = "0.0.0.0:8200"
  tls_disable = 1
}

storage "postgresql" {
  connection_url = "postgresql://openbao:CHANGE_ME@bao-db-rw.security.svc:5432/openbao"
  table          = "openbao_kv_store"
  ha_enabled     = "true"
  ha_table       = "openbao_ha_locks"
}

这里的 tls_disable = 1 只适合用于展示连接关系。生产环境应在 OpenBao listener 上启用 TLS,或者明确由受控的 Service Mesh、入口网关终止 TLS,并用 NetworkPolicy 限制访问范围。

可以从 CloudNativePG 生成的 Secret 构建 OpenBao 配置 Secret,避免把数据库密码写进 Git。下面假设 bao-db-app 提供 uri 字段;运行前应先用上一节的命令确认字段存在。

PG_URI="$(kubectl get secret bao-db-app \
  --namespace security \
  --output jsonpath='{.data.uri}' | base64 --decode)"

kubectl create secret generic openbao-config \
  --namespace security \
  --from-literal=bao.hcl="$(cat <<EOF
ui = true

listener \"tcp\" {
  address     = \"0.0.0.0:8200\"
  tls_disable = 1
}

storage \"postgresql\" {
  connection_url = \"${PG_URI}\"
  table          = \"openbao_kv_store\"
  ha_enabled     = \"true\"
  ha_table       = \"openbao_ha_locks\"
}
EOF
)" \
  --dry-run=client \
  --output yaml | kubectl apply -f -

unset PG_URI

随后可在 OpenBao Deployment、StatefulSet 或 Helm values 中把该 Secret 挂载到 /openbao/config/bao.hcl,并以类似下面的参数启动:

containers:
  - name: openbao
    image: openbao/openbao:<PINNED_VERSION>
    args:
      - server
      - -config=/openbao/config/bao.hcl
    ports:
      - name: http
        containerPort: 8200
    volumeMounts:
      - name: config
        mountPath: /openbao/config
        readOnly: true
volumes:
  - name: config
    secret:
      secretName: openbao-config

<PINNED_VERSION> 替换为团队已经验证并锁定的 OpenBao 镜像版本。不要在生产清单中使用 latest

这种生成方式仍会让完整连接串进入 Kubernetes Secret。Kubernetes Secret 只是 Base64 编码,并不天然等于静态加密;集群应启用 API Server 的 Secret 静态加密,并严格限制 RBAC。更成熟的部署可以使用外部 Secret 管理器、CSI 驱动或启动时模板渲染,减少凭据暴露面。

上线前验证故障路径

部署三个 OpenBao 副本并不自动等于高可用。至少要验证以下场景:

# 查看数据库主备状态
kubectl get pods -n security \
  -l cnpg.io/cluster=bao-db \
  -L role

# 观察 OpenBao 健康状态;sealedcode 仅用于探针识别,不能代替告警
kubectl port-forward -n security svc/openbao 8200:8200
curl -sS 'http://127.0.0.1:8200/v1/sys/health?standbyok=true&sealedcode=204'

测试环境中还应主动删除当前 PostgreSQL 主 Pod,确认 CloudNativePG 能完成切换,OpenBao 在短暂数据库错误后能够恢复请求。随后执行一次真实的备份恢复,把恢复出的数据库接入隔离的 OpenBao 环境,验证密钥和策略可以读取。

采用时的检查清单

这套方案减少了对专有密钥存储和托管数据库的依赖,但代价是团队需要同时承担 OpenBao 与 PostgreSQL 的运行责任。上线前应确认:

  • OpenBao 使用固定版本,并有明确的升级、回滚和兼容性测试流程。
  • PostgreSQL 跨节点或可用区放置,Pod 反亲和规则符合故障域设计。
  • 已配置数据库备份、WAL 归档、保留周期和异地副本。
  • OpenBao 的初始化与自动解封依赖独立信任根,不与 PostgreSQL 共命运。
  • OpenBao listener、数据库连接及备份链路均启用经过验证的 TLS。
  • NetworkPolicy 只允许 OpenBao 访问数据库读写 Service。
  • 监控覆盖复制延迟、主实例切换、连接池耗尽、密封状态和请求错误率。
  • 团队定期演练 PostgreSQL 主库故障、OpenBao Pod 重建以及完整灾难恢复。

OpenBao 加 CloudNativePG 的价值不只是“都能跑在 Kubernetes 上”,而是把密钥服务和数据库都交给可审计、可声明、可迁移的开源组件。真正决定它能否进入生产环境的,则是解封、备份、TLS 与故障演练是否形成了一条完整的恢复链路。


相关推荐