k8s投射数据卷

投射数据

一、Secret详解

1、secret介绍

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

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

Pod需要先引用才能使用某个secret

Pod有2种方式来使用secret:

  1. 作为volume的一个域被一个或多个容器挂载

  2. 在拉取镜像的时候被kubelet引用。

2、secret类型

內建的Secrets:

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

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

创建自己的Secret:

方式1:使用kubectl create secret命令

方式2:yaml文件创建Secret

3、创建secret

3.1、命令方式创建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:      <none>

Annotations:   <none>

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

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

# kubectl get secret db-user-pass -o json 
3.2、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

4、使用Secret

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

4.1、在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:3.2.9

   volumeMounts:

   - name: foo

     mountPath: "/etc/foo"

     readOnly: true

 volumes:

 - name: foo

   secret:

     secretName: mysecret

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

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

4.2、映射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一样调用

4.3、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
4.4、secret扩展内容

被挂载的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

修改镜像或命令行,让它们可以读到环境变量

变量名:admin(secretkey(mysecret–>username=admin))

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

4.5、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中。

1、与 Secret 的区别

ConfigMap 保存的是不需要加密的、应用所需的配置信息。

ConfigMap 的用法几乎与 Secret 完全相同:可以使用 kubectl create configmap 从文件或者目录创建 ConfigMap,也可以直接编写 ConfigMap 对象的 YAML 文件。

2、创建ConfigMap

创建ConfigMap的方式有4种:

方式1:通过直接在命令行中指定configmap参数创建,即–from-literal

方式2:通过指定文件创建,即将一个配置文件创建为一个ConfigMap,–from-file=<文件>

方式3:通过指定目录创建,即将一个目录下的所有配置文件创建为一个ConfigMap,–from-file=<目录>

方式4:事先写好标准的configmap的yaml文件,然后kubectl create -f 创建

2.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
2.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/wing/mysql/etc/mysqld.cnf
port = 3306
socket = /home/wing/mysql/tmp/mysql.sock
pid-file = /wing/mysql/mysql/var/mysql.pid
basedir = /home/mysql/mysql
datadir = /wing/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/wing/mysql/etc/mysqld.cnf
  port = 3306
  socket = /home/wing/mysql/tmp/mysql.sock
  pid-file = /wing/mysql/mysql/var/mysql.pid
  basedir = /home/mysql/mysql
  datadir = /wing/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=<my-key-name>=<path-to-file> 
2.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 

结果发现和上面没有子目录时一样,说明指定目录时只会识别其中的文件,忽略子目录

2.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

3、使用ConfigMap

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

示例ConfigMap文件:

apiVersion: v1

kind: ConfigMap

metadata:

 name: special-config

 namespace: default

data:

 special.how: very

 special.type: charm
3.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
3.2 在启动命令中引用

在容器内执行命令时引用,需要先设置为环境变量,之后可以通过$(VAR_NAME)设置容器启动命令的启动参数:

注:

这个容器在执行完成之后因为没有运行的bash和tty,所以会退出,pod会处于completed状态

可以用共享volume的方式查看最终的结果

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

下面这个例子可以看到结果:

# cat testpod.yaml 
apiVersion: v1
kind: Pod
metadata:
 name: testpod1
spec:
 containers:
  - name: test-container
    image: daocloud.io/library/nginx:1.7.9
    volumeMounts:
    - name: shared-data
      mountPath: /pod-data 
  - name: test-container1
    image: daocloud.io/library/nginx:1.7.9
    volumeMounts:
    - name: shared-data
      mountPath: /data
    command: [ "touch" ]
    args: [ "/data/a.txt" ]
 volumes:
 - name: shared-data
   hostPath:    
    path: /data
3.3 作为volume挂载使用

(1) 把1.4中test-config4所有key/value挂载进来:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: nginx-configmap
spec:
 replicas: 1
 template:
  metadata:
   labels:
    app: nginx-configmap
  spec:
   containers:
   - name: nginx-configmap
     image: 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/都会报错。

例:使用configmap替换nginx的配置文件

# cat configpod.yaml
apiVersion: v1

kind: Pod

metadata:

  name: confignginx

  labels:

   app: nginx

spec:

  containers:

   - name: confignginx

     image: daocloud.io/library/nginx:alpine

     ports:

      - containerPort: 80

     volumeMounts:

      - name: configvolume

        mountPath: /etc/nginx/nginx.conf

        subPath: nginx.conf

  volumes:

   - name: configvolume

     configMap:

      name: configmaptest

4、configmap的热更新

更新 ConfigMap 后

1、使用该 ConfigMap 挂载的 Env 不会同步更新

ENV 是在容器启动的时候注入的,启动之后 kubernetes 就不会再改变环境变量的值,且同一个 namespace 中的 pod 的环境变量是不断累加的。

2、使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新

​ 注意:使用subPath的方式挂载的volume是不能自动更新的

​ 为了更新容器中使用 ConfigMap 挂载的配置,可以通过滚动更新 pod 的方式来强制重新挂载 ConfigMap,也可以在更新了 ConfigMap 后,先将副本数设置为 0,然后再扩容。

三、Downward API

1、Downward API介绍

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

Downward API提供了两种方式用于将 POD 的信息注入到容器内部:

环境变量:

用于单个变量,可以将 POD 信息和容器信息直接注入容器内部。

Volume挂载:

将 POD 信息生成为文件,直接挂载到容器内部中去。

2、环境变量方式使用DownwardAPI

通过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:1.13.0-alpine

    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 去获取:

所有基本信息可以使用下面的方式去查看(describe方式看不出来):

# kubectl  get pod first-pod -o yaml 

创建上面的 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

3、Volume挂载方式使用DownwardAPI

通过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:1.13.0-alpine
    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" create

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

目前 Downward API 支持的字段:

  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

  1. 使用 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 文件的方式获取这些信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值