精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

一文讀懂K8s controller-runtime

云計算 云原生
利用kubebuilder、operator-sdk等框架,可以快速生成相應資源對象的controller代碼。接下來,以kubebuilder為例,對controller代碼邏輯進行解析。

K8s開發中,經常能聽過controller的概念,那么這些概念在K8s底層是如何實現,本文將詳細介紹。

Controller

在K8s中,實現一個controller是通過controller-runtime(https://github.com/kubernetes-sigs/controller-runtime) 框架來實現的,包括Kubebuilder、operator-sdk等工具也只是在controller-runtime上做了封裝,以便開發者快速生成項目的腳手架而已。

Controller定義在pkg/internal/controller/controller,一個controller主要包含Watch和Start兩個方法,以及一個調協方法Reconcile。在controller的定義中,看上去沒有資源對象的Informer或者Indexer數據,而在K8s中所有與kube-apiserver資源的交互是通過Informer實現的,實際上這里是通過下面的 startWatches 屬性做了一層封裝。

type Controller struct {
   // Name is used to uniquely identify a Controller in tracing, logging and monitoring.  Name is required.
   Name string

   // MaxConcurrentReconciles is the maximum number of concurrent Reconciles which can be run. Defaults to 1.
   MaxConcurrentReconciles int

   // Reconciler is a function that can be called at any time with the Name / Namespace of an object and
   // ensures that the state of the system matches the state specified in the object.
   // Defaults to the DefaultReconcileFunc.
   Do reconcile.Reconciler

   // MakeQueue constructs the queue for this controller once the controller is ready to start.
   // This exists because the standard Kubernetes workqueues start themselves immediately, which
   // leads to goroutine leaks if something calls controller.New repeatedly.
   MakeQueue func() workqueue.RateLimitingInterface

   // Queue is an listeningQueue that listens for events from Informers and adds object keys to
   // the Queue for processing
   Queue workqueue.RateLimitingInterface

   // SetFields is used to inject dependencies into other objects such as Sources, EventHandlers and Predicates
   // Deprecated: the caller should handle injected fields itself.
   SetFields func(i interface{}) error

   // mu is used to synchronize Controller setup
   mu sync.Mutex

   // Started is true if the Controller has been Started
   Started bool

   // ctx is the context that was passed to Start() and used when starting watches.
   //
   // According to the docs, contexts should not be stored in a struct: https://golang.org/pkg/context,
   // while we usually always strive to follow best practices, we consider this a legacy case and it should
   // undergo a major refactoring and redesign to allow for context to not be stored in a struct.
   ctx context.Context

   // CacheSyncTimeout refers to the time limit set on waiting for cache to sync
   // Defaults to 2 minutes if not set.
   CacheSyncTimeout time.Duration

   // startWatches maintains a list of sources, handlers, and predicates to start when the controller is started.
   startWatches []watchDescription

   // LogConstructor is used to construct a logger to then log messages to users during reconciliation,
   // or for example when a watch is started.
   // Note: LogConstructor has to be able to handle nil requests as we are also using it
   // outside the context of a reconciliation.
   LogConstructor func(request *reconcile.Request) logr.Logger

   // RecoverPanic indicates whether the panic caused by reconcile should be recovered.
   RecoverPanic *bool
}

Watch()

Watch方法首先會判斷當前的controller是否已啟動,如果未啟動,會將watch的內容暫存到startWatches中等待controller啟動。如果已啟動,則會直接調用src.Start(c.ctx, evthdler, c.Queue, prct...), 其中Source可以為informer、kind、channel等。

// Watch implements controller.Controller.
func (c *Controller) Watch(src source.Source, evthdler handler.EventHandler, prct ...predicate.Predicate) error {
    ...
    // Controller hasn't started yet, store the watches locally and return.
    //
    // These watches are going to be held on the controller struct until the manager or user calls Start(...).
    if !c.Started {
        c.startWatches = append(c.startWatches, watchDescription{src: src, handler: evthdler, predicates: prct})
        return nil
    }

    c.LogConstructor(nil).Info("Starting EventSource", "source", src)
    return src.Start(c.ctx, evthdler, c.Queue, prct...)
}

以informer為例,會通過以下方法添加對應的EventHandler

_, err := is.Informer.AddEventHandler(internal.EventHandler{Queue: queue, EventHandler: handler, Predicates: prct})

以kind為例,會通過以下方法添加對應的EventHandler:

i, lastErr = ks.cache.GetInformer(ctx, ks.Type)
_, err := i.AddEventHandler(internal.EventHandler{Queue: queue, EventHandler: handler, Predicates: prct})

internal.EventHandler 實現了 OnAdd、OnUpdate、OnDelete 幾個方法。也就是說src.Start方法作用是獲取對應的informer,并注冊對應的EventHandler。

Start()

Start方法有兩個主要功能,一是調用所有startWatches中Source的start方法,注冊EventHandler。

for _, watch := range c.startWatches {
   c.LogConstructor(nil).Info("Starting EventSource", "source", fmt.Sprintf("%s", watch.src))

   if err := watch.src.Start(ctx, watch.handler, c.Queue, watch.predicates...); err != nil {
      return err
   }
}

二是啟動Work來處理資源對象。

for i := 0; i < c.MaxConcurrentReconciles; i++ {
   go func() {
      defer wg.Done()
      // Run a worker thread that just dequeues items, processes them, and marks them done.
      // It enforces that the reconcileHandler is never invoked concurrently with the same object.
      for c.processNextWorkItem(ctx) {
      }
   }()
}

processNextWorkItem從Queue中獲取資源對象,reconcileHandler 函數就是我們真正執行元素業務處理的地方,函數中包含了事件處理以及錯誤處理,真正的事件處理是通過c.Do.Reconcile(req) 暴露給開發者的,所以對于開發者來說,只需要在 Reconcile 函數中去處理業務邏輯就可以了。

func (c *Controller) processNextWorkItem(ctx context.Context) bool {
    obj, shutdown := c.Queue.Get()
    if shutdown {
        // Stop working
        return false
    }

    // We call Done here so the workqueue knows we have finished
    // processing this item. We also must remember to call Forget if we
    // do not want this work item being re-queued. For example, we do
    // not call Forget if a transient error occurs, instead the item is
    // put back on the workqueue and attempted again after a back-off
    // period.
    defer c.Queue.Done(obj)

    ctrlmetrics.ActiveWorkers.WithLabelValues(c.Name).Add(1)
    defer ctrlmetrics.ActiveWorkers.WithLabelValues(c.Name).Add(-1)

    c.reconcileHandler(ctx, obj)
    return true
}

// Reconcile implements reconcile.Reconciler.
func (c *Controller) Reconcile(ctx context.Context, req reconcile.Request) (_ reconcile.Result, err error) {
    defer func() {
        if r := recover(); r != nil {
            if c.RecoverPanic != nil && *c.RecoverPanic {
                for _, fn := range utilruntime.PanicHandlers {
                    fn(r)
                }
                err = fmt.Errorf("panic: %v [recovered]", r)
                return
            }

            log := logf.FromContext(ctx)
            log.Info(fmt.Sprintf("Observed a panic in reconciler: %v", r))
            panic(r)
        }
    }()
    return c.Do.Reconcile(ctx, req)
}

Reconcile

Controller的調協邏輯在Reconcile中執行。

type Reconciler interface {
   // Reconcile performs a full reconciliation for the object referred to by the Request.
   // The Controller will requeue the Request to be processed again if an error is non-nil or
   // Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
   Reconcile(context.Context, Request) (Result, error)
}
type Request struct {
   // NamespacedName is the name and namespace of the object to reconcile.
   types.NamespacedName
}

Reconcile方法的入參Request來自于controller.queue,并且會判斷隊列中的數據類型是否為Reconcile.Request,如果數據類型不一致,則不會執行Reconcile的邏輯。

func (c *Controller) reconcileHandler(ctx context.Context, obj interface{}) {
   // Make sure that the object is a valid request.
   req, ok := obj.(reconcile.Request)
   if !ok {
      // As the item in the workqueue is actually invalid, we call
      // Forget here else we'd go into a loop of attempting to
      // process a work item that is invalid.
      c.Queue.Forget(obj)
      c.LogConstructor(nil).Error(nil, "Queue item was not a Request", "type", fmt.Sprintf("%T", obj), "value", obj)
      // Return true, don't take a break
      return
   }
}

那么數據是如何進入隊列queue的呢,實際是通過Informer中的EventHandler入隊的。回到src.Start(c.ctx, evthdler, c.Queue, prct...)方法中,該方法為informer注冊了一個internal.EventHandler。internal.EventHandler實現了OnAdd、OnUpdate、OnDelete等方法,以OnAdd方法為例,該方法最后會調用EventHandler.Create 方法。

type EventHandler struct {
    EventHandler handler.EventHandler
    Queue        workqueue.RateLimitingInterface
    Predicates   []predicate.Predicate
}

// OnAdd creates CreateEvent and calls Create on EventHandler.
func (e EventHandler) OnAdd(obj interface{}) {
    c := event.CreateEvent{}

    // Pull Object out of the object
    if o, ok := obj.(client.Object); ok {
        c.Object = o
    } else {
        log.Error(nil, "OnAdd missing Object",
            "object", obj, "type", fmt.Sprintf("%T", obj))
        return
    }

    for _, p := range e.Predicates {
        if !p.Create(c) {
            return
        }
    }

    // Invoke create handler
    e.EventHandler.Create(c, e.Queue)
}

EventHandler為一個接口,有EnqueueRequestForObject、Funcs、EnqueueRequestForOwner、enqueueRequestsFromMapFunc四個實現類。以EnqueueRequestForObject為例,其create方法為:

// Create implements EventHandler.
func (e *EnqueueRequestForObject) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
    if evt.Object == nil {
        enqueueLog.Error(nil, "CreateEvent received with no metadata", "event", evt)
        return
    }
    q.Add(reconcile.Request{NamespacedName: types.NamespacedName{
        Name:      evt.Object.GetName(),
        Namespace: evt.Object.GetNamespace(),
    }})
}

