一般情况下ConfigMap是用来存储一些非安全的配置信息,如果涉及到一些安全相关的数据的话,用ConfigMap就不妥了,因为ConfigMap是明文存储的,这时候就需要另外一个资源对象,Secret—用来保存敏感信息,例如密码、OAuth令牌和ssh key等等,将这些信息放在Secret中比放在Pod的定义中或者Docker镜像中要更加安全和灵活。

Secret主要使用的有以下三种类型:

  • Opaquebase64编码格式的Secret,用来存储密码、密钥等;但数据也可以通过base64-decode解码得到原始数据,所以加密性很弱。
  • kubernetes.io/dockerconfigjson:用来存储私有docker registry的认证信息。
  • kubernetes.io/service-account-token:用于ServiceAccountServiceAccount创建时。k8s会默认创建一个对应的Secret对象,Pod如果使用了ServiceAccount,对应的Secret会自动挂载到Pod目录/run/secrets/kubernetes.io/serviceaccount中。
  • bootstrap.kubernetes.io/token:用于节点接入集群的校验的Secret。

Opaque Secret

Opaque类型的数据是一个map类型,要求value必须是base64编码格式,比如创建一个用户名为admin,密码为admin321的Secret对象,首先需要先把用户名和密码做base64编码;

  1. $ echo -n "admin" | base64
  2. YWRtaW4=
  3. $ echo -n "admin321" | base64
  4. YWRtaW4zMjE=

然后编写一个Yaml文件(secret-demo.yaml):

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: YWRtaW4zMjE=

使用kubectl来创建:

$ kubectl get secret
NAME                  TYPE                                  DATA      AGE
default-token-n9w2d   kubernetes.io/service-account-token   3         33d
mysecret              Opaque                                2         40s

其中default-token-n9w2d为创建集群时默认创建的Secret,被serviceaccount/default引用。使用describe命令查看详情:

$ kubectl describe secret mysecret
Name:         mysecret
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
password:  8 bytes
username:  5 bytes

利用 describe 命令查看到的 Data 没有直接显示出来,如果想看到 Data 里面的详细信息,可以输出成YAML 文件进行查看:

$ kubectl get secret mysecret -o yaml
apiVersion: v1
data:
  password: YWRtaW4zMjE=
  username: YWRtaW4=
kind: Secret
metadata:
  creationTimestamp: 2018-06-19T15:27:06Z
  name: mysecret
  namespace: default
  resourceVersion: "3694084"
  selfLink: /api/v1/namespaces/default/secrets/mysecret
  uid: 39c139f5-73d5-11e8-a101-525400db4df7
type: Opaque

创建好Secret对象后,有两种方式来使用:

  • 以环境变量的形式
  • 以Volume的形式挂载

环境变量

编写一个yaml文件(secret1-pod.yaml)

apiVersion: v1
kind: Pod
metadata:
  name: secret1-pod
spec:
  containers:
  - name: secret1
    image: busybox
    command: ["/bin/sh", "-c", "env"]
    env:
    - name: USERNAME
      valueFrom:
        secretKeyRef:
          name: mysecret
          key: username
    - name: PASSWORD
      valueFrom:
        secretKeyRef:
          name: mysecret
          key: password

主要注意的是上面环境变量中定义的secretkeyRef字段,和前面configMapKeyRef类似,一个是从Secret对象中获取,一个是从ConfigMap对象中获取,创建上面的Pod:

$ kubectl create -f secret1-pod.yaml
pod "secret1-pod" created

查看日志输出:

$ kubectl logs secret1-pod
...
USERNAME=admin
PASSWORD=admin321
...

Volume挂载

验证下Volume挂载,创建一个Pod文件(secret2-pod.yaml):

apiVersion: v1
kind: Pod
metadata:
  name: secret2-pod
spec:
  volumes:
  - name: secrets
    secret:
      secretName: mysecret
  containers:
  - name: secret2
    image: busybox
    command: ["/bin/sh", "-c", "ls /etc/secrets"]
    volumeMounts:
    - name: secrets
      mountPath: /etc/secrets

创建Pod,然后查看输出日志:

$ kubectl create -f secret-pod2.yaml
pod "secret2-pod" created
$ kubectl logs secret2-pod
password
username

可看到Secret把两个Key挂载成两个对应的文件。当然如果想要挂载到指定的文件上面,???可以在secretName下面添加items指定key和path

kubernetes.io/dockerconfigjson

除了上面的Opaque类型外,还可以创建用户docker registry认证的Secret,直接用kubectl create命令创建,如下:

$ kubectl create secret docker-registry myregistry --docker-server=DOCKER_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
secret "myregistry" created

还可以通过指定文件的方式来创建镜像仓库认证信息,需要注意对应的KEY和TYPE:

$ kubectl create secret generic myregistry --from-file=.dockerconfigjson=/root/.docker/config.json --type=kubernetes.io/dockerconfigjson

查看Secret列表:

$ kubectl get secret
NAME                  TYPE                                  DATA      AGE
default-token-n9w2d   kubernetes.io/service-account-token   3         33d
myregistry            kubernetes.io/dockerconfigjson        1         15s
mysecret              Opaque                                2         34m

