Kubernetes-Secret


1. 简介

Secret 是一种包含少量敏感信息例如密码、令牌或密钥的对象。 这样的信息可能会被放在 Pod 规约中或者镜像中。 使用 Secret 意味着你不需要在应用程序代码中包含机密数据。

由于创建 Secret 可以独立于使用它们的 Pod, 因此在创建、查看和编辑 Pod 的工作流程中暴露 Secret(及其数据)的风险较小。 Kubernetes 和在集群中运行的应用程序也可以对 Secret 采取额外的预防措施, 例如避免将机密数据写入非易失性存储。

Secret 类似于 但专门用于保存机密数据。

2. 4种创建方式

$ kubectl create secret -h
Create a secret using specified subcommand.

Available Commands:
  docker-registry 创建一个给 Docker registry 使用的 secret
  generic         从本地 file, directory 或者 literal value 创建一个 secret
  tls             创建一个 TLS secret

2.4创建的secret进行挂载

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
    imagePullPolicy: IfNotPresent
  volumes:
  - name: foo
    secret:
      secretName: secret-basic-auth

将 Secret 键名映射到特定路径

我们还可以控制 Secret 键名在存储卷中映射的的路径。 你可以使用 spec.volumes[].secret.items 字段修改每个键对应的目标路径:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
    imagePullPolicy: IfNotPresent
  volumes:
  - name: foo
    secret:
      secretName: secret-basic-auth
      items:
      - key: username
        path: my-group/my-username

将会发生什么呢:

  • username Secret 存储在 /etc/foo/my-group/my-username 文件中而不是 /etc/foo/username 中。
  • password Secret 没有被映射

如果使用了 spec.volumes[].secret.items,只有在 items 中指定的键会被映射。 要使用 Secret 中所有键,就必须将它们都列在 items 字段中。 所有列出的键名必须存在于相应的 Secret 中。否则,不会创建卷。

5.2 通过环境变量方式挂载

apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: redis
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: secret-basic-auth
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: secret-basic-auth
            key: password
    imagePullPolicy: IfNotPresent
  restartPolicy: Never

Secret 更新之后对应的环境变量不会被更新

如果某个容器已经在通过环境变量使用某 Secret,对该 Secret 的更新不会被 容器马上看见,除非容器被重启。有一些第三方的解决方案能够在 Secret 发生 变化时触发容器重启。

6. docker-registry auth

如4.2中描述的,可以使用以下的方式创建docker-registry secret

6.1 直接创建docker-registry secret

创建 Secret,命名为 regcred

kubectl create secret docker-registry regcred \
  --docker-server=<你的镜像仓库服务器> \
  --docker-username=<你的用户名> \
  --docker-password=<你的密码> \
  --docker-email=<你的邮箱地址>

在这里:

  • 是你的私有 Docker 仓库全限定域名(FQDN)。 DockerHub 使用 https://index.docker.io/v1/
  • 是你的 Docker 用户名。
  • 是你的 Docker 密码。
  • 是你的 Docker 邮箱。

这样你就成功地将集群中的 Docker 凭证设置为名为 regcred 的 Secret。

如果已经有 Docker 凭据文件,则可以将凭据文件导入为 Kubernetes Secret, 而不是执行上面的命令。 基于已有的 docker 凭据创建 secret 解释了如何完成这一操作。

如果你在使用多个私有容器仓库,这种技术将特别有用。 原因是 kubectl create secret docker-registry 创建的是仅适用于某个私有仓库的 Secret。

说明: Pod 只能引用位于自身所在名字空间中的 Secret,因此需要针对每个名字空间 重复执行上述过程。

4.2中已经记录过)

4.2 中提到了,可以直接指定file地址用来创建kubernetes.io/dockerconfigjson(官方文档中摘抄的),但是本人测试时报错,可能是k8s版本的问题,但是以下的方式是经过测试没问题的

