4. 环境变量注入Secret的实践:yaml编写、主容器读取Secret、Sidecar容器如何共享环境变量
环境变量注入,是Sidecar模式里最直接的Secret传递方式。说白了,就是让两个容器共用同一个Pod的环境变量池。
我记得刚接触Kuberentes时,觉得环境变量很简单——不就是几个key-value吗?后来踩了坑才发现,这里头门道不少。尤其是主容器和Sidecar之间的变量共享,稍不注意,就会出现“Sidecar读不到主容器的Secret”这种诡异问题。
4.1 标准YAML:定义Secret与Pod
先看一个基础例子。我们定义一个Opaque类型的Secret,然后在Pod模板里把它挂给两个容器。
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
DB_USER: "admin"
DB_PASS: "s3cret!2024"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: shared-env-app
spec:
replicas: 3
selector:
matchLabels:
app: shared-env-app
template:
metadata:
labels:
app: shared-env-app
spec:
containers:
- name: main-app
image: my-app:1.0
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_USER
- name: DB_PASS
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASS
- name: sidecar-logger
image: busybox:1.36
command: ["sh", "-c", "while true; do echo 'Secret loaded'; sleep 10; done"]
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_USER
- name: DB_PASS
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASS
先在
stringData里写明文,然后用kubectl get secret -o yaml确认是否已被Base64编码。调试阶段省一次decode的麻烦。
4.2 主容器读取Secret:两种方式
主容器读Secret,最常见的有两种:
- 直接来自SecretRef——像上面那样,在
env里逐字段指定。每个变量都要写一次valueFrom,稍微有点烦人。 - 使用envFrom——把整个Secret一股脑全倒进去。省事,但变量名必须严格匹配代码预期。
我个人比较倾向于envFrom,只要Secret的key和你代码里的变量名一致,一行就搞定所有。不过有个坑——envFrom不会告诉你哪些key被忽略了,如果你Secret里有个多余的key,不会报错,也不会被注入。这会导致“变量没定义”的隐式bug。
实战建议:
用envFrom时,配合fieldRef在启动命令里打一条日志,验证所有预期变量都已渲染。比如:
command: ["sh", "-c", "echo 'DB_USER is set'; env | grep DB_"]
4.3 Sidecar容器如何共享环境变量
你想想看,如果主容器和Sidecar各写各的env,就相当于复制粘贴。万一Secret更新了key,两边都要改,容易遗漏。
真正意义上“共享”,不是指同一个内存空间,而是指同一个Pod内的环境变量作为公共配置层。Sidecar想要共享主容器的Secret,有以下几种方案。
| 方案 | 实现方式 | 优点 | 缺点 |
|---|---|---|---|
| 方案A:各自声明SecretRef | 两个容器各自写env.valueFrom.secretKeyRef |
语法清晰,每个容器只读自己需要的key | 重复声明,维护成本高 |
方案B:使用envFrom共享整个Secret |
两容器都用envFrom指向同一个Secret |
一次性注入所有key,代码简洁 | 变量名必须完全匹配,无法选择性注入 |
| 方案C:共享卷+入口脚本 | Secret被挂载到卷,入口脚本通过source或export变量 |
绕过环境变量长度限制 | 增加了启动延迟和脚本Bug风险 |
我在项目中遇到过方案C。当初一个监控代理需要读取主容器里的,但密钥特别长(超过了环境变量1MiB的限制),只好用卷挂载。然后写了个entrypoint.sh,从文件里读出来export成环境变量。这样做虽然可行,但多了一层调试负担。偶尔脚本更新没同步,Sidecar就报“变量未定义”。
一旦你决定用方案C,我建议在Sidecar的Init Container里先把文件内容校验一遍再export,而不是在Sidecar主进程中直接干。
环境变量注入有一个“默认顺序”——先注入
env里的变量,后注入envFrom里的变量。如果你在env里定义的变量名和envFrom里的key冲突,env会覆盖envFrom。这会导致Sidecar看到的是一个被覆盖的值,而不是Secret里的原始值。排查起来相当头大。
4.4 核心逻辑流程图
下面这张图展示了“Secret定义 → Pod启动 → 环境变量注入 → 主容器与Sidecar共享”的完整路径。
一个实用的Prestop处理
如果主容器重启,Sidecar不能一直持有旧的Secret值。我通常会在Sidecar的Prestop hook里加一条unset DB_PASS,确保变量不会残留在进程空间。下次启动时,再通过envFrom重新注入新的值。
4.5 最佳实践:安全与维护
环境变量注入虽然简单,但有几个原则我一直在遵守:
- 最小权限原则:Sidecar只需要读取权限的Secret,不要给它注入写权限的凭证。
- 变量名前缀:主容器和Sidecar容易重名,我习惯用
SIDECAR_前缀区分。比如SIDECAR_DB_USER,这样一起env也不会冲突。 - 不推荐用
envFrom注入敏感Secret到Sidecar:Sidecar如果是日志代理或监控组件,它的日志可能被转储到文件或远端,一旦日志打印env,Secret就暴露了。应该只注入最小需要的key。
Kubernetes不会对
envFrom注入的变量进行校验。如果你的Secret里某个key包含非法字符(比如DB-PASS带了个短横线),envFrom会静默跳过它,不会报错。调试时一定先用kubectl exec进Pod看环境变量列表。
好了,环境变量注入Secret的核心内容就这些。从定义Secret,到两种注入方式,再到Sidecar如何共享——每一步都有对应的YAML和注意事项。你只需按上面的模板做一次,就能在真实项目中用起来。如果遇到注入不生效,记住先查Pod的事件,再用env命令对比容器内的变量表。