在Kubernetes中有一些资源对象可以以存储卷的形式挂载为容器内的目录或文件,目前包括ConfigMap、Secret、Downward API、ServiceAccountToken、Projected Volume。

1. ConfigMap

ConfigMap主要保存应用程序所需的配置文件,并且通过Volume形式挂载到容器内的文件系统中,供容器内的应用程序读取。

apiVersion: v1
kind: ConfigMap
metadata:
  name: cm-appconfigfiles
data:
  key-serverxml: |
    <?xml version='1.0' encoding='utf-8'?>
  key-loggingproperties: "handlers
    ......
    = 4host-manager.org.apache.juli.FileHandler\r\n\r\n"
---
apiVersion: v1
kind: Pod
metadata:
  name: cm-test-app
spec:
  containers:
  - name: cm-test-app
    image: kubeguide/tomcat-app:v1
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: serverxml                   # 引用Volume的名称
      mountPath: /configfiles           # 挂载到容器内的目录下
  volumes:
  - name: serverxml                     # 定义Volume的名称
    configMap:
      name: cm-appconfigfiles           # 使用ConfigMap的“cm-appconfigfiles”
      items:
      - key: key-serverxml              # key=key-serverxml
        path: server.xml                # 挂载为 server.xml 文件
      - key: key-loggingproperties      # key=key-loggingproperties
        path: logging.properties        # 挂载为 logging.properties文件

在Pod的YAML配置中,将ConfigMap设置为一个Volume,然后在容器中通过volumeMounts将ConfigMap类型的Volume挂载到 /configfiles 目录下

Pod创建成功之后,进入容器内查看,可以看到在/configfiles目录下存在server.xml和logging.properties文件:

[root@master1 ~]# kubectl describe configmaps cm-appconfigfiles 
Name:         cm-appconfigfiles
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
key-loggingproperties:
----
handlers ...... = 4host-manager.org.apache.juli.FileHandler


key-serverxml:
----
<?xml version='1.0' encoding='utf-8'?>

Events:  <none>
[root@master1 ~]#
[root@master1 ~]# kubectl exec -it cm-test-app -- bash
root@cm-test-app:/usr/local/tomcat# ls /configfiles
logging.properties  server.xml
root@cm-test-app:/usr/local/tomcat# cat /configfiles/server.xml 
<?xml version='1.0' encoding='utf-8'?>
root@cm-test-app:/usr/local/tomcat# 
root@cm-test-app:/usr/local/tomcat# cat /configfiles/logging.properties 
handlers ...... = 4host-manager.org.apache.juli.FileHandler

ConfigMap中配置的内容如果是UTF-8编码的字符,则将被系统认为是文件文本。如果是其他字符,则系统将以二进制数据格式进行保存(设置为binaryData字段)。

2. Secret

与ConfigMap的用法类似,在Pod的YAML配置中可以将Secret设置为一个Volume,然后在容器内通过volumeMouns将Secret类型的Volume挂载到/etc/foo目录下。Secret中的data域各子域的值必须为BASE64编码值。

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  password: dmFsdWUtMg0K
  username: dmFsdWUtMg0K
---
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mycontainer
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret

创建资源后查看该secret,并进入Pod那个查看资源:

[root@master1 ~]# kubectl describe secrets mysecret 
Name:         mysecret
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
username:  9 bytes
password:  9 bytes
[root@master1 ~]# 
[root@master1 ~]# kubectl exec -it mypod -- bash
root@mypod:/data# ls /etc/foo/
password  username
root@mypod:/data# cat /etc/foo/password 
value-2
root@mypod:/data# cat /etc/foo/username 
value-2

3. Downward API

名称过Downward API可以将Pod或Container的某些元数据信息(例如Pod名称、Pod IP、Node IP、Label、Annotation、容器资源限制等)以文件的形式挂载到容器内,供容器内的应用使用。

apiVersion: v1
kind: Pod
metadata:
  name: kubernetes-downwardapi-volume-example
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
  annotations:
    build: two
    builder: john-doe  
spec:
  containers:
    - name: client-container
      image: busybox
      command: ["sh", "-c"]
      args:
      - while true; do
          if [[ -e /etc/podinfo/labels ]]; then
            echo -en '\n\n'; cat /etc/podinfo/labels; fi;
          if [[ -e /etc/podinfo/annotations ]]; then
            echo -en '\n\n'; cat /etc/podinfo/annotations; fi;
          sleep 5;
        done;
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
  volumes:
    - name: podinfo
      downwardAPI:
        items:
        - path: "labels"
          fieldRef:
            fieldPath: metadata.labels
        - path: "annotations"
          fieldRef:
            fieldPath: metadata.annotations

创建Pod后,查看Pod的logs信息信息:

[root@master1 ~]# kubectl logs -f kubernetes-downwardapi-volume-example 


cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"

build="two"
builder="john-doe"
kubernetes.io/config.seen="2022-07-08T15:04:31.388541172+08:00"
kubernetes.io/config.source="api"

cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"

build="two"
builder="john-doe"
cni.projectcalico.org/containerID="d13c780eb681436e4f8d5c2e24db63f56553cfa2ca86d981a1ac3af13be1a4a9"
cni.projectcalico.org/podIP="10.0.104.30/32"
cni.projectcalico.org/podIPs="10.0.104.30/32"
kubernetes.io/config.seen="2022-07-08T15:04:31.388541172+08:00"
kubernetes.io/config.source="api"

4. Projected Volume

