Kubernetes存储入门

目录

一,Kubernetes存储概念

1,volume的概念

2,volume的类型

二,配置volume存储

1,通过emprydir共享数据

2,使用hostpath挂载宿主机文件

3,使用nfs挂载至容器

三,配置pv持久卷

1,pv回收策略

2,pv访问策略

3,pv的配置方式

4,PersistentVolumeclaim(Pvc,持久卷声明)

5,创建基于hostpath的pv

6,创建基于nfs的pv

一,Kubernetes存储概念

在生产环境中,应用程序经常需要持久化存储数据,如用户上传的文件、数据库记录等。然而,在Kubernetes 中,由于容器本身具有高度的可扩展性和编排能力,传统的数据存储方式已无法满足需求。因此,Kubernetes 抽象出了 Volume 的概念,用于解决容器的数据存储问题。
Volume 在 Kubernetes 中扮演着至关重要的角色,它允许容器在重启或迁移时保持数据的持久性。本章将详细介绍 Volume 的概念、类型以及使用方法。从 emptyDir 的临时存储,到 HostPath 的宿主机文件挂载,再到 NFS 网络存储的共享,以及 PersistentVolume 和PersistentVolumeclaim 的动态存储管理。

1,volume的概念

对于大多数的项目而言,数据文件的存储是非常常见的需求,比如存储用户上传的头像、文件以及数据库的数据。在 Kubernetes 中,由于应用的部署具有高度的可扩展性和编排能力(不像传统架构部署在固定的位置),因此把数据存放在容器中是非常不可取的,这样也无法保障数据的安全。

在传统的架构中,如果要使用这些存储,需要提前在宿主机挂载,然后程序才能访问,在实际使用时,经常碰到新加节点忘记挂载存储导致的一系列问题。而 Kubernetes 在设计之初就考虑了这些问题,并抽象出 Volume 的概念用于解决数据存储的问题。
在容器中的磁盘文件是短暂的,当容器崩溃时,Kubect1 会重新启动容器,但是容器运行时产生的数据文件都会丢失,之后容器会以干净的状态启动。另外,当一个 Pod 运行多个容器时,各个容器可能需要共享一些文件,诸如此类的需求都可以使用 Volume 解决。

2,volume的类型

在传统架构中,企业内可能有自己的存储平台,比如NFS、Ceph、GlusterFs、Minio 等。如果所在的环境在公有云,也可以使用公有云提供的 NAS、对象存储等。在 Kubernetes 中,Volume 也支持配置这些存储,用于挂载到 Pod 中实现数据的持久化。Kubernetes Volume 文持的卷的类型有很多。

  • 以下是常用卷的类型

emptyDir:emptyDir 是一种临时存储卷,当 Pod 被调度到节点时创建,初始为空,当 Pod 从节点移除时,数据会被永久删除。

hostPath:hostPath 卷将节点的文件系统中的文件或目录挂载到 Pod 中,数据会保存在节点的文件系统上,当 Pod 被删除后,数据仍然保留在节点上。

nfs:nfs 卷允许将 NFS(网络文件系统)共享挂载到 Pod 中,多个 Pod 可以共享同一个 NFS 存储,适合需要跨节点共享数据的场景。

configmap:用于存储配置文件

secret:用于存储敏感数据

PersistentVolumeclaim:对PersistentVolume的申请

二,配置volume存储

1,通过emprydir共享数据

emptyDir 是一个特殊的 Volume 类型,与上述 Volume 不同的是,如果删除Pod,EmptyDir 卷中的数据也将被删除,所以一般 emptyDir 用于 Pod 中不同容器共享数据,比如一个 Pod 存在两个容器A和容器 B,容器A需要使用容器B产生的数据,此时可以采用 emptyir 共享数据,类似的使用如 Filebeat收集容器内程序产生的日志。

编写emprydir的deployment文件

[root@k8s-master ~]# vim nginx-empty.yaml 

  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.7.9
        imagePullPolicy: IfNotPresent
        name: nginx01
        volumeMounts:                 ##这三行定义nginx01挂载的目录
        - mountPath: /opt
          name: share-volume                  
      - image: nginx:1.7.9
        imagePullPolicy: IfNotPresent
        name: nginx02
        command:
        - sh
        - -c
        - sleep 3600
        volumeMounts:                  ##这三行定义nginx01挂载的目录
        - mountPath: /mnt
          name: share-volume
      volumes:                                ##类型为emprydir的卷类型
      - name: share-volume                 ##此处名字要和上面一样
        emptyDir: {}
          #medium: Memory

创建该deployment

[root@k8s-master ~]# ku create -f nginx-empty.yaml 
deployment.apps/nginx created

查看创建的结果

[root@k8s-master ~]# ku get pod
NAME                    READY   STATUS    RESTARTS   AGE
nginx-8f8dcfd8c-sc6xf   2/2     Running   0          4s

