local-path-provisioner的使用(hostPath、local、local-path-provisioner三者对比)

前言

环境:k8s 1.22.17 、centos7.9
有时候,为了使用本地服务器上的磁盘存储资源,我们会使用hostPath这种方式来为k8s提供本地存储,本篇就来对比一下hostPath、local这两种使用本地服务器储存的方案,从而引出第三种local-path本地储存。

hostPath卷

hostPath 卷能将主机节点文件系统上的文件或目录挂载到你的 Pod 中,但是我们知道,pod重启后会随机调度,所以就需要为pod固定主机节点。下面仅演示hostPath的使用方法,挂载一个宿主机上的目录到pod中:

#hostPath示例一
#pod中直接使用hostPath卷
[root@matser data]# vim  nginx.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-179
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-179
  template:
    metadata:
      labels:
        app: nginx-179
    spec:
      nodeName: master					#固定pod能调度的节点,确保pod重启后仍能访问之前的数据
      containers:
      - image: nginx:1.7.9
        imagePullPolicy: IfNotPresent
        name: nginx-container
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        volumeMounts:
        - mountPath: /var/log/nginx		#持久化日志
          name: hostpath-volume
      restartPolicy: Always
      volumes:
      - name: hostpath-volume
        hostPath:						#hostPath卷
          path: /data/nginx				#使用宿主机上的/data/nginx目录
          type: DirectoryOrCreate

kubectl  apply -f nginx.yaml
kubectl  delete  -f nginx.yaml
#hostPath示例二
#pod中使用pvc,pvc与pv关联,pv中定义hostPath,pod固定调度节点
[root@matser data]# cat hostPath.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: hostpath-pv
  labels:
    type: local
spec:
  storageClassName: ""
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  hostPath:
    path: "/data/nginx"
    type: DirectoryOrCreate
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: hostpath-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-179
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-179
  template:
    metadata:
      labels:
        app: nginx-179
    spec:
      nodeName: master					#固定pod能调度的节点,确保pod重启后仍能访问之前的数据
      containers:
      - image: nginx:1.7.9
        imagePullPolicy: IfNotPresent
        name: nginx-container
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        volumeMounts:
        - mountPath: /var/log/nginx
          name: hostpath-volume
      restartPolicy: Always
      volumes:
      - name: hostpath-volume
        persistentVolumeClaim:		#使用pvc
          claimName: hostpath-pvc
[root@matser data]# 
#创建完成pv和pvc,当pvc与pv绑定之后发现并没有创建hostPath目录,只有当pod调度到节点上时才会真正创建hostPath目录。
#当pod重启之后,如果pod没有指定调度的节点,则仍然会在其他节点创建hostPath目录,所以为了使pod能访问之前的数据,都要把pod固定调度到指定节点。
#hostPath示例三
#pod中使用pvc,pvc与pv关联,pv中定义hostPath且定义节点亲和性

#为节点打标签,只打了一个节点,如果是多个节点,那么pod会随机调度到这些节点数据就没有唯一性而言。
[root@matser data]# kubectl  label  nodes node2 storage=true
[root@matser data]# cat hostPath.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: hostpath-pv
  labels:
    type: local
spec:
  storageClassName: ""
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  hostPath:
    path: "/data/nginx"
    type: DirectoryOrCreate
  nodeAffinity:							#定义了节点亲和性
    required:
      nodeSelectorTerms:
      - matchExpressions:				
        - key: storage
          operator: In
          values:
          - "true"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: hostpath-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-179
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-179
  template:
    metadata:
      labels:
        app: nginx-179
    spec:					#pod中不在指定调度节点而是有pv中定义了节点亲和性,k8s会根据pv的节点亲和性来判断pod要调度到哪些节点
      containers:
      - image: nginx:1.7.9
        imagePullPolicy: IfNotPresent
        name: nginx-container
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        volumeMounts:
        - mountPath: /var/log/nginx
          name: hostpath-volume
      restartPolicy: Always
      volumes:
      - name: hostpath-volume
        persistentVolumeClaim:
          claimName: hostpath-pvc
[root@matser data]# 

