日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

kubernetes pv-controller 解析

發布時間:2024/8/23 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 kubernetes pv-controller 解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡介:pv controller是 kcm 的組件之一,它負責處理集群中的pvc/pv對象,對pvc/pv 對象進行狀態轉換。本文將基于 kubernetes 1.23進行解析。

作者 | 牧琦
來源 | 阿里技術公眾號

基于 kubernetes 1.23

一 簡介

pv controller是 kcm 的組件之一,它負責處理集群中的pvc/pv對象,對pvc/pv 對象進行狀態轉換。

二 pvController 初始化

初始化代碼在 pkg/controller/volume/persistentvolume/pv_controller_base.go 文件中,NewController 主要做了如下幾件事情

  • 初始化 eventRecorder
  • 初始化 PersistentVolumeController 對象,
  • 調用 VolumePluginMgr.InitPlugins() 方法 初始化存儲插件,代碼存在于 pkg/volume/plugins.go 文件中
  • 開始創建 informer 監聽集群內的資源,初始化了如下 informer

    • PersistentVolumeInformer
    • PersistentVolumeClaimInformer
    • StorageClassInformer
    • PodInformer
    • NodeInformer
  • 將 PV & PVC 的 event 分別放入 volumeQueue & claimQueue
  • 為了不每次都迭代 pods ,自定義一個通過 pvc 鍵索引 pod 的索引器
  • 初始化 intree 存儲 -> csi 遷移相關功能的 manager

NewController代碼在cmd/kube-controller-manager代碼里面被調用,初始化成功之后緊接著調用go Run()方法運行 pvController

三 開始運行

// 開始運行 pvController func (ctrl *PersistentVolumeController) Run(stopCh <-chan struct{}) {defer utilruntime.HandleCrash()defer ctrl.claimQueue.ShutDown()defer ctrl.volumeQueue.ShutDown()klog.Infof("Starting persistent volume controller")defer klog.Infof("Shutting down persistent volume controller")if !cache.WaitForNamedCacheSync("persistent volume", stopCh, ctrl.volumeListerSynced, ctrl.claimListerSynced, ctrl.classListerSynced, ctrl.podListerSynced, ctrl.NodeListerSynced) {return}ctrl.initializeCaches(ctrl.volumeLister, ctrl.claimLister)go wait.Until(ctrl.resync, ctrl.resyncPeriod, stopCh)go wait.Until(ctrl.volumeWorker, time.Second, stopCh)go wait.Until(ctrl.claimWorker, time.Second, stopCh)metrics.Register(ctrl.volumes.store, ctrl.claims, &ctrl.volumePluginMgr)<-stopCh }

同步緩存之后開始周期性執行 ctrl.resync,ctrl.volumeWorker , ctrl.claimWorker , 我們看下 initalizeCaches 方法