登陆到第一个容器创建文件

[root@k8s-master ~]# ku exec -it nginx-8f8dcfd8c-sc6xf -c nginx01 -- bash
root@nginx-8f8dcfd8c-sc6xf:/# touch /opt/aaa 
root@nginx-8f8dcfd8c-sc6xf:/# touch /opt/bbb

登陆到第二个容器查看

[root@k8s-master ~]# ku exec -it nginx-8f8dcfd8c-sc6xf -c nginx02 -- bash
root@nginx-8f8dcfd8c-sc6xf:/# ls /mnt/
aaa  bbb

2,使用hostpath挂载宿主机文件

HostPath 卷可以将节点上的文件或目录挂载到 Pod 上,用于实现 Pod 和宿主机之间的数据共享,常用的示例有挂载宿主机的时区至 Pod,或者将 Pod 的日志文件挂载到宿主机等。

编写deployment文件,实现hostpath挂载

[root@k8s-master ~]# vim nginx-hostPath.yaml 

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.7.9
        imagePullPolicy: IfNotPresent
        name: nginx
        volumeMounts:
        - mountPath: /etc/localtime            ##容器里面挂载的目录
          name: timezone-time                    ##要和下面name一样


      volumes:
      - name: timezone-time
        hostPath:
          path: /etc/localtime                 ##宿主机的目录
          type: File

注意:/etc/localtime 用于配置系统时区(此文件是一个链接文件,链接自/usr/share/zoneinfo/Asia/Shanghai)。

创建pod

[root@k8s-master ~]# ku delete -f nginx-empty.yaml 
deployment.apps "nginx" deleted

查看创建结果

[root@k8s-master ~]# ku get pod
NAME                     READY   STATUS    RESTARTS   AGE
nginx-59f95c99b5-rzf7t   1/1     Running   0          3s

进入容器测试(目的:配置容器和宿主机时间一致)

[root@k8s-master ~]# ku exec -it nginx-59f95c99b5-rzf7t -- bash
root@nginx-59f95c99b5-rzf7t:/# ls -l /etc/localtime
-rw-r--r--. 4 root root 561 Dec 11  2024 /etc/localtime


root@nginx-59f95c99b5-rzf7t:/# date
Wed Jul  9 11:49:38 CST 2025

3,使用nfs挂载至容器

在每个节点安装nfs   dnf -y install nfs-utils

设置共享目录

[root@k8s-master ~]# mkdir /opt/wwwroot  && echo "this is my test file">/opt/wwwroot/index.html

[root@k8s-master ~]# vim /etc/exports

/opt/wwwroot 192.168.10.0/24(rw,sync,no_root_squash)

开启nfs

[root@k8s-master ~]# systemctl start nfs
[root@k8s-master ~]# systemctl start rpcbind

编写deployment文件,挂载nfs

[root@k8s-master ~]# vim nginx-nfsVolume.yaml 

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.7.9
        imagePullPolicy: IfNotPresent
        name: nginx
        volumeMounts:
        - mountPath: /usr/share/nginx/html            ##容器内的目录
          name: nfs-volume
      volumes:
      - name: nfs-volume
        nfs:
          server: 192.168.10.101
          path: /opt/wwwroot               ##宿主机创建的挂载目录

创建此pod

[root@k8s-master ~]# ku create -f nginx-nfsVolume.yaml 
deployment.apps/nginx created
[root@k8s-master ~]# ku get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
nginx-7bf6df4679-m8vh8   1/1     Running   0          8s    10.244.169.139   k8s-node2            

直接访问容器查看挂载结果

[root@k8s-master ~]# curl 10.244.169.139
this is my test file

三,配置pv持久卷

虽然 volume 已经可以接入大部分存储后端,但是实际使用时还有诸多的问题。比如:

  • 当某个数据卷不再被挂载使用时,里面的数据如何处理?
  • 如果想要实现只读挂载,要如何处理?
  • 如果想要只能有一个 Pod 挂载,要如何处理?

为此,kubernetes 引入了两个新的 API 资源:PersistentVolume(持久卷,简称PV)和PersistentVolumeclaim(持久卷声明,简称PVC)。

1,pv回收策略

当用户使用完卷时,可以从 API 中删除 PVC 对象,从而允许回收资源。回收策略会告诉 PV 如何处理改卷。目前回收策略可以设置为 Retain、Recycle 和 Delete。静态PV默认的为 Retain,动态 PV 默认为 Delete.

  • Retain:保留,该策略允许手动回收资源,当删除 PVC 时,PV 仍然存在,PV 中的数据也存在。volume被视为已释放,管理员可以手动回收卷。
  • 12
  • Recycle:回收,如果 volume 插件文持,Recycle 策略会对卷执行 rm -rf 清理该 PV,卷中的数据已经没了,但卷还在,使其可用于下一个新的 PVC,但是本策略将会被弃用,目前只有 NFS 和 HostPath支持该策略。
  • Delete:删除,如果 volume 插件支持,删除 PVC 时会同时删除 PV,PV 中的数据自然也就没了。动态卷默认为 Delete,目前支持 Delete 的存储后端包括 AWS EBS、GCE PD、Azure Disk、0penstackCinder 等。