hostPath卷总结:我们发现,无论是pod中直接使用hostPath,还是pod调用pvc,pvc调用pv,pv中使用hostPath,两者本质上没啥区别,示例1和实例2中都在pod中固定了调度节点,而实例3是在pv中定义了节点亲和性。

local卷

local 卷所代表的是某个被挂载的本地存储设备,例如磁盘、分区或者目录。
local 卷只能用作静态创建的持久卷。不支持动态配置。

与 hostPath 卷相比,local 卷能够以持久和可移植的方式使用,而无需手动将 Pod 调度到节点。系统通过查看 PersistentVolume 的节点亲和性配置,就能了解卷的节点约束。然而,local 卷仍然取决于底层节点的可用性,并不适合所有应用程序。 如果节点变得不健康,那么 local 卷也将变得不可被 Pod 访问。使用它的 Pod 将不能运行。 使用 local 卷的应用程序必须能够容忍这种可用性的降低,以及因底层磁盘的耐用性特征而带来的潜在的数据丢失风险。

下面是一个使用 local 卷和 nodeAffinity 的持久卷示例:

#local的使用和上面示例三hostPath卷的使用没多大区别,如下
[root@matser data]# cat local.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  local:								#这里定义的是local卷,path字段定义的是目录,这个目录必须先创建
    path: /data/nginx
  nodeAffinity:							#节点亲和性
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: storage
          operator: In
          values:
          - "true"          
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-179
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-179
  template:
    metadata:
      labels:
        app: nginx-179
    spec:
      containers:					#没有定义节点选择器,k8s会根据pv定义的节点亲和性来调度pod
      - image: nginx:1.7.9
        imagePullPolicy: IfNotPresent
        name: nginx-container
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        volumeMounts:
        - mountPath: /var/log/nginx
          name: local-volume
      restartPolicy: Always
      volumes:
      - name: local-volume
        persistentVolumeClaim:
          claimName: local-pvc
[root@matser data]# 
使用 local 卷时,你需要设置 PersistentVolume 对象的 nodeAffinity 字段。 Kubernetes 调度器使用 PersistentVolume 的 nodeAffinity 信息来将使用 local 卷的 Pod 调度到正确的节点。
PersistentVolume 对象的 volumeMode 字段可被设置为 "Block" (而不是默认值 "Filesystem"),以将 local 卷作为原始块设备暴露出来。
使用 local 卷时,建议创建一个 StorageClass 并将其 volumeBindingMode 设置为 WaitForFirstConsumer。要了解更多详细信息,请参考 local StorageClass 示例。 延迟卷绑定的操作可以确保 Kubernetes 在为 PersistentVolumeClaim 作出绑定决策时,会评估 Pod 可能具有的其他节点约束,例如:如节点资源需求、节点选择器、Pod 亲和性和 Pod 反亲和性。

总结:local卷和hostPath没啥区别,都可以通过在pv中定义节点亲和性,这样使用该pv的pod就会被k8s调度到指定的节点。唯一区别的是,local可以使用裸设备磁盘、分区、目录,而hostPath只能是文件或目录。

local-path-provisioner动态供给localPath或local卷

无论是hostPath卷还是local 卷,他们都不支持动态扩容,而local-path-provisioner很好的弥补了这一缺陷。
使用local-path-provisioner的pod会被scheduler和pv controller同时进行控制,以确保在pod 重新调度后,pod还能调度到之前的节点上,从而使用还是读取之前的数据。

kubernetes-sigs版:https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner
注意kubernetes-sigs版不支持动态扩容/动态供给dynamically provisioning,所以建议使用rancher版。这里介绍的都是rancher版。
rancher版:https://github.com/rancher/local-path-provisioner

下载local-path-provisioner yml文件

# 进入https://github.com/rancher/local-path-provisioner,里面有很详细的安装教程
#安装local-path-provisioner
wget https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.24/deploy/local-path-storage.yaml

查看官方configmap解析

#local-path-storage.yaml中定义了一个configmap,我们先来看下官方样例中对这个cm的介绍.
provisioner的配置存储在一个configmap中,configmap包含1个json文件配置即config.json、1个Pod模板即helperPod.yaml、两个bash脚本即setup和teardown,如下所示:

kind: ConfigMap
apiVersion: v1
metadata:
  name: local-path-config
  namespace: local-path-storage
data:
  config.json: |-
        {
    
    
                "nodePathMap":[
                {
    
    
                        "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
                        "paths":["/opt/local-path-provisioner"]
                },
                {
    
    
                        "node":"yasker-lp-dev1",
                        "paths":["/opt/local-path-provisioner", "/data1"]
                },
                {
    
    
                        "node":"yasker-lp-dev3",
                        "paths":[]
                }
                ]
        }
  setup: |-
        #!/bin/sh
        set -eu
        mkdir -m 0777 -p "$VOL_DIR"
  teardown: |-
        #!/bin/sh
        set -eu
        rm -rf "$VOL_DIR"
  helperPod.yaml: |-
        apiVersion: v1
        kind: Pod
        metadata:
          name: helper-pod
        spec:
          containers:
          - name: helper-pod
            image: busybox

nodePathMap字段是一个数组,用于自定义在每个节点上存储数据的位置。
如果一个节点没有在nodePathMap上列出,而Kubernetes想要在它上面创建卷,那么default_path_for_non_listd_nodes中指定的路径将被用于分配。
如果nodePathMap上列出了一个节点,则将使用paths中指定的路径进行发放。
如果仅列出了节点,但路径设置为[],则提供程序将拒绝在该节点上提供服务。
如果节点上指定了多个路径,则在provision供给时将随机选择路径。
规则:路径必须是绝对路径,路径不能是根/,一个节点可以有多个不同的路径,不能重复列出节点,一个节点不能列出相同的路径。

sharedFileSystemPath允许提供者使用同时挂载在所有节点上的文件系统。在这种情况下,支持所有访问模式:ReadWriteOnce, ReadOnlyMany和ReadWriteMany存储声明。
另外,volumeBindingMode: Immediate可以在StorageClass定义中使用。
请注意nodePathMap和sharedFileSystemPath是互斥的。如果使用sharedFileSystemPath,则nodePathMap必须设置为[]。

setup脚本:在创建卷之前运行setup脚本来在节点上准备卷目录。
teardown脚本:删除卷后运行teardown脚本,清理节点上的卷目录。
helperPod.yaml模板:用于创建一个helper pod,用这个helper pod来执行setup和teardown脚本。

provisioner支持自动热重载,即可以在线修改local-path-config配置,provisioner会自动生效配置,从local-path-provisioner pod中可以查看日志,如果provisioner生效失败,则provisioner仍然会保持上一个有效的local-path-config配置。
#指定local-path-provisioner创建的卷类型,可以通过以下2种方式指定local-path-provisioner要给你创建什么类型的卷
当你手动创建PVC时,在pvc的中添加以下注解:
annotations:
  volumeType: <local or hostPath>

亦可以在存储类的定义中添加以下注解:
StorageClass:
annotations:
  defaultVolumeType: <local or hostPath>

需要注意的是:StorageClass的注释将应用于使用它的所有卷,如果PVC提供了注释,则覆盖SC上的注释。如果这两个注解都没有提供,那么默认使用hostPath。

开始安装local-path-provisioner

#查看修改local-path-storage.yaml内容
[root@matser data]# cat local-path-storage.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: local-path-storage
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: local-path-provisioner-service-account
  namespace: local-path-storage
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: local-path-provisioner-role
rules:
  - apiGroups: [ "" ]
    resources: [ "nodes", "persistentvolumeclaims", "configmaps" ]
    verbs: [ "get", "list", "watch" ]
  - apiGroups: [ "" ]
    resources: [ "endpoints", "persistentvolumes", "pods" ]
    verbs: [ "*" ]
  - apiGroups: [ "" ]
    resources: [ "events" ]
    verbs: [ "create", "patch" ]
  - apiGroups: [ "storage.k8s.io" ]
    resources: [ "storageclasses" ]
    verbs: [ "get", "list", "watch" ]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: local-path-provisioner-bind
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: local-path-provisioner-role
subjects:
  - kind: ServiceAccount
    name: local-path-provisioner-service-account
    namespace: local-path-storage
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: local-path-provisioner
  namespace: local-path-storage
