ConfigMap 是一种 API 对象,用来将非机密性的数据保存到键值对中。使用时,Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。ConfigMap 的主要作用就是为了让镜像和配置文件解耦,以便实现镜像的可移植性和可复用性。
ConfigMap 并不提供保密或者加密功能。 如果你想存储的数据是机密的,请使用 Secret, 或者使用其他第三方工具来保证你的数据的私密性,而不是用 ConfigMap。

注意:此处我们用到的例子中的 cm1, cm2, cm3, cm4 后续例子中也会引用到。
这里先给出几个查看 configmap 的命令:
- # 查看 configmap 列表
- kubectl get cm
- # 查看某个 configmap 内容
- kubectl describe cm cm1
- 复制代码
- kubectl create configmap cm1 --from-literal=host=127.0.0.1 --from-literal=port=3306
- 复制代码
- echo -n 127.0.0.1 > host
- echo -n 3306 > port
- 复制代码
- kubectl create configmap cm2 --from-file=./host --from-file=./port
- 复制代码
- vim env.txt
- 复制代码
- host=127.0.0.1
- port=3306
- 复制代码
- kubectl create configmap cm3 --from-env-file=env.txt
- 复制代码
- vim cm4.yml
- 复制代码
- apiVersion: v1
- kind: ConfigMap
- metadata:
- name: cm4
- data:
- host: 127.0.0.1
- port: "3306"
- 复制代码
- kubectl apply -f cm4.yml
- 复制代码
- vim pod-cm1.yml
- 复制代码
- apiVersion: v1
- kind: Pod
- metadata:
- name: pod-cm1
- spec:
- containers:
- - name: mysql-pod
- image: mysql:5.7
- args: [ "/bin/sh", "-c", "sleep 10000" ]
- envFrom: # env方式
- - configMapRef:
- name: cm1 # configmap名称
- 复制代码
- kubectl apply -f pod-cm1.yml
- kubectl exec pod-cm1 --env
- 复制代码
- vim pod-cm2.yml
- 复制代码
- apiVersion: v1
- kind: Pod
- metadata:
- name: pod-cm2
- spec:
- containers:
- - name: mysql-pod
- image: mysql:5.7
- args: [ "/bin/sh", "-c", "sleep 10000" ]
- volumeMounts: # 用volume挂载方式
- - name: vol-cm # 对应下面的volume名
- mountPath: "/etc/mysql" # 挂载到容器内部的路径
- readOnly: true # 只读
-
- volumes:
- - name: vol-cm # 卷名称
- configMap:
- name: cm2 # configmap的名称
- 复制代码
- kubectl apply -f pod-cm2.yml
- kubectl exec pod-cm2 – cat /etc/mysql/host
- kubectl exec pod-cm2 – cat /etc/mysql/port
- 复制代码
使用 subpath 参数覆盖文件:
- cat index.html(内容是 ABCDEFG)
- 复制代码
- kubectl create configmap nginx-index --from-file=index.html
- 复制代码
- vim subpath-cm.yaml
- 复制代码
- apiVersion: v1
- kind: Pod
- metadata:
- name: subpath-cm
- spec:
- containers:
- - name: c1
- image: nginx:1.17.1-alpine
- volumeMounts:
- - name: nginx-config
- mountPath: /usr/share/nginx/html/index.html # configmap要挂载并覆盖的绝对路径
- subPath: index.html # 这里要写相对路径
-
- volumes:
- - name: nginx-config
- configMap:
- name: nginx-index # 对应上面创建的configmap
- 复制代码
- kubectl apply -f subpath-cm.yaml
- 复制代码
此时 nginx 的首页内容就已经被覆盖成了“ABCDEFG”。
我们现在就来验证这种方式:
- kubectl edit cm cm2
- 复制代码
- apiVersion: v1
- data:
- host: 127.0.0.1
- port: "3308" 修改成3308
- kind: ConfigMap
- metadata:
- creationTimestamp: "2020-11-07T12:09:15Z"
- managedFields:
- - apiVersion: v1
- fieldsType: FieldsV1
- fieldsV1:
- f:data:
- .: {}
- f:host: {}
- f:port: {}
- manager: kubectl
- operation: Update
- time: "2020-11-07T12:09:15Z"
- name: cm2
- namespace: default
- resourceVersion: "169707"
- selfLink: /api/v1/namespaces/default/configmaps/cm2
- 复制代码
验证对应的 pod 里的变化,一段时间后会改变:
- kubectl exec pod-cm2 – cat /etc/mysql/port
- 复制代码
Secret 与 ConfigMap 类似, 主要区别是 Secret 存储的是密文, 而 ConfigMap 存储的是明文。
所以 ConfigMap 可以用配置文件管理, 而 Secret 可用于密码, 密钥, token 等敏感数据的配置管理。