2,pv访问策略

在实际使用 PV时,可能针对不同的应用会有不同的访问策略,比如某类 Pod 可以读写,某类 Pod 只能读,或者需要配置是否可以被多个不同的 Pod 同时读写等,此时可以使用 PV的访问策略进行简单控制,目前支持的访问策略如下:

  • Readwrite0nce:单路可读可写,可以被单节点以读写模式挂载,命令行中可以被缩写橙RW0
  • ReadonlyMany:多路只读,可以被多节点以只读模式挂载,命令行中可以被缩写为 ROX。
  • ReadwriteMany:多路可读可写,可以被多个节点以读写模式挂载,命令行中可以被缩写为 RWX
  • Readwrite0ncePod:单节点只读(1.22+),只能被一个 Pod 以读写的模式挂载,命令行中可以被缩写为 RWOP。

虽然 PV 在创建时可以指定不同的访问策略,但是也要后端的存储支持才行。比如一般情况下,大部分块存储是不支持 ReadwriteMany 的。
 

3,pv的配置方式

1,静态配置
静态配置是手动创建 PV 并定义其属性,例如容量、访问模式、存储后端等。在这种情况下,Kubernetes管理员负责管理和配置 PV,然后应用程序可以使用这些 PV。静态配置通常用于一些固定的存储后端,如NFS 


2,动态配置
动态配置允许 Kubernetes 集群根据 PVC 的需求自动创建 PV,在这种情况下,管理员只需为存储后端配置 storageclass,然后应用程序就可以通过 PVC 请求存储。Kubernetes 将自动创建与 PVC 匹配的PV,并将其绑定到 PVC上。这种方法使得存储管理更加灵活和可扩展,允许管理员在集群中动态添加、删除、和管理存储资源

4,PersistentVolumeclaim(Pvc,持久卷声明)


        当 kubernetes 管理员提前创建好了 PV,我们又应该如何使用它呢?这里介绍 kubernetes 的另一个概念 PersistentVolumeclaim(简称PVC)。PVC 是其他技术人员
在 kubernetes 上对存储的申请,他可以标明一个程序需要用到什么样的后端存储、多大的空间以及什么访问模式进行挂载。这一点和 Pod 的 Qos 配置类似,Pod 消耗节点资源,PVC 消耗 PV 资源,Pod 可以请求特定级别的资源(CPU和内存),PVC可以请求特定的大小和访问模式的PV。例如申请一个大小为5G且只能被一个 Pod 只读访问的存储。
在实际使用时,虽然用户通过 PVC获取存储支持,但是用户可能需要具有不同性质的 PV来解决不同的问题,比如使用 SSD 硬盘来提高性能。所以集群管理员需要根据不同的存储后端来提供各种 PV,而不仅仅是大小和访问模式的区别,并且无须让用户了解这些卷的具体实现方式和存储类型,打扫了存储的解耦,降低了存储使用的复杂度。
接下来我们来看看如何让 PVC 和前面创建的 PV 绑定。PVC和 PV进行绑定的前提条件是一些参数必须匹配,比如 accessModes、storageclassName、volumeMode 都需要相同,并且 PVC 的 storage 需要小于等于 PV 的 storage 配置。

5,创建基于hostpath的pv

由于不知道master会把那个容器调度到那个节点,所有需要在所有节点创建主机目录

[root@k8s-master ~]# mkdir /mnt/data

编写hostpath的yaml文件

[root@k8s-master ~]# vim hostpath-pv.yaml 

kind: PersistentVolume
apiVersion: v1
metadata:
  name: mypv-hostpath
  labels:
    type: local
spec:
  storageClassName: pv-hostpath
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"

创建并查看此pv

[root@k8s-master ~]# ku create -f hostpath-pv.yaml 
persistentvolume/mypv-hostpath created
[root@k8s-master ~]# ku  get pv
NAME            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
mypv-hostpath   10Gi       RWO            Retain           Available           pv-hostpath             4s

为hostpath类型的pv创建pvc

[root@k8s-master ~]# vim pvc-hostpath.yaml 

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mypvc-hostpath                  ##hostpv的名字
spec:
  storageClassName: pv-hostpath             ##host storageclass的名字
  accessModes:
    - ReadWriteOnce                   ##指定访问策略为单节点读写
  resources:
    requests:
      storage: 3Gi               ##指定可使用空间为3G