spec:
  replicas: 1
  selector:
    matchLabels:
      app: local-path-provisioner
  template:
    metadata:
      labels:
        app: local-path-provisioner
    spec:
      serviceAccountName: local-path-provisioner-service-account
      containers:
        - name: local-path-provisioner
          image: rancher/local-path-provisioner:v0.0.24
          imagePullPolicy: IfNotPresent
          command:
            - local-path-provisioner
            - --debug
            - start
            - --config
            - /etc/config/config.json
          volumeMounts:
            - name: config-volume
              mountPath: /etc/config/
          env:
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
      volumes:
        - name: config-volume
          configMap:
            name: local-path-config
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-path
  annotations:					#添加了注释,表示StorageClass提供哪些卷类型,可以是hostPath和local,默认值为hostPath
   volumeType: hostPath
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain			#修改为Retain,原来默认是Delete
allowVolumeExpansion: true		#允许扩容
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: local-path-config
  namespace: local-path-storage
data:
  config.json: |-
    {
    
    
            "nodePathMap":[
            {
    
    
                    "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",	#指定其它节点使用/data/local-path-provisioner目录作为存储
                    "paths":["/data/local-path-provisioner"]	#存储目录,会自动创建
            },
            {
    
    
                    "node":"master",			#指定使用master节点的/opt/local-path-provisioner目录作为存储
                    "paths":["/opt/local-path-provisioner"]		#存储目录,会自动创建
            }	#如果不想让指定节点作为存储节点,则需显示的列出来,并且将该节点的path设置[]
            ]
    }
  setup: |-
    #!/bin/sh
    set -eu
    mkdir -m 0777 -p "$VOL_DIR"
  teardown: |-
    #!/bin/sh
    set -eu
    rm -rf "$VOL_DIR"
  helperPod.yaml: |-
    apiVersion: v1
    kind: Pod
    metadata:
      name: helper-pod
    spec:
      containers:
      - name: helper-pod
        image: busybox
        imagePullPolicy: IfNotPresent
[root@matser data]# kubectl apply -f  local-path-storage.yaml

创建pod、pvc进行验证

[root@matser data]# cat local-path.yaml 
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-path-pvc
spec:
  storageClassName: local-path
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-179
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-179
  template:
    metadata:
      labels:
        app: nginx-179
    spec:
      containers:
      - image: nginx:1.7.9
        imagePullPolicy: IfNotPresent
        name: nginx-container
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: local-path-volume
      restartPolicy: Always
      volumes:
      - name: local-path-volume
        persistentVolumeClaim:
          claimName: local-path-pvc
[root@matser data]# kubectl apply -f  local-path.yaml

#验证发现,pod随机调度到node2节点,并且已经自动创建了/data/local-path-provisioner/目录,并且存在权限是777的pvc-e04ceb06-1a30-4b83-8215-7786850f3d93_default_local-path-pvc目录,所有动态分配pv验证成功了。
#验证过程中发现,pv其实还是具有节点亲和性的,这说明,当node节点挂掉的时候,pod无法重新调度到其他节点,因为pod使用的pv定义节点亲和性,
#这也难怪,provisioner创建的卷类型本身就是hostPath或local卷。
[root@matser data]# kubectl  get pv pvc-0926a91c-854a-400f-be54-ad3fdd5e0051  -oyaml
apiVersion: v1
kind: PersistentVolume
metadata:
 ........
  hostPath:				#其实卷的类型仍是hostPath
    path: /opt/local-path-provisioner/pvc-0926a91c-854a-400f-be54-ad3fdd5e0051_default_local-path-pvc
    type: DirectoryOrCreate
  nodeAffinity:			#具有节点亲和性
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - master
........

存在的问题-指定nodeName无法调度

#发现如果在pod中定义nodeName想让pod调度到某台节点,pod会无法调度,pod的events报错如下:
[root@matser data]# kubectl  describe  pod nginx-1791-5d444554cb-b2mvq 
Events:
  Type     Reason       Age   From     Message
  ----     ------       ----  ----     -------
  Warning  FailedMount  6s    kubelet  Unable to attach or mount volumes: unmounted volumes=[local-path-volume], unattached volumes=[local-path-volume kube-api-access-c7h9m]: error processing PVC default/local-path-pvc1: PVC is not bound