apiVersion: v1
kind: Secret
metadata:
  name: secret-dockercfg
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: ewogICJhdXRocyI6IHsKICAgICJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOiB7CiAgICAgICJ1c2VybmFtZSI6ICJ0aWdlciIsCiAgICAgICJwYXNzd29yZCI6ICJwYXNzMTEzIiwKICAgICAgImVtYWlsIjogInRpZ2VyQGFjbWUuY29tIiwKICAgICAgImF1dGgiOiAiZEdsblpYSTZjR0Z6Y3pFeE13PT0iCiAgICB9CiAgfQp9Cg==

.dockerconfigjson 字段的值是 Docker 凭证的 base64 编码值,也就是把~/.docker/config.json 转成了base64值。

$ base64 ~/.docker/config.json
ewogICJhdXRocyI6IHsKICAgICJodHRwczovL2luZGV4LmRvY2tlci5pby92MS8iOiB7CiAgICAg
ICJ1c2VybmFtZSI6ICJ0aWdlciIsCiAgICAgICJwYXNzd29yZCI6ICJwYXNzMTEzIiwKICAgICAg
ImVtYWlsIjogInRpZ2VyQGFjbWUuY29tIiwKICAgICAgImF1dGgiOiAiZEdsblpYSTZjR0Z6Y3pF
eE13PT0iCiAgICB9CiAgfQp9Cg==

6.3 使用docker-registry secret

在创建 Pod 时,可以在 Pod 定义中增加 imagePullSecrets 部分来引用该 Secret,如下配置:

apiVersion: v1
kind: Pod
metadata:
  name: private-reg
spec:
  containers:
  - name: private-reg-container
    image: redis
  # 指定docker-registry secret
  imagePullSecrets:
  # docker-registry secret name
  - name: regcred

7. subPath

如果我们想挂载配置文件时,只覆盖指定mountPath下的指定文件(因为默认会把Secret中的信息挂载到mountPath下,此时mountPath下只有Secret的配置文件,mountPath下本来有的配置文件会被清除掉),那么需要使用subpath来指定要被覆盖的文件

我在的6.0中详细的写了相关的配置,感兴趣的可以了解下。

8. 挂载的 secret 会被自动更新

当已经存储于卷中被使用的 Secret 被更新时,被映射的键也将终将被更新。 组件 kubelet 在周期性同步时检查被挂载的 Secret 是不是最新的。 但是,它会使用其本地缓存的数值作为 Secret 的当前值。

缓存的类型可以使用 KubeletConfiguration 结构 中的 ConfigMapAndSecretChangeDetectionStrategy 字段来配置。 它可以通过 watch 操作来传播(默认),基于 TTL 来刷新,也可以 将所有请求直接重定向到 API 服务器。 因此,从 Secret 被更新到将新 Secret 被投射到 Pod 的那一刻的总延迟可能与 kubelet 同步周期 + 缓存传播延迟一样长,其中缓存传播延迟取决于所选的缓存类型。 对应于不同的缓存类型,该延迟或者等于 watch 传播延迟,或者等于缓存的 TTL, 或者为 0。

说明: 使用 Secret 作为子路径卷挂载的容器 不会收到 Secret 更新。

9. 不可更改的 secret

Kubernetes 的特性 不可变的 Secret 和 ConfigMap 提供了一种可选配置, 可以设置各个 Secret 和 ConfigMap 为不可变的。 对于大量使用 Secret 的集群(至少有成千上万各不相同的 Secret 供 Pod 挂载), 禁止变更它们的数据有下列好处:

  • 防止意外(或非预期的)更新导致应用程序中断
  • 通过将 Secret 标记为不可变来关闭 kube-apiserver 对其的监视,从而显著降低 kube-apiserver 的负载,提升集群性能。

这个特性通过 ImmutableEmphemeralVolumes 来控制,从 v1.19 开始默认启用。 你可以通过将 Secret 的 immutable 字段设置为 true 创建不可更改的 Secret。 例如:

apiVersion: v1
kind: Secret
metadata:
  ...
data:
  ...
immutable: true