func (ctrl *PersistentVolumeController) initializeCaches(volumeLister corelisters.PersistentVolumeLister, claimLister corelisters.PersistentVolumeClaimLister) {// 這里不訪問 apiserver,是從本地緩存拿出的對象,這些對象不可以被外部函數修改volumeList, err := volumeLister.List(labels.Everything())if err != nil {klog.Errorf("PersistentVolumeController can't initialize caches: %v", err)return}for _, volume := range volumeList {// 我們不能改變 volume 對象,所以這里我們copy一份新對象,對新對象進行操作volumeClone := volume.DeepCopy()if _, err = ctrl.storeVolumeUpdate(volumeClone); err != nil {klog.Errorf("error updating volume cache: %v", err)}}claimList, err := claimLister.List(labels.Everything())if err != nil {klog.Errorf("PersistentVolumeController can't initialize caches: %v", err)return}for _, claim := range claimList {if _, err = ctrl.storeClaimUpdate(claim.DeepCopy()); err != nil {klog.Errorf("error updating claim cache: %v", err)}}klog.V(4).Infof("controller initialized") }type persistentVolumeOrderedIndex struct {store cache.Indexer }

該方法將 cache.listener 里面的緩存轉存在 persistentVolumeOrderedIndex 中,它是按 AccessModes 索引并按存儲容量排序的 persistentVolume 的緩存。

1 resync

func (ctrl *PersistentVolumeController) resync() {klog.V(4).Infof("resyncing PV controller")pvcs, err := ctrl.claimLister.List(labels.NewSelector())if err != nil {klog.Warningf("cannot list claims: %s", err)return}for _, pvc := range pvcs {ctrl.enqueueWork(ctrl.claimQueue, pvc)}pvs, err := ctrl.volumeLister.List(labels.NewSelector())if err != nil {klog.Warningf("cannot list persistent volumes: %s", err)return}for _, pv := range pvs {ctrl.enqueueWork(ctrl.volumeQueue, pv)} }

這里將集群內所有的 pvc/pv 統一都放到對應的 claimQueue & volumeQueue 里面重新處理。 這個resyncPeriod 等于一個random time.Duration * config.time(在 kcm 啟動時設置)。

2 volumeWorker

一個無限循環, 不斷的處理從 volumeQueue 里面獲取到的 PersistentVolume

workFunc := func() bool {keyObj, quit := ctrl.volumeQueue.Get()if quit {return true}defer ctrl.volumeQueue.Done(keyObj)key := keyObj.(string)klog.V(5).Infof("volumeWorker[%s]", key)_, name, err := cache.SplitMetaNamespaceKey(key)if err != nil {klog.V(4).Infof("error getting name of volume %q to get volume from informer: %v", key, err)return false}volume, err := ctrl.volumeLister.Get(name)if err == nil {// The volume still exists in informer cache, the event must have// been add/update/syncctrl.updateVolume(volume)return false}if !errors.IsNotFound(err) {klog.V(2).Infof("error getting volume %q from informer: %v", key, err)return false}// The volume is not in informer cache, the event must have been// "delete"volumeObj, found, err := ctrl.volumes.store.GetByKey(key)if err != nil {klog.V(2).Infof("error getting volume %q from cache: %v", key, err)return false}if !found {// The controller has already processed the delete event and// deleted the volume from its cacheklog.V(2).Infof("deletion of volume %q was already processed", key)return false}volume, ok := volumeObj.(*v1.PersistentVolume)if !ok {klog.Errorf("expected volume, got %+v", volumeObj)return false}ctrl.deleteVolume(volume)return false}

我們主要關注 ctrl.updateVolume(volume) 方法

updateVolume

updateVolume 方法是對于集群內的 events 實際 handler 方法,它里面主要調用了 ctrl.syncVolume 方法來處理

func (ctrl *PersistentVolumeController) syncVolume(ctx context.Context, volume *v1.PersistentVolume) error {klog.V(4).Infof("synchronizing PersistentVolume[%s]: %s", volume.Name, getVolumeStatusForLogging(volume))...// [Unit test set 4]if volume.Spec.ClaimRef == nil {// Volume is unusedklog.V(4).Infof("synchronizing PersistentVolume[%s]: volume is unused", volume.Name)if _, err := ctrl.updateVolumePhase(volume, v1.VolumeAvailable, ""); err != nil {// Nothing was saved; we will fall back into the same// condition in the next call to this methodreturn err}return nil} else /* pv.Spec.ClaimRef != nil */ {// Volume is bound to a claim.if volume.Spec.ClaimRef.UID == "" {// The PV is reserved for a PVC; that PVC has not yet been// bound to this PV; the PVC sync will handle it.klog.V(4).Infof("synchronizing PersistentVolume[%s]: volume is pre-bound to claim %s", volume.Name, claimrefToClaimKey(volume.Spec.ClaimRef))if _, err := ctrl.updateVolumePhase(volume, v1.VolumeAvailable, ""); err != nil {// Nothing was saved; we will fall back into the same// condition in the next call to this methodreturn err}return nil}klog.V(4).Infof("synchronizing PersistentVolume[%s]: volume is bound to claim %s", volume.Name, claimrefToClaimKey(volume.Spec.ClaimRef))// Get the PVC by _name_var claim *v1.PersistentVolumeClaimclaimName := claimrefToClaimKey(volume.Spec.ClaimRef)obj, found, err := ctrl.claims.GetByKey(claimName)if err != nil {return err}if !found {if volume.Status.Phase != v1.VolumeReleased && volume.Status.Phase != v1.VolumeFailed {obj, err = ctrl.claimLister.PersistentVolumeClaims(volume.Spec.ClaimRef.Namespace).Get(volume.Spec.ClaimRef.Name)if err != nil && !apierrors.IsNotFound(err) {return err}found = !apierrors.IsNotFound(err)if !found {obj, err = ctrl.kubeClient.CoreV1().PersistentVolumeClaims(volume.Spec.ClaimRef.Namespace).Get(context.TODO(), volume.Spec.ClaimRef.Name, metav1.GetOptions{})if err != nil && !apierrors.IsNotFound(err) {return err}found = !apierrors.IsNotFound(err)}}}if !found {klog.V(4).Infof("synchronizing PersistentVolume[%s]: claim %s not found", volume.Name, claimrefToClaimKey(volume.Spec.ClaimRef))// Fall through with claim = nil} else {var ok boolclaim, ok = obj.(*v1.PersistentVolumeClaim)if !ok {return fmt.Errorf("cannot convert object from volume cache to volume %q!?: %#v", claim.Spec.VolumeName, obj)}klog.V(4).Infof("synchronizing PersistentVolume[%s]: claim %s found: %s", volume.Name, claimrefToClaimKey(volume.Spec.ClaimRef), getClaimStatusForLogging(claim))}if claim != nil && claim.UID != volume.Spec.ClaimRef.UID {klog.V(4).Infof("Maybe cached claim: %s is not the newest one, we should fetch it from apiserver", claimrefToClaimKey(volume.Spec.ClaimRef))claim, err = ctrl.kubeClient.CoreV1().PersistentVolumeClaims(volume.Spec.ClaimRef.Namespace).Get(context.TODO(), volume.Spec.ClaimRef.Name, metav1.GetOptions{})if err != nil && !apierrors.IsNotFound(err) {return err} else if claim != nil {// Treat the volume as bound to a missing claim.if claim.UID != volume.Spec.ClaimRef.UID {klog.V(4).Infof("synchronizing PersistentVolume[%s]: claim %s has a newer UID than pv.ClaimRef, the old one must have been deleted", volume.Name, claimrefToClaimKey(volume.Spec.ClaimRef))claim = nil} else {klog.V(4).Infof("synchronizing PersistentVolume[%s]: claim %s has a same UID with pv.ClaimRef", volume.Name, claimrefToClaimKey(volume.Spec.ClaimRef))}}}if claim == nil {if volume.Status.Phase != v1.VolumeReleased && volume.Status.Phase != v1.VolumeFailed {// Also, log this only once:klog.V(2).Infof("volume %q is released and reclaim policy %q will be executed", volume.Name, volume.Spec.PersistentVolumeReclaimPolicy)if volume, err = ctrl.updateVolumePhase(volume, v1.VolumeReleased, ""); err != nil {// Nothing was saved; we will fall back into the same condition// in the next call to this methodreturn err}}if err = ctrl.reclaimVolume(volume); err != nil {// Release failed, we will fall back into the same condition// in the next call to this methodreturn err}if volume.Spec.PersistentVolumeReclaimPolicy == v1.PersistentVolumeReclaimRetain {// volume is being retained, it references a claim that does not exist now.klog.V(4).Infof("PersistentVolume[%s] references a claim %q (%s) that is not found", volume.Name, claimrefToClaimKey(volume.Spec.ClaimRef), volume.Spec.ClaimRef.UID)}return nil} else if claim.Spec.VolumeName == "" {if pvutil.CheckVolumeModeMismatches(&claim.Spec, &volume.Spec) {volumeMsg := fmt.Sprintf("Cannot bind PersistentVolume to requested PersistentVolumeClaim %q due to incompatible volumeMode.", claim.Name)ctrl.eventRecorder.Event(volume, v1.EventTypeWarning, events.VolumeMismatch, volumeMsg)claimMsg := fmt.Sprintf("Cannot bind PersistentVolume %q to requested PersistentVolumeClaim due to incompatible volumeMode.", volume.Name)ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.VolumeMismatch, claimMsg)// Skipping syncClaimreturn nil}if metav1.HasAnnotation(volume.ObjectMeta, pvutil.AnnBoundByController) {// The binding is not completed; let PVC sync handle itklog.V(4).Infof("synchronizing PersistentVolume[%s]: volume not bound yet, waiting for syncClaim to fix it", volume.Name)} else {// Dangling PV; try to re-establish the link in the PVC syncklog.V(4).Infof("synchronizing PersistentVolume[%s]: volume was bound and got unbound (by user?), waiting for syncClaim to fix it", volume.Name)}ctrl.claimQueue.Add(claimToClaimKey(claim))return nil} else if claim.Spec.VolumeName == volume.Name {// Volume is bound to a claim properly, update status if necessaryklog.V(4).Infof("synchronizing PersistentVolume[%s]: all is bound", volume.Name)if _, err = ctrl.updateVolumePhase(volume, v1.VolumeBound, ""); err != nil {// Nothing was saved; we will fall back into the same// condition in the next call to this methodreturn err}return nil} else {// Volume is bound to a claim, but the claim is bound elsewhereif metav1.HasAnnotation(volume.ObjectMeta, pvutil.AnnDynamicallyProvisioned) && volume.Spec.PersistentVolumeReclaimPolicy == v1.PersistentVolumeReclaimDelete {if volume.Status.Phase != v1.VolumeReleased && volume.Status.Phase != v1.VolumeFailed {// Also, log this only once:klog.V(2).Infof("dynamically volume %q is released and it will be deleted", volume.Name)if volume, err = ctrl.updateVolumePhase(volume, v1.VolumeReleased, ""); err != nil {// Nothing was saved; we will fall back into the same condition// in the next call to this methodreturn err}}if err = ctrl.reclaimVolume(volume); err != nil {return err}return nil} else {if metav1.HasAnnotation(volume.ObjectMeta, pvutil.AnnBoundByController) {klog.V(4).Infof("synchronizing PersistentVolume[%s]: volume is bound by controller to a claim that is bound to another volume, unbinding", volume.Name)if err = ctrl.unbindVolume(volume); err != nil {return err}return nil} else {// The PV must have been created with this ptr; leave it alone.klog.V(4).Infof("synchronizing PersistentVolume[%s]: volume is bound by user to a claim that is bound to another volume, waiting for the claim to get unbound", volume.Name)if err = ctrl.unbindVolume(volume); err != nil {return err}return nil}}}} }

1、當 pv 的 Spec.ClaimRef 的值為空的時候,說明當前 pv 未被使用,調用 ctrl.updateVolumePhase 使得 pv 進入 Available 狀態

2、當 pv 的 Spec.ClaimRef 的值不為空的時候, 說明當前 pv 已綁定一個pvc

  • 當Spec.ClaimRef.UID 為空的時候,說明 pvc 還未綁定 pv, 調用ctrl.updateVolumePhase 使得 pv 進入 Available 狀態, 方法返回,等待 pvc syncClaim 方法處理
  • 使用 Spec.ClaimRef 相關的 pvc 信息獲取 pv_controller緩存的pvc
  • 如果 pvc 沒有找到

    • 有可能是集群壓力過大緩存沒有更新,則進一步從 informercache 中找,如果 informercache里面還是沒有的話則進一步從apiserver中去找
    • 這里如果發現 非 Released & 非 Failed 的pv 經過上述步驟仍然找不到 pvc 的話,說明 pvc 被刪除。在最新的kubernetes 版本中會檢查reclaimPoilcy,對 pv的狀態進行處理
  • 找到 pvc 之后

1)如果 pvc 的 uid 和 Spec.ClaimRef.UID 不一致,這樣一般是 pv 指向的 pvc 被刪了,然后立即創建了一個同名的pvc, 而緩存還沒有更新,這時我們需要doublecheck一下,若 double check 之后依舊不存在,則判斷是pv綁定了一個不存在的pvc, 將pvc置為空,執行上述pvc 沒有找到的邏輯

2)如果pvc 的 volumeName 為空

  • 檢查 pvc的 volumeMode 和 pv 的 volumeMode是否一致,不一致報 event 出來
  • 如果發現有這個 pv 有 AnnBoundByController = "pv.kubernetes.io/bound-by-controller" 這個annotation 說明 pvc/pv 流程正在綁定中
  • 將 pvc 放到 claimQueue 里面, 讓 claimWorker 進行處理

3)如果 pvc.Spec.volumeName == pv.volumeName 的時候,直接將 pv 設置為 bound 狀態

4)如果 pvc.Spec.volumeName != pv.volumeName 的時候

  • 如果是 pv 是動態創建的情況下,并且 pv 的 ReclaimPolicy 是 delete 的情況下, 說明 pvc 已經綁定了其他pv, 將 pv 置為 released 的狀態, 等待deleters 刪除
  • 如果 pv 不是動態創建的情況下,將 pv 的 ClaimRef 字段置為空,將其 unbound 掉

3 claimWorker

