3.1 Secret 简介
kubernetes 的 Secret 对象可以存储和管理敏感信息,比如 passwords、OAuth tokens、以及 ssh keys 等。把敏感信息存储到 Secret 对象中会比放到 Pod 的定义文件或是容器镜像中更加安全、灵活,使用 Secret 对象可以更好的控制数据的使用方式,并且减少意外泄露的风险。
不仅用户可以创建 Secret 对象,kubernetes 系统也会创建一些 Secret 对象。
Secret 被创建以后,可以使用 3 种方式使用它:
- 创建 Pod 时,通过为 Pod 指定 Service Account 自动使用该 Secret
- 通过挂载该 Secret 到 Pod 来使用它
- 在 Docker 镜像下载时使用,通过指定 Pod 的 spc.ImagePullSecrets 来引用它
在存储数据的时候,如何从 ConfigMap 和 Secret 这两者之间进行选择呢?
- 使用 ConfigMap 存储非敏感的配置信息
- 使用 Secret 存储敏感的配置信息
- 如果配置文件既包含敏感信息、又包含非敏感信息,依然选择 Secret 进行存储
Secret 和 ConfigMap 对比,既有相同点、也有不同点,列表如下:
相同点:
- 存储数据都属于 key-value 键值对形式
- 属于某个特定的 namespace
- 可以导出到环境变量
- 可以通过目录/文件形式挂载(支持挂载所有 key 和部分 key)
不同点:
- Secret 可以被 ServerAccount 关联使用
- Secret 可以存储 Register 的鉴权信息,用于 ImagePullSecret 参数中,用于拉取私有仓库的镜像
- Secret 支持 Base64 加密
- Secret 分为 Opaque、kubernetes.io/Service Account、kubernetes.io/dockerconfigjson 三种类型,ConfigMap 不区分类型
- Secret 文件存储在 tmpfs 文件系统中,Pod 删除后 Secret 文件也会对应被删除
3.2 默认令牌 Secret
Service Accounts 使用 API 证书自动创建并且绑定 Secret。
kubernetes 系统可以自动创建用于访问 API 证书的 Secret,并且可以自动绑定到 Pod 中使用这种类型的 Secret。可以根据需要禁用或覆盖重写这类 Secret,但是通常情况下如果考虑到安全性,更加推荐使用系统默认创建的 Secret。
在终端执行如下命令可以查看系统默认创建的 Secret:
$ kubectl get secrets
NAME TYPE DATA AGE
default-token-sths6 kubernetes.io/service-account-token 3 73d
deployment-controller-token-9qzdj kubernetes.io/service-account-token 3 73d
disruption-controller-token-hbc8l kubernetes.io/service-account-token 3 73d
endpoint-controller-token-qr2rf kubernetes.io/service-account-token 3 73d
expand-controller-token-mbmks kubernetes.io/service-account-token 3 73d
generic-garbage-collector-token-5drr7 kubernetes.io/service-account-token 3 73d
horizontal-pod-autoscaler-token-54xdg kubernetes.io/service-account-token 3 73d
job-controller-token-4tgns kubernetes.io/service-account-token 3 73d
kube-proxy-token-9hc24 kubernetes.io/service-account-token 3 73d
metrics-server-token-lj9th kubernetes.io/service-account-token 3 73d
namespace-controller-token-tng4h kubernetes.io/service-account-token 3 73d
nfs-provisioner-token-4wk59 kubernetes.io/service-account-token 3 73d
node-controller-token-x8b8s kubernetes.io/service-account-token 3 73d
persistent-volume-binder-token-whx8d kubernetes.io/service-account-token 3 73d
pod-garbage-collector-token-djjmp kubernetes.io/service-account-token 3 73d
如果想要查看 Secrets 详细信息,可以执行命令 kubectl describe secrets。
$ kubectl describe secrets default-token-sths6
Name: default-token-sths6
Namespace: kube-system
Labels: <none>
Annotations: kubernetes.io/service-account.name: default
kubernetes.io/service-account.uid: 11d565d8-3a67-4f5e-973b-c9b46ca0524f
Type: kubernetes.io/service-account-token
Data
====
token: eyJhbGciOiJSUzI1NiIsImtpZCI6InhNbEpVRDZOUDRzUGlLdndDdk9qWGlYNnplMHMwazVMQVhIdmxaTHAzajQifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkZWZhdWx0LXRva2VuLXN0aHM2Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImRlZmF1bHQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiIxMWQ1NjVkOC0zYTY3LTRmNWUtOTczYi1jOWI0NmNhMDUyNGYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06ZGVmYXVsdCJ9.XQ9_MEbfZroZeZPdxFrue8yAqz9L-Y7YSNC1tldf6iTALWHWjjtHgXVhCABhTUz-slykpN-BD8hCQAnmkxWyyUh6xOVEV4xAhbPA77D-o3R5uvsnGCrXID2Un17b7oWyMVKTmZdKE1OLz4Re5iCNLV5ElXqLFCfgNYZ7sHT9eIbT-YdsRJljJnjXLMHYjEdH6Oc1lSArfxm4sBB_HuXgbOql0DkVqj9cFBYSNJjoSYc9oSwkzdFTbKYiILcL2wMRyTLZvrV6rm2F_jnYPmtXFbXCYFkN72aEFAEx9OJPmWckY-iXu9BEytW_kA-QiZBeDTQorjQovfW2m4mLS3k3zw
ca.crt: 1025 bytes
namespace: 11 bytes
可以看到这个 Secret 包含 3 项配置信息,分别为:namespace、ca.crt、token,这是从 pod 内部安全访问 Kubernetes API 服务器的全部配置信息了。
新建 nginx-manual.yaml 文件,并向该文件中写入如下内容:
apiVersion: v1
kind: Pod
metadata:
name: nginx-manual
spec:
containers:
- image: nginx:1.9.1
name: nginx
ports:
- containerPort: 80
protocol: TCP
执行创建:
$ kubectl create -f nginx-manual.yaml
pod/nginx-manual created
执行命令 kubectl describe pod nginx-manual 查看详细信息,可以发现:
# 这里显示了 default-token-k75dn Secret 被挂载到了容器中的 /var/run/secrets/kubernetes.io/serviceaccount 目录
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-k75dn (ro)
查看容器该目录下的文件名是否符合:
$ kubectl exec nginx-manual ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt
namespace
token
需要注意的是:当 Pod 被 API Server 创建时,API Server 不会校验该 Pod 引用的 Secret 是否存在。一旦这个 Pod 被调度,则 kubelet 将试着获取 Secret 的值。如果 Secret 不存在或暂时无法连接到 API Secret,则 kubelet 会按照一定的时间间隔定期重试获取该 Secret,并发送一个 Event 来解释 Pod 没有启动的原因,一旦 Secret 被 Pod 获取,则 kubelet 将创建并挂载包含 Secret 的 Volume,只有所有 Volume 都挂载成功,Pod 中的 Container 才会被启动。
3.3 使用 kubectl 创建 Secret
本节内容将会介绍 3 种方式创建 Secret,分别为:使用 kubectl 创建 Secret、使用 YAML 文件手动创建 Secret、以及 使用生成器创建 Secret。
使用 kubectl 创建 Secret
有时一些 Pods 需要进入数据库,我们在本地机器上定义 username.txt 和 password.txt 文件存储 Pod 需要使用的用户名和密码。
$ echo -n 'admin' > username.txt
$ echo -n 'Az123456_' > password.txt
使用 kubectl create secret 命令以及参数 --from-file 指定使用这些文件调用 Api 服务器创建 Secret:
$ kubectl create secret generic db-user-pass --from-file=/root/learning/username.txt --from-file=/root/learning/password.txt
secret/db-user-pass created
查看刚刚创建的 Secret:
$ kubectl get secrets
NAME TYPE DATA AGE
db-user-pass Opaque 2 20s
# 使用 describe 默认不会展示 secrets 的内容
$ kubectl describe secrets db-user-pass
Name: db-user-pass
Namespace: user-test
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password.txt: 9 bytes
username.txt: 5 bytes
3.4 使用 YAML 文件手动创建 Secret
可以使用文件创建 Secret 对象,文件可以为 json 或是 yaml 数据格式,定义 Secret 时分别有两种字段:data 和 stringData。data 字段存储被 Base64 编码以后的数据,而 stringData 字段的使用会更加随意一些,可以存储数据的纯文本值。
比如,使用 data 字段在 Secret 中存储两个字符串,使用如下命令将它们使用 Base64 编码:
$ echo -n 'admin' | base64
YWRtaW4=
$ echo -n 'Az123456_' | base64
QXoxMjM0NTZf
新建 secret.yaml 文件,并写入如下内容:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: QXoxMjM0NTZf
执行创建:
$ kubectl create -f secret.yaml
secret/mysecret created
如果想要在配置的时候更加明确配置信息,这个时候就需要使用到 stringData 字段了,使用这个字段可以将未被 Base64 编码的数据直接配置到 YAML 文件中,当创建或是更新 Secret 的时候、数据将自动被 Base64 加密并存储。
新建 secret-2.yaml 文件,并写入如下内容:
apiVersion: v1
kind: Secret
metadata:
name: mysecret-2
type: Opaque
stringData:
config.yaml: |-
apiUrl: "https://my.api.com/api/v1"
username: admin
password: Az123456_
$ kubectl create -f secret-2.yaml
secret/mysecret-2 created
stringData 字段是只写的,使用 kubectl get -o yaml 获取 Secret 的 YAML 格式定义时,不会显示 stringData 字段,stringData 字段下面的所有条目会被 Base64 编码之后展示在 data 字段下:
$ kubectl get secret mysecret-2 -o yaml
apiVersion: v1
data:
config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IGFkbWluCnBhc3N3b3JkOiBBejEyMzQ1Nl8g
kind: Secret
...
如果一个配置项在 data 字段和 stringData 字段中都被定义了,那么会使用来自 stringData 的值,比如新建 secret-3.yaml 文件,并写入如下内容:
apiVersion: v1
kind: Secret
metadata:
name: mysecret-3
type: Opaque
data:
username: YWRtaW4=
stringData:
username: administrator
执行创建并且获取它的配置信息,如下所示:
apiVersion: v1
data:
username: YWRtaW5pc3RyYXRvcg==
kind: Secret
...
其中,YWRtaW5pc3RyYXRvcg== 解码就为 administrator。
3.5 使用生成器创建 Secret
kubectl 从 v1.14 版本开始支持使用 Kustomize 管理对象,具体的使用方法类似于 ConfigMap,生成器需要被明确定义在 kustomization.yaml 文件中。
执行如下命令创建一个 kustomization.yaml 文件,在其中使用 Secret 生成器字段 secretGenerator:
$ cat <<EOF >/root/learning/kustomization.yaml
secretGenerator:
- name: db-user-pass
files:
- username.txt
- password.txt
EOF
执行创建:
$ kubectl apply -k .
secret/db-user-pass-k48fdchcmh created
# 生成的 secret 名称后面带有一个后缀,这样可以保证每次生成器修改之后都生成了新的 secret
$ kubectl get secrets
NAME TYPE DATA AGE
db-user-pass Opaque 2 20m
db-user-pass-k48fdchcmh Opaque 2 35s
也可以使用 key-value 键值对创建 Secret:
$ cat <<EOF >/root/learning/kustomization.yaml
secretGenerator:
- name: db-user-pass
literals:
- username=admin
- password=Az123456_
EOF
$ kubectl apply -k .
secret/db-user-pass-9gkddh495h created
获取详细信息:
$ kubectl get secrets db-user-pass-9gkddh495h -o yaml
apiVersion: v1
data:
password: QXoxMjM0NTZf
username: YWRtaW4=
kind: Secret
...
$ echo 'YWRtaW4=' | base64 --decode
admin
对于一个已经存在的 Secret 可以使用命令 kubectl edit secrets xxx 更新其中的 data 字段。
3.6 挂载 Secret 到 Pod 中作为卷进行使用
在 Pod 的容器中,Secrets 可以被挂载成为数据卷或是引用为环境变量来使用。另外还可以在私有仓库 Docker 镜像拉取时配置进行使用。
挂载 Secret 到 Pod 中作为卷进行使用
通常将 Secret 作为挂载卷在 Pod 中使用的步骤如下:
- 创建一个 Secret 或是使用已经存在的 Secret。通常多个 Pods 会使用同一个 Secret。
- 修改 Pod YAML 文件定义,使用字段
.spec.volumes[]挂载卷,卷的名称可以任意命名,在字段.spec.volumes[].secret.secretName中填写需要挂载的 Secret 对象的名称。 - 在每个需要 Secret 的容器中添加字段
.spec.containers[].volumeMounts[]。规定.spec.containers[].volumeMounts[].readOnly = true以及字段.spec.containers[].volumeMounts[].mountPath表示将 Secret 挂载到某个未使用过的目录下。 - 修改镜像或是命令行以便程序可以在对应的目录下找到需要的文件。Secret
data字段下的每个 key 都会被用作挂载目录下的文件名。
比如这个 YAML 文件定义了将 mysecret 挂载到容器中的 /etc/foo 目录下:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: nginx:1.9
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
执行创建并进入到容器中查看:
$ kubectl exec -it mypod sh
# ls /etc/foo
password username
# cat /etc/foo/username
admin# cat /etc/foo/password
Az123456_#
如果想要指定挂载的文件名可以使用 items 和 path 字段,将上面的定义修改为:
# 将 username secret 的值存储到 /etc/foo/my-group/my-username 文件中,同时 username secret 不会被使用到
......
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
3.7 使用 Secret 作为环境变量
在 Pod 中使用 Secret 作为环境变量的步骤如下:
- 创建一个 Secret 或是使用已经存在的 Secret。通常多个 Pods 会使用同一个 Secret。
- 修改 Pod YAML 文件,在每个容器中添加环境变量引用需要的 Secret 的值,使用字段
env[].valueFrom.secretKeyRef。 - 修改镜像或是命令行以便程序可以通过环境变量找到需要的值。
下面是一个 Pod YAML 文件从环境变量中使用 Secret:
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: mycontainer
image: nginx:1.9
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
restartPolicy: Never
执行创建并进入容器检查环境变量:
$ kubectl exec -it secret-env-pod bash
root@secret-env-pod:/# echo $SECRET_USERNAME
admin
root@secret-env-pod:/# echo $SECRET_PASSWORD
Az123456_
3.8 使用镜像拉取 Secret(ImagePullSecrets)
当需要从某个私有仓库拉取镜像时,可以使用 Secret 存储私有仓库的密码并借助 kubelet 传递密码,具体定义时通过指定 Pod 的字段 spec.ImagePullSecrets 来引用它。
这里有两个步骤:
- 创建包含 Docker 镜像仓库证书的 Secret
- 在 Pod 定义中使用字段 ImagePullSecrets 引用该 Secret
在 Docker Hub 上创建一个账号,使用该账号创建一个私有仓库。
使用如下命令创建用于 Docker 镜像仓库鉴权的 Secret:
# 这里的 xxxx 替换为你们自己的账号信息
$ kubectl create secret docker-registry mydockerhubsecret --docker-username=xxxx --docker-password=xxxx --docker-email=xxxx
secret/mydockerhubsecret created
查看详细信息:
$ kubectl get secret mydockerhubsecret -o yaml
apiVersion: v1
data:
.dockerconfigjson: eyJhdXRocyI6eyJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOnsidXNlcm5hbWUiOiJzYW55aXNoZW5nIiwicGFzc3dvcmQiOiI4MjYxMDMyMCxxIiwiZW1haWwiOiJzaXRlc0B6aGFuamllLm1lIiwiYXV0aCI6ImMyRnVlV2x6YUdWdVp6bzRNall4TURNeU1DeHgifX19
kind: Secret
...
可以看到 mydockerhubsecret Secret 有一个隐藏的 .dockerconfigjson 文件,这个文件就是用于帮助验证的。
然后在创建 Pod 的时候引用这个 mydockerhubsecret:
apiVersion: v1
kind: Pod
metadata:
name: private-pod
spec:
imagePullSecrets:
- name: mydockerhubsecret
containers:
- image: username/private:tag # 这里根据实际情况添加私有仓库的镜像名
name: main
restartPolicy: Never
评论区