乔克视界 乔克视界
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • Kubernetes
  • Prometheus
  • ELK
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档

乔克

云原生爱好者
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • Kubernetes
  • Prometheus
  • ELK
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档
  • Docker

  • Golang

  • AIOps

  • Python

  • DevOps

  • Kubernetes

    • 什么是云原生?
    • Docker容器技术
    • Kubernetes简介
    • Kubernetes核心对象
    • Kubernetes集群管理
    • Kubernetes权限管理
    • Kubernetes工作负载管理
    • Kubernetes调度管理
    • Kubernetes应用质量管理
    • Kubernetes数据持久化管理
      • 安装存储系统
      • PV
      • PVC
      • StorageClass
        • 安装 NFS Provisioner
        • 使用 StorageClass
      • 总结
    • Kubernetes应用访问管理
    • Kubernetes应用配置管理
    • Kubernetes有状态应用管理
    • Kubernetes 网络管理
    • Helm 应用包管理
  • Prometheus

  • ELK

  • 专栏
  • Kubernetes
乔克
2025-07-19
目录

Kubernetes数据持久化管理

Kubernetes 为了能更好的支持有状态应用的数据存储问题,除了基本的 HostPath 和 EmptyDir 提供的数据持久化方案之外,还提供了 PV,PVC 和 StorageClass 资源对象来对存储进行管理。

PV 的全称是 Persistent Volume(持久化卷),是对底层数据存储的抽象,PV 由管理员创建、维护以及配置,它和底层的数据存储实现方法有关,比如 Ceph,NFS,ClusterFS 等,都是通过插件机制完成和共享存储对接。

PVC 的全称是 Persistent Volume Claim(持久化卷声明),我们可以将 PV 比喻为接口,里面封装了我们底层的数据存储,PVC 就是调用接口实现数据存储操作,PVC 消耗的是 PV 的资源。

StorageClass 是为了满足用于对存储设备的不同需求,比如快速存储,慢速存储等,通过对 StorageClass 的定义,管理员就可以将存储设备定义为某种资源类型,用户根据 StorageClass 的描述可以非常直观的知道各种存储资源的具体特性,这样就可以根据应用特性去申请合适的资源了。

# 安装存储系统

存储系统的选择有很多,常见的有 NFS、Ceph、GlusterFS、FastDFS 等,具体使用什么根据企业情况而定。在这里使用的是 NFS,下面简单介绍一下如何安装。

(1)安装服务

yum install nfs-utils rpcbind -y
1

(2)创建共享目录

mkdir /data/k8s -p
1

(3)配置 NFS 配置文件

vim /etc/exports
/data/k8s *(rw,sync,no_root_squash)
1
2

(4)启动服务

systemctl start rpcbind
systemctl start nfs
systemctl enable rpcbind
systemctl enable nfs
1
2
3
4

(5)测试

showmount -e 192.168.205.128
Export list for 192.168.205.128:
/data/k8s *
1
2
3

PS:所有节点都需要安装 NFS 客户端

# PV

PV(Persistent Volume)作为 Kubernetes 存储设备,可以由管理员提前配置,也可以通过 StorageClass 来动态供应。

PV 是集群资源,可以通过 kubectl explain pv 来查看如何配置,主要包括存储能力,访问模式,存储类型,回收信息等关键信息。例如:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv01
  labels:
    storage: pv
spec:
  accessModes:
    - ReadWriteOnce
  capacity:
    storage: 1Gi
  persistentVolumeReclaimPolicy: Recycle
  nfs:
    path: /data/k8s
    server: 192.168.205.128
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

参数说明:

(1)、accessMode:访问模式,有 ReadWriteOnce,ReadOnlyMany,ReadWriteMany。其中:

  • ReadWriteOnce:表示具有读写权限,但是只能被一个 node 挂载一次
  • ReadOnlyMany:表示具有只读权限,可以被多个 node 多次挂载
  • ReadWriteMany:表示具有读写权限,可以被多个 node 多次挂载

