kubernete编排技术七:secret

gq_design
发布于 2022-5-1 08:53
浏览
0收藏

作者 |  朱晋君
来源 | 君哥聊技术(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。

分类
标签
收藏
回复
举报
回复
    相关推荐