使用AccessMode的Kubernetes PVC:ReadWriteOnce

时间:2017-05-25 06:24:34

标签: kubernetes

我们的情景:

我们使用ceph rbd存储一些机器学习训练数据集,工作流程如下:

使用AccessMode创建一个ceph-rbd pvc pvc-tranining-data:ReadWriteOnce。
使用1个pod创建一个写入作业来安装pvc-training-data并将训练数据写入pvc-training-data。
将训练数据写入pvc-training-data后,容器将退出,pvc-trainiing-data pvc将被k8s卸载,写入作业完成。
创建一个包含n个pod的读取作业,其中n >= 1readOnly: true挂载pvc-training-data以使用训练数据。 顺便说一句:我们使用k8s 1.6.1

到目前为止,工作流程适用于我们的使用场景,但我对使用AccessMode的PVC AccessMode和ceph rbd有一些疑问:ReadWriteOnce。

  
      
  1. 如何理解AccessModes:ReadOnlyMany,ReadWriteOnce,ReadWriteMany?   我认为使用范围是ReadOnlyMany< ReadWriteOnce< ReadWriteMay,所以如果我使用AccessMode:ReadWriteOnce应用PVC,我可以使用它   它作为AccessMode:ReadOnlyMany PVC,我是对的吗?

  2.   
  3. ceph rbd是一个块设备,每个容器(在不同的主机上)挂载相同的ceph rbd设备都会有自己的文件系统,所以唯一允许的AccessMode是ReadOnlyMany或ReadWriteOnce,我们是否应该对ReadWriteOnce的使用施加限制k8s代码?

  4.         
        
          
    1. 如果Pod使用readOnly:false安装了ReadWriteOnce pvc,那么在卸载之前,不能再安装Pod,直到卸载它为止。
    2.     
    3. 如果一个Pod使用readOnly:true挂载ReadWriteOnce pvc,只要它们设置为readOnly:true,它就只能挂载到其他Pod。
    4.     
    5. 对同一Pod中的容器没有限制,因为它们从主机$ {KUBELET_ROOT} / plugins / {xx} /
    6. 共享相同的文件系统        

2 个答案:

答案 0 :(得分:0)

关于你的第一个问题:

  
      
  1. 如何理解AccessModes:ReadOnlyMany,ReadWriteOnce,   ReadWriteMany?我认为使用范围是ReadOnlyMany< ReadWriteOnce   < ReadWriteMay,所以如果我使用AccessMode:ReadWriteOnce应用PVC,它   没问题我把它用作AccessMode:ReadOnlyMany PVC,我是对的吗?
  2.   

docs明确指出:

Important! A volume can only be mounted using one access mode at a
time, even if it supports many.

我的第二个问题并不清楚。但我认为考虑到第一个问题的答案可能无效?

答案 1 :(得分:0)

要回答这个问题,看一下源代码可能会很有用。正如我在集群迁移中遇到的问题一样,情况是这样的: 我们有一个带v1.9的集群,一个PV带有accessMode:ReadWriteMany(RWX),PVC带有accessMode:ReadWriteOnce,这两个可以绑定而没有任何错误。

我们将一些应用程序迁移到新集群(v1.12),并且在新集群中,PVC绑定给出了错误:

  

无法绑定到请求的卷“卷名”:不兼容   accessMode

我在v1.12的源代码中搜索了错误,并且看到了以下几行:

v1.12:

//checkVolumeSatisfyClaim checks if the volume requested by the claim satisfies the requirements of the claim
func checkVolumeSatisfyClaim(volume *v1.PersistentVolume, claim *v1.PersistentVolumeClaim) error {
    requestedQty := claim.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
    requestedSize := requestedQty.Value()

    // check if PV's DeletionTimeStamp is set, if so, return error.
    if utilfeature.DefaultFeatureGate.Enabled(features.StorageObjectInUseProtection) {
        if volume.ObjectMeta.DeletionTimestamp != nil {
            return fmt.Errorf("the volume is marked for deletion")
        }
    }

    volumeQty := volume.Spec.Capacity[v1.ResourceStorage]
    volumeSize := volumeQty.Value()
    if volumeSize < requestedSize {
        return fmt.Errorf("requested PV is too small")
    }

    requestedClass := v1helper.GetPersistentVolumeClaimClass(claim)
    if v1helper.GetPersistentVolumeClass(volume) != requestedClass {
        return fmt.Errorf("storageClassName does not match")
    }

    isMisMatch, err := checkVolumeModeMisMatches(&claim.Spec, &volume.Spec)
    if err != nil {
        return fmt.Errorf("error checking volumeMode: %v", err)
    }
    if isMisMatch {
        return fmt.Errorf("incompatible volumeMode")
    }

    if !checkAccessModes(claim, volume) {
        return fmt.Errorf("incompatible accessMode")
    }

    return nil
}

