• k8s中secret解析


    概览

    Secret是用来保存小片敏感数据的k8s资源,例如密码,token,或者秘钥。这类数据当然也可以存放在Pod或者镜像中,但是放在Secret中是为了更方便的控制如何使用数据,并减少暴露的风险。

    用户可以创建自己的secret,系统也会有自己的secret。

    Pod需要先引用才能使用某个secret,Pod有2种方式来使用secret:作为volume的一个域被一个或多个容器挂载;在拉取镜像的时候被kubelet引用。

    內建的Secrets

    由ServiceAccount创建的API证书附加的秘钥

    k8s自动生成的用来访问apiserver的Secret,所有Pod会默认使用这个Secret与apiserver通信

    创建自己的Secret

    使用kubectl create secret命令创建Secret

    假如mougePod要访问数据库,需要用户名密码,分别存放在2个文件中:username.txt,password.txt

    # Create files needed for rest of example.
    $ echo -n 'admin' > ./username.txt
    $ echo -n '1f2d1e2e67df' > ./password.txt

    kubectl create secret指令将用户名密码写到secret中,并在apiserver创建Secret

    $ kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
    secret "db-user-pass" created

    查看创建结果:

    $ kubectl get secrets
    NAME                  TYPE                                  DATA      AGE
    db-user-pass          Opaque                                2         51s
    $ kubectl describe secrets/db-user-pass
    Name:            db-user-pass
    Namespace:       default
    Labels:          <none>
    Annotations:     <none>
    
    Type:            Opaque
    
    Data
    ====
    password.txt:    12 bytes
    username.txt:    5 bytes

    get或describe指令都不会展示secret的实际内容,这是出于对数据的保护的考虑,如果想查看实际内容请继续往下看。

    手动创建Secret

    创建一个secret.yaml文件,内容用base64编码

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

    yaml文件内容:

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

    创建:

    $ kubectl create -f ./secret.yaml
    secret "mysecret" created

    解析Secret中内容

    $ kubectl get secret mysecret -o yaml
    apiVersion: v1
    data:
      username: YWRtaW4=
      password: MWYyZDFlMmU2N2Rm
    kind: Secret
    metadata:
      creationTimestamp: 2016-01-22T18:41:56Z
      name: mysecret
      namespace: default
      resourceVersion: "164619"
      selfLink: /api/v1/namespaces/default/secrets/mysecret
      uid: cfee02d6-c137-11e5-8d73-42010af00002
    type: Opaque

    base64解码:

    $ echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
    1f2d1e2e67df

    使用Secret

    secret可以作为数据卷挂载或者作为环境变量暴露给Pod中的容器使用,也可以被系统中的其他资源使用。比如可以用secret导入与外部系统交互需要的证书文件等。

    在Pod中以文件的形式使用secret

    1. 创建一个Secret,多个Pod可以引用同一个Secret
    2. 修改Pod的定义,在spec.volumes[]加一个volume,给这个volume起个名字,spec.volumes[].secret.secretName记录的是要引用的Secret名字
    3. 在每个需要使用Secret的容器中添加一项spec.containers[].volumeMounts[],指定spec.containers[].volumeMounts[].readOnly = truespec.containers[].volumeMounts[].mountPath要指向一个未被使用的系统路径。
    4. 修改镜像或者命令行使系统可以找到上一步指定的路径。此时Secret中data字段的每一个key都是指定路径下面的一个文件名

    下面是一个Pod中引用Secret的列子:

    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec:
      containers:
      - name: mypod
        image: redis
        volumeMounts:
        - name: foo
          mountPath: "/etc/foo"
          readOnly: true
      volumes:
      - name: foo
        secret:
          secretName: mysecret

    每一个被引用的Secret都要在spec.volumes中定义

    如果Pod中的多个容器都要引用这个Secret那么每一个容器定义中都要指定自己的volumeMounts,但是Pod定义中声明一次spec.volumes就好了。

    映射secret key到指定的路径

    可以控制secret key被映射到容器内的路径,利用spec.volumes[].secret.items来修改被映射的具体路径

    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec:
      containers:
      - name: mypod
        image: redis
        volumeMounts:
        - name: foo
          mountPath: "/etc/foo"
          readOnly: true
      volumes:
      - name: foo
        secret:
          secretName: mysecret
          items:
          - key: username
            path: my-group/my-username

    发生了什么呢?

    • username被映射到了文件/etc/foo/my-group/my-username而不是/etc/foo/username
    • password没有变

    Secret文件权限

    可以指定secret文件的权限,类似linux系统文件权限,如果不指定默认权限是0644,等同于linux文件的-rw-r--r--权限

    设置默认权限位

    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec:
      containers:
      - name: mypod
        image: redis
        volumeMounts:
        - name: foo
          mountPath: "/etc/foo"
      volumes:
      - name: foo
        secret:
          secretName: mysecret
          defaultMode: 256

    上述文件表示将secret挂载到容器的/etc/foo路径,每一个key衍生出的文件,权限位都将是0400

    由于JSON不支持八进制数字,因此用十进制数256表示0400,如果用yaml格式的文件那么就很自然的使用八进制了

    同理可以单独指定某个key的权限

    apiVersion: v1
    kind: Pod
    metadata:
      name: mypod
    spec:
      containers:
      - name: mypod
        image: redis
        volumeMounts:
        - name: foo
          mountPath: "/etc/foo"
      volumes:
      - name: foo
        secret:
          secretName: mysecret
          items:
          - key: username
            path: my-group/my-username
            mode: 511

    从volume中读取secret的值

    值得注意的一点是,以文件的形式挂载到容器中的secret,他们的值已经是经过base64解码的了,可以直接读出来使用。

    $ ls /etc/foo/
    username
    password
    $ cat /etc/foo/username
    admin
    $ cat /etc/foo/password
    1f2d1e2e67df

    被挂载的secret内容自动更新

    也就是如果修改一个Secret的内容,那么挂载了该Secret的容器中也将会取到更新后的值,但是这个时间间隔是由kubelet的同步时间决定的。最长的时间将是一个同步周期加上缓存生命周期(period+ttl)

    特例:以subPath形式挂载到容器中的secret将不会自动更新

    以环境变量的形式使用Secret

    1. 创建一个Secret,多个Pod可以引用同一个Secret
    2. 修改pod的定义,定义环境变量并使用env[].valueFrom.secretKeyRef指定secret和相应的key
    3. 修改镜像或命令行,让它们可以读到环境变量
    apiVersion: v1
    kind: Pod
    metadata:
      name: secret-env-pod
    spec:
      containers:
      - name: mycontainer
        image: redis
        env:
          - name: SECRET_USERNAME
            valueFrom:
              secretKeyRef:
                name: mysecret
                key: username
          - name: SECRET_PASSWORD
            valueFrom:
              secretKeyRef:
                name: mysecret
                key: password
      restartPolicy: Never

    容器中读取环境变量,已经是base64解码后的值了:

    $ echo $SECRET_USERNAME
    admin
    $ echo $SECRET_PASSWORD
    1f2d1e2e67df

    使用imagePullSecrets

    创建一个专门用来访问镜像仓库的secret,当创建Pod的时候由kubelet访问镜像仓库并拉取镜像,具体描述文档在 这里

    设置自动导入的imagePullSecrets

    可以手动创建一个,然后在serviceAccount中引用它。所有经过这个serviceAccount创建的Pod都会默认使用关联的imagePullSecrets来拉取镜像,参考文档

    自动挂载手动创建的Secret

    参考文档

    详情

    限制

    需要被挂载到Pod中的secret需要提前创建,否则会导致Pod创建失败

    secret是有命名空间属性的,只有在相同namespace的Pod才能引用它

    单个Secret容量限制的1Mb,这么做是为了防止创建超大的Secret导致apiserver或kubelet的内存耗尽。但是创建过多的小容量secret同样也会耗尽内存,这个问题在将来可能会有方案解决

    kubelet只支持由API server创建出来的Pod中引用secret,使用特殊方式创建出来的Pod是不支持引用secret的,比如通过kubelet的--manifest-url参数创建的pod,或者--config参数创建的,或者REST API创建的。

    通过secretKeyRef引用一个不存在你secret key会导致pod创建失败

    用例

    Pod中的ssh keys

    创建一个包含ssh keys的secret

    kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub

    创建一个Pod,其中的容器可以用volume的形式使用ssh keys

    kind: Pod
    apiVersion: v1
    metadata:
      name: secret-test-pod
      labels:
        name: secret-test
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: ssh-key-secret
      containers:
      - name: ssh-test-container
        image: mySshImage
        volumeMounts:
        - name: secret-volume
          readOnly: true
          mountPath: "/etc/secret-volume"

    Pod中区分生产和测试证书

    创建2种不同的证书,分别用在生产和测试环境

    $ kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
    secret "prod-db-secret" created
    $ kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
    secret "test-db-secret" created

    再创建2个不同的Pod

    apiVersion: v1
    kind: List
    items:
    - kind: Pod
      apiVersion: v1
      metadata:
        name: prod-db-client-pod
        labels:
          name: prod-db-client
      spec:
        volumes:
        - name: secret-volume
          secret:
            secretName: prod-db-secret
        containers:
        - name: db-client-container
          image: myClientImage
          volumeMounts:
          - name: secret-volume
            readOnly: true
            mountPath: "/etc/secret-volume"
    - kind: Pod
      apiVersion: v1
      metadata:
        name: test-db-client-pod
        labels:
          name: test-db-client
      spec:
        volumes:
        - name: secret-volume
          secret:
            secretName: test-db-secret
        containers:
        - name: db-client-container
          image: myClientImage
          volumeMounts:
          - name: secret-volume
            readOnly: true
            mountPath: "/etc/secret-volume"

    两个容器中都会有下列的文件

    /etc/secret-volume/username
    /etc/secret-volume/password

    以“.”开头的key可以产生隐藏文件

    kind: Secret
    apiVersion: v1
    metadata:
      name: dotfile-secret
    data:
      .secret-file: dmFsdWUtMg0KDQo=
    ---
    kind: Pod
    apiVersion: v1
    metadata:
      name: secret-dotfiles-pod
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: dotfile-secret
      containers:
      - name: dotfile-test-container
        image: k8s.gcr.io/busybox
        command:
        - ls
        - "-l"
        - "/etc/secret-volume"
        volumeMounts:
        - name: secret-volume
          readOnly: true
          mountPath: "/etc/secret-volume"

    会在挂载目录下产生一个隐藏文件,/etc/secret-volume/.secret-file

    最佳实践

    风险

  • 相关阅读:
    前端 JS,localStorage/sessionStorage、cookie 及 url 等实现前台数据共享、传输
    webpack 利用Code Splitting 分批打包、按需下载
    React项目之BrowserRouter路由方式之-------生产环境404问题
    React生产环境打包&&后台环境运行(有跨域+无跨域)
    React前台改用HashRouter并解决两个问题
    React路由基础
    React前台404组件页面+路由控制重定向
    react调用方法
    JavaScript 数组遍历方法的对比
    数据可视化相关库说明
  • 原文地址:https://www.cnblogs.com/ksir16/p/9090001.html
Copyright © 2020-2023  润新知