所以Reconcile協調執行的數據對象,實際是通過Informer中的EventHandler入隊的。

kubebuilder等腳手架框架解析

利用kubebuilder、operator-sdk等框架,可以快速生成相應資源對象的controller代碼。接下來,以kubebuilder為例,對controller代碼邏輯進行解析。

一個完整的controller啟動邏輯包含以下步驟:

1) 在main.go啟動函數中,會定義一個controllerManager對象。

mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
    Scheme:                 scheme,
    MetricsBindAddress:     metricsAddr,
    Port:                   9443,
    HealthProbeBindAddress: probeAddr,
    LeaderElection:         enableLeaderElection,
    LeaderElectionID:       "9a82ee0d.my.domain",
    CertDir:                "dir",
    ...
})

2)通過SetUpWithManager()方法,注冊每種資源對象的controller到controllerManager對象中。

if err = (&controllers.AppServiceReconciler{
    Client: mgr.GetClient(),
    Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
    setupLog.Error(err, "unable to create controller", "controller", "AppService")
    os.Exit(1)
}

3)啟動controllerManager,也即啟動對應資源對象的controller。

if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
    setupLog.Error(err, "problem running manager")
    os.Exit(1)
}

主要的代碼邏輯在于SetUpWithManager()和mgr.Start()這兩個方法中。