使用 Opaque 类型来创建 mysql 密码 Secret:
将明文密码进行base64编码
- echo -n 123456 |base64
- 复制代码
- MTIzNDU2
- 复制代码
编写创建secret的YAML文件
- vim secret-mysql.yml
- 复制代码
- apiVersion: v1
- kind: Secret
- metadata:
- name: secret-mysql
- data:
- password: MTIzNDU2
- 复制代码
创建secret并确认
- kubectl apply -f secret-mysql.yml
- kubectl get secret |grep secret-mysql
- 复制代码
Secret结构与ConfigMap类似,均是键/值对的映射。Secret的使用方法也与ConfigMap相同。
- vim pod-mysql-secret.yml
- 复制代码
- apiVersion: v1
- kind: Pod
- metadata:
- name: pod-mysql-secret1
- spec:
- containers:
- - name: mysql-pod
- image: mysql:5.7
- env:
- - name: MYSQL_ROOT_PASSWORD
- valueFrom:
- secretKeyRef:
- name: secret-mysql # 对应创建的secret名字
- key: password
- 复制代码
- kubectl apply -f pod-mysql-secret.yml
- 复制代码
- vim pod-mysql-secret2.yml
- 复制代码
- apiVersion: v1
- kind: Pod
- metadata:
- name: pod-mysql-secret2
- spec:
- containers:
- - name: busybox
- image: busybox
- args:
- - /bin/sh
- - -c
- - sleep 100000
- volumeMounts:
- - name: vol-secret # 定义挂载的卷,对应下面定义的卷名
- mountPath: "/opt/passwd" # 挂载目录(支持热更新),也可以使用subPath挂载文件(但不支持热更新)
- readOnly: true # 只读
- volumes:
- - name: vol-secret # 定义卷名
- secret: # 使用secret
- secretName: secret-mysql # 对应创建好的secret名
- 复制代码
- kubectl apply -f pod-mysql-secret2.yml
- 复制代码
Pod 本身具有生命周期,这就带了一系列的问题,第一,当一个容器损坏之后,kubelet 会重启这个容器,但是文件会丢失-这个容器会是一个全新的状态;第二,当很多容器在同一 Pod 中运行的时候,很多时候需要数据文件的共享。Docker 支持配置容器使用存储卷将数据持久存储于容器自身文件系统之外的存储空间之中,它们可以是节点文件系统或网络文件系统之上的存储空间。相应的,Kubernetes 也支持类似的存储卷功能,不过,其存储卷是与 Pod 资源绑定而非容器。
Kubernetes 中的卷有明确的寿命 —— 与封装它的 Pod 相同。所以,卷的生命比 Pod 中的所有容器都长,当这个容器重启时数据仍然得以保存。当然,当 Pod 不再存在时,卷也将不复存在。也许更重要的是,Kubernetes 支持多种类型的卷,Pod 可以同时使用任意数量的卷。