注意;我们创建了一个存储为10G的hostpv,但是在pvc中可以定义可使用的空间

创建此pvc

[root@k8s-master ~]# ku create -f pvc-hostpath.yaml 
persistentvolumeclaim/mypvc-hostpath created

查看此pvc的详细信息

[root@k8s-master ~]# ku get pvc
NAME             STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc-hostpath   Bound    mypv-hostpath   10Gi       RWO            pv-hostpath    4s

pvc的使用

  • 上述创建了 PV,并使用 PVC与其绑定,现在还差一步就能让程序使用这块存储,那就是将 PVC 挂载到 Pod。和之前的挂载方式类似,PVC的挂载也是通过 volumes 字段进行配置的,只不过之前需要根据不同的存储后端填写很多复杂的参数’而使用 PVC进行挂载时,只填写PVC的名字即可,不需要再关心任何的存储细节,这样即使不是 Kubemetes 管理员,不懂存储的其他技术人员想要使用存储,也可以非常简单地进行配置和使用。比如我们将之前创建的 hostPath 类型的 PVC 挂载到 Pod 中,可以看到只需要配置-个PersistentVolumeclaim 类型的 volumes,claimName 配置为 PVC 的名称即可

创建pod绑定hostpath的pv

[root@k8s-master ~]# vim pvc-pv-pod-hostpath.yaml 

kind: Pod
apiVersion: v1
metadata:
  name: hostpath-pv-pod
spec:
  volumes:
    - name: task-pv-storage
      persistentVolumeClaim:
       claimName: mypvc-hostpath
  containers:
    - name: task-pv-container
      image: nginx:1.7.9
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts: 
        - mountPath: "/usr/share/nginx/html"            ##指定容器内绑定的路径
          name: task-pv-storage

##宿主机要绑定的路径已经在host pv指定:/mnt/data

查看此pod在那个节点上运行

[root@k8s-master ~]# ku get pod -o wide
NAME              READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
hostpath-pv-pod   1/1     Running   0          4s    10.244.169.140   k8s-node2              

发现在node2节点上运行

在node2节点上创建要访问的内容

[root@k8s-node2 images]# cd /mnt/data
[root@k8s-node2 data]# echo "1111111111">index.html

验证,访问此容器

[root@k8s-master ~]# curl 10.244.169.140 
1111111111

6,创建基于nfs的pv

编写基于nfs的yaml文件

[root@k8s-master ~]# vim nfs-pv.yaml 

apiVersion: v1
kind: PersistentVolume
metadata:
  name: mypv-nfs
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: pvc-nfs
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /opt/wwwroot/                 ##指定路径
    server: 192.168.10.101             ##指定主机ip

创建pv

[root@k8s-master ~]# ku create -f nfs-pv.yaml 
persistentvolume/mypv-nfs created

查看pv创建结果

[root@k8s-master ~]# ku get pv
NAME            CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                    
mypv-nfs        5Gi        RWO            Recycle          Available                            pvc-nfs                 2s

创建基于nfs的pvc

[root@k8s-master ~]# vim pvc-nfs.yaml 

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mypvc-nfs            ##名字要和pv一样
spec:
  storageClassName: pvc-nfs              
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
~                     

创建pvc

[root@k8s-master ~]# ku create -f pvc-nfs.yaml 
persistentvolumeclaim/mypvc-nfs created

查看pvc

[root@k8s-master ~]# ku get pvc
NAME             STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc-nfs        Bound    mypv-nfs        5Gi        RWO            pvc-nfs        5s

使用基于nfs的pvc,先创建pod并绑定nfs的pvc

[root@k8s-master ~]# vim pvc-pv-pod-nfs.yaml 

kind: Pod
apiVersion: v1
metadata:
  name: pvc-nfs
spec:
  volumes:
    - name: pvc-nfs01
      persistentVolumeClaim:
       claimName: mypvc-nfs             ##这个名字是基于nfs创建的pvc的名字
  containers:
    - name: task-pv-container
      image: nginx:1.7.9
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: pvc-nfs01

创建此pod

[root@k8s-master ~]# ku create -f pvc-pv-pod-nfs.yaml 
pod/pvc-nfs created

验证

[root@k8s-master ~]# ku get pod -o wide          ##查看此pod的IP
NAME              READY   STATUS    RESTARTS   AGE     IP               NODE       
pvc-nfs           1/1     Running   0          35s     10.244.169.141   k8s-node2              

[root@k8s-master ~]# curl 10.244.169.141
this is my test file


注意:这里访问的内容是之前创建的nfs
[root@k8s-master ~]# cd /opt/wwwroot/
[root@k8s-master wwwroot]# cat index.html 
this is my test file

你可能感兴趣的:(kubernetes,容器,云原生)