投射数据卷 Projected Volume

注:Projected Volume 是 Kubernetes v1.11 之后的新特性

什么是Projected Volume?
在 k8s 中,有几种特殊的 Volume,它们的意义不是为了存放容器里的数据,也不是用来进行容器和宿主机之间的数据交换。而是为容器提供预先定义好的数据。
从容器的角度来看,这些 Volume 里的信息仿佛是被 k8s "投射"(Project)进入容器当中的。

  • k8s 支持的 Projected Volume 一共有四种:
    1.Secret
    2.ConfigMap
    3.Downward API
    4.ServiceAccountToken

一、Secret 详解

secret用来保存小片敏感数据的k8s资源,例如密码,token,或者秘钥。这类数据当然也可以存放在Pod或者镜像中,但是放在Secret中是为了更方便的控制如何使用数据,并减少暴露的风险。
用户可以创建自己的secret,系统也会有自己的secret。
Pod需要先引用才能使用某个secret

Pod有2种方式来使用secret:
    1. 作为volume的一个域被一个或多个容器挂载
    2. 在拉取镜像的时候被kubelet引用。

內建的Secrets:
    由ServiceAccount创建的API证书附加的秘钥
    k8s自动生成的用来访问apiserver的Secret,所有Pod会默认使用这个Secret与apiserver通信

创建自己的Secret:
    方式1:使用kubectl create secret命令
    方式2:yaml文件创建Secret

命令方式创建secret:
假如某个Pod要访问数据库,需要用户名密码,分别存放在2个文件中:username.txt,password.txt
例子:
# 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

注:opaque:英[əʊˈpeɪk] 美[oʊˈpeɪk] 模糊
 
# kubectl describe secrets/db-user-pass
Name:            db-user-pass
Namespace:       default
Labels:          
Annotations:     

Type:            Opaque

Data
====
password.txt:    12 bytes
username.txt:    5 bytes

get或describe指令都不会展示secret的实际内容,这是出于对数据的保护的考虑,如果想查看实际内容使用命令:
# kubectl get secret db-user-pass -o json

yaml方式创建Secret:
创建一个secret.yaml文件,内容用base64编码
# echo -n 'admin' | base64
YWRtaW4=
# echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm
 
yaml文件内容:
# vim secret.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
创建一个Secret,多个Pod可以引用同一个Secret
修改Pod的定义,在spec.volumes[]加一个volume,给这个volume起个名字,spec.volumes[].secret.secretName记录的是要引用的Secret名字
在每个需要使用Secret的容器中添加一项spec.containers[].volumeMounts[],指定spec.containers[].volumeMounts[].readOnly = true,spec.containers[].volumeMounts[].mountPath要指向一个未被使用的系统路径。
修改镜像或者命令行使系统可以找到上一步指定的路径。此时Secret中data字段的每一个key都是指定路径下面的一个文件名

一个Pod中引用Secret的列子:
# vim pod_use_secret.yaml
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没有被使用,这种方式每个key的调用需要单独用key像username一样调用

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
这里用十进制数256表示0400,可以使用八进制0400

