概述

Secret是用来保存小片敏感数据的k8s资源,例如密码,token,或者秘钥。这类数据当然也可以存放在Pod或者镜像中,但是放在Secret中是为了更方便的控制如何使用数据,并减少暴露的风险。
用户可以创建自己的secret,系统也会有自己的secret。
Pod需要先引用才能使用某个secret,Pod有2种方式来使用secret:作为volume的一个域被一个或多个容器挂载;在拉取镜像的时候被kubelet引用。

内建 Secret

由 ServiceAccount 创建的 API 证书附加的秘钥。
它是 k8s 自动生成的用来访问apiserver的Secret,所有Pod会默认使用这个 Secret 与 apiserver 通信。

创建自己的 Secret

使用kubectl create secret命令可以创建Secret 。
假如 mouge Pod 要访问数据库,需要用户名密码,分别存放在2个文件中:username.txt,password.txt 。

  1. echo -n 'admin' > ./username.txt
  2. echo -n '1f2d1e2e67df' > ./password.txt

kubectl create secret 指令将用户名密码写到 secret 中,并在 apiserver 创建 Secret:

  1. kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
  2. # secret "db-user-pass" created

上述命令中,我们创建了一个 secret ,名为 db-user-pass。
同时,在该 Secret 中包含了两个文件:username.txt 和 password.txt。
我们可以查看一下:

  1. kubectl get secrets
  2. # NAME TYPE DATA AGE
  3. # db-user-pass Opaque 2 51s

get或describe指令都不会展示secret的实际内容,这是出于对数据的保护的考虑,如果想查看实际内容请继续往下看。

解析 Secret 内容

  1. kubectl get secret db-user-pass -o yaml
  2. # apiVersion: v1
  3. # data:
  4. # username.txt: YWRtaW4=
  5. # password.txt: MWYyZDFlMmU2N2Rm
  6. # kind: Secret
  7. # metadata:
  8. # creationTimestamp: 2016-01-22T18:41:56Z
  9. # name: mysecret
  10. # namespace: default
  11. # resourceVersion: "164619"
  12. # selfLink: /api/v1/namespaces/default/secrets/mysecret
  13. # uid: cfee02d6-c137-11e5-8d73-42010af00002
  14. # type: Opaque

其中,文件中的内容是经过 base64 加密的,我们可以对其进行解密:

  1. echo 'YWRtaW4=' | base64 --decode
  2. # admin

使用 Secret

secret可以作为数据卷挂载或者作为环境变量暴露给Pod中的容器使用,也可以被系统中的其他资源使用。比如可以用secret导入与外部系统交互需要的证书文件等。

在 Pod 中以文件的形式使用 secret

  • 创建一个Secret,多个Pod可以引用同一个Secret
  • 修改Pod的定义,在spec.volumes[]加一个volume,给这个volume起个名字,spec.volumes[].secret.secretName记录的是要引用的Secret名字
  • 在每个需要使用Secret的容器中添加一项spec.containers[].volumeMounts[],指定spec.containers[].volumeMounts[].readOnly = true,spec.containers[].volumeMounts[].mountPath要指向一个未被使用的系统路径。
  • 修改镜像或者命令行使系统可以找到上一步指定的路径。此时Secret中data字段的每一个key都是指定路径下面的一个文件名。

下面是一个Pod中引用Secret的例子:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: mypod
  5. spec:
  6. containers:
  7. - name: mypod
  8. image: redis
  9. volumeMounts:
  10. - name: foo
  11. mountPath: "/etc/foo"
  12. readOnly: true
  13. volumes:
  14. - name: foo
  15. secret:
  16. secretName: mysecret

每一个被引用的Secret都要在spec.volumes中定义。
如果Pod中的多个容器都要引用这个Secret那么每一个容器定义中都要指定自己的volumeMounts,但是Pod定义中声明一次spec.volumes就好了。

此外,我们还可以控制 secret key 被映射到容器内的路径,利用 spec.volumes[].secret.items 来修改被映射的具体路径:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: mypod
  5. spec:
  6. containers:
  7. - name: mypod
  8. image: redis
  9. volumeMounts:
  10. - name: foo
  11. mountPath: "/etc/foo"
  12. readOnly: true
  13. volumes:
  14. - name: foo
  15. secret:
  16. secretName: mysecret
  17. items:
  18. - key: username
  19. path: my-group/my-username

上述配置文件会有什么效果呢?

  • username 被映射到了文件 /etc/foo/my-group/my-username 而不是 /etc/foo/username 。
  • password没有变。

同时,在挂载的同时,我们还可以直接挂载后文件的权限:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: mypod
  5. spec:
  6. containers:
  7. - name: mypod
  8. image: redis
  9. volumeMounts:
  10. - name: foo
  11. mountPath: "/etc/foo"
  12. volumes:
  13. - name: foo
  14. secret:
  15. secretName: mysecret
  16. defaultMode: 256

可以指定 secret 文件的权限,类似linux系统文件权限,如果不指定默认权限是0644,等同于linux文件的-rw-r—r—权限。
上述文件表示将secret挂载到容器的/etc/foo路径,每一个key衍生出的文件,权限位都将是0400。
PS:十进制数 256 对应八进制的 0400。

值得注意的一点是,以文件的形式挂载到容器中的secret,他们的值已经是经过base64解码的了,可以直接读出来使用。
如果修改一个Secret的内容,那么挂载了该Secret的容器中也将会取到更新后的值,但是这个时间间隔是由kubelet的同步时间决定的。最长的时间将是一个同步周期加上缓存生命周期(period+ttl)。

Pod 中以环境变量的形式使用Secret

下面,我们来通过示例来看一下如何将 Secret 作为环境变量的形式注入到 Pod 中。

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: secret-env-pod
  5. spec:
  6. containers:
  7. - name: mycontainer
  8. image: redis
  9. env:
  10. - name: SECRET_USERNAME
  11. valueFrom:
  12. secretKeyRef:
  13. name: mysecret
  14. key: username
  15. - name: SECRET_PASSWORD
  16. valueFrom:
  17. secretKeyRef:
  18. name: mysecret
  19. key: password
  20. restartPolicy: Never

可以看到,我们在 Pod 的定义中主动定义了对应的 env,并且设置了 env[].valueFrom.secretKeyRef 来指定对应的 secret 和对应的 key。
和文件挂载一样,容器中读取环境变量,已经是base64解码后的值了。