目 录CONTENT

文章目录

持久化存储入门

简中仙
2022-05-10 / 0 评论 / 0 点赞 / 115 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于2024-01-03,若内容或图片失效,请留言反馈。 本文如有错误或者侵权的地方,欢迎您批评指正!

一、Volumes

Container(容器)中的磁盘文件是短暂的,当容器崩溃时,kubelet会重新启动容器,但最初的文件将丢失,Container会以最干净的状态启动。另外,当一个Pod运行多个Container时,各个容器可能需要共享一些文件。Kubernetes Volume可以解决这两个问题。

一些需要持久化数据的程序才会用到Volumes,或者一些需要共享数据的容器需要volumes。

Redis-Cluster:nodes.conf

日志收集的需求:需要在应用程序的容器里面加一个sidecar,这个容器是一个收集日志的容器,比如filebeat,它通过volumes共享应用程序的日志文件目录。

Volumes:官方文档 https://kubernetes.io/zh/docs/concepts/storage/volumes/

http://docs.kubernetes.org.cn/429.html

背景

Docker也有卷的概念,但是在Docker中卷只是磁盘上或另一个Container中的目录,其生命周期不受管理。虽然目前Docker已经提供了卷驱动程序,但是功能非常有限,例如从Docker 1.7版本开始,每个Container只允许一个卷驱动程序,并且无法将参数传递给卷。

另一方面,Kubernetes卷具有明确的生命周期,与使用它的Pod相同。因此,在Kubernetes中的卷可以比Pod中运行的任何Container都长,并且可以在Container重启或者销毁之后保留数据。Kubernetes支持多种类型的卷,Pod可以同时使用任意数量的卷。

从本质上讲,卷只是一个目录,可能包含一些数据,Pod中的容器可以访问它。要使用卷Pod需要通过.spec.volumes字段指定为Pod提供的卷,以及使用.spec.containers.volumeMounts 字段指定卷挂载的目录。从容器中的进程可以看到由Docker镜像和卷组成的文件系统视图,卷无法挂载其他卷或具有到其他卷的硬链接,Pod中的每个Container必须独立指定每个卷的挂载位置。

1、emptyDir

和上述volume不同的是,如果删除Pod,emptyDir卷中的数据也将被删除,一般emptyDir卷用于Pod中的不同Container共享数据。它可以被挂载到相同或不同的路径上。

默认情况下,emptyDir卷支持节点上的任何介质,可能是SSD、磁盘或网络存储,具体取决于自身的环境。可以将emptyDir.medium字段设置为Memory,让Kubernetes使用tmpfs(内存支持的文件系统),虽然tmpfs非常快,但是tmpfs在节点重启时,数据同样会被清除,并且设置的大小会被计入到Container的内存限制当中。

使用emptyDir卷的示例,直接指定emptyDir为{}即可:

# cat nginx-deploy.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: "2020-09-19T02:41:11Z"
  generation: 1
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  progressDeadlineSeconds: 600
  replicas: 2
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: nginx
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.15.2
        imagePullPolicy: IfNotPresent
        name: nginx
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:			# 挂载在容器内
        - mountPath: /opt
          name: share-volume
      - image: nginx:1.15.2
        imagePullPolicy: IfNotPresent
        name: nginx2
        command:		# 同一命名空间内nginx80端口冲突
        - sh
        - -c
        - sleep 3600
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /mnt
          name: share-volume
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - name: share-volume
        emptyDir: {}		#emptyDir配置
          #medium: Memory		# 配置使用内存

启动pod,并查看状态