Kubernetes 支持非常丰富的存储卷类型,包括本地存储(节点)和网络存储系统中的诸多存储机制,还支持 ConfigMap 和 Secret 这样的特殊存储资源。 通过命令kubectl explain pod.spec可以查看当前 kubernetes 版本支持的存储卷类型。常用类型如下:
- 非持久性存储
- emptyDir
- hostPath
- 网络连接性存储
- SAN:iscsi
- NFS:nfs、cfs
- 分布式存储
- glusterfs、cephfs、rbd
- 云端存储
- awsElasticBlockStore、azureDisk、gitRepo
emptyDir 存储卷是 Pod 对象生命周期中的一个临时目录,类似于 Docker 上的 “docker 挂载卷”,在 Pod 对象启动时即被创建,而在 Pod 对象被移除时会被一并删除(永久删除)。Pod 中的容器都可以读写这个目录,这个目录可以被挂载到各个容器相同或者不相同的路径下。注意:一个容器崩溃了不会导致数据的丢失,因为容器的崩溃并不移除 Pod。
emptyDir 的作用:
emptyDir 的示例:
这里定义了一个 Pod资源对象(vol-emptydir-pod),在其内部定义了两个容器,其中一个容器是辅助容器 sidecar,每隔10秒生成一行信息追加到 index.html 文件中;另一个是 nginx 容器,将存储卷挂载到站点家目录。然后访问 nginx 的 html 页面验证两个容器之间挂载的 emptyDir 实现共享。

- vim vol-emptydir.yaml
- 复制代码
- apiVersion: v1
- kind: Pod
- metadata:
- name: vol-emptydir-pod
- spec:
- volumes: #定义存储卷
- - name: html #定义存储卷的名称
- emptyDir: {} #定义存储卷的类型
- containers:
- - name: nginx
- image: nginx:1.12
- volumeMounts: #在容器中定义挂载存储卷的名和路径
- - name: html
- mountPath: /usr/share/nginx/html
- - name: sidecar
- image: alpine
- volumeMounts: #在容器中定义挂载存储卷的名和路径
- - name: html
- mountPath: /html
- command: ["/bin/sh", "-c"]
- args:
- - while true; do
- echo $(hostname) $(date) >> /html/index.html;
- sleep 10;
- done
- 复制代码
- kubectl apply -f vol-emptydir.yaml
- 复制代码
hostPath 类型的存储卷是指将工作节点上的某文件系统的目录或文件挂载于 Pod 中的一种存储卷,独立于 Pod 资源的生命周期,具有持久性。在 Pod 删除时,数据不会丢失。

