容器的生命周期可能很短,会被频繁地创建和销毁。容器在销毁时,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器的数据,kubernetes引入了Volume的概念。
Volume是Pod中能够被多个container访问的共享目录。它被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下。Kubernetes通过Volume实现:
Volume的生命周期不与Pod中单个container的生命周期相关联。当container终止或重启时,Volume中的数据也不会丢失。
Kubernetes的Volume支持多种类型,常见的有:
EmptyDir、HostPath、NFSPV、PVCConfigMap、Secret最基础的Volume类型,一个EmptyDir就是Host上的一个空目录。
EmptyDir是在Pod分配到Node时创建的,它的初始内容为空,并且无需指定宿主机上对应的目录文件,因为kubernetes会自动分配一个目录。当Pod销毁时,EmptyDir中的数据也会被永久删除。EmptyDir用途为:
下面演示一个示例:在一个Pod中准备2个容器:nginx和busybox,然后声明一个Volume分别挂在2个容器的目录中。Nginx负责向Volume中写日志,busybox中通过命令将日志内容读到控制台。
如下图所示:

volume-emptydir.yaml
apiVersion: v1 kind: Pod metadata: name: volume-emptydir namespace: default spec: containers: - name: nginx image: nginx:1.17.1 ports: - containerPort: 80 volumeMounts: - name: logs-volume mountPath: /var/log/nginx - name: busybox image: busybox:1.30 command: ["/bin/sh", "-c", "tail -f /logs/access.log"] volumeMounts: - name: logs-volume mountPath: /logs volumes: - name: logs-volume emptyDir: {} kubectl apply -f volume-emptydir.yaml 
kubectl get pods -o wide 
curl 10.244.2.10 
kubectl logs -f volume-emptydir -n default -c busybox 
上面EmptyDir提到,EmptyDir中数据不会被持久化,它会随着Pod的结束而销毁,如果想简单的将数据持久化到主机中,可以选择HostPath。
HostPath就是将Node主机中一个实际目录挂在到Pod中,以供容器使用,这样的设计就可以保证Pod销毁了,但是数据依据可以存在于Node主机上。

volume-hostpath.yaml
apiVersion: v1 kind: Pod metadata: name: volume-hostpath namespace: default spec: containers: - name: nginx image: nginx:1.17.1 ports: - containerPort: 80 volumeMounts: - name: logs-volume mountPath: /var/log/nginx - name: busybox image: busybox:1.30 command: [ "/bin/sh","-c","tail -f /logs/access.log" ] volumeMounts: - name: logs-volume mountPath: /logs volumes: - name: logs-volume hostPath: path: /root/logs type: DirectoryOrCreate # 目录存在就使用,不存在就先创建后使用 关于type类型的说明:
| 类型 | 说明 |
|---|---|
| DirectoryOrCreate | 目录存在就使用,不存在就先创建 |
| Directory | 目录必须存在 |
| FileOrCreate | 文件存在就使用,不存在就先创建 |
| File | 文件必须存在 |
| Socket | unix套接字必须存在 |
| CharDevice | 字符设备必须存在 |
| BlockDevice | 块设备必须存在 |
kubectl apply -f volume-hostpath.yaml 
kubectl get pods -n default -o wide 
curl 10.244.1.9 
下面的操作需要到Pod所在的节点运行(案例中是k8s-node1)
tail -f /root/logs/access.log 
echo "hello HostPath" > test | 在host本地创建文件验证同步 |
|---|
![]() |
| 分别进入nginx和busybox容器查看 |
![]() |
kubectl delete pods volume-hostpath -n default 
HostPath可以解决数据持久化的问题,但是一旦Node节点故障了,Pod如果转移到了别的节点,又会出现问题了(会有新的hostpath volume在别的Node创建,但是原来的数据丢失了),此时需要准备单独的网络存储系统,比较常用的用NFS、CIFS。
NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统上,这样的话,无论Pod在节点上怎么转移,只要Node跟NFS的对接没问题,数据就可以成功访问。

