概念
官方文档:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-volume-storage/
卷:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/
容器的生命周期可能很短,会被频繁地创建和销毁。那么容器在销毁时,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器的数据,kubernetes引入了Volume的概念。
Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下,kubernetes通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储。Volume的生命容器不与Pod中单个容器的生命周期相关,当容器终止或者重启时,Volume中的数据也不会丢失。
kubernetes的Volume支持多种类型,比较常见的有下面几个:
- 简单存储:EmptyDir、HostPath、NFS
- 高级存储:PV、PVC
- 配置存储:ConfigMap、Secret
基本存储之EmptyDir
官方文档:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/#emptydir
EmptyDir是在Pod被分配到Node时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为kubernetes会自动分配一个目录,当Pod销毁时,EmptyDir中的数据也会被永久删除。使用在临时缓存文件、中间计算结果(无需持久化)。

特点
- Pod 创建时自动创建空目录,Pod 删除时数据清除。
- 数据仅存于 Pod 所在节点的内存或磁盘(可通过medium参数指定,默认""表示节点默认存储,Memory表示内存存储,数据易失)。
- 支持 Pod 内多个容器共享数据。
EmptyDir使用场景
- 临时缓存文件、中间计算结果(无需持久化)
- 例如:filebeat采集日志
EmptyDir实战案例
示例:
# 定义清单文件 [root@master01 ~/volumes]# cat empty-pod.yaml apiVersion: v1 kind: Pod metadata: name: empty-pod spec: # 定义数据卷 volumes: # 数据卷名称 - name: data-volume # volume的类型 emptyDir: {} containers: - name: writer-busybox image: busybox command: ["/bin/sh", "-c"] args: - | echo "hello emptyDir" >> /data/hello.txt; sleep 3600 # 指定挂载的数据卷 volumeMounts: # 要挂载的数据卷名称 - name: data-volume # 挂载到容器内部的路径 mountPath: /data # 是否只读,false为可读可写,true为只读 readOnly: false - name: reader-busybox image: busybox command: ["/bin/sh", "-c"] args: - | cat /data/hello.txt; sleep 3600 volumeMounts: - name: data-volume mountPath: /data readOnly: true
查看pod打印的日志
[root@master01 ~/volumes]# kubectl logs empty-pod reader-busybox hello emptyDir
基本存储之HostPath
官方文档:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/#hostpath
EmptyDir中数据不会被持久化,它会随着Pod的结束而销毁,如果想简单的将数据持久化到主机中,可以选择HostPath。
HostPath就是将Node主机中一个实际目录挂在到Pod中,以供容器使用,这样的设计就可以保证Pod销毁了,但是数据依据可以存在于Node主机上。

