Secret必须Base64编码,但应由kubectl自动生成(如--from-literal)避免手动编码出错;Go应用需用os.LookupEnv校验环境变量是否存在,挂载文件权限需通过fsGroup适配,且Secret更新后必须重启Pod才能生效。

Secret 必须用 base64 编码,但别手算
直接写明文到 data 字段会失败,Kubernetes 要求所有 data 值必须是 base64 编码字符串。但你不需要手动执行 echo -n "password" | base64 —— 容易漏掉 -n 导致换行符混入,解码后密码末尾多一个 \n,Go 应用连不上 MySQL。
正确做法是让 kubectl 自动生成:
kubectl create secret generic mysql-secret \ --from-literal=username=user \ --from-literal=password=secret123 \ --dry-run=client -o yaml > mysql-secret.yaml
这样生成的 YAML 中 data 是安全编码的,且字段名和值一一对应,不会因空格或特殊字符出错。
Go 应用里读 Secret 环境变量要加前缀校验
Deployment 中通过 env 注入 Secret 后,Go 代码不能直接信任 os.Getenv("MYSQL_PASSWORD") 的返回值。它可能是空字符串(Secret 挂载失败)、"null"(YAML 错误导致字段被解析为 null)或根本未设置(key 名拼错)。
建议在启动时做显式检查:
- 用
os.LookupEnv而非os.Getenv,能区分“未设置”和“空值” - 对每个必需字段(如
MYSQL_USER、MYSQL_PASSWORD、MYSQL_HOST)都加非空判断 - 启动失败时 panic 并打印明确错误,比如
"missing required env MYSQL_PASSWORD",而不是等到连接数据库时报"access denied"
Secret 挂载进容器后权限是 644,Go 程序得能读
默认情况下,Secret 以文件形式挂载到容器内(比如 /etc/secrets/mysql/),文件权限是 -rw-r--r--(644)。如果你的 Go 应用以非 root 用户运行(推荐做法),而该用户不属于文件所属组,就可能读不到。
两种解法:
- 在 Deployment 的
securityContext中指定runAsUser和fsGroup,让挂载点自动适配组权限 - 改用环境变量注入(即上文用
secretKeyRef的方式),绕过文件系统权限问题 - 避免把 Secret 挂成文件再用
ioutil.ReadFile读取——除非你明确需要该行为,否则纯属增加故障面
Secret 更新后 Go Pod 不会自动重载,必须重启
修改 Secret 再 kubectl apply,已运行的 Pod **不会**自动更新环境变量或挂载文件内容。这是 Kubernetes 的设计:Secret 是只读快照,挂载后生命周期绑定 Pod。
所以线上改密码后,必须触发滚动更新:
- 改完 Secret,再给 Deployment 加个无关 annotation,比如
kubectl patch deploy/go-app -p '{"spec":{"template":{"metadata":{"annotations":{"timestamp":"'$(date +%s)'}}}}}' - 或者直接删 Pod:
kubectl delete pod -l app=go-app,由 Deployment 重建 - 千万别指望 Go 应用自己轮询 /etc/secrets 下文件变化——既没必要,也容易引发并发读写问题
最常被忽略的一点:Secret 和 ConfigMap 共享同一套挂载机制,但 Secret 更敏感,更新路径更隐蔽。上线前务必验证新 Pod 的 env | grep MYSQL 输出是否符合预期,而不是只看 Pod Running 状态。











