介绍
应用程序通常需要一些不应直接放入容器镜像或明文清单(manifest)中的值,例如密码、令牌或数据库凭据。Kubernetes Secret 将这类敏感配置存储为独立的对象,并允许 Pod 在启动时使用它们。
在本实验中,你将创建一个 Secret,仔细检查其编码数据,将其连接到应用程序 Pod,更新 Secret,并重新创建 Pod 以便应用程序读取新值。本实验重点介绍 Secret 的核心工作流程。后续实验将练习更详细的环境变量注入和基于文件的配置挂载。
准备 Secret 练习命名空间
在此步骤中,你将准备一个用于 Secret 练习的干净命名空间。
从 /home/labex/project 开始。你将在此目录中编写一个 Pod 清单。
mkdir -p /home/labex/project
cd /home/labex/project
检查 Kubernetes API 服务器是否可达。如果虚拟机刚启动且节点尚未就绪,请等待几秒钟后再运行该命令。
kubectl get nodes --request-timeout=5s
预期输出包括:
NAME STATUS ROLES AGE VERSION
labex-v135 Ready control-plane ... v1.35...
在创建应用程序对象之前,请等待集群网络 Pod 就绪。
kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s
预期输出:
pod/calico-node-... condition met
创建一个名为 ckad-prep-secrets 的命名空间:
kubectl create namespace ckad-prep-secrets
将此命名空间设置为当前上下文的命名空间:
kubectl config set-context --current --namespace=ckad-prep-secrets
确认当前命名空间:
kubectl config view --minify --output 'jsonpath={..namespace}'; echo
预期输出:
ckad-prep-secrets
创建并检查 Secret
在此步骤中,你将创建一个存储小型应用程序数据库连接设置的 Secret。
kubectl create secret generic 命令用于创建一个不透明(Opaque)的 Secret。generic 意味着 Kubernetes 不会为这些键分配特殊的内置含义。每个 --from-literal 标志都会向 Secret 添加一个键值对。
kubectl create secret generic db-credentials \
--from-literal=DB_USER=orders-app \
--from-literal=DB_PASSWORD=dev-password-123 \
--from-literal=DB_NAME=orders
预期输出:
secret/db-credentials created
检查 Secret 摘要:
kubectl get secret db-credentials
预期输出:
NAME TYPE DATA AGE
db-credentials Opaque 3 ...
以 YAML 格式查看 Secret。Kubernetes 将 data 下的值存储为 base64 编码的字符串,因此你不应期望在此输出中看到原始密码。
kubectl get secret db-credentials -o yaml
预期输出包括:
data:
DB_NAME: b3JkZXJz
DB_PASSWORD: ...
DB_USER: b3JkZXJzLWFwcA==
type: Opaque
Base64 编码不是加密。任何能够读取 Secret 对象的人都可以解码其值。此处仅解码用户名,以便在不打印密码的情况下了解其表示方式。
kubectl get secret db-credentials -o 'jsonpath={.data.DB_USER}' | base64 --decode; echo
预期输出:
orders-app
通过 Secret 配置 Pod
在此步骤中,你将启动一个将 Secret 读取为环境变量的 Pod。
envFrom.secretRef 字段将 Secret 中的所有键导入到容器环境中。这类似于上一个实验中的 ConfigMap 模式,但值来自 Secret 对象。此 Pod 中的命令会打印用户名和数据库名称,然后仅确认密码已加载。它不会打印密码本身。
创建 secret-reader-pod.yaml:
cat > secret-reader-pod.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: secret-reader
labels:
app: secret-reader
spec:
terminationGracePeriodSeconds: 0
containers:
- name: reader
image: busybox:1.36
command:
- /bin/sh
- -c
- 'echo user=${DB_USER}; echo database=${DB_NAME}; if [ -n "${DB_PASSWORD}" ]; then echo password-loaded; fi; sleep 3600'
envFrom:
- secretRef:
name: db-credentials
EOF
应用 Pod 清单:
kubectl apply -f secret-reader-pod.yaml
预期输出:
pod/secret-reader created
等待 Pod 就绪:
kubectl wait --for=condition=Ready pod/secret-reader --timeout=120s
预期输出:
pod/secret-reader condition met
读取 Pod 日志。应用程序确认 Secret 值可用,但未显示密码值。
kubectl logs secret-reader -c reader
预期输出:
user=orders-app
database=orders
password-loaded
更新 Secret 并重新创建 Pod
在此步骤中,你将更新 Secret 并重新创建 Pod,以便容器接收新值。
容器环境是在容器启动时捕获的。如果 Secret 是作为环境变量使用的,更新 Secret 对象不会重写已运行容器内的环境。当应用程序必须读取更新后的 Secret 值时,请启动一个新的 Pod。
使用新的应用程序用户和密码修补(patch)Secret:
kubectl patch secret db-credentials --type merge --patch '{"stringData":{"DB_USER":"orders-worker","DB_PASSWORD":"rotated-password-456","DB_NAME":"orders"}}'
预期输出:
secret/db-credentials patched
通过仅解码 DB_USER 来确认更新后的用户名:
kubectl get secret db-credentials -o 'jsonpath={.data.DB_USER}' | base64 --decode; echo
预期输出:
orders-worker
删除旧的 Pod:
kubectl delete pod secret-reader
预期输出包括:
pod "secret-reader" deleted
从相同的清单重新创建 Pod 并等待其就绪:
kubectl apply -f secret-reader-pod.yaml
kubectl wait --for=condition=Ready pod/secret-reader --timeout=120s
预期输出:
pod/secret-reader created
pod/secret-reader condition met
再次读取日志。新的 Pod 现在可以看到来自 Secret 的更新后的用户,并且仍然避免打印原始密码。
kubectl logs secret-reader -c reader
预期输出:
user=orders-worker
database=orders
password-loaded
总结
你创建了一个 Kubernetes Secret,检查了其编码数据,解码了一个非敏感键以了解其表示方式,将 Secret 连接到应用程序 Pod,并在更新 Secret 后重新创建了 Pod。你还练习了一个处理敏感配置的重要习惯:验证应用程序是否已接收到密码,同时不在日志中打印密码值。