master节点做nfs服务器
yum install -y nfs-utils mkdir -pv /root/data/nfs echo "/root/data/nfs 192.168.112.0/24(rw,sync,no_subtree_check)" >> /etc/exports systemctl start nfs && systemctl enable nfs 不需要启动
yum install -y nfs-utils volume-nfs.yaml
apiVersion: v1 kind: Pod metadata: name: volume-nfs namespace: default spec: containers: - name: nginx image: nginx:1.17.1 ports: - containerPort: 80 volumeMounts: - name: logs-volume mountPath: /var/log/nginx - name: busybox image: busybox:1.30 command: [ "/bin/sh","-c","tail -f /logs/access.log" ] volumeMounts: - name: logs-volume mountPath: /logs volumes: - name: logs-volume nfs: server: 192.168.112.10 #nfs服务器地址 path: /root/data/nfs #共享文件路径 kubectl apply -f volume-nfs.yaml 
kubectl get pods -o wide 
curl 10.244.2.3 
cd /root/data/nfs && ls cat access.log 
为了能够屏蔽底层存储实现的细节,方便用户使用,kubernetes引入了PV和PVC两种资源对象。
持久卷(PersistentVolume,PV)是集群中由管理员配置的一段网络存储。它是集群中的资源,就像节点是集群资源一样。一般情况下PV由kubernetes管理员进行创建和配置,它与底层具体的共享存储技术有关。PV持久卷和普通的Volume一样,也是使用卷插件来实现的,只是它们拥有独立于任何使用PV的Pod的生命周期。此API对象捕获存储实现的详细信息,包括NFS,iSCSI或特定于云提供程序的存储系统。
持久卷申领(PersistentVolumeClaim,PVC)表达的是用户对存储的请求。概念上与Pod类似。Pod会耗用节点资源,而PVC申领会耗用PV资源。Pod可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 。

对于用户来说,只提存储需求(PVC),专业的具体存储配置细节(PV)由管理员完成。
使用了PV和PVC后,工作可以进一步细分:
PV是存储资源的抽象
apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv spec: capacity: storage: 10Gi volumeMode: Filesystem accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: nfs-storage mountOptions: - hard - nfsvers=4.1 nfs: path: /nfs/data server: nfs-server.example.com 用于描述用户应用对存储资源的访问权限,访问权限包括
k8s v1.29):卷可以被单个 Pod 以读写方式挂载。mkdir -pv /root/data/pv{1..3} 
echo -e "/root/data/pv1\t192.168.112.0/24(rw,no_root_squash)\n/root/data/pv2\t192.168.112.0/24(rw,no_root_squash)\n/root/data/pv3\t192.168.112.0/24(rw,no_root_squash)" > /root/data/nfs_exports 
systemctl restart nfs pv.yaml
apiVersion: v1 kind: PersistentVolume metadata: name: pv1 spec: capacity: storage: 1Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain nfs: path: /root/data/pv1 server: 192.168.112.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: pv2 spec: capacity: storage: 2Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain nfs: path: /root/data/pv2 server: 192.168.112.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: pv3 spec: capacity: storage: 3Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain nfs: path: /root/data/pv3 server: 192.168.112.10 kubectl apply -f pv.yaml 
kubectl get pv -o wide 
PVC作为用户对存储资源的需求申请,主要包括存储空间请求、访问模式、PV选择条件和存储类别等信息的设置。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: myclaim spec: accessModes: - ReadWriteOnce volumeMode: Filesystem resources: requests: storage: 8Gi storageClassName: slow selector: matchLabels: release: "stable" matchExpressions: - {key: environment, operator: In, values: [dev]} apiVersion: Kubernetes API 版本,这里是 v1。
kind: 资源类型,这里是 PersistentVolumeClaim。
metadata:
spec: PVC 的具体规格。
accessModes: PVC 请求的访问模式列表。这里请求的是 ReadWriteOnce,意味着 PVC 只能被单个节点以读写方式挂
volumeMode: PVC 请求的 PV 的模式,这里为 Filesystem 表示这是一个文件系统类型的 PV。
resources:
storageClassName: PVC 请求的存储类名称,这里是 slow。如果未指定,则使用默认存储类。
selector: PVC 可以选择特定的 PV。如果指定,PVC 只能绑定到符合这些选择器条件的 PV。
pvc.yaml
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc1 namespace: default spec: accessModes: - ReadWriteMany resources: requests: storage: 1Gi --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc2 namespace: default spec: accessModes: - ReadWriteMany resources: requests: storage: 3Gi --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc3 namespace: default spec: accessModes: - ReadWriteMany resources: requests: storage: 5Gi kubectl applpy -f pvc.yaml 
kubectl get pvc -o wide 
kubectl get pv -o wide 
pods.yaml
apiVersion: v1 kind: Pod metadata: name: pod1 namespace: default spec: containers: - name: busybox image: busybox:1.30 command: [ "/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;" ] volumeMounts: - name: volume mountPath: /root/ volumes: - name: volume persistentVolumeClaim: claimName: pvc1 readOnly: false --- apiVersion: v1 kind: Pod metadata: name: pod2 namespace: default spec: containers: - name: busybox image: busybox:1.30 command: [ "/bin/sh","-c","while true;do echo pod2 >> /root/out.txt; sleep 10; done;" ] volumeMounts: - name: volume mountPath: /root/ volumes: - name: volume persistentVolumeClaim: claimName: pvc2 readOnly: false kubectl apply -f pods.yaml 
kubectl get pods,pvc,pv -o wide 
cat /root/data/pv1/out.txt cat /root/data/pv2/out.txt cat /root/data/pv3/out.txt | /root/data/pv1/out.txt |
|---|
![]() |
| /root/data/pv2/out.txt |
![]() |
| /root/data/pv3/out.txt |
![]() |
Pod1 使用了 PVC1,PVC1 绑定了 PV1。因此,Pod1 写入的文件位于 /root/data/pv1/out.txt。
Pod2 使用了 PVC2,PVC2 绑定了 PV3。因此,Pod2 写入的文件位于 /root/data/pv3/out.txt。
PV2 未被使用,因此 /root/data/pv2/out.txt 文件不存在。
kubectl delete -f pods.yaml kubectl delete -f pvc.yaml kubectl get pv -o wide 
PVC和PV是一一对应的,PV和PVC之间的相互作用遵循以下生命周期
1、资源供应:管理员手动创建底层存储和PV
2、资源绑定:用户创建PVC,kubernetes负责根据PVC的声明去寻找PV,并绑定在用户定义好PVC之后,系统将根据PVC对存储资源的请求在已存在的PV中选择一个满足条件的
一旦找到,就将该PV与用户定义的PVC进行绑定,用户的应用就可以使用这个PVC了。PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再与其他PVC进行绑定了
如果找不到,PVC则会无限期处于Pending状态,直到等到系统管理员创建了一个符合其要求的PV
3、资源使用:用户可在pod中像volume一样使用pvc
Pod使用Volume的定义,将PVC挂载到容器内的某个路径进行使用。
4、资源释放:用户删除pvc来释放pv
当存储资源使用完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为“已释放”,但还不能立刻与其他PVC进行绑定。通过之前PVC写入的数据可能还被留在存储设备上,只有在清除之后该PV才能再次使用。
5、资源回收:kubernetes根据pv设置的回收策略进行资源的回收
对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收,才能供新的PVC绑定和使用