# kubectl apply -f pod_emptydir.yaml
pod/pod-emptydir created
#
# kubectl get pod -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES
pod-emptydir   2/2     Running   0          10s   10.244.2.166   k8s-node02   <none>           <none>
#
# kubectl describe pod pod-emptydir
Name:         pod-emptydir
Namespace:    default
Priority:     0
Node:         k8s-node02/172.16.1.112
Start Time:   Fri, 12 Jun 2020 22:49:11 +0800
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"pod-emptydir","namespace":"default"},"spec":{"containers":[{"image":"...
Status:       Running
IP:           10.244.2.166
IPs:
  IP:  10.244.2.166
Containers:
  myapp-pod:
    Container ID:   docker://d45663776b40a24e7cfc3cf46cb08cf3ed6b98b023a5d2cb5f42bee2234c7338
    Image:          registry.cn-beijing.aliyuncs.com/google_registry/myapp:v1
    Image ID:       docker-pullable://10.0.0.110:5000/k8s-secret/myapp@sha256:9eeca44ba2d410e54fccc54cbe9c021802aa8b9836a0bcf3d3229354e4c8870e
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Fri, 12 Jun 2020 22:49:12 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /cache from cache-volume (rw)  ##### 挂载信息
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-v48g4 (ro)
  busybox-pod:
    Container ID:  docker://c2917ba30c3322fb0caead5d97476b341e691f9fb1990091264364b8cd340512
    Image:         registry.cn-beijing.aliyuncs.com/google_registry/busybox:1.24
    Image ID:      docker-pullable://registry.cn-beijing.aliyuncs.com/ducafe/busybox@sha256:f73ae051fae52945d92ee20d62c315306c593c59a429ccbbdcba4a488ee12269
    Port:          <none>
    Host Port:     <none>
    Command:
      /bin/sh
      -c
      sleep 3600
    State:          Running
      Started:      Fri, 12 Jun 2020 22:49:12 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /test/cache from cache-volume (rw)  ##### 挂载信息
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-v48g4 (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  cache-volume:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
    SizeLimit:  <unset>
  default-token-v48g4:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-v48g4
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age   From                 Message
  ----    ------     ----  ----                 -------
  Normal  Scheduled  3s    default-scheduler    Successfully assigned default/pod-emptydir to k8s-node02
  Normal  Pulled     2s    kubelet, k8s-node02  Container image "registry.cn-beijing.aliyuncs.com/google_registry/myapp:v1" already present on machine
  Normal  Created    2s    kubelet, k8s-node02  Created container myapp-pod
  Normal  Started    2s    kubelet, k8s-node02  Started container myapp-pod
  Normal  Pulled     2s    kubelet, k8s-node02  Container image "registry.cn-beijing.aliyuncs.com/google_registry/busybox:1.24" already present on machine
  Normal  Created    2s    kubelet, k8s-node02  Created container busybox-pod
  Normal  Started    2s    kubelet, k8s-node02  Started container busybox-pod

emptyDir验证

在pod中的myapp-pod容器内操作

# kubectl exec -it pod-emptydir -c myapp-pod -- sh
/ # cd /cache
/cache # pwd
/cache
/cache # date >> data.info
/cache # ls -l
total 4
-rw-r--r--    1 root     root            29 Jun 12 14:53 data.info
/cache # cat data.info
Fri Jun 12 14:53:27 UTC 2020

在pod中的busybox-pod容器内操作

# kubectl exec -it pod-emptydir -c busybox-pod -- sh
/ # cd /test/cache
/test/cache # ls -l
total 4
-rw-r--r--    1 root     root            29 Jun 12 14:53 data.info
/test/cache # cat data.info
Fri Jun 12 14:53:27 UTC 2020
/test/cache # echo "===" >> data.info
/test/cache # date >> data.info
/test/cache # cat data.info
Fri Jun 12 14:53:27 UTC 2020
===
Fri Jun 12 14:56:05 UTC 2020

由上可见,一个Pod中多个容器可共享同一个emptyDir卷。

2、hostPath

hostPath卷可将节点上的文件或目录挂载到Pod上,用于Pod自定义日志输出或访问Docker内部的容器等。

hostPath 的一些用法有

  • 运行一个需要访问 Docker 引擎内部机制的容器;请使用 hostPath 挂载 /var/lib/docker 路径。
  • 在容器中运行 cAdvisor 时,以 hostPath 方式挂载 /sys。
  • 允许 Pod 指定给定的 hostPath 在运行 Pod 之前是否应该存在,是否应该创建以及应该以什么方式存在。

使用hostPath卷的示例。将主机的/data目录挂载到Pod的/test-pd目录:

 # cat nginx-deploy.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: "2020-09-19T02:41:11Z"
  generation: 1
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  progressDeadlineSeconds: 600
  replicas: 2
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: nginx
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.15.2
        imagePullPolicy: IfNotPresent
        name: nginx
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /opt
          name: share-volume
        - mountPath: /etc/timezone				# 挂载时间
          name: timezone
      - image: nginx:1.15.2
        imagePullPolicy: IfNotPresent
        name: nginx2
        command:
        - sh
        - -c
        - sleep 3600
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /mnt
          name: share-volume
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - name: timezone
        hostPath:
          path: /etc/timezone
          type: File

hostPath卷常用的type(类型)如下:

类型说明
空字符串默认选项,意味着挂载hostPath卷之前不会执行任何检查
DirectoryOrCreate如果给定的path不存在任何东西,那么将根据需要创建一个权限为0755的空目录,和Kubelet具有相同的组和权限
Directory目录必须存在于给定的路径下
FileOrCreate如果给定的路径不存储任何内容,则会根据需要创建一个空文件,权限设置为0644,和Kubelet具有相同的组和所有权
File文件,必须存在于给定路径中
SocketUNIX套接字,必须存在于给定路径中
CharDevice字符设备,必须存在于给定路径中
BlockDevice块设备,必须存在于给定路径中

启动pod,并查看状态

# kubectl apply -f pod_hostpath.yaml
pod/pod-hostpath created
# kubectl get pod -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES
pod-hostpath   1/1     Running   0          17s   10.244.4.133   k8s-node01   <none>           <none>
# kubectl describe pod pod-hostpath
Name:         pod-hostpath
Namespace:    default
Priority:     0
Node:         k8s-node01/172.16.1.111
Start Time:   Sat, 13 Jun 2020 16:12:15 +0800
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"pod-hostpath","namespace":"default"},"spec":{"containers":[{"image":"...
Status:       Running
IP:           10.244.4.133
IPs:
  IP:  10.244.4.133
Containers:
  myapp-pod:
    Container ID:   docker://8cc87217fb483288067fb6d227c46aa890d02f75cae85c6d110646839435ab96
    Image:          registry.cn-beijing.aliyuncs.com/google_registry/myapp:v1
    Image ID:       docker-pullable://registry.cn-beijing.aliyuncs.com/google_registry/myapp@sha256:9eeca44ba2d410e54fccc54cbe9c021802aa8b9836a0bcf3d3229354e4c8870e
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Sat, 13 Jun 2020 16:12:17 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /test-k8s/hostpath-dir from hostpath-dir-volume (rw)
      /test/hostpath-file/test.conf from hostpath-file-volume (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-v48g4 (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  hostpath-dir-volume:
    Type:          HostPath (bare host directory volume)
    Path:          /k8s/hostpath-dir
    HostPathType:  DirectoryOrCreate
  hostpath-file-volume:
    Type:          HostPath (bare host directory volume)
    Path:          /k8s2/hostpath-file/test.conf
    HostPathType:  FileOrCreate
  default-token-v48g4:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-v48g4
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age        From                 Message
  ----    ------     ----       ----                 -------
  Normal  Scheduled  <unknown>  default-scheduler    Successfully assigned default/pod-hostpath to k8s-node01
  Normal  Pulled     12m        kubelet, k8s-node01  Container image "registry.cn-beijing.aliyuncs.com/google_registry/myapp:v1" already present on machine
  Normal  Created    12m        kubelet, k8s-node01  Created container myapp-pod
  Normal  Started    12m        kubelet, k8s-node01  Started container myapp-pod

宿主机操作

根据pod,在k8s-node01节点宿主机操作【因为Pod分配到了该节点】

# 对挂载的目录操作
# pwd
/k8s/hostpath-dir
# echo "dir" >> info
# date >> info
# cat info
dir
Sat Jun 13 16:22:37 CST 2020
# 对挂载的文件操作
# pwd
/k8s2/hostpath-file
# echo "file" >> test.conf
# date >> test.conf
# cat test.conf
file
Sat Jun 13 16:23:05 CST 2020

在Pod 容器中操作

# 进入pod 中的指定容器【如果只有一个容器,那么可以不指定容器】
# kubectl exec -it pod-hostpath -c myapp-pod -- /bin/sh
##### 对挂载的目录操作
/ # cd /test-k8s/hostpath-dir
/test-k8s/hostpath-dir # ls -l
total 4
-rw-r--r--    1 root     root            33 Jun 13 08:22 info
/test-k8s/hostpath-dir # cat info
dir
Sat Jun 13 16:22:37 CST 2020
/test-k8s/hostpath-dir # date >> info
/test-k8s/hostpath-dir # cat info
dir
Sat Jun 13 16:22:37 CST 2020
Sat Jun 13 08:26:10 UTC 2020
##### 对挂载的文件操作
# cd /test/hostpath-file/
/test/hostpath-file # cat test.conf
file
Sat Jun 13 16:23:05 CST 2020
/test/hostpath-file # echo "file====" >> test.conf
/test/hostpath-file # cat test.conf
file
Sat Jun 13 16:23:05 CST 2020
file====

3、NFS

NFS 是Network File System的缩写,即网络文件系统。Kubernetes中通过简单地配置就可以挂载NFS到Pod中,而NFS中的数据是可以永久保存的,同时NFS支持同时写操作。Pod被删除时,Volume被卸载,内容被保留。这就意味着NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递。

所有节点

yum install nfs-utils -y

安装nfs服务, node01

systemctl start nfs
sudo systemctl enable nfs
mkdir -p /data/nfs
vim /etc/exports
/data/nfs/ 192.168.0.0/24(rw,sync,no_subtree_check,no_root_squash)
exportfs -rv
systemctl reload nfs-server
权限说明
rwread-write,可读写
sync文件同时写入硬盘和内存
async文件暂存于内存,而不是直接写入内存
no_root_squashNFS客户端连接服务端时如果使用的是root的话,那么对服务端分享的目录来说,也拥有root权限。显然开启这项是不安全的
root_squashNFS客户端连接服务端时如果使用的是root的话,那么对服务端分享的目录来说,拥有匿名用户权限,通常他将使用nobody或nfsnobody身份
all_squash不论NFS客户端连接服务端时使用什么用户,对服务端分享的目录来说都是拥有匿名用户权限
anonuid匿名用户的UID值,通常是nobody或nfsnobody,可以在此处自行设定
anongid匿名用户的GID值
insecure当mount监听端口大于1024时需要使用此参数

master01

showmount -e xx.xx.xx.xx   # xx.xx.xx.xx为服务端IP,有返回信息说明成功
mount -t nfs 192.168.0.204:/data/nfs /mnt/
cd /mnt/
touch 123
unmount /mnt

volumes挂载

案例https://www.cnblogs.com/xiajq/p/11395211.html

生产环境不用nfs,也不直接用NFS,用PV来连接

可以用云服务商的来存储

# cat nginx-deploy.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: "2020-09-19T02:41:11Z"
  generation: 1
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  progressDeadlineSeconds: 600
  replicas: 2
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: nginx
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.15.2
        imagePullPolicy: IfNotPresent
        name: nginx
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /opt
          name: share-volume
        - mountPath: /etc/timezone
          name: timezone
      - image: nginx:1.15.2
        imagePullPolicy: IfNotPresent
        name: nginx2
        command:
        - sh
        - -c
        - sleep 3600
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /mnt
          name: share-volume
        - mountPath: /opt
          name: nfs-volume
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - name: nfs-volume
        nfs:
          server: 192.168.0.204
          path: /data/nfs/test-dp

二、PV&PVC

1、PV、PVC

PersistentVolume:简称PV,是由Kubernetes管理员设置的存储,可以配置Ceph、NFS、GlusterFS等常用存储配置,相对于Volume配置,提供了更多的功能,比如生命周期的管理、大小的限制。PV分为静态和动态。没有命名空间限制。

PersistentVolumeClaim:简称PVC,是对存储PV的请求,表示需要什么类型的PV,需要存储的技术人员只需要配置PVC即可使用存储,或者Volume配置PVC的名称即可。有命名空间限制。

官方文档:https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/

2、存储配置

1、PV回收策略

PV回收策略说明
Retain保留,该策略允许手动回收资源,当删除PVC时,PV仍然存在,PV被视为已释放,管理员可以手动回收卷
Recycle回收,如果Volume插件支持,Recycle策略会对卷执行rm -rf清理该PV,并使其可用于下一个新的PVC,但是本策略将来会被弃用,目前只有NFS和HostPath支持该策略
Delete删除,如果Volume插件支持,删除PVC时会同时删除PV,动态卷默认为Delete,目前支持Delete的存储后端包括AWS EBS, GCE PD, Azure Disk, or OpenStack Cinder等

可以通过persistentVolumeReclaimPolicy: Recycle字段配置

官方文档:https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes/#reclaim-policy

2、PV访问策略

PV访问策略说明
ReadWriteOnce可以被单节点以读写模式挂载,命令行中可以被缩写为RWO
ReadOnlyMany可以被多个节点以只读模式挂载,命令行中可以被缩写为ROX
ReadWriteMany可以被多个节点以读写模式挂载,命令行中可以被缩写为RWX
ReadWriteOncePod只允许被单个Pod访问,需要K8s 1.22+以上版本,并且是CSI创建的PV才可使用

官方文档:https://kubernetes.io/zh-cn/docs/concepts/storage/persistent-volumes/#access-modes

3、存储分类

存储分类说明
文件存储一些数据可能需要被多个节点使用,比如用户的头像、用户上传的文件等,实现方式:NFS、NAS、FTP、CephFS等
块存储一些数据只能被一个节点使用,或者是需要将一块裸盘整个挂载使用,比如数据库、Redis等,实现方式:Ceph、GlusterFS、公有云
对象存储由程序代码直接实现的一种存储方式,云原生应用无状态化常用的实现方式,实现方式:一般是符合S3协议的云存储,比如AWS的S3存储、Minio、七牛云等

4、PV配置示例

1、NFS/NAS

# cat nfs-pv.yaml                  
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0001
spec:
  capacity:
    storage: 5Gi   # 定义内存大小,PVC的要比这个小
  volumeMode: Filesystem  # 文件系统类型
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle  # 策略上面有介绍
  storageClassName: nfs-slow   # 这个名字是PVC创建的时候要对应的名字
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:   #对应上nfs服务器的 ip 共享的文件夹
    path: /data/nfs       
    server: 192.168.1.104

# CREATE pv
# kubectl create -f nfs-pv.yaml 
persistentvolume/pv0001 created

#  查看PV
# kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
pv0001   5Gi        RWO            Recycle          Available           nfs-slow                2m3s
参数说明
capacity容量配置
volumeMode卷的模式,目前支持Filesystem(文件系统) 和 Block(块),其中Block类型需要后端存储支持,默认为文件系统
accessModes该PV的访问模式
storageClassNamePV的类,一个特定类型的PV只能绑定到特定类别的PVC,PVC和PV的这个名字一样,才能被绑定
persistentVolumeReclaimPolicy回收策略
mountOptions非必须,新版本中已弃用
nfsNFS服务配置,包括以下两个选项
path:NFS上的共享目录 /server:NFS的IP地址

2、NFS配置

NFS服务器安装服务端

yum install nfs* rpcbind -y

所有K8s节点安装NFS客户端

yum install nfs-utils -y

NFS服务端

mkdir /data/k8s -p

NFS服务器创建共享目录

# vim /etc/exports
/data/k8s/ *(rw,sync,no_subtree_check,no_root_squash)
# exportfs -r
# systemctl restart nfs rpcbind

挂载测试

mount -t nfs nfs-serverIP:/data/k8s /mnt/
Pv的状态说明
Available空闲的PV,没有被任何PVC绑定
Bound已经被PVC绑定
ReleasedPVC被删除,但是资源未被重新使用
Failed自动回收失败

3、HostPath

kind: PersistentVolume
apiVersion: v1
metadata:
  name: task-pv-volume
  labels:
    type: local
spec:
  storageClassName: hostpath
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:						# hostPath服务配置
    path: "/mnt/data"			# 宿主机路径

4、CephRBD

apiVersion: v1
kind: PersistentVolume
metadata:
  name: ceph-rbd-pv
spec:
  capacity:
    storage: 1Gi
  storageClassName: ceph-fast
  accessModes:
    - ReadWriteOnce
  rbd:
    monitors:
      - 192.168.1.123:6789
      - 192.168.1.124:6789
      - 192.168.1.125:6789
    pool: rbd
    image: ceph-rbd-pv-test
    user: admin
    secretRef:
      name: ceph-secret
    fsType: ext4
    readOnly: false
参数说明
monitorsCeph的monitor节点的IP
pool所用Ceph Pool的名称,可以使用ceph osd pool ls查看
imageCeph块设备中的磁盘映像文件,可以使用rbd create POOL_NAME/IMAGE_NAME --size 1024创建,使用rbd list POOL_NAME查看
userRados的用户名,默认是admin
secretRef用于验证Ceph身份的密钥
fsType文件类型,可以是ext4、XFS等
readOnly是否是只读挂载

5、挂载PVC

创建一个PVC

# 绑定到指定类型的PV
# cat test-pvc.yaml 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: task-pvc-claim   # PVC的名字,可自取
spec:
  storageClassName: nfs-slow  # 名字要对应上想绑定的PV上
  accessModes:
    - ReadWriteMany
#  volumeMode: Filesystem
  resources:
    requests:
      storage: 3Gi  
  
# create PVC
# kubectl create -f  test-pvc.yaml  
persistentvolumeclaim/myclaim created

# 查看PVC
# kubectl get pvc
NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
myclaim   Bound    pv0001   5Gi        RWX            nfs-slow       34s

# 再次查看PV,可看到状态已经发生改变
# kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
pv0001   5Gi        RWX            Recycle          Bound    default/myclaim   nfs-slow                13m

绑定到指定类型的PV

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  replicas: 2
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: nginx
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      volumes:
      - name: task-pv-storage
        persistentVolumeClaim:
          claimName: task-pvc-claim
      containers:
      - env:
        - name: TZ
          value: Asia/Shanghai
        - name: LANG
          value: C.UTF-8
        image: nginx
        imagePullPolicy: IfNotPresent
        name: nginx
        volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: task-pv-storage  # 名字要对应上想绑定的PV上

6、更改deployment使用PVC类型的volume

# 更改后的yaml,在对应位置加上以下参数调用PVC
volumeMounts:
- mountPath: /opt/pvc
  name: mypd

volumes:
   - name: mypd
     persistentVolumeClaim:
       claimName: myclaim
       
# 然后进入容器查看是否挂载成功
# kubectl exec -it nginx-5bb6d88dfb-w78k8 -c nginx2 -- bash
root@nginx-5bb6d88dfb-w78k8:/# df -h
Filesystem               Size  Used Avail Use% Mounted on
overlay                   37G  4.3G   33G  12% /
tmpfs                     64M     0   64M   0% /dev
tmpfs                    985M     0  985M   0% /sys/fs/cgroup
/dev/mapper/centos-root   37G  4.3G   33G  12% /mnt
192.168.1.104:/data/nfs   37G  3.0G   35G   9% /opt/pvc   # 这就是刚刚挂载的PVC

# 文件能共享
root@nginx-5bb6d88dfb-w78k8:/opt/pvc# ls 
pvc  qqq  test
root@nginx-5bb6d88dfb-w78k8:/opt/pvc# echo 11 > test 
root@nginx-5bb6d88dfb-w78k8:/opt/pvc# cat test       
11

7、PVC创建和挂载失败的原因

PVC一直Pending的原因:

  • lPVC的空间申请大小大于PV的大小
  • lPVC的StorageClassName没有和PV的一致
  • lPVC的accessModes和PV的不一致

挂载PVC的Pod一直处于Pending:

  • lPVC没有创建成功/PVC不存在
  • lPVC和Pod不在同一个Namespace

删除PVC后k8s会创建一个用于回收的Pod,根据PV的回收策略进行pv的回收,回收完以后PV的状态就会变成可被绑定的状态也就是空闲状态,其他的Pending状态的PVC如果匹配到了这个PV,他就能和这个PV进行绑定。

作者: 杜宽
链接: https://ke.qq.com/course/2738602

0

评论区