查看pvc的报错如下:
[root@matser data]# kubectl  describe  pvc local-path-pvc1
Events:
  Type    Reason                Age               From                         Message
  ----    ------                ----              ----                         -------
  Normal  WaitForFirstConsumer  5s (x2 over 15s)  persistentvolume-controller  waiting for first consumer to be created before binding

这是怎么回事呢?pod显示pvc没有绑定,pvc又在等待pod来消费,这不是死循环吗。如果pod中不指定nodeName,让pod随机调度,是能正常创建的,但是一旦定义了nodeName就不能正常了,这是什么回事呢?

使用节点亲和性调度pod

既然使用nodeName无法调度,那么我们使用pod的节点亲和性调度:

#先定义节点标签,这里只在master节点打storage=true标签
kubectl  label nodes node1 storage-
kubectl  label nodes node2 storage-
kubectl  label nodes master storage=true

[root@matser local-path]# cat local-path.yaml 
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-path-pvc1
spec:
  storageClassName: local-path
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-179
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-179
  template:
    metadata:
      labels:
        app: nginx-179
    spec:
      affinity:
        nodeAffinity:				#使用节点亲和性
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: storage
                operator: In
                values: ["true"]
      containers:
      - image: nginx:1.7.9
        imagePullPolicy: IfNotPresent
        name: nginx-container
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: local-path-volume
      restartPolicy: Always
      volumes:
      - name: local-path-volume
        persistentVolumeClaim:
          claimName: local-path-pvc1
[root@matser local-path]# kubectl  get pod nginx-179-5ff5854467-kgt87  -owide	#pod成功调度到了master节点
NAME                         READY   STATUS    RESTARTS   AGE     IP               NODE     NOMINATED NODE   READINESS GATES
nginx-179-5ff5854467-kgt87   1/1     Running   0          3m48s   10.244.219.105   master   <none>           <none>
[root@matser local-path]# 
#以上说明,在deployment中定义affinity指定pod调度节点亲和性是可以的

使用nodeSelector调度pod

[root@matser local-path]# cat  local-path-2.yaml 
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-path-pvc1
spec:
  storageClassName: local-path
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-179
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-179
  template:
    metadata:
      labels:
        app: nginx-179
    spec:
      nodeSelector:
        storage: "true"
      containers:
      - image: nginx:1.7.9
        imagePullPolicy: IfNotPresent
        name: nginx-container
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: local-path-volume
      restartPolicy: Always
      volumes:
      - name: local-path-volume
        persistentVolumeClaim:
          claimName: local-path-pvc1
[root@matser local-path]# 
[root@matser local-path]# kubectl  get pod -owide	#可以正常调度
NAME                         READY   STATUS    RESTARTS   AGE     IP               NODE     NOMINATED NODE   READINESS GATES
nginx-179-5d7dbffbbc-6fj8k   1/1     Running   0          2m3s    10.244.219.113   master   <none>           <none>

#以上说明,在deployment中定义nodeSelector指定pod调度节点是可以的

local-path-provisioner仅支持ReadWriteOnce访问模式

在写pvc的时候,如果将accessModes写成 ReadWriteMany,则pvc将一直处于penging状态,查看local-path-provisioner pod的日志或kubectl describe pvc local-path-pvc 发现了以下报错:

 Type     Reason                Age                  From                                                                                                Message
  ----     ------                ----                 ----                                                                                                -------
  Normal   WaitForFirstConsumer  11m                  persistentvolume-controller                                                                         waiting for first consumer to be created before binding
  Normal   ExternalProvisioning  102s (x42 over 11m)  persistentvolume-controller                                                                         waiting for a volume to be created, either by external provisioner "rancher.io/local-path" or manually created by system administrator
  Normal   Provisioning          52s (x7 over 11m)    rancher.io/local-path_local-path-provisioner-6464fcbd8b-bhmqs_d68778ed-2a18-4f48-9788-1b2dc0f2b88f  External provisioner is provisioning volume for claim "default/local-path-pvc"
  Warning  ProvisioningFailed    52s (x7 over 11m)    rancher.io/local-path_local-path-provisioner-6464fcbd8b-bhmqs_d68778ed-2a18-4f48-9788-1b2dc0f2b88f  failed to provision volume with StorageClass "local-path": Only support ReadWriteOnce access mode
