在 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 与故障演练是否形成了一条完整的恢复链路。