摘要:详情如下。 当然,你也可以不使用而手动创建。 第一步是将其添加为记录。 然后,您可以创建不同的字段并将它们拆分为一个字段。 不要忘记在模板中声明它。 请注意,这是您创建的第一个实例的名称。 对于表达式中未指定的声明,在簇内未找到匹配项。
什么是本地持久卷?
Kubernetes 1.14 版本引入了本地持久卷(以下简称 LPV)已经成为了。 General Availability(GA)LPV的概念最早在1.7(alpha)中提出,并在1.10版本中升级到Beat版本。 现在,用户终于可以在生产中使用LPV的功能和API了。
首先,本地持久卷表示直接绑定到计算节点的本地磁盘。
Kubernetes 提供了一组卷插件标准,使 k8s 集群中的工作负载能够使用各种块和文件存储。 大多数磁盘插件使用远程存储来保持持久数据和计算节点彼此独立,但远程存储通常无法提供与本地存储一样强大的读写性能。 LPV 插件允许 Kubernetes 工作负载使用相同卷 API 来使用容器内的本地磁盘。
这个和hostPath有什么区别?
HostPath是一个卷,允许pod挂载主机上的文件或目录(如果挂载路径不存在)创建并安装为目录或文件)。
最大的区别在于调度器能否理解磁盘和节点之间的对应关系。 如果使用hostPath的Pod被重新调度,它可能会被调度到与原始节点不同的节点。 这会导致 Pod 内的数据丢失。 使用LPV的Pod总是被调度到同一个节点(否则调度将失败)。
如何使用LPV
首先需要创建一个StorageClass
kind: StorageClassapiVersion: storage.k8s.io/v1metadata: name: local-storageprovisioner: kubernetes.io/no-provisionervolumeBindingMode: WaitForFirstConsumer
注意这里volumeBindingMode字段的值为WaitForFirstConsumer。 这种绑定模式的含义如下:
Kubernetes PV 控制器使用该类型的 PV,直到创建具有相应 PVC 的 pod 并调度该 pod。 只有此时pv和pvc是绑定的,此时pv的选择结合了调度的节点和pv的节点亲和力。
接下来,预先准备好的provisioner动态创建PV。
$ kubectl get pvNAME 容量访问模式回收策略状态请求存储类别原因年龄本地-pv-27c0f084 368Gi RWO 删除可用本地-存储8slocal-pv- 3796b049 368Gi RWO 删除可用local-storage 7slocal-pv-3ddecaea 368Gi RWO 删除可用的local-storage 7s
LPV详细信息如下:
$ kubectl destroy pv local[ k4]pv-ce05be60 名称:local-pv-ce05be60 标签:注释:pv.kubernetes.io/provisioned-by= local-volume-provisioner- Minikube - 18F57FB2 - A186 - 11E7 - B543 - 080027D51893STORAGECLASS:本地 - FastStatus:AvailableAblaim:Recaly 策略:Deletacas 模式:Rwocapacity: 1022220kinodefffedinity:fidedefiffest。[my-node] 消息主机名:来源:类型:本地卷(由节点上的本地存储支持的持久卷)路径:/mnt/disks/vol1Events:
当然,您也可以不使用provisioner手动创建PV。 不过需要注意的是,LPV需要填充nodeAffinity。 (在 1.10 之前,k8s 将节点关联性记录为 PV 内的注释,但从 1.10 开始,它已被分离到单独的字段中。)
apiVersion: v1kind: Persistent Volumemetadata: name : example-pvspec:容量:存储:100Gi #volumeMode字段需要启用Block Volume Alpha功能门。 VolumeMode:文件系统 accessModes:- ReadWriteOncepersistentVolumeReclaimPolicy:删除 storageClassName:local-storage local:路径:/mnt/disks/ssd1 nodeAffinity:必需:nodeSelectorTerms:- matchExpressions:- key:kubernetes.io/hostname运算符:值:- example-node
然后您可以创建不同的工作负载。 请记住在工作负载模板中声明volumeClaimTemplates。
apiVersion: apps/v1kind: StatefulSetmetadata: 名称: local-testspec:serviceName:“local-service”副本:3 选择器:matchLabels:应用程序:本地-测试模板:元数据:标签:应用程序:本地-测试规范:容器:-名称:test-容器镜像:k8s.gcr.io/busybox命令:-“/bin/sh”参数:-“-c”-“sleep 100000”volumeMounts:-名称: local-vol mountPath: /usr/test-pod volumeClaimTemplates: - 元数据: 名称: local-vol 规格: accessModes: [ "ReadWriteOnce" ] storageClassName: "local-storage" 资源:请求:存储:368Gi
请注意,volumeClaimTemplates.spec.storageClassName 是本地-存储。 这是您创建的第一个存储类实例。 姓名。
使用LPV的Pod调度流程
上面的stateful set创建完成后,控制器会创建一个与之对应的PVC,并为该PVC找到一个符合条件的PV。 但是,控制器不会处理 pvc 到 pv 的绑定,因为您使用 Local-Storage WaitForFirstConsumer 配置了它。
同时,调度器在调度 pod 时,谓词算法也会根据 PVC 的要求来搜索 pod。 属于可用 PV 且与“LPV 亲和性”不匹配的节点将被排除。 最后,调度程序发现了以下内容:
pv:example-pv满足pvc的要求。
node:example-node pv:满足example-pv的节点亲和性要求。
因此调度程序尝试绑定 pv 和 pvc 并重新调度 pod。
当 pod 重新调度时,调度程序检测到 pod 的 PVC 资源已满(所有 PV 都已绑定),并且绑定 PV 的节点亲和性设置为确保匹配:示例[ k4]节点。 因此,Pod 被调度到节点 example-node 上。 完成你的日程安排。
如何创建 LPV
在您的计算机上创建一个目录:mkdir -p /mnt/disks/ssd1
在机器上运行命令将卷挂载到目录:mount -t /dev/vdc /mnt/disks/ssd1
在集群中创建对应的storageClass到 。 。 见上文。
通过配置器手动或自动为本地卷创建 PV。 请参阅上面的手动创建的模板。
如何移除LPV
对于Pod绑定并使用的LPV,必须将其移除请遵循流程。 否则,删除将会失败。
使用此 pv 删除 pod。
从节点中删除磁盘(每个 PV 一个磁盘)。
删除pvc
删除pv
LPV后期绑定部分代码解读
关键是volumeBinder位于SchedulerVolumeBinder接口的结构中。
类型 Scheduler VolumeBinder 接口 { FindPodVolumes(pod *v1.Pod, node *v1.Node) AssumePodVolumes(assumedPod *v1.Pod, nodeName string) BindPodVolumes( allocatePod *v1.Pod) error GetBindingsCache() PodBindingCache }
FindPodVolumes
了解调度器原理的人应该知道调度器的谓词算法是一次使用一个。 调度 Pod 时。 使用节点执行谓词来检查该节点是否可以被调度。 这称为预选阶段。
VolumeBindingChecker 是一个检查器。 调度器的算法工厂初始化的最后一步是向工厂注册检查算法,这样当调度器执行谓词时,最后一步就是执行volumeBinding检查。 如果您查看 func (c *VolumeBindingChecker) 谓词方法,您可以看到它执行 FindPodVolumes 并确定返回值是 true 还是 err。 空:
unboundSatisfied、boundSatisfied, err := c.binder.Binder.FindPodVolumes(pod, node)
boundSatisfied 为 false。 这意味着绑定到 pod 的 PV 与当前计算的 PV 没有亲和力。 节点。
unboundSatisfied 如果为 false,则意味着 pod 内声明的未绑定 PVC 无法在集群内的 PV 之间进行匹配。
这样,调度程序将重复重试调度并重复运行FindPodVolumes,直到我们(或提供者)创建 PV。 例如,此时新创建的PV具有与节点A对应的nodeAffinity。 在本次调度中,当我们对节点A进行谓词计算时,发现pod中声明的unbound pvc在集群中有合适的pv,并且该pv的nodeAffinity是节点A,所以返回的unboundSatisfied 将为 true,调度程序最终会找到合适的节点。
然后调度程序接管 Pod。 在采用 Pod 之前,调度程序必须首先采用 Pod 内的绑定卷。 请参阅func (sched *Scheduler) allocateAndBindVolumes(假设 *v1.Pod,主机字符串)错误。 此函数调用 volumeBinder 的 AssumePodVolumes 方法。
AssumePodVolumes
Assume 表示假设。 顾名思义,该方法首先缓存在调度程序中。 pod调度到节点A,缓存中的pv和pvc更新绑定等资源,看是否成功,并返回一些消息。
Allbound,宾迪ngRequired,err := sched.config。VolumeBinder.Binder.AssumePodVolumes(假定,主机)
allBound 为 true。 这意味着所有 pv 和 pvc 都已绑定在缓存中。 如果为 false,则此调度最终将失败。
bindingRequired 为 true,表示某些 pv 必须与 pvc 绑定。 如果为 true,调度程序会将用例写入 volumeBinder 的 BindQueue。 该队列由工作人员轮询以执行相应的工作。
BindPodVolumes 的工作是什么?
BindPodVolumes
当调度程序运行时,将启动一个协程并执行 bindVolumesWorker 迭代。 。 您可以看到该工作线程正在尝试从 volumeBinder 的 BindQueue 中获取任务并运行 BindPodVolumes。 如果成功,则任务完成。 如果失败,会报错并重试。
读取BindPodVolumes这个方法非常简单。 从缓存中查找对应的pod、pvs、pvcs等内容,更新到API服务器。
由于我们更新了AssumePodVolumes中的缓存,所以这里的apiserver更新实际上绑定了pv和pvc。
接下来会发生什么?
在worker上,即使BindPodVolumes成功,也会构造一个pod调度失败事件,并且pod会看到状态PodScheduled 更新为 PodScheduled。 b>. 这样做是为了将 pod 放回到调度队列中,以便调度程序可以再次调度它。
该 Pod 是只申报了一辆 LPV。 在前面描述的BindPodVolumes操作中,这个LPV和Pod中的PVC与APIserver绑定。 然后,调度程序下次调度 pod 时,会检测到该 pod allBound 且 AssumePodVolumes ,调度程序会继续后续操作,导致最终当 pod 已成功调度资源(绑定已创建),apiserver 更新 pod 的节点名称)。
PV 控制器重要吗?
当您创建 PVC 时,PV 控制器将添加一个工作线程 syncUnboundClaim 来管理未绑定的 PVC。 如果 spec.VolumeName 是非空 pvc,则该工作线程执行绑定操作以确保 pv 和 pvc 已绑定。 如果spec.VolumeName是一个空的pvc,检查是否是后期绑定,并为集群中的pvc找到一个合适的pv(这里没有节点的概念,所以搜索更多了)基于 AccessModes 上的选择器和过滤器)。
func findMatchingVolume(claim *v1.PersistentVolumeClaim,卷 []*v1.PersistentVolume,节点 *v1.Node,excludedVolumes map[string]*v1.PersistentVolume,lateBinding bool) (*v1)。 .PersistentVolume, error)
找到过滤逻辑。 您在这里需要知道的是,对于后期绑定的 pvc,所有 pv 都会被过滤掉,最后通过发出 WaitForFirstConsumer 事件来终止工作线程。
可见,pv 控制器导致延迟调度 PVC 以自己的方式运行。 findMatchingVolume方法也有官方注释:
if node == nil && LateBinding { // PV控制器不绑定此声明。 // 调度程序处理未绑定卷的绑定 // 调度程序路径包含节点 != nil continue}
本地磁盘使用过程概述
待补充:PV控制器和CSI工作机制
评论前必须登录!
注册