hostPath 存储卷的 type 类型:
| type | 说明 |
|---|---|
| DirectoryOrCreate | 指定的路径不存在时自动创建其权限为0755的空目录,属主和属组为kubelet |
| Directory | 必须存在的目录路径 |
| FileOrCreate | 指定的路径不存在时自动创建其权限为0644的空文件,属主和属组为kubelet |
| File | 必须存在的文件路径 |
| Socket | 必须存在的Socket文件路径 |
| CharDevice | 必须存在的字符设备文件路径 |
| BlockDevice | 必须存在的块设备文件路径 |
hostPath 的示例:
- vim vol-hostpath.yaml
- 复制代码
- apiVersion: v1
- kind: Pod
- metadata:
- name: pod-vol-hostpath
- namespace: default
- spec:
- containers:
- - name: myapp
- image: nginx:1.17.1
- imagePullPolicy: IfNotPresent
- volumeMounts:
- - name: html
- mountPath: /usr/share/nginx/html
- volumes:
- - name: html
- hostPath:
- path: /data/pod/volume1
- type: DirectoryOrCreate
- 复制代码
- kubectl apply -f vol-hostpath.yaml
- 复制代码
NFS 是 Network FileSystem 的缩写,顾名思义就是网络文件存储系统, 分为服务端(Server)和客户端(Client)。最早由 sun 公司开发,是类 unix 系统间实现磁盘共享的一种方法。 它允许网络中的计算机之间通过 TCP/IP 网络共享资源。通过 NFS,我们本地 NFS 的客户端应用可以透明地读写位于服务端 NFS 服务器上的文件,就像访问本地文件一样方便。简单的理解,NFS 就是可以透过网络,让不同的主机、不同的操作系统可以共享存储的服务。
NFS 在文件传送或信息传送过程中依赖于 RPC(Remote Procedure Call) 协议,即远程过程调用,NFS 的各项功能都必须要向 RPC 来注册,如此一来 RPC 才能了解 NFS 这个服务的各项功能 Port、PID、NFS 在服务器所监听的 IP 等,而客户端才能够透过 RPC 的询问找到正确对应的端口,所以,NFS必须要有 RPC 存在时才能成功的提供服务,简单的理解二者关系:NFS是 一个文件存储系统,而 RPC是负责信息的传输。
宿主机搭建 NFS:
(1) 通过 yum 安装
- yum -y install rpcbind nfs-utils
- 复制代码
(2) 建立网上邻居共享目录
- mkdir /mnt/share
- 复制代码
(3) 配置这个共享目录
- vim /etc/exports
- 复制代码
写入下边的内容:
- /mnt/share 192.168.138.0/24(rw,no_root_squash,async,fsid=0)
- 复制代码
(4) 使配置内容生效
- exportfs -r
- 复制代码
(5) 启动 rpcbind、nfs 服务及开机自启动
- systemctl start rpcbind
- systemctl start nfs
- systemctl enable rpcbind
- systemctl enable nfs
- 复制代码
前面提到 Kubernetes 提供那么多存储接口,但是首先 Kubernetes 的各个 Node 节点能管理这些存储,但是各种存储参数也需要专业的存储工程师才能了解,由此我们的 Kubernetes 管理变的更加复杂。由此 kubernetes 提出了 PV 和 PVC 的概念,这样开发人员和使用者就不需要关注后端存储是什么,使用什么参数等问题。
PersistentVolume(PV)是集群中已由管理员配置的一段网络存储。集群中的资源就像一个节点是一个集群资源。PV 是诸如卷之类的卷插件,但是具有独立于使用 PV 的任何单个 Pod 的生命周期。该 API 对象捕获存储的实现细节,即 NFS,ISCSI或云提供商特定的存储系统。
PersistentVolumeClaim(PVC)是用户存储的请求。它类似于 Pod。Pod 消耗节点资源,PVC 消耗存储资源。Pod 可以请求特定级别的资源(CPU 和内存)。权限要求可以请求特定的大小和访问模式。
虽然 PersistentVolumeClaims 允许用户使用抽象存储资源,但是常见的是,用户需要具有不同属性(如性能)的 PersistentVolumes ,用于不同的问题。集群管理员需要能够提供多种不同于 PersistentVolumes 的 PersistentVolumes ,而不仅仅是大小和访问模式,而不会使用户了解这些卷的实现细节。对于这些需求,存在 StorageClass 资源。
StorageClass 为管理员提供了一种描述他们提供的存储的“类”的方法。不同的类可能映射到服务质量级别,或备份策略,或者由集群管理员确定的任意策略。Kubernetes 本身对于什么类别代表是不言而喻的。这个概念有时在其它存储系统中称为“配置文件”。
PV 是集群中的资源。PVC 是对这些资源的请求,也是对资源的索赔检查。PV 和 PVC 之间的相互作用遵循这个生命周期:
- Provisioning —> Binding —> Using —> Releasing —> Recycling
- 复制代码
PV 有两种提供方式:静态或者动态
用户创建 PVC 并指定需要的资源和访问模式。在找到可用 PV 之前,PVC 会保持未绑定状态。
用户可在 Pod 中像 volume 一样使用 PVC。
用户删除 PVC 来回收存储资源,PV 将变成 “released” 状态。由于还保留着之前的数据,这些数据要根据不同的策略来处理,否则这些存储资源无法被其它 PVC 使用。
PV 可以设置三种回收策略:保留(Retain)、回收(Recycle)和删除(Delete)。
PersistentVolume Spec 主要支持以下几个通用字段,用于定义 PV 的容量、访问模式、和回收策略:
| 字段 | 说明 |
|---|---|
| capacity | 当前PV的容量;目前,capacity仅支持空间设定,将来应该还可以指定IOPS和throughput。 |
| accessModes | 访问模式;尽管在PV层看起来并无差异,但存储设备支持及启用的功能特性却可能不尽相同。例如NFS存储支持多客户端同时挂载及读写操作,但也可能是在共享时仅启用了只读操作,其他存储系统也存在类似的可配置特性。因此,PV底层的设备或许存在其特有的访问模式,用户使用时必须在其特性范围内设定其功能。 - ReadWribeOnce:仅可被单个节点读写挂载;命令行中简写为RWO。 - ReadOnlyMany:可被多个节点同时只读挂载;命令行中简写为ROX。 - ReadWriteMany:可被多个节点同时读写挂载;命令行中简写为RWX。 |
| persistentVolumeReclaimPolicy | PV空间被释放时的处理机制;可用类型仅为Retain(默认)、Recycle或Delete,具体说明如下。 - Retain:保持不动,由管理员随后手动回收。 - Recycle:空间回收,即删除存储卷目录下的所有文件(包括子目录和隐藏文件),目前仅NFS和hostPath支持此操作。 - Delete:删除存储卷,仅部分云端存储系统支持,如AWS EBS、GCE PD、Azure Disk和Cinder。 |
| volumeMode | 卷模型,用于指定此卷可被用作文件系统还是裸格式的块设备;默认为Filesystem。 |
| storageClassName | 当前PV所属的StorageClass的名称;默认为空值,即不属于任何StorageClass。 |
| mountOptions | 挂载选项组成的列表,如ro、soft和hard等。 |
PersistentVolumeClaim 是存储卷类型的资源,它通过申请占用某个 PersistentVolume 而创建,它与 PV 是一对一的关系,用户无须关系其底层实现细节。申请时,用户只需要指定目标空间的大小、访问模式、PV标签选择器和 StorageClass 等相关信息即可。PVC 的 Spec 字段的可嵌套字段具体如下:
| 字段 | 说明 |
|---|---|
| accessModes | 当前PVC的访问模式,其可用模式与PV相同 |
| resources | 当前PVC存储卷需要占用的资源量最小值;目前,PVC的资源限定仅指其空间大小 |
| selector | 绑定时对PV应用的标签选择器(matchLabels)或匹配条件表达式(matchEx-pressions),用于挑选要绑定的PV;如果同时指定了两种挑选机制,则必须同时满足两种选择机制的PV才能被选出 |
| storageClassName | 所依赖的存储卷的名称 |
| volumeMode | 卷模型,用于指定此卷可被用作于文件系统还是裸格式的块设备;默认为“Filesystem” |
| volumeName | 用于直接指定要绑定的PV的卷名 |
准备了一台 NFS Server 创建了几个共享目录提供给 Kubernetes 作为 PV 使用。在创建 PV 的同时指定了不同的大小和不同的访问权限,然后在创建 PVC 时候指定了大小为 6Gi ,故满足条件的 PV 只有 pv003~pv005 ,这里通过标签选择器选择了 pv003 。Pod中的容器使用了 MySQL,并将 MySQL 的数据目录挂载到 PV 上。示例图如下:

- (1)创建存储卷对应的目录
- [root@storage ~]# mkdir /data/volumes/v{1..5} -p
-
- (2)修改nfs的配置文件
- [root@storage ~]# vim /etc/exports
- /data/volumes/v1 192.168.1.0/24(rw,no_root_squash)
- /data/volumes/v2 192.168.1.0/24(rw,no_root_squash)
- /data/volumes/v3 192.168.1.0/24(rw,no_root_squash)
- /data/volumes/v4 192.168.1.0/24(rw,no_root_squash)
- /data/volumes/v5 192.168.1.0/24(rw,no_root_squash)
-
- (3)查看nfs的配置
- [root@storage ~]# exportfs -arv
- exporting 192.168.1.0/24:/data/volumes/v5
- exporting 192.168.1.0/24:/data/volumes/v4
- exporting 192.168.1.0/24:/data/volumes/v3
- exporting 192.168.1.0/24:/data/volumes/v2
- exporting 192.168.1.0/24:/data/volumes/v1
-
- (4)使配置生效
- [root@storage ~]# showmount -e
- Export list for storage:
- /data/volumes/v5 192.168.1.0/24
- /data/volumes/v4 192.168.1.0/24
- /data/volumes/v3 192.168.1.0/24
- /data/volumes/v2 192.168.1.0/24
- /data/volumes/v1 192.168.1.0/24
- 复制代码
- vim pv-nfs-demo.yaml
- 复制代码
- apiVersion: v1
- kind: PersistentVolume
- metadata:
- name: pv-nfs-001
- labels:
- name: pv001
- spec:
- nfs:
- path: /data/volumes/v1
- server: 192.168.1.34
- readOnly: false
- accessModes: ["ReadWriteOnce","ReadWriteMany"]
- capacity:
- storage: 2Gi
- persistentVolumeReclaimPolicy: Retain
- ---
- apiVersion: v1
- kind: PersistentVolume
- metadata:
- name: pv-nfs-002
- labels:
- name: pv002
- spec:
- nfs:
- path: /data/volumes/v2
- server: 192.168.1.34
- readOnly: false
- accessModes: ["ReadWriteOnce"]
- capacity:
- storage: 5Gi
- persistentVolumeReclaimPolicy: Retain
- ---
- apiVersion: v1
- kind: PersistentVolume
- metadata:
- name: pv-nfs-003
- labels:
- name: pv003
- spec:
- nfs:
- path: /data/volumes/v3
- server: 192.168.1.34
- readOnly: false
- accessModes: ["ReadWriteOnce","ReadWriteMany"]
- capacity:
- storage: 10Gi
- persistentVolumeReclaimPolicy: Retain
- ---
- apiVersion: v1
- kind: PersistentVolume
- metadata:
- name: pv-nfs-004
- labels:
- name: pv004
- spec:
- nfs:
- path: /data/volumes/v4
- server: 192.168.1.34
- readOnly: false
- accessModes: ["ReadWriteOnce","ReadWriteMany"]
- capacity:
- storage: 15Gi
- persistentVolumeReclaimPolicy: Retain
- ---
- apiVersion: v1
- kind: PersistentVolume
- metadata:
- name: pv-nfs-005
- labels:
- name: pv005
- spec:
- nfs:
- path: /data/volumes/v5
- server: 192.168.1.34
- readOnly: false
- accessModes: ["ReadWriteOnce","ReadWriteMany"]
- capacity:
- storage: 20Gi
- persistentVolumeReclaimPolicy: Retain
- 复制代码
- kubectl apply -f pv-nfs-demo.yaml
- 复制代码
- vim vol-nfs-pvc.yaml
- 复制代码
- #创建PVC
- apiVersion: v1
- kind: PersistentVolumeClaim
- metadata:
- name: nfs-pvc
- spec:
- accessModes: ["ReadWriteMany"]
- resources:
- requests:
- storage: 6Gi #指定PVC大小为6Gi
- selector: #这里通过标签选择器指定了所使用的pv卷为key为name,value为pv003的pv资源
- matchLabels:
- name: pv003
- ---
- #创建Pod
- apiVersion: v1
- kind: Pod
- metadata:
- name: pvc-mysql
- labels:
- app: mysql
- spec:
- containers:
- - name: pvc-mysql-pod
- image: mysql:latest
- imagePullPolicy: IfNotPresent
- ports:
- - name: mysqlport
- containerPort: 3306
- volumeMounts:
- - name: mysqldata
- mountPath: /var/lib/mysql
- env:
- - name: MYSQL_ROOT_PASSWORD
- value: "mysql"
- volumes:
- - name: mysqldata
- persistentVolumeClaim: #通过该字段定义使用pvc
- claimName: nfs-pvc #指定pvc的名称
- readOnly: false #关闭只读