Vault · 密钥管理

HashiCorp Vault 是整个集群的密钥真相来源。它以单副本 Deployment 跑在 workload: infra 节点上,使用 storage "file" 本地文件存储、Shamir 封印(seal),并通过 ExternalSecrets 把密钥分发给各个应用。集群里所有 Secret 都不进 git,统一从 Vault 拉取。

概览

  • Name
    镜像
    Description
    hashicorp/vault:2.0.2
  • Name
    namespace
    Description
    vault
  • Name
    副本数
    Description
    1(单副本,无 HA)
  • Name
    存储后端
    Description
    storage "file",路径 /vault/data,由名为 vault-data 的 PVC 提供(nfs-client storageClass,10Gi,ReadWriteMany)
  • Name
    封印方式
    Description
    Shamir seal,需 unseal key 解封
  • Name
    调度
    Description
    nodeSelector: workload=infra
  • Name
    端口
    Description
    8200(http / API + UI)、8201(internal / 集群通信)
  • Name
    VPA
    Description
    有,vault-vpa,updateMode Recreate,CPU 50m–1、内存 64Mi–1Gi
  • Name
    HPA / PDB
    Description
    无(单副本不适用)

资源 requests/limits 为 CPU 250m/500m、内存 512Mi/1Gi。配置(vault.hcl)从 vault-config ConfigMap 挂到 /vault/config,开启了 ui = true 和监听器 telemetry 的无认证 metrics 访问。

部署形态

Deployment 以 server 参数启动,容器内通过环境变量定位自己:

VAULT_ADDR     = http://127.0.0.1:8200                       # postStart hook 本地访问
VAULT_API_ADDR = http://vault.vault.svc.cluster.local:8200   # 对外宣告地址

监听器配置为 tls_disable = 1(TLS 在 ingress 层终止,集群内走明文)。Service 是 ClusterIP 类型,暴露 8200/8201 两个端口。Pod 挂载三个卷:vault-config ConfigMap(配置)、vault-data PVC(数据)、vault-unseal-key Secret(解封密钥,只读)。

自动解封机制

Vault 经过 Shamir 封印后,每次重启都需要重新 unseal 才能服务请求。这套部署没有 init 容器,而是用 Deployment 的 postStart 生命周期钩子在容器启动后自动解封:

sleep 10
UNSEAL_KEY=$(cat /vault/unseal/key)
vault operator unseal $UNSEAL_KEY || true

unseal key 来自挂载在 /vault/unsealvault-unseal-key Secret,该 Secret 由 ExternalSecret vault-unseal-key 从 Vault 自身的路径 vault/vault-unseal-key 拉取(refreshInterval 1h)。这样重启的 Pod 会自动解封,无需人工介入。

作为 External Secrets 后端

Vault 是 External Secrets 的后端,通过 ClusterSecretStore vault-backend 接入:

provider:
  vault:
    server: 'http://vault.vault.svc.cluster.local:8200'
    path: 'secret' # KV v2 mount
    version: 'v2'

约定上每个应用的密钥放在 yldm/production/<app>,外加共享路径如 yldm/database。注意 ESO 的 remoteRef.key 省略 secret/ mount 前缀(store 会自动加上),而 vault CLI 不省略 —— 写入时要用 vault kv put secret/...

另外,集群里 Vault 的 Kubernetes auth 方法依赖一条 ClusterRoleBinding vault-auth-delegator:它把 system:auth-delegator 授给 vault/default ServiceAccount,让 Vault 能用自己的 Pod SA 调 TokenReview API 校验登录 JWT。缺这条绑定时所有 Kubernetes-auth 登录都会 403(曾经全集群范围内中断)—— 不要把它当孤儿资源删掉。

升级 2.0.0+ 镜像的坑

因此一次纯镜像 tag 的 bump(argocd-image-updater / renovate 产出的那种)并不够:必须同时在 configmaps-vault-config.yamlvault.hcl)里设置 disable_mlock = true。节点上已禁用 swap,所以这样做是安全的。这个坑在 1.21 → 2.0.2 升级时踩过:镜像在 #983,配置修复在 #1019。Deployment 里保留的 IPC_LOCK capability 现在已是 no-op,但留着无害。

当前配置已设好 disable_mlock = true,并保留 telemetry 段(Prometheus retention 30s,disable_hostname)。

访问入口与监控

Vault UI / API 通过 Traefik ingress 暴露在 vault.yldm.techingressClassName: traefik,经 Cloudflare Tunnel,app-root 重定向到 /ui/,后端指向 8200 端口)。

监控方面有 ServiceMonitor vault,每 30s 从 http 端口抓 /v1/sys/metrics?format=prometheus。配置里 listener 的 telemetry 开了 unauthenticated_metrics_access = true,所以 Prometheus 无需认证即可抓取。

相关页面:External Secrets · 集群架构 · GitOps 工作流

评论