<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用 OpenKruise 增強 Kubernetes 工作負(fù)載之 CloneSet

          共 13510字,需瀏覽 28分鐘

           ·

          2022-03-04 00:30

          OpenKruise 是一個基于 Kubernetes 的擴(kuò)展套件,主要聚焦于云原生應(yīng)用的自動化,比如部署、發(fā)布、運維以及可用性防護(hù)。OpenKruise 提供的絕大部分能力都是基于 CRD 擴(kuò)展來定義的,它們不存在于任何外部依賴,可以運行在任意純凈的 Kubernetes 集群中。Kubernetes 自身提供的一些應(yīng)用部署管理功能,對于大規(guī)模應(yīng)用與集群的場景這些功能是遠(yuǎn)遠(yuǎn)不夠的,OpenKruise 彌補了 Kubernetes 在應(yīng)用部署、升級、防護(hù)、運維等領(lǐng)域的不足。

          OpenKruise 提供了以下的一些核心能力:

          • 增強版本的 Workloads:OpenKruise 包含了一系列增強版本的工作負(fù)載,比如 CloneSet、Advanced StatefulSet、Advanced DaemonSet、BroadcastJob 等。它們不僅支持類似于 Kubernetes 原生 Workloads 的基礎(chǔ)功能,還提供了如原地升級、可配置的擴(kuò)縮容/發(fā)布策略、并發(fā)操作等。其中,原地升級是一種升級應(yīng)用容器鏡像甚至環(huán)境變量的全新方式,它只會用新的鏡像重建 Pod 中的特定容器,整個 Pod 以及其中的其他容器都不會被影響。因此它帶來了更快的發(fā)布速度,以及避免了對其他 Scheduler、CNI、CSI 等組件的負(fù)面影響。
          • 應(yīng)用的旁路管理:OpenKruise 提供了多種通過旁路管理應(yīng)用 sidecar 容器、多區(qū)域部署的方式,“旁路” 意味著你可以不需要修改應(yīng)用的 Workloads 來實現(xiàn)它們。比如,SidecarSet 能幫助你在所有匹配的 Pod 創(chuàng)建的時候都注入特定的 sidecar 容器,甚至可以原地升級已經(jīng)注入的 sidecar 容器鏡像、并且對 Pod 中其他容器不造成影響。而 WorkloadSpread 可以約束無狀態(tài) Workload 擴(kuò)容出來 Pod 的區(qū)域分布,賦予單一 workload 的多區(qū)域和彈性部署的能力。
          • 高可用性防護(hù):OpenKruise 可以保護(hù)你的 Kubernetes 資源不受級聯(lián)刪除機制的干擾,包括 CRD、Namespace、以及幾乎全部的 Workloads 類型資源。相比于 Kubernetes 原生的 PDB 只提供針對 Pod Eviction 的防護(hù),PodUnavailableBudget 能夠防護(hù) Pod Deletion、Eviction、Update 等許多種 voluntary disruption 場景。
          • 高級的應(yīng)用運維能力:OpenKruise 也提供了很多高級的運維能力來幫助你更好地管理應(yīng)用,比如可以通過 ImagePullJob 來在任意范圍的節(jié)點上預(yù)先拉取某些鏡像,或者指定某個 Pod 中的一個或多個容器被原地重啟。

          架構(gòu)

          下圖是 OpenKruise 的整體架構(gòu):

          首先我們要清楚所有 OpenKruise 的功能都是通過 Kubernetes CRD 來提供的:

          ??kubectl?get?crd?|?grep?kruise.io
          advancedcronjobs.apps.kruise.io????????????2021-09-16T06:02:36Z
          broadcastjobs.apps.kruise.io???????????????2021-09-16T06:02:36Z
          clonesets.apps.kruise.io???????????????????2021-09-16T06:02:36Z
          containerrecreaterequests.apps.kruise.io???2021-09-16T06:02:36Z
          daemonsets.apps.kruise.io??????????????????2021-09-16T06:02:36Z
          imagepulljobs.apps.kruise.io???????????????2021-09-16T06:02:36Z
          nodeimages.apps.kruise.io??????????????????2021-09-16T06:02:36Z
          podunavailablebudgets.policy.kruise.io?????2021-09-16T06:02:36Z
          resourcedistributions.apps.kruise.io???????2021-09-16T06:02:36Z
          sidecarsets.apps.kruise.io?????????????????2021-09-16T06:02:36Z
          statefulsets.apps.kruise.io????????????????2021-09-16T06:02:36Z
          uniteddeployments.apps.kruise.io???????????2021-09-16T06:02:37Z
          workloadspreads.apps.kruise.io?????????????2021-09-16T06:02:37Z

          其中 Kruise-manager 是一個運行控制器和 webhook 的中心組件,它通過 Deployment 部署在 kruise-system 命名空間中。從邏輯上來看,如 cloneset-controllersidecarset-controller 這些的控制器都是獨立運行的,不過為了減少復(fù)雜度,它們都被打包在一個獨立的二進(jìn)制文件、并運行在 kruise-controller-manager-xxx 這個 Pod 中。除了控制器之外,kruise-controller-manager-xxx 中還包含了針對 Kruise CRD 以及 Pod 資源的 admission webhook。Kruise-manager 會創(chuàng)建一些 webhook configurations 來配置哪些資源需要感知處理、以及提供一個 Service 來給 kube-apiserver 調(diào)用。

          從 v0.8.0 版本開始提供了一個新的 Kruise-daemon 組件,它通過 DaemonSet 部署到每個節(jié)點上,提供鏡像預(yù)熱、容器重啟等功能。

          安裝

          這里我們同樣還是使用 Helm 方式來進(jìn)行安裝,需要注意從 v1.0.0 開始,OpenKruise 要求在 Kubernetes >= 1.16 以上版本的集群中安裝和使用。

          首先添加 charts 倉庫:

          ??helm?repo?add?openkruise?https://openkruise.github.io/charts
          ??helm?repo?update

          然后執(zhí)行下面的命令安裝最新版本的應(yīng)用:

          ??helm?upgrade?--install?kruise?openkruise/kruise?--version?1.0.1

          該 charts 在模板中默認(rèn)定義了命名空間為 kruise-system,所以在安裝的時候可以不用指定,如果你的環(huán)境訪問 DockerHub 官方鏡像較慢,則可以使用下面的命令將鏡像替換成阿里云的鏡像:

          ??helm?upgrade?--install?kruise?openkruise/kruise?--set?manager.image.repository=openkruise-registry.cn-hangzhou.cr.aliyuncs.com/openkruise/kruise-manager?--version?1.0.1

          應(yīng)用部署完成后會在 kruise-system 命名空間下面運行2個 kruise-manager 的 Pod,同樣它們之間采用 leader-election 的方式選主,同一時間只有一個提供服務(wù),達(dá)到高可用的目的,此外還會以 DaemonSet 的形式啟動 kruise-daemon 組件:

          ??kubectl?get?pods?-n?kruise-system
          NAME????????????????????????????????????????READY???STATUS????RESTARTS???AGE
          kruise-controller-manager-f5c9b55c5-7hgt9???1/1?????Running???0??????????4m3s
          kruise-controller-manager-f5c9b55c5-v9ptf???1/1?????Running???0??????????4m3s
          kruise-daemon-bqf5v?????????????????????????1/1?????Running???0??????????4m3s
          kruise-daemon-hvgwv?????????????????????????1/1?????Running???0??????????4m3s
          kruise-daemon-tnqsx?????????????????????????1/1?????Running???0??????????4m3s

          如果不想使用默認(rèn)的參數(shù)進(jìn)行安裝,也可以自定義配置,可配置的 values 值可以參考 charts 文檔 https://github.com/openkruise/charts 進(jìn)行定制。

          CloneSet

          CloneSet 控制器是 OpenKruise 提供的對原生 Deployment 的增強控制器,在使用方式上和 Deployment 幾乎一致,如下所示是我們聲明的一個 CloneSet 資源對象:

          #?cloneset-demo.yaml
          apiVersion:?apps.kruise.io/v1alpha1
          kind:?CloneSet
          metadata:
          ??name:?cs-demo
          spec:
          ??replicas:?3
          ??selector:
          ????matchLabels:
          ??????app:?cs
          ??template:
          ????metadata:
          ??????labels:
          ????????app:?cs
          ????spec:
          ??????containers:
          ??????-?name:?nginx
          ????????image:?nginx:alpine
          ????????imagePullPolicy:?IfNotPresent
          ????????ports:
          ????????-?containerPort:?80

          直接創(chuàng)建上面的這個 CloneSet 對象:

          ??kubectl?apply?-f?cloneset-demo.yaml
          ??kubectl?get?cloneset?cs-demo
          NAME??????DESIRED???UPDATED???UPDATED_READY???READY???TOTAL???AGE
          cs-demo???3?????????3?????????3???????????????3???????3???????112s
          ??kubectl?describe?cloneset?cs-demo
          Name:?????????cs-demo
          Namespace:????default
          Labels:???????
          Annotations:??kubectl.kubernetes.io/last-applied-configuration:
          ????????????????{"apiVersion":"apps.kruise.io/v1alpha1","kind":"CloneSet","metadata":{"annotations":{},"name":"cs-demo","namespace":"default"},"spec":{"re...
          API?Version:??apps.kruise.io/v1alpha1
          Kind:?????????CloneSet
          ......
          Events:
          ??Type????Reason????????????Age???From?????????????????Message
          ??----????------????????????----??----?????????????????-------
          ??Normal??SuccessfulCreate??53s???cloneset-controller??succeed?to?create?pod?cs-demo-b6r6t
          ??Normal??SuccessfulCreate??53s???cloneset-controller??succeed?to?create?pod?cs-demo-fsbx5
          ??Normal??SuccessfulCreate??53s???cloneset-controller??succeed?to?create?pod?cs-demo-fv5gb

          該對象創(chuàng)建完成后我們可以通過 kubectl describe 命令查看對應(yīng)的 Events 信息,可以發(fā)現(xiàn) cloneset-controller 是直接創(chuàng)建的 Pod,這個和原生的 Deployment 就有一些區(qū)別了,Deployment 是通過 ReplicaSet 去創(chuàng)建的 Pod,所以從這里也可以看出來 CloneSet 是直接管理 Pod 的,3個副本的 Pod 此時也創(chuàng)建成功了:

          ??kubectl?get?pods?-l?app=cs
          NAME????????????READY???STATUS????RESTARTS???AGE
          cs-demo-b6r6t???1/1?????Running???0??????????5m19s
          cs-demo-fsbx5???1/1?????Running???0??????????5m19s
          cs-demo-fv5gb???1/1?????Running???0??????????5m19s

          CloneSet 雖然在使用上和 Deployment 比較類似,但還是有非常多比 Deployment 更高級的功能,下面我們來詳細(xì)介紹下。

          擴(kuò)縮容

          CloneSet 在擴(kuò)容的時候可以通過 ScaleStrategy.MaxUnavailable 來限制擴(kuò)容的步長,這樣可以對服務(wù)應(yīng)用的影響最小,可以設(shè)置一個絕對值或百分比,如果不設(shè)置該值,則表示不限制。

          比如我們在上面的資源清單中添加如下所示數(shù)據(jù):

          apiVersion:?apps.kruise.io/v1alpha1
          kind:?CloneSet
          metadata:
          ??name:?cs-demo
          spec:
          ??minReadySeconds:?60
          ??scaleStrategy:
          ????maxUnavailable:?1
          ??replicas:?5
          ??......

          上面我們配置 scaleStrategy.maxUnavailable 為1,結(jié)合 minReadySeconds 參數(shù),表示在擴(kuò)容時,只有當(dāng)上一個擴(kuò)容出的 Pod 已經(jīng) Ready 超過一分鐘后,CloneSet 才會執(zhí)行創(chuàng)建下一個 Pod,比如這里我們擴(kuò)容成5個副本,更新上面對象后查看 CloneSet 的事件:

          ??kubectl?describe?cloneset?cs-demo
          ......
          Events:
          ??Type?????Reason????????????Age???????????????????From?????????????????Message
          ??----?????------????????????----??????????????????----?????????????????-------
          ??Normal???SuccessfulCreate??35m???????????????????cloneset-controller??succeed?to?create?pod?cs-demo-b6r6t
          ??Normal???SuccessfulCreate??35m???????????????????cloneset-controller??succeed?to?create?pod?cs-demo-fsbx5
          ??Normal???SuccessfulCreate??35m???????????????????cloneset-controller??succeed?to?create?pod?cs-demo-fv5gb
          ??Warning??ScaleUpLimited????2m39s?????????????????cloneset-controller??scaleUp?is?limited?because?of?scaleStrategy.maxUnavailable,?limit:?1
          ??Normal???SuccessfulCreate??2m39s?????????????????cloneset-controller??succeed?to?create?pod?cs-demo-xlsdg
          ??Normal???SuccessfulCreate??98s???????????????????cloneset-controller??succeed?to?create?pod?cs-demo-8w7h4
          ??Warning??ScaleUpLimited????68s?(x12?over?2m39s)??cloneset-controller??scaleUp?is?limited?because?of?scaleStrategy.maxUnavailable,?limit:?0
          ??Normal???SuccessfulCreate??37s???????????????????cloneset-controller??succeed?to?create?pod?cs-demo-79rcx

          可以看到第一時間擴(kuò)容了一個 Pod,由于我們配置了 minReadySeconds: 60,也就是新擴(kuò)容的 Pod 創(chuàng)建成功超過1分鐘后才會擴(kuò)容另外一個 Pod,上面的 Events 信息也能表現(xiàn)出來,查看 Pod 的 AGE 也能看出來擴(kuò)容的2個 Pod 之間間隔了1分鐘左右:

          ??kubectl?get?pods?-l?app=cs
          NAME????????????READY???STATUS????RESTARTS???AGE
          cs-demo-79rcx???1/1?????Running???0??????????2m3s
          cs-demo-8w7h4???1/1?????Running???0??????????3m4s
          cs-demo-b6r6t???1/1?????Running???0??????????36m
          cs-demo-fv5gb???1/1?????Running???0??????????36m
          cs-demo-p4kmw???1/1?????Running???0??????????36s

          當(dāng) CloneSet 被縮容時,我們還可以指定一些 Pod 來刪除,這對于 StatefulSet 或者 Deployment 來說是無法實現(xiàn)的, StatefulSet 是根據(jù)序號來刪除 Pod,而 Deployment/ReplicaSet 目前只能根據(jù)控制器里定義的排序來刪除。而 CloneSet 允許用戶在縮小 replicas 數(shù)量的同時,指定想要刪除的 Pod 名字,如下所示:

          apiVersion:?apps.kruise.io/v1alpha1
          kind:?CloneSet
          metadata:
          ??name:?cs-demo
          spec:
          ??minReadySeconds:?60
          ??scaleStrategy:
          ????maxUnavailable:?1
          ????podsToDelete:
          ????-?cs-demo-79rcx
          ??replicas:?4
          ??......

          更新上面的資源對象后,會將應(yīng)用縮到4個 Pod,如果在 podsToDelete 列表中指定了 Pod 名字,則控制器會優(yōu)先刪除這些 Pod,對于已經(jīng)被刪除的 Pod,控制器會自動從 podsToDelete 列表中清理掉。比如我們更新上面的資源對象后 cs-demo-79rcx 這個 Pod 會被移除,其余會保留下來:

          ??kubectl?get?pods?-l?app=cs
          NAME????????????READY???STATUS????RESTARTS??????AGE
          cs-demo-8w7h4???1/1?????Running???4?(51m?ago)???3d6h
          cs-demo-b6r6t???1/1?????Running???4?(51m?ago)???3d6h
          cs-demo-fv5gb???1/1?????Running???4?(51m?ago)???3d6h
          cs-demo-p4kmw???1/1?????Running???4?(51m?ago)???3d6h

          如果你只把 Pod 名字加到 podsToDelete,但沒有修改 replicas 數(shù)量,那么控制器會先把指定的 Pod 刪掉,然后再擴(kuò)一個新的 Pod,另一種直接刪除 Pod 的方式是在要刪除的 Pod 上打 apps.kruise.io/specified-delete: true 標(biāo)簽。

          相比于手動直接刪除 Pod,使用 podsToDeleteapps.kruise.io/specified-delete: true 方式會有 CloneSet 的 maxUnavailable/maxSurge 來保護(hù)刪除, 并且會觸發(fā) PreparingDelete 生命周期的鉤子。

          升級

          CloneSet 一共提供了 3 種升級方式:

          • ReCreate: 刪除舊 Pod 和它的 PVC,然后用新版本重新創(chuàng)建出來,這是默認(rèn)的方式
          • InPlaceIfPossible: 會優(yōu)先嘗試原地升級 Pod,如果不行再采用重建升級
          • InPlaceOnly: 只允許采用原地升級,因此,用戶只能修改上一條中的限制字段,如果嘗試修改其他字段會被拒絕

          這里有一個重要概念:原地升級,這也是 OpenKruise 提供的核心功能之一,當(dāng)我們要升級一個 Pod 中鏡像的時候,下圖展示了重建升級原地升級的區(qū)別:

          重建升級時我們需要刪除舊 Pod、創(chuàng)建新 Pod:

          • Pod 名字和 uid 發(fā)生變化,因為它們是完全不同的兩個 Pod 對象(比如 Deployment 升級)
          • Pod 名字可能不變、但 uid 變化,因為它們是不同的 Pod 對象,只是復(fù)用了同一個名字(比如 StatefulSet 升級)
          • Pod 所在 Node 名字可能發(fā)生變化,因為新 Pod 很可能不會調(diào)度到之前所在的 Node 節(jié)點
          • Pod IP 發(fā)生變化,因為新 Pod 很大可能性是不會被分配到之前的 IP 地址

          但是對于原地升級,我們?nèi)匀粡?fù)用同一個 Pod 對象,只是修改它里面的字段:

          • 可以避免如調(diào)度分配 IP掛載盤等額外的操作和代價
          • 更快的鏡像拉取,因為會復(fù)用已有舊鏡像的大部分 layer 層,只需要拉取新鏡像變化的一些 layer
          • 當(dāng)一個容器在原地升級時,Pod 中的其他容器不會受到影響,仍然維持運行

          所以顯然如果能用原地升級方式來升級我們的工作負(fù)載,對在線應(yīng)用的影響是最小的。上面我們提到 CloneSet 升級類型支持 InPlaceIfPossible,這意味著 Kruise 會盡量對 Pod 采取原地升級,如果不能則退化到重建升級,以下的改動會被允許執(zhí)行原地升級:

          • 更新 workload 中的 spec.template.metadata.*,比如 labels/annotations,Kruise 只會將 metadata 中的改動更新到存量 Pod 上。
          • 更新 workload 中的 spec.template.spec.containers[x].image,Kruise 會原地升級 Pod 中這些容器的鏡像,而不會重建整個 Pod。
          • 從 Kruise v1.0 版本開始,更新 spec.template.metadata.labels/annotations 并且 container 中有配置 env from 這些改動的 labels/anntations,Kruise 會原地升級這些容器來生效新的 env 值。

          否則,其他字段的改動,比如 spec.template.spec.containers[x].envspec.template.spec.containers[x].resources,都是會回退為重建升級。

          比如我們將上面的應(yīng)用升級方式設(shè)置為 InPlaceIfPossible,只需要在資源清單中添加 spec.updateStrategy.type: InPlaceIfPossible 即可:

          apiVersion:?apps.kruise.io/v1alpha1
          kind:?CloneSet
          metadata:
          ??name:?cs-demo
          spec:
          ??updateStrategy:
          ????type:?InPlaceIfPossible
          ??......

          更新后可以發(fā)現(xiàn) Pod 的狀態(tài)并沒有發(fā)生什么大的變化,名稱、IP 都一樣,唯一變化的是鏡像 tag:

          ??kubectl?get?pods?-l?app=cs
          NAME????????????READY???STATUS????RESTARTS??????AGE
          cs-demo-8w7h4???1/1?????Running???4?(55m?ago)???3d6h
          cs-demo-b6r6t???1/1?????Running???4?(55m?ago)???3d6h
          cs-demo-fv5gb???1/1?????Running???5?(20s?ago)???3d6h
          cs-demo-p4kmw???1/1?????Running???5?(83s?ago)???3d6h
          ??kubectl?describe?cloneset?cs-demo
          Name:?????????cs-demo
          Namespace:????default
          Labels:???????
          Annotations:??
          API?Version:??apps.kruise.io/v1alpha1
          Kind:?????????CloneSet
          ......
          Events:
          ??Type????Reason??????????????????????Age????From?????????????????Message
          ??----????------??????????????????????----???----?????????????????-------
          ??Normal??SuccessfulDelete????????????4m44s??cloneset-controller??succeed?to?delete?pod?cs-demo-79rcx
          ??Normal??SuccessfulUpdatePodInPlace??97s????cloneset-controller??successfully?update?pod?cs-demo-p4kmw?in-place(revision?cs-demo-7cb9c88699)
          ??Normal??SuccessfulUpdatePodInPlace??34s????cloneset-controller??successfully?update?pod?cs-demo-fv5gb?in-place(revision?cs-demo-7cb9c88699)
          ??kubectl?describe?pod?cs-demo-p4kmw
          ......
          Events:
          ??Type?????Reason??????????????????Age??????????????????From?????Message
          ??----?????------??????????????????----?????????????????----?????-------
          ??Normal???Pulled??????????????????56m??????????????????kubelet??Container?image?"nginx:alpine"?already?present?on?machine
          ??Normal???Created?????????????????2m28s?(x2?over?56m)??kubelet??Created?container?nginx
          ??Normal???Killing?????????????????2m28s????????????????kubelet??Container?nginx?definition?changed,?will?be?restarted
          ??Normal???Pulled??????????????????2m28s????????????????kubelet??Container?image?"nginx:1.7.9"?already?present?on?machine
          ??Normal???Started?????????????????2m27s?(x2?over?56m)??kubelet??Started?container?nginx

          這就是原地升級的效果,原地升級整體工作流程如下圖所示:

          如果你在安裝或升級 Kruise 的時候啟用了 PreDownloadImageForInPlaceUpdate 這個 feature-gate,CloneSet 控制器會自動在所有舊版本 pod 所在節(jié)點上預(yù)熱你正在灰度發(fā)布的新版本鏡像,這對于應(yīng)用發(fā)布加速很有幫助。

          默認(rèn)情況下 CloneSet 每個新鏡像預(yù)熱時的并發(fā)度都是 1,也就是一個個節(jié)點拉鏡像,如果需要調(diào)整,你可以在 CloneSet annotation 上設(shè)置并發(fā)度:

          apiVersion:?apps.kruise.io/v1alpha1
          kind:?CloneSet
          metadata:
          ??annotations:
          ????apps.kruise.io/image-predownload-parallelism:?"5"

          注意,為了避免大部分不必要的鏡像拉取,目前只針對 replicas > 3 的 CloneSet 做自動預(yù)熱。

          此外 CloneSet 還支持分批進(jìn)行灰度,在 updateStrategy 屬性中可以配置 partition 參數(shù),該參數(shù)可以用來保留舊版本 Pod 的數(shù)量或百分比,默認(rèn)為0:

          • 如果是數(shù)字,控制器會將 (replicas - partition) 數(shù)量的 Pod 更新到最新版本
          • 如果是百分比,控制器會將 (replicas * (100% - partition)) 數(shù)量的 Pod 更新到最新版本

          比如,我們將上面示例中的的 image 更新為 nginx:latest 并且設(shè)置 partition=2,更新后,過一會查看可以發(fā)現(xiàn)只升級了2個 Pod:

          ??kubectl?get?pods?-l?app=cs?-L?controller-revision-hash
          NAME????????????READY???STATUS????RESTARTS??????AGE????CONTROLLER-REVISION-HASH
          cs-demo-dx4lb???1/1?????Running???0?????????????69s????cs-demo-6599fc6cdd
          cs-demo-fv5gb???1/1?????Running???0?????????????3d7h???cs-demo-7cb9c88699
          cs-demo-nngtm???1/1?????Running???0?????????????8s?????cs-demo-6599fc6cdd
          cs-demo-p4kmw???1/1?????Running???0?????????????3d6h???cs-demo-7cb9c88699

          此外 CloneSet 還支持一些更高級的用法,比如可以定義優(yōu)先級策略來控制 Pod 發(fā)布的優(yōu)先級規(guī)則,還可以定義策略來將一類 Pod 打散到整個發(fā)布過程中,也可以暫停 Pod 發(fā)布等操作。

          瀏覽 85
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人午夜天天爽 | 404notfound网红视频在线观看 | 日韩一区二区三区四区 | 国产福利精品在线播放 | 草溜短视频 |