同理可以单独指定某个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(https://kubernetes.io/docs/concepts/storage/volumes/#using-subpath)形式挂载到容器中的secret将不会自动更新

以环境变量的形式使用Secret
创建一个Secret,多个Pod可以引用同一个Secret
修改pod的定义,定义环境变量并使用env[].valueFrom.secretKeyRef指定secret和相应的key
修改镜像或命令行,让它们可以读到环境变量

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

参考文档:https://kubernetes.io/docs/tasks/inject-data-application/podpreset/


限制

需要被挂载到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

1. Secret实验

Secret:
作用是帮你把 Pod 想要访问的加密数据,存放到 Etcd 中。然后,就可以通过在 Pod 的容器里挂载 Volume 的方式,访问到这些 Secret 里保存的信息了。

Secret 典型的使用场景:
存放数据库的 Credential 信息

例子:
# cat 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

定义了一个容器,它声明挂载的 Volume是 projected 类型 ,并不是常见的 emptyDir 或者 hostPath 类型,
而这个 Volume 的数据来源(sources),则是名为 user 和 pass 的 Secret 对象,分别对应的是数据库的用户名和密码。

这里用到的数据库的用户名、密码,正是以 Secret 对象的方式交给 Kubernetes 保存的。

方法1. 使用 kubectl create secret 指令创建Secret对象
# cat ./username.txt
admin
# cat ./password.txt
c1oudc0w!

# kubectl create secret generic user --from-file=./username.txt
# kubectl create secret generic pass --from-file=./password.txt
username.txt 和 password.txt 文件里,存放的就是用户名和密码;而 user 和 pass,则是为 Secret 对象指定的名字。

查看Secret 对象:
# kubectl get secrets
NAME           TYPE                                DATA      AGE
user          Opaque                                1         51s
pass          Opaque                                1         51s

方法2. 通过编写 YAML 文件的方式来创建这个 Secret 对象
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  user: YWRtaW4=
  pass: MWYyZDFlMmU2N2Rm

Secret 对象要求这些数据必须是经过 Base64 转码的,以免出现明文密码的安全隐患。
转码操作:
# echo -n 'admin' | base64
YWRtaW4=
# echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm
注意:像这样创建的 Secret 对象,它里面的内容仅仅是经过了转码,并没有被加密。生产环境中,需要在 Kubernetes 中开启 Secret 的加密插件,增强数据的安全性。

用yaml方式创建的secret调用方法如下:
# cat test-projected-volume.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: test-projected-volume1 
spec:
  containers:
  - name: test-secret-volume1
    image: busybox
    args:
    - sleep
    - "86400"
    volumeMounts:
    - name: mysql-cred
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
  - name: mysql-cred
    secret:
      secretName: mysecret

创建这个 Pod:
# kubectl create -f test-projected-volume.yaml

验证这些 Secret 对象是不是已经在容器里了:
# kubectl exec -it test-projected-volume -- /bin/sh

注意:
    报错:上面这条命令会报错如下
    # kubectl exec -it test-projected-volume /bin/sh
    error: unable to upgrade connection: Forbidden (user=system:anonymous,verb=create,resource=nodes,subresource=proxy)

    解决:绑定一个cluster-admin的权限
    # kubectl create clusterrolebinding system:anonymous   --clusterrole=cluster-admin   --user=system:anonymous
    clusterrolebinding.rbac.authorization.k8s.io/system:anonymous created

/ # ls /projected-volume/
user
pass
/ # cat /projected-volume/user
root
/ # cat /projected-volume/pass
1f2d1e2e67df

结果中看到,保存在 Etcd 里的用户名和密码信息,已经以文件的形式出现在了容器的 Volume 目录里。
而这个文件的名字,就是 kubectl create secret 指定的 Key,或者说是 Secret 对象的 data 字段指定的 Key。

同步更新:
通过挂载方式进入到容器里的 Secret,一旦其对应的 Etcd 里的数据被更新,这些 Volume 里的文件内容,同样也会被更新,kubelet 组件在定时维护这些 Volume。
1. 生成新的密码数据:
# echo -n '111111' | base64
MTExMTEx

2 . 修改数据:
# cat mysecret.yaml 
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  user: YWRtaW4=
  pass: MTExMTEx

3. 更新数据:
# kubectl apply -f mysecret.yaml 
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
secret/mysecret configured

4. 查看对应pod里的数据是否更新:
# kubectl exec -it test-projected-volume1 /bin/sh
/ # cat projected-volume/pass 
111111/ #
注:这个更新可能会有一定的延时。所以在编写应用程序时,在发起数据库连接的代码处写好重试和超时的逻辑,绝对是个好习惯。

查看secret具体的值:
# kubectl get secret mysecret -o json
{
    "apiVersion": "v1",
    "data": {
        "pass": "MTExMTEx",
        "user": "YWRtaW4="
    },
    "kind": "Secret",
    "metadata": {
        "annotations": {
            "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"data\":{\"pass\":\"MTExMTEx\",\"user\":\"YWRtaW4=\"},\"kind\":\"Secret\",\"metadata\":{\"annotations\":{},\"name\":\"mysecret\",\"namespace\":\"default\"},\"type\":\"Opaque\"}\n"
        },
        "creationTimestamp": "2019-01-21T07:31:05Z",
        "name": "mysecret",
        "namespace": "default",
        "resourceVersion": "125857",
        "selfLink": "/api/v1/namespaces/default/secrets/mysecret",
        "uid": "82e20780-1d4e-11e9-baa8-000c29f01606"
    },
    "type": "Opaque"
}

二、ConfigMap详解

ConfigMap 与 Secret 类似,用来存储配置文件的kubernetes资源对象,所有的配置内容都存储在etcd中。

  • ConfigMap 与 Secret 的区别:
    ConfigMap 保存的是不需要加密的、应用所需的配置信息。
    ConfigMap 的用法几乎与 Secret 完全相同:可以使用 kubectl create configmap 从文件或者目录创建 ConfigMap,也可以直接编写 ConfigMap 对象的 YAML 文件。

1. 创建ConfigMap

  • 创建ConfigMap的方式有4种:
    方式1:通过直接在命令行中指定configmap参数创建,即--from-literal
    方式2:通过指定文件创建,即将一个配置文件创建为一个ConfigMap,--from-file=<文件>
    方式3:通过指定目录创建,即将一个目录下的所有配置文件创建为一个ConfigMap,--from-file=<目录>
    方式4:事先写好标准的configmap的yaml文件,然后kubectl create -f 创建
1.1 通过命令行参数--from-literal创建
创建命令:
[root@master yaml]# kubectl create configmap test-config1 --from-literal=db.host=10.5.10.116 --from-literal=db.port='3306'
configmap/test-config1 created

结果如下面的data内容所示: 
[root@master yaml]# kubectl get configmap test-config1 -o yaml
apiVersion: v1
data:
  db.host: 10.5.10.116
  db.port: "3306"
kind: ConfigMap
metadata:
  creationTimestamp: "2019-02-14T08:22:34Z"
  name: test-config1
  namespace: default
  resourceVersion: "7587"
  selfLink: /api/v1/namespaces/default/configmaps/test-config1
  uid: adfff64c-3031-11e9-abbe-000c290a5b8b
1.2 通过指定文件创建
编辑配置文件app.properties内容如下: 
[root@master yaml]# cat app.properties 
property.1 = value-1
property.2 = value-2
property.3 = value-3
property.4 = value-4

[mysqld]
!include /home/tiger/mysql/etc/mysqld.cnf
port = 3306
socket = /home/tiger/mysql/tmp/mysql.sock
pid-file = /tiger/mysql/mysql/var/mysql.pid
basedir = /home/mysql/mysql
datadir = /tiger/mysql/mysql/var
 
创建(可以有多个--from-file):
# kubectl create configmap test-config2 --from-file=./app.properties

结果如下面data内容所示: 
[root@master yaml]# kubectl get configmap test-config2 -o yaml
apiVersion: v1
data:
  app.properties: |
    property.1 = value-1
    property.2 = value-2
    property.3 = value-3
    property.4 = value-4

    [mysqld]
    !include /home/tiger/mysql/etc/mysqld.cnf
    port = 3306
    socket = /home/tiger/mysql/tmp/mysql.sock
    pid-file = /tiger/mysql/mysql/var/mysql.pid
    basedir = /home/mysql/mysql
    datadir = /tiger/mysql/mysql/var
kind: ConfigMap
metadata:
  creationTimestamp: "2019-02-14T08:29:33Z"
  name: test-config2
  namespace: default
  resourceVersion: "8176"
  selfLink: /api/v1/namespaces/default/configmaps/test-config2
  uid: a8237769-3032-11e9-abbe-000c290a5b8b

通过指定文件创建时,configmap会创建一个key/value对,key是文件名,value是文件内容。
如不想configmap中的key为默认的文件名,可以在创建时指定key名字:
# kubectl create configmap game-config-3 --from-file==
1.3 指定目录创建
configs 目录下的config-1和config-2内容如下所示: 
[root@master yaml]# tail configs/config-1
aaa
bbb
c=d
[root@master yaml]# tail configs/config-2
eee
fff
h=k

创建:
# kubectl create configmap test-config3 --from-file=./configs

结果下面data内容所示: 
[root@master yaml]# kubectl get configmap test-config3 -o yaml
apiVersion: v1
data:
  config-1: |
    aaa
    bbb
    c=d
  config-2: |
    eee
    fff
    h=k
kind: ConfigMap
metadata:
  creationTimestamp: "2019-02-14T08:37:05Z"
  name: test-config3
  namespace: default
  resourceVersion: "8808"
  selfLink: /api/v1/namespaces/default/configmaps/test-config3
  uid: b55ffbeb-3033-11e9-abbe-000c290a5b8b

指定目录创建时,configmap内容中的各个文件会创建一个key/value对,key是文件名,value是文件内容。

假如目录中还包含子目录: 
在上一步的configs目录下创建子目录subconfigs,并在subconfigs下面创建两个配置文件,指定目录configs创建名为test-config4的configmap:

# kubectl create configmap test-config4 --from-file=./configs

结果发现和上面没有子目录时一样,说明指定目录时只会识别其中的文件,忽略子目录
1.4 通过事先写好configmap的标准yaml文件创建
yaml文件内容如下: 注意其中一个key的value有多行内容时的写法
[root@master yaml]# cat configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: test-config4
  namespace: default
data:
  cache_host: memcached-gcxt
  cache_port: "11211"
  cache_prefix: gcxt
  my.cnf: |
   [mysqld]
   log-bin = mysql-bin
   haha = hehe

创建:
[root@master yaml]# kubectl apply -f configmap.yaml 
configmap/test-config4 created
   
结果如下面data内容所示: 
[root@master yaml]# kubectl get configmap test-config4 -o yaml
apiVersion: v1
data:
  cache_host: memcached-gcxt
  cache_port: "11211"
  cache_prefix: gcxt
  my.cnf: |
    [mysqld]
    log-bin = mysql-bin
    haha = hehe
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"cache_host":"memcached-gcxt","cache_port":"11211","cache_prefix":"gcxt","my.cnf":"[mysqld]\nlog-bin = mysql-bin\nhaha = hehe\n"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test-config4","namespace":"default"}}
  creationTimestamp: "2019-02-14T08:46:57Z"
  name: test-config4
  namespace: default
  resourceVersion: "9639"
  selfLink: /api/v1/namespaces/default/configmaps/test-config4
  uid: 163fbe1e-3035-11e9-abbe-000c290a5b8b

查看configmap的详细信息:
# kubectl describe configmap

2. 使用ConfigMap

使用ConfigMap有三种方式:
第一种是通过环境变量的方式,直接传递pod。
第二种是通过在pod的命令行下运行的方式。
第三种是使用volume的方式挂载入到pod内。

示例ConfigMap文件:
apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
  special.type: charm
2.1 通过环境变量使用
(1) 使用valueFrom、configMapKeyRef、name、key指定要用的key:
[root@master yaml]# cat testpod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: daocloud.io/library/nginx
      env:
        - name: SPECIAL_LEVEL_KEY   //这里是容器里设置的新变量的名字
          valueFrom:
            configMapKeyRef:
              name: special-config    //这里是来源于哪个configMap
              key: special.how           //configMap里的key
        - name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.type
  restartPolicy: Never

测试:
[root@master yaml]# kubectl exec -it dapi-test-pod /bin/bash
root@dapi-test-pod:/# echo $SPECIAL_TYPE_KEY
charm


(2) 通过envFrom、configMapRef、name使得configmap中的所有key/value对都自动变成环境变量:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: daocloud.io/library/nginx
      envFrom:
      - configMapRef:
          name: special-config
  restartPolicy: Never

这样容器里的变量名称直接使用configMap里的key名:
[root@master yaml]# kubectl exec -it dapi-test-pod -- /bin/bash
root@dapi-test-pod:/# env  
HOSTNAME=dapi-test-pod
NJS_VERSION=1.15.8.0.2.7-1~stretch
NGINX_VERSION=1.15.8-1~stretch
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_PORT=tcp://10.96.0.1:443
PWD=/
special.how=very
HOME=/root
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
TERM=xterm
SHLVL=1
KUBERNETES_SERVICE_PORT=443
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
special.type=charm
KUBERNETES_SERVICE_HOST=10.96.0.1
2.2 在启动命令中引用 (未测试--了解)
在命令行下引用时,需要先设置为环境变量,之后可以通过$(VAR_NAME)设置容器启动命令的启动参数:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: k8s.gcr.io/busybox
      command: [ "/bin/sh","-c","echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: SPECIAL_LEVEL
        - name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: SPECIAL_TYPE
  restartPolicy: Never
2.3 作为volume挂载使用
(1) 把1.4中test-config4所有key/value挂载进来:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-configmap
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-configmap
  template:
    metadata:
      labels:
        app: nginx-configmap
    spec:
      containers:
      - name: nginx-configmap
        image: daocloud.io/library/nginx
        ports:
        - containerPort: 80
        volumeMounts:     
        - name: config-volume4
          mountPath: /tmp/config4
      volumes:
      - name: config-volume4
        configMap:
          name: test-config4

进入容器中/tmp/config4查看: 
[root@master yaml]# kubectl  exec -it nginx-configmap-7447bf77d6-svj2t /bin/bash

root@nginx-configmap-7447bf77d6-svj2t:/# ls /tmp/config4/
cache_host  cache_port  cache_prefix  my.cnf

root@nginx-configmap-7447bf77d6-svj2t:/# cat /tmp/config4/cache_host 
memcached-gcxt

可以看到,在config4文件夹下以每一个key为文件名value为值创建了多个文件。


(2) 假如不想以key名作为配置文件名可以引入items 字段,在其中逐个指定要用相对路径path替换的key:
     volumes:
      - name: config-volume4
        configMap:
          name: test-config4
          items:
          - key: my.cnf    //原来的key名
            path: mysql-key
          - key: cache_host   //原来的key名
            path: cache-host

备注:
删除configmap后原pod不受影响;然后再删除pod后,重启的pod的events会报找不到cofigmap的volume;
pod起来后再通过kubectl edit configmap …修改configmap,过一会pod内部的配置也会刷新。
在容器内部修改挂进去的配置文件后,过一会内容会再次被刷新为原始configmap内容


(3) 还可以为以configmap挂载进的volume添加subPath字段:
        volumeMounts:
        - name: config-volume5
          mountPath: /tmp/my
          subPath: my.cnf
        - name: config-volume5
          mountPath: /tmp/host
          subPath: cache_host
        - name: config-volume5
          mountPath: /tmp/port
          subPath: cache_port
        - name: config-volume5
          mountPath: /tmp/prefix
          subPath: cache_prefix
      volumes:
      - name: config-volume5
        configMap:
          name: test-config4
          
注意在容器中的形式与(2)中的不同,(2)中是个链接,链到..data/。 

备注:
删除configmap后原pod不受影响;然后再删除pod后,重启的pod的events会报找不到cofigmap的volume。
pod起来后再通过kubectl edit configmap …修改configmap,pod内部的配置也会自动刷新。
在容器内部修改挂进去的配置文件后,内容可以持久保存,除非杀掉再重启pod才会刷回原始configmap的内容。
subPath必须要与configmap中的key同名。
mountPath如/tmp/prefix: 
<1>当/tmp/prefix不存在时(备注:此时/tmp/prefix和/tmp/prefix/无异),会自动创建prefix文件并把value写进去; 
<2>当/tmp/prefix存在且是个文件时,里面内容会被configmap覆盖; 
<3>当/tmp/prefix存在且是文件夹时,无论写/tmp/prefix还是/tmp/prefix/都会报错。

3. configmap的热更新

更新 ConfigMap 后:

使用该 ConfigMap 挂载的 Env 不会同步更新
使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新
ENV 是在容器启动的时候注入的,启动之后 kubernetes 就不会再改变环境变量的值,且同一个 namespace 中的 pod 的环境变量是不断累加的,参考 Kubernetes中的服务发现与docker容器间的环境变量传递源码探究。为了更新容器中使用 ConfigMap 挂载的配置,可以通过滚动更新 pod 的方式来强制重新挂载 ConfigMap,也可以在更新了 ConfigMap 后,先将副本数设置为 0,然后再扩容。

三、Downward API

用于在容器中获取 POD 的基本信息,kubernetes原生支持。

  • Downward API提供了两种方式用于将 POD 的信息注入到容器内部:
    1.环境变量:
    用于单个变量,可以将 POD 信息和容器信息直接注入容器内部。
    2.Volume挂载:
    将 POD 信息生成为文件,直接挂载到容器内部中去。

1. 环境变量的方式

1.1 将Pod信息注入为环境变量
通过Downward API来将 POD 的 IP、名称以及所对应的 namespace 注入到容器的环境变量中去,然后在容器中打印全部的环境变量来进行验证

使用fieldRef获取 POD 的基本信息:
# cat test-env-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
    name: test-env-pod
    namespace: kube-system
spec:
    containers:
    - name: test-env-pod
      image: daocloud.io/library/nginx:latest
      env:
      - name: POD_NAME
        valueFrom:
          fieldRef:
            fieldPath: metadata.name
      - name: POD_NAMESPACE
        valueFrom:
          fieldRef:
            fieldPath: metadata.namespace
      - name: POD_IP
        valueFrom:
          fieldRef:
            fieldPath: status.podIP

注意: POD 的 name 和 namespace 属于元数据,是在 POD 创建之前就已经定下来了的,所以使用 metata 获取就可以了,但是对于 POD 的 IP 则不一样,因为POD IP 是不固定的,POD 重建了就变了,它属于状态数据,所以使用 status 去获取。

创建上面的 POD:
# kubectl create -f test-env-pod.yaml
pod "test-env-pod" created

POD 创建成功后,查看:
[root@master yaml]# kubectl exec -it test-env-pod -- /bin/bash -n kube-system
root@test-env-pod:/# env | grep POD
POD_IP=172.30.19.24
POD_NAME=test-env-pod
POD_NAMESPACE=kube-system
1.2 将容器资源信息注入为容器环境变量
# cat test-env-pod1.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod3
spec:
  containers:
    - name: dapi-test-container-nginx
      image: daocloud.io/library/nginx:latest
      resources:
        requests:
          memory: "32Mi"
          cpu: "125m"
        limits:
          memory: "64Mi"
          cpu: "250m"
      env:
        - name: MY_CPU_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: dapi-test-container-nginx
              resource: requests.cpu
        - name: MY_CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: dapi-test-container-nginx
              resource: limits.cpu
        - name: MY_MEM_REQUEST
          valueFrom:
            resourceFieldRef:
              containerName: dapi-test-container-nginx
              resource: requests.memory
        - name: MY_MEM_LIMIT
          valueFrom:
            resourceFieldRef:
              containerName: dapi-test-container-nginx
              resource: limits.memory

创建上面的 POD:
# kubectl create -f test-env-pod1.yaml
pod "test-env-pod1" created

# kubectl exec -it test-env-pod1 -- /bin/bash
root@test-env-pod1:/# echo $MY_MEM_REQUEST
33554432
root@test-env-pod1:/# echo $MY_MEM_LIMIT
67108864
root@test-env-pod1:/# echo $MY_CPU_LIMIT
1
root@test-env-pod1:/# echo $MY_CPU_REQUEST
1

   注意 valueFrom这种特殊的DownwardAPI语法,目前 resourceFieldRef 可以将容器的资源请求和资源限制等信息映射为容器内部的环境变量:
   requests.cpu:     容器的 CPU 的请求指
   limits.cpu        容器的 CPU 的限制值
   requests.memory   容器的 MEM 的请求指
   limits.memory     容器的 MEM     的限制值

2. Volume挂载

通过Downward API将 POD 的 Label、Annotation 等信息通过 Volume 挂载到容器的某个文件中去,然后在容器中打印出该文件的值来验证。

# test-volume-pod.yaml
apiVersion: v1
kind: Pod
metadata:
    name: test-volume-pod
    namespace: kube-system
    labels:
        k8s-app: test-volume
        node-env: test
    annotations:
        build: test
        own: qikqiak
spec:
    containers:
    - name: test-volume-pod-container
      image: daocloud.io/library/nginx:latest
      volumeMounts:
      - name: podinfo
        mountPath: /etc/podinfo
    volumes:
    - name: podinfo
      downwardAPI:
        items:
        - path: "labels"
          fieldRef:
            fieldPath: metadata.labels
        - path: "annotations"
          fieldRef:
            fieldPath: metadata.annotations

将元数据 labels 和 annotaions 以文件的形式挂载到了/etc/podinfo目录下,创建上面的 POD :
# kubectl create -f test-volume-pod.yaml
pod "test-volume-pod" created

在实际应用中,如果你的应用有获取 POD 的基本信息的需求,就可以利用Downward API来获取基本信息,然后编写一个启动脚本或者利用initContainer将 POD 的信息注入到容器中去,然后在自己的应用中就可以正常的处理相关逻辑了。

3. 目前 Downward API 支持的字段

3.1 使用 fieldRef 可以声明使用:

spec.nodeName - 宿主机名字
status.hostIP - 宿主机 IP
metadata.name - Pod 的名字
metadata.namespace - Pod 的 Namespace
status.podIP - Pod 的 IP
spec.serviceAccountName - Pod 的 Service Account 的名字
metadata.uid - Pod 的 UID
metadata.labels[''] - 指定 的 Label 值
metadata.annotations[''] - 指定 的 Annotation 值
metadata.labels - Pod 的所有 Label
metadata.annotations - Pod 的所有 Annotation

3.2 使用 resourceFieldRef 可以声明使用:

容器的 CPU limit
容器的 CPU request
容器的 memory limit
容器的 memory request
上面这个列表的内容,随着 Kubernetes 项目的发展肯定还会不断增加。所以这里列出来的信息仅供参考,在使用 Downward API 时,还是要记得去查阅一下官方文档。

注意:Downward API 能够获取到的信息,一定是 Pod 里的容器进程启动之前就能够确定下来的信息。而如果你想要获取 Pod 容器运行后才会出现的信息,比如,容器进程的 PID,那就肯定不能使用 Downward API 了,而应该考虑在 Pod 里定义一个 sidecar 容器。

Secret、ConfigMap,以及 Downward API 这三种 Projected Volume 定义的信息,大多还可以通过环境变量的方式出现在容器里。但是,通过环境变量获取这些信息的方式,不具备自动更新的能力。一般情况下,建议使用 Volume 文件的方式获取这些信息。

四、Service Account

官方文档地址:https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/

k8s中提供了良好的多租户认证管理机制,如RBAC、ServiceAccount还有各种Policy等。

什么是 Service Account ?

当用户访问集群(例如使用kubectl命令)时,apiserver 会将用户认证为一个特定的 User Account(目前通常是admin,除非系统管理员自定义了集群配置)。

Pod 容器中的进程也可以与 apiserver 联系。 当它们在联系 apiserver 的时候,它们会被认证为一个特定的 Service Account(例如default)。

使用场景:

Service Account它并不是给kubernetes集群的用户使用的,而是给pod里面的进程使用的,它为pod提供必要的身份认证。

Service account与User account不同之处:

  1. User account是为人设计的,而service account则是为Pod中的进程调用Kubernetes API或其他外部服务而设计的
  2. User account是跨namespace的,而service account则是仅局限它所在的namespace;
  3. 每个namespace都会自动创建一个default service account
  4. Token controller检测service account的创建,并为它们创建secret
  5. 开启ServiceAccount Admission Controller后:
    5.1 每个Pod在创建后都会自动设置spec.serviceAccount为default(除非指定了其他ServiceAccout)
    5.2 验证Pod引用的service account已经存在,否则拒绝创建
    5.3 如果Pod没有指定ImagePullSecrets,则把service account的ImagePullSecrets加到Pod中
    5.4 每个container启动后都会挂载该service account的token和ca.crt到/var/run/secrets/kubernetes.io/serviceaccount/
# kubectl exec nginx-3137573019-md1u2 ls /run/secrets/kubernetes.io/serviceaccount
ca.crt
namespace
token

查看系统的config配置:
这里用到的token就是被授权过的SeviceAccount账户的token,集群利用token来使用ServiceAccount账户

# cat /root/.kube/config

1. 默认的 Service Account

1.1 默认在 pod 中使用自动挂载的 service account 凭证来访问 API,如 Accessing the Cluster 中所描述。Accessing the Cluster(中文)
当创建 pod 的时候,如果没有指定一个 service account,系统会自动在与该pod 相同的 namespace 下为其指派一个default service account,并且使用默认的 Service Account 访问 API server。
例如:
  获取刚创建的 pod 的原始 json 或 yaml 信息,将看到spec.serviceAccountName字段已经被设置为 default。
  # kubectl get pods podename -o yaml
1.2 取消为 service account 自动挂载 API 凭证

注:因为我们平时用不到取消默认service account,所以本实验未测试取消之后的效果,你如果感兴趣可以自行测试

Service account 是否能够取得访问 API 的许可取决于你使用的 授权插件和策略

在 1.6 以上版本中,可以选择取消为 service account 自动挂载 API 凭证,只需在 service account 中设置 automountServiceAccountToken: false:
apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
automountServiceAccountToken: false
...


只取消单个 pod 的 API 凭证自动挂载:
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  serviceAccountName: build-robot
  automountServiceAccountToken: false
  ...


pod 设置中的优先级更高:
    如果在 pod 和 service account 中同时设置了 automountServiceAccountToken ,pod 设置中的优先级更高。

2. ServiceAccount应用实例 01

Service Account(服务账号)测试示例
因为平时系统会使用默认service account,我们不需要自己创建,感觉不到service account的存在,本实验是使用自己手动创建的service account

1、创建serviceaccount
# kubectl  create  serviceaccount  mysa
 
2、查看mysa
# kubectl  describe sa mysa
Name:                mysa
Namespace:           default
Labels:              
Annotations:         
Image pull secrets:  
Mountable secrets:   mysa-token-2zjlp
Tokens:              mysa-token-2zjlp
Events:              

3、查看mysa自动创建的secret
# kubectl  get secret
NAME                  TYPE                                  DATA      AGE
default-token-29dpx   kubernetes.io/service-account-token   3         119d
mysa-token-2zjlp      kubernetes.io/service-account-token   3         1m
 
4、使用mysa的sa资源配置pod
# cat mysa-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: my-pod
      
spec:
  containers:
  - name: my-pod
    image: daocloud.io/library/nginx
    ports:
    - name: http
      containerPort: 80
  serviceAccountName: mysa

5、导入
# kubectl apply -f  mysa-pod.yaml
 
6、查看
# kubectl  describe pod nginx-pod
 
7、查看使用的token和secret(使用的是mysa的token)
# kubectl  get  pod nginx-pod  -o jsonpath={".spec.volumes"}

3. ServiceAccount应用实例 02

使用 Service Account 作为用户权限管理配置 kubeconfig

创建服务账号(serviceAccount):
# kubectl create serviceaccount sample-sc
 
查看 serviceaccount 账号:
# kubectl get serviceaccount sample-sc -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2019-02-15T02:49:31Z"
  name: sample-sc
  namespace: default
  resourceVersion: "37256"
  selfLink: /api/v1/namespaces/default/serviceaccounts/sample-sc
  uid: 518ad3d6-30cc-11e9-abbe-000c290a5b8b
secrets:
- name: sample-sc-token-4brlw

查看sample-sc帐号的token:
因为在使用 serviceaccount 账号配置 kubeconfig 的时候需要使用到 sample-sc 的 token, 该 token 保存在该 serviceaccount 保存的 secret 中

其中 {data.token} 就是用户 token 的 base64 编码,之后配置 kubeconfig 的时候将会用到它
# kubectl get secret sample-sc-token-4brlw -o yaml
apiVersion: v1
data:
  ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1ESXhOREEyTlRNME5Gb1hEVEk1TURJeE1UQTJOVE0wTkZvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTHdmCjRIZEhod2hPRnVLNlVBRWcxekhzSGFBZS9VUUh5VGdPOG5LZ3FaK1BQQU9iOVc2dDZoeG1MKzBscGNFSS9xWFkKT3JFY2l5cVpnT0piV05jSUFYaCs3WmxDdk9BdzZpYmxmQy9oMUw1cWZGWkwvVXlNb3pUSWFVRUsweEV1bFNFMAp6SVJCN3R5aUNLSy9RODJEakk3NEVwQmxyUTE4UkYrQytrR3Fjb0d4a0FXZzZRU2x0cmJCTGRXQlc3NEdBVkI0ClZSRFk4S3c0Mm96WWZZRFErZ09BMjd4VnNaQWJRRDZUWmVTU0RHOVVyaU9NOVhmMVQ3ZytQY3hoMHZCL1ROcGgKdXJNL2tNUTM5eHNqazFEUms4L0tLQnlNZE9xd1lxZ3RwWUdvQnZVMDZnK1ZvL2tkWXJ2WnFoTy9oVmFBem00dwo1YmNsYUx5WDlBc1RZeFhYQXIwQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFINW5YOXJBS2lDSGcvOWNVT1dXMWdRK2hZc1gKb2Flakt0TnAvbmJKTnJITGZrV3hWTFc4eU5Xem1BWnYzcDFZOENCMDF2OEtGNEdrVy9CSk81aVpXRTVvSVZuVQo2Nkp5Y1doTVV2RmFTQWN5MnJUVXpvcWh5YnVDaVJsSS9ScUVZNjQvbVZVdjFTK1dndzBaYSszK1FpSC9LNHVUCmVzcnhvQUdHRnBpd3JCQ0FGMjZxZHRuMTdjRTJRcHNHcFlyY1hOZ3BlSHpaK2ZLMkZuUzBiYkJMb082YlA4ZGYKQlVPZldvb1JteWUxdmtHWWlKcFZUajFDcDBKNkcxazc3NWZGMUlkQmNrelhyQmF2N0xkdml5S2dtRFN1ODFYegpuZ3AzMWx1bE9wdXVETHRiM0svVTZUTHNRVWlHN2hlOUg3eVMybk5tVnA0cWRIRC9MMzJiUU40dDEzRT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
  namespace: ZGVmYXVsdA==
  token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklpSjkuZXlKcGMzTWlPaUpyZFdKbGNtNWxkR1Z6TDNObGNuWnBZMlZoWTJOdmRXNTBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5dVlXMWxjM0JoWTJVaU9pSmtaV1poZFd4MElpd2lhM1ZpWlhKdVpYUmxjeTVwYnk5elpYSjJhV05sWVdOamIzVnVkQzl6WldOeVpYUXVibUZ0WlNJNkluTmhiWEJzWlMxell5MTBiMnRsYmkwMFluSnNkeUlzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVnlkbWxqWlMxaFkyTnZkVzUwTG01aGJXVWlPaUp6WVcxd2JHVXRjMk1pTENKcmRXSmxjbTVsZEdWekxtbHZMM05sY25acFkyVmhZMk52ZFc1MEwzTmxjblpwWTJVdFlXTmpiM1Z1ZEM1MWFXUWlPaUkxTVRoaFpETmtOaTB6TUdOakxURXhaVGt0WVdKaVpTMHdNREJqTWprd1lUVmlPR0lpTENKemRXSWlPaUp6ZVhOMFpXMDZjMlZ5ZG1salpXRmpZMjkxYm5RNlpHVm1ZWFZzZERwellXMXdiR1V0YzJNaWZRLmRQdkJFQzV1djhlTHZfSFNfQjRLd3phYkx2TUdjZzVmU3dXX19GaXNYS3FSTmRuU0dFUjBFNE4zUDRCd0UyY0xJUWloRlNSNnhOc3ZpMU5qb0h0QTk1Qjd6UmNFRFUzaVBjdXN3cWJNN1BINWQxcnBmWHVjSkNlaWpZcnRzVXprWEJ0TGVHRDRYMlpDdGFzYjU1UjlaWE1ldXZmRllQdzhzejBWWmh4a1RabjVrRDA4V2VnZEFnTWIyR3ZzX0psMWVxZWd5WF9zUm9sSUFKa3o2QnQ5SW8tV0lfcDhxeFZ3SEFhZlpuazBnVW5penV0eGFHZ1RjaVRZNk9FTUxmNVhnZFp2U0taTUpVY0FKeENOUWNmYmRMckRtSXdYWDBoMHVjM2tQTi14OVNZMjFMZlNlQV9zeHRiSmkzSnhlMjV2cFg4YmhYWGl4Y25xRWgwR19ITjNOdw==
kind: Secret
metadata:
  annotations:
    kubernetes.io/service-account.name: sample-sc
    kubernetes.io/service-account.uid: 518ad3d6-30cc-11e9-abbe-000c290a5b8b
  creationTimestamp: "2019-02-15T02:49:31Z"
  name: sample-sc-token-4brlw
  namespace: default
  resourceVersion: "37255"
  selfLink: /api/v1/namespaces/default/secrets/sample-sc-token-4brlw
  uid: 518d0a75-30cc-11e9-abbe-000c290a5b8b
type: kubernetes.io/service-account-token

创建角色(这里以后是rbac权限管理的内容了)
比如想创建一个只可以查看集群deployments,services,pods 相关的角色,使用如下配置

apiVersion: rbac.authorization.k8s.io/v1
## 这里也可以使用 Role
kind: ClusterRole
metadata:
  name: mofang-viewer-role
  labels:
    from: mofang
rules:
- apiGroups:            # 空字符串""表明使用core API group
  - ""
  resources:
  - pods
  - pods/status
  - pods/log
  - services
  - services/status
  - endpoints
  - endpoints/status
  - deployments
  verbs:
  - get
  - list
  - watch

创建角色绑定
apiVersion: rbac.authorization.k8s.io/v1
## 这里也可以使用 RoleBinding
kind: ClusterRoleBinding
metadata:
  name: sample-role-binding
  labels:
    from: mofang
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: mofang-viewer-role    #这里即绑定上面创建的clusterrole
subjects:
- kind: ServiceAccount        #将clusterrole绑定到这个服务账户上
  name: sample-sc
  namespace: default
  
注意上面配置中的注意在复制的时候要删除干净,包括空格,否则出错

使用service account
    利用创建的 serviceaccount 配置kubeconfig来访问集群:
     经过以上的步骤,就可以利用最开始创建的 serviceaccount 配置kubeconfig来访问集群了, 同时可以动态更改 ClusterRole 的授权来及时控制某个账号的权限(这也是使用 serviceaccount 的好处);

配置如下:
apiVersion: v1
clusters:
- cluster:
    ## 这个是集群的 TLS 证书,与授权无关,使用统一的就可以
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvekNDQW1pZ0F3SUJBZ0lEQnN0WU1BMEdDU3FHU0liM0RRRUJDd1VBTUdJeEN6QUpCZ05WQkFZVEFrTk8KTVJFd0R3WURWUVFJREFoYWFHVkthV0Z1WnpFUk1BOEdBMVVFQnd3SVNHRnVaMXBvYjNVeEVEQU9CZ05WQkFvTQpCMEZzYVdKaFltRXhEREFLQmdOVkJBc01BMEZEVXpFTk1Bc0dBMVVFQXd3RWNtOXZkREFlRncweE9EQTFNamt3Ck16UXdNREJhRncwek9EQTFNalF3TXpRMU5UbGFNR294S2pBb0JnTlZCQW9USVdNMlpUbGpObUpqWVRjellqRTAKWTJNMFlXRTNPVE13WWpNNE5ERXhORFpqWVRFUU1BNEdBMVVFQ3hNSFpHVm1ZWFZzZERFcU1DZ0dBMVVFQXhNaApZelpsT1dNMlltTmhOek5pTVRSall6UmhZVGM1TXpCaU16ZzBNVEUwTm1OaE1JR2ZNQTBHQ1NxR1NJYjNEUUVCCkFRVUFBNEdOQURDQmlRS0JnUURMY0VaYnJwK0FrS1o0dThOSVUzbmNoVThiQTEyeEpHSkkzOXF1N3hoUWxHeUcKZmpBMWp1dXhxUzJoTi9ManAzbVc2R0hpbTN3aUl3Y3VZS1Q3dEY5b1R6OCtPOEFDNkdiemRYTEgvVFBNa0JnZgo5U1hoR2h3WGd2SUxvdjNudmVLUzNESXFTdSt5L04rWG4zOE45bndIcXpLSnZYTVE5a0lpQm5NeDBWeXNIUUlECkFRQUJvNEc2TUlHM01BNEdBMVVkRHdFQi93UUVBd0lDckRBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUI4R0ExVWQKSXdRWU1CYUFGSVZhLzkwanpTVnZXRUZ2bm0xRk9adFlmWFgvTUR3R0NDc0dBUVVGQndFQkJEQXdMakFzQmdncgpCZ0VGQlFjd0FZWWdhSFIwY0RvdkwyTmxjblJ6TG1GamN5NWhiR2w1ZFc0dVkyOXRMMjlqYzNBd05RWURWUjBmCkJDNHdMREFxb0NpZ0pvWWthSFIwY0RvdkwyTmxjblJ6TG1GamN5NWhiR2w1ZFc0dVkyOXRMM0p2YjNRdVkzSnMKTUEwR0NTcUdTSWIzRFFFQkN3VUFBNEdCQUpYVGlYSW9DVVg4MUVJTmJ1VFNrL09qdEl4MzRyRXR5UG5PK0FTagpqSzNpNHd3UFEwS3kwOGZOU25TZnhDUTJ4RjU1MjE1U29TMzFTd0x6WUlUSnVYWkE3bFdPb1FTVFkvaUFMdVBCCi9rM0JsOE5QY2Z6OEY1eVk3L25jU3pYNHBNeDE4cjBjb09MTWJmWlRRcm1IcGZDTndtZGNCZUIrQm5EUkxQSkYKaDNJRAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
    server: https://192.168.1.205:6443
  name: beta
contexts:
- context:
    cluster: beta
    user: beta-viewer
  name: beta-viewer
current-context: beta-viewer
kind: Config
preferences: {}
users:
- name: beta-viewer
  user:
    ## 这个是我们在创建 serviceaccount 生成相关 secret 之后的 data.token 的 base64 解码字符,它本质是一个 jwt token
    token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklpSjkuZXlKcGMzTWlPaUpyZFdKbGNtNWxkR1Z6TDNObGNuWnBZMlZoWTJOdmRXNTBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5dVlXMWxjM0JoWTJVaU9pSmtaV1poZFd4MElpd2lhM1ZpWlhKdVpYUmxjeTVwYnk5elpYSjJhV05sWVdOamIzVnVkQzl6WldOeVpYUXVibUZ0WlNJNkluTmhiWEJzWlMxell5MTBiMnRsYmkwMFluSnNkeUlzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVnlkbWxqWlMxaFkyTnZkVzUwTG01aGJXVWlPaUp6WVcxd2JHVXRjMk1pTENKcmRXSmxjbTVsZEdWekxtbHZMM05sY25acFkyVmhZMk52ZFc1MEwzTmxjblpwWTJVdFlXTmpiM1Z1ZEM1MWFXUWlPaUkxTVRoaFpETmtOaTB6TUdOakxURXhaVGt0WVdKaVpTMHdNREJqTWprd1lUVmlPR0lpTENKemRXSWlPaUp6ZVhOMFpXMDZjMlZ5ZG1salpXRmpZMjkxYm5RNlpHVm1ZWFZzZERwellXMXdiR1V0YzJNaWZRLmRQdkJFQzV1djhlTHZfSFNfQjRLd3phYkx2TUdjZzVmU3dXX19GaXNYS3FSTmRuU0dFUjBFNE4zUDRCd0UyY0xJUWloRlNSNnhOc3ZpMU5qb0h0QTk1Qjd6UmNFRFUzaVBjdXN3cWJNN1BINWQxcnBmWHVjSkNlaWpZcnRzVXprWEJ0TGVHRDRYMlpDdGFzYjU1UjlaWE1ldXZmRllQdzhzejBWWmh4a1RabjVrRDA4V2VnZEFnTWIyR3ZzX0psMWVxZWd5WF9zUm9sSUFKa3o2QnQ5SW8tV0lfcDhxeFZ3SEFhZlpuazBnVW5penV0eGFHZ1RjaVRZNk9FTUxmNVhnZFp2U0taTUpVY0FKeENOUWNmYmRMckRtSXdYWDBoMHVjM2tQTi14OVNZMjFMZlNlQV9zeHRiSmkzSnhlMjV2cFg4YmhYWGl4Y25xRWgwR19ITjNOdw==

你可能感兴趣的:(投射数据卷 Projected Volume)