一個無限循環,不斷的處理從 claimQueue 里面獲取到的 PersistentVolumeClaim

workFunc := func() bool {keyObj, quit := ctrl.claimQueue.Get()if quit {return true}defer ctrl.claimQueue.Done(keyObj)key := keyObj.(string)klog.V(5).Infof("claimWorker[%s]", key)namespace, name, err := cache.SplitMetaNamespaceKey(key)if err != nil {klog.V(4).Infof("error getting namespace & name of claim %q to get claim from informer: %v", key, err)return false}claim, err := ctrl.claimLister.PersistentVolumeClaims(namespace).Get(name)if err == nil {// The claim still exists in informer cache, the event must have// been add/update/syncctrl.updateClaim(claim)return false}if !errors.IsNotFound(err) {klog.V(2).Infof("error getting claim %q from informer: %v", key, err)return false}// The claim is not in informer cache, the event must have been "delete"claimObj, found, err := ctrl.claims.GetByKey(key)if err != nil {klog.V(2).Infof("error getting claim %q from cache: %v", key, err)return false}if !found {// The controller has already processed the delete event and// deleted the claim from its cacheklog.V(2).Infof("deletion of claim %q was already processed", key)return false}claim, ok := claimObj.(*v1.PersistentVolumeClaim)if !ok {klog.Errorf("expected claim, got %+v", claimObj)return false}ctrl.deleteClaim(claim)return false}

我們主要關注 ctrl.updateClaim(claim) 方法, 與上面同樣,它里面主要調用了 ctrl.syncClaim 方法來處理, 在 syncClaim 里面根據 pvc 的狀態分別調用了 ctrl.syncUnboundClaim & ctrl.syncBoundClaim 方法來處理

syncUnboundClaim

func (ctrl *PersistentVolumeController) syncUnboundClaim(ctx context.Context, claim *v1.PersistentVolumeClaim) error {if claim.Spec.VolumeName == "" {// User did not care which PV they get.delayBinding, err := pvutil.IsDelayBindingMode(claim, ctrl.classLister)if err != nil {return err}// [Unit test set 1]volume, err := ctrl.volumes.findBestMatchForClaim(claim, delayBinding)if err != nil {klog.V(2).Infof("synchronizing unbound PersistentVolumeClaim[%s]: Error finding PV for claim: %v", claimToClaimKey(claim), err)return fmt.Errorf("error finding PV for claim %q: %w", claimToClaimKey(claim), err)}if volume == nil {klog.V(4).Infof("synchronizing unbound PersistentVolumeClaim[%s]: no volume found", claimToClaimKey(claim))switch {case delayBinding && !pvutil.IsDelayBindingProvisioning(claim):if err = ctrl.emitEventForUnboundDelayBindingClaim(claim); err != nil {return err}case storagehelpers.GetPersistentVolumeClaimClass(claim) != "":if err = ctrl.provisionClaim(ctx, claim); err != nil {return err}return nildefault:ctrl.eventRecorder.Event(claim, v1.EventTypeNormal, events.FailedBinding, "no persistent volumes available for this claim and no storage class is set")}// Mark the claim as Pending and try to find a match in the next// periodic syncClaimif _, err = ctrl.updateClaimStatus(claim, v1.ClaimPending, nil); err != nil {return err}return nil} else /* pv != nil */ {// Found a PV for this claim// OBSERVATION: pvc is "Pending", pv is "Available"claimKey := claimToClaimKey(claim)klog.V(4).Infof("synchronizing unbound PersistentVolumeClaim[%s]: volume %q found: %s", claimKey, volume.Name, getVolumeStatusForLogging(volume))if err = ctrl.bind(volume, claim); err != nil {metrics.RecordMetric(claimKey, &ctrl.operationTimestamps, err)return err}metrics.RecordMetric(claimKey, &ctrl.operationTimestamps, nil)return nil}} else /* pvc.Spec.VolumeName != nil */ {klog.V(4).Infof("synchronizing unbound PersistentVolumeClaim[%s]: volume %q requested", claimToClaimKey(claim), claim.Spec.VolumeName)obj, found, err := ctrl.volumes.store.GetByKey(claim.Spec.VolumeName)if err != nil {return err}if !found {klog.V(4).Infof("synchronizing unbound PersistentVolumeClaim[%s]: volume %q requested and not found, will try again next time", claimToClaimKey(claim), claim.Spec.VolumeName)if _, err = ctrl.updateClaimStatus(claim, v1.ClaimPending, nil); err != nil {return err}return nil} else {volume, ok := obj.(*v1.PersistentVolume)if !ok {return fmt.Errorf("cannot convert object from volume cache to volume %q!?: %+v", claim.Spec.VolumeName, obj)}klog.V(4).Infof("synchronizing unbound PersistentVolumeClaim[%s]: volume %q requested and found: %s", claimToClaimKey(claim), claim.Spec.VolumeName, getVolumeStatusForLogging(volume))if volume.Spec.ClaimRef == nil {klog.V(4).Infof("synchronizing unbound PersistentVolumeClaim[%s]: volume is unbound, binding", claimToClaimKey(claim))if err = checkVolumeSatisfyClaim(volume, claim); err != nil {klog.V(4).Infof("Can't bind the claim to volume %q: %v", volume.Name, err)// send an eventmsg := fmt.Sprintf("Cannot bind to requested volume %q: %s", volume.Name, err)ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.VolumeMismatch, msg)// volume does not satisfy the requirements of the claimif _, err = ctrl.updateClaimStatus(claim, v1.ClaimPending, nil); err != nil {return err}} else if err = ctrl.bind(volume, claim); err != nil {// On any error saving the volume or the claim, subsequent// syncClaim will finish the binding.return err}// OBSERVATION: pvc is "Bound", pv is "Bound"return nil} else if pvutil.IsVolumeBoundToClaim(volume, claim) {// User asked for a PV that is claimed by this PVC// OBSERVATION: pvc is "Pending", pv is "Bound"klog.V(4).Infof("synchronizing unbound PersistentVolumeClaim[%s]: volume already bound, finishing the binding", claimToClaimKey(claim))// Finish the volume binding by adding claim UID.if err = ctrl.bind(volume, claim); err != nil {return err}// OBSERVATION: pvc is "Bound", pv is "Bound"return nil} else {// User asked for a PV that is claimed by someone else// OBSERVATION: pvc is "Pending", pv is "Bound"if !metav1.HasAnnotation(claim.ObjectMeta, pvutil.AnnBoundByController) {klog.V(4).Infof("synchronizing unbound PersistentVolumeClaim[%s]: volume already bound to different claim by user, will retry later", claimToClaimKey(claim))claimMsg := fmt.Sprintf("volume %q already bound to a different claim.", volume.Name)ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.FailedBinding, claimMsg)// User asked for a specific PV, retry laterif _, err = ctrl.updateClaimStatus(claim, v1.ClaimPending, nil); err != nil {return err}return nil} else {klog.V(4).Infof("synchronizing unbound PersistentVolumeClaim[%s]: volume already bound to different claim %q by controller, THIS SHOULD NEVER HAPPEN", claimToClaimKey(claim), claimrefToClaimKey(volume.Spec.ClaimRef))claimMsg := fmt.Sprintf("volume %q already bound to a different claim.", volume.Name)ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.FailedBinding, claimMsg)return fmt.Errorf("invalid binding of claim %q to volume %q: volume already claimed by %q", claimToClaimKey(claim), claim.Spec.VolumeName, claimrefToClaimKey(volume.Spec.ClaimRef))}}}} }

梳理下整體流程

  • 如果當前 pvc 的 volumeName 為空

    • 判斷當前pvc 是否是延遲綁定的
    • 調用 volume, err := ctrl.volumes.findBestMatchForClaim(claim, delayBinding) 找出對應的 pv
  • 如果找到 volume 的話

    • 調用 ctrl.bind(volume, claim) 方法進行綁定
  • 如果沒有找到 volume 的話

    • 如果是延遲綁定, 并且還未觸發(pod 未引用)則 emit event 到 pvc 上
    • 如果 pvc 綁定了 sc, 調用 ctrl.provisionClaim(ctx, claim) 方法
  • 分析 pvc yaml, 找到 provisioner driver
  • 啟動一個 goroutine
  • 調用 ctrl.provisionClaimOperation(ctx, claim, plugin, storageClass) 進行創建工作
  • provisionClaimOperation

    func (ctrl *PersistentVolumeController) provisionClaimOperation(ctx context.Context,claim *v1.PersistentVolumeClaim,plugin vol.ProvisionableVolumePlugin,storageClass *storage.StorageClass) (string, error) {claimClass := storagehelpers.GetPersistentVolumeClaimClass(claim)klog.V(4).Infof("provisionClaimOperation [%s] started, class: %q", claimToClaimKey(claim), claimClass)pluginName := plugin.GetPluginName()if pluginName != "kubernetes.io/csi" && claim.Spec.DataSource != nil {strerr := fmt.Sprintf("plugin %q is not a CSI plugin. Only CSI plugin can provision a claim with a datasource", pluginName)return pluginName, fmt.Errorf(strerr)}provisionerName := storageClass.Provisioner// Add provisioner annotation to be consistent with external provisioner workflownewClaim, err := ctrl.setClaimProvisioner(ctx, claim, provisionerName)if err != nil {// Save failed, the controller will retry in the next syncklog.V(2).Infof("error saving claim %s: %v", claimToClaimKey(claim), err)return pluginName, err}claim = newClaimpvName := ctrl.getProvisionedVolumeNameForClaim(claim)volume, err := ctrl.kubeClient.CoreV1().PersistentVolumes().Get(context.TODO(), pvName, metav1.GetOptions{})if err != nil && !apierrors.IsNotFound(err) {klog.V(3).Infof("error reading persistent volume %q: %v", pvName, err)return pluginName, err}if err == nil && volume != nil {// Volume has been already provisioned, nothing to do.klog.V(4).Infof("provisionClaimOperation [%s]: volume already exists, skipping", claimToClaimKey(claim))return pluginName, err}// Prepare a claimRef to the claim early (to fail before a volume is// provisioned)claimRef, err := ref.GetReference(scheme.Scheme, claim)if err != nil {klog.V(3).Infof("unexpected error getting claim reference: %v", err)return pluginName, err}// Gather provisioning optionstags := make(map[string]string)tags[CloudVolumeCreatedForClaimNamespaceTag] = claim.Namespacetags[CloudVolumeCreatedForClaimNameTag] = claim.Nametags[CloudVolumeCreatedForVolumeNameTag] = pvNameoptions := vol.VolumeOptions{PersistentVolumeReclaimPolicy: *storageClass.ReclaimPolicy,MountOptions: storageClass.MountOptions,CloudTags: &tags,ClusterName: ctrl.clusterName,PVName: pvName,PVC: claim,Parameters: storageClass.Parameters,}// Refuse to provision if the plugin doesn't support mount options, creation// of PV would be rejected by validation anywayif !plugin.SupportsMountOption() && len(options.MountOptions) > 0 {strerr := fmt.Sprintf("Mount options are not supported by the provisioner but StorageClass %q has mount options %v", storageClass.Name, options.MountOptions)klog.V(2).Infof("Mount options are not supported by the provisioner but claim %q's StorageClass %q has mount options %v", claimToClaimKey(claim), storageClass.Name, options.MountOptions)ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr)return pluginName, fmt.Errorf("provisioner %q doesn't support mount options", plugin.GetPluginName())}// Provision the volumeprovisioner, err := plugin.NewProvisioner(options)if err != nil {strerr := fmt.Sprintf("Failed to create provisioner: %v", err)klog.V(2).Infof("failed to create provisioner for claim %q with StorageClass %q: %v", claimToClaimKey(claim), storageClass.Name, err)ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr)return pluginName, err}var selectedNode *v1.Node = nilif nodeName, ok := claim.Annotations[pvutil.AnnSelectedNode]; ok {selectedNode, err = ctrl.NodeLister.Get(nodeName)if err != nil {strerr := fmt.Sprintf("Failed to get target node: %v", err)klog.V(3).Infof("unexpected error getting target node %q for claim %q: %v", nodeName, claimToClaimKey(claim), err)ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr)return pluginName, err}}allowedTopologies := storageClass.AllowedTopologiesopComplete := util.OperationCompleteHook(plugin.GetPluginName(), "volume_provision")volume, err = provisioner.Provision(selectedNode, allowedTopologies)opComplete(volumetypes.CompleteFuncParam{Err: &err})if err != nil {ctrl.rescheduleProvisioning(claim)strerr := fmt.Sprintf("Failed to provision volume with StorageClass %q: %v", storageClass.Name, err)klog.V(2).Infof("failed to provision volume for claim %q with StorageClass %q: %v", claimToClaimKey(claim), storageClass.Name, err)ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr)return pluginName, err}klog.V(3).Infof("volume %q for claim %q created", volume.Name, claimToClaimKey(claim))// Create Kubernetes PV object for the volume.if volume.Name == "" {volume.Name = pvName}// Bind it to the claimvolume.Spec.ClaimRef = claimRefvolume.Status.Phase = v1.VolumeBoundvolume.Spec.StorageClassName = claimClass// Add AnnBoundByController (used in deleting the volume)metav1.SetMetaDataAnnotation(&volume.ObjectMeta, pvutil.AnnBoundByController, "yes")metav1.SetMetaDataAnnotation(&volume.ObjectMeta, pvutil.AnnDynamicallyProvisioned, plugin.GetPluginName())// Try to create the PV object several timesfor i := 0; i < ctrl.createProvisionedPVRetryCount; i++ {klog.V(4).Infof("provisionClaimOperation [%s]: trying to save volume %s", claimToClaimKey(claim), volume.Name)var newVol *v1.PersistentVolumeif newVol, err = ctrl.kubeClient.CoreV1().PersistentVolumes().Create(context.TODO(), volume, metav1.CreateOptions{}); err == nil || apierrors.IsAlreadyExists(err) {// Save succeeded.if err != nil {klog.V(3).Infof("volume %q for claim %q already exists, reusing", volume.Name, claimToClaimKey(claim))err = nil} else {klog.V(3).Infof("volume %q for claim %q saved", volume.Name, claimToClaimKey(claim))_, updateErr := ctrl.storeVolumeUpdate(newVol)if updateErr != nil {// We will get an "volume added" event soon, this is not a big errorklog.V(4).Infof("provisionClaimOperation [%s]: cannot update internal cache: %v", volume.Name, updateErr)}}break}// Save failed, try again after a while.klog.V(3).Infof("failed to save volume %q for claim %q: %v", volume.Name, claimToClaimKey(claim), err)time.Sleep(ctrl.createProvisionedPVInterval)}if err != nil {strerr := fmt.Sprintf("Error creating provisioned PV object for claim %s: %v. Deleting the volume.", claimToClaimKey(claim), err)klog.V(3).Info(strerr)ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr)var deleteErr errorvar deleted boolfor i := 0; i < ctrl.createProvisionedPVRetryCount; i++ {_, deleted, deleteErr = ctrl.doDeleteVolume(volume)if deleteErr == nil && deleted {// Delete succeededklog.V(4).Infof("provisionClaimOperation [%s]: cleaning volume %s succeeded", claimToClaimKey(claim), volume.Name)break}if !deleted {klog.Errorf("Error finding internal deleter for volume plugin %q", plugin.GetPluginName())break}// Delete failed, try again after a while.klog.V(3).Infof("failed to delete volume %q: %v", volume.Name, deleteErr)time.Sleep(ctrl.createProvisionedPVInterval)}if deleteErr != nil {strerr := fmt.Sprintf("Error cleaning provisioned volume for claim %s: %v. Please delete manually.", claimToClaimKey(claim), deleteErr)klog.V(2).Info(strerr)ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningCleanupFailed, strerr)}} else {klog.V(2).Infof("volume %q provisioned for claim %q", volume.Name, claimToClaimKey(claim))msg := fmt.Sprintf("Successfully provisioned volume %s using %s", volume.Name, plugin.GetPluginName())ctrl.eventRecorder.Event(claim, v1.EventTypeNormal, events.ProvisioningSucceeded, msg)}return pluginName, nil }

    provisionClaimOperation 的基本邏輯如下

    • 檢查driver,只有 csi 類型的 driver 才允許使用 dataSource 字段
    • 為 pvc 加 claim.Annotations["volume.kubernetes.io/storage-provisioner"] = class.Provisioner annotation
    • 根據規則拼出 pv Name = "pvc-" + pvc.UID
    • 如果找到了 pv, 則說明 pv已經存在,跳過 provision
    • 收集pvc/pv 基本信息封裝到 options 中
    • 對 plugin 進行校驗, 如果plugin不支持mount操作,則直接拒絕provision 請求
    • 調用plugin.NewProvisioner(options) 創建 provisioner, 接口實現了Provision(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) 方法,注意,該方法為同步方法
    • Provision 方法返回了 PersistentVolume實例
    • 為創建出來的 pv 關聯 pvc 對象(ClaimRef),嘗試創建 pv 對象 (重復多次)
    • 如果創建 pv 失敗,則嘗試調用 Delete 方法刪除創建的volume資源

    syncBoundClaim

    func (ctrl *PersistentVolumeController) syncBoundClaim(claim *v1.PersistentVolumeClaim) error {if claim.Spec.VolumeName == "" {// Claim was bound before but not any more.if _, err := ctrl.updateClaimStatusWithEvent(claim, v1.ClaimLost, nil, v1.EventTypeWarning, "ClaimLost", "Bound claim has lost reference to PersistentVolume. Data on the volume is lost!"); err != nil {return err}return nil}obj, found, err := ctrl.volumes.store.GetByKey(claim.Spec.VolumeName)if err != nil {return err}if !found {// Claim is bound to a non-existing volume.if _, err = ctrl.updateClaimStatusWithEvent(claim, v1.ClaimLost, nil, v1.EventTypeWarning, "ClaimLost", "Bound claim has lost its PersistentVolume. Data on the volume is lost!"); err != nil {return err}return nil} else {volume, ok := obj.(*v1.PersistentVolume)if !ok {return fmt.Errorf("cannot convert object from volume cache to volume %q!?: %#v", claim.Spec.VolumeName, obj)}klog.V(4).Infof("synchronizing bound PersistentVolumeClaim[%s]: volume %q found: %s", claimToClaimKey(claim), claim.Spec.VolumeName, getVolumeStatusForLogging(volume))if volume.Spec.ClaimRef == nil {// Claim is bound but volume has come unbound.// Or, a claim was bound and the controller has not received updated// volume yet. We can't distinguish these cases.// Bind the volume again and set all states to Bound.klog.V(4).Infof("synchronizing bound PersistentVolumeClaim[%s]: volume is unbound, fixing", claimToClaimKey(claim))if err = ctrl.bind(volume, claim); err != nil {// Objects not saved, next syncPV or syncClaim will try againreturn err}return nil} else if volume.Spec.ClaimRef.UID == claim.UID {// All is well// NOTE: syncPV can handle this so it can be left out.// NOTE: bind() call here will do nothing in most cases as// everything should be already set.klog.V(4).Infof("synchronizing bound PersistentVolumeClaim[%s]: claim is already correctly bound", claimToClaimKey(claim))if err = ctrl.bind(volume, claim); err != nil {// Objects not saved, next syncPV or syncClaim will try againreturn err}return nil} else {// Claim is bound but volume has a different claimant.// Set the claim phase to 'Lost', which is a terminal// phase.if _, err = ctrl.updateClaimStatusWithEvent(claim, v1.ClaimLost, nil, v1.EventTypeWarning, "ClaimMisbound", "Two claims are bound to the same volume, this one is bound incorrectly"); err != nil {return err}return nil}} }

    1)如果 pvc.Spec.VolumeName 為空, 說明這個 pvc 之前被 bound 過,但是已經不存在指向的pv, 報出event并返回

    2)從 cache 里面找 pvc 綁定的 pv

    • 如果沒找到, 說明 pvc 綁定了一個不存在的pv,報 event 并返回
    • 如果找到了pv

      • 檢查 pv.Spec.ClaimRef 字段, 如果 為空,說明 pv 還沒有綁定 pvc, 調用 ctrl.bind(volume, claim); 方法進行綁定
      • pv.ClaimRef.UID == pvc.UID, 調用 bind 方法,但是大多數情況會直接返回(因為所有的操作都已經做完了)
      • 其他情況說明 volume 綁定了其他的 pvc, 更新pvc 的狀態 為 lost 并報出 event

    四 總結

    最后用一張 pvc/pv 的狀態流轉圖來總結一下

    原文鏈接

    本文為阿里云原創內容,未經允許不得轉載。?

    總結

    以上是生活随笔為你收集整理的kubernetes pv-controller 解析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    91高清免费在线观看 | 日本久久不卡视频 | 91成人免费 | 久久久久国产一区二区三区四区 | 欧美在线aa | 日韩在线| 欧美日本日韩aⅴ在线视频 插插插色综合 | 欧美日韩不卡一区二区 | 男女精品久久 | 久久免费视频这里只有精品 | 色视频在线免费观看 | 91av大全 | 成人黄色小说网 | 99精品一级欧美片免费播放 | 国产香蕉在线 | 91成人精品一区在线播放69 | 精品美女在线视频 | 黄色的视频网站 | 亚洲精品久久久蜜桃直播 | 中文字幕乱码视频 | 国产午夜精品理论片在线 | 日本三级人妇 | av资源免费在线观看 | 国产精品久久亚洲 | 中文av日韩 | 中国一级特黄毛片大片久久 | 天天色成人 | av福利超碰网站 | 综合国产视频 | 国产精品不卡在线播放 | 国产拍揄自揄精品视频麻豆 | 精品国产乱码一区二区三区在线 | 精品在线看 | 欧美日韩xx | 99免费在线观看视频 | 午夜性福利 | 国产专区一| 日韩超碰在线 | av噜噜噜在线播放 | 国产视频网站在线观看 | 日韩成人xxxx | 天天操天天艹 | 午夜久久久久久久 | 久久午夜免费观看 | www.久久久.cum | 日韩资源在线 | 精品在线观看国产 | 高清中文字幕av | 人人爽人人爽人人爽人人爽 | 最近高清中文字幕 | 日韩av看片 | 丁香综合五月 | 91超国产 | 天天色天天综合 | 久久国产免费视频 | 国产一级不卡视频 | 日日夜夜网 | 四虎在线影视 | 久久国产经典视频 | 国产男女爽爽爽免费视频 | 国产h片在线观看 | 中文字幕一区三区 | 91视频一8mav | 夜夜躁日日躁 | 91试看| 亚洲国产欧美一区二区三区丁香婷 | 午夜精品一区二区三区视频免费看 | 国产在线观看免 | 久久精品91视频 | 亚洲成人av在线播放 | 中文字幕在线免费 | 天天艹天天干天天 | 91精品视频免费看 | 亚洲欧美视频 | 在线观看国产区 | 亚洲人在线7777777精品 | 日韩国产在线观看 | 成人av在线网 | 天堂av在线中文在线 | 欧美色图亚洲图片 | 国产精品久久免费看 | 久久综合激情 | 人人爽久久久噜噜噜电影 | 久久免费国产视频 | 丁香六月婷婷 | 狠狠操操| 激情久久网 | 中文字幕资源网在线观看 | 麻豆视频成人 | 国内视频 | 日韩免费观看视频 | 久久国产精品久久精品国产演员表 | 中文字幕超清在线免费 | 亚洲一区欧美精品 | 美女网站在线看 | 日韩高清在线一区二区 | 精品国产不卡 | 特级西西人体444是什么意思 | 亚洲成人资源在线观看 | 不卡视频在线 | 一区二区三区www | 香蕉在线视频播放网站 | 一区二区三区高清不卡 | 久久手机免费视频 | 国产成人一区二区三区在线观看 | 国产成人亚洲在线观看 | 久久国产亚洲 | 在线观看av免费 | wwwwwww黄| 国产免费xvideos视频入口 | 超碰在线色 | 91传媒在线 | av黄色免费网站 | 五月激情姐姐 | 91麻豆国产福利在线观看 | 成人免费视频网 | 在线看黄色的网站 | 国产真实在线 | 久久精品国产免费 | 五月天婷婷综合 | 黄网站色视频 | 在线国产精品一区 | 波多野结衣精品在线 | 亚洲香蕉视频 | 久久久久久久久久久久久久av | 成人av久久 | 黄色免费网战 | 精品久久久一区二区 | 久久久福利 | 日本护士撒尿xxxx18 | 日日干日日 | 成人av电影免费在线观看 | 免费精品视频在线观看 | 99久久精品久久亚洲精品 | 最新婷婷色 | 国产精品视频地址 | 久久视频在线观看 | 国产一区免费视频 | 免费观看www7722午夜电影 | 国产中文在线视频 | 日韩免费一级a毛片在线播放一级 | 18国产精品白浆在线观看免费 | 射九九| 在线观看视频你懂得 | 中文字幕在线播放第一页 | 久久精品一区二区 | 中文字幕日韩电影 | 黄色1级大片 | 青青草在久久免费久久免费 | 久久婷婷五月综合色丁香 | 成人三级视频 | 五月婷婷久久丁香 | 精品久久久久久久久久久久 | 97超碰人人模人人人爽人人爱 | 日夜夜精品视频 | 国产色视频123区 | 欧美日韩18 | 中文字幕国产一区 | 成人18视频 | 欧美午夜性| 亚洲精品国产日韩 | 一区av在线播放 | 亚洲日本激情 | 在线国产99 | 色婷婷综合久久久 | 免费韩国av | 992tv在线观看网站 | 国产精品久久久久久久午夜片 | 久草在线视频网 | 6080yy午夜一二三区久久 | 欧美日韩裸体免费视频 | 99久久www| 蜜臀av性久久久久蜜臀av | 一区二区 不卡 | 500部大龄熟乱视频 欧美日本三级 | 成人av高清在线观看 | 一本—道久久a久久精品蜜桃 | 色综合小说 | 久久99精品热在线观看 | 99精品免费久久久久久久久 | 青青河边草观看完整版高清 | 久久久黄视频 | 久久久久久久网 | 日韩欧美第二页 | 日韩免费 | 91成人网在线 | 亚洲激情 在线 | 国产精品白浆 | 黄色小说视频在线 | 91视频国产高清 | 在线观看的黄色 | 激情五月综合网 | 国产精品一区二区麻豆 | 午夜婷婷在线播放 | 很黄很污的视频网站 | 亚洲国产午夜视频 | 国产一区视频导航 | 色婷av| 日日爱影视 | 亚洲精品在线视频播放 | 五月激情亚洲 | 97超碰中文字幕 | 人人狠| 婷婷激情综合网 | 99精品欧美一区二区三区黑人哦 | 日本黄色大片儿 | 免费日韩 精品中文字幕视频在线 | 精品视频在线视频 | 国内外成人在线 | 国产成人福利片 | 91porny九色91啦中文 | 欧洲在线免费视频 | 免费人做人爱www的视 | 亚洲日本在线一区 | 在线免费黄色片 | 中文字幕精品在线 | 国产精品区免费视频 | 黄色免费看片网站 | 人人干,人人爽 | 香蕉视频久久久 | 在线91av| 91资源在线播放 | 国产 色 | 欧美日韩免费一区二区 | 日韩在线一级 | 狠狠色丁香婷婷综合久小说久 | 中文字幕在线播放第一页 | 亚洲精品五月 | 欧美韩日视频 | 精品国产一区二区三区四 | 最近中文字幕免费 | 久久免费精品 | 日韩xxx视频 | 丁香资源影视免费观看 | 国产一区视频在线观看免费 | 日韩精选在线观看 | 在线播放精品一区二区三区 | 狠狠躁夜夜躁人人爽超碰97香蕉 | 开心婷婷色 | 久草在线最新 | 国产精品久久久久三级 | www.香蕉 | 久久亚洲精品国产亚洲老地址 | 午夜精品久久久久久 | 天天插视频 | 国产黄网在线 | 欧美先锋影音 | 久久手机视频 | 我爱av激情网 | 91精品国自产在线 | 亚洲免费视频观看 | 97人人爽人人 | 国产无套一区二区三区久久 | 日日夜夜天天干 | 午夜在线免费观看 | 高清中文字幕 | 福利电影一区二区 | 久草视频在线播放 | 午夜视频在线瓜伦 | 日本不卡123区 | 久久夜夜操 | 五月婷婷色播 | 日韩欧美高清一区二区三区 | 操高跟美女 | 国产特黄色片 | 国产精品麻豆果冻传媒在线播放 | 欧美日韩一区二区视频在线观看 | 夜夜操狠狠干 | www狠狠操 | 国产欧美精品一区二区三区四区 | 99久久婷婷国产综合精品 | 97免费视频在线 | 香蕉视频日本 | 亚洲日本va午夜在线影院 | 亚洲精品高清视频在线观看 | 久久综合欧美精品亚洲一区 | 高清av影院 | 日韩欧美成人网 | 中文字幕日韩免费视频 | 亚洲精品免费观看视频 | 久久五月婷婷丁香社区 | 国产第一页精品 | 亚洲第一区在线观看 | 久久免费在线观看视频 | 国产99久久精品一区二区永久免费 | 五月天六月丁香 | 亚洲天天综合 | 婷婷精品 | 奇米影视8888在线观看大全免费 | 亚洲丝袜一区 | 2018好看的中文在线观看 | 日韩av电影免费观看 | 永久免费观看视频 | 中文免费| 99热在线国产 | 日韩美在线| 久草线 | 96av视频 | 国产又黄又爽无遮挡 | 激情五月综合网 | 久草视频在线新免费 | 天天色 天天 | 亚洲一区二区三区在线看 | 狠狠干干 | 91免费版成人 | 涩涩网站在线播放 | 黄网站www| 蜜臀av一区二区 | 五月色综合 | 欧美成人精品xxx | 亚洲精品大片www | 豆豆色资源网xfplay | 成人久久18免费网站 | 日韩激情视频在线 | 四虎国产精品成人免费影视 | 九九爱免费视频在线观看 | 一区二区毛片 | 久草手机视频 | 日韩性xxxx | 国产精品毛片一区二区 | 91精品国产成 | 91麻豆视频 | 久久婷婷网 | 99精品视频在线观看视频 | 99人久久精品视频最新地址 | 丁香色婷| 一区二区三区在线播放 | 成年人免费观看在线视频 | 亚洲国产精品一区二区久久hs | 免费日韩 精品中文字幕视频在线 | 麻豆视频免费在线 | 91成人短视频在线观看 | 日本韩国精品在线 | 亚洲精品在线电影 | 97av视频 | 日韩精品中文字幕一区二区 | 免费观看成人网 | 91在线超碰 | 国产男男gay做爰 | 超碰人人国产 | 天天色天天操综合 | 久草免费资源 | 精品国产免费一区二区三区五区 | 欧美福利片在线观看 | 激情丁香5月 | 一级黄色电影网站 | 欧美黑人性爽 | 久久在线视频在线 | 天天久久综合 | 最近在线中文字幕 | 91视频在线播放视频 | 久久在线免费观看 | 久久夜色精品国产欧美一区麻豆 | 日韩特级毛片 | 亚洲蜜桃av | 中文字幕视频网站 | 99视频在线精品免费观看2 | 天天干亚洲 | 91免费在线播放 | 久久免费看毛片 | 国产精品美女久久久久久久久 | 九九九视频在线 | 人人超在线公开视频 | 激情综合网五月婷婷 | 欧美激情精品久久久久久免费 | www.xxxx变态.com | 五月综合色 | 午夜精品福利在线 | 伊人影院得得 | 天堂成人在线 | 国产精品久久久久永久免费看 | 开心激情网五月天 | 亚洲国内在线 | 婷婷丁香在线视频 | 久久久99精品免费观看app | 国产精品久久嫩一区二区免费 | 在线看国产一区 | 亚洲成人精品久久 | 久久激情婷婷 | www夜夜操 | 91探花系列在线播放 | 超碰97人| 色综合中文字幕 | 国产高清中文字幕 | 国产69熟 | 欧美成人久久 | 成人在线视 | 国产精品福利在线 | 欧美夫妻性生活电影 | 在线观看国产中文字幕 | 国产精品一区二区在线看 | 国产精品久久久久一区二区国产 | 欧美日韩在线第一页 | 97在线观看免费高清完整版在线观看 | 欧美日韩高清一区二区 | 深爱婷婷久久综合 | 九九九九热精品免费视频点播观看 | 免费在线观看亚洲视频 | 九九视频精品在线 | 国产不卡精品视频 | 在线电影日韩 | 丁香九月激情综合 | 国产91九色蝌蚪 | 日日操网| 2018好看的中文在线观看 | www久久99 | 日韩中文字幕免费视频 | 日韩一级网站 | 久久精品网站免费观看 | 韩国三级一区 | 精品久久久久久久久久久久 | 亚洲欧美视频一区二区三区 | 免费a视频| 日韩精品一卡 | 九九久久精品视频 | 日本一区二区三区免费观看 | 欧美一级片免费播放 | 欧美热久久| 亚洲aⅴ久久精品 | 日韩电影在线观看一区 | 国内精品在线一区 | 国产99久久九九精品免费 | 人成电影网 | 日韩精品免费在线播放 | 91麻豆免费看 | 激情五月婷婷综合网 | 一本一本久久a久久精品综合 | 国产不卡视频在线播放 | 日韩国产在线观看 | 亚洲精品在线免费 | 精品一区 在线 | 欧美日韩免费一区二区 | 国产一级小视频 | 久久99久国产精品黄毛片入口 | 国产拍在线 | 中文字幕在线高清 | 国产视频中文字幕在线观看 | 波多野结衣久久资源 | 99精品久久久 | 天天色天天综合网 | 成人精品福利 | 在线91观看| 成人免费视频网站 | 免费在线观看成人小视频 | 国产精品免费麻豆入口 | 午夜久操 | 天天做天天看 | 日韩欧美精品在线观看 | 久久久国产精品视频 | 国产香蕉久久 | 在线中文字幕av观看 | 黄色网址中文字幕 | 国产二区免费视频 | 日韩免费一级电影 | 成人在线视频免费 | 午夜视频在线观看网站 | 在线一级片 | 日韩免 | 在线免费看片 | 欧美专区亚洲专区 | 爱爱av在线 | 欧美一区二区在线刺激视频 | 国产免费国产 | 国产视频1区2区3区 久久夜视频 | 免费在线观看91 | 深爱开心激情 | 日本久久久久 | 狂野欧美激情性xxxx | 国产高清视频免费在线观看 | 色wwww| 欧美精品一区二区免费 | 麻豆视频免费播放 | 色姑娘综合 | 国产午夜av | 成人h动漫精品一区二 | 日韩精品一区二区三区三炮视频 | 伊人小视频| 亚洲自拍av在线 | 国产 在线 高清 精品 | 国产精品va最新国产精品视频 | 伊人天天干 | av在线免费播放 | 国产一区二区三区高清播放 | 黄色av免费在线 | 碰超在线观看 | 91精品一区二区三区蜜臀 | 免费美女久久99 | 欧洲精品久久久久毛片完整版 | 97精品国产97久久久久久免费 | 精品一区精品二区高清 | 麻豆视频在线免费观看 | 亚洲一区美女视频在线观看免费 | 亚洲欧美乱综合图片区小说区 | 99热官网| 国产91粉嫩白浆在线观看 | 亚洲精品www久久久 www国产精品com | 国产精品免费成人 | 亚洲麻豆精品 | 国产精品自产拍在线观看网站 | 99精品在线免费在线观看 | 久久视频在线视频 | 精品久久久久国产免费第一页 | av福利超碰网站 | 五月婷婷av | 探花视频免费观看 | 日韩在线观看一区二区 | 日韩欧美精品在线观看视频 | 黄网av在线 | 蜜臀av一区二区 | 天天操天天射天天 | 天天综合操 | 免费久久片 | 在线观看视频黄 | 中文字幕一区二区三区视频 | 四虎免费av | 免费看国产视频 | 91亚洲欧美 | 色综合久久88色综合天天6 | 91福利视频一区 | 日韩av资源站 | 天天操天天是 | 99高清视频有精品视频 | 成人午夜电影在线观看 | 又黄又爽又刺激 | 97超碰成人 | 色先锋av资源中文字幕 | 亚洲另类人人澡 | 麻豆国产精品永久免费视频 | wwwww.国产| 在线观看资源 | 国产福利91精品一区 | 五月天综合网 | 黄污网站在线 | 欧美贵妇性狂欢 | 久久精品福利视频 | 狠狠操影视 | 在线观看精品视频 | 午夜精品一区二区三区视频免费看 | 国产69精品久久久久久 | 国产不卡在线观看视频 | 日韩理论片 | 天天干天天操天天拍 | 96视频在线 | 免费精品国产va自在自线 | 日韩欧美在线一区二区 | 国产高清精品在线观看 | 蜜臀av免费一区二区三区 | 97看片吧 | 波多野结衣电影一区二区三区 | 成人av在线网址 | 免费亚洲一区二区 | 我爱av激情网 | 在线观看视频在线观看 | 色综合久久88色综合天天人守婷 | 国产在线观看午夜 | 久久美女高清视频 | 99国产精品久久久久久久久久 | 成人av一区二区兰花在线播放 | av色综合网 | 狠狠做深爱婷婷综合一区 | www.91成人| 国色天香av| 亚洲视屏在线播放 | 国产精品入口传媒 | 碰碰影院| 免费在线播放 | 99热99re6国产在线播放 | 91人人在线| 久久久99久久 | 久精品在线观看 | 在线а√天堂中文官网 | 中文字幕国产视频 | 日韩免费在线网站 | 永久精品视频 | 亚洲成人av免费 | 久久久电影 | 黄色毛片在线观看 | 久久精品视频免费观看 | 综合婷婷丁香 | 欧美精品首页 | 在线国产黄色 | 激情婷婷av | 国产一二三区av | 日韩中文在线字幕 | 黄色在线成人 | 在线观看激情av | 伊人国产在线播放 | av国产在线观看 | 热99久久精品 | 亚洲视频综合在线 | 91网站在线视频 | 激情欧美丁香 | 在线观看www. | 天天干天天上 | 天天躁天天狠天天透 | 久久在线精品视频 | 热re99久久精品国产66热 | 亚洲无线视频 | 黄污在线看 | 特黄色大片| 国产福利专区 | 中文字幕亚洲精品在线观看 | 日韩三级中文字幕 | 成在人线av| 欧美一区在线观看视频 | h网站免费在线观看 | 久久久久久久久久久久电影 | 欧美另类人妖 | 精品美女久久 | 欧美久草网 | 久草视频资源 | 久久电影国产免费久久电影 | 日韩免费大片 | 国产69精品久久app免费版 | 夜夜夜夜爽| 亚洲成人网av | 91片黄在线观 | 奇米影视四色8888 | 99久久国产免费,99久久国产免费大片 | 日韩免费在线 | 69国产盗摄一区二区三区五区 | 国产手机视频 | 婷婷开心久久网 | 97色在线观看免费视频 | 日日爱999| 国产亚洲成人精品 | 中文字幕视频免费观看 | 91黄色在线观看 | 久久成人国产精品一区二区 | 亚洲专区一二三 | 日韩av电影网站在线观看 | 最近日本中文字幕a | 在线a亚洲视频播放在线观看 | 亚洲精品午夜久久久久久久 | 91精品国产欧美一区二区成人 | 婷婷丁香六月天 | 狠狠色伊人亚洲综合网站色 | 久久久18| 91视频免费观看 | 亚洲精品国产成人av在线 | 日韩小视频 | 日韩在线一区二区免费 | 亚洲专区视频在线观看 | 国产精品久久久久久a | 日韩在线高清免费视频 | 97精品国产一二三产区 | 91九色蝌蚪在线 | 国产一性一爱一乱一交 | 国产美女主播精品一区二区三区 | 国产亚洲综合在线 | 黄色亚洲精品 | 成人免费电影 | 人人爽人人射 | 久久久久久影视 | 国产一级在线观看 | 久久免费电影 | 97福利社 | 国产主播99 | 91精品欧美一区二区三区 | 国产九九精品视频 | 午夜av影院 | 丁香婷婷在线观看 | 国产在线精品一区二区 | 日韩免费播放 | 久久综合天天 | 欧美激情视频一区 | 久久这里只有精品视频99 | 成人aⅴ视频 | h视频在线看 | 中文字幕超清在线免费 | 久久久久久久久久久久影院 | 蜜臀久久99静品久久久久久 | 亚洲精品美女久久久久 | 人人爽人人片 | 91在线网址 | 亚洲精品高清一区二区三区四区 | 国产综合小视频 | 国产成人精品一区在线 | 久久国产一区二区三区 | 狠狠躁夜夜av | 808电影免费观看三年 | 狠狠的干狠狠的操 | 天天做天天爽 | 色中色综合| 久视频在线播放 | 亚洲精品黄色 | 日本久久免费电影 | 91精品国产高清自在线观看 | 五月香婷 | 国产精品国产自产拍高清av | 久久午夜精品 | 亚洲精品乱码久久久久v最新版 | 国产成人精品av | 人人澡人人爽欧一区 | 国产精品久久久久久久久岛 | 综合网av| 91片黄在线观看动漫 | 国产视频精选在线 | 黄色一级片视频 | 欧美日韩国产免费视频 | 日本黄色免费大片 | 国产精品久久久av久久久 | 亚洲国产高清视频 | 免费看在线看www777 | 亚洲欧美经典 | 美女天天操 | 在线 影视 一区 | 五月天综合激情网 | 人人插人人做 | 久久久久久美女 | 国产精品福利午夜在线观看 | 91片在线观看 | 在线观看黄色av | 精品一区二区免费 | 成人av免费在线播放 | 久久综合九色欧美综合狠狠 | 欧美最猛性xxxxx(亚洲精品) | 天天干天天干 | 九九久久精品 | www.夜夜夜| 国产啊v在线 | 最近中文字幕高清字幕免费mv | 亚洲精品国产精品国自产观看浪潮 | 精品一区精品二区高清 | 国产精品国产三级国产aⅴ9色 | 日韩精品免费 | 日本精品一区二区 | 欧美日韩一级视频 | 久久狠狠干 | 久草香蕉在线 | 久久中文字幕导航 | 成人动漫精品一区二区 | 亚洲激情婷婷 | 噜噜色官网| 丁香激情综合国产 | 国产精品久久久久久电影 | 国产精品毛片一区二区 | 久久婷婷国产色一区二区三区 | 蜜桃av人人夜夜澡人人爽 | 天天操天天爱天天爽 | 91亚·色| 国产伦精品一区二区三区高清 | 国产精品一区二区三区在线 | 久久成年人| 国产精品黄色在线观看 | av在线一级 | 国产91精品一区二区绿帽 | 国产精品区二区三区日本 | 亚洲一二三久久 | 色资源网免费观看视频 | 美女视频黄在线观看 | 国产精品成人一区二区 | 国产日产欧美在线观看 | 日韩久久电影 | 亚洲午夜精品久久久久久久久久久久 | 免费久久99精品国产婷婷六月 | 波多野结衣在线视频免费观看 | 91精品国自产拍天天拍 | 国产午夜精品一区二区三区欧美 | 99 色| 日韩毛片在线免费观看 | 天天干,天天插 | 国内精品久久久精品电影院 | 国产精品 视频 | 日韩av在线不卡 | 中文视频在线看 | 欧美成人精品欧美一级乱黄 | 9幺看片 | 在线观看亚洲a | 中文字幕人成乱码在线观看 | a极黄色片| 成人视屏免费看 | 亚洲欧洲精品一区二区 | 色综合久久综合 | 国产精彩在线视频 | 日韩欧美在线第一页 | 日本69hd| 六月丁香在线视频 | av免费观看网址 | 天天插综合 | 中文字幕成人在线观看 | 在线 成人 | 国产精品美女视频网站 | 欧美日韩激情视频8区 | 91在线视频网址 | 成人网在线免费视频 | 九九色网| 亚洲精品色视频 | 91插插视频 | 久久伦理| 精品在线小视频 | 麻豆精品在线视频 | 欧美福利在线播放 | 成人午夜精品 | 欧美日韩另类在线观看 | 免费观看性生活大片3 | 黄色小说在线观看视频 | 久久黄色成人 | 在线中文字幕电影 | 成人在线免费视频 | 久久噜噜少妇网站 | 日韩激情综合 | 久久99精品久久久久婷婷 | 亚洲蜜桃在线 | 久久人人添人人爽添人人88v | 久久久免费观看 | 日韩高清黄色 | 日本精品一区二区三区在线播放视频 | 超碰资源在线 | 久久久免费观看完整版 | 黄色视屏在线免费观看 | 国内99视频| 中文字幕久久精品一区 | 久久欧美在线电影 | 亚洲 欧美 综合 在线 精品 | 午夜精品视频一区二区三区在线看 | 在线 影视 一区 | 国产成人61精品免费看片 | www.夜色321.com| 99 久久久久 | 成人午夜黄色影院 | 国产精品一区二区av日韩在线 | 日本黄色免费看 | a视频在线看 | 91av在线免费观看 | 精品国产_亚洲人成在线 | 狠狠干在线 | 久久久色 | 欧美专区国产专区 | 91精品资源| 欧美一级大片在线观看 | 亚洲影院一区 | 深夜免费福利 | 最新免费av在线 | 成人av资源在线 | 亚洲欧美乱综合图片区小说区 | 天天操天天干天天爽 | 国产一区二区三区网站 | 中文字幕一区二区三区在线视频 | 在线免费观看视频一区 | 欧美日韩另类视频 | 欧美另类v | 日韩美女av在线 | 欧美激情在线看 | 婷婷精品在线视频 | 91欧美视频网站 | 81国产精品久久久久久久久久 | 久久久久久久久免费视频 | 中文字幕欧美日韩va免费视频 | 五月婷婷在线视频观看 | 91精品夜夜| 亚洲视频电影在线 | 国内精品久久久久久久久久 | 成人在线观看影院 | 成人国产一区二区 | 97精品超碰一区二区三区 | 久99久在线视频 | 色婷婷精品大在线视频 | 人人天天夜夜 | 亚洲精品99久久久久中文字幕 | 黄色免费观看视频 | 国产拍在线 | 国产成人精品一区二三区 | 在线视频亚洲 | 久久成熟| 黄色国产精品 | 国产视频 久久久 | 久久久久久久久久久久电影 | 免费看片在线观看 | 成人网在线免费视频 | 草久在线观看 | 日韩午夜三级 | 2022国产精品视频 | 精品久久久久国产 | 精品国产一区二区三区四区在线观看 | 日本黄色黄网站 | 亚洲在线国产 | 久久久久久看片 | aaa毛片视频 | 日本护士三级少妇三级999 | 国产精品久久久久久久久久三级 | 国产精品一区二区在线播放 | 久久成人亚洲欧美电影 | 国产中文字幕免费 | 99视频在线免费观看 | 亚洲 欧美变态 另类 综合 | 激情视频在线高清看 | 免费视频久久久 | 97视频一区 | 亚洲天天摸日日摸天天欢 | 日本在线观看视频一区 | 久久免费视频一区 | 成人理论在线观看 | 亚洲国产中文字幕在线观看 | 天堂在线视频免费观看 | 亚洲高清av在线 | 国产一区播放 | 日韩黄色大片在线观看 | 国产高清不卡一区二区三区 | 久草男人天堂 | 91大神dom调教在线观看 | 丝袜av一区 | 精品国产一区二区三区免费 | 成人av影院在线观看 | 国产精品久久久久影视 | 成人欧美一区二区三区黑人麻豆 | 超碰av在线 | 国产精品亚洲成人 | 97在线观视频免费观看 | 色wwww| 色综合天天综合在线视频 | 欧美亚洲国产精品久久高清浪潮 | 欧美国产精品久久久久久免费 | 国产尤物在线视频 | 97超碰国产精品女人人人爽 | 亚洲精品mv在线观看 | 久久久久这里只有精品 | 亚洲精品午夜一区人人爽 | 欧美久久久久久久久 | 最新av在线网址 | 国产成在线观看免费视频 | 天堂av官网 | 在线播放精品一区二区三区 | 99综合电影在线视频 | 国产成人黄色在线 | 日韩性xxx| 亚洲午夜久久久久久久久久久 | 正在播放国产一区二区 | 人人涩| 久久99国产精品视频 | 欧美日本在线观看视频 | 精品国产伦一区二区三区观看体验 | 免费成人短视频 | 中文字幕在线免费看线人 | 亚洲精品国产自产拍在线观看 | 亚洲精品玖玖玖av在线看 | 黄色片网站av | 亚洲第一成网站 | 日韩xxxxxxxxx| v片在线播放 | 欧美 日韩精品 | 欧美日韩视频精品 | 亚洲国产高清在线观看视频 | 免费看成人片 | 天天干天天干天天色 | 欧洲一区二区三区精品 | 99精品在线视频播放 | 国产成人精品一区二 | 国产美女精品视频免费观看 | 日韩精品久久久久久中文字幕8 | 天天干人人| 国产一级做a | 久久久国产在线视频 | 欧美极品一区二区三区 | 国产免费高清 | 最近乱久中文字幕 | 日韩久久精品一区二区三区下载 | 亚洲在线看 | 91精品免费 | 韩国av电影在线观看 | 亚洲精品国久久99热 | 在线天堂日本 | 国产 亚洲 欧美 在线 | 国产精品成人自拍 | 91久久国产综合精品女同国语 | 热re99久久精品国产99热 | 亚洲综合色丁香婷婷六月图片 | 91福利影院在线观看 | 亚洲精品视频中文字幕 | 日韩 在线| 插插插色综合 | 五月天电影免费在线观看一区 | 久久久久这里只有精品 | 毛片网在线播放 | 四虎国产视频 | 国产精品久久久一区二区三区网站 | 国产成人精品免费在线观看 | 在线视频a| 九九九国产 | 久久五月天综合 | 久久艹综合 | 青青河边草免费直播 | 国产精品18毛片一区二区 | 成年人在线观看网站 | 最新国产一区二区三区 | 国产在线欧美 | 在线播放 日韩专区 | 免费在线观看中文字幕 | 精品av网站 | 99精品视频免费全部在线 | 久久视频在线观看免费 | 在线观看岛国 | 亚洲在线成人精品 | 色综合久久天天 | 久久国产成人午夜av影院宅 | 婷婷精品国产一区二区三区日韩 | 97中文字幕 | 天天艹日日干 |