ConfigMap 是 Kubernetes 中的一种 API 对象,用于存储非敏感的配置数据。它允许开发者将配置数据与应用程序容器分离,从而使得配置可以在不重新构建容器镜像的情况下进行更改。ConfigMap 可以存储简单的键值对或者一组文件,并且可以通过多种方式被应用程序容器访问。
apiVersion: v1 kind: ConfigMap metadata: name: game-demo data: # 类属性键;每一个键都映射到一个简单的值 player_initial_lives: "3" ui_properties_file_name: "user-interface.properties" # 类文件键 game.properties: | enemy.types=aliens,monsters player.maximum-lives=5 user-interface.properties: | color.good=purple color.bad=yellow allow.textmode=true apiVersion: v1
kind: ConfigMap
metadata:
data:
player_initial_lives: "3"player_initial_lives,值为 "3"。这个键值对表示玩家初始的生命值为 3。ui_properties_file_name: "user-interface.properties"ui_properties_file_name,值为 "user-interface.properties"。这个键值对表示 UI 属性文件的名称。game.properties: |user-interface.properties: |官方示例的 ConfigMap 可以通过多种方式被 Pod 使用
apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: my-container image: my-image env: - name: PLAYER_INITIAL_LIVES valueFrom: configMapKeyRef: name: game-demo key: player_initial_lives - name: UI_PROPERTIES_FILE_NAME valueFrom: configMapKeyRef: name: game-demo key: ui_properties_file_name apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: my-container image: my-image volumeMounts: - name: config-volume mountPath: /etc/game-demo volumes: - name: config-volume configMap: name: game-demo apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: my-container image: my-image volumeMounts: - name: config-volume mountPath: /etc/game-demo volumes: - name: config-volume configMap: name: game-demo items: - key: player_initial_lives path: player_initial_lives.properties - key: ui_properties_file_name path: ui_properties_file_name.properties - key: game.properties path: game.properties - key: user-interface.properties path: user-interface.properties apiVersion: v1 kind: ConfigMap metadata: name: configmap namespace: default data: # info是key,后面的都是value info: | # | 代表保留换行符 username:admin password:123456 kubectl apply -f configmap.yaml 
kubectl describe configmap configmap 
apiVersion: v1 kind: Pod metadata: name: pod-configmap namespace: default spec: containers: - name: nginx image: nginx:1.17.1 volumeMounts: # 将configmap挂载到目录 - name: config mountPath: /configmap/config volumes: # 引用configmap - name: config configMap: name: configmap # 注意这里的name就是上面创建好的configmap kubectl apply -f pod-configmap.yaml kubectl exec -it pod-configmap -c nginx /bin/bash cd configmap/config/ && ls && cat info 
可以看到映射已经成功,每个configmap都映射成了一个目录
key—>文件 value---->文件中的内容
kubectl edit configmap configmap kubectl exec -it pod-configmap -c nginx /bin/bash cd configmap/config/ && ls && cat change info | kubectl edit 在线修改ConfigMap |
|---|
![]() |
| 进入到容器查看内容变化 |
![]() |
在kubernetes中,还存在一种和ConfigMap非常类似的对象,称为Secret对象。用于存储敏感信息,如密码、密钥和其他机密数据。Secret 的设计目的是为了安全地管理这些敏感信息,避免它们被硬编码到配置文件或源代码中。
创建 Secret 时,你可以使用 Secret 资源的 type 字段,或者与其等价的 kubectl 命令行参数(如果有的话)为其设置类型。 Secret 类型有助于对 Secret 数据进行编程处理。
Kubernetes 提供若干种内置的类型,用于一些常见的使用场景。 针对这些类型,Kubernetes 所执行的合法性检查操作以及对其所实施的限制各不相同。
| 内置类型 | 用法 |
|---|---|
Opaque | 用户定义的任意数据 |
kubernetes.io/service-account-token | 服务账号令牌 |
kubernetes.io/dockercfg | ~/.dockercfg 文件的序列化形式 |
kubernetes.io/dockerconfigjson | ~/.docker/config.json 文件的序列化形式 |
kubernetes.io/basic-auth | 用于基本身份认证的凭据 |
kubernetes.io/ssh-auth | 用于 SSH 身份认证的凭据 |
kubernetes.io/tls | 用于 TLS 客户端或者服务器端的数据 |
bootstrap.kubernetes.io/token | 启动引导令牌数据 |
通过为 Secret 对象的 type 字段设置一个非空的字符串值,你也可以定义并使用自己 Secret 类型(如果 type 值为空字符串,则被视为 Opaque 类型)。
Kubernetes 并不对类型的名称作任何限制。不过,如果你要使用内置类型之一, 则你必须满足为该类型所定义的所有要求。
如果你要定义一种公开使用的 Secret 类型,请遵守 Secret 类型的约定和结构, 在类型名前面添加域名,并用 / 隔开。 例如:cloud-hosting.example.net/cloud-api-credentials。
echo -n 'root' | base64 echo -n 'zhangsan' | base64 
在创建 Kubernetes
Secret时,使用-n参数可以确保 base64 编码的字符串不会包含不必要的换行符,因为换行符可能会导致 base64 编码的结果不正确。
secret.yaml
apiVersion: v1 kind: Secret metadata: name: secret namespace: default type: Opaque data: username: cm9vdA== password: emhhbmdzYW4= kubectl apply -f secret.yaml 
kubectl get secret secret -o yaml 
pod-secret.yaml
apiVersion: v1 kind: Pod metadata: name: pod-secret namespace: default spec: containers: - name: nginx image: nginx:1.17.1 volumeMounts: # 将secret挂载到目录 - name: config mountPath: /secret/config volumes: - name: config secret: secretName: secret kubectl apply -f pod-secret.yaml kubectl get pods -o wide 
kubectl exec -it pod-secret -- bash 