(2)、capacity:持久卷资源和容量的描述,存储大小是唯一可设置或请求的资源。

(3)、persistentVolumeReclaimPolicy: 回收策略,也就是释放持久化卷时的策略,其有以下几种:

  • Retain:保留数据,如果要清理需要手动清理数据,默认的策略;
  • Delete:删除,将从 Kubernetes 中删除 PV 对象,以及外部基础设施中相关的存储资产,比如 AWS EBS, GCE PD, Azure Disk, 或 Cinder volume;
  • Recycle:回收,清楚 PV 中的所有数据,相当于执行 rm -rf /pv-volume/*;

创建过后,PV 的状态如下:

kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
my-pv01   1Gi        RWO            Recycle          Available                                   5s

kubectl describe pv my-pv01
Name:            my-pv01
Labels:          storage=pv
Annotations:     <none>
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:
Status:          Available
Claim:
Reclaim Policy:  Recycle
Access Modes:    RWO
VolumeMode:      Filesystem
Capacity:        1Gi
Node Affinity:   <none>
Message:
Source:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    192.168.205.128
    Path:      /data/k8s
    ReadOnly:  false
Events:        <none>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

当前 PV 的状态是 Available,表示处于随时可用状态。PV 总共有以下四种状态:

  • Available(可用):表示可用状态,还未被任何 PVC 绑定
  • Bound(已绑定):表示 PVC 已经被 PVC 绑定
  • Released(已释放):PVC 被删除,但是资源还未被集群重新声明
  • Failed(失败): 表示该 PV 的自动回收失败

单纯的创建 PV,我们并不能直接使用,需要使用 PVC(Persistent Volume Claim)来进行声明。

# PVC

PVC(Persistent Volume Claim)用于表达用户对存储的需求,申请 PVC 会消耗掉 PV 的资源,可以通过kubectl explain pvc来查看帮助文档。

在上一节我们创建了 PV,现在要申明 PVC,如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-test
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
1
2
3
4
5
6
7
8
9
10

spec 参数说明:

(1)、accessModes:主要定义卷所应该拥有的访问模式

(2)、resources:主要定义卷应该拥有的最小资源

(3)、dataSource:定义如果提供者具有卷快照功能,就会创建卷,并将数据恢复到卷中,反之不创建

(4)、selector:定义绑定卷的标签查询

(5)、storageClassName:定义的 storageClass 的名字

(6)、volumeMode:定义卷的类型

(7)、volumeName:需要绑定的 PV 的名称链接

创建过后,查看 PV 和 PVC 的状态,如下:

kubectl get pvc
NAME       STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-test   Bound    my-pv01   1Gi        RWO                           2s
kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE
my-pv01   1Gi        RWO            Recycle          Bound    default/pvc-test                           20m
1
2
3
4
5
6

我们从上面可以看到 pvc 处于 Bound 状态,Bound 的 VOLUME 是 my-pv01,我们再看 pv 的状态有 Available 变为 Bound,其 CLAIM 是 default/pvc-test,其中 default 为 namespace 名称。

在上面我们创建了一个 PVC,其绑定了我们创建的 PV,如果此时我们再创建一个 PVC,结果又会如何?

我们 copy 以下上面的 PVC 文件,将其名称改一下,如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-test2
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
1
2
3
4
5
6
7
8
9
10

然后查看 PVC 的状态,如下

kubectl get pvc
NAME        STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-test    Bound     my-pv01   1Gi        RWO                           3m57s
pvc-test2   Pending                                                      4s
1
2
3
4

我们可以看到我们刚创建的 pvc-test2 的 STATUS 处于 Pending 状态,这是由于集群里声明的 PV 都使用完了,PVC 在申请的时候没有找到合适的 PV,所以处于这个状态,这时候如果我们创建一个新的并满足要求的 PV,则可以看到这个 PVC 会处于 Bound 状态。如下:

kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM              STORAGECLASS   REASON   AGE
my-pv01   1Gi        RWO            Recycle          Bound       default/pvc-test                           27m
my-pv02   1Gi        RWO            Recycle          Available                                              5s
kubectl get pvc
NAME        STATUS   VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-test    Bound    my-pv01   1Gi        RWO                           6m50s
pvc-test2   Bound    my-pv02   1Gi        RWO                           2m57s
1
2
3
4
5
6
7
8

PVC 也在申领 PV 的时候也不是随意申领的,它需要符合以下要求:

(1)PVC 申领的模式要和 PV 匹配上,假如 PVC 的模式是 ReadWriteOnce,而 PV 的模式是 ReadWriteMany,则申领部成功。

(2)PVC 申领的容量要小于等于 PV 的容量,否则申请不成功。

(3)一个 PV 只能绑定一个 PVC

另外,如果我们的 PVC 需求的容量小于 PV 的可用容量,绑定的容量是 PV 的可用容量。

# StorageClass

上面介绍的 PV 和 PVC 模式是需要运维人员先创建好 PV,然后开发人员定义好 PVC 进行一对一的 Bond,但是如果 PVC 请求成千上万,那么就需要创建成千上万的 PV,对于运维人员来说维护成本很高,Kubernetes 提供一种自动创建 PV 的机制,叫 StorageClass,它的作用就是创建 PV 的模板。

具体来说,StorageClass 会定义一下两部分:

  1. PV 的属性 ,比如存储的大小、类型等;
  2. 创建这种 PV 需要使用到的存储插件,比如 Ceph 等;

有了这两部分信息,Kubernetes 就能够根据用户提交的 PVC,找到对应的 StorageClass,然后 Kubernetes 就会调用 StorageClass 声明的存储插件,创建出需要的 PV。

这里我们以 NFS 为例,要使用 NFS,我们就需要一个 nfs-client 的自动装载程序,我们称之为 Provisioner,这个程序会使用我们已经配置好的 NFS 服务器自动创建持久卷,也就是自动帮我们创建 PV。

说明:

  • 自动创建的 PV 会以${namespace}-${pvcName}-${pvName}的目录格式放到 NFS 服务器上;
  • 如果这个 PV 被回收,则会以 archieved-${namespace}-${pvcName}-${pvName}这样的格式存放到 NFS 服务器上;

# 安装 NFS Provisioner

(1)创建 ServiceAccount,为 NFS Provisioner 授权

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: nfs-client-provisioner-clusterrole
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["list", "watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["create", "delete", "get", "list", "watch", "patch", "update"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: nfs-client-provisioner-clusterrolebinding
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-clusterrole
  apiGroup: rbac.authorization.k8s.io
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

(2)创建 NFS Provisioner

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-prosioner
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-prosioner
  template:
    metadata:
      labels:
        app: nfs-client-prosioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-prosioner
          image: registry.cn-hangzhou.aliyuncs.com/rookieops/nfs-client-provisioner:4.0
          imagePullPolicy: IfNotPresent
          volumeMounts:
            - name: nfs-client-root
              mountPath: /data/pv
          env:
            - name: PROVISIONER_NAME
              value: rookieops/nfs
            - name: NFS_SERVER
              value: 192.168.205.128
            - name: NFS_PATH
              value: /data/k8s
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.205.128
            path: /data/k8s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

执行完成后,查看 NFS Provisioner 的状态,如下:

kubectl get po
NAME                                    READY   STATUS    RESTARTS   AGE
nfs-client-prosioner-54d64dfc85-b4ht4   1/1     Running   0          10s
1
2
3

# 使用 StorageClass

上面已经创建好 NFS Provisioner,现在我们可以直接创建 StroageClass,如下:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs
provisioner: rookieops/nfs
1
2
3
4
5

每个 StorageClass 都包含 provisioner、parameters 和 reclaimPolicy 字段, 这些字段会在 StorageClass 需要动态分配 PersistentVolume 时会使用到。

在配置 StorageClass 的时候,如果没有指定 reclaimPolicy,则默认是 Delete,除此之外,还有 Retain。

StorageClass 对象的命名很重要,用户使用这个命名来请求生成一个特定的类。 当创建 StorageClass 对象时,管理员设置 StorageClass 对象的命名和其他参数,一旦创建了对象就不能再对其更新。

使用kubectl apply -f sc.yaml创建 StorageClass,创建完成过后如下:

kubectl get sc
NAME   PROVISIONER     RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs    rookieops/nfs   Delete          Immediate           false                  9m41s
1
2
3

现在,我们就可以使用动态存储申领 PVC,如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-from-sc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: nfs
  resources:
    requests:
      storage: 1Gi
1
2
3
4
5
6
7
8
9
10
11

使用kubectl apply -f pvc-from-sc.yaml,查看 PVC 创建情况,如下:

kubectl get pvc
NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-from-sc   Bound    pvc-a4a71b8c-5664-4d1a-b286-9e4adcf6f96a   1Gi        RWO            nfs            8s
kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS   REASON   AGE
pvc-a4a71b8c-5664-4d1a-b286-9e4adcf6f96a   1Gi        RWO            Delete           Bound    default/pvc-from-sc   nfs                     86s
1
2
3
4
5
6

可以看到自动创建了一个 PV,然后和 PVC 进行绑定。

为了方便使用,有时候会给集群默认设置一个 StorageClass,以便在需要使用动态存储,但是未声明的情况下使用默认的动态存储。设置方式如下:

kubectl patch storageclass nfs -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
1

通过向其添加 storageclass.kubernetes.io/is-default-class 注解来将特定的 StorageClass 标记为默认。 当集群中存在默认的 StorageClass 并且用户创建了一个未指定 storageClassName 的 PersistentVolumeClaim 时, DefaultStorageClass 准入控制器会自动向其中添加指向默认存储类的 storageClassName 字段。

请注意,集群上最多只能有一个 默认 存储类,否则无法创建没有明确指定 storageClassName 的 PersistentVolumeClaim。

如果要取消默认 StorageClass,只需要把注解设置为 flase 即可,如下:

kubectl patch storageclass nfs -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
1

如果我们要在 Pod 中使用 PVC,则直接如下声明:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
    - name: nginx
      image: nginx
      imagePullPolicy: IfNotPresent
      volumeMounts:
        - name: nfs-pvc
          mountPath: /mnt
  restartPolicy: Never
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: pvc-from-sc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

可以进入容器,到挂载目录输出,例如:

kubectl exec -it nginx -- /bin/bash
root@nginx:/mnt## echo "test" > /mnt/text.txt
1
2

然后到 NFS 对应的目录查看是否一致。

cd /data/k8s/default-pvc-from-sc-pvc-a4a71b8c-5664-4d1a-b286-9e4adcf6f96a
cat text.txt
test
1
2
3

这表示 Pod 使用持久化成功。

# 总结

在 Kubernetes 中,虽然我们建议使用无状态应用,但是对于有些特殊应用,数据持久化还是必不可少的。数据持久化的难度不在于创建几个 PV 或者 PVC,而是后端的存储系统,比如 Ceph,如果使用它作为后端存储,你必须对其非常熟悉,方便在出问题的时候好排查,如果你对这些存储系统都不熟悉,在使用的时候可能会出现很多问题。

上次更新: 2025/07/19, 21:23:02
Kubernetes应用质量管理
Kubernetes应用访问管理

← Kubernetes应用质量管理 Kubernetes应用访问管理→

最近更新
01
elastic 账户认证 401 问题
07-20
02
使用 helm 安装 es 和 kibana
07-20
03
elastic stack 搭建
07-20
更多文章>
Theme by Vdoing | Copyright © 2019-2025 乔克 | MIT License | 渝ICP备20002153号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式