// SetupWithManager sets up the controller with the Manager.
func (r *AppServiceReconciler) SetupWithManager(mgr ctrl.Manager) error {
    return ctrl.NewControllerManagedBy(mgr).
        For(&appexamplecomv1.AppService{}).
        Complete(r)
}

Builder

ctrl.NewControllerManagedBy(mgr)會返回一個builder對象。

NewControllerManagedBy = builder.ControllerManagedBy

func ControllerManagedBy(m manager.Manager) *Builder {
    return &Builder{mgr: m}
}

builder為controller的構造器,其結構定義為:

type Builder struct {
    forInput         ForInput
    ownsInput        []OwnsInput
    watchesInput     []WatchesInput
    mgr              manager.Manager
    globalPredicates []predicate.Predicate
    ctrl             controller.Controller
    ctrlOptions      controller.Options
    name             string
}

ctrlOptions指定構建controller的一些配置,主要是Reconciler。forInput指定被協調的對象本身,通過build.For()進行設置。ownsInput指定被協調監聽的子對象資源,通過build.Owns()進行設置。watchesInput能夠自定義EventHandler處理邏輯,通過build.Watches()進行設置。所以,kubebuilder生成的controller默認只會對協調的對象本身進行調協。

type WatchesInput struct {
    src              source.Source
    eventhandler     handler.EventHandler
    predicates       []predicate.Predicate
    objectProjection objectProjection
}

