Kubernetes 入门教程之八
大纲
- Kubernetes 入门教程之一、Kubernetes 入门教程之二、Kubernetes 入门教程之三
- Kubernetes 入门教程之四、Kubernetes 入门教程之五、Kubernetes 入门教程之六
- Kubernetes 入门教程之七、Kubernetes 入门教程之八、Kubernetes 入门教程之九
- Kubernetes 入门教程之十
Kubernetes 核心技术
持久化存储
Volume
Volume 的概述
Volume(卷)是 Pod 中可被多个容器共同访问的共享目录。Kubernetes 的 Volume 定义在 Pod 上,并可由该 Pod 内的多个容器挂载到各自的文件路径下。Volume 的生命周期与 Pod 相同,但独立于容器的生命周期。当容器终止或重启时,Volume 中的数据不会丢失。在使用 Volume 时,Pod 需要指定 Volume 的类型和内容(volumes 字段),以及在容器中挂载的位置(volumeMounts 字段)。Kubernetes 支持多种类型的 Volume,包括:emptyDir、hostPath、gcePersistentDisk、awsElasticBlockStore、nfs、iscsi、flocker、glusterfs、rbd、cephfs、gitRepo、secret、persistentVolumeClaim、downwardAPI、azureFile、azureDisk、vsphereVolume、quobyte、portworxVolume、scaleIO 等。
特别注意
在 Kubernetes 中,Volume 是定义在 Pod 层级上的,而不是容器层级的。这意味着同一个 Pod 内的多个容器可以通过挂载同一个 Volume 来共享数据。几乎所有类型的 Volume(如 emptyDir、hostPath、nfs、configMap、secret 等)都支持在同一个 Pod 内被多个容器同时访问和使用。
emptyDir 的使用
emptyDir 类型的 Volume 会在 Pod 被调度到某个节点(宿主机)时创建,Pod 内的所有容器都可以读写该目录中的数据。一旦 Pod 被删除或从该节点(宿主机)迁移,emptyDir 中的数据会被永久清除。因此,emptyDir 可以理解为本地存储,通常用于存放临时数据,例如 Web 服务器的日志文件或应用运行时的临时目录。emptyDir 类型的 Volume 的配置示例如下:
1 | apiVersion: v1 |
| 配置字段 | 说明 |
|---|---|
volumeMounts | 用于将 Pod 中定义的 Volume 挂载到容器内的指定路径。 |
emptyDir: {} | 表示创建一个临时目录,当 Pod 删除或迁移时,该目录中的数据会被永久清除。 |
hostPath 的使用
hostPath 类型的 Volume 允许容器访问所在宿主机上的指定目录。例如,当需要运行一个访问 Docker 系统目录的容器时,可以将宿主机的 /var/lib/docker 目录挂载为一个 hostPath 类型的 Volume;或者在容器中运行 cAdvisor 时,可以将 /dev/cgroups 目录挂载为 hostPath Volume。需要注意的是,当 Pod 从当前宿主机上删除或迁移时,hostPath 中的数据不会被删除,但也不会随 Pod 一同迁移到新的宿主机上。此外,由于不同宿主机的文件系统结构和内容可能存在差异,相同的 Pod 在不同宿主机上使用 hostPath 时,可能会出现不同的行为。hostPath 类型的 Volume 的配置示例如下:
1 | apiVersion: v1 |
| 配置字段 | 说明 |
|---|---|
volumeMounts | 定义容器内部的挂载路径 /test-data。 |
volumes.hostPath.path | 指定宿主机上对应的物理目录 /data。 |
hostPath | 该类型的卷允许容器直接访问宿主机上的文件系统资源。 |
nfs 的使用
nfs 类型的 Volume 允许将已有的 NFS(Network File System,网络文件系统)存储挂载到 Pod 中,这样同一个 Pod 内的多个容器就可以共享使用。通过 NFS,可以让不同节点上的 Pod 访问同一个远程存储目录,从而实现跨主机的数据共享与持久化。与 emptyDir 或 hostPath 不同,nfs 的数据存储在远程服务器上,不会因 Pod 或节点的重建、迁移而丢失,非常适合需要共享存储或持久化数据的场景,比如数据库、缓存或日志存储。nfs 类型的 Volume 的配置示例如下:
1 | apiVersion: apps/v1 |
| 配置字段 | 说明 |
|---|---|
revisionHistoryLimit: 2 | 仅保留最近 2 个历史版本的 ReplicaSet。 |
mountPath: /data | 容器内 Redis 的数据存储路径。 |
nfs | 通过 NFS 网络存储提供持久化数据目录。 |
特别注意
在 Kubernetes 集群中,如果需要使用 nfs 类型的 Volume(卷),则需要先在所有集群节点上分别手动安装 NFS 客户端,否则 Volume(卷)会无法正常挂载。比如,CentOS 系统安装 NFS 客户端,可以使用命令 sudo yum install -y nfs-utils。
PV 与 PVC
基本概念
(1) 在 Kubernetes 中,存储管理是计算管理中的一个重要问题。为此,Kubernetes 提供了 PersistentVolume(PV)子系统,为用户和管理员提供了一个抽象层,用于屏蔽底层存储实现的复杂性,并通过统一的 API 管理存储资源的使用。该子系统引入了两个新的 API 资源:PersistentVolume(PV) 和 PersistentVolumeClaim(PVC)。
(2) PersistentVolume(PV)是由集群管理员预先配置的一块网络存储,它是集群级别的资源,就像节点(Node)一样。PV 可以看作是一种容量插件(类似于 Volume),但其生命周期独立于使用它的任何 Pod。PV 对象中包含了底层存储实现的详细信息,例如 NFS、iSCSI,或特定云服务提供商的存储系统。
(3) PersistentVolumeClaim(PVC)是用户发起的存储资源请求,类似于 Pod 对节点资源的使用。Pod 消耗节点资源(如 CPU、内存),而 PVC 消耗 PV 资源。用户可以在 PVC 中指定所需的存储大小以及访问模式(例如:单次读写或多次只读)。
(4) 虽然 PVC 让用户能够以抽象的方式使用存储资源,但在实际应用中,不同场景往往对存储有不同的特性需求(如性能、可靠性、备份策略等)。为满足这种灵活性,Kubernetes 提供了 StorageClass 资源。StorageClass 允许管理员定义存储的 “类别”,用以描述不同类型的存储服务。不同的存储类可以对应不同的服务质量(QoS)等级、备份策略或其他由管理员定义的策略。Kubernetes 本身并不限定这些类别的具体含义,这一概念在其他系统中有时被称为 “存储配置文件(Profile)”。
(5) 在实际使用中,PVC 与 PV 通常是一一对应的,PVC 会自动绑定到满足其需求的 PV 上,从而实现持久化存储的自动化管理。
生命周期
PersistentVolume(PV)是集群中的资源,PersistentVolumeClaim(PVC)是用户对这些资源的请求并充当对资源的检查。PV 与 PVC 之间的交互遵循下面的生命周期阶段:
1 | Provisioning → Binding → Using → Releasing → Recycling |
(1) Provisioning(供应 / 准备)
- 通过集群外的存储系统或者云平台来提供持久化存储支持,有两类方式:
- 静态提供(Static):集群管理员事先创建若干 PV,并在每个 PV 中描述底层真实存储的详细信息(例如 NFS、iSCSI、云盘等)。这些 PV 以资源对象存在于 Kubernetes API 中,供用户通过 PVC 消费。
- 动态提供(Dynamic):当没有现成的、满足 PVC 要求的静态 PV 时,Kubernetes 可以基于 StorageClass 自动为 PVC 动态创建 PV(即动态配置卷)。要使用动态提供:
- PVC 必须指定某个
storageClassName(或者使用默认 StorageClass)。 - 对应的 StorageClass 必须已由管理员创建并配置好相应的 Provisioner(即外部存储插件)。
- 如果 PVC 明确请求一个不存在的类,则视为禁用动态配置(不会触发动态 Provisioning)。
- PVC 必须指定某个
- 通过集群外的存储系统或者云平台来提供持久化存储支持,有两类方式:
(2) Binding(绑定)
- 用户创建 PVC 并在其中指定所需的容量和访问模式(Access Modes)。
- Kubernetes 会查找符合 PVC 要求的 PV,并将其绑定(Bind)到该 PVC。
- 在找到合适的 PV 之前,PVC 处于未绑定(Pending)状态。
(3) Using(使用)
- 一旦 PVC 与 PV 绑定,用户可以在 Pod 的
volumes中使用 PVC,就像使用普通 Volume 一样,Pod 内的容器可以通过volumeMounts挂载并访问该存储卷。
- 一旦 PVC 与 PV 绑定,用户可以在 Pod 的
(4) Releasing(释放)
- 当用户删除 PVC(释放对存储的请求)时,PV 会进入 Released(已释放)状态。
- 注意:被释放的 PV 上可能仍然保留有先前使用者的数据。在这种状态下,如果不对数据做处理,该 PV 通常不能直接被新的 PVC 使用(取决于回收策略)。
(5) Recycling(回收)
- PV 上可以设置回收策略,用于指定在 PVC 删除后如何处理底层存储资源,常见策略包括:
- Retain(保留):默认回收策略,保留底层存储与数据,管理员需手动处理(例如备份或清理),然后手动将 PV 重新配置为可供新的 PVC 使用。
- Delete(删除):删除 PV 对象,并同时删除外部存储资源(删除操作需要底层存储插件支持)。
- Recycle(回收):旧版本 Kubernetes 支持(现已废弃),对底层卷执行简单的清理(比如
rm -rf /thevolume/*),清理后该 PV 可再次被新的 PVC 使用(回收操作需要相应插件支持或实现)。
- PV 上可以设置回收策略,用于指定在 PVC 删除后如何处理底层存储资源,常见策略包括:
总结
- PV 是集群级别的存储资源,生命周期独立于单个 Pod。
- PVC 是对 PV 的请求,用于记录存储容量与存储访问模式等需求。
- 生命周期的完整流程为:准备(静态 / 动态)→ 绑定 → 使用 → 释放 → 回收 / 删除 / 保留,回收策略由 PV 的
reclaimPolicy决定,管理员需要根据实际场景选择合适策略并配置相应的 StorageClass / Provisioner。
PV 的类型
在 Kubernetes 中,PersistentVolume(PV)的类型有以下几种:
| PV 类型 | 说明 |
|---|---|
| GCEPersistentDisk | 使用 Google Compute Engine 提供的持久磁盘(Persistent Disk)作为存储卷。 |
| AWSElasticBlockStore | 使用 AWS 的 EBS(Elastic Block Store)卷作为存储卷。 |
| AzureFile | 使用 Azure File 存储(基于 SMB 协议)作为共享文件卷。 |
| AzureDisk | 使用 Azure 的托管磁盘(Managed Disk)或非托管磁盘作为块存储卷。 |
| FC (Fibre Channel) | 通过光纤通道(Fibre Channel)协议连接的块存储设备。 |
| FlexVolume | 可扩展的卷插件机制,允许用户通过外部驱动程序自定义存储挂载逻辑。 |
| Flocker | 已弃用的存储方案,原用于容器与外部数据卷的动态关联。 |
| NFS | 使用网络文件系统(Network File System,NFS)协议挂载远程共享存储,支持多容器共享访问。 |
| iSCSI | 通过 iSCSI 协议访问远程块存储设备。 |
| RBD (Ceph Block Device) | 使用 Ceph 提供的 RADOS 块设备(RBD)作为存储卷。 |
| CephFS | 使用 Ceph 提供的分布式文件系统(CephFS)作为共享文件卷。 |
| Cinder (OpenStack block storage) | 使用 OpenStack 的 Cinder 服务提供的块存储。 |
| Glusterfs | 使用 GlusterFS 提供的分布式文件系统存储,支持多节点共享访问。 |
| VsphereVolume | 使用 VMware vSphere 平台提供的虚拟磁盘(vmdk)作为存储卷。 |
| Quobyte Volumes | 使用 Quobyte 提供的分布式文件系统作为存储卷。 |
| HostPath | 将宿主机上的目录或文件挂载到 Pod 中(仅适用于单节点测试环境,不支持集群环境)。 |
| Portworx Volumes | 使用 Portworx 存储解决方案提供的高可用分布式块存储。 |
| ScaleIO Volumes | 使用 Dell EMC 的 ScaleIO(现 PowerFlex)分布式块存储。 |
| StorageOS | 使用 StorageOS 提供的容器原生分布式存储系统。 |
PV 的阶段状态
在 Kubernetes 中,PersistentVolume(PV)的生命周期会经历多个阶段(Phase),用于描述其当前的使用状态。PV 的阶段状态有以下几个:
| 状态 | 说明 |
|---|---|
| Available | PV 资源尚未被任何 PVC(PersistentVolumeClaim)绑定,可供新的 Claim 使用。 |
| Bound | PV 已经成功绑定到某个 PVC,正在被使用。 |
| Released | 与该 PV 绑定的 PVC 已被删除,卷已释放但尚未被回收,此时卷中的数据可能仍然存在。 |
| Failed | PV 自动回收失败,需要管理员手动干预或清理。 |
提示
- PV 的状态转换是由 Kubernetes 控制器自动管理的。
- 如果存储类(StorageClass)指定回收策略为保留(
reclaimPolicy: Retain),那么在 PVC 删除后 PV 会保持Released状态,需管理员手动处理。
PV 的使用案例
提示
本节将演示在 Kubernetes 集群中,如何配合使用 PV + PVC + Pod,使用的 PV 类型是 nfs。
创建 PV
- 通过 YAML 文件(比如
pv-demo.yaml)创建 5 个 PersistentVolume(PV),类型都为nfs,但存储大小各不相同,是否可读也不相同(请自行更改 NFS 服务器的 IP 地址,并在 NFS 服务器上提前创建好相应的共享目录)
1 | apiVersion: v1 |
- 创建或更新 YAML 文件(比如
pv-demo.yaml)中定义的 PV 对象
1 | kubectl apply -f pv-demo.yaml |
- 查看所有 PV
1 | kubectl get pv |
1 | NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE |
创建 PVC 并绑定 PV
- 通过 YAML 文件(比如
pvc-demo.yaml)创建一个 PersistentVolumeClaim(PVC),需要 6G 存储空间,所以不会匹配上面的pv001、pv002、pv003
1 | apiVersion: v1 |
| 配置字段 | 含义 |
|---|---|
namespace | PVC 所在命名空间为 default。 |
accessModes | 设置访问模式,这里为 ReadWriteMany,表示允许多个节点同时读写。 |
resources.requests.storage | 申请的存储大小,这里为 6Gi。 |
- 创建或更新 YAML 文件(比如
pvc-demo.yaml)中定义的 PVC 对象
1 | kubectl apply -f pvc-demo.yaml |
- 查看所有 PVC
1 | kubectl get pvc |
1 | NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE |
- 查看所有 PV
1 | kubectl get pv |
1 | NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE |
创建 Pod 并挂载 PVC
- 通过 YAML 文件(比如
pod-demo.yaml)创建一个 Deployment 和 Service,并挂载 PVC
1 | apiVersion: v1 |
| 配置字段 | 含义 |
|---|---|
containers.volumeMounts.mountPath | 指定容器内的挂载路径 /usr/share/nginx/html/。 |
volumes.persistentVolumeClaim.claimName | 绑定前面创建的 PVC 名称 mypvc。 |
- 创建或更新 YAML 文件(比如
pod-demo.yaml)中定义的 Deployment 和 Service 对象
1 | kubectl apply -f pod-demo.yaml |
- 查看所有 Pod
1 | kubectl get pod -o wide |
1 | NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES |
- 查看所有 Service
1 | kubectl get svc |
1 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE |
- 在 NFS 的共享目录中,创建 Nginx 的首页文件(
index.html),避免挂载卷(Volume)后覆盖了 Nginx 镜像原有的默认首页,导致 Nginx 首页访问出现 403 错误
1 | # 连接进 Nginx 容器内,在 NFS 共享目录中创建 Nginx 首页的 HTML 文件 |
- 最后通过任意一个集群节点的 IP 与 Service 对外暴露的端口(比如
http://192.168.2.191:31566),就可以在 Kubernetes 集群外部通过浏览器访问 Nginx 的首页(如下图所示)

手动回收 Released PV
在上面的案例中,当 Pod 和 PVC 都被删除后,PV 会处于 Released 状态,对应的底层存储与数据会保留下来。此时,集群管理员需要手动处理(例如备份或清理数据),然后手动将 PV 重新配置,这样该 PV 才可以供新的 PVC 使用。
- 删除 Pod 与 PVC
1 | # 删除 Pod |
- 查看所有 PV
1 | kubectl get pv |
1 | NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE |
从上面的输出信息,可以看到 pv004 的状态为 STATUS: Released,表示该 PV 原先被某个 PVC(这里是 default/mypvc)绑定过,但 PVC 已被删除。由于该 PV 的回收策略是 Retain,Kubernetes 不会自动清理其中的数据,也不会重新将它标记为可用(Available)。要让处于 Released 状态的 PV 再次可用(允许重新绑定新的 PVC),必须手动回收该 PV,以下是标准的做法。
1、删除旧 PVC 产生的残留数据
- 通常 PV 对应一个存储路径(比如 NFS、
hostPath、或本地目录等)。 - (1) 先找到 PV 对应的存储目录路径:
1
kubectl describe pv pv004
- (2) 然后在输出内容中找到:
1
2
3Source:
Server: 192.168.2.188
Path: /data/volumes/v4 - (3) 最后手动删除存储目录路径中的所有文件:
1
2# 这一步骤会删除上一个 PVC 的所有数据,请谨慎执行
sudo rm -rf /data/volumes/v4/*
- 通常 PV 对应一个存储路径(比如 NFS、
2、移除 PV 上的旧 Claim 信息
- 因为 PV 仍然绑定了旧的 PVC(
claimRef字段),所以必须解除旧 PVC 的绑定。 - Kubernetes 不允许直接编辑已绑定的 PV
- 需要强制修改 PV
- 强制修改 PV
1
kubectl patch pv pv004 -p '{"spec":{"claimRef": null}}'
- 修改完成后,PV 的状态会改变为
Available
- 强制修改 PV
- 或者手动编辑 PV
- 手动编辑 PV
1
kubectl edit pv pv004
- 删除如下字段(
claimRef)内容1
2
3
4
5
6
7claimRef:
apiVersion: v1
kind: PersistentVolumeClaim
name: mypvc
namespace: default
resourceVersion: "473636"
uid: 1be643da-a7fe-4957-b8ec-887952ae7763 - 保存退出后,PV 的状态会改变为
Available
- 手动编辑 PV
- 需要强制修改 PV
- 因为 PV 仍然绑定了旧的 PVC(
3、确认 PV 可用
- 查看所有 PV
1
2# 查看 PV 列表
kubectl get pv1
2
3
4
5
6NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 2Gi RWO,RWX Retain Available 3h56m
pv002 3Gi RWO Retain Available 3h56m
pv003 5Gi RWO,RWX Retain Available 3h56m
pv004 10Gi RWO,RWX Retain Available 3h56m
pv005 15Gi RWO,RWX Retain Available 3h56m
- 查看所有 PV