[root@matser data]# kubectl  describe  pvc local-path-pvc 
#为什么local-path-provisioner创建的pv仅支持ReadWriteOnce访问模式而已的呢?
答:ReadWriteOnce——该卷可以被单个节点以读/写模式挂载,想一下,pv是具有节点亲和性的,也就是说pod只能调度到一个节点上,不管deployment
的pod副本是1还是多个,这些pod由于都使用同一个pvc,所以势必都会因为pvc关联的pv具有节点亲和性从而都调度到同一个节点。所以,这就说通了为什
么pv仅支持ReadWriteOnce访问模式。在同一个节点上的pod都能对pv进行读写。
#对于sts的pod而言,则每个pod都有自己的pv。

PV 可以通过配置 accessModes 参数,设置访问模式来限制应用对资源的访问权限,有以下机制访问模 式:
(1) ReadWriteOnce——该卷可以被单个节点以读/写模式挂载
(2) ReadOnlyMany——该卷可以被多个节点以只读模式挂载
(3) ReadWriteMany——该卷可以被多个节点以读/写模式挂载

卸载local-path-provisioner

#1、手动删除pv,确保已经没有pv是使用local-path 这个存储类的
#2、kubectl delete  -f local-path-storage.yaml
#3、手动删除节点上的目录

总结

1、pod中直接使用hostPath来挂载宿主机的指定目录,但是为了防止pod重启后没有调度到上一次调度的宿主机节点,所以pod中需要定义nodeName指定调度主机。
2、可以在pv中使用hostPath,然后创建pvc与pv进行绑定,然后pod中使用pvc即可,pod中仍需要定义nodeName指定调度主机。
3、可以在pv中使用hostPath并且pv中定义节点亲和性,然后创建pvc与pv进行绑定,然后pod中使用pvc即可,此时pod中不需要定义nodeName指定调度主机,k8s会根据pv中定义的节点亲和性来选择调度pod,这样其实也是间接的指定了pod要调度的主机。
4、local的使用和上面第3点相同,都是在pv中定义节点亲和性,local与hostPath的区别在于,local可以使用裸设备、目录、分区,而hostPath只能是目录或文件。
5、local和hostPath都没有动态供给,都是手动的。
6、引入local-path-provisioner只是为了实现local或hostPath的动态供给。
7、创建一个local-path-provisioner 的pod、存储类、cm、服务账号等资源对象来实现动态供给。
8、本质上local-path-provisioner提供的卷仍是hostPath或local,所以local-path-provisioner提供的pv仍是具有节点亲和性的,这就说明当宿主机挂机了的时候,由于pod绑定的pv具有节点亲和性,所以pod无法在其他节点重新调度,pod就会存在故障。这其实就是hostPath、local卷的劣势。
9、使用local-path-provisioner动态供给pv,发现pod无法使用nodeName节点选择器,这一点有待研究。
10、在实际企业中如果使用local-path-provisioner动态供给pv,我们更推荐划分某些节点为存储节点,目录单独挂载lvm逻辑卷,然后为这些节点打
上节点标签,然后定义local-path-config的存储目录,定义deployment或sts时为pod定义节点亲和性。
11、使用local-path-provisioner动态供给pv,pvc只能是ReadWriteOnce,因为local-path-provisioner pod显示StorageClass "local-path": Only support ReadWriteOnce access mode;
12、对于deployment扩容多个pod,由于pod都使用同一个pvc,pvc绑定的pv具有节点亲和性,所以势必多个pod都会调度到同一个节点,所以这也解释了为pv的访问模式只能是ReadWriteOnce(单个节点以读/写模式挂载)。

猜你喜欢

转载自blog.csdn.net/MssGuo/article/details/132120705