builder.Complete()會調用Builder.Build()進行構造。Build()包含doController()和doWatch()這兩個重要方法。

DoController()

doController通過資源對象的 GVK 來獲取 Controller 的名稱,最后通過一個 newController 函數來實例化Controller。

controllerName, err := blder.getControllerName(gvk, hasGVK)
blder.ctrl, err = newController(controllerName, blder.mgr, ctrlOptions)

newContrller為controller.New的別名,方法為:func New(name string, mgr manager.Manager, options Options) (Controller, error) { c, err := NewUnmanaged(name, mgr, options) if err != nil { return nil, err }

// Add the controller as a Manager components
   return c, mgr.Add(c)
}

c, err := NewUnmanaged(name, mgr, options)初始化Controller實例,Controller 實例化完成后,又通過 mgr.Add(c) 函數將控制器添加到 Manager 中去進行管理。controllerManager 的 Add 函數傳遞的是一個 Runnable 參數,Runnable 是一個接口,用來表示可以啟動的一個組件,而恰好 Controller 實際上就實現了這個接口的 Start 函數,所以可以通過 Add 函數來添加 Controller 實例。

DoWatch()

DoWatch實現比較簡單,就是調用controller.watch來注冊EventHandler事件。DoWatch方法會調用controller.Watch()方法來注冊EventHandler。可以看到對于forInput這類資源,默認的EventHandler為EnqueueRequestForObject,對于ownsInput這類資源,默認的EventHandler為EnqueueRequestForOwner,這兩類handler已在上文提到過,均實現了Create()、Update()、Delete()等方法,能夠將被調協的資源對象入隊。

func (blder *Builder) doWatch() error {
    // Reconcile type
    typeForSrc, err := blder.project(blder.forInput.object, blder.forInput.objectProjection)
    if err != nil {
        return err
    }
    src := &source.Kind{Type: typeForSrc}
    hdler := &handler.EnqueueRequestForObject{}
    allPredicates := append(blder.globalPredicates, blder.forInput.predicates...)
    if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil {
        return err
    }

    // Watches the managed types
    for _, own := range blder.ownsInput {
        typeForSrc, err := blder.project(own.object, own.objectProjection)
        if err != nil {
            return err
        }
        src := &source.Kind{Type: typeForSrc}
        hdler := &handler.EnqueueRequestForOwner{
            OwnerType:    blder.forInput.object,
            IsController: true,
        }
        allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...)
        allPredicates = append(allPredicates, own.predicates...)
        if err := blder.ctrl.Watch(src, hdler, allPredicates...); err != nil {
            return err
        }
    }

    // Do the watch requests
    for _, w := range blder.watchesInput {
        allPredicates := append([]predicate.Predicate(nil), blder.globalPredicates...)
        allPredicates = append(allPredicates, w.predicates...)

        // If the source of this watch is of type *source.Kind, project it.
        if srckind, ok := w.src.(*source.Kind); ok {
            typeForSrc, err := blder.project(srckind.Type, w.objectProjection)
            if err != nil {
                return err
            }
            srckind.Type = typeForSrc
        }

        if err := blder.ctrl.Watch(w.src, w.eventhandler, allPredicates...); err != nil {
            return err
        }
    }
    return nil
}

