kubernete编排技术七:secret
作者 | 朱晋君
来源 | 君哥聊技术(ID:gh_1f109b82d301)
在前面文章中,我们讲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。