一个 PVC 是否可以绑定到特定的 PV?

80
这是由k8s维护人员在https://github.com/kubernetes/kubernetes/issues/7438#issuecomment-97148195中讨论的:
“允许用户请求特定的PV会破坏它们之间的分离。”
我不认同这个观点。我们允许用户选择节点。虽然不是常见情况,但存在这样做的原因。
最后它以什么方式结束了?如何才能像https://github.com/kubernetes/kubernetes/tree/master/examples/nfs中那样拥有超过1个PV和PVC?
我们使用NFS,并且PersistentVolume是一个方便的抽象层,因为我们可以在那里保留server IP和path。但是PersistentVolumeClaim获取任何满足大小要求的PV,从而防止path的重复使用。
可以在PVC spec块中设置volumeName(请参见https://github.com/kubernetes/kubernetes/pull/7529),但没有区别。
9个回答

93

今天有一种方法可以将PV预先绑定到PVC,以下是一个示例:

  1. Create a PV object with a ClaimRef field referencing a PVC that you will subsequently create:
     $ kubectl create -f pv.yaml
     persistentvolume "pv0003" created
    
    where pv.yaml contains:
     apiVersion: v1
     kind: PersistentVolume
     metadata:
       name: pv0003
     spec:
       storageClassName: ""
       capacity:
         storage: 5Gi
       accessModes:
         - ReadWriteOnce
       persistentVolumeReclaimPolicy: Retain
       claimRef:
         namespace: default
         name: myclaim
       nfs:
         path: /tmp
         server: 172.17.0.2
    
  2. Then create the PVC with the same name:
     kind: PersistentVolumeClaim
     apiVersion: v1
     metadata:
       name: myclaim
     spec:
       storageClassName: ""
       accessModes:
         - ReadWriteOnce
       resources:
         requests:
           storage: 5Gi
    
  3. The PV and PVC should be bound immediately:
     $ kubectl get pvc
     NAME      STATUS    VOLUME    CAPACITY   ACCESSMODES   AGE
     myclaim   Bound     pv0003    5Gi        RWO           4s
     $ ./cluster/kubectl.sh get pv
     NAME      CAPACITY   ACCESSMODES   STATUS    CLAIM             REASON    AGE
     pv0003    5Gi        RWO           Bound     default/myclaim             57s
    

1
这绝对是一个雄心勃勃的任务。鉴于无论实施哪个提案,都可以进行卷选择器,因此它是否可能在架构改进之前就存在呢? - solsson
好的。请参考 https://github.com/kubernetes/kubernetes/issues/18359,它将独立解决实现问题。 - Saad Ali
#18359会很完美。由于1.2即将到来,我猜我们只能希望它能够在1.3中实现了? - solsson
这个不行。当我按照指示创建PV时,claimRef会被静默忽略,因此PV就像任何其他PV一样。 - sosiouxme
https://github.com/kubernetes/kubernetes/pull/25917已合并,但我在文档中找不到任何相关引用。或者它是https://github.com/kubernetes/kubernetes/blob/release-1.3/docs/proposals/volume-selectors.md吗? - solsson
显示剩余6条评论

56

可以使用关键字 volumeName: 来完成:

例如:

apiVersion: "v1"
kind: "PersistentVolumeClaim"
metadata:
  name: "claimapp80"
spec:
  accessModes:
    - "ReadWriteOnce"
  resources:
    requests:
      storage: "10Gi"
  volumeName: "app080"

将声称特定的 PV app080


3
你可能需要将storageClass也设为空字符串。请参考此示例:https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/preexisting-pd - Jonas Kello
1
如果PV不存在(且启用了动态配置),会发生什么情况?它会创建具有该名称的PV还是崩溃? - Daniele Sassoli

16
最好在pvc中同时指定volumeName和claimRef。在pv和pvc中都使用storageClassName:manual可以相互绑定,但如果有许多手动pv和pvc,则不能保证绑定成功。
在PVC中指定volumeName并不会防止其他PVC在你之前绑定到指定的PV上。你的声明将保持Pending状态,直到PV可用为止。
在PV中指定claimRef并不会防止指定的PVC被绑定到另一个PV上。PVC可以根据常规绑定过程自由选择要绑定的另一个PV。因此,为避免这些情况并确保您的声明被绑定到所需的卷,请确保同时指定volumeName和claimRef。
通过检查Bound PV和PVC对的pv.kubernetes.io/bound-by-controller注释,可以判断您设置的volumeName和/或claimRef是否影响了匹配和绑定过程。您自己设置了volumeName和/或claimRef的PV和PVC将没有这样的注释,但普通的PV和PVC将将其设置为“是”。
当PV的claimRef设置为某个PVC的名称和命名空间,并按照Retain回收策略进行回收时,即使PVC或整个命名空间不存在,其claimRef仍将保持设置为相同的PVC名称和命名空间。
来源:https://docs.openshift.com/container-platform/3.11/dev_guide/persistent_volumes.html 永久卷是 OpenShift 中的存储机制,它将数据从容器中分离出来,并使其在容器之间保持持久性和可共享性。可以使用不同的后端技术(如 NFS、iSCSI 和本地存储)创建永久卷。这些永久卷可以手动或自动地连接到不同的容器上,并且可以在卷之间自由移动。
开发人员可以通过声明式配置定义他们的应用程序需要哪些永久卷,并指定这些卷的属性。管理员可以为整个集群或特定项目设置默认永久卷。

1
在您的第一句话中,claimRef 在 PV 中指定,而不是 PVC 中... - Ryan Chase

12

PV和PVC中的storageClassName应该相同。在PVC中将持久卷名称作为volumeName添加,以将PVC绑定到特定的PV。

例如:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-name
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 40Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/data"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-name
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  volumeName: pv-name

我收到了错误信息io.k8s.api.core.v1.ResourceRequirements中未知的字段"volumeName" - Matthias

4
根据文档

控制平面可以将 PersistentVolumeClaims 绑定到群集中匹配的 PersistentVolumes。然而,如果您希望 PVC 绑定到特定的 PV,则需要预先绑定它们。

通过在 PersistentVolumeClaim 中指定 PersistentVolume,您声明了该特定 PV 和 PVC 之间的绑定关系。如果 PersistentVolume 存在且未通过其 claimRef 字段保留 PersistentVolumeClaims,则 PersistentVolume 和 PersistentVolumeClaim 将被绑定。

绑定发生的条件与一些卷匹配条件无关,包括节点亲和性。控制平面仍会检查存储类、访问模式和请求的存储大小是否有效。


尽管StatefulSets 是用于管理有状态应用程序的资源,但我已经将它们提供的 pv/pvc 示例修改为 Deployment 示例(使用 nginx,兼容至少 minikube,请评论/编辑与云提供商的兼容性):

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: foo-pvc
spec:
  volumeName: foo-pv
  storageClassName: "standard"
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: "1Gi"
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: foo-pv
#  name: bar-pv
spec:
  storageClassName: "standard"
  capacity:
    storage: "1Gi"
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/mnt/test/"
#  claimRef:
#    name: foo-pvc
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: foo-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
        volumeMounts:
          - name: nginx-storage
            mountPath: /test/
      volumes:
      - name: nginx-storage
        persistentVolumeClaim:
          claimName: foo-pvc

因此,为了详细说明文档,如果填写了PV的spec.claimRef.name(如foo-pvc或任何名称),您的PVC将保持挂起状态,并且无法绑定。(我将它注释掉了。)但是,您会注意到,如果您像上面那样在创建PV之后进行编辑,使用kubectl edit pv foo-pv,那么它会被控制平面设置为这样。

另外,我也留下了另一种PV metadata.name(已注释),您可以切换注释行,并查看它是否不会绑定,如果其值与PVC.spec.volumeName指定的不匹配。

其他注意事项:我发现,如果您在部署上述内容之前未创建/mnt/test文件夹,则在容器内部写入数据时,它会创建该文件夹。


我遇到了你所说的问题。然而,我发现原因是在PV的claimRef中需要两个其他字段。PVC的命名空间和种类,如下所示: claimRef: kind: PersistentVolumeClaim
name: foo-pvc namespace: default
- SteveSims

4

是的,在 PVC 中实际上可以提供 volumeName。它将精确绑定到在 volumeName 中提供的那个 PV 名称(同时 spec 应该同步)。


3

现在我们可以使用storageClassName(至少从 Kubernetes 1.7.x 开始)

详细信息请参见https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage

这里也复制了示例代码

kind:PersistentVolume
apiVersion:v1
metadata:
  name:task-pv-volume
  labels:
    type:local
spec:
  storageClassName:manual
  capacity:
    storage:10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path:"/tmp/data"
---
kind:PersistentVolumeClaim
apiVersion:v1
metadata:
  name:task-pv-claim
spec:
  storageClassName:manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage:3Gi

1
storageClassName 可以是任何东西(即用户定义的),还是必须是 manual 或从特定集合中选择? - Dan Nissenbaum
3
StorageClass 只绑定到“类”PV,而不是特定的一个。 - harryz
如果有多个pv怎么办? - shellakkshellu

2
我认为@jayme对原始答案的编辑不具备向前兼容性。
尽管在PVC中只记录为proposal,但在Kubernetes 1.3.0中似乎可以使用label selectors
我写了一个example,定义了两个卷,除了labels不同之外完全相同。两者都可以满足任何要求,但当要求指定时,情况就不同了。
selector:
    matchLabels:
      id: test2

很明显其中一个依赖的pod无法启动,而test1 PV保持未绑定状态。
例如,在minikube中可以进行测试。
$ kubectl create -f volumetest.yml
$ sleep 5
$ kubectl get pods
NAME                              READY     STATUS    RESTARTS   AGE
volumetest1                       1/1       Running   0          8m
volumetest1-conflict              0/1       Pending   0          8m
$ kubectl get pv
NAME      CAPACITY   ACCESSMODES   STATUS      CLAIM          REASON    AGE
pv1       1Gi        RWO           Available                            8m
pv2       1Gi        RWO           Bound       default/test             8m

我认为Kubernetes 1.3.0中的PetSet(http://kubernetes.io/docs/user-guide/petset/)解决了无需标签选择器的确定性映射。它解决了我的最初的用例。来自复制控制器或部署的Pod并不真正意味着具有持久性存储。 - solsson

2
如何使用标签选择器(label selector)?这在kubernetes文档中有描述:声明可以指定一个标签选择器来进一步过滤卷的集合。只有那些符合选择器标签的卷才能与声明绑定。选择器可以包括两个字段:matchLabels(匹配标签)——卷必须具有此值的标签;matchExpressions(匹配表达式)——通过指定键、值列表和操作符来制定的需求列表,以关联键和值。有效运算符包括In、NotIn、Exists和DoesNotExist。保留HTML标签,不做解释。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接