在前面文章中,我们讲RBAC的时候提到了secret,kubernetes中的secret对象用来保存一些敏感信息,比如密码、token、ssh key等。相比于这些敏感信息存放在pod声明或者容器镜像,secret对象保存的方式更加灵活和安全。
创建secret
创建secret方式有多种,下面介绍5种创建方式,无论哪种方式,都需要在定义yaml文件的时候,把kind定义为secret。
1.双引号引用常量字符创
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque stringData: config.yaml: |- apiUrl: "https://my.api.com/api/v1" username: "admin" password: "jinjunzhu"
使用这种方式我们创建这个对象:
[root@master secret]# kubectl create -f mysecrete.yaml secret/mysecret created [root@master secret]# kubectl get secret NAME TYPE DATA AGE mysecret Opaque 1 20s
创建成功后我们查看一下这个对象的详情:
[root@master secret]# kubectl get secret mysecret -o yaml apiVersion: v1 data: config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6ICJhZG1pbiIKcGFzc3dvcmQ6ICJqaW5qdW56aHUi kind: Secret metadata: creationTimestamp: "2020-08-19T09:39:16Z" name: mysecret namespace: default resourceVersion: "19727" selfLink: /api/v1/namespaces/default/secrets/mysecret uid: 91d335ed-586d-496c-9f3f-09ddd6b37c39 type: Opaque
2.不加双引号需要使用base64转码后的值
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm
3.把引号引用的字符串放在配置文件里面,部署的时候动态地替换,如下,可以在创建这个对象之前替换username和password
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque stringData: config.yaml: |- apiUrl: "https://my.api.com/api/v1" username: {{username}} password: {{password}}
4.使用generator来创建,这时我们需要在目录下提前生成这2个文件,如下:
secretGenerator: - name: db-user-pass files: - username.txt - password.txt
使用这种方式创建这个对象:
[root@master secret]# kubectl apply -k . secret/db-user-pass-dtc6dh8968 created [root@master secret]# kubectl get secret NAME TYPE DATA AGE db-user-pass-dtc6dh8968 Opaque 2 17s
这时我们查看这个对象的describe,结果如下:
[root@master secret]# kubectl describe secret db-user-pass-dtc6dh8968 Name: db-user-pass-dtc6dh8968 Namespace: default Labels: <none> Annotations: Type: Opaque Data ==== password.txt: 10 bytes username.txt: 6 bytes
5.使用literals(字面量)来创建,这个创建命令跟4一样
secretGenerator: - name: db-user-pass literals: - username=admin - password=secret
使用secret
secret可以作为数据卷挂载在pod中的容器上,也可以作为环境变量暴露给pod中的容器。
1.作为pod中的文件
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: spingboot-mybatis imagePullPolicy: IfNotPresent image: zjj2006forever/springboot-mybatis:1.3 volumeMounts: - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: secretName: mysecret #使用上节中第1个 defaultMode: 0400
创建这个pod后,我们进入查看,如下:
[root@master secret]# kubectl exec -it mypod -- /bin/sh / # cd /etc/foo/ /etc/foo # ls config.yaml /etc/foo # cat config.yaml apiUrl: "https://my.api.com/api/v1" username: "admin" password: "jinjunzhu"
可以看到这个secret对象以文件的方式挂载在了容器里。
注意:上面的defaultMode: 0400是配置secret生成的文件的访问权限,如果不配置默认是0644,创建pod后,看如下输出:
/etc # cd foo/ /etc/foo # ls -al total 0 drwxrwxrwt 3 root root 100 Aug 19 10:35 . drwxr-xr-x 1 root root 77 Aug 19 10:35 .. drwxr-xr-x 2 root root 60 Aug 19 10:35 ..2020_08_19_10_35_01.510700789 lrwxrwxrwx 1 root root 31 Aug 19 10:35 ..data -> ..2020_08_19_10_35_01.510700789 lrwxrwxrwx 1 root root 18 Aug 19 10:35 config.yaml -> ..data/config.yaml /etc/foo # cd ..data/ /etc/foo/..2020_08_19_10_35_01.510700789 # ls -l total 4 -r-------- 1 root root 75 Aug 19 10:35 config.yaml
2.把不同的key放到不同的路径
下面我们编写yaml文件my-secret-pod2.yaml 如下:
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: spingboot-mybatis imagePullPolicy: IfNotPresent image: zjj2006forever/springboot-mybatis:1.3 volumeMounts: - name: foo mountPath: "/etc/foo" readOnly: true volumes: - name: foo secret: secretName: mysecret #使用上节中第2个 items: - key: username path: my-group/my-username - key: password path: my-group/my-password
我们创建这个pod:
[root@master secret]# kubectl create -f my-secret-pod2.yaml pod/mypod created
创建成功后我们查看key存放的路径:
[root@master secret]# kubectl exec -it mypod -- /bin/sh / # cd /etc/foo/ /etc/foo # ls my-group /etc/foo # cd my-group/ /etc/foo/..2020_08_20_03_30_40.155209638/my-group # ls my-password my-username /etc/foo/..2020_08_20_03_30_40.155209638/my-group # cat my-username admin /etc/foo/..2020_08_20_03_30_40.155209638/my-group # cat my-password 1f2d1e2e67df
3.使用secret作为环境变量
看下面在这个yaml文件my-secret-pod3.yaml
apiVersion: v1 kind: Pod metadata: name: secret-env-pod spec: containers: - name: spingboot-mybatis imagePullPolicy: IfNotPresent image: zjj2006forever/springboot-mybatis:1.3 env: - name: SECRET_USERNAME valueFrom: secretKeyRef: name: mysecret key: username - name: SECRET_PASSWORD valueFrom: secretKeyRef: name: mysecret key: password restartPolicy: Never
创建这个pod:
[root@master secret]# kubectl create -f my-secret-pod3.yaml pod/secret-env-pod created [root@master secret]# kubectl get pods NAME READY STATUS RESTARTS AGE secret-env-pod 1/1 Running 0 6s
进入pod查看环境变量,如下所示:
[root@master secret]# kubectl exec -it mypod -- /bin/sh Error from server (NotFound): pods "mypod" not found [root@master secret]# kubectl exec -it secret-env-pod -- /bin/sh / # echo $SECRET_USERNAME admin / # echo $SECRET_PASSWORD 1f2d1e2e67df
可以看到,username和password这2个参数已经正确配置到容器中的环境变量。
secret更新
当已经挂载使用的secret被更新时,projected key也最终会被更新。kubelete会周期性地检查secret是否被更新。但是kubelete获取secret的值是从缓存中获取的,secret更新的时候,更新缓存有2种机制,默认使用基于ttl的watch机制,还有一种方式就是把请求直接重定向到api server。因此secret更新的时候,总会有一个延迟时间,这个延迟时间等于kubelete检查更新的周期时间加上刷新缓存的时间。同时要注意:使用子路径挂载secret方式的容器是无法更新secret的。
所以kubernete v1.18版本引入了不可更新的Secrets和ConfigMaps,这样Secrets和ConfigMaps就是不能更新的,如果要更新,只能删除重建。对于有成千上万secret的集群来说,这有2个好处:
- 防止因为更新secret而导致的意外程序中断;
- 关闭kubelete对secret的watch机制,可以减轻kube-apiserver的负载,让集群性能更好。
如下面这个secret的声明,只要定义immutable是true就可以让secret变成不可更新。
apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque data: username: YWRtaW4= password: MWYyZDFlMmU2N2Rm immutable: true
使用secret注意事项
1.pod使用secret时必须先成功创建secret,然后才能创建pod,否则pod创建失败。
2.只有secret和pod使用同一个namespace,pod才能使用这个secret。
3.单个secret内存必须小于1M,因此不建议创建很大的secret,因为这会消耗api server和kubelete的内存。不过创建非常多的小的secret也会消耗很大的内存。
4.kubelete只支持用kubectl创建的pod,或者通知控制器复制的pod使用secret,其他方式创建的pod不能使用secret。
5.作为容器中环境变量的使用,如果secretKeyRef字段依赖的key在secret中不存在,pod将创建失败。
6.作为容器中环境变量的使用,如果环境变量名使用envFrom字段来声明(如下代码),这时如果环境变量名不合法,就会被跳过,但是pod可以正常启动。启动后可以通过下面命令来查看被跳过的环境变量:
kubectl get events LASTSEEN FIRSTSEEN COUNT NAME KIND SUBOBJECT TYPE REASON 0s 0s 1 dapi-test-pod Pod Warning InvalidEnvironmentVariableNames kubelet, 127.0.0.1 Keys [1badkey, 2alsobad] from the EnvFrom secret default/mysecret were skipped since they are considered invalid environment variable names. apiVersion: v1 kind: Pod metadata: name: secret-test-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ "/bin/sh", "-c", "env" ] envFrom: - secretRef: name: mysecret restartPolicy: Never
总结
在secret中保存一些敏感信息确实比较灵活和安全,但也有一些限制,最好把secret设置为不可修改的。
secret的使用主要还是在访问控制中,比如上篇文章讲的RBAC,在多数场景下,我们使用ServiceAccount作为用户,系统会为我们创建默认的secret。