注意上面的TYPE类型,myregistry对应的是kubernetes.io/dockerconfigjson,使用describe命令查看详细信息:

$ kubectl describe secret myregistry
Name:         myregistry
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  kubernetes.io/dockerconfigjson

Data
====
.dockerconfigjson:  152 bytes

使用-o yaml来展示完整信息:

$ kubectl get secret myregistry -o yaml
apiVersion: v1
data:
  .dockerconfigjson: eyJhdXRocyI6eyJET0NLRVJfU0VSVkVSIjp7InVzZXJuYW1lIjoiRE9DS0VSX1VTRVIiLCJwYXNzd29yZCI6IkRPQ0tFUl9QQVNTV09SRCIsImVtYWlsIjoiRE9DS0VSX0VNQUlMIiwiYXV0aCI6IlJFOURTMFZTWDFWVFJWSTZSRTlEUzBWU1gxQkJVMU5YVDFKRSJ9fX0=
kind: Secret
metadata:
  creationTimestamp: 2018-06-19T16:01:05Z
  name: myregistry
  namespace: default
  resourceVersion: "3696966"
  selfLink: /api/v1/namespaces/default/secrets/myregistry
  uid: f91db707-73d9-11e8-a101-525400db4df7
type: kubernetes.io/dockerconfigjson

把上面的data.dockerconfigjson下的数据做base64解码,看下数据:

$ echo eyJhdXRocyI6eyJET0NLRVJfU0VSVkVSIjp7InVzZXJuYW1lIjoiRE9DS0VSX1VTRVIiLCJwYXNzd29yZCI6IkRPQ0tFUl9QQVNTV09SRCIsImVtYWlsIjoiRE9DS0VSX0VNQUlMIiwiYXV0aCI6IlJFOURTMFZTWDFWVFJWSTZSRTlEUzBWU1gxQkJVMU5YVDFKRSJ9fX0= | base64 -d
{"auths":{"DOCKER_SERVER":{"username":"DOCKER_USER","password":"DOCKER_PASSWORD","email":"DOCKER_EMAIL","auth":"RE9DS0VSX1VTRVI6RE9DS0VSX1BBU1NXT1JE"}}}

如果需要拉去私有仓库中的Docker镜像的话,需要使用上面的myregistry这个Secret:

apiVersion: v1
kind: Pod
metadata:
  name: foo
spec:
  containers:
  - name: foo
    image: 192.168.1.100:5000/test:v1
  imagePullSecret:
  - name: myregistry

imagePullSecret与secret不同,因为Secret可以挂载到Pod中,但是imagePullSecret只能有kubelet访问。

需要拉去私有仓库镜像192.168.1.100:5000/test:v1,就需要针对该私有仓库来创建一个如上的Secret,然后在Pod中指定imagePullSecrets。

除了设置Pod.spec.imagePullSecrets这种方式来获取私有镜像外,还可以通过在ServiceAccount中设置imagePullsecrets,然后就会自动为使用该SA的Pod注入imagePullsecrets信息:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2019-11-08T12:00:04Z"
  name: default
  namespace: default
  resourceVersion: "322"
  selfLink: /api/v1/namespaces/default/serviceaccount/default
  uid: cc37a719-c4fe-4ebf-92da-e92c3e24d5d0
secrets:
- name: default-token-Stsh4
imagePullSecrets:
- name: myregistry

kubernetes.io/service-account-token

另外一种Secret类型就是kubernetes.io/service-account-token,用于被ServiceAccount引用。ServiceAccount创建时Kubernetes会默认创建对应的Secret、Pod如果使用了ServiceAccount,对应的Secret会自动挂载到Pod的/var/run/secrets/kubernetes.io/serviceaccount/目录中。如下所示随意创建一个 Pod:

$ kubectl run secret-pod3 --image nginx:1.7.9
deployment.apps "secret-pod3" created
$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
...
secret-pod3-78c8c76db8-7zmqm   1/1       Running   0          13s
...

查看Pod的详细信息:

    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-5tsh4
      readOnly: true
  ......
  serviceAccount: default
  serviceAccountName: default
  volumes:
  - name: default-token-5tsh4
    secret:
      defaultMode: 420
      secretName: default-token-5tsh4

可以看到默认把名为default(自动创建的)的ServiceAccount对应的Secret对象通过Volume挂载到了容器的/var/run/secrets/kubernetes.io/serviceaccount的目录中。

Secret vs ConfigMap

相同点

  • key/value的形式
  • 属于某个特定的命名空间
  • 可以导出到环境变量
  • 可以通过目录/文件形式挂载
  • 通过Volume挂载的配置信息均可热更新

不同点

  • Secret可以被ServiceAccount关联
  • Secret可以寻出docker registry的鉴权信息,用在imagePullSecret参数中,用于拉取私有仓库的镜像
  • Secret支持Base64加密
  • Secret分为kubernetes.io/service-account-token、kubernetes.io/dockerconfigjson、Opaque三种类型,而ConfigMap不区分类型

同样 Secret 文件大小限制为 1MB(ETCD 的要求);Secret 虽然采用 Base64 编码,但是我们还是可以很方便解码获取到原始信息,所以对于非常重要的数据还是需要慎重考虑,可以考虑使用 Vault 来进行加密管理。