// Returns true if PV satisfies all the PVC's requested AccessModes
func checkAccessModes(claim *v1.PersistentVolumeClaim, volume *v1.PersistentVolume) bool {
    pvModesMap := map[v1.PersistentVolumeAccessMode]bool{}
    for _, mode := range volume.Spec.AccessModes {
        pvModesMap[mode] = true
    }

    for _, mode := range claim.Spec.AccessModes {
        _, ok := pvModesMap[mode]
        if !ok {
            return false
        }
    }
    return true
}

v1.9:

//checkVolumeSatisfyClaim checks if the volume requested by the claim satisfies the requirements of the claim
func checkVolumeSatisfyClaim(volume *v1.PersistentVolume, claim *v1.PersistentVolumeClaim) error {
    requestedQty := claim.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
    requestedSize := requestedQty.Value()
    isMisMatch, err := checkVolumeModeMisMatches(&claim.Spec, &volume.Spec)
    if err != nil {
        return fmt.Errorf("error checking if volumeMode was a mismatch: %v", err)
    }

    volumeQty := volume.Spec.Capacity[v1.ResourceStorage]
    volumeSize := volumeQty.Value()
    if volumeSize < requestedSize {
        return fmt.Errorf("Storage capacity of volume[%s] requested by claim[%v] is not enough", volume.Name, claimToClaimKey(claim))
    }

    requestedClass := v1helper.GetPersistentVolumeClaimClass(claim)
    if v1helper.GetPersistentVolumeClass(volume) != requestedClass {
        return fmt.Errorf("Class of volume[%s] is not the same as claim[%v]", volume.Name, claimToClaimKey(claim))
    }

    if isMisMatch {
        return fmt.Errorf("VolumeMode[%v] of volume[%s] is incompatible with VolumeMode[%v] of claim[%v]", volume.Spec.VolumeMode, volume.Name, claim.Spec.VolumeMode, claim.Name)
    }

    return nil
}

// checkVolumeModeMatches is a convenience method that checks volumeMode for PersistentVolume
// and PersistentVolumeClaims along with making sure that the Alpha feature gate BlockVolume is
// enabled.
// This is Alpha and could change in the future.
func checkVolumeModeMisMatches(pvcSpec *v1.PersistentVolumeClaimSpec, pvSpec *v1.PersistentVolumeSpec) (bool, error) {
    if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
        if pvSpec.VolumeMode != nil && pvcSpec.VolumeMode != nil {
            requestedVolumeMode := *pvcSpec.VolumeMode
            pvVolumeMode := *pvSpec.VolumeMode
            return requestedVolumeMode != pvVolumeMode, nil
        } else {
            // This also should retrun an error, this means that
            // the defaulting has failed.
            return true, fmt.Errorf("api defaulting for volumeMode failed")
        }
    } else {
        // feature gate is disabled
        return false, nil
    }
}

当您查看 checkAccessModes 函数中的v1.12代码时,它会将卷accessModes放入 Map 中,如果不能,则在该映射中搜索PVC accessModes。在该地图中找到PVC accessMode,它将返回false,这会导致不兼容的accessMode 错误。

那么为什么我们在v1.9中没有收到此错误?因为它在 checkVolumeModeMisMatches 函数中具有不同的控件。 它检查默认情况下为假的Alpha功能,称为 BlockVolume 。因为它是假的,所以不会遇到

if isMisMatch {
    return fmt.Errorf("VolumeMode[%v] of volume[%s] is incompatible with VolumeMode[%v] of claim[%v]", volume.Spec.VolumeMode, volume.Name, claim.Spec.VolumeMode, claim.Name)
}

代码块。

您可以使用以下命令检查主节点上的BlockVolume功能:

ps aux | grep apiserver | grep feature-gates

我希望它能澄清您的问题。尤其是v1.12中的 checkAccessModes 函数也在PV-PVC accessMode控件的当前master分支中。