Secret 是一种包含少量敏感信息例如密码、令牌或密钥的对象。 这样的信息可能会被放在 Pod 规约中或者镜像中。 用户可以创建 Secret,同时系统也创建了一些 Secret。
一个 Secret 可以包含 Pod 访问数据库所需的用户凭证。 例如,由用户名和密码组成的数据库连接字符串。 你可以在本地计算机上,将用户名存储在文件 ./username.txt
中,将密码存储在文件 ./password.txt
中。
echo -n 'admin' > ./username.txt
echo -n '1f2d1e2e67df' > ./password.txt
在这些命令中,-n
标志确保生成的文件在文本末尾不包含额外的换行符。 这一点很重要,因为当 kubectl 读取文件并将内容编码为 base64 字符串时,多余的换行符也会被编码。
kubectl create secret
命令将这些文件打包成一个 Secret 并在 API 服务器上创建对象。
$ kubectl create secret generic db-user-pass \
--from-file=./username.txt \
--from-file=./password.txt
输出类似于:
secret/db-user-pass created
默认密钥名称是文件名。 可以使用 --from-file=[key=]source
来设置密钥名称。
例如:
$ kubectl create secret generic db-user-pass \
--from-file=username=./username.txt \
--from-file=password=./password.txt
不需要对文件中包含的密码字符串中的特殊字符进行转义。
还可以使用 --from-literal=
需要注意,特殊字符(例如:$,\,*,= 和 !)由你的 shell 解释执行,而且需要转义。
在大多数 shell 中,转义密码最简便的方法是用单引号括起来。 比如,如果你的密码是 S!B\*d$zDsb=
, 可以像下面一样执行命令:
$ kubectl create secret generic db-user-pass \
--from-literal=username=devuser \
--from-literal=password='S!B\*d$zDsb='
检查 secret 是否已创建:
$ kubectl get secrets
输出类似于:
NAME TYPE DATA AGE
db-user-pass Opaque 2 51s
你可以查看 Secret 的描述:
$ kubectl describe secrets/db-user-pass
输出类似于:
Name: db-user-pass
Namespace: default
Labels: >
Annotations: >
Type: Opaque
Data
====
password: 12 bytes
username: 5 bytes
kubectl get
和 kubectl describe
命令默认不显示 Secret 的内容。 这是为了防止 Secret 被意外暴露或存储在终端日志中。
要查看创建的 Secret 的内容,运行以下命令:
$ kubectl get secret db-user-pass -o jsonpath='{.data}'
这里会输出你的账号和密码,我就不贴了。
现在你可以解码 password 的数据:
# 这是一个用于文档说明的示例。
# 如果你这样做,数据 'MWYyZDFlMmU2N2Rm' 可以存储在你的 shell 历史中。
# 可以进入你电脑的人可以找到那个记住的命令并可以在你不知情的情况下 base-64 解码这个 Secret。
# 通常最好将这些步骤结合起来,如页面后面所示。
echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
输出类似于:
1f2d1e2e67df
为了避免在 shell 历史记录中存储 Secret 的编码值,可以执行如下命令:
$ kubectl get secret db-user-pass -o jsonpath='{.data.password}' | base64 --decode
输出应与上述类似。
删除创建的 Secret:
$ kubectl delete secret db-user-pass
你可以先用 JSON 或 YAML 格式在一个清单文件中定义 Secret 对象,然后创建该对象。 Secret 资源包含 2 个键值对:data 和 stringData。 data 字段用来存储 base64 编码的任意数据。 提供 stringData 字段是为了方便,它允许 Secret 使用未编码的字符串。 data 和 stringData 的键必须由字母、数字、-、_ 或 . 组成。
我来使用 data 字段在 Secret 中存储两个字符串:
echo -n 'admin' | base64
echo -n '1f2d1e2e67df' | base64
输出类似于:
YWRtaW4=
MWYyZDFlMmU2N2Rm
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
Secret 对象的名称必须是有效的 DNS 子域名。
kubectl apply
创建 Secret:$ kubectl apply -f ./secret.yaml
输出类似于:
secret/mysecret created
对于某些场景,你可能希望使用 stringData 字段。 这个字段可以将一个非 base64 编码的字符串直接放入 Secret 中, 当创建或更新该 Secret 时,此字段将被编码。
上述用例的实际场景可能是这样:当你部署应用时,使用 Secret 存储配置文件, 你希望在部署过程中,填入部分内容到该配置文件。
如果你的应用程序使用以下配置文件:
apiUrl: "https://my.api.com/api/v1"
username: ""
password: ""
你可以使用以下定义将其存储在 Secret 中:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
stringData:
config.yaml: |
apiUrl: "https://my.api.com/api/v1"
username:
password:
当你检索 Secret 数据时,此命令将返回编码的值,并不是你在 stringData 中提供的纯文本值。
运行以下命令:
$ kubectl get secret mysecret -o yaml
输出类似于:
apiVersion: v1
data:
config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IHt7dXNlcm5hbWV9fQpwYXNzd29yZDoge3twYXNzd29yZH19
kind: Secret
metadata:
creationTimestamp: 2018-11-15T20:40:59Z
name: mysecret
namespace: default
resourceVersion: "7225"
uid: c280ad2e-e916-11e8-98f2-025000000001
type:
如果在 data 和 stringData 中设置了同一个字段,则使用来自 stringData 中的值。
可以定义以下 Secret:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
stringData:
username: administrator
所创建的 Secret 对象如下:
apiVersion: v1
data:
username: YWRtaW5pc3RyYXRvcg==
kind: Secret
metadata:
creationTimestamp: 2018-11-15T20:46:46Z
name: mysecret
namespace: default
resourceVersion: "7579"
uid: 91460ecb-e917-11e8-98f2-025000000001
type: Opaque
YWRtaW5pc3RyYXRvcg== 解码成 administrator。
删除你创建的 Secret:
$ kubectl delete secret mysecret
从 kubernetes v1.14 开始,kubectl 支持使用 Kustomize 管理对象。 Kustomize 提供了资源生成器(Generators)来创建 Secret 和 ConfigMap。 Kustomize 生成器应该在某个目录的 kustomization.yaml 文件中指定。 生成 Secret 后,你可以使用 kubectl apply 在 API 服务器上创建该 Secret。
你可以在 kustomization.yaml
中定义 secreteGenerator 字段,并在定义中引用其它本地文件生成 Secret。 例如:下面的 kustomization 文件 引用了 ./username.txt
和 ./password.txt
文件:
secretGenerator:
- name: db-user-pass
files:
- username.txt
- password.txt
你也可以在 kustomization.yaml
文件中指定一些字面量定义 secretGenerator 字段。 例如:下面的 kustomization.yaml
文件中包含了 username 和 password 两个字面量:
secretGenerator:
- name: db-user-pass
literals:
- username=admin
- password=1f2d1e2e67df
你也可以使用 .env 文件在 kustomization.yaml 中定义 secretGenerator。 例如:下面的 kustomization.yaml 文件从 .env.secret 文件获取数据。
secretGenerator:
- name: db-user-pass
envs:
- .env.secret
注意,上面两种情况,你都不需要使用 base64 编码。
在包含 kustomization.yaml 文件的目录下使用 kubectl apply
命令创建 Secret。
$ kubectl apply -k .
输出类似于:
secret/db-user-pass-96mffmfh4k created
请注意,生成 Secret 时,Secret 的名称最终是由 name 字段和数据的哈希值拼接而成。 这将保证每次修改数据时生成一个新的 Secret。
你可以检查刚才创建的 Secret:
$ kubectl get secrets
输出类似于:
NAME TYPE DATA AGE
db-user-pass-96mffmfh4k Opaque 2 51s
你可以看到 Secret 的描述:
$ kubectl describe secrets/db-user-pass-96mffmfh4k
输出类似于:
Name: db-user-pass-96mffmfh4k
Namespace: default
Labels: >
Annotations: >
Type: Opaque
Data
====
password.txt: 12 bytes
username.txt: 5 bytes
kubectl get
和 kubectl describe
命令默认不显示 Secret 的内容。 这是为了防止 Secret 被意外暴露给旁观者或存储在终端日志中。
删除你创建的 Secret:
$ kubectl delete secret db-user-pass-96mffmfh4k
通过 API 创建的 Pod 时,不会检查应用的 secret 是否存在。一旦 Pod 被调度,kubelet 就会尝试获取该 secret 的值。如果获取不到该 secret,或者暂时无法与 API server 建立连接,kubelet 将会定期重试。Kubelet 将会报告关于 pod 的事件,并解释它无法启动的原因。一旦获取的 secret,kubelet将创建并装载一个包含它的卷。在安装所有pod的卷之前,都不会启动 pod 的容器。