Projected Volume 是一种特殊的存储卷类型,用于将一个或多个上述资源对象(ConfigMap、Secret、DownwardAPI)一次性挂载到容器内的同一个目录下。
常见的应用场景如下:

  • 通过Pod 的标签生成不同的配置文件,需要使用配置文件,以及用户名和密码,这时需要使用3种资源:ConfigMap、Secret、DownwardAPI。
  • 自动化运维应用中使用配置文件和账号信息时,需要使用ConfigMap、Secret。
  • 在配置文件内使用Pod名称(metadata.name)记录日志时,需要使用ConfigMap、DownwardAPI。
  • 使用某个Secret对Pod所在命名空间(metadata.namespace)进行加密时,需要使用Secret、DownwardAPI。

Projected Volume在Pod的Volume定义中类型为projected,通过sources字段设置一个或多个ConfigMap、Secret、DownwardAPI、ServiceAccountToken资源。各种类型的资源的配置内容与被单独设置为Volume时基本一样,但是有两个不同点。

  • 对于Secret类型的Volume,字段名“secretName”在projected.sources.secret中被改为“name”。
  • Volume的挂载模式“defaultMode”仅可以设置在projected级别,对于各子项,仍然可以设置各自的挂载模式,使用的字段名为“mode”。

一个使用ProjectedVolume挂载ConfigMap、Secret、Downward API共3种资源的示例:

apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfigmap
data:
  config: |
   timezone=Shanghai

---

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  password: dmFsdWUtMg0K
  username: dmFsdWUtMg0K

---

apiVersion: v1
kind: Pod
metadata:
  name: volume-test
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
spec:
  containers:
    - name: container-test
      image: busybox
      command: ["sh", "-c"]
      args:
      - while true; do
          sleep 3600;
        done;
      volumeMounts:
        - name: all-in-one
          mountPath: "/projected-volume"
          readOnly: true
  volumes:
    - name: all-in-one
      projected:
        sources:
          - secret:
              name: mysecret
              items:
                - key: username
                  path: my-group/my-username
                - key: password
                  path: my-group/my-password
          - downwardAPI:
              items:
              - path: "labels"
                fieldRef:
                  fieldPath: metadata.labels
              - path: "cpu_limit"
                resourceFieldRef:
                  containerName: container-test
                  resource: limits.cpu
          - configMap:
              name: myconfigmap
              items:
              - key: config
                path: my-group/my-config

创建资源后进入Pod,查看挂载的数据:

[root@master1 ~]# kubectl exec -it volume-test -- sh
/ # cd /projected-volume/
/projected-volume # ls
cpu_limit  labels     my-group
/projected-volume # cat cpu_limit 
2/projected-volume # 
/projected-volume # cat labels 
cluster="test-cluster1"
rack="rack-22"
zone="us-est-coast"/projected-volume # 
/projected-volume # cat my-group/my-username 
value-2
/projected-volume # cat my-group/my-password 
value-2
/projected-volume # cat my-group/my-config 
timezone=Shanghai
/projected-volume #

下面是一个使用Projected Volume挂载两个Secret资源,其中一个设置了非默认挂载模式(mode)的示例:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: dmFsdWUtMg0K

---
apiVersion: v1
kind: Secret
metadata:
  name: mysecret2
type: Opaque
data:
  password: dmFsdWUtMg0K

---
apiVersion: v1
kind: Pod
metadata:
  name: volume-test
spec:
  containers:
  - name: container-test
    image: busybox
    command: ["sh", "-c"]
    args:
    - while true; do
        sleep 3600;
      done;
    volumeMounts:
    - name: all-in-one
      mountPath: "/projected-volume"
      readOnly: true
  volumes:
    - name: all-in-one
      projected:
        sources:
          - secret:
              name: mysecret
              items:
                - key: username
                  path: my-group/my-username
          - secret:
              name: mysecret2
              items:
                - key: password
                  path: my-group/my-password
                  mode: 511

创建资源后,查看Pod里挂载的权限信息:

[root@master1 ~]# kubectl exec -it volume-test -- sh
/ # ls /projected-volume
my-group
/ # cd /projected-volume
/projected-volume # ls -l my-group/
total 8
-rwxrwxrwx    1 root     root             9 Jul  8 08:14 my-password
-rw-r--r--    1 root     root             9 Jul  8 08:14 my-username
/projected-volume #

kubectl explain pod.spec.volumes.projected.sources.secret.items.mode查询对mode的解释如下:

[root@master1 ~]# kubectl explain pod.spec.volumes.projected.sources.secret.items.mode
KIND:     Pod
VERSION:  v1

FIELD:    mode <integer>

DESCRIPTION:
     Optional: mode bits used to set permissions on this file. Must be an octal
     value between 0000 and 0777 or a decimal value between 0 and 511. YAML
     accepts both octal and decimal values, JSON requires decimal values for
     mode bits. If not specified, the volume defaultMode will be used. This
     might be in conflict with other options that affect the file mode, like
     fsGroup, and the result can be other mode bits set.

大概意思就是:用于设置此文件权限的模式位。必须为0000 ~ 0777之间的八进制值或0 ~ 511之间的十进制值。YAML接受八进制和十进制值,JSON需要十进制值作为模式位。如果未指定,则将使用卷defaultMode。这可能与其他影响文件模式的选项(如fsGroup)冲突,结果可能是设置了其他模式位。

511的十进制转换为八进制是777,正如看到my-password的权限就是777。

参考:《kubernetes权威指南(第五版)》