特点
- 挂载节点上的本地文件或目录(如/var/lib/data)到 Pod 中。
- 数据随节点存在而保留,Pod 删除后数据仍在节点上,但跨节点调度时无法共享。
- 需注意节点路径权限(如使用hostPath.type指定路径类型,如DirectoryOrCreate自动创建目录)。
HostPath实战案例
# 定义清单文件 [root@master01 ~/volumes]# cat hostpath-pod.yaml apiVersion: v1 kind: Pod metadata: name: hostpath-pod spec: volumes: - name: hostpath hostPath: # 节点上的存储位置 path: /data/nginx/ # 类型,文件夹不存在时自动创建 type: DirectoryOrCreate containers: - name: nginx image: nginx volumeMounts: - name: hostpath mountPath: /usr/share/nginx/html # 创建Pod [root@master01 ~/volumes]# kubectl apply -f hostpath-pod.yaml pod/hostpath-pod created # 查看Pod调度到哪个节点上 [root@master01 ~/volumes]# kubectl get po hostpath-pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES hostpath-pod 1/1 Running 0 89s 100.95.185.242 node02 <none> <none>
前往node02节点上查看
# 发现目录已经创建成功了 [root@node02 ~]# stat /data/nginx/ File: /data/nginx/ Size: 4096 Blocks: 8 IO Block: 4096 directory Device: fd00h/64768d Inode: 1441794 Links: 2 Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2025-05-11 12:52:34.507395315 +0800 Modify: 2025-05-11 12:51:28.658324447 +0800 Change: 2025-05-11 12:51:28.658324447 +0800 Birth: 2025-05-11 12:49:08.272029814 +0800 # 写入一个文件进行访问测试 [root@node02 ~]# echo I am huangsir > /data/nginx/index.html [root@node02 ~]# cat /data/nginx/index.html I am huangsir # 访问nginx,发现绑定成功 [root@node02 ~]# curl 100.95.185.242 I am huangsir
hostPath中type的可用值
-
DirectoryOrCreate:如果在给定路径上什么都不存在,那么将根据需要创建空目录,权限设置为 0755,具有与 kubelet 相同的组和属主信息。
-
Directory:在给定路径上必须存在的目录。
-
FileOrCreate:如果在给定路径上什么都不存在,那么将在那里根据需要创建空文件,权限设置为 0644,具有与 kubelet 相同的组和所有权。
-
File:在给定路径上必须存在的文件。
-
Socket:在给定路径上必须存在的 UNIX 套接字。
-
CharDevice:(仅 Linux 节点) 在给定路径上必须存在的字符设备。
-
BlockDevice:(仅 Linux 节点) 在给定路径上必须存在的块设备。
基本存储之NFS
官方文档:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/#nfs
HostPath依赖节点,如果Pod重启之后调度到另一个节点中,那么所依赖数据将不存在。
NFS可以解决这个问题,
NFS存储特点
- 挂载远程 NFS 服务器的共享目录,支持跨节点共享数据。
- 数据持久化存储在 NFS 服务器,不依赖 Pod 或节点生命周期。
使用场景
多 Pod 共享数据(如分布式应用的共享配置)。
NFS实战案例
安装NFS
首先我们需要安装NFS,可以参考这篇文章:NFS搭建及使用
master节点安装服务端
apt update -y apt install -y nfs-kernel-server # 检查状态 systemctl status nfs-kernel-server # 创建共享目录 mkdir -p /data/nfs/nginx echo '/data/nfs/nginx 10.0.0.0/24(rw,sync,no_root_squash,no_subtree_check)' >> /etc/exports exportfs -ra systemctl restart nfs-kernel-server
node节点安装客户端(可以不用操作)
apt update -y apt install -y nfs-common mkdir -p /data/nfs/nginx # 挂载 mount -t nfs 10.0.0.30:/data/nfs/nginx /data/nfs/nginx # 开机自启动挂载 echo 10.0.0.30:/data/nfs/nginx /data/nfs/nginx nfs defaults 0 0 >> /etc/fstab # 检查是否挂载成功 df -h | grep /data/nfs/nginx
创建Pod测试
# 定义资源文件 [root@master01 ~/volumes]# cat nfs-pod.yaml apiVersion: v1 kind: Pod metadata: name: nfs-pod spec: # 指定Pod调度到node01节点上 nodeName: node01 volumes: - name: nfs nfs: # nfs服务端的地址 server: 10.0.0.30 # 挂载nfs服务器的路径 path: /data/nfs/nginx containers: - name: nginx image: nginx volumeMounts: - name: nfs mountPath: /usr/share/nginx/html # 创建Pod [root@master01 ~/volumes]# kubectl apply -f nfs-pod.yaml pod/nfs-pod created # 查看pod信息,发现调度到node01节点上,IP为100.117.144.145 [root@master01 ~/volumes]# kubectl get po nfs-pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nfs-pod 1/1 Running 0 16s 100.117.144.145 node01 <none> <none>
配置测试内容并访问测试
[root@master01 ~/volumes]# echo nfs-server > index.html # 访问测试 [root@master01 ~/volumes]# curl 100.117.144.145 nfs-server
再将Pod调度到node02节点上,测试访问内容是否会发生变化
[root@master01 ~/volumes]# cat nfs-pod.yaml apiVersion: v1 kind: Pod metadata: name: nfs-pod spec: # 指定Pod调度到node02节点上 nodeName: node02 volumes: - name: nfs nfs: # nfs服务端的地址 server: 10.0.0.30 # 挂载nfs服务器的路径 path: /data/nfs/nginx containers: - name: nginx image: nginx volumeMounts: - name: nfs mountPath: /usr/share/nginx/html # 创建Pod,需要将上一步创建Pod删除哦~ [root@master01 ~/volumes]# kubectl apply -f nfs-pod.yaml pod/nfs-pod created # 查看pod调度及IP [root@master01 ~/volumes]# kubectl get po nfs-pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nfs-pod 1/1 Running 0 10s 100.95.185.244 node02 <none> <none> # 访问,发现内容并没有发生变化 [root@master01 ~/volumes]# curl 100.95.185.244 nfs-seerver
配置存储ConfigMap和Secret
可以参考下面这两篇文章