k8s secret对象详解

1. Secret 介绍-分为三大类

Secret解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者Pod Spec中。Secret可以以Volume或者环境变量的方式使用。

Secret有三种类型:

  • Service Account:用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的/run/secrets/kubernetes.io/serviceaccount目录中;
  • Opaque:base64编码格式的Secret,用来存储密码、密钥等;
  • kubernetes.io/dockerconfigjson:用来存储私有docker registry的认证信息。

具体详见结构体定义:type Secret struct

1.1 Opaque Secret方式

Opaque类型的数据是一个map类型,要求value是base64编码格式:

$ echo -n "admin" | base64
YWRtaW4=
$ echo -n "1f2d1e2e67df" | base64
MWYyZDFlMmU2N2Rm

secrets.yml

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

接着,就可以创建secret了:kubectl create -f secrets.yml

创建好secret之后,有两种方式来使用它:

  • 以Volume方式
  • 以环境变量方式

(1)volume方式

#test-projected-volume.yaml
 
apiVersion: v1
kind: Pod
metadata:
  name: test-projected-volume 
spec:
  containers:
  - name: test-secret-volume
    image: busybox
    args:
    - sleep
    - "86400"
    volumeMounts:
    - name: mysql-cred
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: mysql-cred
    projected:
      sources:
      - secret:
          name: user
      - secret:
          name: pass

当 Pod 变成 Running 状态之后,我们再验证一下这些 Secret 对象是不是已经在容器里了:

$ kubectl exec -it test-projected-volume -- /bin/sh
$ ls /projected-volume/
user
pass
$ cat /projected-volume/user
admin
$ cat /projected-volume/pass

(2)通过环境变量

#pod-secret-env.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-secret-env
spec:
  containers:
  - name: myapp
image: busybox
args:
    - sleep
    - "86400"
env:
    - name: SECRET_USERNAME
      valueFrom:
        secretKeyRef:
          name: mysecret
          key: user
    - name: SECRET_PASSWORD
      valueFrom:
        secretKeyRef:
          name: mysecret
          key: pass
  restartPolicy: Never

pod运行成功后:

$ kubectl exec -it pod-secret-env -- /bin/sh

进入容器中查看环境变量: env

1.1.1 通过volume挂载和环境变量的区别

通过Volume挂载到容器内部时,当该Secret的值发生变化时,容器内部具备自动更新的能力,但是通过环境变量设置到容器内部该值不具备自动更新的能力。所以一般推荐使用Volume挂载的方式使用Secret。

热更新原理参考附录

1.1.2 Secret 与 ConfigMap 对比

最后我们来对比下Secret和ConfigMap这两种资源对象的异同点:

相同点:

key/value的形式

属于某个特定的namespace

可以导出到环境变量

可以通过目录/文件形式挂载

通过 volume 挂载的配置信息均可热更新

不同点:

Secret 可以被 ServerAccount 关联

Secret 可以存储 docker register 的鉴权信息,用在 ImagePullSecret 参数中,用于拉取私有仓库的镜像

Secret 支持 Base64 加密

Secret 分为 kubernetes.io/service-account-token、kubernetes.io/dockerconfigjson、Opaque 三种类型,而 Configmap 不区分类型

1.2 kubernetes.io/dockerconfigjson

这个是为了应付 pull 私有image时候的权限问题。常见用法是:

(1)创建secrect

$ cat ~/.docker/config.json | base64
$ cat > myregistrykey.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: myregistrykey
data:
  .dockerconfigjson: UmVhbGx5IHJlYWxseSByZWVlZWVlZWVlZWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGx5eXl5eXl5eXl5eXl5eXl5eXl5eSBsbGxsbGxsbGxsbGxsbG9vb29vb29vb29vb29vb29vb29vb29vb29vb25ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubmdnZ2dnZ2dnZ2dnZ2dnZ2dnZ2cgYXV0aCBrZXlzCg==
type: kubernetes.io/dockerconfigjson
EOF
$ kubectl create -f myregistrykey.yaml

(2) 将这个secret和serviceaccount绑定

root@cld-kmaster1-1022:/home/ngadm# kubectl get serviceaccount -n test-nsp-gzchenyifan
NAME      SECRETS   AGE
default   1         3h28m


root@cld-kmaster1-1022:/home/ngadm# kubectl get serviceaccount -n test-nsp-gzchenyifan -oyaml
apiVersion: v1
items:
- apiVersion: v1
  imagePullSecrets:
  - name: myregistrykey
  kind: ServiceAccount
  metadata:
    creationTimestamp: "2022-11-03T06:00:30Z"
    name: default
    namespace: test-test
    resourceVersion: "1540683279"
    selfLink: /api/v1/namespaces/test-nsp-gzchenyifan/serviceaccounts/default
    uid: 957739c2-cac9-4bae-bad9-0862ca413dd2
  secrets:
  - name: default-token-tb8xx   //默认的secret
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""
  
// 再查看pod yaml的时候,就会发现指定了这个myregistrykey
 imagePullSecrets:
  - name: nsp-dev

1.3 Service Account类型