watchesInput這類資源需要自己實現EventHandler,使用類似以下方式實現相應功能。根據之前的結論,controller中調協的資源對象來自于queue,而queue中的數據是通過EventHandler的Create、Update、Delete等處理邏輯進行入隊的。因此這時controller的處理順序為:EventHandler中定義的邏輯->入隊->Reconcile。

func (r *AppServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {

    klog.Infof("開始Reconcile邏輯")
    ...
    return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *AppServiceReconciler) SetupWithManager(mgr ctrl.Manager) error {
    return ctrl.NewControllerManagedBy(mgr).
        Named("appServiceController").
        Watches(
            &source.Kind{
                Type: &appexamplecomv1.AppService{},
            },
            handler.Funcs{
                CreateFunc: func(createEvent event.CreateEvent, limitingInterface workqueue.RateLimitingInterface) {
                    klog.Infof("createFunc")
                    limitingInterface.Add(reconcile.Request{NamespacedName: types.NamespacedName{
                       Name:      createEvent.Object.GetName(),
                       Namespace: createEvent.Object.GetNamespace(),
                    }})
                },
                UpdateFunc: func(updateEvent event.UpdateEvent, limitingInterface workqueue.RateLimitingInterface) {
                    klog.Infof("updateFunc")
                },
                DeleteFunc: func(deleteEvent event.DeleteEvent, limitingInterface workqueue.RateLimitingInterface) {
                    klog.Infof("deleteFunc")
                },
            }).
        Complete(r)
}

上述代碼只有在Create時進行了入隊處理,因此只有在創建資源時會進入Reconcile的邏輯。

Manager.Start()

在注冊controller到manager后,需要使用mgr.Start(ctrl.SetupSignalHandler())來啟動manager。之前說過,注冊Controller時調用DoController方法中的mgr.Add()將controller已runnable的形式添加到了Manager。Manager.start()正是調用了cm.runnables的start方法,也即controller.start()來啟動controller。

func (cm *controllerManager) Start(ctx context.Context) (err error) {
    ...
    if err := cm.runnables.Webhooks.Start(cm.internalCtx); err != nil {
        if !errors.Is(err, wait.ErrWaitTimeout) {
            return err
        }
    }

    // Start and wait for caches.
    if err := cm.runnables.Caches.Start(cm.internalCtx); err != nil {
        if !errors.Is(err, wait.ErrWaitTimeout) {
            return err
        }
    }

    // Start the non-leaderelection Runnables after the cache has synced.
    if err := cm.runnables.Others.Start(cm.internalCtx); err != nil {
        if !errors.Is(err, wait.ErrWaitTimeout) {
            return err
        }
    }
    ...
}
責任編輯:武曉燕 來源: CNCF
相關推薦

2022-11-24 14:32:00

云原生K8S

2023-12-20 08:13:54

K8S監控管理

2021-11-29 13:13:57

網絡虛擬化容器

2021-08-04 16:06:45

DataOps智領云

2023-12-22 19:59:15

2024-10-23 11:34:18

云計算KubernetesAkamai

2022-09-22 09:00:46

CSS單位

2018-09-28 14:06:25

前端緩存后端

2022-11-06 21:14:02

數據驅動架構數據

2025-04-03 10:56:47

2023-11-27 17:35:48

ComponentWeb外層

2023-05-20 17:58:31

低代碼軟件

2022-10-20 08:01:23

2022-07-05 06:30:54

云網絡網絡云原生

2025-10-14 09:01:20

2022-12-01 17:23:45

2021-12-29 18:00:19

無損網絡網絡通信網絡

2022-07-26 00:00:03

語言模型人工智能

2021-05-07 14:03:36

大數據存儲接口CSI
點贊
收藏

51CTO技術棧公眾號

国产不卡在线视频| 欧美成人69| 在线观看视频91| 性高潮久久久久久久久| 国产极品久久久| 亚洲调教一区| 欧美二区三区91| 免费av手机在线观看| 自拍视频在线网| 夫妻av一区二区| 国产精品美女网站| 貂蝉被到爽流白浆在线观看| 亚洲午夜免费| 欧美日韩亚洲另类| 奇米精品一区二区三区| 四虎精品在线| 国产激情91久久精品导航| 日韩av手机在线观看| 强乱中文字幕av一区乱码| 亚洲老女人视频免费| 日韩一区二区三免费高清| 波多野结衣作品集| 免费在线国产视频| √…a在线天堂一区| 国产在线久久久| 欧美a∨亚洲欧美亚洲| 中国成人一区| 色偷偷av一区二区三区| 精品人妻无码一区二区三区| 风间由美一区二区av101| 欧美酷刑日本凌虐凌虐| 日韩毛片在线免费看| 在线播放麻豆| 国产日韩精品一区| 免费在线成人av| 熟妇高潮一区二区高潮| 国产不卡视频一区二区三区| 91午夜理伦私人影院| 影音先锋国产在线| 日本不卡一二三区黄网| 国产69久久精品成人| 日本视频www| 国产精品黄色| 欧美日韩第一页| 中文幕无线码中文字蜜桃| 全球中文成人在线| 欧美视频在线观看一区二区| 无人在线观看的免费高清视频| 麻豆视频在线| 亚洲欧美自拍偷拍| 亚洲欧美日韩精品在线| 天堂中文8资源在线8| 国产精品久久久久久久午夜片 | 在线播放精品一区二区三区 | 国产一区二区日韩精品| 国产精品久久久久久一区二区 | 97国产suv精品一区二区62| 人妻在线日韩免费视频| 久久电影在线| 亚洲精品视频播放| 免费看黄色的视频| 日韩激情在线| 伦理中文字幕亚洲| 欧美片一区二区| 在线不卡亚洲| 欧美在线视频播放| 国产美女www爽爽爽| 免费观看久久久4p| 性欧美亚洲xxxx乳在线观看| 亚洲少妇xxx| 欧美一区影院| 久久久久久久久久久av| xxxx.国产| 青青草91视频| 99在线视频播放| 无码精品一区二区三区在线 | 亚洲男人都懂的| 欧美大片免费播放| 波多野结依一区| 亚洲免费av在线| 日韩小视频网站| 婷婷激情一区| 91精品国产一区二区三区蜜臀| 啊啊啊国产视频| 国产精品视频首页| 亚洲成人久久久久| 美国黄色特级片| 国产精品啊啊啊| 国产精品爱久久久久久久| 97在线播放免费观看| 不卡的av网站| 一区二区三区的久久的视频| а_天堂中文在线| 欧美性大战久久久久久久蜜臀| 成年人网站大全| 日本一区二区三区视频在线看| 3d成人h动漫网站入口| 久久久久中文字幕亚洲精品| 国产成人黄色| 亚洲区免费影片| 日韩一区二区三区四区在线| 久久精品动漫| 国产精品aaaa| 亚洲欧美另类一区| 国产精品久久久爽爽爽麻豆色哟哟| 亚洲激情图片| 女人让男人操自己视频在线观看| 午夜电影网亚洲视频| 99re精彩视频| 2020国产精品小视频| 国产视频久久久久久久| 69xx绿帽三人行| 蜜桃一区二区三区在线观看| 国产欧美日韩综合精品二区| 黄av在线播放| 欧美日韩精品一区二区三区蜜桃| 91视频福利网| 日韩dvd碟片| 欧美尤物巨大精品爽| 亚洲欧美日韩一区二区三区四区| 日本午夜一区二区| 精品一区二区不卡| 国产第一页在线| 色菇凉天天综合网| 男男做爰猛烈叫床爽爽小说| 伊人情人综合网| 国产欧亚日韩视频| 玖玖综合伊人| 一本久久精品一区二区| 国产中文字幕一区二区| 欧美三级免费| 99理论电影网| 九色蝌蚪在线| 一本色道综合亚洲| 中文字幕剧情在线观看| 久久国产成人午夜av影院宅| 国产精品成人品| 99re只有精品| 亚洲人精品午夜| 播放灌醉水嫩大学生国内精品| 粉嫩av一区二区三区四区五区| 日韩欧美成人激情| 国产a免费视频| 国产老肥熟一区二区三区| 久久久久久国产精品mv| 午夜在线播放| 7777精品伊人久久久大香线蕉经典版下载 | 精品一区免费观看| 懂色av中文字幕一区二区三区| 欧美日韩一区在线视频| 午夜激情在线播放| 亚洲乱码国产乱码精品精| 一级黄色大片视频| 国产午夜三级一区二区三| 妺妺窝人体色www在线观看| 欧美一级淫片| 91九色国产社区在线观看| 美女国产在线| 日韩免费在线观看| 久久久久久久毛片| 一区三区视频| 久久精品国产99精品国产亚洲性色| 动漫一区在线| 精品日韩99亚洲| 亚洲欧美精品久久| 国产一区二区在线观看免费| 视频一区不卡| 欧美成人精品一区二区男人小说| 欧美成人三级电影在线| 69精品久久久| 国产亚洲一本大道中文在线| 超碰成人在线播放| 1000部精品久久久久久久久| 欧美一级日本a级v片| 小视频免费在线观看| 精品国产乱码久久久久久影片| 在线免费看av网站| 盗摄精品av一区二区三区| 强伦女教师2:伦理在线观看| 91嫩草精品| 国产第一区电影| 超碰在线无需免费| 日韩av在线免费播放| 久久精品久久精品久久| 久久一夜天堂av一区二区三区| a级黄色片免费| 日韩欧美天堂| 91久久久精品| 亚洲天堂免费电影| 久久久精品影院| 午夜福利理论片在线观看| 精品视频在线视频| 日韩欧美高清在线观看| 国产精品女主播av| 亚洲美女在线播放| 精品综合久久久久久8888| 国产免费观看高清视频| 国产大片一区| 欧美一级日本a级v片| 懂色av一区二区| 国产欧美日韩中文字幕在线| 国内精彩免费自拍视频在线观看网址| 欧美精品一区二区三区在线播放| 久久精品视频9| 国产精品色在线| 久久午夜夜伦鲁鲁片| 免费久久99精品国产自在现线| 欧美精品一区在线发布| 欧美特黄不卡| 国产精品色视频| 中文字幕这里只有精品| 久久97精品久久久久久久不卡 | 97精品国产99久久久久久免费| 一区二区三区天堂av| 人成网站在线观看| 欧美一区二区在线播放| 精品乱码一区内射人妻无码| 图片区小说区国产精品视频| 欧洲av一区二区三区| 99久久免费国产| 中文字幕人妻熟女人妻a片| 麻豆成人av在线| 欧洲美女和动交zoz0z| 超碰成人久久| 欧美不卡1区2区3区| 里番精品3d一二三区| eeuss一区二区三区| 澳门成人av网| 久久国产精品久久久久久久久久| 四虎永久在线精品免费网址| 日韩欧美久久久| 超碰在线人人干| 日韩欧美在线视频日韩欧美在线视频 | 高清欧美精品xxxxx| 综合天堂久久久久久久| 国产系列第一页| 国产精品久久久久久麻豆一区软件| 不卡日韩av| 亚洲视频精选| 春色成人在线视频| 亚洲网一区二区三区| 国产98在线|日韩| 伊人久久亚洲| 国产乱码一区| 欧美一区自拍| 欧美精品亚洲精品| 欧美日韩亚洲在线观看| 午夜精品一区二区在线观看的 | 精品人妻无码一区二区三区换脸| 久久99最新地址| 神马午夜伦理影院| 欧美精品99| 色欲色香天天天综合网www| 在线日韩欧美| 国产日韩一区二区在线观看| 老司机午夜精品视频在线观看| 日韩精品一区二区在线视频| 国产精品啊啊啊| 国产97在线 | 亚洲| 日韩精品成人一区二区三区| wwwwww.色| 国内成人免费视频| 不许穿内裤随时挨c调教h苏绵| 久久精品国产一区二区三| 亚洲另类第一页| 国产精品18久久久久| 免费在线观看污网站| 国产成人免费高清| 一本色道久久综合亚洲精品图片| 大尺度一区二区| 黄色a一级视频| av资源网一区| 一区二区三区在线观看免费视频| 91麻豆蜜桃一区二区三区| 久久精品国产亚洲av久| 亚洲欧洲av在线| 日韩欧美a级片| 欧美羞羞免费网站| www香蕉视频| 亚洲人av在线影院| a黄色片在线观看| 欧美自拍视频在线| 天堂√中文最新版在线| 午夜精品一区二区三区视频免费看 | 久久视频精品在线| av资源中文在线| 国产91九色视频| 91精品短视频| 亚洲在线色站| 色综合蜜月久久综合网| 每日在线观看av| 国产一区导航| 婷婷激情5月天| 久久久久久久网| 91人妻一区二区三区蜜臀| 亚洲国产毛片aaaaa无费看 | 亚洲在线视频一区| 波多野结衣电影在线播放| 日韩一区二区免费在线观看| 国产在线视频网址| 深夜成人在线观看| 在线观看爽视频| 99久久无色码| 91一区在线| 日韩毛片在线免费看| 风流少妇一区二区| 日本裸体美女视频| 色偷偷成人一区二区三区91| www日本高清| 日韩视频永久免费观看| 日本精品600av| 91地址最新发布| 日本伊人久久| 亚洲一区三区| 日韩精品久久久久久| 影音先锋人妻啪啪av资源网站| 久久综合久色欧美综合狠狠| 欧美性猛交xxxxx少妇| 精品久久久久久久久久久久久| 午夜精品一区二| 日韩电视剧免费观看网站| 曰本三级在线| 91在线观看欧美日韩| 日韩av在线播放网址| 日韩国产成人无码av毛片| 精品一区二区免费| gv天堂gv无码男同在线观看| 色一情一乱一乱一91av| 久久免费看视频| 国产91精品网站| 久久综合色占| 红桃av在线播放| 久久尤物电影视频在线观看| 五月天婷婷久久| 日韩电视剧免费观看网站| 三妻四妾完整版在线观看电视剧| 国产日韩欧美在线看| 久久国产精品亚洲人一区二区三区 | 亚洲v精品v日韩v欧美v专区| www.久久伊人| 欧美韩日一区二区| 96sao在线精品免费视频| 欧美日韩激情四射| 成人小视频免费观看| 男人天堂中文字幕| 亚洲第一页中文字幕| 男人的天堂免费在线视频| 欧美日韩电影一区二区| 久久中文欧美| 国产真人做爰视频免费| 欧美日韩精品三区| 成年人黄视频在线观看| 不卡的av一区| 欧美专区18| 中文字幕在线视频播放| 亚洲va国产天堂va久久en| 欧美偷拍视频| 国产精品高潮呻吟视频 | 国产高清在线一区| 亚洲国产高清一区| 免费观看av网站| 欧美午夜精品免费| 黄色一级片在线观看| 99re在线国产| 国产精品亚洲综合久久| 99国产精品免费视频| 国产精品剧情在线亚洲| 日本黄色中文字幕| 日韩精品在线免费播放| 朝桐光一区二区| 亚洲免费不卡| 国产高清不卡二三区| 特级毛片www| 久久韩剧网电视剧| 极品束缚调教一区二区网站| 日韩 欧美 视频| 国产网站一区二区三区| 国产视频一区二区三区四区五区| 中文字幕亚洲综合久久筱田步美| 成人黄色免费短视频| 99热这里只有精品7| av在线免费不卡| 这里只有精品6| 国内精品小视频在线观看| 精品视频免费在线观看| 在线成人精品视频| 欧美伊人久久久久久午夜久久久久| 久久av少妇| 91在线精品观看| 久久蜜桃资源一区二区老牛| 国产一二三四区| 精品日韩成人av| 六九午夜精品视频| 欧美深夜福利视频| 久久综合九色综合久久久精品综合| 日韩特级黄色片| 精品中文字幕在线2019| 欧美精选一区二区三区| 在线观看免费视频国产| 欧美日韩免费在线视频|