1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        基于 KubeVela 的 GitOps 交付

        共 33002字,需瀏覽 67分鐘

         ·

        2023-10-26 14:44

        KubeVela 作為一個聲明式的應用交付控制平面,天然就可以以 GitOps 的方式進行使用,并且這樣做會在 GitOps 的基礎上為用戶提供更多的益處和端到端的體驗,包括:

        • 應用交付工作流(CD 流水線):KubeVela 支持在 GitOps 模式中描述過程式的應用交付,而不只是簡單的聲明終態(tài);
        • 處理部署過程中的各種依賴關系和拓撲結(jié)構;
        • 在現(xiàn)有各種 GitOps 工具的語義之上提供統(tǒng)一的上層抽象,簡化應用交付與管理過程;
        • 統(tǒng)一進行云服務的聲明、部署和服務綁定;
        • 提供開箱即用的交付策略(金絲雀、藍綠發(fā)布等);
        • 提供開箱即用的混合云/多云部署策略(放置規(guī)則、集群過濾規(guī)則等);
        • 在多環(huán)境交付中提供 Kustomize 風格的 Patch 來描述部署差異,而無需學習任何 Kustomize 本身的細節(jié)

        GitOps 模式需要依賴 FluxCD 插件,所以在使用 GitOps 模式下交付應用之前需要先啟用 FluxCD 插件。

        vela addon enable fluxcd

        GitOps 工作流分為 CI 和 `CD 兩個部分:

        • CI:持續(xù)集成對業(yè)務代碼進行代碼構建、構建鏡像并推送至鏡像倉庫。目前有許多成熟的 CI 工具:如開源項目常用的 GitHub Action、Travis 等,以及企業(yè)中常用的 Jenkins、Tekton 等,KubeVela 圍繞 GitOps 可以對接任意工具下的 CI 流程。
        • CD:持續(xù)部署會自動更新集群中的配置,如將鏡像倉庫中的最新鏡像更新到集群中。目前主要有兩種方案的 CD:
          • Push-Based:Push 模式的 CD 主要是通過配置 CI 流水線來完成的,這種方式需要將集群的訪問秘鑰共享給 CI,從而使得 CI 流水線能夠通過命令將更改推送到集群中。前面我們講解的 Jenkins 方式就屬于該方案。
          • Pull-Based:Pull 模式的 CD 會在集群中監(jiān)聽倉庫(代碼倉庫或者配置倉庫)的變化,并且將這些變化同步到集群中。這種方式與 Push 模式相比,由集群主動拉取更新,從而避免了秘鑰暴露的問題。前面課程中我們講解的 Argo CD 與 Flux CD 就屬于這種模式。

        而交付面向的人員有以下兩種:

        • 面向平臺管理員/運維人員的基礎設施交付,用戶可以通過直接更新倉庫中的配置文件,從而更新集群中的基礎設施配置,如系統(tǒng)的依賴軟件、安全策略、存儲、網(wǎng)絡等基礎設施配置。
        • 面向終端開發(fā)者的交付,用戶的代碼一旦合并到應用代碼倉庫,就自動化觸發(fā)集群中應用的更新,可以更高效的完成應用的迭代,與 KubeVela 的灰度發(fā)布、流量調(diào)撥、多集群部署等功能結(jié)合可以形成更為強大的自動化發(fā)布能力。

        面向平臺管理員/運維人員的交付

        如下圖所示,對于平臺管理員/運維人員而言,他們并不需要關心應用的代碼,所以只需要準備一個 Git 配置倉庫并部署 KubeVela 配置文件,后續(xù)對于應用及基礎設施的配置變動,便可通過直接更新 Git 配置倉庫來完成,使得每一次配置變更可追蹤。

        這里我們將部署一個 MySQL 數(shù)據(jù)庫作為項目的基礎設施,同時部署一個業(yè)務應用,使用這個數(shù)據(jù)庫。配置倉庫的目錄結(jié)構如下:

        • clusters/ 中包含集群中的 KubeVela GitOps 配置,用戶需要將 clusters/ 中的文件手動部署到集群中。這個是一次性的管控操作,執(zhí)行完成后,KubeVela 便能自動監(jiān)聽配置倉庫中的文件變動且自動更新集群中的配置。其中,clusters/apps.yaml 將監(jiān)聽 apps/ 下所有應用的變化,clusters/infra.yaml 將監(jiān)聽 infrastructure/ 下所有基礎設施的變化。
        • apps/ 目錄中包含業(yè)務應用的所有配置,在本例中為一個使用數(shù)據(jù)庫的業(yè)務應用。
        • infrastructure/ 中包含一些基礎設施相關的配置和策略,在本例中為 MySQL 數(shù)據(jù)庫。
        ├── apps
        │   └── my-app.yaml
        ├── clusters
        │   ├── apps.yaml
        │   └── infra.yaml
        └── infrastructure
            └── mysql.yaml

        KubeVela 建議使用如上的目錄結(jié)構管理你的 GitOps 倉庫。clusters/ 中存放相關的 KubeVela GitOps 配置并需要被手動部署到集群中,apps/infrastructure/ 中分別存放你的應用和基礎設施配置。通過把應用和基礎配置分開,能夠更為合理的管理你的部署環(huán)境,隔離應用的變動影響。

        clusters/ 目錄

        首先,我們來看下 clusters 目錄,這也是 KubeVela 對接 GitOps 的初始化操作配置目錄。

        clusters/infra.yaml 為例:

        apiVersion: core.oam.dev/v1beta1
        kind: Application
        metadata:
          name: infra
        spec:
          components:
            - name: database-config
              type: kustomize
              properties:
                repoType: git
                # 將此處替換成你需要監(jiān)聽的 git 配置倉庫地址
                url: https://github.com/cnych/KubeVela-GitOps-Infra-Demo
                # 如果是私有倉庫,還需要關聯(lián) git secret
                # secretRef: git-secret
                # 自動拉取配置的時間間隔,由于基礎設施的變動性較小,此處設置為十分鐘
                pullInterval: 10m
                git:
                  # 監(jiān)聽變動的分支
                  branch: main
                # 監(jiān)聽變動的路徑,指向倉庫中 infrastructure 目錄下的文件
                path: ./infrastructure

        apps.yamlinfra.yaml 幾乎保持一致,只不過監(jiān)聽的文件目錄有所區(qū)別。在 apps.yaml 中,properties.path 的值將改為 ./apps,表明監(jiān)聽 apps/ 目錄下的文件變動。

        cluster 文件夾中的 GitOps 管控配置文件需要在初始化的時候一次性手動部署到集群中,在此之后 KubeVela 將自動監(jiān)聽 apps/ 以及 infrastructure/ 目錄下的配置文件并定期更新同步。

        apps/ 目錄

        apps/ 目錄中存放著應用配置文件,這是一個配置了數(shù)據(jù)庫信息以及 Ingress 的簡單應用。該應用將連接到一個 MySQL 數(shù)據(jù)庫,并簡單地啟動服務。在默認的服務路徑下,會顯示當前版本號。在 /db 路徑下,會列出當前數(shù)據(jù)庫中的信息。

        apiVersion: core.oam.dev/v1beta1
        kind: Application
        metadata:
          name: my-app
          namespace: default
        spec:
          components:
            - name: my-server
              type: webservice
              properties:
                image: cnych/kubevela-gitops-demo:main-76a34322-1697703461
                port: 8088
                env:
                  - name: DB_HOST
                    value: mysql-cluster-mysql.default.svc.cluster.local:3306
                  - name: DB_PASSWORD
                    valueFrom:
                      secretKeyRef:
                        name: mysql-secret
                        key: ROOT_PASSWORD
              traits:
                - type: scaler
                  properties:
                    replicas: 1
                - type: gateway
                  properties:
                    class: nginx
                    classInSpec: true
                    domain: vela-gitops-demo.k8s.local
                    http:
                      /: 8088
                    pathType: ImplementationSpecific

        這是一個使用了 KubeVela 內(nèi)置組件類型 webservice 的應用,該應用綁定了 gateway 運維特征。通過在應用中聲明運維能力的方式,只需一個文件,便能將底層的 Deployment、Service、Ingress 集合起來,從而更為便捷地管理應用。

        infrastructure/ 目錄

        infrastructure/ 目錄下存放一些基礎設施的配置。此處我們使用 mysql controller 來部署了一個 MySQL 集群。

        apiVersion: core.oam.dev/v1beta1
        kind: Application
        metadata:
          name: mysql
          namespace: default
        spec:
          components:
            - name: mysql-secret
              type: k8s-objects # 需要添加一個包含 ROOT_PASSWORD 的 secret
              properties:
                objects:
                  - apiVersion: v1
                    kind: Secret
                    metadata:
                      name: mysql-secret
                    type: Opaque
                    stringData:
                      ROOT_PASSWORD: root321
            - name: mysql-operator
              type: helm
              properties:
                repoType: helm
                url: https://helm-charts.bitpoke.io
                chart: mysql-operator
                version: 0.6.3
            - name: mysql-cluster
              type: raw
              dependsOn:
                - mysql-operator
                - mysql-secret
              properties:
                apiVersion: mysql.presslabs.org/v1alpha1
                kind: MysqlCluster
                metadata:
                  name: mysql-cluster
                spec:
                  replicas: 1
                  secretName: mysql-secret

        在這個 MySQL 應用中,我們添加了 3 個 KubeVela 的組件,第一個是一個 k8s-objects 類型的組件,也就是直接應用 Kubernetes 資源對象,我們這里需要部署一個 Secret 對象;然后添加一個 helm 類型的組件,用來部署 MySQL 的 Operator。當 Operator 部署成功且正確運行后,最后我們將開始部署 MySQL 集群。

        部署 clusters/ 目錄下的文件

        配置完以上文件并存放到 Git 配置倉庫后,我們需要在集群中手動部署 clusters/ 目錄下的 KubeVela GitOps 配置文件。

        首先,在集群中部署 clusters/infra.yaml??梢钥吹剿詣釉诩褐欣鹆?infrastructure/ 目錄下的 MySQL 部署文件:

        $ kubectl apply -f clusters/infra.yaml
        $ vela ls
        APP             COMPONENT       TYPE            TRAITS          PHASE   HEALTHY STATUS                                                          CREATED-TIME
        infra           database-config kustomize                       running healthy                                                                 2023-10-19 15:27:28 +0800 CST
        mysql           mysql-operator  helm                            running healthy Fetch repository successfully, Create helm release              2023-10-19 15:27:31 +0800 CST
                                                                                        successfully
        └─              mysql-cluster   raw                             running healthy                                                                 2023-10-19 15:27:31 +0800 CST

        至此,我們通過部署 KubeVela GitOps 配置文件,自動在集群中拉起了數(shù)據(jù)庫基礎設施。

        $ kubectl get pods
        NAME                                     READY   STATUS    RESTARTS         AGE
        mysql-cluster-mysql-0                    4/4     Running   0                35m
        mysql-operator-0                         2/2     Running   0                35m

        通過這種方式,我們可以方便地通過更新 Git 配置倉庫中的文件,從而自動化更新集群中的配置。

        面向終端開發(fā)者的交付

        對于終端開發(fā)者而言,在 KubeVela Git 配置倉庫以外,還需要準備一個應用代碼倉庫。在用戶更新了應用代碼倉庫中的代碼后,需要配置一個 CI 來自動構建鏡像并推送至鏡像倉庫中。KubeVela 會監(jiān)聽鏡像倉庫中的最新鏡像,并自動更新配置倉庫中的鏡像配置,最后再更新集群中的應用配置。使用戶可以達成在更新代碼后,集群中的配置也自動更新的效果,代碼倉庫位于 https://github.com/cnych/KubeVela-GitOps-App-Demo。

        準備代碼倉庫

        準備一個代碼倉庫,里面包含一些源代碼以及對應的 Dockerfile。這些代碼將連接到一個 MySQL 數(shù)據(jù)庫,并簡單地啟動服務。在默認的服務路徑下,會顯示當前版本號。在 /db 路徑下,會列出當前數(shù)據(jù)庫中的信息,基本代碼如下所示:

        http.HandleFunc("/"func(w http.ResponseWriter, r *http.Request) {
            _, _ = fmt.Fprintf(w, "Version: %s\n", VERSION)
        })
        http.HandleFunc("/db"func(w http.ResponseWriter, r *http.Request) {
            rows, err := db.Query("select * from userinfo;")
            if err != nil {
                _, _ = fmt.Fprintf(w, "Error: %v\n", err)
            }
            for rows.Next() {
                var username string
                var desc string
                err = rows.Scan(&username, &desc)
                if err != nil {
                    _, _ = fmt.Fprintf(w, "Scan Error: %v\n", err)
                }
                _, _ = fmt.Fprintf(w, "User: %s \nDescription: %s\n\n", username, desc)
            }
        })

        if err := http.ListenAndServe(":8088"nil); err != nil {
            panic(err.Error())
        }

        我們希望用戶改動代碼進行提交后,自動構建出最新的鏡像并推送到鏡像倉庫。這一步 CI 可以通過前面我們講解的 Jenkins 來實現(xiàn),基本一致。

        首先為代碼倉庫創(chuàng)建一個 Webhook,指向 Jenkins 的觸發(fā)器地址:

        然后在 Jenkins 中創(chuàng)建一個名為 KubeVela-GitOps-App-Demo 的流水線:

        并勾選 GitHub hook trigger for GITScm polling 觸發(fā)器。

        觸發(fā)器

        然后添加如下所示的流水線腳本:

        void setBuildStatus(String message, String state) {
          step([
              $class: "GitHubCommitStatusSetter",
              reposSource: [$class: "ManuallyEnteredRepositorySource"url: "https://github.com/cnych/KubeVela-GitOps-App-Demo"],
              contextSource: [$class: "ManuallyEnteredCommitContextSource"context: "ci/jenkins/deploy-status"],
              errorHandlers: [[$class: "ChangingBuildStatusErrorHandler"result: "UNSTABLE"]],
              statusResultSource: [ $class: "ConditionalStatusResultSource"results: [[$class: "AnyBuildResult"message: message, state: state]] ]
          ]);
        }
        pipeline {
            agent {
              kubernetes {
                cloud 'Kubernetes'
                defaultContainer 'jnlp'
                yaml '''
                spec:
                  serviceAccountName: jenkins
                  containers:
                  - name: golang
                    image: golang:1.16-alpine3.15
                    command:
                    - cat
                    tty: true
                  - name: docker
                    image: docker:latest
                    command:
                    - cat
                    tty: true
                    env:
                    - name: DOCKER_HOST
                      value: tcp://docker-dind:2375
        '''

              }
            }
            stages {
                stage('Prepare') {
                    steps {
                        script {
                            def checkout = git branch: 'main'url: 'https://github.com/cnych/KubeVela-GitOps-App-Demo.git'
                            env.GIT_COMMIT = checkout.GIT_COMMIT
                            env.GIT_BRANCH = checkout.GIT_BRANCH

                            def unixTime = (new Date().time.intdiv(1000))
                            def gitBranch = env.GIT_BRANCH.replace("origin/""")
                            env.BUILD_ID = "${gitBranch}-${env.GIT_COMMIT.substring(0,8)}-${unixTime}"

                            echo "env.GIT_BRANCH=${env.GIT_BRANCH},env.GIT_COMMIT=${env.GIT_COMMIT}"
                            echo "env.BUILD_ID=${env.BUILD_ID}"

                            setBuildStatus("Deploy running""PENDING");
                        }
                    }
                }
                stage('Test') {
                    steps {
                      container('golang') {
                        sh 'GOPROXY=https://goproxy.io CGO_ENABLED=0 GOCACHE=$(pwd)/.cache go test *.go'
                      }
                    }
                }
                stage('Build') {
                    steps {
                      withCredentials([[$class: 'UsernamePasswordMultiBinding',
                          credentialsId: 'docker-auth',
                          usernameVariable: 'DOCKER_USER',
                          passwordVariable: 'DOCKER_PASSWORD']]) {
                          container('docker') {
                              sh """
                              docker login -u ${DOCKER_USER} -p ${DOCKER_PASSWORD}
                              docker build -t cnych/kubevela-gitops-demo:${env.BUILD_ID} .
                              docker push cnych/kubevela-gitops-demo:${env.BUILD_ID}
                              """

                          }
                      }
                    }
                }
            }
            post {
                success {
                    setBuildStatus("Deploy success""SUCCESS");
                }
                failure {
                    setBuildStatus("Deploy failed""FAILURE");
                }
            }
        }

        構建后我們就可以將應用的鏡像打包后推送到 Docker Hub 去。

        配置秘鑰信息

        在新的鏡像推送到鏡像倉庫后,KubeVela 會識別到新的鏡像,并更新倉庫及集群中的 Application 配置文件。因此,我們需要一個含有 Git 信息的 Secret,使 KubeVela 向 Git 倉庫進行提交。部署如下文件,將其中的用戶名和密碼替換成你的 Git 用戶名及密碼(或 Token):

        apiVersion: v1
        kind: Secret
        metadata:
          name: git-secret
        type: kubernetes.io/basic-auth
        stringData:
          username: <your username>
          password: <your password>

        準備配置倉庫

        配置倉庫與之前面向運維人員的配置大同小異,只需要加上與鏡像倉庫相關的配置即可。

        修改 clusters/ 中的 apps.yaml,該 GitOps 配置會監(jiān)聽倉庫中 apps/ 下的應用文件變動以及鏡像倉庫中的鏡像更新:

        # ...... 省略其他的
        imageRepository:
          # 鏡像地址
          image: <your image>
          # 如果這是一個私有的鏡像倉庫,可以通過 `kubectl create secret docker-registry` 創(chuàng)建對應的鏡像秘鑰并相關聯(lián)
          secretRef: dockerhub-secret
          filterTags:
            # 可對鏡像 tag 進行過濾
            pattern: "^main-[a-f0-9]+-(?P<ts>[0-9]+)"
            extract: "$ts"
          # 通過 policy 篩選出最新的鏡像 Tag 并用于更新
          policy:
            numerical:
              order: asc
          # 追加提交信息
          commitMessage: "Image: {{range .Updated.Images}}{{println .}}{{end}}"

        修改 apps/my-app.yaml 中的 image 字段,在后面加上 # {"$imagepolicy": "default:apps"} 的注釋,KubeVela 會通過該注釋去更新對應的鏡像字段,default:apps 是上面 GitOps 配置對應的命名空間和名稱。

        spec:
          components:
            - name: my-server
              type: webservice
              properties:
                image: cnych/kubevela-gitops-demo:main-9e8d2465-1697703645 # {"$imagepolicy": "default:apps"}

        clusters/ 中包含鏡像倉庫配置的文件更新到集群中后,我們便可以通過修改代碼來完成應用的更新。

        部署 clusters/apps.yaml

        $ kubectl apply -f clusters/apps.yaml
        $ vela ls
        APP             COMPONENT       TYPE            TRAITS          PHASE           HEALTHY         STATUS                                                     CREATED-TIME
        apps            apps            kustomize                       running         healthy                                                                    2023-10-19 16:31:49 +0800 CST
        my-app          my-server       webservice      scaler,gateway  runningWorkflow unhealthy       Ready:0/1                                                  2023-10-19 16:32:11 +0800 CST
        $ kubectl get pods
        NAME                                     READY   STATUS    RESTARTS         AGE
        my-server-6947fd65f9-84zhv               1/1     Running   0                2m

        這樣我們就可以通過部署 KubeVela GitOps 配置文件,自動在集群中拉起應用了。我們可以通過 curl 應用的 Ingress 來驗證結(jié)果是否正確,可以看到目前的版本是 0.1.5,并且成功地連接到了數(shù)據(jù)庫:

        $ kubectl get ingress
        NAME           CLASS   HOSTS                        ADDRESS   PORTS   AGE
        my-server      nginx   vela-gitops-demo.k8s.local             80      115s
        $ curl -H "Host:vela-gitops-demo.k8s.local" http://192.168.0.100
        Version: 0.1.8
        $ curl -H "Host:vela-gitops-demo.k8s.local" http://192.168.0.100/db
        User: KubeVela
        Description: It's a test user

        修改代碼

        將代碼文件中的 Version 改為 0.2.0,并修改數(shù)據(jù)庫中的數(shù)據(jù):

        const VERSION = "0.2.0"

        ...

        func InsertInitData(db *sql.DB) {
            stmt, err := db.Prepare(insertInitData)
            if err != nil {
                panic(err)
            }
            defer stmt.Close()

            _, err = stmt.Exec("KubeVela2""It's another test user")
            if err != nil {
                panic(err)
            }
        }

        提交該改動至代碼倉庫,正常我們配置的 CI 流水線就會自動開始構建鏡像并推送至鏡像倉庫。

        而 KubeVela 會通過監(jiān)聽鏡像倉庫,根據(jù)最新的鏡像 Tag 來更新配置倉庫中 apps/ 下的應用 my-app。

        此時,可以看到配置倉庫中有一條來自 kubevelabot 的提交,提交信息均帶有 Update image automatically. 前綴。你也可以通過 {{range .Updated.Images}}{{println .}}{{end}}commitMessage 字段中追加你所需要的信息。

        經(jīng)過一段時間后,應用 my-app 就自動更新了。KubeVela 會通過你配置的 interval 時間間隔,來每隔一段時間分別從配置倉庫及鏡像倉庫中獲取最新信息:

        • 當 Git 倉庫中的配置文件被更新時,KubeVela 將根據(jù)最新的配置更新集群中的應用。
        • 當鏡像倉庫中多了新的 Tag 時,KubeVela 將根據(jù)你配置的 policy 規(guī)則,篩選出最新的鏡像 Tag,并更新到 Git 倉庫中。而當代碼倉庫中的文件被更新后,KubeVela 將重復第一步,更新集群中的文件,從而達到了自動部署的效果。

        通用我們可以通過 curl 對應的 Ingress 查看當前版本和數(shù)據(jù)庫信息:

        $ kubectl get ingress
        NAME           CLASS   HOSTS                        ADDRESS   PORTS   AGE
        my-server      nginx   vela-gitops-demo.k8s.local             80      12m

        $ curl -H "Host:vela-gitops-demo.k8s.local" http://<ingress-ip>
        Version: 0.2.0

        $ curl -H "Host:vela-gitops-demo.k8s.local" http://<ingress-ip>/db
        User: KubeVela
        Description: It's a test user

        User: KubeVela2
        Description: It'
        s another test user

        版本已被成功更新!至此,我們完成了從變更代碼,到自動部署至集群的全部操作。

        總結(jié)

        在運維側(cè),如若需要更新基礎設施(如數(shù)據(jù)庫)的配置,或是應用的配置項,只需要修改配置倉庫中的文件,KubeVela 將自動把配置同步到集群中,簡化了部署流程。

        在研發(fā)側(cè),用戶修改代碼倉庫中的代碼后,KubeVela 將自動更新配置倉庫中的鏡像,從而進行應用的版本更新。通過與 GitOps 的結(jié)合,KubeVela 加速了應用從開發(fā)到部署的整個流程??赡苣銜X得這和 Flux CD 不是差不多嗎?的確是這樣的,KubeVela 的 GitOps 功能本身就是依賴 Flux CD 的,但是 KubeVela 的功能可遠遠不止于此,比如說上面我們的應用使用的 MySQL 數(shù)據(jù)我們是通過 MySQL Operator 來部署的,那如果我現(xiàn)在還換成云資源 RDS 呢?按照以前的方式方法,那么我們需要去云平臺手動開通 RDS 或者使用 Terraform 來進行管理,但在 KubeVela 中我們完全可以幫助開發(fā)者集成、編排不同類型的云資源,涵蓋混合多云環(huán)境,讓你用統(tǒng)一地方式去使用不同廠商的云資源。同樣的我們只需要在 GitOps 倉庫中的配置文件 Application 對象中去添加云資源的管理配置即可,這樣做到了一個對象管理多種資源的能力,這也是 KubeVela 的核心能力之一。

        最后如果你覺得應用太多管理不太方便,那么我們還可以使用 vela top 命令獲取平臺的概覽信息以及對應用程序的資源狀態(tài)進行診斷。

        參考文檔:https://kubevela.io/zh/docs/end-user/gitops/fluxcd/

        瀏覽 1522
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            kaori全部av作品大全 | 狠狠操免费视频 | 很黄很污视频 | 婷婷五月免费视频 | 中国美女光屁股无遮挡 | 色丁香婷婷 | 放荡护士夹得我好爽 | 欧美黄色三级视频 | 亚洲爆乳无码一区二区三区 | 欧美激情一区 |