Service Account用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的/run/secrets/kubernetes.io/serviceaccount目录中。

$ kubectl run nginx --image nginx
deployment "nginx" created
$ kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
nginx-3137573019-md1u2   1/1       Running   0          13s
$ kubectl exec nginx-3137573019-md1u2 ls /run/secrets/kubernetes.io/serviceaccount
ca.crt
namespace
token

serviceAccount资源介绍

参考github源码分析 https://github.com/zoux86/learning-k8s-source-code/blob/master/k8s/kube-apiserver/17-k8s%E4%B9%8Bserviceaccount.md

1.4 secret三种类型的原理

其实都是kubelet 的secretpulgin在起作用。如果是dockerconfig类型,他通过拉取secret的值,填充pod的imagePull策略。如果是service account, 他通过拉取sa, token值。

具体代码:pkg/volume/secret/secret.go

3.附录

3.1 K8S Configmap 和 Secret 作为 Volume 的热更新原理

configmap/secret 作为 volume 挂载在容器内,如果 configmap 值发生变化,最大等待时间在 kubelet resyncInterval(60s) 内 该 mount 的 key 就会变成最新值。比如 cilium pod 挂载 cilium-config configmap,如果修改该 configmap 的 debug:false 为 true, 最多等待 60s,容器内该 debug 文件值就是 true。

但是作为环境变量 env 和 volume subpath 不支持热更新,环境变量在初始化过程就固定了。

热更新原理

(1) kubelet 会在每 60s 内去 syncPod(),检查 pod 的 volume kubelet.volumeManager.WaitForAttachAndMount(pod), github.com/kubernetes/… github.com/kubernetes/…

这里重点是 ReprocessPod(),会把这个 pod 又标记为未处理,等待 desiredStateOfWorldPopulator 下一次循环去 MarkRemountRequired()

(2) desiredStateOfWorldPopulator 下一次循环,会走 findAndAddNewPods() -> processPodVolumes() 这里重点是 dswp.actualStateOfWorld.MarkRemountRequired(uniquePodName),在 actual 里 MarkRemountRequired github.com/kubernetes/… 这里会判断每一个 volumePlugin.RequiresRemount(),而对于 configmap/secret volume 是 true,对于 csi 是 false github.com/kubernetes/… github.com/kubernetes/… github.com/kubernetes/…

(3) 然后再下一次循环里去 mountAttachVolumes() PodExistsInVolume() 会走 podObj.remountRequired,因为 MarkRemountRequired() 已经设置了需要 remount,然后 mountAttachVolumes() 里走 MountVolume() 逻辑:github.com/kubernetes/… 这样就走 configmap/secret mount 逻辑。

(4) configmap/secret mount 会使用 emptyDir plugin 来创建落盘目录 configmap 用的 v1.StorageMediumDefault github.com/kubernetes/…

secret 用的 v1.StorageMediumMemory github.com/kubernetes/… , 对于 secret 首次 mount 会使用命令 mount -t tmpfs xxx: github.com/kubernetes/… github.com/kubernetes/…

对于 configmap 这里的 wrapped 是 emptyDir,主要用来创建文件和权限

wrapped, err := b.plugin.host.NewWrapperMounter(b.volName, wrappedVolumeSpec(), &b.pod, *b.opts)
wrapped.SetUpAt(dir, mounterArgs)

// 这里的 getConfigMap 是 configmapManager 的 configMapManager.GetConfigMap()
// https://github.com/kubernetes/kubernetes/blob/v1.19.7/pkg/kubelet/configmap/configmap_manager.go#L82-L91
// 注意,kubelet 默认使用 kubeletconfiginternal.WatchChangeDetectionStrategy 的 configmapManager,所以 configmap
// 发生变化,configmapManager 立刻拿到最新的值:https://github.com/kubernetes/kubernetes/blob/v1.19.7/pkg/kubelet/kubelet.go#L538-L540
// 只是需要等待 kubelet 每次的 resyncInterval 60s 去 syncPod,所以每次修改 configmap 最大等待时间是 60s。
configMap, err := b.getConfigMap(b.pod.Namespace, b.source.Name)

// 然后把最新的 configmap 对象数据写到每一个文件里
payload, err := MakePayload(b.source.Items, configMap, b.source.DefaultMode, optional)
err = writer.Write(payload)

复制代码
参考文献

mounted-configmaps-are-updated-automatically

mounted-configmaps-are-updated-automatically

Kubernetes Pod 中的 ConfigMap 配置更新

分别测试使用 ConfigMap 挂载 Env 和 Volume 的情况

开始只有 NewCachingConfigMapManager(),除了 kubelet resyncInterval 时间还有个 ttl 时间,经过讨论后期加了 NewWatchingConfigMapManager, 直接 watch 立刻拿到最新的 configmap,只需要等待最大 kubelet resyncInterval 时间。下面链接是 issue 和 pr:

Kubelet watches necessary secrets/configmaps instead of periodic polling

Migrate kubelet to ConfigMapManager interface and use TTL-based caching manager

kubelet refresh times for configmaps is long and random

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容